diff options
| author | Matthew Petroff <matthew@mpetroff.net> | 2013-09-12 23:14:21 +0000 |
|---|---|---|
| committer | Matthew Petroff <matthew@mpetroff.net> | 2013-09-12 23:14:21 +0000 |
| commit | 51c44884aca2d41b8b38df329835e2a3c2d4e0ef (patch) | |
| tree | 3b7576e868b9edbabc1bc77d5e0320971ddad243 /src | |
| parent | Finish fixing document unit change undo bug. (diff) | |
| parent | Merge in fix for critical blocker #166371 (diff) | |
| download | inkscape-51c44884aca2d41b8b38df329835e2a3c2d4e0ef.tar.gz inkscape-51c44884aca2d41b8b38df329835e2a3c2d4e0ef.zip | |
Merge from trunk.
(bzr r12475.1.16)
Diffstat (limited to 'src')
101 files changed, 44856 insertions, 4146 deletions
diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h index bb6f2d2b8..eb9705a4b 100644 --- a/src/2geom/generic-rect.h +++ b/src/2geom/generic-rect.h @@ -261,7 +261,7 @@ public: expandBy(amount, amount); } /** @brief Expand the rectangle in both directions. - * Note that this is different from scaling. Negative values wil shrink the + * Note that this is different from scaling. Negative values will shrink the * rectangle. If <code>-x</code> is larger than * half of the width, the X interval will contain only the X coordinate * of the midpoint; same for height. */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4f7592119..42e091f43 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -235,7 +235,7 @@ set(inkscape_SRC knotholder.cpp layer-fns.cpp layer-manager.cpp - layer-model.cpp + layer-model.cpp line-geometry.cpp line-snapper.cpp lpe-tool-context.cpp @@ -323,6 +323,7 @@ set(inkscape_SRC color-profile.h color-rgba.h color.h + colorspace.h common-context.h composite-undo-stack-observer.h conditions.h @@ -555,6 +556,7 @@ add_subdirectory(libavoid) add_subdirectory(libcola) add_subdirectory(libcroco) add_subdirectory(libgdl) +add_subdirectory(libuemf) add_subdirectory(libvpsc) add_subdirectory(livarot) add_subdirectory(libnrtype) @@ -594,6 +596,7 @@ target_link_libraries(inkscape cola_LIB vpsc_LIB livarot_LIB + uemf_LIB 2geom_LIB ${INKSCAPE_LIBS} diff --git a/src/Makefile.am b/src/Makefile.am index 3ca99869f..77ba1f567 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,6 +28,7 @@ noinst_LIBRARIES = \ libcroco/libcroco.a \ libavoid/libavoid.a \ $(internal_GDL) \ + libuemf/libuemf.a \ libcola/libcola.a \ libvpsc/libvpsc.a \ livarot/libvarot.a \ @@ -124,6 +125,7 @@ include live_effects/Makefile_insert include live_effects/parameter/Makefile_insert include libvpsc/Makefile_insert include libcola/Makefile_insert +include libuemf/Makefile_insert include svg/Makefile_insert include widgets/Makefile_insert include debug/Makefile_insert @@ -140,9 +142,7 @@ include 2geom/Makefile_insert # Extra files not mentioned as sources to include in the source tarball EXTRA_DIST += \ - $(top_srcdir)/Doxyfile \ - sp-skeleton.cpp sp-skeleton.h \ - util/makefile.in \ + 2geom/makefile.in \ debug/makefile.in \ dialogs/makefile.in \ display/makefile.in \ @@ -153,32 +153,36 @@ EXTRA_DIST += \ filters/makefile.in \ helper/makefile.in \ io/makefile.in \ - io/crystalegg.xml \ - io/doc2html.xsl \ - libgdl/makefile.in \ + libavoid/makefile.in \ libcroco/makefile.in \ + libgdl/makefile.in \ libnrtype/makefile.in \ - libavoid/makefile.in \ + libuemf/makefile.in \ livarot/makefile.in \ live_effects/makefile.in \ live_effects/parameter/makefile.in \ svg/makefile.in \ trace/makefile.in \ - ui/makefile.in \ ui/cache/makefile.in \ ui/dialog/makefile.in \ + ui/makefile.in \ ui/view/makefile.in \ ui/widget/makefile.in \ util/makefile.in \ + util/makefile.in \ widgets/makefile.in \ xml/makefile.in \ - 2geom/makefile.in \ - extension/internal/emf-win32-inout.cpp \ - extension/internal/emf-win32-inout.h \ - extension/internal/emf-win32-print.cpp \ - extension/internal/emf-win32-print.h \ + \ + $(top_srcdir)/Doxyfile \ + extension/internal/emf-inout.cpp \ + extension/internal/emf-inout.h \ + extension/internal/emf-print.cpp \ + extension/internal/emf-print.h \ helper/sp-marshal.list \ + io/crystalegg.xml \ + io/doc2html.xsl \ show-preview.bmp \ + sp-skeleton.cpp sp-skeleton.h \ winconsole.cpp \ $(CXXTEST_TEMPLATE) diff --git a/src/attribute-rel-util.cpp b/src/attribute-rel-util.cpp index 933339632..15c71daa7 100644 --- a/src/attribute-rel-util.cpp +++ b/src/attribute-rel-util.cpp @@ -141,7 +141,11 @@ void sp_attribute_clean_style(Node *repr, unsigned int flags) { // sp_repr_css_set( repr, css, "style"); // Don't use as it will cause loop. Glib::ustring value; sp_repr_css_write_string(css, value); - repr->setAttribute("style", value.c_str()); + if( value.empty() ) { + repr->setAttribute("style", NULL ); + } else { + repr->setAttribute("style", value.c_str()); + } sp_repr_css_attr_unref( css ); } diff --git a/src/attributes-test.h b/src/attributes-test.h index 295ad618c..eee12d3c1 100644 --- a/src/attributes-test.h +++ b/src/attributes-test.h @@ -276,6 +276,9 @@ struct {char const *attr; bool supported;} const all_attrs[] = { {"text-align", true}, {"text-anchor", true}, {"text-decoration", true}, + {"text-decoration-line", false}, + {"text-decoration-style", false}, + {"text-decoration-color", false}, {"text-indent", true}, {"text-rendering", true}, {"text-transform", true}, diff --git a/src/attributes.cpp b/src/attributes.cpp index 30cf30f30..b93bcfd57 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -414,6 +414,9 @@ static SPStyleProp const props[] = { {SP_PROP_TEXT_INDENT, "text-indent"}, {SP_PROP_TEXT_ALIGN, "text-align"}, {SP_PROP_TEXT_DECORATION, "text-decoration"}, + {SP_PROP_TEXT_DECORATION_LINE, "text-decoration-line"}, + {SP_PROP_TEXT_DECORATION_STYLE,"text-decoration-style"}, + {SP_PROP_TEXT_DECORATION_COLOR,"text-decoration-color"}, {SP_PROP_LINE_HEIGHT, "line-height"}, {SP_PROP_LETTER_SPACING, "letter-spacing"}, {SP_PROP_WORD_SPACING, "word-spacing"}, diff --git a/src/attributes.h b/src/attributes.h index 87cd9ed45..24244c237 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -414,7 +414,10 @@ enum SPAttributeEnum { /* Text */ SP_PROP_TEXT_INDENT, SP_PROP_TEXT_ALIGN, - SP_PROP_TEXT_DECORATION, + SP_PROP_TEXT_DECORATION, /* SVG 1 underline etc.( no color or style) OR SVG2 with _LINE, _STYLE, _COLOR values */ + SP_PROP_TEXT_DECORATION_LINE, /* SVG 2 underline etc. */ + SP_PROP_TEXT_DECORATION_STYLE, /* SVG 2 proposed solid [SVG 1], dotted, etc.)*/ + SP_PROP_TEXT_DECORATION_COLOR, /* SVG 2 proposed same as text [SVG 1], specified*/ SP_PROP_LINE_HEIGHT, SP_PROP_LETTER_SPACING, SP_PROP_WORD_SPACING, diff --git a/src/display/drawing-context.h b/src/display/drawing-context.h index b8c4667c2..d4f0dbfc3 100644 --- a/src/display/drawing-context.h +++ b/src/display/drawing-context.h @@ -67,6 +67,21 @@ public: void rectangle(Geom::IntRect const &r) { cairo_rectangle(_ct, r.left(), r.top(), r.width(), r.height()); } + // Used in drawing-text.cpp to overwrite glyphs, which have the opposite path rotation as a regular rect + void revrectangle(Geom::Rect const &r) { + cairo_move_to ( _ct, r.left(), r.top() ); + cairo_rel_line_to (_ct, 0, r.height() ); + cairo_rel_line_to (_ct, r.width(), 0 ); + cairo_rel_line_to (_ct, 0, -r.height() ); + cairo_close_path ( _ct); + } + void revrectangle(Geom::IntRect const &r) { + cairo_move_to ( _ct, r.left(), r.top() ); + cairo_rel_line_to (_ct, 0, r.height() ); + cairo_rel_line_to (_ct, r.width(), 0 ); + cairo_rel_line_to (_ct, 0, -r.height() ); + cairo_close_path ( _ct); + } void newPath() { cairo_new_path(_ct); } void newSubpath() { cairo_new_sub_path(_ct); } void path(Geom::PathVector const &pv); diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index 2a6505c67..234006983 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -18,9 +18,11 @@ #include "helper/geom.h" #include "libnrtype/font-instance.h" #include "style.h" +#include "2geom/pathvector.h" namespace Inkscape { + DrawingGlyphs::DrawingGlyphs(Drawing &drawing) : DrawingItem(drawing) , _font(NULL) @@ -61,12 +63,34 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext return STATE_ALL; } + _pick_bbox = Geom::IntRect(); _bbox = Geom::IntRect(); - Geom::OptRect b = bounds_exact_transformed(*_font->PathVector(_glyph), ctx.ctm); + Geom::OptRect b; + +/* orignally it did the one line below, + but it did not handle ws characters at all, and it had problems with scaling for overline/underline. + Replaced with the section below, which seems to be much more stable. + + b = bounds_exact_transformed(*_font->PathVector(_glyph), ctx.ctm); +*/ + /* Make a bounding box that is a little taller and lower (currently 10% extra) than the font's drawing box. Extra space is + to hold overline or underline, if present. All characters in a font use the same ascent and descent, + but different widths. This lets leading and trailing spaces have text decorations. If it is not done + the bounding box is limited to the box surrounding the drawn parts of visible glyphs only, and draws outside are ignored. + */ + + float scale = 1.0; + if(_transform){ scale /= _transform->descrim(); } + + Geom::Rect bigbox(Geom::Point(0.0, _asc*scale*1.1),Geom::Point(_width*scale, -_dsc*scale*1.1)); + b = bigbox * ctx.ctm; + if (b && (ggroup->_nrstyle.stroke.type != NRStyle::PAINT_NONE)) { float width, scale; + + // this expands the selection box for cases where the stroke is "thick" scale = ctx.ctm.descrim(); if (_transform) { scale /= _transform->descrim(); // FIXME temporary hack @@ -75,7 +99,8 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext if ( fabs(ggroup->_nrstyle.stroke_width * scale) > 0.01 ) { // FIXME: this is always true b->expandBy(0.5 * width); } - // save bbox without miters for picking + + // save bbox without miters for picking _pick_bbox = b->roundOutwards(); float miterMax = width * ggroup->_nrstyle.miter_limit; @@ -89,7 +114,12 @@ unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext _bbox = b->roundOutwards(); _pick_bbox = *_bbox; } - +/* +std::cout << "DEBUG _bbox" +<< " { " << _bbox->min()[Geom::X] << " , " << _bbox->min()[Geom::Y] +<< " } , { " << _bbox->max()[Geom::X] << " , " << _bbox->max()[Geom::Y] +<< " }" << std::endl; +*/ return STATE_ALL; } @@ -106,7 +136,7 @@ DrawingGlyphs::_pickItem(Geom::Point const &p, double delta, unsigned /*flags*/) return NULL; } - // With text we take a simple approach: pick if the point is in a characher bbox + // With text we take a simple approach: pick if the point is in a character bbox Geom::Rect expanded(_pick_bbox); expanded.expandBy(delta); if (expanded.contains(p)) return this; @@ -129,17 +159,28 @@ DrawingText::clear() _children.clear_and_dispose(DeleteDisposer()); } -void -DrawingText::addComponent(font_instance *font, int glyph, Geom::Affine const &trans) +bool +DrawingText::addComponent(font_instance *font, int glyph, Geom::Affine const &trans, + float width, float ascent, float descent, float phase_length) { +/* original, did not save a glyph for white space characters, causes problems for text-decoration if (!font || !font->PathVector(glyph)) { - return; + return(false); } +*/ + if (!font)return(false); _markForRendering(); DrawingGlyphs *ng = new DrawingGlyphs(_drawing); ng->setGlyph(font, glyph, trans); + if(font->PathVector(glyph)){ ng->_drawable = true; } + else { ng->_drawable = false; } + ng->_width = width; // only used when _drawable = false + ng->_asc = ascent; // of font, not of this one character + ng->_dsc = descent; // of font, not of this one character + ng->_pl = phase_length; // used for phase of dots, dashes, and wavy appendChild(ng); + return(true); } void @@ -156,9 +197,175 @@ DrawingText::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, un return DrawingGroup::_updateItem(area, ctx, flags, reset); } +void DrawingText::decorateStyle(DrawingContext &ct, double vextent, double xphase, Geom::Point p1, Geom::Point p2) +{ + double wave[16]={ + 0.000000, 0.382499, 0.706825, 0.923651, 1.000000, 0.923651, 0.706825, 0.382499, + 0.000000, -0.382499, -0.706825, -0.923651, -1.000000, -0.923651, -0.706825, -0.382499, + }; + int dashes[16]={ + 8, 7, 6, 5, + 4, 3, 2, 1, + -8, -7, -6, -5 + -4, -3, -2, -1 + }; + int dots[16]={ + 4, 3, 2, 1, + -4, -3, -2, -1, + 4, 3, 2, 1, + -4, -3, -2, -1 + }; + Geom::Point p3,p4,ps,pf; + double step = vextent/32.0; + unsigned i = 15 & (unsigned) round(xphase/step); // xphase is >= 0.0 + + /* For most spans draw the last little bit right to p2 or even a little beyond. + This allows decoration continuity within the line, and does not step outside the clip box off the end + For the first/last section on the line though, stay well clear of the edge, or when the + text is dragged it may "spray" pixels. + if(_nrstyle.tspan_line_end){ pf = p2 - Geom::Point(2*step, 0.0); } + else { pf = p2; } + if(_nrstyle.tspan_line_start){ ps = p1 + Geom::Point(2*step, 0.0); + i = 15 & (i + 2); + } + else { ps = p1; } + */ + /* snap to nearest step in X */ +ps = Geom::Point(step * round(p1[Geom::X]/step),p1[Geom::Y]); +pf = Geom::Point(step * round(p2[Geom::X]/step),p2[Geom::Y]); + + if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_ISDOUBLE){ + ps -= Geom::Point(0, vextent/12.0); + pf -= Geom::Point(0, vextent/12.0); + ct.moveTo(ps); + ct.lineTo(pf); + ps += Geom::Point(0, vextent/6.0); + pf += Geom::Point(0, vextent/6.0); + ct.moveTo(ps); + ct.lineTo(pf); + } + /* The next three have a problem in that they are phase dependent. The bits of a line are not + necessarily passing through this routine in order, so we have to use the xphase information + to figure where in each of their cycles to start. Only accurate to 1 part in 16. + Huge possitive offset should keep the phase calculation from ever being negative. + */ + else if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_DOTTED){ + while(1){ + if(dots[i]>0){ + if(ps[Geom::X]> pf[Geom::X])break; + ct.moveTo(ps); + ps += Geom::Point(step * (double)dots[i], 0.0); + if(ps[Geom::X]>= pf[Geom::X]){ + ct.lineTo(pf); + break; + } + else { + ct.lineTo(ps); + } + ps += Geom::Point(step * 4.0, 0.0); + } + else { + ps += Geom::Point(step * -(double)dots[i], 0.0); + } + i = 0; // once in phase, it stays in phase + } + } + else if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_DASHED){ + while(1){ + if(dashes[i]>0){ + if(ps[Geom::X]> pf[Geom::X])break; + ct.moveTo(ps); + ps += Geom::Point(step * (double)dashes[i], 0.0); + if(ps[Geom::X]>= pf[Geom::X]){ + ct.lineTo(pf); + break; + } + else { + ct.lineTo(ps); + } + ps += Geom::Point(step * 8.0, 0.0); + } + else { + ps += Geom::Point(step * -(double)dashes[i], 0.0); + } + i = 0; // once in phase, it stays in phase + } + } + else if(_nrstyle.text_decoration_style & TEXT_DECORATION_STYLE_WAVY){ + double amp = vextent/10.0; + double x = ps[Geom::X]; + double y = ps[Geom::Y]; + ct.moveTo(Geom::Point(x, y + amp * wave[i])); + while(1){ + i = ((i + 1) & 15); + x += step; + ct.lineTo(Geom::Point(x, y + amp * wave[i])); + if(x >= pf[Geom::X])break; + } + } + else { // TEXT_DECORATION_STYLE_SOLID, also default in case it was not set for some reason + ct.moveTo(ps); + ct.lineTo(pf); +// ct.revrectangle(Geom::Rect(ps,pf)); + } +} + +/* returns scaled line thickness */ +double DrawingText::decorateItem(DrawingContext &ct, Geom::Affine aff, double phase_length) +{ + double tsp_width_adj, tsp_asc_adj, tsp_size_adj; + double final_underline_thickness, final_line_through_thickness; + double thickness; + + tsp_width_adj = _nrstyle.tspan_width / _nrstyle.font_size; + tsp_asc_adj = _nrstyle.ascender / _nrstyle.font_size; + tsp_size_adj = (_nrstyle.ascender + _nrstyle.descender) / _nrstyle.font_size; +#define VALTRUNC(A,B,C) (A < B ? B : ( A > C ? C : A )) + final_underline_thickness = VALTRUNC(_nrstyle.underline_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0); + final_line_through_thickness = VALTRUNC(_nrstyle.line_through_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0); + Inkscape::DrawingContext::Save save(ct); + + double scale = aff.descrim(); + double xphase = phase_length/ _nrstyle.font_size; // used to figure out phase of patterns + + ct.transform(aff); // must be leftmost affine in span + Geom::Point p1; + Geom::Point p2; + // All lines must be the same thickness, in combinations, line_through trumps underline + thickness = final_underline_thickness; + if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_UNDERLINE){ + p1 = Geom::Point(0.0, -_nrstyle.underline_position); + p2 = Geom::Point(tsp_width_adj,-_nrstyle.underline_position); + decorateStyle(ct, tsp_size_adj, xphase, p1, p2); + } + if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_OVERLINE){ + p1 = Geom::Point(0.0, tsp_asc_adj -_nrstyle.underline_position + 1 * final_underline_thickness); + p2 = Geom::Point(tsp_width_adj,tsp_asc_adj -_nrstyle.underline_position + 1 * final_underline_thickness); + decorateStyle(ct, tsp_size_adj, xphase, p1, p2); + } + if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_LINETHROUGH){ + thickness = final_line_through_thickness; + p1 = Geom::Point(0.0, _nrstyle.line_through_position); + p2 = Geom::Point(tsp_width_adj,_nrstyle.line_through_position); + decorateStyle(ct, tsp_size_adj, xphase, p1, p2); + } + // Obviously this does not blink, but it does indicate which text has been set with that attribute + if(_nrstyle.text_decoration_line & TEXT_DECORATION_LINE_BLINK){ + thickness = final_line_through_thickness; + p1 = Geom::Point(0.0, _nrstyle.line_through_position - 2*final_line_through_thickness); + p2 = Geom::Point(tsp_width_adj,_nrstyle.line_through_position - 2*final_line_through_thickness); + decorateStyle(ct, tsp_size_adj, xphase, p1, p2); + p1 = Geom::Point(0.0, _nrstyle.line_through_position + 2*final_line_through_thickness); + p2 = Geom::Point(tsp_width_adj,_nrstyle.line_through_position + 2*final_line_through_thickness); + decorateStyle(ct, tsp_size_adj, xphase, p1, p2); + } + thickness *= scale; + return(thickness); +} + unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*area*/, unsigned /*flags*/, DrawingItem * /*stop_at*/) { - if (_drawing.outline()) { + if (_drawing.outline()) { guint32 rgba = _drawing.outlinecolor; Inkscape::DrawingContext::Save save(ct); ct.setSource(rgba); @@ -169,34 +376,72 @@ unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*are if (!g) throw InvalidItemException(); Inkscape::DrawingContext::Save save(ct); - // skip glpyhs with singular transforms + // skip glyphs with singular transforms if (g->_ctm.isSingular()) continue; ct.transform(g->_ctm); - ct.path(*g->_font->PathVector(g->_glyph)); - ct.fill(); + if(g->_drawable){ + ct.path(*g->_font->PathVector(g->_glyph)); + ct.fill(); + } } return RENDER_OK; } // NOTE: this is very similar to drawing-shape.cpp; the only difference is in path feeding - bool has_stroke, has_fill; - - has_fill = _nrstyle.prepareFill(ct, _item_bbox); + double leftmost = DBL_MAX; + double phase_length = 0.0; + bool firsty = true; + bool decorate = true; + double starty = 0.0; + bool has_stroke, has_fill; + Geom::Affine aff; + using Geom::X; + using Geom::Y; + + has_fill = _nrstyle.prepareFill( ct, _item_bbox); has_stroke = _nrstyle.prepareStroke(ct, _item_bbox); if (has_fill || has_stroke) { + Geom::Affine rotinv; + bool invset=false; for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) { DrawingGlyphs *g = dynamic_cast<DrawingGlyphs *>(&*i); if (!g) throw InvalidItemException(); + if(!invset){ + rotinv = g->_ctm.withoutTranslation().inverse(); + invset = true; + } Inkscape::DrawingContext::Save save(ct); if (g->_ctm.isSingular()) continue; ct.transform(g->_ctm); - ct.path(*g->_font->PathVector(g->_glyph)); + if(g->_drawable){ + ct.path(*g->_font->PathVector(g->_glyph)); + } + // get the leftmost affine transform (leftmost defined with respect to the x axis of the first transform). + // That way the decoration will work no matter what mix of L->R, R->L text is in the span. + if(_nrstyle.text_decoration_line != TEXT_DECORATION_LINE_CLEAR){ + Geom::Point pt =g->_ctm.translation() * rotinv; + if(pt[X] < leftmost){ + leftmost = pt[X]; + aff = g->_ctm; + phase_length = g->_pl; + } + /* If the text has been mapped onto a path, which causes y to vary, drop the text decorations. + To handle that properly would need a conformal map + */ + if(firsty){ + firsty = false; + starty = pt[Y]; + } + else { + if(fabs(pt[Y] - starty) > 1.0e-6)decorate=false; + } + } } Inkscape::DrawingContext::Save save(ct); - ct.transform(_ctm); +// ct.transform(_ctm); // Seems to work fine without this line, which was in the original. if (has_fill) { _nrstyle.applyFill(ct); ct.fillPreserve(); @@ -206,6 +451,29 @@ unsigned DrawingText::_renderItem(DrawingContext &ct, Geom::IntRect const &/*are ct.strokePreserve(); } ct.newPath(); // clear path + if(_nrstyle.text_decoration_line != TEXT_DECORATION_LINE_CLEAR && decorate){ + guint32 ergba; + if(_nrstyle.text_decoration_useColor){ // color different from the glyph + ergba = SP_RGBA32_F_COMPOSE( + _nrstyle.text_decoration_color.color.v.c[0], + _nrstyle.text_decoration_color.color.v.c[1], + _nrstyle.text_decoration_color.color.v.c[2], + 1.0); + } + else { // whatever the current fill color is + ergba = SP_RGBA32_F_COMPOSE( + _nrstyle.fill.color.v.c[0], + _nrstyle.fill.color.v.c[1], + _nrstyle.fill.color.v.c[2], + 1.0); + } + ct.setSource(ergba); + ct.setTolerance(0.5); + double thickness = decorateItem(ct, aff, phase_length); + ct.setLineWidth(thickness); + ct.strokePreserve(); + ct.newPath(); // clear path + } } return RENDER_OK; } @@ -231,7 +499,9 @@ void DrawingText::_clipItem(DrawingContext &ct, Geom::IntRect const &/*area*/) Inkscape::DrawingContext::Save save(ct); ct.transform(g->_ctm); - ct.path(*g->_font->PathVector(g->_glyph)); + if(g->_drawable){ + ct.path(*g->_font->PathVector(g->_glyph)); + } } ct.fill(); } diff --git a/src/display/drawing-text.h b/src/display/drawing-text.h index 79b1b097c..99b46bc8a 100644 --- a/src/display/drawing-text.h +++ b/src/display/drawing-text.h @@ -35,8 +35,13 @@ protected: virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); font_instance *_font; - int _glyph; - Geom::IntRect _pick_bbox; + int _glyph; + bool _drawable; + float _width; // These three are used to set up bounding box + float _asc; // + float _dsc; // + float _pl; // phase length + Geom::IntRect _pick_bbox; friend class DrawingText; }; @@ -49,9 +54,11 @@ public: ~DrawingText(); void clear(); - void addComponent(font_instance *font, int glyph, Geom::Affine const &trans); + bool addComponent(font_instance *font, int glyph, Geom::Affine const &trans, + float width, float ascent, float descent, float phase_length); void setStyle(SPStyle *style); + protected: virtual unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset); @@ -61,6 +68,8 @@ protected: virtual DrawingItem *_pickItem(Geom::Point const &p, double delta, unsigned flags); virtual bool _canClip(); + double decorateItem(DrawingContext &ct, Geom::Affine aff, double phase_length); + void decorateStyle(DrawingContext &ct, double vextent, double xphase, Geom::Point p1, Geom::Point p2); NRStyle _nrstyle; friend class DrawingGlyphs; diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp index 26d70ad15..a18bc0523 100644 --- a/src/display/nr-style.cpp +++ b/src/display/nr-style.cpp @@ -54,6 +54,20 @@ NRStyle::NRStyle() , line_join(CAIRO_LINE_JOIN_MITER) , fill_pattern(NULL) , stroke_pattern(NULL) + , text_decoration_line(TEXT_DECORATION_LINE_CLEAR) + , text_decoration_style(TEXT_DECORATION_STYLE_CLEAR) + , phase_length(0.0) + , tspan_line_start(false) + , tspan_line_end(false) + , tspan_width(0) + , ascender(0) + , descender(0) + , line_gap(0) + , underline_thickness(0) + , underline_position(0) + , line_through_thickness(0) + , line_through_position(0) + , font_size(0) {} NRStyle::~NRStyle() @@ -144,6 +158,50 @@ void NRStyle::set(SPStyle *style) dash = NULL; } + text_decoration_line = TEXT_DECORATION_LINE_CLEAR; + if(style->text_decoration_line.inherit ){ text_decoration_line |= TEXT_DECORATION_LINE_INHERIT; } + if(style->text_decoration_line.underline ){ text_decoration_line |= TEXT_DECORATION_LINE_UNDERLINE + TEXT_DECORATION_LINE_SET; } + if(style->text_decoration_line.overline ){ text_decoration_line |= TEXT_DECORATION_LINE_OVERLINE + TEXT_DECORATION_LINE_SET; } + if(style->text_decoration_line.line_through){ text_decoration_line |= TEXT_DECORATION_LINE_LINETHROUGH + TEXT_DECORATION_LINE_SET; } + if(style->text_decoration_line.blink ){ text_decoration_line |= TEXT_DECORATION_LINE_BLINK + TEXT_DECORATION_LINE_SET; } + + text_decoration_style = TEXT_DECORATION_STYLE_CLEAR; + if(style->text_decoration_style.inherit ){ text_decoration_style |= TEXT_DECORATION_STYLE_INHERIT; } + if(style->text_decoration_style.solid ){ text_decoration_style |= TEXT_DECORATION_STYLE_SOLID + TEXT_DECORATION_STYLE_SET; } + if(style->text_decoration_style.isdouble ){ text_decoration_style |= TEXT_DECORATION_STYLE_ISDOUBLE + TEXT_DECORATION_STYLE_SET; } + if(style->text_decoration_style.dotted ){ text_decoration_style |= TEXT_DECORATION_STYLE_DOTTED + TEXT_DECORATION_STYLE_SET; } + if(style->text_decoration_style.dashed ){ text_decoration_style |= TEXT_DECORATION_STYLE_DASHED + TEXT_DECORATION_STYLE_SET; } + if(style->text_decoration_style.wavy ){ text_decoration_style |= TEXT_DECORATION_STYLE_WAVY + TEXT_DECORATION_STYLE_SET; } + + if( style->text_decoration_color.set || + style->text_decoration_color.inherit || + style->text_decoration_color.currentcolor || + style->text_decoration_color.colorSet){ + text_decoration_color.set(style->text_decoration_color.value.color); + text_decoration_useColor = true; + } + else { + text_decoration_color.clear(); + text_decoration_useColor = false; + } + + if(text_decoration_line != TEXT_DECORATION_LINE_CLEAR){ + phase_length = style->text_decoration_data.phase_length; + tspan_line_start = style->text_decoration_data.tspan_line_start; + tspan_line_end = style->text_decoration_data.tspan_line_end; + tspan_width = style->text_decoration_data.tspan_width; + ascender = style->text_decoration_data.ascender; + descender = style->text_decoration_data.descender; + line_gap = style->text_decoration_data.line_gap; + underline_thickness = style->text_decoration_data.underline_thickness; + underline_position = style->text_decoration_data.underline_position; + line_through_thickness = style->text_decoration_data.line_through_thickness; + line_through_position = style->text_decoration_data.line_through_position; + font_size = style->font_size.computed; + } + + text_direction = style->direction.computed; + update(); } diff --git a/src/display/nr-style.h b/src/display/nr-style.h index cd0bd208f..df4c4f921 100644 --- a/src/display/nr-style.h +++ b/src/display/nr-style.h @@ -67,6 +67,43 @@ struct NRStyle { cairo_pattern_t *fill_pattern; cairo_pattern_t *stroke_pattern; + +#define TEXT_DECORATION_LINE_CLEAR 0x00 +#define TEXT_DECORATION_LINE_SET 0x01 +#define TEXT_DECORATION_LINE_INHERIT 0x02 +#define TEXT_DECORATION_LINE_UNDERLINE 0x04 +#define TEXT_DECORATION_LINE_OVERLINE 0x08 +#define TEXT_DECORATION_LINE_LINETHROUGH 0x10 +#define TEXT_DECORATION_LINE_BLINK 0x20 + +#define TEXT_DECORATION_STYLE_CLEAR 0x00 +#define TEXT_DECORATION_STYLE_SET 0x01 +#define TEXT_DECORATION_STYLE_INHERIT 0x02 +#define TEXT_DECORATION_STYLE_SOLID 0x04 +#define TEXT_DECORATION_STYLE_ISDOUBLE 0x08 +#define TEXT_DECORATION_STYLE_DOTTED 0x10 +#define TEXT_DECORATION_STYLE_DASHED 0x20 +#define TEXT_DECORATION_STYLE_WAVY 0x40 + + int text_decoration_line; + int text_decoration_style; + Paint text_decoration_color; + bool text_decoration_useColor; // if false, use whatever the glyph color was + // These are the same as in style.h + float phase_length; + bool tspan_line_start; + bool tspan_line_end; + float tspan_width; + float ascender; + float descender; + float line_gap; + float underline_thickness; + float underline_position; + float line_through_thickness; + float line_through_position; + float font_size; + + int text_direction; }; #endif diff --git a/src/extension/CMakeLists.txt b/src/extension/CMakeLists.txt index 4507d9ce2..6310ceaea 100644 --- a/src/extension/CMakeLists.txt +++ b/src/extension/CMakeLists.txt @@ -37,14 +37,15 @@ set(extension_SRC internal/cairo-renderer.cpp internal/cairo-renderer-pdf-out.cpp internal/cdr-input.cpp - internal/emf-win32-inout.cpp - internal/emf-win32-print.cpp + internal/emf-inout.cpp + internal/emf-print.cpp internal/gdkpixbuf-input.cpp internal/gimpgrad.cpp internal/grid.cpp internal/image-resolution.cpp internal/latex-pstricks.cpp internal/latex-pstricks-out.cpp + internal/metafile-print.cpp internal/odf.cpp internal/latex-text-renderer.cpp internal/pdf-input-cairo.cpp @@ -52,7 +53,10 @@ set(extension_SRC internal/javafx-out.cpp internal/svg.cpp internal/svgz.cpp + internal/text_reassemble.c internal/vsd-input.cpp + internal/wmf-inout.cpp + internal/wmf-print.cpp internal/filter/filter-all.cpp internal/filter/filter-file.cpp @@ -101,8 +105,8 @@ set(extension_SRC internal/cairo-renderer.h internal/cdr-input.h internal/clear-n_.h - internal/emf-win32-inout.h - internal/emf-win32-print.h + internal/emf-inout.h + internal/emf-print.h internal/filter/bevels.h internal/filter/blurs.h internal/filter/bumps.h @@ -125,6 +129,7 @@ set(extension_SRC internal/latex-pstricks-out.h internal/latex-pstricks.h internal/latex-text-renderer.h + internal/metafile-print.h internal/odf.h internal/pdf-input-cairo.h internal/pdfinput/pdf-input.h @@ -133,7 +138,10 @@ set(extension_SRC internal/pov-out.h internal/svg.h internal/svgz.h + internal/text_reassemble.h internal/vsd-input.h + internal/wmf-inout.h + internal/wmf-print.h ) if(WIN32) diff --git a/src/extension/init.cpp b/src/extension/init.cpp index d403d270f..1a163d4c2 100644 --- a/src/extension/init.cpp +++ b/src/extension/init.cpp @@ -34,10 +34,10 @@ #include "system.h" #include "db.h" #include "internal/svgz.h" -#ifdef WIN32 -# include "internal/emf-win32-inout.h" -# include "internal/emf-win32-print.h" -#endif +# include "internal/emf-inout.h" +# include "internal/emf-print.h" +# include "internal/wmf-inout.h" +# include "internal/wmf-print.h" #ifdef HAVE_CAIRO_PDF # include "internal/cairo-renderer-pdf-out.h" # include "internal/cairo-png-out.h" @@ -181,10 +181,10 @@ init() Internal::PdfInputCairo::init(); } #endif -#ifdef WIN32 - Internal::PrintEmfWin32::init(); - Internal::EmfWin32::init(); -#endif + Internal::PrintEmf::init(); + Internal::Emf::init(); + Internal::PrintWmf::init(); + Internal::Wmf::init(); Internal::PovOutput::init(); Internal::JavaFXOutput::init(); Internal::OdfOutput::init(); @@ -324,7 +324,7 @@ build_module_from_dir(gchar const *dirname) continue; } - gchar *pathname = g_build_filename(dirname, filename, NULL); + gchar *pathname = g_build_filename(dirname, filename, (char *) NULL); build_from_file(pathname); g_free(pathname); } diff --git a/src/extension/internal/Makefile_insert b/src/extension/internal/Makefile_insert index 9e55598df..726bca28f 100644 --- a/src/extension/internal/Makefile_insert +++ b/src/extension/internal/Makefile_insert @@ -156,10 +156,18 @@ ink_common_sources += \ extension/internal/filter/filter-file.cpp \ extension/internal/filter/filter.cpp \ extension/internal/filter/filter.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-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/cairo-ps-out.cpp b/src/extension/internal/cairo-ps-out.cpp index bfbdd8149..e06c9f30d 100644 --- a/src/extension/internal/cairo-ps-out.cpp +++ b/src/extension/internal/cairo-ps-out.cpp @@ -97,7 +97,7 @@ 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); diff --git a/src/extension/internal/cairo-render-context.cpp b/src/extension/internal/cairo-render-context.cpp index 0ea1fd591..3c222bd9e 100644 --- a/src/extension/internal/cairo-render-context.cpp +++ b/src/extension/internal/cairo-render-context.cpp @@ -111,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), @@ -124,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); } @@ -426,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; @@ -1331,11 +1343,36 @@ CairoRenderContext::_setStrokeStyle(SPStyle const *style, Geom::OptRect const &p 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); @@ -1408,6 +1445,8 @@ bool CairoRenderContext::renderImage(GdkPixbuf *pb, return true; } + _prepareRenderGraphic(); + int w = gdk_pixbuf_get_width (pb); int h = gdk_pixbuf_get_height (pb); @@ -1489,7 +1528,12 @@ 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; /// \fixme why is this variable never used? gpointer fonthash = (gpointer)font; diff --git a/src/extension/internal/cairo-render-context.h b/src/extension/internal/cairo-render-context.h index 8829940c6..f8426aebe 100644 --- a/src/extension/internal/cairo-render-context.h +++ b/src/extension/internal/cairo-render-context.h @@ -94,6 +94,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 +111,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; @@ -150,6 +155,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 +168,7 @@ protected: unsigned int _ps_level; bool _eps; bool _is_texttopath; + bool _is_omittext; bool _is_filtertobitmap; int _bitmapresolution; @@ -182,6 +194,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 +210,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 f4eed1c57..0c314a576 100644 --- a/src/extension/internal/cairo-renderer-pdf-out.cpp +++ b/src/extension/internal/cairo-renderer-pdf-out.cpp @@ -94,7 +94,7 @@ 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); diff --git a/src/extension/internal/cairo-renderer.cpp b/src/extension/internal/cairo-renderer.cpp index 84f090745..3ffd6b2ed 100644 --- a/src/extension/internal/cairo-renderer.cpp +++ b/src/extension/internal/cairo-renderer.cpp @@ -101,7 +101,6 @@ namespace Extension { namespace Internal { CairoRenderer::CairoRenderer(void) - : _omitText(false) {} CairoRenderer::~CairoRenderer(void) @@ -578,11 +577,6 @@ 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); diff --git a/src/extension/internal/cairo-renderer.h b/src/extension/internal/cairo-renderer.h index c1482d82e..1ab8f1872 100644 --- a/src/extension/internal/cairo-renderer.h +++ b/src/extension/internal/cairo-renderer.h @@ -57,10 +57,6 @@ public: /** 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/emf-inout.cpp b/src/extension/internal/emf-inout.cpp new file mode 100644 index 000000000..b185d3348 --- /dev/null +++ b/src/extension/internal/emf-inout.cpp @@ -0,0 +1,3545 @@ +/** @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 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 "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; + +/** 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 * Emf::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 +Emf::my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + PMEMPNG p=(PMEMPNG)png_get_io_ptr(png_ptr); + + size_t nsize = p->size + length; + + /* allocate or grow buffer */ + if(p->buffer){ p->buffer = (char *) realloc(p->buffer, nsize); } + else{ p->buffer = (char *) malloc(nsize); } + + if(!p->buffer){ png_error(png_ptr, "Write Error"); } + + /* copy new bytes to end of buffer */ + memcpy(p->buffer + p->size, data, length); + p->size += length; +} + +void Emf::toPNG(PMEMPNG accum, int width, int height, const char *px){ + bitmap_t bmStore; + bitmap_t *bitmap = &bmStore; + accum->buffer=NULL; // PNG constructed in memory will end up here, caller must free(). + accum->size=0; + bitmap->pixels=(pixel_t *)px; + bitmap->width = width; + bitmap->height = height; + + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + size_t x, y; + png_byte ** row_pointers = NULL; + /* The following number is set by trial and error only. I cannot + see where it it is documented in the libpng manual. + */ + 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 Emf::sethexcolor(U_COLORREF color){ + + uint32_t out; + out = (U_RGBAGetR(color) << 16) + + (U_RGBAGetG(color) << 8 ) + + (U_RGBAGetB(color) ); + return(out); +} + + +Emf::Emf (void) // The null constructor +{ + return; +} + + +Emf::~Emf (void) //The destructor +{ + return; +} + + +bool +Emf::check (Inkscape::Extension::Extension * /*module*/) +{ + if (NULL == Inkscape::Extension::db.get(PRINT_EMF)) + return FALSE; + return TRUE; +} + + +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); + ret = mod->finish(); + /* Release arena */ + mod->base->invoke_hide(mod->dkey); + mod->base = NULL; + mod->root = NULL; // deleted by invoke_hide +/* end */ + + mod->set_param_string("destination", oldoutput); + g_free(oldoutput); + + return; +} + + +void +Emf::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filename) +{ + Inkscape::Extension::Extension * ext; + + ext = Inkscape::Extension::db.get(PRINT_EMF); + if (ext == NULL) + return; + + bool new_val = mod->get_param_bool("textToPath"); + bool new_FixPPTCharPos = mod->get_param_bool("FixPPTCharPos"); // character position bug + // reserve FixPPT2 for opacity bug. Currently EMF does not export opacity values + bool new_FixPPTDashLine = mod->get_param_bool("FixPPTDashLine"); // dashed line bug + bool new_FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys"); // gradient bug + bool new_FixPPTPatternAsHatch = mod->get_param_bool("FixPPTPatternAsHatch"); // force all patterns as standard EMF hatch + 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("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 worldTranform return the scale in the matrix part. Assumes that the + matrix is not used to skew, invert, or make another distorting transformation. */ +double Emf::current_scale(PEMF_CALLBACK_DATA d){ + double scale = + d->dc[d->level].worldTransform.eM11 * d->dc[d->level].worldTransform.eM22 - + d->dc[d->level].worldTransform.eM12 * d->dc[d->level].worldTransform.eM21; + if(scale <= 0.0)scale=1.0; /* something is dreadfully wrong with the matrix, but do not crash over it */ + scale=sqrt(scale); + return(scale); +} + +/* given the transformation matrix from worldTranform and the current x,y position in inkscape coordinates, + generate an SVG transform that gives the same amount of rotation, no scaling, and maps x,y back onto x,y. This is used for + rotating objects when the location of at least one point in that object is known. Returns: + "matrix(a,b,c,d,e,f)" (WITH the double quotes) +*/ +std::string Emf::current_matrix(PEMF_CALLBACK_DATA d, double x, double y, int useoffset){ + std::stringstream cxform; + double scale = current_scale(d); + cxform << "\"matrix("; + cxform << d->dc[d->level].worldTransform.eM11/scale; cxform << ","; + cxform << d->dc[d->level].worldTransform.eM12/scale; cxform << ","; + cxform << d->dc[d->level].worldTransform.eM21/scale; cxform << ","; + cxform << d->dc[d->level].worldTransform.eM22/scale; cxform << ","; + if(useoffset){ + /* for the "new" coordinates drop the worldtransform translations, not used here */ + double newx = x * d->dc[d->level].worldTransform.eM11/scale + y * d->dc[d->level].worldTransform.eM21/scale; + double newy = x * d->dc[d->level].worldTransform.eM12/scale + y * d->dc[d->level].worldTransform.eM22/scale; + cxform << x - newx; cxform << ","; + cxform << y - newy; + } + else { + cxform << "0,0"; + } + cxform << ")\""; + return(cxform.str()); +} + +/* given the transformation matrix from worldTranform return the rotation angle in radians. + counter clocwise from the x axis. */ +double Emf::current_rotation(PEMF_CALLBACK_DATA d){ + return -std::atan2(d->dc[d->level].worldTransform.eM12, d->dc[d->level].worldTransform.eM11); +} + +/* 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; + + MEMPNG mempng; // PNG in memory comes back in this + mempng.buffer = NULL; + + char *rgba_px = NULL; // RGBA pixels + const char *px = NULL; // DIB pixels + const U_RGBQUAD *ct = NULL; // DIB color table + U_RGBQUAD ct2[2]; + uint32_t width, height, colortype, numCt, invert; + if( !cbBits || + !cbBmi || + (iUsage != U_DIB_RGB_COLORS) || + !(dibparams = get_DIB_params( // this returns pointers and values, but allocates no memory + pEmr, + offBits, + offBmi, + &px, + (const U_RGBQUAD **) &ct, + &numCt, + &width, + &height, + &colortype, + &invert + )) + ){ + + // U_EMRCREATEMONOBRUSH uses text/bk colors instead of what is in the color map. + if(((PU_EMR)pEmr)->iType == U_EMR_CREATEMONOBRUSH){ + if(numCt==2){ + ct2[0] = U_RGB2BGR(d->dc[d->level].textColor); + ct2[1] = U_RGB2BGR(d->dc[d->level].bkColor); + ct = &ct2[0]; + } + else { // createmonobrush renders on other platforms this way + return(0xFFFFFFFF); + } + } + + if(!DIB_to_RGBA( + px, // DIB pixel array + ct, // DIB color table + numCt, // DIB color table number of entries + &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. + width, // Width of pixel array in record + height, // Height of pixel array in record + colortype, // DIB BitCount Enumeration + numCt, // Color table used if not 0 + invert // If DIB rows are in opposite order from RGBA rows + ) && + rgba_px + ){ + toPNG( // Get the image from the RGBA px into mempng + &mempng, + width, height, // of the SRC bitmap + rgba_px + ); + free(rgba_px); + } + } + gchar *base64String; + if(dibparams == U_BI_JPEG || dibparams==U_BI_PNG){ + base64String = g_base64_encode((guchar*) px, numCt ); + idx = in_images(d, (char *) base64String); + } + else if(mempng.buffer){ + base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + idx = in_images(d, (char *) base64String); + } + else { + // insert a random 3x4 blotch otherwise + width = 3; + height = 4; + base64String = g_strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="); + idx = in_images(d, (char *) base64String); + } + if(!idx){ // add it if not already present - we looked at the actual data for comparison + if(d->images.count == d->images.size){ enlarge_images(d); } + idx = d->images.count; + d->images.strings[d->images.count++]=strdup(base64String); + + sprintf(imagename,"EMFimage%d",idx++); + sprintf(xywh," x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" ",width,height); // reuse this buffer + + *(d->defs) += "\n"; + *(d->defs) += " <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) += " />\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); + + /* 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); +} + + +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: // treat all of these as black + case U_SRCINVERT: + case U_DSTINVERT: + case U_BLACKNESS: + case U_SRCERASE: + case U_NOTSRCCOPY: + fill_rgb[0]=fill_rgb[1]=fill_rgb[2]=0.0; + break; + case U_SRCCOPY: // treat all of these as white + case U_NOTSRCERASE: + case U_PATCOPY: + case U_WHITENESS: + fill_rgb[0]=fill_rgb[1]=fill_rgb[2]=1.0; + break; + case U_SRCPAINT: // use the existing color + case U_SRCAND: + case U_MERGECOPY: + case U_MERGEPAINT: + case U_PATPAINT: + default: + break; + } + d->dwRop3 = 0; // might as well reset it here, it must be set for each BITBLT + } + + // Implement some of these, the ones where the original screen color does not matter. + // The options that merge screen and pen colors cannot be done correctly because we + // have no way of knowing what color is already on the screen. For those just pass the + // pen color through. + switch(d->dwRop2){ + case U_R2_BLACK: + fill_rgb[0] = fill_rgb[1] = fill_rgb[2] = 0.0; + stroke_rgb[0]= stroke_rgb[1]= stroke_rgb[2] = 0.0; + break; + case U_R2_NOTMERGEPEN: + case U_R2_MASKNOTPEN: + break; + case U_R2_NOTCOPYPEN: + fill_rgb[0] = 1.0 - fill_rgb[0]; + fill_rgb[1] = 1.0 - fill_rgb[1]; + fill_rgb[2] = 1.0 - fill_rgb[2]; + stroke_rgb[0] = 1.0 - stroke_rgb[0]; + stroke_rgb[1] = 1.0 - stroke_rgb[1]; + stroke_rgb[2] = 1.0 - stroke_rgb[2]; + break; + case U_R2_MASKPENNOT: + case U_R2_NOT: + case U_R2_XORPEN: + case U_R2_NOTMASKPEN: + case U_R2_NOTXORPEN: + case U_R2_NOP: + case U_R2_MERGENOTPEN: + case U_R2_COPYPEN: + case U_R2_MASKPEN: + case U_R2_MERGEPENNOT: + case U_R2_MERGEPEN: + break; + case U_R2_WHITE: + fill_rgb[0] = fill_rgb[1] = fill_rgb[2] = 1.0; + stroke_rgb[0]= stroke_rgb[1]= stroke_rgb[2] = 1.0; + break; + default: + break; + } + + +// tmp_id << "\n\tid=\"" << (d->id++) << "\""; +// *(d->outsvg) += tmp_id.str().c_str(); + *(d->outsvg) += "\n\tstyle=\""; + if (iType == U_EMR_STROKEPATH || !d->dc[d->level].fill_set) { + tmp_style << "fill:none;"; + } else { + switch(d->dc[d->level].fill_mode){ + // both of these use the url(#) method + case DRAW_PATTERN: + snprintf(tmp, 1023, "fill:url(#%s); ",d->hatches.strings[d->dc[d->level].fill_idx]); + tmp_style << tmp; + break; + case DRAW_IMAGE: + snprintf(tmp, 1023, "fill:url(#EMFimage%d_ref); ",d->dc[d->level].fill_idx); + tmp_style << tmp; + break; + case DRAW_PAINT: + default: // <-- this should never happen, but just in case... + snprintf( + tmp, 1023, + "fill:#%02x%02x%02x;", + SP_COLOR_F_TO_U(fill_rgb[0]), + SP_COLOR_F_TO_U(fill_rgb[1]), + SP_COLOR_F_TO_U(fill_rgb[2]) + ); + tmp_style << tmp; + break; + } + snprintf( + tmp, 1023, + "fill-rule:%s;", + (d->dc[d->level].style.fill_rule.value == 0 ? "evenodd" : "nonzero") + ); + tmp_style << tmp; + tmp_style << "fill-opacity:1;"; + + // if 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_PAINT: + default: // <-- this should never happen, but just in case... + snprintf( + tmp, 1023, + "stroke:#%02x%02x%02x;", + SP_COLOR_F_TO_U(stroke_rgb[0]), + SP_COLOR_F_TO_U(stroke_rgb[1]), + SP_COLOR_F_TO_U(stroke_rgb[2]) + ); + tmp_style << tmp; + break; + } + tmp_style << "stroke-width:" << + MAX( 0.001, d->dc[d->level].style.stroke_width.value ) << "px;"; + + tmp_style << "stroke-linecap:" << + ( + d->dc[d->level].style.stroke_linecap.computed == 0 ? "butt" : + d->dc[d->level].style.stroke_linecap.computed == 1 ? "round" : + d->dc[d->level].style.stroke_linecap.computed == 2 ? "square" : + "unknown" + ) << ";"; + + tmp_style << "stroke-linejoin:" << + ( + d->dc[d->level].style.stroke_linejoin.computed == 0 ? "miter" : + d->dc[d->level].style.stroke_linejoin.computed == 1 ? "round" : + d->dc[d->level].style.stroke_linejoin.computed == 2 ? "bevel" : + "unknown" + ) << ";"; + + // Set miter limit if known, even if it is not needed immediately (not miter) + tmp_style << "stroke-miterlimit:" << + MAX( 2.0, d->dc[d->level].style.stroke_miterlimit.value ) << ";"; + + if (d->dc[d->level].style.stroke_dasharray_set && + d->dc[d->level].style.stroke_dash.n_dash && d->dc[d->level].style.stroke_dash.dash) + { + tmp_style << "stroke-dasharray:"; + for (int i=0; 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(); +} + + +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 i = 0; + int penstyle = (pEmr->lopn.lopnStyle & U_PS_STYLE_MASK); + d->dc[d->level].style.stroke_dash.n_dash = + penstyle == U_PS_DASHDOTDOT ? 6 : penstyle == U_PS_DASHDOT ? 4 : 2; + if (d->dc[d->level].style.stroke_dash.dash && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dash.dash!=d->dc[d->level-1].style.stroke_dash.dash))) + delete[] d->dc[d->level].style.stroke_dash.dash; + d->dc[d->level].style.stroke_dash.dash = new double[d->dc[d->level].style.stroke_dash.n_dash]; + if (penstyle==U_PS_DASH || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 3; + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + } + if (penstyle==U_PS_DOT || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + } + if (penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + } + + d->dc[d->level].style.stroke_dasharray_set = 1; + break; + } + + case U_PS_SOLID: + default: + { + d->dc[d->level].style.stroke_dasharray_set = 0; + break; + } + } + + switch (pEmr->lopn.lopnStyle & U_PS_ENDCAP_MASK) { + case U_PS_ENDCAP_ROUND: { d->dc[d->level].style.stroke_linecap.computed = 1; break; } + case U_PS_ENDCAP_SQUARE: { d->dc[d->level].style.stroke_linecap.computed = 2; break; } + case U_PS_ENDCAP_FLAT: + default: { d->dc[d->level].style.stroke_linecap.computed = 0; break; } + } + + switch (pEmr->lopn.lopnStyle & U_PS_JOIN_MASK) { + case U_PS_JOIN_BEVEL: { d->dc[d->level].style.stroke_linejoin.computed = 2; break; } + case U_PS_JOIN_MITER: { d->dc[d->level].style.stroke_linejoin.computed = 0; break; } + case U_PS_JOIN_ROUND: + default: { d->dc[d->level].style.stroke_linejoin.computed = 1; break; } + } + + d->dc[d->level].stroke_set = true; + + if (pEmr->lopn.lopnStyle == U_PS_NULL) { + d->dc[d->level].style.stroke_width.value = 0; + d->dc[d->level].stroke_set = false; + } else if (pEmr->lopn.lopnWidth.x) { + int cur_level = d->level; + d->level = d->emf_obj[index].level; + double pen_width = pix_to_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) { + 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; +// 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_dash.dash[i] = dash_length; + } + d->dc[d->level].style.stroke_dasharray_set = 1; + } else { + d->dc[d->level].style.stroke_dasharray_set = 0; + } + break; + } + + case U_PS_DASH: + case U_PS_DOT: + case U_PS_DASHDOT: + case U_PS_DASHDOTDOT: + { + int i = 0; + int penstyle = (pEmr->elp.elpPenStyle & U_PS_STYLE_MASK); + d->dc[d->level].style.stroke_dash.n_dash = + penstyle == U_PS_DASHDOTDOT ? 6 : penstyle == U_PS_DASHDOT ? 4 : 2; + if (d->dc[d->level].style.stroke_dash.dash && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dash.dash!=d->dc[d->level-1].style.stroke_dash.dash))) + delete[] d->dc[d->level].style.stroke_dash.dash; + d->dc[d->level].style.stroke_dash.dash = new double[d->dc[d->level].style.stroke_dash.n_dash]; + if (penstyle==U_PS_DASH || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 3; + d->dc[d->level].style.stroke_dash.dash[i++] = 2; + } + if (penstyle==U_PS_DOT || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + d->dc[d->level].style.stroke_dash.dash[i++] = 2; + } + if (penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + d->dc[d->level].style.stroke_dash.dash[i++] = 2; + } + + d->dc[d->level].style.stroke_dasharray_set = 1; + break; + } + case U_PS_SOLID: +/* 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 == 0xFFFFFFFF){ // This happens if createmonobrush has a DIB that isn't monochrome + double r, g, b; + r = SP_COLOR_U_TO_F( U_RGBAGetR(d->dc[d->level].textColor)); + g = SP_COLOR_U_TO_F( U_RGBAGetG(d->dc[d->level].textColor)); + b = SP_COLOR_U_TO_F( U_RGBAGetB(d->dc[d->level].textColor)); + d->dc[d->level].style.fill.value.color.set( r, g, b ); + d->dc[d->level].fill_mode = DRAW_PAINT; + } + else { + d->dc[d->level].fill_idx = tidx; + d->dc[d->level].fill_mode = DRAW_IMAGE; + } + d->dc[d->level].fill_set = true; + } + } +} + + +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; + + tmp_image << " y=\"" << dy << "\"\n x=\"" << dx <<"\"\n "; + + // The image ID is filled in much later when tmp_image is converted + + + MEMPNG mempng; // PNG in memory comes back in this + mempng.buffer = NULL; + + char *rgba_px = NULL; // RGBA pixels + char *sub_px = NULL; // RGBA pixels, subarray + const char *px = NULL; // DIB pixels + const U_RGBQUAD *ct = NULL; // DIB color table + uint32_t width, height, colortype, numCt, invert; + if(!cbBits || + !cbBmi || + (iUsage != U_DIB_RGB_COLORS) || + !(dibparams = get_DIB_params( // this returns pointers and values, but allocates no memory + pEmr, + offBits, + offBmi, + &px, + (const U_RGBQUAD **) &ct, + &numCt, + &width, + &height, + &colortype, + &invert + )) + ){ + if(sw == 0 || sh == 0){ + sw = width; + sh = height; + } + + if(!DIB_to_RGBA( + px, // DIB pixel array + ct, // DIB color table + numCt, // DIB color table number of entries + &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. + width, // Width of pixel array + height, // Height of pixel array + colortype, // DIB BitCount Enumeration + numCt, // Color table used if not 0 + invert // If DIB rows are in opposite order from RGBA rows + ) && + rgba_px + ){ + sub_px = RGBA_to_RGBA( + rgba_px, // full pixel array from DIB + width, // Width of pixel array + height, // Height of pixel array + sx,sy, // starting point in pixel array + &sw,&sh // columns/rows to extract from the pixel array (output array size) + ); + + if(!sub_px)sub_px=rgba_px; + toPNG( // Get the image from the RGBA px into mempng + &mempng, + sw, sh, // size of the extracted pixel array + sub_px + ); + free(sub_px); + } + } + gchar *base64String; + if(dibparams == U_BI_JPEG){ + tmp_image << " xlink:href=\"data:image/jpeg;base64,"; + base64String = g_base64_encode((guchar*) px, numCt ); + tmp_image << base64String ; + g_free(base64String); + } + else if(dibparams==U_BI_PNG){ + tmp_image << " xlink:href=\"data:image/png;base64,"; + base64String = g_base64_encode((guchar*) px, numCt ); + tmp_image << base64String ; + g_free(base64String); + } + else if(mempng.buffer){ + tmp_image << " xlink:href=\"data:image/png;base64,"; + gchar *base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + tmp_image << base64String ; + g_free(base64String); + } + else { + tmp_image << " xlink:href=\"data:image/png;base64,"; + // insert a random 3x4 blotch otherwise + tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="; + } + + tmp_image << "\"\n height=\"" << dh << "\"\n width=\"" << dw << "\"\n"; + + tmp_image << " transform=" << current_matrix(d, dx, dy, 1); // calculate appropriate offset + *(d->outsvg) += "\n\t <image\n"; + *(d->outsvg) += tmp_image.str().c_str(); + + *(d->outsvg) += "/> \n"; + *(d->path) = ""; +} + +/** + \fn myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA lpData) + \param contents binary contents of an EMF file + \param length length in bytes of contents + \param d Inkscape data structures returned by this call +*/ +//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.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 != 0xFFFFFFFF) && (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 != 0xFFFFFFFF) && // next record is valid type + (d->mask & U_DRAW_VISIBLE) && // Current set of objects are drawable + ( + (d->mask & U_DRAW_FORCE) || // This draw is forced by STROKE/FILL/STROKEANDFILL PATH + (emr_mask & U_DRAW_ALTERS) || // Next record would alter the drawing environment in some way + ( + (emr_mask & U_DRAW_VISIBLE) && // Next record is visible... + ( + ( !(d->mask & U_DRAW_ONLYTO) ) || // Non *TO records cannot be followed by any Visible + ((d->mask & U_DRAW_ONLYTO) && !(emr_mask & U_DRAW_ONLYTO) )// *TO records can only be followed by other *TO records + ) + ) + ) + ){ +// std::cout << "PATH DRAW at TOP" << std::endl; + *(d->outsvg) += " <path "; // this is the ONLY place <path should be used!!!! + 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!!!! + *(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 = d->MMX * Inkscape::Util::Quantity::convert(1, "mm", "px"); + d->PixelsOutY = d->MMY * Inkscape::Util::Quantity::convert(1, "mm", "px"); + + // Upper left corner, from header rclBounds, in device units, usually both 0, but not always + d->ulCornerInX = pEmr->rclBounds.left; + 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_dash.dash && (old_level==0 || (old_level>0 && d->dc[old_level].style.stroke_dash.dash!=d->dc[old_level-1].style.stroke_dash.dash))){ + delete[] d->dc[old_level].style.stroke_dash.dash; + } + if(d->dc[old_level].font_name){ + free(d->dc[old_level].font_name); // else memory leak + d->dc[old_level].font_name = NULL; + } + old_level--; + } + break; + } + 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, ¢er, &start, &end, &size); + if(!stat){ + tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; + d->mask |= emr_mask; + } + 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, ¢er, &start, &end, &size)){ + tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; + tmp_path << " z "; + d->mask |= emr_mask; + } + 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, ¢er, &start, &end, &size)){ + tmp_path << "\n\tM " << pix_to_xy(d, center.x, center.y); + tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; + tmp_path << " z "; + d->mask |= emr_mask; + } + 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, ¢er, &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 + *(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 + + 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: dbg_str << "<!-- U_EMR_GRADIENTFILL -->\n"; break; + /* Gradient fill is doable for rectangles because those correspond to linear gradients. However, + the general case for the triangle fill, with a different color in each corner of the triangle, + has no SVG equivalent and cannot be easily emulated with SVG gradients. Except that so far + I (DM) have not been able to make an EMF with a rectangular gradientfill record which is not + completely toxic to other EMF readers. So far now, do nothing. + */ + case U_EMR_SETLINKEDUFIS: dbg_str << "<!-- 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; +} + + +// Aldus Placeable Header =================================================== +// Since we are a 32bit app, we have to be sure this structure compiles to +// be identical to a 16 bit app's version. To do this, we use the #pragma +// to adjust packing, we use a uint16_t for the hmf handle, and a SMALL_RECT +// for the bbox rectangle. +#pragma pack( push ) +#pragma pack( 2 ) +typedef struct _SMALL_RECT { + int16_t Left; + int16_t Top; + int16_t Right; + int16_t Bottom; +} SMALL_RECT, *PSMALL_RECT; +typedef struct +{ + uint32_t dwKey; + uint16_t hmf; + SMALL_RECT bbox; + uint16_t wInch; + uint32_t dwReserved; + uint16_t wCheckSum; +} APMHEADER, *PAPMHEADER; +#pragma pack( pop ) + +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; + + // 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); + + 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; + + 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); + + 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=\"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..787a2fdb3 --- /dev/null +++ b/src/extension/internal/emf-inout.h @@ -0,0 +1,211 @@ +/** @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_H +#define SEEN_EXTENSION_INTERNAL_EMF_H + +#define PNG_SKIP_SETJMP_CHECK // else any further png.h include blows up in the compiler +#include <png.h> +#include <libuemf/uemf.h> +#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 + +/* + both emf-inout.h and wmf-inout.h are included by init.cpp, so whichever one goes in first defines these ommon types +*/ +#ifndef SEEN_EXTENSION_INTERNAL_METAFILECOMMON_ +#define SEEN_EXTENSION_INTERNAL_METAFILECOMMON_ +/* 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; +#endif + +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. + TR_INFO *tri; // Text Reassembly data structure + + + int n_obj; + PEMF_OBJECT emf_obj; +} EMF_CALLBACK_DATA, *PEMF_CALLBACK_DATA; + +class Emf : Inkscape::Extension::Implementation::Implementation { //This is a derived class + +public: + + Emf(); // Empty constructor + + virtual ~Emf();//Destructor + + bool check(Inkscape::Extension::Extension *module); //Can this module load (always yes for now) + + void save(Inkscape::Extension::Output *mod, // Save the given document to the given filename + SPDocument *doc, + gchar const *filename); + + virtual SPDocument *open( Inkscape::Extension::Input *mod, + const gchar *uri ); + + static void init(void);//Initialize the class + +private: + +protected: + + 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 uint32_t sethexcolor(U_COLORREF color); + 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 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..bb5625ef3 --- /dev/null +++ b/src/extension/internal/emf-print.cpp @@ -0,0 +1,1758 @@ +/** @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 "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 = 20.0f; +static U_XFORM worldTransform; +static bool FixPPTCharPos, FixPPTDashLine, FixPPTGrad2Polys, 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"); + + FixPPTCharPos = mod->get_param_bool("FixPPTCharPos"); + FixPPTDashLine = mod->get_param_bool("FixPPTDashLine"); + FixPPTGrad2Polys = mod->get_param_bool("FixPPTGrad2Polys"); + 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, dpi=90 -> 3543.3 dpm + (void) drawing_size((int) ceil(dwInchesX * 25.4), (int) ceil(dwInchesY * 25.4), 3.543307, &rclBounds, &rclFrame); + + // set up the reference device as 100 X A4 horizontal, (1200 dpi/25.4 -> dpmm). Extra digits maintain dpi better in EMF + int MMX = 21600; + int MMY = 27900; + (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"); + } + + + // Correct for dpi in EMF (1200) vs dpi in Inkscape (always 90). + // Also correct for the scaling in PX2WORLD, which is set to 20. + + worldTransform.eM11 = 1200. / (90.0 * PX2WORLD); + worldTransform.eM12 = 0.0; + worldTransform.eM21 = 0.0; + worldTransform.eM22 = 1200. / (90.0 * PX2WORLD); + worldTransform.eDx = 0; + worldTransform.eDy = 0; + + rec = U_EMRMODIFYWORLDTRANSFORM_set(worldTransform, U_MWT_LEFTMULTIPLY); + if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) { + 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, dwInchesX * Inkscape::Util::Quantity::convert(1, "in", "mm"), dwInchesY * Inkscape::Util::Quantity::convert(1, "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; + GdkPixbuf *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; + 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 + } + + 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 *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!! + colortype = U_BCBM_COLOR32; + (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1); + // Not sure why the next swap is needed because the preceding does it, and the code is identical + // to that in stretchdibits_set, which does not need this. + swapRBinRGBA(px, width * height); + Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0); + Bmi = bitmapinfo_set(Bmih, ct); + rec = createdibpatternbrushpt_set(&brush, eht, U_DIB_RGB_COLORS, Bmi, cbPx, px); + if (!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)) { + 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; + GdkPixbuf *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 *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!! + colortype = U_BCBM_COLOR32; + (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1); + // Not sure why the next swap is needed because the preceding does it, and the code is identical + // to that in stretchdibits_set, which does not need this. + swapRBinRGBA(px, width * height); + Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0); + Bmi = bitmapinfo_set(Bmih, ct); + } else { // pattern + 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_dash.n_dash && + style->stroke_dash.dash) { + if (FixPPTDashLine) { // will break up line into many smaller lines. Override gradient if that was set, cannot do both. + brushStyle = U_BS_SOLID; + hatchType = U_HS_HORIZONTAL; + } else { + int i = 0; + while ((linestyle != U_PS_USERSTYLE) && (i < style->stroke_dash.n_dash)) { + if (style->stroke_dash.dash[i] > 0.00000001) { + linestyle = U_PS_USERSTYLE; + } + i++; + } + + if (linestyle == U_PS_USERSTYLE) { + n_dash = style->stroke_dash.n_dash; + dash = new uint32_t[n_dash]; + for (i = 0; i < style->stroke_dash.n_dash; i++) { + dash[i] = (uint32_t)(style->stroke_dash.dash[i]); + } + } + } + } + + elp = extlogpen_set( + U_PS_GEOMETRIC | linestyle | linecap | linejoin, + linewidth, + 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; + } +} + + +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; + + if (create_brush(style, NULL)) { // 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; + c1 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa); + sp_color_get_rgb_floatv(&tg->vector.stops[nstops - 1].color, rgb); + opa = tg->vector.stops[nstops - 1].opacity; + c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa); + + doff = 0.0; + doff_base = 0.0; + doff_range = tg->vector.stops[1].offset; // next or last stop + + if (gv.mode == DRAW_RADIAL_GRADIENT) { + Geom::Point xv = gv.p2 - gv.p1; // X' vector + Geom::Point yv = gv.p3 - gv.p1; // Y' vector + Geom::Point xuv = Geom::unit_vector(xv); // X' unit vector + double rx = hypot(xv[X], xv[Y]); + double ry = hypot(yv[X], yv[Y]); + double range = fmax(rx, ry); // length along the gradient + double step = range / divisions; // adequate approximation for gradient + double overlap = step / 4.0; // overlap slices slightly + double start; + double stop; + Geom::PathVector pathvc, pathvr; + + /* radial gradient might stop part way through the shape, fill with outer color from there to "infinity". + Do this first so that outer colored ring will overlay it. + */ + pathvc = center_elliptical_hole_as_SVG_PathV(gv.p1, rx * (1.0 - overlap / range), ry * (1.0 - overlap / range), asin(xuv[Y])); + pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_oddEven, frb); + wc = weight_opacity(c2); + (void) create_brush(style, &wc); + print_pathv(pathvr, fill_transform); + + sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb); + opa = tg->vector.stops[istop].opacity; + c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa); + + for (start = 0.0; start < range; start += step, doff += 1. / divisions) { + stop = start + step + overlap; + if (stop > range) { + stop = range; + } + wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base)); + (void) create_brush(style, &wc); + + pathvc = center_elliptical_ring_as_SVG_PathV(gv.p1, rx * start / range, ry * start / range, rx * stop / range, ry * stop / range, asin(xuv[Y])); + + pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb); + print_pathv(pathvr, fill_transform); // show the intersection + + if (doff >= doff_range - doff_base) { + istop++; + if (istop >= nstops) { + continue; // could happen on a rounding error + } + doff_base = doff_range; + doff_range = tg->vector.stops[istop].offset; // next or last stop + c1 = c2; + sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb); + opa = tg->vector.stops[istop].opacity; + c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa); + } + } + } else if (gv.mode == DRAW_LINEAR_GRADIENT) { + Geom::Point uv = Geom::unit_vector(gv.p2 - gv.p1); // unit vector + Geom::Point puv = uv.cw(); // perp. to unit vector + double range = Geom::distance(gv.p1, gv.p2); // length along the gradient + double step = range / divisions; // adequate approximation for gradient + double overlap = step / 4.0; // overlap slices slightly + double start; + double stop; + Geom::PathVector pathvc, pathvr; + + /* before lower end of gradient, overlap first slice position */ + wc = weight_opacity(c1); + (void) create_brush(style, &wc); + pathvc = rect_cutter(gv.p1, uv * (overlap), uv * (-50000.0), puv * 50000.0); + pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb); + print_pathv(pathvr, fill_transform); + + /* after high end of gradient, overlap last slice poosition */ + wc = weight_opacity(c2); + (void) create_brush(style, &wc); + pathvc = rect_cutter(gv.p2, uv * (-overlap), uv * (50000.0), puv * 50000.0); + pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb); + print_pathv(pathvr, fill_transform); + + sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb); + opa = tg->vector.stops[istop].opacity; + c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa); + + for (start = 0.0; start < range; start += step, doff += 1. / divisions) { + stop = start + step + overlap; + if (stop > range) { + stop = range; + } + pathvc = rect_cutter(gv.p1, uv * start, uv * stop, puv * 50000.0); + + wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base)); + (void) create_brush(style, &wc); + Geom::PathVector pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb); + print_pathv(pathvr, fill_transform); // show the intersection + + if (doff >= doff_range - doff_base) { + istop++; + if (istop >= nstops) { + continue; // could happen on a rounding error + } + doff_base = doff_range; + doff_range = tg->vector.stops[istop].offset; // next or last stop + c1 = c2; + sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb); + opa = tg->vector.stops[istop].opacity; + c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa); + } + } + } else { + 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_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) || + !all_closed + ) { + print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv + use_fill = false; + } + } + + 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_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) { + // convert the path, gets its complete length, and then make a new path with parameter length instead of t + Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw; // pathv-> sbasis + Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw2; // sbasis using arc length parameter + Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw3; // new (discontinuous) path, composed of dots/dashes + Geom::Piecewise<Geom::D2<Geom::SBasis> > first_frag; // first fragment, will be appended at end + int n_dash = style->stroke_dash.n_dash; + int i = 0; //dash index + double tlength; // length of tmp_pathpw + double slength = 0.0; // start of gragment + double elength; // end of gragment + for (unsigned int i = 0; i < pathv.size(); i++) { + tmp_pathpw.concat(pathv[i].toPwSb()); + } + tlength = length(tmp_pathpw, 0.1); + tmp_pathpw2 = arc_length_parametrization(tmp_pathpw); + + // go around the dash array repeatedly until the entire path is consumed (but not beyond). + while (slength < tlength) { + elength = slength + style->stroke_dash.dash[i++]; + if (elength > tlength) { + elength = tlength; + } + Geom::Piecewise<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_dash.dash[i++]; // the gap + if (i >= n_dash) { + i = 0; + } + } + tmp_pathpw3.concat(first_frag); // may merge line around start point + Geom::PathVector out_pathv = Geom::path_from_piecewise(tmp_pathpw3, 0.01); + print_pathv(out_pathv, tf); + } else { + print_pathv(pathv, tf); + } + + use_stroke = false; + use_fill = false; + + 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_ignore*/, /** WRONG affine transform, 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 = atof(style->object->getAttribute("x")); + y1 = atof(style->object->getAttribute("y")); + dw = atof(style->object->getAttribute("width")); + dh = atof(style->object->getAttribute("height")); + 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); + 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; //map pLL2 (now in EMF coordinates) back onto itself after the rotation + 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=\"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-win32-print.h b/src/extension/internal/emf-print.h index e7bd08477..1546fcaea 100644 --- a/src/extension/internal/emf-win32-print.h +++ b/src/extension/internal/emf-print.h @@ -8,59 +8,33 @@ * * 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_EMF_PRINT_H +#define SEEN_INKSCAPE_EXTENSION_INTERNAL_EMF_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/uemf.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 PrintEmf : 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, 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); - 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); + PrintEmf(); /* Print functions */ virtual unsigned int setup (Inkscape::Extension::Print * module); @@ -69,8 +43,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 +53,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); - - void destroy_brush(); - - void create_pen(SPStyle const *style, const Geom::Affine &transform); - - void destroy_pen(); - - void flush_fill(); + static void smuggle_adxkyrtl_out(const char *string, uint32_t **adx, double *ky, int *rtl, int *ndx, float scale); + 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 /* WIN32 */ -#endif /* __INKSCAPE_EXTENSION_INTERNAL_PRINT_EMF_WIN32_H__ */ +#endif /* __INKSCAPE_EXTENSION_INTERNAL_PRINT_EMF_H__ */ /* Local Variables: diff --git a/src/extension/internal/emf-win32-inout.cpp b/src/extension/internal/emf-win32-inout.cpp deleted file mode 100644 index b9ab2e385..000000000 --- a/src/extension/internal/emf-win32-inout.cpp +++ /dev/null @@ -1,2582 +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 "clear-n_.h" -#include "document.h" -#include "util/units.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 = Inkscape::Util::Quantity::convert(1, "px", "pt"); -static float device_x; -static float device_y; -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(); - if (ret) { - g_free(oldoutput); - throw Inkscape::Extension::Output::save_failed(); - } - /* 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 = 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, ppx); - - x *= device_scale; - x -= device_x; - - return x; -} - -static double -pix_to_y_point(PEMF_CALLBACK_DATA d, double px, double py) -{ - double ppy = 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, ppy); - - y *= device_scale; - y -= device_y; - - 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_SELECTOBJECT && - 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; - device_x = Inkscape::Util::Quantity::convert(pEmr->rclFrame.left/100.0, "mm", "px"); - device_y = Inkscape::Util::Quantity::convert(pEmr->rclFrame.top/100.0, "mm", "px"); - - d->MMX = d->dc[d->level].PixelsInX / 100.0; - d->MMY = d->dc[d->level].PixelsInY / 100.0; - - d->dc[d->level].PixelsOutX = Inkscape::Util::Quantity::convert(d->MMX, "mm", "px"); - d->dc[d->level].PixelsOutY = Inkscape::Util::Quantity::convert(d->MMY, "mm", "px"); - - // calculate ratio of Inkscape dpi/device dpi - if (pEmr->szlMillimeters.cx && pEmr->szlDevice.cx) - device_scale = Inkscape::Util::Quantity::convert(pEmr->szlMillimeters.cx/pEmr->szlDevice.cx, "mm", "px"); - - 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"; - - PEMRPIE pEmr = (PEMRPIE) lpEMFR; - RECTL rc = pEmr->rclBox; - - double l = pix_to_x_point( d, rc.left, rc.top ); - double t = pix_to_y_point( d, rc.left, rc.top ); - double r = pix_to_x_point( d, rc.right, rc.bottom ); - double b = pix_to_y_point( d, rc.right, rc.bottom ); - double x1 = pix_to_x_point( d, pEmr->ptlStart.x, pEmr->ptlStart.y ); - double y1 = pix_to_y_point( d, pEmr->ptlStart.x, pEmr->ptlStart.y ); - double x2 = pix_to_x_point( d, pEmr->ptlEnd.x, pEmr->ptlEnd.y ); - double y2 = pix_to_y_point( d, pEmr->ptlEnd.x, pEmr->ptlEnd.y ); - - SVGOStringStream tmp_pie; - tmp_pie << "d=\"" << - "\n\tM " << x1 << " " << y1 << " "; - - double angle1 = -atan2(y1 - (t + b) / 2.0, x1 - (l + r) / 2.0); - double angle2 = -atan2(y2 - (t + b) / 2.0, x2 - (l + r) / 2.0); - double angle = angle2 - angle1; - if (angle < 0) angle += 2*M_PI; - - bool large_arc_flag = false; - if (angle > M_PI) large_arc_flag = true; - - tmp_pie << "\n\tA " << - fabs(l - r) / 2.0 << " " << - fabs(t - b) / 2.0 << " " << - "0 " << large_arc_flag << " 0 " << - x2 << " " << y2 << " " << - "\n\tL " << (l + r) / 2.0 << " " << (t + b) / 2.0 << " " << - "\n\tz"; - - assert_empty_path(d, "EMR_PIE"); - - *(d->outsvg) += " <path "; - output_style(d, lpEMFR->iType); - *(d->outsvg) += "\n\t"; - *(d->outsvg) += tmp_pie.str().c_str(); - *(d->outsvg) += " \" /> \n"; - *(d->path) = ""; - 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; - - //BUG in SetMiterLimit() on mingw, possibly in underlying Windows(at least on XP?) - //The function takes a float but saves a 32 bit int in the EMR_SETMITERLIMIT record. - float miterlimit = *((int32_t *) &(pEmr->eMiterLimit)); - d->dc[d->level].style.stroke_miterlimit.value = miterlimit; //ratio, not a pt size - if (d->dc[d->level].style.stroke_miterlimit.value < 1.0) - d->dc[d->level].style.stroke_miterlimit.value = 1.0; - break; - } - case EMR_BEGINPATH: - { - dbg_str << "<!-- 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 == PATCOPY) || (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) { - if ((wide_text[0] < 32) && (strlen(ansi_text) == 1)) { - g_free(ansi_text); // filter out isolated control characters - ansi_text = g_strdup(""); - } -// 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 ); - d.pDesc[dwNeeded] = 0; - if ( GetEnhMetaFileDescription( hemf, dwNeeded, d.pDesc ) == 0 ) - lstrcpy( d.pDesc, "" ); - if ((lstrlen(d.pDesc) > 1) && (lstrlen(d.pDesc) < dwNeeded)) - 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 e01782257..000000000 --- a/src/extension/internal/emf-win32-print.cpp +++ /dev/null @@ -1,940 +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 "util/units.h" - -#include "style.h" -#include "inkscape-version.h" -#include "sp-root.h" - -#include "emf-win32-print.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().value("px"); - _height = doc->getHeight().value("px"); - - 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 .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, Inkscape::Util::Quantity::convert(dwInchesX, "in", "mm"), Inkscape::Util::Quantity::convert(dwInchesY, "in", "mm")); - 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().value("px"))); /// @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) -{ - if (style) { - float opacity = SP_SCALE24_TO_FLOAT(style->fill_opacity.value); - if (opacity <= 0.0) - return 1; - - float rgb[3]; - 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 * Inkscape::Util::Quantity::convert(style->stroke_width.computed, "px", "in") * 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) (Inkscape::Util::Quantity::convert(style->stroke_dash.dash[i], "px", "in") * dwDPI); - } - } - } - - hpen = ExtCreatePen( - PS_GEOMETRIC | linestyle | linecap | linejoin, - linewidth, - &lb, - n_dash, - dash ); - - if ( !hpen && linestyle == PS_USERSTYLE ) { - hpen = ExtCreatePen( - PS_GEOMETRIC | PS_SOLID | linecap | linejoin, - linewidth, - &lb, - 0, - NULL ); - } - - if ( !hpen ) { - hpen = CreatePen( - PS_SOLID, - linewidth, - lb.lbColor ); - } - - hpenOld = (HPEN) SelectObject( hdc, hpen ); - - if (linejoin == PS_JOIN_MITER) { - float oldmiterlimit; - float miterlimit = style->stroke_miterlimit.value; //ratio, not a pt size - - SetMiterLimit( - hdc, - miterlimit, - &oldmiterlimit ); - } - - if (n_dash) { - delete[] dash; - } - } - else { // if (!style) - hpen = CreatePen( PS_SOLID, 1, RGB(0, 0, 0) ); - hpenOld = (HPEN) SelectObject( hdc, hpen ); - } -} - - -void PrintEmfWin32::destroy_pen() -{ - SelectObject( hdc, hpenOld ); - if (hpen) - DeleteObject( hpen ); - hpen = NULL; -} - - -void PrintEmfWin32::flush_fill() -{ - if (!fill_pathv.empty()) { - stroke_and_fill = false; - fill_only = true; - print_pathv(fill_pathv, fill_transform); - fill_only = false; - if (!simple_shape) - FillPath( hdc ); - destroy_brush(); - fill_pathv.clear(); - } -} - -unsigned int PrintEmfWin32::bind(Inkscape::Extension::Print * /*mod*/, Geom::Affine const &transform, float /*opacity*/) -{ - if (!m_tr_stack.empty()) { - Geom::Affine tr_top = m_tr_stack.top(); - m_tr_stack.push(transform * tr_top); - } else { - m_tr_stack.push(transform); - } - - return 1; -} - -unsigned int PrintEmfWin32::release(Inkscape::Extension::Print * /*mod*/) -{ - m_tr_stack.pop(); - return 1; -} - -unsigned int PrintEmfWin32::fill(Inkscape::Extension::Print * /*mod*/, - Geom::PathVector const &pathv, Geom::Affine const & /*transform*/, SPStyle const *style, - Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/) -{ - if (!hdc) return 0; - - Geom::Affine tf = m_tr_stack.top(); - - flush_fill(); // flush any pending fills - - if (style->fill.isColor()) { - if (create_brush(style)) - return 0; - } else { - // create_brush(NULL); - return 0; - } - - fill_pathv.clear(); - std::copy(pathv.begin(), pathv.end(), std::back_inserter(fill_pathv)); - fill_transform = tf; - - // postpone fill in case of stroke-and-fill - - return 0; -} - - -unsigned int PrintEmfWin32::stroke (Inkscape::Extension::Print * /*mod*/, - Geom::PathVector const &pathv, const Geom::Affine &/*transform*/, const SPStyle *style, - Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/) -{ - if (!hdc) return 0; - - Geom::Affine tf = m_tr_stack.top(); - - stroke_and_fill = ( pathv == fill_pathv ); - - if (!stroke_and_fill) { - flush_fill(); // flush any pending fills - } - - if (style->stroke.isColor()) { - create_pen(style, tf); - } else { - // create_pen(NULL, tf); - return 0; - } - - print_pathv(pathv, tf); - - if (stroke_and_fill) { - if (!simple_shape) - StrokeAndFillPath( hdc ); - destroy_brush(); - fill_pathv.clear(); - } else { - if (!simple_shape) - StrokePath( hdc ); - } - - destroy_pen(); - - return 0; -} - - -bool PrintEmfWin32::print_simple_shape(Geom::PathVector const &pathv, const Geom::Affine &transform) -{ - Geom::PathVector pv = pathv_to_linear_and_cubic_beziers( pathv * transform ); - - int nodes = 0; - int moves = 0; - int lines = 0; - int curves = 0; - - for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) - { - moves++; - nodes++; - - for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) - { - nodes++; - - if ( is_straight_curve(*cit) ) { - lines++; - } - else if (Geom::CubicBezier const *cubic = dynamic_cast<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] = (Inkscape::Util::Quantity::convert(p0[X], "px", "in") * dwDPI); - p0[Y] = (Inkscape::Util::Quantity::convert(p0[Y], "px", "in") * 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] = (Inkscape::Util::Quantity::convert(p0[X], "px", "in") * dwDPI); - p1[X] = (Inkscape::Util::Quantity::convert(p1[X], "px", "in") * dwDPI); - //p0[Y] = (Inkscape::Util::Quantity::convert(p0[Y], "px", "in") * dwDPI); - p1[Y] = (Inkscape::Util::Quantity::convert(p1[Y], "px", "in") * 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] = (Inkscape::Util::Quantity::convert(p0[X], "px", "in") * dwDPI); - p1[X] = (Inkscape::Util::Quantity::convert(p1[X], "px", "in") * dwDPI); - p2[X] = (Inkscape::Util::Quantity::convert(p2[X], "px", "in") * dwDPI); - p3[X] = (Inkscape::Util::Quantity::convert(p3[X], "px", "in") * dwDPI); - //p0[Y] = (Inkscape::Util::Quantity::convert(p0[Y], "px", "in") * dwDPI); - p1[Y] = (Inkscape::Util::Quantity::convert(p1[Y], "px", "in") * dwDPI); - p2[Y] = (Inkscape::Util::Quantity::convert(p2[Y], "px", "in") * dwDPI); - p3[Y] = (Inkscape::Util::Quantity::convert(p3[Y], "px", "in") * 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] = (Inkscape::Util::Quantity::convert(p0[X], "px", "in") * dwDPI); - p0[Y] = (Inkscape::Util::Quantity::convert(p0[Y], "px", "in") * 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] = (Inkscape::Util::Quantity::convert(p0[X], "px", "in") * dwDPI); - p1[X] = (Inkscape::Util::Quantity::convert(p1[X], "px", "in") * dwDPI); - //p0[Y] = (Inkscape::Util::Quantity::convert(p0[Y], "px", "in") * dwDPI); - p1[Y] = (Inkscape::Util::Quantity::convert(p1[Y], "px", "in") * 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] = (Inkscape::Util::Quantity::convert(p0[X], "px", "in") * dwDPI); - p1[X] = (Inkscape::Util::Quantity::convert(p1[X], "px", "in") * dwDPI); - p2[X] = (Inkscape::Util::Quantity::convert(p2[X], "px", "in") * dwDPI); - p3[X] = (Inkscape::Util::Quantity::convert(p3[X], "px", "in") * dwDPI); - //p0[Y] = (Inkscape::Util::Quantity::convert(p0[Y], "px", "in") * dwDPI); - p1[Y] = (Inkscape::Util::Quantity::convert(p1[Y], "px", "in") * dwDPI); - p2[Y] = (Inkscape::Util::Quantity::convert(p2[Y], "px", "in") * dwDPI); - p3[Y] = (Inkscape::Util::Quantity::convert(p3[Y], "px", "in") * 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 = Inkscape::Util::Quantity::convert(-style->font_size.computed, "px", "in") * 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] = (Inkscape::Util::Quantity::convert(p2[Geom::X], "px", "in") * dwDPI); - p2[Geom::Y] = (Inkscape::Util::Quantity::convert(p2[Geom::Y], "px", "in") * 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/latex-text-renderer.cpp b/src/extension/internal/latex-text-renderer.cpp index b1c67aa81..1303b82e2 100644 --- a/src/extension/internal/latex-text-renderer.cpp +++ b/src/extension/internal/latex-text-renderer.cpp @@ -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; @@ -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,6 +585,20 @@ 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, float bleedmargin_px, SPItem *base) { @@ -625,11 +656,12 @@ LaTeXTextRenderer::setupDocument(SPDocument *doc, bool pageBoundingBox, float bl 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 0fa94c9e6..9aecf5ed9 100644 --- a/src/extension/internal/latex-text-renderer.h +++ b/src/extension/internal/latex-text-renderer.h @@ -47,11 +47,20 @@ public: 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-print.cpp b/src/extension/internal/metafile-print.cpp new file mode 100644 index 000000000..9d080bd96 --- /dev/null +++ b/src/extension/internal/metafile-print.cpp @@ -0,0 +1,491 @@ +/** @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]", + ¶ms.f1, ¶ms.f2, ¶ms.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 ¶ms) +{ + 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; +} + +#define clrweight(a,b,t) ((1-t)*((double) a) + (t)*((double) b)) +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; +} + +U_COLORREF PrintMetafile::weight_colors(U_COLORREF c1, U_COLORREF c2, double t) +{ + U_COLORREF result; + result.Red = clrweight(c1.Red, c2.Red, t); + result.Green = clrweight(c1.Green, c2.Green, t); + result.Blue = clrweight(c1.Blue, c2.Blue, t); + result.Reserved = clrweight(c1.Reserved, c2.Reserved, t); + + // now handle the opacity, mix the RGB with background at the weighted opacity + + if (result.Reserved != 255) { + result = weight_opacity(result); + } + + return result; +} + +// 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, GdkPixbuf **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..e64ba92f3 --- /dev/null +++ b/src/extension/internal/metafile-print.h @@ -0,0 +1,132 @@ +/** @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" + +struct SPGradient; +struct SPObject; + +namespace Inkscape { +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, GdkPixbuf **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/text_reassemble.c b/src/extension/internal/text_reassemble.c new file mode 100644 index 000000000..9ed6c9c3a --- /dev/null +++ b/src/extension/internal/text_reassemble.c @@ -0,0 +1,2884 @@ +/** + @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.10 +Date: 06-MAY-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 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(fti->fonts[i].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, *fpat; + FcFontSet *fontset; + 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 */ + if((status = ftinfo_find_loaded_by_spec(fti, (uint8_t *) fontspec))>=0){ + return(status); + } + + ftinfo_make_insertable(fti); + fi_idx = fti->used; + + if(!(pattern = FcNameParse((const FcChar8 *)fontspec)) )return(2); + if(!FcConfigSubstitute(NULL, pattern, FcMatchPattern) )return(3); + 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)return(4); + if(!(fpat = FcFontRenderPrepare(NULL, pattern, fontset->fonts[0])) )return(405); + if(FcPatternGetString( fpat, FC_FILE, 0, (FcChar8 **)&filename) != FcResultMatch)return(5); + if(FcPatternGetDouble( fpat, FC_SIZE, 0, &fd) != FcResultMatch)return(6); + + /* 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; + + /* release FC's own memory related to this call that does not need to be kept around so that face will work */ + FcPatternDestroy(pattern); + + /* 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 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 a text element upstream from the start element and in the reversed 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; } + } + 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->use_kern = 1; + tri->usebk = BKCLR_NONE; + tri->load_flags = FT_LOAD_NO_SCALE; + tri->kern_mode = FT_KERNING_UNSCALED; + tri->out = NULL; /* This will allocate as needed, it might not ever be needed. */ + tri->outspace = 0; + tri->outused = 0; + 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){ + tri->dirty = 0; /* set these back to their defaults */ + tri->esc = 0.0; + /* Do NOT modify use_kern, usebk, load_flags, or kern_mode */ + + 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; + }; + 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); + 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 */ + } + } + + + tsp = tpi->chunks; + /* 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. + */ + 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 the quantization error it is probably noise, set it to zero */ + if(fabs(tspj->xkern)<tri->qe)tspj->xkern = 0.0; + if(fabs(tspj->ykern)<tri->qe)tspj->ykern = 0.0; + + + 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.co = 0; + 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..d85b233be --- /dev/null +++ b/src/extension/internal/text_reassemble.h @@ -0,0 +1,386 @@ +/** + @file text_reassemble.h libTERE headers. + +See text_reassemble.c for notes + +File: text_reassemble.h +Version: 0.0.12 +Date: 14-MAY-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 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 */ + 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/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp new file mode 100644 index 000000000..451d94c0e --- /dev/null +++ b/src/extension/internal/wmf-inout.cpp @@ -0,0 +1,3232 @@ +/** @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 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 "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; + +/** Construct a PNG in memory from an RGB from the WMF 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 * Wmf::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 +Wmf::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 Wmf::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 WMF 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 WMF RGB(A) color to 0RGB +inverse of gethexcolor() in emf-print.cpp +*/ +uint32_t Wmf::sethexcolor(U_COLORREF color){ + + uint32_t out; + out = (U_RGBAGetR(color) << 16) + + (U_RGBAGetG(color) << 8 ) + + (U_RGBAGetB(color) ); + return(out); +} + + +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 worldTranform, 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 worldTranform, 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 worldTranform, 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; + + 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((iUsage != U_DIB_RGB_COLORS) || + !(dibparams = wget_DIB_params( // this returns pointers and values, but allocates no memory + dib, + &px, + &ct, + &numCt, + &width, + &height, + &colortype, + &invert + )) + ){ + + if(!DIB_to_RGBA( + px, // DIB pixel array + ct, // DIB color table + numCt, // DIB color table number of entries + &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. + width, // Width of pixel array in record + height, // Height of pixel array in record + colortype, // DIB BitCount Enumeration + numCt, // Color table used if not 0 + invert // If DIB rows are in opposite order from RGBA rows + ) && + rgba_px + ){ + toPNG( // Get the image from the RGBA px into mempng + &mempng, + width, height, // of the SRC bitmap + rgba_px + ); + free(rgba_px); + } + } + gchar *base64String; + if(dibparams == U_BI_JPEG || dibparams==U_BI_PNG){ + base64String = g_base64_encode((guchar*) px, numCt ); + idx = in_images(d, (char *) base64String); + } + else if(mempng.buffer){ + base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + idx = in_images(d, (char *) base64String); + } + else { + // insert a random 3x4 blotch otherwise + width = 3; + height = 4; + base64String = g_strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="); + idx = in_images(d, (char *) base64String); + } + if(!idx){ // add it if not already present - we looked at the actual data for comparison + if(d->images.count == d->images.size){ enlarge_images(d); } + idx = d->images.count; + d->images.strings[d->images.count++]=strdup(base64String); + + sprintf(imagename,"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) += " />\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); + 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(0xFFFFFFFF); // 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... + 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 + ) && rgba_px) + { + toPNG( // Get the image from the RGBA px into mempng + &mempng, + width, height, // of the SRC bitmap + rgba_px + ); + free(rgba_px); + } + gchar *base64String; + if(mempng.buffer){ + base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + } + else { + // insert a random 3x4 blotch otherwise + width = 3; + height = 4; + base64String = g_strdup("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="); + } + idx = in_images(d, (char *) base64String); + if(!idx){ // add it if not already present - we looked at the actual data for comparison + if(d->images.count == d->images.size){ enlarge_images(d); } + idx = d->images.count; + d->images.strings[d->images.count++]=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) += " />\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); + 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_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(#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 i = 0; + int penstyle = (up.Style & U_PS_STYLE_MASK); + d->dc[d->level].style.stroke_dash.n_dash = + penstyle == U_PS_DASHDOTDOT ? 6 : penstyle == U_PS_DASHDOT ? 4 : 2; + if (d->dc[d->level].style.stroke_dash.dash && (d->level==0 || (d->level>0 && d->dc[d->level].style.stroke_dash.dash!=d->dc[d->level-1].style.stroke_dash.dash))) + delete[] d->dc[d->level].style.stroke_dash.dash; + d->dc[d->level].style.stroke_dash.dash = new double[d->dc[d->level].style.stroke_dash.n_dash]; + if (penstyle==U_PS_DASH || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 3; + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + } + if (penstyle==U_PS_DOT || penstyle==U_PS_DASHDOT || penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + } + if (penstyle==U_PS_DASHDOTDOT) { + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + d->dc[d->level].style.stroke_dash.dash[i++] = 1; + } + + d->dc[d->level].style.stroke_dasharray_set = 1; + break; + } + + case U_PS_SOLID: + default: + { + d->dc[d->level].style.stroke_dasharray_set = 0; + break; + } + } + + switch (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); + // Bm16 not handled yet + 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 == 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; + } + else { + g_message("Please send WMF file to developers - select_brush U_WMR_DIBCREATEPATTERNBRUSH not bm16 or dib, not handled"); + } + } +} + + +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; + + tmp_image << " y=\"" << dy << "\"\n x=\"" << dx <<"\"\n "; + + // The image ID is filled in much later when tmp_image is converted + + MEMPNG mempng; // PNG in memory comes back in this + mempng.buffer = NULL; + + char *rgba_px = NULL; // RGBA pixels + char *sub_px = NULL; // RGBA pixels, subarray + const char *px = NULL; // DIB pixels + const U_RGBQUAD *ct = NULL; // color table + int32_t width, height, colortype, numCt, invert; + if( (iUsage != U_DIB_RGB_COLORS) || + !(dibparams = wget_DIB_params( // this returns pointers and values, but allocates no memory + dib, + &px, + &ct, + &numCt, + &width, + &height, + &colortype, + &invert + )) + ){ + if(sw == 0 || sh == 0){ + sw = width; + sh = height; + } + + if(!DIB_to_RGBA( + px, // DIB pixel array + ct, // DIB color table + numCt, // DIB color table number of entries + &rgba_px, // U_RGBA pixel array (32 bits), created by this routine, caller must free. + width, // Width of pixel array + height, // Height of pixel array + colortype, // DIB BitCount Enumeration + numCt, // Color table used if not 0 + invert // If DIB rows are in opposite order from RGBA rows + ) && + rgba_px + ){ + sub_px = RGBA_to_RGBA( + rgba_px, // full pixel array from DIB + width, // Width of pixel array + height, // Height of pixel array + sx,sy, // starting point in pixel array + &sw,&sh // columns/rows to extract from the pixel array (output array size) + ); + + if(!sub_px)sub_px=rgba_px; + toPNG( // Get the image from the RGBA px into mempng + &mempng, + sw, sh, // size of the extracted pixel array + sub_px + ); + free(sub_px); + } + } + gchar *base64String; + if(dibparams == U_BI_JPEG){ + tmp_image << " xlink:href=\"data:image/jpeg;base64,"; + base64String = g_base64_encode((guchar*) px, numCt ); + tmp_image << base64String ; + g_free(base64String); + } + else if(dibparams==U_BI_PNG){ + tmp_image << " xlink:href=\"data:image/png;base64,"; + base64String = g_base64_encode((guchar*) px, numCt ); + tmp_image << base64String ; + g_free(base64String); + } + else if(mempng.buffer){ + tmp_image << " xlink:href=\"data:image/png;base64,"; + gchar *base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + tmp_image << base64String ; + g_free(base64String); + } + else { + tmp_image << " xlink:href=\"data:image/png;base64,"; + // insert a random 3x4 blotch otherwise + tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="; + } + + tmp_image << "\"\n height=\"" << dh << "\"\n width=\"" << dw << "\"\n"; + + tmp_image << " transform=" << current_matrix(d, 0.0, 0.0, 0); // returns an identity matrix, no offsets. + *(d->outsvg) += "\n\t <image\n"; + *(d->outsvg) += tmp_image.str().c_str(); + + *(d->outsvg) += "/> \n"; + *(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 << " y=\"" << dy << "\"\n x=\"" << dx <<"\"\n "; + + // The image ID is filled in much later when tmp_image is converted + + MEMPNG mempng; // PNG in memory comes back in this + mempng.buffer = NULL; + + char *rgba_px = NULL; // RGBA pixels + char *sub_px = NULL; // RGBA pixels, subarray + 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... + 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 + ) && + rgba_px + ){ + sub_px = RGBA_to_RGBA( + rgba_px, // full pixel array from DIB + width, // Width of pixel array + height, // Height of pixel array + sx,sy, // starting point in pixel array + &sw,&sh // columns/rows to extract from the pixel array (output array size) + ); + + if(!sub_px)sub_px=rgba_px; + toPNG( // Get the image from the RGBA px into mempng + &mempng, + sw, sh, // size of the extracted pixel array + sub_px + ); + free(sub_px); + } + if(mempng.buffer){ + tmp_image << " xlink:href=\"data:image/png;base64,"; + gchar *base64String = g_base64_encode((guchar*) mempng.buffer, mempng.size ); + free(mempng.buffer); + tmp_image << base64String; + g_free(base64String); + } + else { + tmp_image << " xlink:href=\"data:image/png;base64,"; + // insert a random 3x4 blotch otherwise + tmp_image << "iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAA3NCSVQICAjb4U/gAAAALElEQVQImQXBQQ2AMAAAsUJQMSWI2H8qME1yMshojwrvGB8XcHKvR1XtOTc/8HENumHCsOMAAAAASUVORK5CYII="; + } + + tmp_image << "\"\n height=\"" << dh << "\"\n width=\"" << dw << "\"\n"; + + tmp_image << " transform=" << current_matrix(d, 0.0, 0.0, 0); // returns an identity matrix, no offsets. + *(d->outsvg) += "\n\t <image\n"; + *(d->outsvg) += tmp_image.str().c_str(); + + *(d->outsvg) += "/> \n"; + *(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.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 */ + + 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; + nSize = 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 + 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=\"" << d->PixelsOutX/ Inkscape::Util::Quantity::convert(1, "mm", "px") << "mm\"\n" << + " height=\"" << d->PixelsOutY/ Inkscape::Util::Quantity::convert(1, "mm", "px") << "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 != 0xFFFFFFFF) && (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 != 0xFFFFFFFF) && // next record is valid type + (d->mask & U_DRAW_VISIBLE) && // This record is drawable + ( + (d->mask & U_DRAW_FORCE) || // This draw is forced by STROKE/FILL/STROKEANDFILL PATH + (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); + 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; + } + } + + if (!d->dc[d->level].sizeView.x || !d->dc[d->level].sizeView.y) { + d->dc[d->level].sizeView = d->dc[d->level].sizeWnd; + } + + /* 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, ¢er, &start, &end, &size); + if(!stat){ + tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; + d->mask |= 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, ¢er, &start, &end, &size)){ + tmp_path << "\n\tM " << pix_to_xy(d, center.x, center.y); + tmp_path << "\n\tL " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; + tmp_path << " z "; + d->mask |= 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){ + 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){ + 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_dash.dash && (old_level==0 || (old_level>0 && d->dc[old_level].style.stroke_dash.dash!=d->dc[old_level-1].style.stroke_dash.dash))){ + delete[] d->dc[old_level].style.stroke_dash.dash; + } + if(d->dc[old_level].font_name){ + free(d->dc[old_level].font_name); // else memory leak + d->dc[old_level].font_name = NULL; + } + old_level--; + } + break; + } + 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: + select_brush(d, index); + break; + case U_WMR_CREATEFONTINDIRECT: + select_font(d, index); + break; + case U_WMR_CREATEPATTERNBRUSH: // <- this one did not display properly on XP, DIBCREATEPATTERNBRUSH works + 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, ¢er, &start, &end, &size)){ + tmp_path << "\n\tM " << pix_to_xy(d, start.x, start.y); + tmp_path << " A " << pix_to_abs_size(d, size.x)/2.0 << "," << pix_to_abs_size(d, size.y)/2.0 ; + tmp_path << " "; + tmp_path << 180.0 * current_rotation(d)/M_PI; + tmp_path << " "; + tmp_path << " " << f1 << "," << f2 << " "; + tmp_path << pix_to_xy(d, end.x, end.y) << " \n"; + tmp_path << " z "; + d->mask |= 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); + } + 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 + + 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; + } + + if (d.dc[0].style.stroke_dash.dash) + delete[] d.dc[0].style.stroke_dash.dash; + + for(int i=0; i<=d.level;i++){ + if(d.dc[i].font_name)free(d.dc[i].font_name); + } + + d.tri = trinfo_release_except_FC(d.tri); + + 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..90f424c46 --- /dev/null +++ b/src/extension/internal/wmf-inout.h @@ -0,0 +1,213 @@ +/** @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 + +#define PNG_SKIP_SETJMP_CHECK // else any further png.h include blows up in the compiler +#include <png.h> +#include <libuemf/uwmf.h> +#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 + +/* + both emf-inout.h and wmf-inout.h are included by init.cpp, so whichever one goes in first defines these ommon types +*/ +#ifndef SEEN_EXTENSION_INTERNAL_METAFILECOMMON_ +#define SEEN_EXTENSION_INTERNAL_METAFILECOMMON_ +/* 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; +#endif + + + +// 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 : Inkscape::Extension::Implementation::Implementation { //This is a derived class + +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 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 uint32_t sethexcolor(U_COLORREF color); + 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..67d42d605 --- /dev/null +++ b/src/extension/internal/wmf-print.cpp @@ -0,0 +1,1569 @@ +/** @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 "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 = 1200.0 / 90.0; // inkscape is 90 dpi, WMF file is 1200 +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"); + + 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 + + PU_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((PU_METARECORD)rec, wt, 1); + free(ps); + + rec = U_WMRSETWINDOWEXT_set(point16_set(dwPxX, dwPxY)); + if (!rec || wmf_append((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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, PU_COLORREF fcolor) +{ + float rgb[3]; + char *rec; + U_WLOGBRUSH lb; + uint32_t brush, fmode; + MFDrawMode fill_mode; + GdkPixbuf *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((PU_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((PU_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((PU_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; + PU_RGBQUAD ct; + int numCt; + U_BITMAPINFOHEADER Bmih; + PU_BITMAPINFO Bmi; + rgba_px = (char *) gdk_pixbuf_get_pixels(pixbuf); // Do NOT free this!!! + colortype = U_BCBM_COLOR32; + (void) RGBA_to_DIB(&px, &cbPx, &ct, &numCt, rgba_px, width, height, width * 4, colortype, 0, 1); + // Not sure why the next swap is needed because the preceding does it, and the code is identical + // to that in stretchdibits_set, which does not need this. + swapRBinRGBA(px, width * height); + Bmih = bitmapinfoheader_set(width, height, 1, colortype, U_BI_RGB, 0, PXPERMETER, PXPERMETER, numCt, 0); + Bmi = bitmapinfo_set(Bmih, ct); + rec = wcreatedibpatternbrush_srcdib_set(&brush, wht, U_DIB_RGB_COLORS, Bmi, cbPx, px); + if (!rec || wmf_append((PU_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((PU_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((PU_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((PU_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((PU_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((PU_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_dash.n_dash && + style->stroke_dash.dash) { + 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((PU_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((PU_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((PU_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((PU_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 poosition */ + wc = weight_opacity(c2); + (void) create_brush(style, &wc); + pathvc = rect_cutter(gv.p2, uv * (-overlap), uv * (50000.0), puv * 50000.0); + pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb); + print_pathv(pathvr, fill_transform); + + sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb); + opa = tg->vector.stops[istop].opacity; + c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa); + + for (start = 0.0; start < range; start += step, doff += 1. / divisions) { + stop = start + step + overlap; + if (stop > range) { + stop = range; + } + pathvc = rect_cutter(gv.p1, uv * start, uv * stop, puv * 50000.0); + + wc = weight_colors(c1, c2, (doff - doff_base) / (doff_range - doff_base)); + (void) create_brush(style, &wc); + Geom::PathVector pathvr = sp_pathvector_boolop(pathvc, pathv, bool_op_inters, (FillRule) fill_nonZero, frb); + print_pathv(pathvr, fill_transform); // show the intersection + + if (doff >= doff_range - doff_base) { + istop++; + if (istop >= nstops) { + continue; // could happen on a rounding error + } + doff_base = doff_range; + doff_range = tg->vector.stops[istop].offset; // next or last stop + c1 = c2; + sp_color_get_rgb_floatv(&tg->vector.stops[istop].color, rgb); + opa = tg->vector.stops[istop].opacity; + c2 = U_RGBA(255 * rgb[0], 255 * rgb[1], 255 * rgb[2], 255 * opa); + } + } + } else { + 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_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) || + !all_closed + ) { + print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv + use_fill = false; + } + } + + 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_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) { + // convert the path, gets its complete length, and then make a new path with parameter length instead of t + Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw; // pathv-> sbasis + Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw2; // sbasis using arc length parameter + Geom::Piecewise<Geom::D2<Geom::SBasis> > tmp_pathpw3; // new (discontinuous) path, composed of dots/dashes + Geom::Piecewise<Geom::D2<Geom::SBasis> > first_frag; // first fragment, will be appended at end + int n_dash = style->stroke_dash.n_dash; + int i = 0; //dash index + double tlength; // length of tmp_pathpw + double slength = 0.0; // start of gragment + double elength; // end of gragment + for (unsigned int i = 0; i < pathv.size(); i++) { + tmp_pathpw.concat(pathv[i].toPwSb()); + } + tlength = length(tmp_pathpw, 0.1); + tmp_pathpw2 = arc_length_parametrization(tmp_pathpw); + + // go around the dash array repeatedly until the entire path is consumed (but not beyond). + while (slength < tlength) { + elength = slength + style->stroke_dash.dash[i++]; + if (elength > tlength) { + elength = tlength; + } + Geom::Piecewise<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_dash.dash[i++]; // the gap + if (i >= n_dash) { + i = 0; + } + } + tmp_pathpw3.concat(first_frag); // may merge line around start point + Geom::PathVector out_pathv = Geom::path_from_piecewise(tmp_pathpw3, 0.01); + print_pathv(out_pathv, tf); + } else { + print_pathv(pathv, tf); + } + + use_stroke = false; + use_fill = false; + + if (usebk) { // OPAQUE was set, revert to TRANSPARENT + usebk = false; + rec = U_WMRSETBKMODE_set(U_TRANSPARENT); + if (!rec || wmf_append((PU_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((PU_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_ignore, /** WRONG affine transform, 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((PU_METARECORD)rec, wt, U_REC_FREE)) { + g_error("Fatal programming error in PrintWmf::image at EMRHEADER"); + } + + x1 = atof(style->object->getAttribute("x")); + y1 = atof(style->object->getAttribute("y")); + dw = atof(style->object->getAttribute("width")); + dh = atof(style->object->getAttribute("height")); + 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_POINT16 Dest = point16_set(round(pLL2[Geom::X] * PX2WORLD), round(pLL2[Geom::Y] * PX2WORLD)); + U_POINT16 cDest = point16_set(round(dw * PX2WORLD), round(dh * 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((PU_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; + PU_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 = (PU_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((PU_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 = (PU_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((PU_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((PU_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((PU_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((PU_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((PU_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. + + PU_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((PU_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((PU_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((PU_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((PU_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((PU_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/wmf-print.h b/src/extension/internal/wmf-print.h new file mode 100644 index 000000000..1e5d4c323 --- /dev/null +++ b/src/extension/internal/wmf-print.h @@ -0,0 +1,94 @@ +/** @file + * @brief Windows Metafile printing - implementation + */ +/* Author: + * Ulf Erikson <ulferikson@users.sf.net> + * + * Copyright (C) 2006-2008 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ +#ifndef SEEN_INKSCAPE_EXTENSION_INTERNAL_WMF_PRINT_H +#define SEEN_INKSCAPE_EXTENSION_INTERNAL_WMF_PRINT_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <libuemf/uwmf.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 PrintWmf : public PrintMetafile +{ + 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); + +public: + PrintWmf(); + + /* 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_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(); + int create_pen(SPStyle const *style, const Geom::Affine &transform); + void destroy_pen(); +}; + +} /* namespace Internal */ +} /* namespace Extension */ +} /* namespace Inkscape */ + + +#endif /* __INKSCAPE_EXTENSION_INTERNAL_PRINT_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/file.cpp b/src/file.cpp index bb66e3330..caea05d95 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -128,21 +128,46 @@ SPDesktop *sp_file_new(const Glib::ustring &templ) { SPDocument *doc = SPDocument::createNewDoc( !templ.empty() ? templ.c_str() : 0 , TRUE, true ); g_return_val_if_fail(doc != NULL, NULL); + + // Remove all the template info from xml tree + Inkscape::XML::Node *myRoot = doc->getReprRoot(); + Inkscape::XML::Node *nodeToRemove = sp_repr_lookup_name(myRoot, "inkscape:_templateinfo"); + if (nodeToRemove != NULL){ + sp_repr_unparent(nodeToRemove); + delete nodeToRemove; + DocumentUndo::clearUndo(doc); + } + + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (desktop) { + desktop->setWaitingCursor(); + } + + SPDocument *existing = desktop ? sp_desktop_document(desktop) : NULL; + + if (existing && existing->virgin) { + // If the current desktop is empty, open the document there + doc->ensureUpToDate(); // TODO this will trigger broken link warnings, etc. + desktop->change_document(doc); + doc->emitResizedSignal(doc->getWidth().value("px"), doc->getHeight().value("px")); + } else { + // create a whole new desktop and window + SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); // TODO this will trigger broken link warnings, etc. + g_return_val_if_fail(dtw != NULL, NULL); + sp_create_window(dtw, TRUE); + desktop = static_cast<SPDesktop *>(dtw->view); + } - SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); - g_return_val_if_fail(dtw != NULL, NULL); doc->doUnref(); - sp_create_window(dtw, TRUE); - SPDesktop *dt = static_cast<SPDesktop *>(dtw->view); - sp_namedview_window_from_document(dt); - sp_namedview_update_layers_from_document(dt); + sp_namedview_window_from_document(desktop); + sp_namedview_update_layers_from_document(desktop); #ifdef WITH_DBUS - Inkscape::Extension::Dbus::dbus_init_desktop_interface(dt); + Inkscape::Extension::Dbus::dbus_init_desktop_interface(desktop); #endif - return dt; + return desktop; } Glib::ustring sp_file_default_template_uri() @@ -200,6 +225,7 @@ SPDesktop* sp_file_new_default() return desk; } + /*###################### ## D E L E T E ######################*/ @@ -232,7 +258,8 @@ sp_file_exit() */ bool sp_file_open(const Glib::ustring &uri, Inkscape::Extension::Extension *key, - bool add_to_recent, bool replace_empty) + bool add_to_recent, + bool replace_empty) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop) { @@ -257,6 +284,7 @@ bool sp_file_open(const Glib::ustring &uri, } if (doc) { + SPDocument *existing = desktop ? sp_desktop_document(desktop) : NULL; if (existing && existing->virgin && replace_empty) { @@ -273,7 +301,7 @@ bool sp_file_open(const Glib::ustring &uri, doc->virgin = FALSE; - // everyone who cares now has a reference, get rid of ours + // everyone who cares now has a reference, get rid of our`s doc->doUnref(); // resize the window to match the document properties diff --git a/src/file.h b/src/file.h index eeb4c6fc5..682ca422e 100644 --- a/src/file.h +++ b/src/file.h @@ -62,6 +62,7 @@ void sp_file_exit (void); /** * Opens a new file and window from the given URI */ + bool sp_file_open( const Glib::ustring &uri, Inkscape::Extension::Extension *key, diff --git a/src/helper/CMakeLists.txt b/src/helper/CMakeLists.txt index 2e1e724f5..b0e473ddd 100644 --- a/src/helper/CMakeLists.txt +++ b/src/helper/CMakeLists.txt @@ -17,10 +17,7 @@ set(helper_SRC pixbuf-ops.cpp png-write.cpp stock-items.cpp - unit-menu.cpp - units.cpp #units-test.cpp - unit-tracker.cpp window.cpp # we generate this file and it's .h counter-part @@ -38,10 +35,6 @@ set(helper_SRC pixbuf-ops.h png-write.h stock-items.h - unit-menu.h - unit-tracker.h - units-test.h - units.h window.h ) diff --git a/src/helper/geom.cpp b/src/helper/geom.cpp index 879026521..df0e4fcf2 100644 --- a/src/helper/geom.cpp +++ b/src/helper/geom.cpp @@ -21,6 +21,7 @@ #include <2geom/rect.h> #include <2geom/coord.h> #include <2geom/sbasis-to-bezier.h> +#include <math.h> // for M_PI using Geom::X; using Geom::Y; @@ -491,6 +492,315 @@ pathv_to_linear_and_cubic_beziers( Geom::PathVector const &pathv ) return output; } +/* + * Converts all segments in all paths to Geom::LineSegment. There is an intermediate + * stage where some may be converted to beziers. maxdisp is the maximum displacement from + * the line segment to the bezier curve. + * + * This is NOT a terribly fast method, but it should give a solution close to the one with the + * fewest points. + */ +Geom::PathVector +pathv_to_linear( Geom::PathVector const &pathv, double maxdisp) +{ + Geom::PathVector output; + Geom::PathVector tmppath = pathv_to_linear_and_cubic_beziers(pathv); + + // Now all path segments are either already lines, or they are beziers. + + for (Geom::PathVector::const_iterator pit = tmppath.begin(); pit != tmppath.end(); ++pit) { + output.push_back( Geom::Path() ); + output.back().start( pit->initialPoint() ); + output.back().close( pit->closed() ); + + for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) { + if (is_straight_curve(*cit)) { + Geom::LineSegment ls(cit->initialPoint(), cit->finalPoint()); + output.back().append(ls); + } + else { /* all others must be Bezier curves */ + Geom::BezierCurve const *curve = dynamic_cast<Geom::BezierCurve const *>(&*cit); + Geom::CubicBezier b((*curve)[0], (*curve)[1], (*curve)[2], (*curve)[3]); + std::vector<Geom::Point> bzrpoints = b.points(); + Geom::Point A = bzrpoints[0]; + Geom::Point B = bzrpoints[1]; + Geom::Point C = bzrpoints[2]; + Geom::Point D = bzrpoints[3]; + std::vector<Geom::Point> pointlist; + pointlist.push_back(A); + recursive_bezier4( + A[X], A[Y], + B[X], B[Y], + C[X], C[Y], + D[X], D[Y], + pointlist, + 0); + pointlist.push_back(D); + Geom::Point r0; + Geom::Point r1 = pointlist[0]; + for (unsigned int i=1; i<pointlist.size();i++){ + r0 = r1; + r1 = pointlist[i]; + Geom::LineSegment ls(r0, r1); + output.back().append(ls); + } + pointlist.clear(); + } + } + } + + return output; +} + +// The next routine is modified from curv4_div::recursive_bezier from file agg_curves.cpp +//---------------------------------------------------------------------------- +// Anti-Grain Geometry (AGG) - Version 2.5 +// A high quality rendering engine for C++ +// Copyright (C) 2002-2006 Maxim Shemanarev +// Contact: mcseem@antigrain.com +// mcseemagg@yahoo.com +// http://antigrain.com +// +// AGG is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// AGG is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with AGG; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +// MA 02110-1301, USA. +//---------------------------------------------------------------------------- +void +recursive_bezier4(const double x1, const double y1, + const double x2, const double y2, + const double x3, const double y3, + const double x4, const double y4, + std::vector<Geom::Point> &m_points, + int level) + { + // some of these should be parameters, but do it this way for now. + const double curve_collinearity_epsilon = 1e-30; + const double curve_angle_tolerance_epsilon = 0.01; + double m_cusp_limit = 0.0; + double m_angle_tolerance = 0.0; + double m_approximation_scale = 1.0; + double m_distance_tolerance_square = 0.5 / m_approximation_scale; + m_distance_tolerance_square *= m_distance_tolerance_square; + enum curve_recursion_limit_e { curve_recursion_limit = 32 }; +#define calc_sq_distance(A,B,C,D) ((A-C)*(A-C) + (B-D)*(B-D)) + + if(level > curve_recursion_limit) + { + return; + } + + + // Calculate all the mid-points of the line segments + //---------------------- + double x12 = (x1 + x2) / 2; + double y12 = (y1 + y2) / 2; + double x23 = (x2 + x3) / 2; + double y23 = (y2 + y3) / 2; + double x34 = (x3 + x4) / 2; + double y34 = (y3 + y4) / 2; + double x123 = (x12 + x23) / 2; + double y123 = (y12 + y23) / 2; + double x234 = (x23 + x34) / 2; + double y234 = (y23 + y34) / 2; + double x1234 = (x123 + x234) / 2; + double y1234 = (y123 + y234) / 2; + + + // Try to approximate the full cubic curve by a single straight line + //------------------ + double dx = x4-x1; + double dy = y4-y1; + + double d2 = fabs(((x2 - x4) * dy - (y2 - y4) * dx)); + double d3 = fabs(((x3 - x4) * dy - (y3 - y4) * dx)); + double da1, da2, k; + + switch((int(d2 > curve_collinearity_epsilon) << 1) + + int(d3 > curve_collinearity_epsilon)) + { + case 0: + // All collinear OR p1==p4 + //---------------------- + k = dx*dx + dy*dy; + if(k == 0) + { + d2 = calc_sq_distance(x1, y1, x2, y2); + d3 = calc_sq_distance(x4, y4, x3, y3); + } + else + { + k = 1 / k; + da1 = x2 - x1; + da2 = y2 - y1; + d2 = k * (da1*dx + da2*dy); + da1 = x3 - x1; + da2 = y3 - y1; + d3 = k * (da1*dx + da2*dy); + if(d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) + { + // Simple collinear case, 1---2---3---4 + // We can leave just two endpoints + return; + } + if(d2 <= 0) d2 = calc_sq_distance(x2, y2, x1, y1); + else if(d2 >= 1) d2 = calc_sq_distance(x2, y2, x4, y4); + else d2 = calc_sq_distance(x2, y2, x1 + d2*dx, y1 + d2*dy); + + if(d3 <= 0) d3 = calc_sq_distance(x3, y3, x1, y1); + else if(d3 >= 1) d3 = calc_sq_distance(x3, y3, x4, y4); + else d3 = calc_sq_distance(x3, y3, x1 + d3*dx, y1 + d3*dy); + } + if(d2 > d3) + { + if(d2 < m_distance_tolerance_square) + { + m_points.push_back(Geom::Point(x2, y2)); + return; + } + } + else + { + if(d3 < m_distance_tolerance_square) + { + m_points.push_back(Geom::Point(x3, y3)); + return; + } + } + break; + + case 1: + // p1,p2,p4 are collinear, p3 is significant + //---------------------- + if(d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + if(m_angle_tolerance < curve_angle_tolerance_epsilon) + { + m_points.push_back(Geom::Point(x23, y23)); + return; + } + + // Angle Condition + //---------------------- + da1 = fabs(atan2(y4 - y3, x4 - x3) - atan2(y3 - y2, x3 - x2)); + if(da1 >= M_PI) da1 = 2*M_PI - da1; + + if(da1 < m_angle_tolerance) + { + m_points.push_back(Geom::Point(x2, y2)); + m_points.push_back(Geom::Point(x3, y3)); + return; + } + + if(m_cusp_limit != 0.0) + { + if(da1 > m_cusp_limit) + { + m_points.push_back(Geom::Point(x3, y3)); + return; + } + } + } + break; + + case 2: + // p1,p3,p4 are collinear, p2 is significant + //---------------------- + if(d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + if(m_angle_tolerance < curve_angle_tolerance_epsilon) + { + m_points.push_back(Geom::Point(x23, y23)); + return; + } + + // Angle Condition + //---------------------- + da1 = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1)); + if(da1 >= M_PI) da1 = 2*M_PI - da1; + + if(da1 < m_angle_tolerance) + { + m_points.push_back(Geom::Point(x2, y2)); + m_points.push_back(Geom::Point(x3, y3)); + return; + } + + if(m_cusp_limit != 0.0) + { + if(da1 > m_cusp_limit) + { + m_points.push_back(Geom::Point(x2, y2)); + return; + } + } + } + break; + + case 3: + // Regular case + //----------------- + if((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + // If the curvature doesn't exceed the distance_tolerance value + // we tend to finish subdivisions. + //---------------------- + if(m_angle_tolerance < curve_angle_tolerance_epsilon) + { + m_points.push_back(Geom::Point(x23, y23)); + return; + } + + // Angle & Cusp Condition + //---------------------- + k = atan2(y3 - y2, x3 - x2); + da1 = fabs(k - atan2(y2 - y1, x2 - x1)); + da2 = fabs(atan2(y4 - y3, x4 - x3) - k); + if(da1 >= M_PI) da1 = 2*M_PI - da1; + if(da2 >= M_PI) da2 = 2*M_PI - da2; + + if(da1 + da2 < m_angle_tolerance) + { + // Finally we can stop the recursion + //---------------------- + m_points.push_back(Geom::Point(x23, y23)); + return; + } + + if(m_cusp_limit != 0.0) + { + if(da1 > m_cusp_limit) + { + m_points.push_back(Geom::Point(x2, y2)); + return; + } + + if(da2 > m_cusp_limit) + { + m_points.push_back(Geom::Point(x3, y3)); + return; + } + } + } + break; + } + + // Continue subdivision + //---------------------- + recursive_bezier4(x1, y1, x12, y12, x123, y123, x1234, y1234, m_points, level + 1); + recursive_bezier4(x1234, y1234, x234, y234, x34, y34, x4, y4, m_points, level + 1); +} + /** * rounds all corners of the rectangle 'outwards', i.e. x0 and y0 are floored, x1 and y1 are ceiled. diff --git a/src/helper/geom.h b/src/helper/geom.h index ecee58ec2..caff95733 100644 --- a/src/helper/geom.h +++ b/src/helper/geom.h @@ -26,6 +26,11 @@ void pathv_matrix_point_bbox_wind_distance ( Geom::PathVector const & pathv, Geo Geom::Coord tolerance, Geom::Rect const *viewbox); Geom::PathVector pathv_to_linear_and_cubic_beziers( Geom::PathVector const &pathv ); +Geom::PathVector pathv_to_linear( Geom::PathVector const &pathv, double maxdisp ); +void recursive_bezier4(const double x1, const double y1, const double x2, const double y2, + const double x3, const double y3, const double x4, const double y4, + std::vector<Geom::Point> &pointlist, + int level); void round_rectangle_outwards(Geom::Rect & rect); diff --git a/src/interface.cpp b/src/interface.cpp index 98c30bf3c..026890c67 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -717,13 +717,6 @@ sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/) g_free(uri); } -static void -sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri) -{ - sp_file_new(uri); -} - - static bool compare_file_basenames(gchar const *a, gchar const *b) { bool rc; @@ -752,91 +745,6 @@ compare_file_basenames(gchar const *a, gchar const *b) { } static void -sp_menu_get_svg_filenames_from_dir(gchar const *dirname, std::list<gchar const*> *files) -{ - if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) { - GError *err = 0; - GDir *dir = g_dir_open(dirname, 0, &err); - - if (dir) { - for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) { - if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz")) { - continue; // skip non-svg files - } - - { - gchar *basename = g_path_get_basename(file); - if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default.")) { - g_free(basename); - basename = 0; - continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already - } - g_free(basename); - basename = 0; - } - - gchar const *filepath = g_build_filename(dirname, file, NULL); - files->push_front(filepath); - } - g_dir_close(dir); - } - } - - files->sort(compare_file_basenames); -} - -static void -sp_menu_add_filenames_to_menu(GtkWidget *menu, Inkscape::UI::View::View *view, std::list<gchar const*> *files) -{ - if (!files->empty()) { - GtkWidget *sep = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), sep); - } - - for(std::list<gchar const*>::iterator it=files->begin(); it != files->end(); ++it) { - gchar const *filepath = *it; - gchar const *file = g_path_get_basename(filepath); - gchar *dupfile = g_strndup(file, strlen(file) - 4); - gchar *filename = g_filename_to_utf8(dupfile, -1, NULL, NULL, NULL); - g_free(dupfile); - - GtkWidget *item = gtk_menu_item_new_with_label(filename); - g_free(filename); - - gtk_widget_show(item); - // how does "filepath" ever get freed? - g_signal_connect(G_OBJECT(item), - "activate", - G_CALLBACK(sp_file_new_from_template), - (gpointer) filepath); - - if (view) { - // set null tip for now; later use a description from the template file - g_object_set_data(G_OBJECT(item), "view", (gpointer) view); - g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL ); - g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL); - } - - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - } - -} -static void -sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view) -{ - // user's local dir - std::list<gchar const*> userfiles; - sp_menu_get_svg_filenames_from_dir(profile_path("templates"), &userfiles); - sp_menu_add_filenames_to_menu(menu, view, &userfiles); - - // system templates dir - std::list<gchar const*> templatefiles; - sp_menu_get_svg_filenames_from_dir(INKSCAPE_TEMPLATESDIR, &templatefiles); - sp_menu_add_filenames_to_menu(menu, view, &templatefiles); - -} - -static void sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view) { //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu", @@ -996,10 +904,7 @@ static void sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, I gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); continue; } - if (!strcmp(menu_pntr->name(), "template-list")) { - sp_menu_append_new_templates(menu, view); - continue; - } + if (!strcmp(menu_pntr->name(), "recent-file-list")) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp index 61225ad0c..fd0cdd3d4 100644 --- a/src/libnrtype/FontInstance.cpp +++ b/src/libnrtype/FontInstance.cpp @@ -675,6 +675,41 @@ bool font_instance::FontMetrics(double &ascent,double &descent,double &leading) return true; } +bool font_instance::FontDecoration( + double &underline_position, double &underline_thickness, + double &linethrough_position, double &linethrough_thickness +){ + if ( pFont == NULL ) { + return false; + } + InitTheFace(); + if ( theFace == NULL ) { + return false; + } +#ifdef USE_PANGO_WIN32 + OUTLINETEXTMETRIC otm; + if ( !GetOutlineTextMetrics(daddy->hScreenDC,sizeof(otm),&otm) ) { + return false; + } + double scale=1.0/daddy->fontSize; + underline_position = fabs(otm.otmUnderscorePosition *scale); + underline_thickness = fabs(otm.otmUnderscoreSize *scale); + linethrough_position = fabs(otm.otmStrikeoutPosition *scale); + linethrough_thickness = fabs(otm.otmStrikeoutSize *scale); +#else + if ( theFace->units_per_EM == 0 ) { + return false; // bitmap font + } + underline_position = fabs(((double)theFace->underline_position )/((double)theFace->units_per_EM)); + underline_thickness = fabs(((double)theFace->underline_thickness)/((double)theFace->units_per_EM)); + // there is no specific linethrough information, mock it up from other font fields + linethrough_position = fabs(((double)theFace->ascender / 3.0 )/((double)theFace->units_per_EM)); + linethrough_thickness = fabs(((double)theFace->underline_thickness)/((double)theFace->units_per_EM)); +#endif + return true; +} + + bool font_instance::FontSlope(double &run, double &rise) { run = 0.0; diff --git a/src/libnrtype/Layout-TNG-Compute.cpp b/src/libnrtype/Layout-TNG-Compute.cpp index 7e684e7e3..1b2704a7e 100644 --- a/src/libnrtype/Layout-TNG-Compute.cpp +++ b/src/libnrtype/Layout-TNG-Compute.cpp @@ -120,8 +120,8 @@ class Layout::Calculator BrokenSpan. */ struct UnbrokenSpan { PangoGlyphString *glyph_string; - int pango_item_index; /// index into _para.pango_items, or -1 if this is style only - unsigned input_index; /// index into Layout::_input_stream + int pango_item_index; /// index into _para.pango_items, or -1 if this is style only + unsigned input_index; /// index into Layout::_input_stream Glib::ustring::const_iterator input_stream_first_character; double font_size; LineHeight line_height; /// This is not the CSS line-height attribute! @@ -213,6 +213,43 @@ class Layout::Calculator /* *********************************************************************************************************/ // Per-line functions +/** + * For debugging, not called in distributed code + * + * Input: para->first_input_index, para->pango_items + */ +static void dumpPangoItemsOut(ParagraphInfo *para){ + std::cout << "Pango items: " << para->pango_items.size() << std::endl; + for(unsigned pidx = 0 ; pidx < para->pango_items.size(); pidx++){ + std::cout + << "idx: " << pidx + << " offset: " + << para->pango_items[pidx].item->offset + << " length: " + << para->pango_items[pidx].item->length + << std::endl; + } +} + +/** + * For debugging, not called in distributed code + * + * Input: para->first_input_index, para->pango_items + */ +static void dumpUnbrokenSpans(ParagraphInfo *para){ + std::cout << "Unbroken Spans: " << para->unbroken_spans.size() << std::endl; + for(unsigned uidx = 0 ; uidx < para->unbroken_spans.size(); uidx++){ + std::cout + << "idx: " << uidx + << " pango_item_index: " << para->unbroken_spans[uidx].pango_item_index + << " input_index: " << para->unbroken_spans[uidx].input_index + << " char_index_in_para: " << para->unbroken_spans[uidx].char_index_in_para + << " text_bytes: " << para->unbroken_spans[uidx].text_bytes + << std::endl; + } +} + + bool _goToNextWrapShape(); @@ -246,8 +283,10 @@ class Layout::Calculator { span->setZero(); - if (span->start.iter_span->dx._set && span->start.char_byte == 0) - span->width += span->start.iter_span->dx.computed; + if (span->start.iter_span->dx._set && span->start.char_byte == 0){ + if(para.direction == RIGHT_TO_LEFT){ span->width -= span->start.iter_span->dx.computed; } + else { span->width += span->start.iter_span->dx.computed; } + } if (span->start.iter_span->pango_item_index == -1) { // if this is a style-only span there's no text in it @@ -510,15 +549,19 @@ class Layout::Calculator x = 0.0; } else { direction_sign = -1.0; - if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()) + if (para.alignment == FULL && !_flow._input_wrap_shapes.empty()){ x = it_chunk->scanrun_width; - else + } + else { x = it_chunk->text_width; + } } for (std::vector<BrokenSpan>::const_iterator it_span = it_chunk->broken_spans.begin() ; it_span != it_chunk->broken_spans.end() ; it_span++) { // begin adding spans to the list UnbrokenSpan const &unbroken_span = *it_span->start.iter_span; + double x_in_span_last = 0.0; // set at the END when a new cluster starts + double x_in_span = 0.0; // set from the preceding at the START when a new cluster starts. if (it_span->start.char_byte == 0) { // Start of an unbroken span, we might have dx, dy or rotate still to process @@ -535,7 +578,6 @@ class Layout::Calculator } Layout::Span new_span; - double x_in_span = 0.0; new_span.in_chunk = _flow._chunks.size() - 1; new_span.line_height = unbroken_span.line_height; @@ -581,14 +623,19 @@ class Layout::Calculator InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_flow._input_stream[unbroken_span.input_index]); Glib::ustring::const_iterator iter_source_text = Glib::ustring::const_iterator(unbroken_span.input_stream_first_character.base() + it_span->start.char_byte) ; unsigned char_index_in_unbroken_span = it_span->start.char_index; - unsigned cluster_start_char_index = _flow._characters.size(); - double font_size_multiplier = new_span.font_size / (PANGO_SCALE * _font_factory_size_multiplier); + double font_size_multiplier = new_span.font_size / (PANGO_SCALE * _font_factory_size_multiplier); + int log_cluster_size_glyphs = 0; // Number of glyphs in this log_cluster + int log_cluster_size_chars = 0; // Number of characters in this log_cluster + unsigned end_byte = 0; for (unsigned glyph_index = it_span->start_glyph_index ; glyph_index < it_span->end_glyph_index ; glyph_index++) { - unsigned char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base(); - if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start) - cluster_start_char_index = _flow._characters.size(); - + unsigned char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base(); + int newcluster = 0; + if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start){ + newcluster = 1; + x_in_span = x_in_span_last; + } + if (unbroken_span.glyph_string->log_clusters[glyph_index] < (int)unbroken_span.text_bytes && *iter_source_text == UNICODE_SOFT_HYPHEN && glyph_index + 1 != it_span->end_glyph_index) { @@ -611,7 +658,7 @@ class Layout::Calculator // create the Layout::Glyph Layout::Glyph new_glyph; new_glyph.glyph = unbroken_span.glyph_string->glyphs[glyph_index].glyph; - new_glyph.in_character = cluster_start_char_index; + new_glyph.in_character = _flow._characters.size(); new_glyph.rotation = glyph_rotate; /* put something like this back in when we do glyph-rotation-horizontal/vertical @@ -652,28 +699,42 @@ class Layout::Calculator } new_glyph.x -= cluster_width; } - _flow._glyphs.push_back(new_glyph); + _flow._glyphs.push_back(new_glyph); // create the Layout::Character(s) double advance_width = new_glyph.width; - unsigned end_byte; - if (glyph_index == (unsigned)unbroken_span.glyph_string->num_glyphs - 1) - end_byte = it_span->start.iter_span->text_bytes; - else { - // output chars for the whole cluster that is commenced by this glyph - if (unbroken_span.glyph_string->glyphs[glyph_index].attr.is_cluster_start) { - int next_cluster_glyph_index = glyph_index + 1; - while (next_cluster_glyph_index < unbroken_span.glyph_string->num_glyphs - && !unbroken_span.glyph_string->glyphs[next_cluster_glyph_index].attr.is_cluster_start) - next_cluster_glyph_index++; - if (next_cluster_glyph_index < unbroken_span.glyph_string->num_glyphs) - end_byte = unbroken_span.glyph_string->log_clusters[next_cluster_glyph_index]; - else - end_byte = it_span->start.iter_span->text_bytes; - } else - end_byte = char_byte; // don't output any chars if we're not at the start of a cluster + if (newcluster){ + // find where the text ends for this log_cluster + end_byte = it_span->start.iter_span->text_bytes; // Upper limit + for(unsigned next_glyph_index = glyph_index+1; next_glyph_index < it_span->end_glyph_index; next_glyph_index++){ + if(unbroken_span.glyph_string->glyphs[next_glyph_index].attr.is_cluster_start){ + end_byte = unbroken_span.glyph_string->log_clusters[next_glyph_index]; + break; + } + } + // Figure out how many glyphs and characters are in the log_cluster. + log_cluster_size_glyphs = 0; + log_cluster_size_chars = 0; + for(; log_cluster_size_glyphs + glyph_index < it_span->end_glyph_index; log_cluster_size_glyphs++){ + if(unbroken_span.glyph_string->log_clusters[glyph_index ] != + unbroken_span.glyph_string->log_clusters[glyph_index + log_cluster_size_glyphs])break; + } + Glib::ustring::const_iterator lclist = iter_source_text; + unsigned lcb = char_byte; + while(lcb < end_byte){ + log_cluster_size_chars++; + lclist++; + lcb = lclist.base() - unbroken_span.input_stream_first_character.base(); + } } while (char_byte < end_byte) { + /* Hack to survive ligatures: in log_cluster keep the number of available chars >= number of glyphs remaining. + When there are no ligatures these two sizes are always the same. + */ + if(log_cluster_size_chars < log_cluster_size_glyphs){ + log_cluster_size_glyphs--; + break; + } Layout::Character new_character; new_character.in_span = _flow._spans.size(); new_character.x = x_in_span; @@ -687,23 +748,27 @@ class Layout::Calculator iter_source_text++; char_index_in_unbroken_span++; char_byte = iter_source_text.base() - unbroken_span.input_stream_first_character.base(); + log_cluster_size_chars--; } - advance_width *= direction_sign; - if (new_span.direction != para.direction) { - counter_directional_width_remaining -= advance_width; - x -= advance_width; - x_in_span -= advance_width; - } else { - x += advance_width; - x_in_span += advance_width; + if (newcluster){ + advance_width *= direction_sign; + if (new_span.direction != para.direction) { + counter_directional_width_remaining -= advance_width; + x -= advance_width; + x_in_span_last -= advance_width; + } else { + x += advance_width; + x_in_span_last += advance_width; + } } + newcluster = 0; } } else if (_flow._input_stream[unbroken_span.input_index]->Type() == CONTROL_CODE) { x += static_cast<InputStreamControlCode const *>(_flow._input_stream[unbroken_span.input_index])->width; } - new_span.x_end = new_span.x_start + x_in_span; + new_span.x_end = new_span.x_start + x_in_span_last; _flow._spans.push_back(new_span); previous_direction = new_span.direction; } @@ -879,8 +944,9 @@ void Layout::Calculator::ParagraphInfo::free() * * Input: para.first_input_index. * Output: para.direction, para.pango_items, para.char_attributes. + * Returns: the number of spans created by pango_itemize */ -void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) const +void Layout::Calculator::_buildPangoItemizationForPara(ParagraphInfo *para) const { Glib::ustring para_text; PangoAttrList *attributes_list; @@ -974,8 +1040,9 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font line_height->setZero(); *line_height_multiplier = 1.0; } - else + else { font->FontMetrics(line_height->ascent, line_height->descent, line_height->leading); + } *line_height *= font_size; // yet another borked SPStyle member that we're going to have to fix ourselves @@ -1008,6 +1075,11 @@ void Layout::Calculator::_computeFontLineHeight(font_instance *font, double font *line_height_multiplier = LINE_HEIGHT_NORMAL * font_size / line_height->total(); } +bool compareGlyphWidth(const PangoGlyphInfo &a, const PangoGlyphInfo &b) +{ + return (a.geometry.width > b.geometry.width); +} + /** * Split the paragraph into spans. Also call pango_shape() on them. @@ -1034,13 +1106,13 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const break; // stop at the end of the paragraph else if (control_code->code == ARBITRARY_GAP) { UnbrokenSpan new_span; - new_span.pango_item_index = -1; - new_span.input_index = input_index; - new_span.line_height.ascent = control_code->ascent; + new_span.pango_item_index = -1; + new_span.input_index = input_index; + new_span.line_height.ascent = control_code->ascent; new_span.line_height.descent = control_code->descent; new_span.line_height.leading = 0.0; - new_span.text_bytes = 0; - new_span.char_index_in_para = char_index_in_para; + new_span.text_bytes = 0; + new_span.char_index_in_para = char_index_in_para; para->unbroken_spans.push_back(new_span); TRACE(("add gap span %d\n", para->unbroken_spans.size() - 1)); } @@ -1123,6 +1195,13 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const g_assert( span_start_byte_in_source + new_span.text_bytes <= text_source->text->bytes() ); g_assert( memchr(text_source->text->data() + span_start_byte_in_source, '\0', static_cast<size_t>(new_span.text_bytes)) == NULL ); + /* Notes as of 4/29/13. Pango_shape is not generating English language ligatures, but it is generating + them for Hebrew (and probably other similar languages). In the case observed 3 unicode characters (a base + and 2 Mark, nonspacings) are merged into two glyphs (the base + first Mn, the 2nd Mn). All of these map + from glyph to first character of the log_cluster range. This destroys the 1:1 correspondence between + characters and glyphs. A big chunk of the conditional code which immediately follows this call + is there to clean up the resulting mess. + */ pango_shape(text_source->text->data() + span_start_byte_in_source, new_span.text_bytes, ¶->pango_items[pango_item_index].item->analysis, @@ -1134,24 +1213,55 @@ unsigned Layout::Calculator::_buildSpansForPara(ParagraphInfo *para) const // let's reverse the glyphstring on a cluster-by-cluster basis const unsigned nglyphs = new_span.glyph_string->num_glyphs; std::vector<PangoGlyphInfo> infos(nglyphs); - std::vector<gint> clusters(nglyphs); - unsigned i, cluster_start = 0; - - for (i = 0 ; i < nglyphs ; ++i) { - if (new_span.glyph_string->glyphs[i].attr.is_cluster_start) { - if (i != cluster_start) { - std::copy(&new_span.glyph_string->glyphs[cluster_start], &new_span.glyph_string->glyphs[i], infos.end() - i); - std::copy(&new_span.glyph_string->log_clusters[cluster_start], &new_span.glyph_string->log_clusters[i], clusters.end() - i); - } - cluster_start = i; + std::vector<gint> clusters(nglyphs); + unsigned i, j; + for (i = 0 ; i < nglyphs ; i++)new_span.glyph_string->glyphs[i].attr.is_cluster_start = 0; + for (i = 0 ; i < nglyphs ; i++) { + j=i; + while( (j < nglyphs-1) && + (new_span.glyph_string->log_clusters[j+1] == new_span.glyph_string->log_clusters[i]) + )j++; + /* + CAREFUL, within a log_cluster the order of glyphs may not map 1:1, or + even in the same order, to the original unicode characters!!! Among + other things, diacritical mark glyphs can end up in front of the base + character. That makes determining kerning, even approximately, difficult + later on. To resolve this somewhat sort the glyphs with the same + log_cluster into descending order by width. In theory there should be 1 + that is nonzero, and N that are zero. The order of the zero width ones + does not matter. Sort the glyphs before copying. If ligatures other than with + Mark, nonspacing are ever implemented in Pango this will screw up, for instance + changing "fi" to "if". + */ + if(j - i){ + std::sort(&(new_span.glyph_string->glyphs[i]), &(new_span.glyph_string->glyphs[j+1]), compareGlyphWidth); } - } - if (i != cluster_start) { - std::copy(&new_span.glyph_string->glyphs[cluster_start], &new_span.glyph_string->glyphs[i], infos.end() - i); - std::copy(&new_span.glyph_string->log_clusters[cluster_start], &new_span.glyph_string->log_clusters[i], clusters.end() - i); + + new_span.glyph_string->glyphs[i].attr.is_cluster_start = 1; + std::copy(&new_span.glyph_string->glyphs[ i], &new_span.glyph_string->glyphs[ j+1], infos.end() - j -1); + std::copy(&new_span.glyph_string->log_clusters[i], &new_span.glyph_string->log_clusters[j+1], clusters.end() - j -1); + i = j; } std::copy(infos.begin(), infos.end(), new_span.glyph_string->glyphs); std::copy(clusters.begin(), clusters.end(), new_span.glyph_string->log_clusters); + /* glyphs[].x_offset values are probably out of order within any log_clusters, apparently harmless */ + } + else { // ltr sections are in order but glyphs in a log_cluster following a ligature may not be. Sort, but no block swapping. + const unsigned nglyphs = new_span.glyph_string->num_glyphs; + unsigned i, j; + for (i = 0 ; i < nglyphs ; i++)new_span.glyph_string->glyphs[i].attr.is_cluster_start = 0; + for (i = 0 ; i < nglyphs ; i++) { + j=i; + while( (j < nglyphs-1) && + (new_span.glyph_string->log_clusters[j+1] == new_span.glyph_string->log_clusters[i]) + )j++; + if(j - i){ + std::sort(&(new_span.glyph_string->glyphs[i]), &(new_span.glyph_string->glyphs[j+1]), compareGlyphWidth); + } + new_span.glyph_string->glyphs[i].attr.is_cluster_start = 1; + i = j; + } + /* glyphs[].x_offset values may be out of order within any log_clusters, apparently harmless */ } new_span.pango_item_index = pango_item_index; _computeFontLineHeight(para->pango_items[pango_item_index].font, new_span.font_size, text_source->style, &new_span.line_height, &new_span.line_height_multiplier); @@ -1469,7 +1579,7 @@ bool Layout::Calculator::calculate() if (_scanline_maker == NULL) break; // we're trying to flow past the last wrap shape - _buildPangoItemizationForPara(¶); + _buildPangoItemizationForPara(¶); unsigned para_end_input_index = _buildSpansForPara(¶); if (_flow._input_stream[para.first_input_index]->Type() == TEXT_SOURCE) diff --git a/src/libnrtype/Layout-TNG-Output.cpp b/src/libnrtype/Layout-TNG-Output.cpp index 0f853c681..f7f910c2f 100644 --- a/src/libnrtype/Layout-TNG-Output.cpp +++ b/src/libnrtype/Layout-TNG-Output.cpp @@ -20,6 +20,7 @@ #include "extension/internal/cairo-render-context.h" #include "display/curve.h" #include <2geom/pathvector.h> +#include <libuemf/symbol_convert.h> namespace Inkscape { @@ -36,6 +37,49 @@ using Inkscape::Extension::Internal::CairoGlyphInfo; namespace Inkscape { namespace Text { +/* + dx array (character widths) and + ky (vertical kerning for entire span) + rtl (+1 for LTR, -1 RTL) + + are smuggled through to the EMF (ignored by others) as: + text<nul>N w1 w2 w3 ...wN<nul>y1 y2 y3 .. yN<nul><nul> + The ndx, widths, y kern, and rtl are all 7 characters wide. ndx and rtl are ints, the widths and ky are + formatted as ' 6f'. +*/ +char *smuggle_adxkyrtl_in(const char *string, int ndx, float *adx, float ky, float rtl){ + int slen = strlen(string); + /* holds: string + fake terminator (one \0) + Number of widths (ndx) + series of widths (ndx entries) + fake terminator (one \0) + y kern value (one float) + rtl value (one float) + real terminator (two \0) + */ + int newsize=slen + 1 + 7 + 7*ndx + 1 + 7 + 7 + 2; + newsize = 8*((7 + newsize)/8); // suppress valgrind messages if it is a multiple of 8 bytes??? + char *smuggle=(char *)malloc(newsize); + strcpy(smuggle,string); // text to pass, includes the first fake terminator + char *cptr = smuggle + slen + 1; // immediately after the first fake terminator + sprintf(cptr,"%07d",ndx); // number of widths to pass + cptr+=7; // advance over ndx + for(int i=0; i<ndx ; i++){ // all the widths + sprintf(cptr," %6f",adx[i]); + cptr+=7; // advance over space + width + } + *cptr='\0'; + cptr++; // second fake terminator + sprintf(cptr," %6f",ky); // y kern for span + cptr+=7; // advance over space + ky + sprintf(cptr," %6d",(int) rtl); // rtl multiplier for span + cptr+=7; // advance over rtl + *cptr++ = '\0'; // Set the real terminators + *cptr = '\0'; + return(smuggle); +} + void Layout::_clearOutputObjects() { _paragraphs.clear(); @@ -77,21 +121,66 @@ void Layout::_getGlyphTransformMatrix(int glyph_index, Geom::Affine *matrix) con void Layout::show(DrawingGroup *in_arena, Geom::OptRect const &paintbox) const { int glyph_index = 0; + double phase0 = 0.0; for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) { if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) continue; InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[_spans[span_index].in_input_stream_item]); + text_source->style->text_decoration_data.tspan_width = _spans[span_index].width(); + text_source->style->text_decoration_data.ascender = _spans[span_index].line_height.getAscent(); + text_source->style->text_decoration_data.descender = _spans[span_index].line_height.getDescent(); + text_source->style->text_decoration_data.line_gap = _spans[span_index].line_height.getLeading(); + if(!span_index || + (_chunks[_spans[span_index].in_chunk].in_line != _chunks[_spans[span_index-1].in_chunk].in_line)){ + text_source->style->text_decoration_data.tspan_line_start = true; + } + else { + text_source->style->text_decoration_data.tspan_line_start = false; + } + if((span_index == _spans.size() -1) || + (_chunks[_spans[span_index].in_chunk].in_line != _chunks[_spans[span_index+1].in_chunk].in_line)){ + text_source->style->text_decoration_data.tspan_line_end = true; + } + else { + text_source->style->text_decoration_data.tspan_line_end = false; + } + if(_spans[span_index].font){ + double underline_thickness, underline_position, line_through_thickness,line_through_position; + _spans[span_index].font->FontDecoration(underline_position, underline_thickness, line_through_position, line_through_thickness); + text_source->style->text_decoration_data.underline_thickness = underline_thickness; + text_source->style->text_decoration_data.underline_position = underline_position; + text_source->style->text_decoration_data.line_through_thickness = line_through_thickness; + text_source->style->text_decoration_data.line_through_position = line_through_position; + } + else { // can this case ever occur? + text_source->style->text_decoration_data.underline_thickness = + text_source->style->text_decoration_data.underline_position = + text_source->style->text_decoration_data.line_through_thickness = + text_source->style->text_decoration_data.line_through_position = 0.0; + } + DrawingText *nr_text = new DrawingText(in_arena->drawing()); - nr_text->setStyle(text_source->style); + bool first_line_glyph = true; while (glyph_index < (int)_glyphs.size() && _characters[_glyphs[glyph_index].in_character].in_span == span_index) { if (_characters[_glyphs[glyph_index].in_character].in_glyph != -1) { Geom::Affine glyph_matrix; _getGlyphTransformMatrix(glyph_index, &glyph_matrix); - nr_text->addComponent(_spans[span_index].font, _glyphs[glyph_index].glyph, glyph_matrix); + if(first_line_glyph && text_source->style->text_decoration_data.tspan_line_start){ + first_line_glyph = false; + phase0 = glyph_matrix.translation()[Geom::X]; + } + // save the starting coordinates for the line - these are needed for figuring out dot/dash/wave phase + (void) nr_text->addComponent(_spans[span_index].font, _glyphs[glyph_index].glyph, glyph_matrix, + _glyphs[glyph_index].width, + _spans[span_index].line_height.getAscent(), + _spans[span_index].line_height.getDescent(), + glyph_matrix.translation()[Geom::X] - phase0 + ); } glyph_index++; } + nr_text->setStyle(text_source->style); nr_text->setItemBounds(paintbox); in_arena->prependChild(nr_text); } @@ -123,27 +212,30 @@ Geom::OptRect Layout::bounds(Geom::Affine const &transform, int start, int lengt return bbox; } +/* This version is much simpler than the old one +*/ void Layout::print(SPPrintContext *ctx, Geom::OptRect const &pbox, Geom::OptRect const &dbox, Geom::OptRect const &bbox, Geom::Affine const &ctm) const { - if (_input_stream.empty()) return; +bool text_to_path = ctx->module->textToPath(); +int oldtarget = 0; +int newtarget = 0; +#define MAX_DX 2048 +float hold_dx[MAX_DX]; // For smuggling dx values (character widths) into print functions, unlikely any simple text output will be longer than this. +float ky; // For smuggling y kern value for span +int ndx = 0; +double rtl = 1.0; // 1 L->R, -1 R->L, constant across a span. 1.0 for t->b b->t??? +Geom::Affine glyph_matrix; - Direction block_progression = _blockProgression(); - bool text_to_path = ctx->module->textToPath(); - for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; ) { - if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) { - // invisible glyphs - unsigned same_character = _glyphs[glyph_index].in_character; - while (_glyphs[glyph_index].in_character == same_character) - glyph_index++; - continue; - } - Geom::Affine glyph_matrix; - Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span]; - InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]); - if (text_to_path || _path_fitted) { + if (_input_stream.empty()) return; + if (!_glyphs.size()) return; // yes, this can happen. + if (text_to_path || _path_fitted) { + for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) { + if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1)continue; //invisible glyphs + Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span]; Geom::PathVector const * pv = span.font->PathVector(_glyphs[glyph_index].glyph); + InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]); if (pv) { _getGlyphTransformMatrix(glyph_index, &glyph_matrix); Geom::PathVector temp_pv = (*pv) * glyph_matrix; @@ -152,9 +244,28 @@ void Layout::print(SPPrintContext *ctx, if (!text_source->style->stroke.isNone()) sp_print_stroke(ctx, temp_pv, ctm, text_source->style, pbox, dbox, bbox); } - glyph_index++; - } else { - Geom::Point g_pos(0,0); // all strings are output at (0,0) because we do the translation using the matrix + } + } + else { + /* index by characters, referencing glyphs and spans only as needed */ + double char_x; + int doUTN = CanUTN(); // Unicode to Nonunicode translation enabled if true + Direction block_progression = _blockProgression(); + + for (unsigned char_index = 0 ; char_index < _characters.size() ; ) { + Glib::ustring text_string; // accumulate text for record in this + Geom::Point g_pos(0,0); // all strings are output at (0,0) because we do the translation using the matrix + int glyph_index = _characters[char_index].in_glyph; + if(glyph_index == -1){ // if the character maps to an invisible glyph we cannot know its geometry, so skip it and move on + char_index++; + continue; + } + ky = _glyphs[glyph_index].y; // same value for all positions in a span + unsigned span_index = _characters[char_index].in_span; + Span const &span = _spans[span_index]; + char_x = 0.0; + Glib::ustring::const_iterator text_iter = span.input_stream_first_character; + InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]); glyph_matrix = Geom::Scale(1.0, -1.0) * (Geom::Affine)Geom::Rotate(_glyphs[glyph_index].rotation); if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) { glyph_matrix[4] = span.line(this).baseline_y + span.baseline_shift; @@ -164,38 +275,113 @@ void Layout::print(SPPrintContext *ctx, glyph_matrix[4] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x; glyph_matrix[5] = span.line(this).baseline_y + span.baseline_shift; } - Glib::ustring::const_iterator span_iter = span.input_stream_first_character; - unsigned char_index = _glyphs[glyph_index].in_character; - unsigned original_span = _characters[char_index].in_span; - while (char_index && _characters[char_index - 1].in_span == original_span) { - char_index--; - span_iter++; + switch(span.direction){ + case Layout::TOP_TO_BOTTOM: + case Layout::BOTTOM_TO_TOP: + case Layout::LEFT_TO_RIGHT: rtl = 1.0; break; + case Layout::RIGHT_TO_LEFT: rtl = -1.0; break; } + if(doUTN)oldtarget=SingleUnicodeToNon(*text_iter); // this should only ever be with a 1:1 glyph:character situation + + // accumulate a record to write - // try to output as many characters as possible in one go by detecting kerning and stopping when we encounter it - Glib::ustring span_string; - double char_x = _characters[_glyphs[glyph_index].in_character].x; - unsigned this_span_index = _characters[_glyphs[glyph_index].in_character].in_span; - do { - span_string += *span_iter; - span_iter++; - - unsigned same_character = _glyphs[glyph_index].in_character; - while (glyph_index < _glyphs.size() && _glyphs[glyph_index].in_character == same_character) { - char_x += _glyphs[glyph_index].width; - glyph_index++; + unsigned lc_index = char_index; + unsigned hold_iisi = _spans[span_index].in_input_stream_item; + while(1){ + glyph_index = _characters[lc_index].in_glyph; + if(glyph_index == -1){ // end of a line within a paragraph, for instance + lc_index++; + break; + } + + // always append if here + text_string += *text_iter; + + // figure out char widths, used by EMF, not currently used elsewhere + double cwidth; + if(lc_index == _glyphs[glyph_index].in_character){ // Glyph width is used only for the first character, these may be 0 + cwidth = rtl * _glyphs[glyph_index].width; // width might be zero + } + else { + cwidth = 0; + } + char_x += cwidth; +/* +std:: cout << "DEBUG Layout::print in while " +<< " char_index " << char_index +<< " lc_index " << lc_index +<< " character " << std::hex << (int) *text_iter << std::dec +<< " glyph_index " << glyph_index +<< " glyph_xy " << _glyphs[glyph_index].x << " , " << _glyphs[glyph_index].y +<< " span_index " << span_index +<< " hold_iisi " << hold_iisi +<< std::endl; //DEBUG +*/ + if(ndx < MAX_DX){ + hold_dx[ndx++] = fabs(cwidth); + } + else { // silently truncate any text line silly enough to be longer than MAX_DX + lc_index = _characters.size(); + break; } - } while (glyph_index < _glyphs.size() - && _path_fitted == NULL - && _characters[_glyphs[glyph_index].in_character].in_span == this_span_index - && fabs(char_x - _characters[_glyphs[glyph_index].in_character].x) < 1e-4); + + + // conditions that prevent this character from joining the record + lc_index++; + if(lc_index >= _characters.size()) break; // nothing more to process, so it must be the end of the record + text_iter++; + if(doUTN)newtarget=SingleUnicodeToNon(*text_iter); // this should only ever be with a 1:1 glyph:character situation + if(newtarget != oldtarget)break; // change in unicode to nonunicode translation status + // MUST exit on any major span change, but not on some little events, like a font substitution event irrelvant for the file save + unsigned next_span_index = _characters[lc_index].in_span; + if(span_index != next_span_index){ + /* on major changes break out of loop. + 1st case usually indicates an entire input line has been processed (out of several in a paragraph) + 2nd case usually indicates that a format change within a line (font/size/color/etc) is present. + */ +/* +std:: cout << "DEBUG Layout::print in while --- " +<< " char_index " << char_index +<< " lc_index " << lc_index +<< " cwidth " << cwidth +<< " _char.x (next) " << (lc_index < _characters.size() ? _characters[lc_index].x : -1) +<< " char_x (end this)" << char_x +<< " diff " << fabs(char_x - _characters[lc_index].x) +<< " oldy " << ky +<< " nexty " << _glyphs[_characters[lc_index].in_glyph].y +<< std::endl; //DEBUG +*/ + if(hold_iisi != _spans[next_span_index].in_input_stream_item)break; // major change, font, size, color, etc, must exit + if(fabs(char_x - _spans[next_span_index].x_start) >= 1e-4)break; // xkerning change + if(ky != _glyphs[_characters[lc_index].in_glyph].y)break; // ykerning change + /* + None of the above? Then this is a minor "pangito", update span_index and keep going. + The font used by the display may have failed over, but print does not care and can continue to use + whatever was specified in the XML. + */ + span_index = next_span_index; + text_iter = _spans[span_index].input_stream_first_character; + } + + } + // write it sp_print_bind(ctx, glyph_matrix, 1.0); - sp_print_text(ctx, span_string.c_str(), g_pos, text_source->style); + + // the dx array is smuggled through to the EMF driver (ignored by others) as: + // text<nul>w1 w2 w3 ...wn<nul><nul> + // where the widths are floats 7 characters wide, including the space + + char *smuggle_string=smuggle_adxkyrtl_in(text_string.c_str(),ndx, &hold_dx[0], ky, rtl); + sp_print_text(ctx, smuggle_string, g_pos, text_source->style); + free(smuggle_string); sp_print_release(ctx); + ndx=0; + char_index = lc_index; } } } + #ifdef HAVE_CAIRO_PDF void Layout::showGlyphs(CairoRenderContext *ctx) const { @@ -348,10 +534,38 @@ Glib::ustring Layout::getFontFamily(unsigned span_index) const Glib::ustring Layout::dumpAsText() const { Glib::ustring result; + char line[256]; + + Glib::ustring::const_iterator icc; + + snprintf(line, sizeof(line), "spans %zu\n", _spans.size()); + result += line; + snprintf(line, sizeof(line), "chars %zu\n", _characters.size()); + result += line; + snprintf(line, sizeof(line), "glyphs %zu\n", _glyphs.size()); + result += line; + unsigned lastspan=5000; + if(_characters.size() > 1){ + for(unsigned j = 0; j < _characters.size() ; j++){ + if(lastspan != _characters[j].in_span){ + lastspan = _characters[j].in_span; + icc = _spans[lastspan].input_stream_first_character; + } + snprintf(line, sizeof(line), "char %4d: '%c' 0x%4.4x x=%8.4f glyph=%3d span=%3d\n", j, *icc, *icc, _characters[j].x, _characters[j].in_glyph, _characters[j].in_span); + result += line; + icc++; + } + } + if(_glyphs.size()){ + for(unsigned j = 0; j < _glyphs.size() ; j++){ + snprintf(line, sizeof(line), "glyph %4d: %4d (%8.4f,%8.4f) rot=%8.4f cx=%8.4f char=%4d\n", + j, _glyphs[j].glyph, _glyphs[j].x, _glyphs[j].y, _glyphs[j].rotation, _glyphs[j].width, _glyphs[j].in_character); + result += line; + } + } for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) { - char line[256]; - snprintf(line, sizeof(line), "==== span %d\n", span_index); + snprintf(line, sizeof(line), "==== span %d \n", span_index); result += line; snprintf(line, sizeof(line), " in para %d (direction=%s)\n", _lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph, direction_to_text(_paragraphs[_lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph].base_direction)); @@ -385,8 +599,8 @@ Glib::ustring Layout::dumpAsText() const if (_characters[char_index].in_span != span_index) continue; if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) { snprintf(line, sizeof(line), " %d: control x=%f flags=%03x glyph=%d\n", char_index, _characters[char_index].x, *u.uattr, _characters[char_index].in_glyph); - } else { - snprintf(line, sizeof(line), " %d: '%c' x=%f flags=%03x glyph=%d\n", char_index, *iter_char, _characters[char_index].x, *u.uattr, _characters[char_index].in_glyph); + } else { // some text has empty tspans, iter_char cannot be dereferenced + snprintf(line, sizeof(line), " %d: '%c' 0x%4.4x x=%f flags=%03x glyph=%d\n", char_index, *iter_char, *iter_char, _characters[char_index].x, *u.uattr, _characters[char_index].in_glyph); iter_char++; } result += line; diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index 0f5f08a53..573301c69 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -568,6 +568,9 @@ public: inline void setZero() {ascent = descent = leading = 0.0;} inline LineHeight& operator*=(double x) {ascent *= x; descent *= x; leading *= x; return *this;} void max(LineHeight const &other); /// makes this object contain the largest of all three members between this object and other + inline double getAscent() const {return ascent; } + inline double getDescent() const {return descent; } + inline double getLeading() const {return leading; } }; /// see _enum_converter() @@ -717,6 +720,7 @@ private: float font_size; float x_start; /// relative to the start of the chunk float x_end; /// relative to the start of the chunk + inline float width() const {return std::abs(x_start - x_end);} LineHeight line_height; double baseline_shift; /// relative to the line's baseline Direction direction; /// See CSS3 section 3.2. Either rtl or ltr diff --git a/src/libnrtype/font-instance.h b/src/libnrtype/font-instance.h index d00569984..ef10a04d0 100644 --- a/src/libnrtype/font-instance.h +++ b/src/libnrtype/font-instance.h @@ -59,6 +59,8 @@ public: double Advance(int glyph_id, bool vertical); // nominal advance of the font. bool FontMetrics(double &ascent, double &descent, double &leading); + bool FontDecoration(double &underline_position, double &underline_thickness, + double &linethrough_position, double &linethrough_thickness); bool FontSlope(double &run, double &rise); // for generating slanted cursors for oblique fonts Geom::OptRect BBox(int glyph_id); diff --git a/src/libuemf/CMakeLists.txt b/src/libuemf/CMakeLists.txt new file mode 100644 index 000000000..dbf9fb36c --- /dev/null +++ b/src/libuemf/CMakeLists.txt @@ -0,0 +1,24 @@ + +set(libuemf_SRC + symbol_convert.c + uemf.c + uemf_endian.c + uemf_print.c + uemf_utf.c + uwmf.c + uwmf_endian.c + uwmf_print.c + + # ------- + # Headers + symbol_convert.h + uemf.h + uemf_endian.h + uemf_print.h + uemf_utf.h + uwmf.h + uwmf_endian.h + uwmf_print.h +) + +add_inkscape_lib(uemf_LIB "${libuemf_SRC}") diff --git a/src/libuemf/Makefile_insert b/src/libuemf/Makefile_insert new file mode 100644 index 000000000..ca14ed19f --- /dev/null +++ b/src/libuemf/Makefile_insert @@ -0,0 +1,24 @@ +## Makefile.am fragment sourced by src/Makefile.am. + +libuemf/all: libuemf.a + +libuemf/clean: + rm -f libuemf/libuemf.a $(libuemf_libuemf_a_OBJECTS) + +libuemf_libuemf_a_SOURCES = \ + libuemf/uemf.c \ + libuemf/uemf.h \ + libuemf/uemf_endian.c \ + libuemf/uemf_endian.h \ + libuemf/uemf_print.c \ + libuemf/uemf_print.h \ + libuemf/uemf_utf.c \ + libuemf/uemf_utf.h \ + libuemf/uwmf.c \ + libuemf/uwmf.h \ + libuemf/uwmf_endian.c \ + libuemf/uwmf_endian.h \ + libuemf/uwmf_print.c \ + libuemf/uwmf_print.h \ + libuemf/symbol_convert.c \ + libuemf/symbol_convert.h diff --git a/src/libuemf/README b/src/libuemf/README new file mode 100644 index 000000000..53e2469e3 --- /dev/null +++ b/src/libuemf/README @@ -0,0 +1,5 @@ +Theis directory contains the libUEMF library, +a cross-platform library for Windows Metafile and Enhanced Metafile +handling, created by David Mathog. + +http://sourceforge.net/projects/libuemf/ diff --git a/src/libuemf/makefile.in b/src/libuemf/makefile.in new file mode 100644 index 000000000..f1a595e1d --- /dev/null +++ b/src/libuemf/makefile.in @@ -0,0 +1,17 @@ +# Convenience stub makefile to call the real Makefile. + +@SET_MAKE@ + +OBJEXT = @OBJEXT@ + +# Explicit so that it's the default rule. +all: + cd .. && $(MAKE) libuemf/all + +clean %.a %.$(OBJEXT): + cd .. && $(MAKE) libuemf/$@ + +.PHONY: all clean + +.SUFFIXES: +.SUFFIXES: .a .$(OBJEXT) diff --git a/src/libuemf/symbol_convert.c b/src/libuemf/symbol_convert.c new file mode 100644 index 000000000..650f4332d --- /dev/null +++ b/src/libuemf/symbol_convert.c @@ -0,0 +1,1008 @@ +/** @file + * @brief Windows-only Enhanced Metafile input and output. + */ +/* Authors: + * David mathog <mathog@caltech.edu> + * + * Copyright (C) 2012 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + * + * References: + * see unicode-convert.h + * + * v1.4 08/21/2012 Changed this so that the incoming routines use uint32_t and the outgoing use uint16_t. This gets rid + * of wchar_t, which was different sizes on windows/linux, and required lots of ifdef's elsewhere in the code. + * v1.3 04/03/2012 Bullets were a problem. Symbol bullet -> Times New Roman Bullet looks OK, but + * it looked bad going the other way. Changed mapping Symbol bullet to 2219 (Bullet operator, math + * symbol.) That way Symbol bullet can map in and out, while other font bullet an remain in that + * font's bullet glyph. + * v1.2 03/26/2012 Introduced bug into SingleUnicodeToNon repaired. + * v1.1 03/25/2012 Changed ampersand mapping on Wingdings (to avoid normal Ampersand mapping + * to Wingdings ampersand when not intended. Fixed access bugs for when no conversion is + * mapped in UnicodeToNon and SingleUnicodeToNon + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include "symbol_convert.h" + + +static bool hold_symb=0; // if any of these change, (re)generate the map table +static bool hold_wing=0; +static bool hold_zdng=0; +static bool hold_pua=0; +static unsigned char *from_unicode=NULL; +static unsigned char *to_font=NULL; + +/* The following tables were created from the files + adobe-dingbats.enc.gz + adobe-symbol.enc.gz + adobe-standard.enc.gz + + which came as part of the X11-font-encodings rpm on Mandriva 2010. + The original source for the data must have been Adobe. + Some also from: + ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/ADOBE/symbol.txt + http://www.csn.ul.ie/~caolan/wingdings/proposal/ + www.renderx.com/Tests/zapf-dingbats.pdf + + The intent is as follows: + + on conversion from ASCII/extended -> Unicode use the appropriate table for + the font and change font + code (symbol, zapf dingbats, wingdings). + Going the other way, set up two transfer tables, + the first is unicode -> 0-FF values, and the seond is unicode -> cvt_to_font. + These tables are filled dingbats, wingdings, then symbols, so with the rightmost one getting + precedence if both contain the symbol. + + Whereever possible do NOT map two input characters to the same output character, use a slightly + off character if it is somewhat close and disambiguates. + +v 1.0.0 14-MAR-2012, David Mathog + +*/ + +static unsigned int wingdings_convert[256]={ + 0xFFFD, // 0x00 no replacement + 0xFFFD, // 0x01 no replacement + 0xFFFD, // 0x02 no replacement + 0xFFFD, // 0x03 no replacement + 0xFFFD, // 0x04 no replacement + 0xFFFD, // 0x05 no replacement + 0xFFFD, // 0x06 no replacement + 0xFFFD, // 0x07 no replacement + 0xFFFD, // 0x08 no replacement + 0xFFFD, // 0x09 no replacement + 0xFFFD, // 0x0A no replacement + 0xFFFD, // 0x0B no replacement + 0xFFFD, // 0x0C no replacement + 0xFFFD, // 0x0D no replacement + 0xFFFD, // 0x0E no replacement + 0xFFFD, // 0x0F no replacement + 0xFFFD, // 0x10 no replacement + 0xFFFD, // 0x11 no replacement + 0xFFFD, // 0x12 no replacement + 0xFFFD, // 0x13 no replacement + 0xFFFD, // 0x14 no replacement + 0xFFFD, // 0x15 no replacement + 0xFFFD, // 0x16 no replacement + 0xFFFD, // 0x17 no replacement + 0xFFFD, // 0x18 no replacement + 0xFFFD, // 0x19 no replacement + 0xFFFD, // 0x1A no replacement + 0xFFFD, // 0x1B no replacement + 0xFFFD, // 0x1C no replacement + 0xFFFD, // 0x1D no replacement + 0xFFFD, // 0x1E no replacement + 0xFFFD, // 0x1F no replacement + 0x0020, // 0x20 SPACE + 0x270E, // 0x21 LOWER RIGHT PENCIL (close, but not exact) + 0x2702, // 0x22 BLACK SCISSORS + 0x2701, // 0x23 UPPER BLADE SCISSORS + 0xFFFD, // 0x24 no replacement + 0xFFFD, // 0x25 no replacement + 0xFFFD, // 0x26 no replacement + 0xFFFD, // 0x27 no replacement + 0x260E, // 0x28 BLACK TELEPHONE + 0x2706, // 0x29 TELEPHONE LOCATION SIGN (close, but not exact) + 0x2709, // 0x2A ENVELOPE + 0x2709, // 0x2B ENVELOPE (close, but not exact) + 0xFFFD, // 0x2C no replacement + 0xFFFD, // 0x2D no replacement + 0xFFFD, // 0x2E no replacement + 0xFFFD, // 0x2F no replacement + 0xFFFD, // 0x30 no replacement + 0xFFFD, // 0x31 no replacement + 0xFFFD, // 0x32 no replacement + 0xFFFD, // 0x33 no replacement + 0xFFFD, // 0x34 no replacement + 0xFFFD, // 0x35 no replacement + 0x231B, // 0x36 HOURGLASS + 0x2328, // 0x37 KEYBOARD + 0xFFFD, // 0x38 no replacement + 0xFFFD, // 0x39 no replacement + 0xFFFD, // 0x3A no replacement + 0xFFFD, // 0x3B no replacement + 0xFFFD, // 0x3C no replacement + 0xFFFD, // 0x3D no replacement + 0x2707, // 0x3E TAPE DRIVE + 0x270D, // 0x3F WRITING HAND + 0x270D, // 0x40 WRITING HAND (close, but not exact) + 0x270C, // 0x41 VICTORY HAND + 0xFFFD, // 0x42 3 FINGER UP HAND (no replacement) + 0xFFFD, // 0x43 THUMBS UP HAND (no replacement) + 0xFFFD, // 0x44 THUMBS DOWN HAND (no replacement) + 0x261C, // 0x45 WHITE LEFT POINTING INDEX + 0x261E, // 0x46 WHITE RIGHT POINTING INDEX + 0x261D, // 0x47 WHITE UP POINTING INDEX + 0x261F, // 0x48 WHITE DOWN POINTING INDEX + 0xFFFD, // 0x49 OPEN HAND (no replacement) + 0x263A, // 0x4A WHITE SMILING FACE + 0x263A, // 0x4B WHITE SMILING FACE (close, but not exact) + 0x2639, // 0x4C WHITE FROWNING FACE + 0xFFFD, // 0x4D BOMB (no replacement. 1F4A3) + 0x2620, // 0x4E SKULL AND CROSSBONES + 0x2690, // 0x4F WHITE FLAG (not exact) + 0x2691, // 0x50 WHITE PENANT (use BLACK FLAG) + 0x2708, // 0x51 AIRPLANE + 0x263C, // 0x52 WHITE SUN WITH RAYS (close, but not exact) + 0x2602, // 0x53 RAINDROP (use UMBRELLA) + 0x2744, // 0x54 SNOWFLAKE + 0x271D, // 0x55 WHITE LATIN CROSS (use BLACK CROSS) + 0x271E, // 0x56 SHADOWED WHITE LATIN CROSS + 0x271F, // 0x57 CELTIC CROSS (use OUTLINED LATIN CROSS) + 0x2720, // 0x58 MALTESE CROSS + 0x2721, // 0x59 STAR OF DAVID + 0x262A, // 0x5A STAR AND CRESCENT + 0x262F, // 0x5B YIN YANG + 0x0950, // 0x5C DEVANGARI OM CORRECT|CLOSE: Perhaps PROPOSE SACRED OM ? + 0x2638, // 0x5D WHEEL OF DHARMA + 0x2648, // 0x5E ARIES + 0x2649, // 0x5F TAURUS + 0x264A, // 0x60 GEMINI + 0x264B, // 0x61 CANCER + 0x264C, // 0x62 LEO + 0x264D, // 0x63 VIRGO + 0x264E, // 0x64 LIBRA + 0x264F, // 0x65 SCORPIUS + 0x2650, // 0x66 SAGITTARIUS + 0x2651, // 0x67 CAPRICORN + 0x2652, // 0x68 AQUARIUS + 0x2653, // 0x69 PISCES + 0xFFFD, // 0x6A LOWER CASE AMPERSAND)?) (no replacement) + 0xFF06, // 0x6B AMPERSAND (use FULL WIDTH AMPERSAND, close, but not exact. Do NOT use 0026, or it maps normal Ampersands to Wingdings Ampersand) + 0x25CF, // 0x6C BLACK CIRCLE + 0x274D, // 0x6D SHADOWED WHITE CIRCLE (close, but not exact) + 0x25A0, // 0x6E BLACK SQUARE + 0x25A3, // 0x6F WHITE SQUARE IN BLACK RECTANGLE (use BLACK SQUSRE in WHITE SQUARE) + 0x25A1, // 0x70 WHITE SQUARE (close, but not exact) + 0x2751, // 0x71 LOWER RIGHT SHADOWED WHITE SQUARE + 0x2752, // 0x72 UPPER RIGHT SHADOWED WHITE SQUARE + 0x25CA, // 0x73 LOZENGE (close, but not exact) + 0x25CA, // 0x74 LOZENGE (close, but not exact) + 0x25C6, // 0x75 BLACK DIAMOND + 0x2756, // 0x76 BLACK DIAMOND MINUS WHITE X + 0x25C6, // 0x77 BLACK DIAMOND (close, but not exact) + 0x2327, // 0x78 X IN A RECTANGLE BOX + 0x2353, // 0x79 APL FUNCTIONAL SYMBOL QUAD UP CARET(close, but not exact) + 0x2318, // 0x7A PLACE OF INTEREST SIGN + 0x2740, // 0x7B WHITE FLORETTE (close, but not exact) + 0x273F, // 0x7C BLACK FLORETTE (close, but not exact) + 0x275D, // 0x7D HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT + 0x275E, // 0x7E HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT + 0xFFFD, // 0x7F unused + 0x24EA, // 0x80 CIRCLED DIGIT ZERO + 0x2460, // 0x81 CIRCLED DIGIT ONE + 0x2461, // 0x82 CIRCLED DIGIT TWO + 0x2462, // 0x83 CIRCLED DIGIT THREE + 0x2463, // 0x84 CIRCLED DIGIT FOUR + 0x2464, // 0x85 CIRCLED DIGIT FIVE + 0x2465, // 0x86 CIRCLED DIGIT SIX + 0x2466, // 0x87 CIRCLED DIGIT SEVEN + 0x2467, // 0x88 CIRCLED DIGIT EIGHT + 0x2468, // 0x89 CIRCLED DIGIT NINE + 0x2469, // 0x8A CIRCLED NUMBER TEN + 0xFFFD, // 0x8B no replacement + 0x2776, // 0x8C DINGBAT NEGATIVE CIRCLED DIGIT ONE + 0x2777, // 0x8D DINGBAT NEGATIVE CIRCLED DIGIT TWO + 0x2778, // 0x8E DINGBAT NEGATIVE CIRCLED DIGIT THREE + 0x2779, // 0x8F DINGBAT NEGATIVE CIRCLED DIGIT FOUR + 0x277A, // 0x90 DINGBAT NEGATIVE CIRCLED DIGIT FIVE + 0x277B, // 0x91 DINGBAT NEGATIVE CIRCLED DIGIT SIX + 0x277C, // 0x92 DINGBAT NEGATIVE CIRCLED DIGIT SEVEN + 0x277D, // 0x93 DINGBAT NEGATIVE CIRCLED DIGIT EIGHT + 0x277E, // 0x94 DINGBAT NEGATIVE CIRCLED DIGIT NINE + 0x277F, // 0x95 DINGBAT NEGATIVE CIRCLED NUMBER TEN + 0xFFFD, // 0x96 ROTATED FLORAL HEART BULLET (no good replacement) + 0xFFFD, // 0x97 REVERSED ROTATED FLORAL HEART BULLET (no good replacement) + 0xFFFD, // 0x98 REVERSED ROTATED FLORAL HEART BULLET (no good replacement) + 0xFFFD, // 0x99 ROTATED FLORAL HEART BULLET (no good replacement) + 0xFFFD, // 0x9A ROTATED FLORAL HEART BULLET (no good replacement) + 0xFFFD, // 0x9B REVERSED ROTATED FLORAL HEART BULLET (no good replacement) + 0xFFFD, // 0x9C REVERSED ROTATED FLORAL HEART BULLET (no good replacement) + 0xFFFD, // 0x9D ROTATED FLORAL HEART BULLET (no good replacement) + 0x2219, // 0x9E BULLET (use BULLET operator, so normal font BULLET will not convert to Symbol BULLET) + 0x25CF, // 0x9F BLACK CIRCLE (close, but not exact) + 0x25AA, // 0xA0 BLACK VERY SMALL SQUARE + 0x26AA, // 0xA1 WHITE CIRCLE (use MEDIUM WHITE CIRCLE) + 0x25CB, // 0xA2 HEAVY WHITE CIRCLE (use WHITE CIRCLE) + 0x25CD, // 0xA3 HEAVIEST CIRCLE (use CIRCLE WITH VERTICAL FILL) + 0x25C9, // 0xA4 CIRCLE WITH A CENTRAL DOT (close, dot much bigger) + 0x25CE, // 0xA5 BULLSEYE + 0x274D, // 0xA6 SHADOWED WHITE CIRCLE (close, but not exact) + 0xFFED, // 0xA7 BLACK SMALL SQUARE + 0x2610, // 0xA8 WHITE SQUARE (close, but not exact, different fro 25A1) + 0xFFFD, // 0xA9 no replacement + 0x2726, // 0xAA BLACK FOUR POINTED STAR MAYBE + 0x2605, // 0xAB BLACK STAR + 0x2736, // 0xAC SIX POINTED BLACK STAR + 0x2737, // 0xAD EIGHT POINTED RECTILINEAR BLACK STAR + 0x2738, // 0xAE TWELVE POINTED BLACK STAR + 0x2735, // 0xAF EIGHT POINTED PINWHEEL STAR + 0xFFFD, // 0xB0 no replacement + 0xFFFD, // 0xB1 no replacement + 0x2727, // 0xB2 WHITE FOUR POINTED STAR + 0x2726, // 0xB3 ROTATED WHITE FOUR POINTED STAR (use BLACK FOUR POINTED STAR) + 0xFFFD, // 0xB4 REPLACEMENT CHARACTER (close, but not exact) + 0x272A, // 0xB5 CIRCLED WHITE STAR + 0x2730, // 0xB6 SHADOWED WHITE STAR + 0xFFFD, // 0xB7 ANALOG CLOCK 1 (no replacement) + 0xFFFD, // 0xB8 ANALOG CLOCK 2 (no replacement) + 0xFFFD, // 0xB9 ANALOG CLOCK 3 (no replacement) + 0xFFFD, // 0xBA ANALOG CLOCK 4 (no replacement) + 0xFFFD, // 0xBB ANALOG CLOCK 5 (no replacement) + 0xFFFD, // 0xBC ANALOG CLOCK 6 (no replacement) + 0xFFFD, // 0xBD ANALOG CLOCK 7 (no replacement) + 0xFFFD, // 0xBE ANALOG CLOCK 8 (no replacement) + 0xFFFD, // 0xBF ANALOG CLOCK 9 (no replacement) + 0xFFFD, // 0xC0 ANALOG CLOCK 10 (no replacement) + 0xFFFD, // 0xC1 ANALOG CLOCK 11 (no replacement) + 0xFFFD, // 0xC2 ANALOG CLOCK 12 (no replacement) + 0x21B2, // 0xC3 TURN ARROW DOWN AND LEFT (Meaning close, shape differs) + 0x21B3, // 0xC4 TURN ARROW DOWN AND RIGHT (Meaning close, shape differs) + 0x21B0, // 0xC5 TURN ARROW UP AND LEFT (Meaning close, shape differs) + 0x21B1, // 0xC6 TURN ARROW UP AND RIGHT (Meaning close, shape differs) + 0x2B11, // 0xC7 TURN ARROW LEFT AND UP (Meaning close, shape differs) + 0x2B0F, // 0xC8 TURN ARROW RIGHT AND UP (Meaning close, shape differs) + 0x2B10, // 0xC9 TURN ARROW LEFT AND DOWN (Meaning close, shape differs) + 0x2B0E, // 0xCA TURN ARROW RIGHT AND DOWN (Meaning close, shape differs) + 0xFFFD, // 0xCB no replacement + 0xFFFD, // 0xCC no replacement + 0xFFFD, // 0xCD no replacement + 0xFFFD, // 0xCE no replacement + 0xFFFD, // 0xCF no replacement + 0xFFFD, // 0xD0 no replacement + 0xFFFD, // 0xD1 no replacement + 0xFFFD, // 0xD2 no replacement + 0xFFFD, // 0xD3 no replacement + 0xFFFD, // 0xD4 no replacement + 0x232B, // 0xD5 ERASE TO THE LEFT + 0x2326, // 0xD6 ERASE TO THE RIGHT + 0x25C0, // 0xD7 THREE-D LIGHTED LEFT ARROWHEAD (Use BLACK LEFT TRIANGLE) + 0x25B6, // 0xD8 THREE-D LIGHTED RIGHT ARROWHEAD (Use BLACK RIGHT TRIANGLE, 27A2 is exact but has no other directions) + 0x25B2, // 0xD9 THREE-D LIGHTED UP ARROWHEAD (Use BLACK UP TRIANGLE) + 0x25BC, // 0xDA THREE-D LIGHTED DOWN ARROWHEAD (Use BLACK DOWN TRIANGLE) + 0xFFFD, // 0xDB no replacement + 0x27B2, // 0xDC CIRCLED HEAVY WHITE RIGHTWARDS ARROW + 0xFFFD, // 0xDD no replacement + 0xFFFD, // 0xDE no replacement + 0x2190, // 0xDF LEFT ARROW + 0x2192, // 0xE0 RIGHT ARROW + 0x2191, // 0xE1 UP ARROW + 0x2193, // 0xE2 DOWN ARROW + 0x2196, // 0xE3 UPPER LEFT ARROW + 0x2197, // 0xE4 UPPER RIGHT ARROW + 0x2199, // 0xE5 LOWER LEFT ARROW + 0x2198, // 0xE6 LOWER RIGHT ARROW + 0x2B05, // 0xE7 HEAVY LEFT BLACK ARROW (same as regular BLACK ARROW) + 0x2B08, // 0xE8 HEAVY RIGHT BLACK ARROW (same as regular BLACK ARROW) + 0x2B06, // 0xE9 HEAVY UP BLACK ARROW (no equiv BLACK ARROW) + 0x2B07, // 0xEA HEAVY DOWN BLACK ARROW (same as regular BLACK ARROW) + 0x2B09, // 0xEB HEAVY UPPER LEFT BLACK ARROW same as regular BLACK ARROW) + 0x2B08, // 0xEC HEAVY UPPER RIGHT BLACK ARROW same as regular BLACK ARROW) + 0x2B0B, // 0xED HEAVY LOWER LEFT BLACK ARROW (same as regular BLACK ARROW) + 0x2B0A, // 0xEE HEAVY LOWER RIGHT BLACK ARROW (same as regular BLACK ARROW) + 0x21E6, // 0xEF LEFTWARDS WHITE ARROW + 0x21E8, // 0xF0 RIGHTWARDS WHITE ARROW + 0x21E7, // 0xF1 UPWARDS WHITE ARROW + 0x21E9, // 0xF2 DOWNWARDS WHITE ARROW + 0x21D4, // 0xF3 LEFT RIGHT DOUBLE ARROW + 0x21D5, // 0xF4 UP DOWN DOUBLE ARROW + 0x21D6, // 0xF5 NORTH WEST DOUBLE ARROW (close, but not exact) + 0x21D7, // 0xF6 NORTH EAST DOUBLE ARROW (close, but not exact) + 0x21D9, // 0xF7 SOUTH WEST DOUBLE ARROW (close, but not exact) + 0x21D8, // 0xF8 SOUTH EAST DOUBLE ARROW (close, but not exact) + 0xFFFD, // 0xF9 no replacement + 0xFFFD, // 0xFA no replacement + 0x2717, // 0xFB BALLOT X + 0x2713, // 0xFC CHECK MARK + 0x2612, // 0xFD BALLOT BOX WITH X + 0x2611, // 0xFE BALLOT BOX WITH CHECK + 0xFFFD // 0xFF no replacement +}; + +/* characters from zapf dingbat font, conversion to a unicode font. Change both the + code and the font on conversion. These are untested as the development machine did + not have the font installed. */ +static unsigned int dingbats_convert[256]={ + 0xFFFD, // 0x00 no replacement + 0xFFFD, // 0x01 no replacement + 0xFFFD, // 0x02 no replacement + 0xFFFD, // 0x03 no replacement + 0xFFFD, // 0x04 no replacement + 0xFFFD, // 0x05 no replacement + 0xFFFD, // 0x06 no replacement + 0xFFFD, // 0x07 no replacement + 0xFFFD, // 0x08 no replacement + 0xFFFD, // 0x09 no replacement + 0xFFFD, // 0x0A no replacement + 0xFFFD, // 0x0B no replacement + 0xFFFD, // 0x0C no replacement + 0xFFFD, // 0x0D no replacement + 0xFFFD, // 0x0E no replacement + 0xFFFD, // 0x0F no replacement + 0xFFFD, // 0x10 no replacement + 0xFFFD, // 0x11 no replacement + 0xFFFD, // 0x12 no replacement + 0xFFFD, // 0x13 no replacement + 0xFFFD, // 0x14 no replacement + 0xFFFD, // 0x15 no replacement + 0xFFFD, // 0x16 no replacement + 0xFFFD, // 0x17 no replacement + 0xFFFD, // 0x18 no replacement + 0xFFFD, // 0x19 no replacement + 0xFFFD, // 0x1A no replacement + 0xFFFD, // 0x1B no replacement + 0xFFFD, // 0x1C no replacement + 0xFFFD, // 0x1D no replacement + 0xFFFD, // 0x1E no replacement + 0xFFFD, // 0x1F no replacement + 0x0020, // 0x20 SPACE + 0x2701, // 0x21 UPPER BLADE SCISSORS + 0x2702, // 0x22 BLACK SCISSORS + 0x2703, // 0x23 LOWER BLADE SCISSORS + 0x2704, // 0x24 WHITE SCISSORS + 0x260E, // 0x25 BLACK TELEPHONE + 0x2706, // 0x26 TELEPHONE LOCATION SIGN + 0x2707, // 0x27 TAPE DRIVE + 0x2708, // 0x28 AIRPLANE + 0x2709, // 0x29 ENVELOPE + 0x261B, // 0x2A BLACK RIGHT POINTING INDEX + 0x261E, // 0x2B WHITE RIGHT POINTING INDEX + 0x270C, // 0x2C VICTORY HAND + 0x270D, // 0x2D WRITING HAND + 0x270E, // 0x2E LOWER RIGHT PENCIL + 0x270F, // 0x2F PENCIL + 0x2710, // 0x30 UPPER RIGHT PENCIL + 0x2711, // 0x31 WHITE NIB + 0x2712, // 0x32 BLACK NIB + 0x2713, // 0x33 CHECK MARK + 0x2714, // 0x34 HEAVY CHECK MARK + 0x2715, // 0x35 MULTIPLICATION X + 0x2716, // 0x36 HEAVY MULTIPLICATION X + 0x2717, // 0x37 BALLOT X + 0x2718, // 0x38 HEAVY BALLOT X + 0x2719, // 0x39 OUTLINED GREEK CROSS + 0x271A, // 0x3A HEAVY GREEK CROSS + 0x271B, // 0x3B OPEN CENTRE CROSS + 0x271C, // 0x3C HEAVY OPEN CENTRE CROSS + 0x271D, // 0x3D LATIN CROSS + 0x271E, // 0x3E SHADOWED WHITE LATIN CROSS + 0x271F, // 0x3F OUTLINED LATIN CROSS + 0x2720, // 0x40 MALTESE CROSS + 0x2721, // 0x41 STAR OF DAVID + 0x2722, // 0x42 FOUR TEARDROP-SPOKED ASTERISK + 0x2723, // 0x43 FOUR BALLOON-SPOKED ASTERISK + 0x2724, // 0x44 HEAVY FOUR BALLOON-SPOKED ASTERISK + 0x2725, // 0x45 FOUR CLUB-SPOKED ASTERISK + 0x2726, // 0x46 BLACK FOUR POINTED STAR + 0x2727, // 0x47 WHITE FOUR POINTED STAR + 0x2605, // 0x48 BLACK STAR + 0x2729, // 0x49 STRESS OUTLINED WHITE STAR + 0x272A, // 0x4A CIRCLED WHITE STAR + 0x272B, // 0x4B OPEN CENTRE BLACK STAR + 0x272C, // 0x4C BLACK CENTRE WHITE STAR + 0x272D, // 0x4D OUTLINED BLACK STAR + 0x272E, // 0x4E HEAVY OUTLINED BLACK STAR + 0x272F, // 0x4F PINWHEEL STAR + 0x2730, // 0x50 SHADOWED WHITE STAR + 0x2731, // 0x51 HEAVY ASTERISK + 0x2732, // 0x52 OPEN CENTRE ASTERISK + 0x2733, // 0x53 EIGHT SPOKED ASTERISK + 0x2734, // 0x54 EIGHT POINTED BLACK STAR + 0x2735, // 0x55 EIGHT POINTED PINWHEEL STAR + 0x2736, // 0x56 SIX POINTED BLACK STAR + 0x2737, // 0x57 EIGHT POINTED RECTILINEAR BLACK STAR + 0x2738, // 0x58 HEAVY EIGHT POINTED RECTILINEAR BLACK STAR + 0x2739, // 0x59 TWELVE POINTED BLACK STAR + 0x273A, // 0x5A SIXTEEN POINTED ASTERISK + 0x273B, // 0x5B TEARDROP-SPOKED ASTERISK + 0x273C, // 0x5C OPEN CENTRE TEARDROP-SPOKED ASTERISK + 0x273D, // 0x5D HEAVY TEARDROP-SPOKED ASTERISK + 0x273E, // 0x5E SIX PETALLED BLACK AND WHITE FLORETTE + 0x273F, // 0x5F BLACK FLORETTE + 0x2740, // 0x60 WHITE FLORETTE + 0x2741, // 0x61 EIGHT PETALLED OUTLINED BLACK FLORETTE + 0x2742, // 0x62 CIRCLED OPEN CENTRE EIGHT POINTED STAR + 0x2743, // 0x63 HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK + 0x2744, // 0x64 SNOWFLAKE + 0x2745, // 0x65 TIGHT TRIFOLIATE SNOWFLAKE + 0x2746, // 0x66 HEAVY CHEVRON SNOWFLAKE + 0x2747, // 0x67 SPARKLE + 0x2748, // 0x68 HEAVY SPARKLE + 0x2749, // 0x69 BALLOON-SPOKED ASTERISK + 0x274A, // 0x6A EIGHT TEARDROP-SPOKED PROPELLER ASTERISK + 0x274B, // 0x6B HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK + 0x25CF, // 0x6C BLACK CIRCLE + 0x274D, // 0x6D SHADOWED WHITE CIRCLE + 0x25A0, // 0x6E BLACK SQUARE + 0x274F, // 0x6F LOWER RIGHT DROP-SHADOWED WHITE SQUARE + 0x2750, // 0x70 UPPER RIGHT DROP-SHADOWED WHITE SQUARE + 0x2751, // 0x71 LOWER RIGHT SHADOWED WHITE SQUARE + 0x2752, // 0x72 UPPER RIGHT SHADOWED WHITE SQUARE + 0x25B2, // 0x73 BLACK UP-POINTING TRIANGLE + 0x25BC, // 0x74 BLACK DOWN-POINTING TRIANGLE + 0x25C6, // 0x75 BLACK DIAMOND + 0x2756, // 0x76 BLACK DIAMOND MINUS WHITE X + 0x25D7, // 0x77 RIGHT HALF BLACK CIRCLE + 0x2758, // 0x78 LIGHT VERTICAL BAR + 0x2759, // 0x79 MEDIUM VERTICAL BAR + 0x275A, // 0x7A HEAVY VERTICAL BAR + 0x275B, // 0x7B HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT + 0x275C, // 0x7C HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT + 0x275D, // 0x7D HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT + 0x275E, // 0x7E HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT + 0xFFFD, // 0x7F no replacement + 0xF8D7, // 0x80 MEDIUM LEFT PARENTHESIS ORNAMENT + 0xF8D8, // 0x81 MEDIUM RIGHT PARENTHESIS ORNAMENT + 0xF8D9, // 0x82 MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT + 0xF8DA, // 0x83 MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT + 0xF8DB, // 0x84 MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT + 0xF8DC, // 0x85 MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT + 0xF8DD, // 0x86 HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT + 0xF8DE, // 0x87 HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT + 0xF8DF, // 0x88 HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT + 0xF8E0, // 0x89 HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT + 0xF8E1, // 0x8A LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT + 0xF8E2, // 0x8B LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT + 0xF8E3, // 0x8C MEDIUM LEFT CURLY BRACKET ORNAMENT + 0xF8E4, // 0x8D MEDIUM RIGHT CURLY BRACKET ORNAMENT + 0xFFFD, // 0x8E no replacement + 0xFFFD, // 0x8F no replacement + 0xFFFD, // 0x90 no replacement + 0xFFFD, // 0x91 no replacement + 0xFFFD, // 0x92 no replacement + 0xFFFD, // 0x93 no replacement + 0xFFFD, // 0x94 no replacement + 0xFFFD, // 0x95 no replacement + 0xFFFD, // 0x96 no replacement + 0xFFFD, // 0x97 no replacement + 0xFFFD, // 0x98 no replacement + 0xFFFD, // 0x99 no replacement + 0xFFFD, // 0x9A no replacement + 0xFFFD, // 0x9B no replacement + 0xFFFD, // 0x9C no replacement + 0xFFFD, // 0x9D no replacement + 0xFFFD, // 0x9E no replacement + 0xFFFD, // 0x9F no replacement + 0xFFFD, // 0xA0 no replacement + 0x2761, // 0xA1 CURVED STEM PARAGRAPH SIGN ORNAMENT + 0x2762, // 0xA2 HEAVY EXCLAMATION MARK ORNAMENT + 0x2763, // 0xA3 HEAVY HEART EXCLAMATION MARK ORNAMENT + 0x2764, // 0xA4 HEAVY BLACK HEART + 0x2765, // 0xA5 ROTATED HEAVY BLACK HEART BULLET + 0x2766, // 0xA6 FLORAL HEART + 0x2767, // 0xA7 ROTATED FLORAL HEART BULLET + 0x2663, // 0xA8 BLACK CLUB SUIT + 0x2666, // 0xA9 BLACK DIAMOND SUIT + 0x2665, // 0xAA BLACK HEART SUIT + 0x2660, // 0xAB BLACK SPADE SUIT + 0x2460, // 0xAC CIRCLED DIGIT ONE + 0x2461, // 0xAD CIRCLED DIGIT TWO + 0x2462, // 0xAE CIRCLED DIGIT THREE + 0x2463, // 0xAF CIRCLED DIGIT FOUR + 0x2464, // 0xB0 CIRCLED DIGIT FIVE + 0x2465, // 0xB1 CIRCLED DIGIT SIX + 0x2466, // 0xB2 CIRCLED DIGIT SEVEN + 0x2467, // 0xB3 CIRCLED DIGIT EIGHT + 0x2468, // 0xB4 CIRCLED DIGIT NINE + 0x2469, // 0xB5 CIRCLED NUMBER TEN + 0x2776, // 0xB6 DINGBAT NEGATIVE CIRCLED DIGIT ONE + 0x2777, // 0xB7 DINGBAT NEGATIVE CIRCLED DIGIT TWO + 0x2778, // 0xB8 DINGBAT NEGATIVE CIRCLED DIGIT THREE + 0x2779, // 0xB9 DINGBAT NEGATIVE CIRCLED DIGIT FOUR + 0x277A, // 0xBA DINGBAT NEGATIVE CIRCLED DIGIT FIVE + 0x277B, // 0xBB DINGBAT NEGATIVE CIRCLED DIGIT SIX + 0x277C, // 0xBC DINGBAT NEGATIVE CIRCLED DIGIT SEVEN + 0x277D, // 0xBD DINGBAT NEGATIVE CIRCLED DIGIT EIGHT + 0x277E, // 0xBE DINGBAT NEGATIVE CIRCLED DIGIT NINE + 0x277F, // 0xBF DINGBAT NEGATIVE CIRCLED NUMBER TEN + 0x2780, // 0xC0 DINGBAT CIRCLED SANS-SERIF DIGIT ONE + 0x2781, // 0xC1 DINGBAT CIRCLED SANS-SERIF DIGIT TWO + 0x2782, // 0xC2 DINGBAT CIRCLED SANS-SERIF DIGIT THREE + 0x2783, // 0xC3 DINGBAT CIRCLED SANS-SERIF DIGIT FOUR + 0x2784, // 0xC4 DINGBAT CIRCLED SANS-SERIF DIGIT FIVE + 0x2785, // 0xC5 DINGBAT CIRCLED SANS-SERIF DIGIT SIX + 0x2786, // 0xC6 DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN + 0x2787, // 0xC7 DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT + 0x2788, // 0xC8 DINGBAT CIRCLED SANS-SERIF DIGIT NINE + 0x2789, // 0xC9 DINGBAT CIRCLED SANS-SERIF NUMBER TEN + 0x278A, // 0xCA DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE + 0x278B, // 0xCB DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO + 0x278C, // 0xCC DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE + 0x278D, // 0xCD DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR + 0x278E, // 0xCE DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE + 0x278F, // 0xCF DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX + 0x2790, // 0xD0 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN + 0x2791, // 0xD1 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT + 0x2792, // 0xD2 DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE + 0x2793, // 0xD3 DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN + 0x2794, // 0xD4 HEAVY WIDE-HEADED RIGHTWARDS ARROW + 0x2192, // 0xD5 RIGHTWARDS ARROW + 0x2194, // 0xD6 LEFT RIGHT ARROW + 0x2195, // 0xD7 UP DOWN ARROW + 0x2798, // 0xD8 HEAVY SOUTH EAST ARROW + 0x2799, // 0xD9 HEAVY RIGHTWARDS ARROW + 0x279A, // 0xDA HEAVY NORTH EAST ARROW + 0x279B, // 0xDB DRAFTING POINT RIGHTWARDS ARROW + 0x279C, // 0xDC HEAVY ROUND-TIPPED RIGHTWARDS ARROW + 0x279D, // 0xDD TRIANGLE-HEADED RIGHTWARDS ARROW + 0x279E, // 0xDE HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW + 0x279F, // 0xDF DASHED TRIANGLE-HEADED RIGHTWARDS ARROW + 0x27A0, // 0xE0 HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW + 0x27A1, // 0xE1 BLACK RIGHTWARDS ARROW + 0x27A2, // 0xE2 THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD + 0x27A3, // 0xE3 THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD + 0x27A4, // 0xE4 BLACK RIGHTWARDS ARROWHEAD + 0x27A5, // 0xE5 HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW + 0x27A6, // 0xE6 HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW + 0x27A7, // 0xE7 SQUAT BLACK RIGHTWARDS ARROW + 0x27A8, // 0xE8 HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW + 0x27A9, // 0xE9 RIGHT-SHADED WHITE RIGHTWARDS ARROW + 0x27AA, // 0xEA LEFT-SHADED WHITE RIGHTWARDS ARROW + 0x27AB, // 0xEB BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW + 0x27AC, // 0xEC FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW + 0x27AD, // 0xED HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW + 0x27AE, // 0xEE HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW + 0x27AF, // 0xEF NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW + 0xFFFD, // 0xF0 no replacement + 0x27B1, // 0xF1 NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW + 0x27B2, // 0xF2 CIRCLED HEAVY WHITE RIGHTWARDS ARROW + 0x27B3, // 0xF3 WHITE-FEATHERED RIGHTWARDS ARROW + 0x27B4, // 0xF4 BLACK-FEATHERED SOUTH EAST ARROW + 0x27B5, // 0xF5 BLACK-FEATHERED RIGHTWARDS ARROW + 0x27B6, // 0xF6 BLACK-FEATHERED NORTH EAST ARROW + 0x27B7, // 0xF7 HEAVY BLACK-FEATHERED SOUTH EAST ARROW + 0x27B8, // 0xF8 HEAVY BLACK-FEATHERED RIGHTWARDS ARROW + 0x27B9, // 0xF9 HEAVY BLACK-FEATHERED NORTH EAST ARROW + 0x27BA, // 0xFA TEARDROP-BARBED RIGHTWARDS ARROW + 0x27BB, // 0xFB HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW + 0x27BC, // 0xFC WEDGE-TAILED RIGHTWARDS ARROW + 0x27BD, // 0xFD HEAVY WEDGE-TAILED RIGHTWARDS ARROW + 0x27BE, // 0xFE OPEN-OUTLINED RIGHTWARDS ARROW + 0xFFFD // 0xFF no replacement +}; + +/* characters from symbol font, conversion to a unicode font. Change both the + code and the font on conversion. */ +static unsigned int symbol_convert[256]={ + 0xFFFD, // 0x00 no replacement + 0xFFFD, // 0x01 no replacement + 0xFFFD, // 0x02 no replacement + 0xFFFD, // 0x03 no replacement + 0xFFFD, // 0x04 no replacement + 0xFFFD, // 0x05 no replacement + 0xFFFD, // 0x06 no replacement + 0xFFFD, // 0x07 no replacement + 0xFFFD, // 0x08 no replacement + 0xFFFD, // 0x09 no replacement + 0xFFFD, // 0x0A no replacement + 0xFFFD, // 0x0B no replacement + 0xFFFD, // 0x0C no replacement + 0xFFFD, // 0x0D no replacement + 0xFFFD, // 0x0E no replacement + 0xFFFD, // 0x0F no replacement + 0xFFFD, // 0x10 no replacement + 0xFFFD, // 0x11 no replacement + 0xFFFD, // 0x12 no replacement + 0xFFFD, // 0x13 no replacement + 0xFFFD, // 0x14 no replacement + 0xFFFD, // 0x15 no replacement + 0xFFFD, // 0x16 no replacement + 0xFFFD, // 0x17 no replacement + 0xFFFD, // 0x18 no replacement + 0xFFFD, // 0x19 no replacement + 0xFFFD, // 0x1A no replacement + 0xFFFD, // 0x1B no replacement + 0xFFFD, // 0x1C no replacement + 0xFFFD, // 0x1D no replacement + 0xFFFD, // 0x1E no replacement + 0xFFFD, // 0x1F no replacement + 0x0020, // 0x20 SPACE + 0x0021, // 0x21 EXCLAMATION MARK + 0x2200, // 0x22 FOR ALL + 0x0023, // 0x23 NUMBER SIGN + 0x2203, // 0x24 THERE EXISTS + 0x0025, // 0x25 PERCENT SIGN + 0x0026, // 0x26 AMPERSAND + 0x220B, // 0x27 CONTAINS AS MEMBER + 0x0028, // 0x28 OPENING PARENTHESIS + 0x0029, // 0x29 CLOSING PARENTHESIS + 0x2217, // 0x2A ASTERISK OPERATOR + 0x002B, // 0x2B PLUS SIGN + 0x002C, // 0x2C COMMA + 0x2212, // 0x2D MINUS SIGN + 0x002E, // 0x2E PERIOD + 0x002F, // 0x2F SLASH + 0x0030, // 0x30 DIGIT ZERO + 0x0031, // 0x31 DIGIT ONE + 0x0032, // 0x32 DIGIT TWO + 0x0033, // 0x33 DIGIT THREE + 0x0034, // 0x34 DIGIT FOUR + 0x0035, // 0x35 DIGIT FIVE + 0x0036, // 0x36 DIGIT SIX + 0x0037, // 0x37 DIGIT SEVEN + 0x0038, // 0x38 DIGIT EIGHT + 0x0039, // 0x39 DIGIT NINE + 0x003A, // 0x3A COLON + 0x003B, // 0x3B SEMICOLON + 0x003C, // 0x3C LESS-THAN SIGN + 0x003D, // 0x3D EQUALS SIGN + 0x003E, // 0x3E GREATER-THAN SIGN + 0x003F, // 0x3F QUESTION MARK + 0x2245, // 0x40 APPROXIMATELY EQUAL TO + 0x0391, // 0x41 GREEK CAPITAL LETTER ALPHA + 0x0392, // 0x42 GREEK CAPITAL LETTER BETA + 0x03A7, // 0x43 GREEK CAPITAL LETTER CHI + 0x0394, // 0x44 GREEK CAPITAL LETTER DELTA + 0x0395, // 0x45 GREEK CAPITAL LETTER EPSILON + 0x03A6, // 0x46 GREEK CAPITAL LETTER PHI + 0x0393, // 0x47 GREEK CAPITAL LETTER GAMMA + 0x0397, // 0x48 GREEK CAPITAL LETTER ETA + 0x0399, // 0x49 GREEK CAPITAL LETTER IOTA + 0x03D1, // 0x4A GREEK SMALL LETTER SCRIPT THETA + 0x039A, // 0x4B GREEK CAPITAL LETTER KAPPA + 0x039B, // 0x4C GREEK CAPITAL LETTER LAMBDA + 0x039C, // 0x4D GREEK CAPITAL LETTER MU + 0x039D, // 0x4E GREEK CAPITAL LETTER NU + 0x039F, // 0x4F GREEK CAPITAL LETTER OMICRON + 0x03A0, // 0x50 GREEK CAPITAL LETTER PI + 0x0398, // 0x51 GREEK CAPITAL LETTER THETA + 0x03A1, // 0x52 GREEK CAPITAL LETTER RHO + 0x03A3, // 0x53 GREEK CAPITAL LETTER SIGMA + 0x03A4, // 0x54 GREEK CAPITAL LETTER TAU + 0x03A5, // 0x55 GREEK CAPITAL LETTER UPSILON + 0x03C2, // 0x56 GREEK SMALL LETTER FINAL SIGMA + 0x03A9, // 0x57 GREEK CAPITAL LETTER OMEGA + 0x039E, // 0x58 GREEK CAPITAL LETTER XI + 0x03A8, // 0x59 GREEK CAPITAL LETTER PSI + 0x0396, // 0x5A GREEK CAPITAL LETTER ZETA + 0x005B, // 0x5B OPENING SQUARE BRACKET + 0x2234, // 0x5C THEREFORE + 0x005D, // 0x5D CLOSING SQUARE BRACKET + 0x22A5, // 0x5E UP TACK + 0x005F, // 0x5F SPACING UNDERSCORE + 0x203E, // 0x60 SPACING OVERSCORE + 0x03B1, // 0x61 GREEK SMALL LETTER ALPHA + 0x03B2, // 0x62 GREEK SMALL LETTER BETA + 0x03C7, // 0x63 GREEK SMALL LETTER CHI + 0x03B4, // 0x64 GREEK SMALL LETTER DELTA + 0x03B5, // 0x65 GREEK SMALL LETTER EPSILON + 0x03C6, // 0x66 GREEK SMALL LETTER PHI + 0x03B3, // 0x67 GREEK SMALL LETTER GAMMA + 0x03B7, // 0x68 GREEK SMALL LETTER ETA + 0x03B9, // 0x69 GREEK SMALL LETTER IOTA + 0x03D5, // 0x6A GREEK SMALL LETTER SCRIPT PHI + 0x03BA, // 0x6B GREEK SMALL LETTER KAPPA + 0x03BB, // 0x6C GREEK SMALL LETTER LAMBDA + 0x03BC, // 0x6D GREEK SMALL LETTER MU + 0x03BD, // 0x6E GREEK SMALL LETTER NU + 0x03BF, // 0x6F GREEK SMALL LETTER OMICRON + 0x03C0, // 0x70 GREEK SMALL LETTER PI + 0x03B8, // 0x71 GREEK SMALL LETTER THETA + 0x03C1, // 0x72 GREEK SMALL LETTER RHO + 0x03C3, // 0x73 GREEK SMALL LETTER SIGMA + 0x03C4, // 0x74 GREEK SMALL LETTER TAU + 0x03C5, // 0x75 GREEK SMALL LETTER UPSILON + 0x03D6, // 0x76 GREEK SMALL LETTER OMEGA PI + 0x03C9, // 0x77 GREEK SMALL LETTER OMEGA + 0x03BE, // 0x78 GREEK SMALL LETTER XI + 0x03C8, // 0x79 GREEK SMALL LETTER PSI + 0x03B6, // 0x7A GREEK SMALL LETTER ZETA + 0x007B, // 0x7B OPENING CURLY BRACKET + 0x007C, // 0x7C VERTICAL BAR + 0x007D, // 0x7D CLOSING CURLY BRACKET + 0x223C, // 0x7E TILDE OPERATOR + 0xFFFD, // 0x7F no replacement + 0xFFFD, // 0x80 no replacement + 0xFFFD, // 0x81 no replacement + 0xFFFD, // 0x82 no replacement + 0xFFFD, // 0x83 no replacement + 0xFFFD, // 0x84 no replacement + 0xFFFD, // 0x85 no replacement + 0xFFFD, // 0x86 no replacement + 0xFFFD, // 0x87 no replacement + 0xFFFD, // 0x88 no replacement + 0xFFFD, // 0x89 no replacement + 0xFFFD, // 0x8A no replacement + 0xFFFD, // 0x8B no replacement + 0xFFFD, // 0x8C no replacement + 0xFFFD, // 0x8D no replacement + 0xFFFD, // 0x8E no replacement + 0xFFFD, // 0x8F no replacement + 0xFFFD, // 0x90 no replacement + 0xFFFD, // 0x91 no replacement + 0xFFFD, // 0x92 no replacement + 0xFFFD, // 0x93 no replacement + 0xFFFD, // 0x94 no replacement + 0xFFFD, // 0x95 no replacement + 0xFFFD, // 0x96 no replacement + 0xFFFD, // 0x97 no replacement + 0xFFFD, // 0x98 no replacement + 0xFFFD, // 0x99 no replacement + 0xFFFD, // 0x9A no replacement + 0xFFFD, // 0x9B no replacement + 0xFFFD, // 0x9C no replacement + 0xFFFD, // 0x9D no replacement + 0xFFFD, // 0x9E no replacement + 0xFFFD, // 0x9F no replacement + 0x20AC, // 0xA0 EURO SIGN + 0x03D2, // 0xA1 GREEK CAPITAL LETTER UPSILON HOOK + 0x2032, // 0xA2 PRIME + 0x2264, // 0xA3 LESS THAN OR EQUAL TO + 0x2044, // 0xA4 FRACTION SLASH + 0x221E, // 0xA5 INFINITY + 0x0192, // 0xA6 LATIN SMALL LETTER SCRIPT F + 0x2663, // 0xA7 BLACK CLUB SUIT + 0x2666, // 0xA8 BLACK DIAMOND SUIT + 0x2665, // 0xA9 BLACK HEART SUIT + 0x2660, // 0xAA BLACK SPADE SUIT + 0x2194, // 0xAB LEFT RIGHT ARROW + 0x2190, // 0xAC LEFT ARROW + 0x2191, // 0xAD UP ARROW + 0x2192, // 0xAE RIGHT ARROW + 0x2193, // 0xAF DOWN ARROW + 0x00B0, // 0xB0 DEGREE SIGN + 0x00B1, // 0xB1 PLUS-OR-MINUS SIGN + 0x2033, // 0xB2 DOUBLE PRIME + 0x2265, // 0xB3 GREATER THAN OR EQUAL TO + 0x00D7, // 0xB4 MULTIPLICATION SIGN + 0x221D, // 0xB5 PROPORTIONAL TO + 0x2202, // 0xB6 PARTIAL DIFFERENTIAL + 0x2219, // 0xB7 BULLET (use BULLET operator, so normal font BULLET will not convert to Symbol BULLET) + 0x00F7, // 0xB8 DIVISION SIGN + 0x2260, // 0xB9 NOT EQUAL TO + 0x2261, // 0xBA IDENTICAL TO + 0x2248, // 0xBB ALMOST EQUAL TO + 0x2026, // 0xBC HORIZONTAL ELLIPSIS + 0xF8E6, // 0xBD VERTICAL ARROW EXTENDER + 0xF8E7, // 0xBE HORIZONTAL ARROW EXTENDER + 0x21B5, // 0xBF DOWN ARROW WITH CORNER LEFT + 0x2135, // 0xC0 FIRST TRANSFINITE CARDINAL + 0x2111, // 0xC1 BLACK-LETTER I + 0x211C, // 0xC2 BLACK-LETTER R + 0x2118, // 0xC3 SCRIPT P + 0x2297, // 0xC4 CIRCLED TIMES + 0x2295, // 0xC5 CIRCLED PLUS + 0x2205, // 0xC6 EMPTY SET + 0x2229, // 0xC7 INTERSECTION + 0x222A, // 0xC8 UNION + 0x2283, // 0xC9 SUPERSET OF + 0x2287, // 0xCA SUPERSET OF OR EQUAL TO + 0x2284, // 0xCB NOT A SUBSET OF + 0x2282, // 0xCC SUBSET OF + 0x2286, // 0xCD SUBSET OF OR EQUAL TO + 0x2208, // 0xCE ELEMENT OF + 0x2209, // 0xCF NOT AN ELEMENT OF + 0x2220, // 0xD0 ANGLE + 0x2207, // 0xD1 NABLA + 0x00AE, // 0xD2 REGISTERED TRADE MARK SIGN + 0x00A9, // 0xD3 COPYRIGHT SIGN + 0x2122, // 0xD4 TRADEMARK + 0x220F, // 0xD5 N-ARY PRODUCT + 0x221A, // 0xD6 SQUARE ROOT + 0x22C5, // 0xD7 DOT OPERATOR + 0x00AC, // 0xD8 NOT SIGN + 0x2227, // 0xD9 LOGICAL AND + 0x2228, // 0xDA LOGICAL OR + 0x21D4, // 0xDB LEFT RIGHT DOUBLE ARROW + 0x21D0, // 0xDC LEFT DOUBLE ARROW + 0x21D1, // 0xDD UP DOUBLE ARROW + 0x21D2, // 0xDE RIGHT DOUBLE ARROW + 0x21D3, // 0xDF DOWN DOUBLE ARROW + 0x25CA, // 0xE0 LOZENGE + 0x2329, // 0xE1 BRA + 0x00AE, // 0xE2 REGISTERED TRADE MARK SIGN + 0x00A9, // 0xE3 COPYRIGHT SIGN + 0x2122, // 0xE4 TRADEMARK + 0x2211, // 0xE5 N-ARY SUMMATION + 0x239B, // 0xE6 LEFT PAREN TOP + 0x239C, // 0xE7 LEFT PAREN EXTENDER + 0x239D, // 0xE8 LEFT PAREN BOTTOM + 0x23A1, // 0xE9 LEFT SQUARE BRACKET TOP + 0x23A2, // 0xEA LEFT SQUARE BRACKET EXTENDER + 0x23A3, // 0xEB LEFT SQUARE BRACKET BOTTOM + 0x23A7, // 0xEC LEFT CURLY BRACKET TOP + 0x23A8, // 0xED LEFT CURLY BRACKET MID + 0x23A9, // 0xEE LEFT CURLY BRACKET BOTTOM + 0x23AA, // 0xEF CURLY BRACKET EXTENDER + 0xFFFD, // 0xF0 no replacement + 0x232A, // 0xF1 KET + 0x222B, // 0xF2 INTEGRAL + 0x2320, // 0xF3 TOP HALF INTEGRAL + 0x23AE, // 0xF4 INTEGRAL EXTENDER + 0x2321, // 0xF5 BOTTOM HALF INTEGRAL + 0x239E, // 0xF6 RIGHT PAREN TOP + 0x239F, // 0xF7 RIGHT PAREN EXTENDER + 0x23A0, // 0xF8 RIGHT PAREN BOTTOM + 0x23A4, // 0xF9 RIGHT SQUARE BRACKET TOP + 0x23A5, // 0xFA RIGHT SQUARE BRACKET EXTENDER + 0x23A6, // 0xFB RIGHT SQUARE BRACKET BOTTOM + 0x23AB, // 0xFC RIGHT CURLY BRACKET TOP + 0x23AC, // 0xFD RIGHT CURLY BRACKET MID + 0x23AD, // 0xFE RIGHT CURLY BRACKET BOTTOM + 0xFFFD // 0xFF no replacement +}; + +/* Use this for debugging */ +#include <stdio.h> +void UC_log_message(char *text){ +FILE *fp; + fp=fopen("c:/temp/debug.txt","a"); + fprintf(fp,"%s",text); + fclose(fp); +} + + +//if any character is in the MS private use area (F020 through F0FF) subtract F000, for use with Symbol and Wingdings* from older software +void msdepua (uint32_t *text) +{ + while(*text){ + if(*text >= 0xF020 && *text <= 0xF0FF){ *text -= 0xF000; } + text++; + } +} + +//move characters up to MS private use area (F020 through F0FF) +void msrepua (uint16_t *text) +{ + while(*text){ + if(*text >= 0x20 && *text <= 0xFF){ *text += 0xF000; } + text++; + } +} + +// Returns the font classification code +int isNon(char *font){ +int retval; + if(!strcmp(font,"Symbol")){ + retval=CVTSYM; + } + else if(!strcmp(font,"Wingdings")){ + retval=CVTWDG; + } + else if(!strcmp(font,"ZapfDingbats")){ + retval=CVTZDG; + } + else { + retval=CVTNON; + } + return retval; +} + +// Returns the font name, given the classification code, or NULL +// The returned value must NOT be free'd +char *FontName(int code){ +char *cptr; +static char name_symbol[]="Symbol"; +static char name_wingdings[]="Wingdings"; +static char name_zapfdingbats[]="ZapfDingbats"; + switch(code){ + case CVTSYM: cptr=&name_symbol[0]; break; + case CVTWDG: cptr=&name_wingdings[0]; break; + case CVTZDG: cptr=&name_zapfdingbats[0]; break; + default: cptr=NULL; break; + } + return(cptr); +} + + +// Goes through the uint32_t string converting as needed. +int NonToUnicode(uint32_t *text, char *font){ +int retval; +unsigned int *convert_from=NULL; + retval=isNon(font); + switch(retval){ + case CVTSYM: convert_from=symbol_convert; break; + case CVTWDG: convert_from=wingdings_convert; break; + case CVTZDG: convert_from=dingbats_convert; break; + default: return(retval); //no conversion + } + while(*text){ + if(*text > 0xFF){ *text = 0xFFFD; } // out of range + else { *text = convert_from[*text]; } + text++; + } + return(retval); +} + +//returns 1 if tables are defines for UnicodeToNon translation +int CanUTN(void){ + if(from_unicode)return(1); + return(0); +} + +//translates from Unicode to some non unicode font until the target font changes. +//A target font change is like symbol -> wingdings, or symbol -> no translation +//returns the number of characters changed in ecount +//returns the enum value for the destination value in edest +void UnicodeToNon(uint16_t *text, int *ecount, int *edest){ +int count=0; +unsigned char target=0; + if(to_font){ + if(text && (target=to_font[*text])){ //There is actually something here to convert + while(*text && target==to_font[*text]){ + *text=from_unicode[*text] + (hold_pua ? 0xF000 : 0 ); + text++; + count++; + } + } + *ecount=count; + *edest=target; + } + else { // no translation tables, so change nothing and return + *ecount=0; + *edest=CVTNON; + } +} + +//Indicates the type of translation for a single character, Unicode to some non unicode +//returns the enum value for the destination value. +//If no translation tables are defined returns CVTNON (no conversions) +int SingleUnicodeToNon(uint16_t text){ + if(to_font){return(to_font[text]); } + else { return(CVTNON); } +} + +void table_filler(unsigned int *src, int code){ +unsigned int i; + for(i=0;i<0x100;i++){ + if(src[i] == 0xFFFD)continue; /* no mapping Unicode -> nonUnicode */ + if(src[i] == i)continue; /* no remapping of spaces back to spaces, for instance */ + from_unicode[src[i]] = i; + to_font[src[i]] = code; + } +} + +//possibly (re)generate the tables +void TableGen(bool new_symb,bool new_wing, bool new_zdng, bool new_pua){ +int i; + if(hold_symb != new_symb || hold_wing != new_wing + || hold_zdng != new_zdng || hold_pua != new_pua ){ // must (re)generate tables + if(!from_unicode){ // create arrays + from_unicode = (unsigned char *) calloc(0x10000,sizeof(char)); + to_font = (unsigned char *) calloc(0x10000,sizeof(char)); + // should check here for malloc error + } + hold_symb = new_symb; + hold_wing = new_wing; + hold_zdng = new_zdng; + hold_pua = new_pua; + for(i=0;i<0x10000;i++){ from_unicode[i] = to_font[i] = 0; } + if(hold_zdng)table_filler(&dingbats_convert[0],CVTZDG); + if(hold_wing)table_filler(&wingdings_convert[0],CVTWDG); + if(hold_symb)table_filler(&symbol_convert[0],CVTSYM); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/src/libuemf/symbol_convert.h b/src/libuemf/symbol_convert.h new file mode 100644 index 000000000..ac1795092 --- /dev/null +++ b/src/libuemf/symbol_convert.h @@ -0,0 +1,51 @@ +/** @file + * @brief Enhanced Metafile Input/Output + */ +/* Authors: + * David Mathog <mathog@caltech.edu> + * + * Copyright (C) 2012 Authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SEEN_UNICODE_CONVERT_H +#define SEEN_UNICODE_CONVERT_H +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> + + enum cvt_to_font {CVTNON, CVTSYM, CVTZDG, CVTWDG}; + + void msdepua(uint32_t *text); //translate down from Microsoft Private Use Area + void msrepua(uint16_t *text); //translate up to Microsoft Private Use Area + int isNon(char *font); //returns one of the cvt_to_font enum values + char *FontName(int code); //returns the font name (or NULL) given the enum code + int NonToUnicode(uint32_t *text, char *font); //nonunicode to Unicode translation + int CanUTN(void); // 1 if tables exist for UnicodeToNon translation + int SingleUnicodeToNon(uint16_t text); //retuns the enum value for this translation + void UnicodeToNon(uint16_t *text, int *ecount, int *edest); //translate Unicode to NonUnicode + void TableGen(bool symb, bool wing, bool zdng, bool pua); + +#ifdef __cplusplus +} +#endif + +#endif /* SEEN_UNICODE_CONVERT_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/libuemf/uemf.c b/src/libuemf/uemf.c new file mode 100644 index 000000000..b06990dbd --- /dev/null +++ b/src/libuemf/uemf.c @@ -0,0 +1,5523 @@ +/** + @file uemf.c Functions for manipulating EMF files and structures. + + [U_EMR*]_set all take data and return a pointer to memory holding the constructed record. + The size of that record is also returned in recsize. + It is also in the second int32 in the record, but may have been byte swapped and so not usable. + If something goes wrong a NULL pointer is returned and recsize is set to 0. + + Compile with "U_VALGRIND" defined defined to enable code which lets valgrind check each record for + uninitialized data. + + Compile with "SOL8" defined for Solaris 8 or 9 (Sparc). +*/ + +/* +File: uemf.c +Version: 0.0.21 +Date: 20-FEB-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <iconv.h> +#include <wchar.h> +#include <errno.h> +#include <string.h> +#include <limits.h> // for INT_MAX, INT_MIN +#include <math.h> // for U_ROUND() +#include <stddef.h> /* for offsetof() macro */ +#if 0 +#include <windef.h> //Not actually used, looking for collisions +#include <winnt.h> //Not actually used, looking for collisions +#include <wingdi.h> //Not actually used, looking for collisions +#endif +#include "uemf.h" +/* one prototype from uemf_endian. Put it here because end user should never need to see it, sno +not in uemf.h or uemf_endian.h */ +void U_swap2(void *ul, unsigned int count); + +/** + \brief Look up the name of the EMR record by type. Returns U_EMR_INVALID if out of range. + + \return name of the EMR record, "U_EMR_INVALID" if out of range. + \param idx EMR record type. + +*/ +char *U_emr_names(unsigned int idx){ + if(idx<U_EMR_MIN || idx > U_EMR_MAX){ idx = 0; } + static char *U_WMR_NAMES[U_EMR_MAX+1]={ + "U_EMR_INVALID", + "U_EMR_HEADER", + "U_EMR_POLYBEZIER", + "U_EMR_POLYGON", + "U_EMR_POLYLINE", + "U_EMR_POLYBEZIERTO", + "U_EMR_POLYLINETO", + "U_EMR_POLYPOLYLINE", + "U_EMR_POLYPOLYGON", + "U_EMR_SETWINDOWEXTEX", + "U_EMR_SETWINDOWORGEX", + "U_EMR_SETVIEWPORTEXTEX", + "U_EMR_SETVIEWPORTORGEX", + "U_EMR_SETBRUSHORGEX", + "U_EMR_EOF", + "U_EMR_SETPIXELV", + "U_EMR_SETMAPPERFLAGS", + "U_EMR_SETMAPMODE", + "U_EMR_SETBKMODE", + "U_EMR_SETPOLYFILLMODE", + "U_EMR_SETROP2", + "U_EMR_SETSTRETCHBLTMODE", + "U_EMR_SETTEXTALIGN", + "U_EMR_SETCOLORADJUSTMENT", + "U_EMR_SETTEXTCOLOR", + "U_EMR_SETBKCOLOR", + "U_EMR_OFFSETCLIPRGN", + "U_EMR_MOVETOEX", + "U_EMR_SETMETARGN", + "U_EMR_EXCLUDECLIPRECT", + "U_EMR_INTERSECTCLIPRECT", + "U_EMR_SCALEVIEWPORTEXTEX", + "U_EMR_SCALEWINDOWEXTEX", + "U_EMR_SAVEDC", + "U_EMR_RESTOREDC", + "U_EMR_SETWORLDTRANSFORM", + "U_EMR_MODIFYWORLDTRANSFORM", + "U_EMR_SELECTOBJECT", + "U_EMR_CREATEPEN", + "U_EMR_CREATEBRUSHINDIRECT", + "U_EMR_DELETEOBJECT", + "U_EMR_ANGLEARC", + "U_EMR_ELLIPSE", + "U_EMR_RECTANGLE", + "U_EMR_ROUNDRECT", + "U_EMR_ARC", + "U_EMR_CHORD", + "U_EMR_PIE", + "U_EMR_SELECTPALETTE", + "U_EMR_CREATEPALETTE", + "U_EMR_SETPALETTEENTRIES", + "U_EMR_RESIZEPALETTE", + "U_EMR_REALIZEPALETTE", + "U_EMR_EXTFLOODFILL", + "U_EMR_LINETO", + "U_EMR_ARCTO", + "U_EMR_POLYDRAW", + "U_EMR_SETARCDIRECTION", + "U_EMR_SETMITERLIMIT", + "U_EMR_BEGINPATH", + "U_EMR_ENDPATH", + "U_EMR_CLOSEFIGURE", + "U_EMR_FILLPATH", + "U_EMR_STROKEANDFILLPATH", + "U_EMR_STROKEPATH", + "U_EMR_FLATTENPATH", + "U_EMR_WIDENPATH", + "U_EMR_SELECTCLIPPATH", + "U_EMR_ABORTPATH", + "U_EMR_UNDEF69", + "U_EMR_COMMENT", + "U_EMR_FILLRGN", + "U_EMR_FRAMERGN", + "U_EMR_INVERTRGN", + "U_EMR_PAINTRGN", + "U_EMR_EXTSELECTCLIPRGN", + "U_EMR_BITBLT", + "U_EMR_STRETCHBLT", + "U_EMR_MASKBLT", + "U_EMR_PLGBLT", + "U_EMR_SETDIBITSTODEVICE", + "U_EMR_STRETCHDIBITS", + "U_EMR_EXTCREATEFONTINDIRECTW", + "U_EMR_EXTTEXTOUTA", + "U_EMR_EXTTEXTOUTW", + "U_EMR_POLYBEZIER16", + "U_EMR_POLYGON16", + "U_EMR_POLYLINE16", + "U_EMR_POLYBEZIERTO16", + "U_EMR_POLYLINETO16", + "U_EMR_POLYPOLYLINE16", + "U_EMR_POLYPOLYGON16", + "U_EMR_POLYDRAW16", + "U_EMR_CREATEMONOBRUSH", + "U_EMR_CREATEDIBPATTERNBRUSHPT", + "U_EMR_EXTCREATEPEN", + "U_EMR_POLYTEXTOUTA", + "U_EMR_POLYTEXTOUTW", + "U_EMR_SETICMMODE", + "U_EMR_CREATECOLORSPACE", + "U_EMR_SETCOLORSPACE", + "U_EMR_DELETECOLORSPACE", + "U_EMR_GLSRECORD", + "U_EMR_GLSBOUNDEDRECORD", + "U_EMR_PIXELFORMAT", + "U_EMR_DRAWESCAPE", + "U_EMR_EXTESCAPE", + "U_EMR_UNDEF107", + "U_EMR_SMALLTEXTOUT", + "U_EMR_FORCEUFIMAPPING", + "U_EMR_NAMEDESCAPE", + "U_EMR_COLORCORRECTPALETTE", + "U_EMR_SETICMPROFILEA", + "U_EMR_SETICMPROFILEW", + "U_EMR_ALPHABLEND", + "U_EMR_SETLAYOUT", + "U_EMR_TRANSPARENTBLT", + "U_EMR_UNDEF117", + "U_EMR_GRADIENTFILL", + "U_EMR_SETLINKEDUFIS", + "U_EMR_SETTEXTJUSTIFICATION", + "U_EMR_COLORMATCHTOTARGETW", + "U_EMR_CREATECOLORSPACEW" + }; + return(U_WMR_NAMES[idx]); +} + + + +/* ********************************************************************************************** +These definitions are for code pieces that are used many times in the following implementation. These +definitions are not needed in end user code, so they are here rather than in uemf.h. +*********************************************************************************************** */ + +//! @cond + +// this one may also be used A=Msk,B=MskBmi and F=cbMsk +#define SET_CB_FROM_PXBMI(A,B,C,D,E,F) /* A=Px, B=Bmi, C=cbImage, D=cbImage4, E=cbBmi, F=cbPx */ \ + if(A){\ + if(!B)return(NULL); /* size is derived from U_BIMAPINFO, but NOT from its size field, go figure*/ \ + C = F;\ + D = UP4(C); /* pixel array might not be a multiples of 4 bytes*/ \ + E = sizeof(U_BITMAPINFOHEADER) + 4 * get_real_color_count((const char *) &(B->bmiHeader)); /* bmiheader + colortable*/ \ + }\ + else { C = 0; D = 0; E=0; } + +// variable "off" must be declared in the function + +#define APPEND_PXBMISRC(A,B,C,D,E,F,G) /* A=record, B=U_EMR,C=cbBmi, D=Bmi, E=Px, F=cbImage, G=cbImage4 */ \ + if(C){\ + memcpy(A + off, D, C);\ + ((B *) A)->offBmiSrc = off;\ + ((B *) A)->cbBmiSrc = C;\ + off += C;\ + memcpy(A + off, E, F);\ + ((B *) A)->offBitsSrc = off;\ + ((B *) A)->cbBitsSrc = F;\ + if(G - F){ \ + off += F;\ + memset(A + off, 0, G - F); \ + }\ + }\ + else {\ + ((B *) A)->offBmiSrc = 0;\ + ((B *) A)->cbBmiSrc = 0;\ + ((B *) A)->offBitsSrc = 0;\ + ((B *) A)->cbBitsSrc = 0;\ + } + +// variable "off" must be declared in the function + +#define APPEND_MSKBMISRC(A,B,C,D,E,F,G) /* A=record, B=U_EMR*,C=cbMskBmi, D=MskBmi, E=Msk, F=cbMskImage, G=cbMskImage4 */ \ + if(C){\ + memcpy(A + off, D, C);\ + ((B *) A)->offBmiMask = off;\ + ((B *) A)->cbBmiMask = C;\ + off += C;\ + memcpy(A + off, Msk, F);\ + ((B *) A)->offBitsMask = off;\ + ((B *) A)->cbBitsMask = F;\ + if(G - F){ memset(A + off, 0, G - F); }\ + }\ + else {\ + ((B *) A)->offBmiMask = 0;\ + ((B *) A)->cbBmiMask = 0;\ + ((B *) A)->offBitsMask = 0;\ + ((B *) A)->cbBitsMask = 0;\ + } + +//! @endcond + +/* ********************************************************************************************** +These functions are used for development and debugging and should be be includied in production code. +*********************************************************************************************** */ + +/** + \brief Debugging utility, used with valgrind to find uninitialized values. Not for use in production code. + \param buf memory area to examine ! + \param size length in bytes of buf! +*/ +int memprobe( + const void *buf, + size_t size + ){ + int sum=0; + char *ptr=(char *)buf; + for(;size;size--,ptr++){ sum += *ptr; } // read all bytes, trigger valgrind warning if any uninitialized + return(sum); +} + +/** + \brief Dump an EMFHANDLES structure. Not for use in production code. + \param string Text to output before dumping eht structure + \param handle Handle + \param eht EMFHANDLES structure to dump +*/ +void dumpeht( + char *string, + unsigned int *handle, + EMFHANDLES *eht + ){ + uint32_t i; + printf("%s\n",string); + printf("sptr: %d peak: %d top: %d\n",eht->sptr,eht->peak,eht->top); + if(handle){ + printf("handle: %d \n",*handle); + } + for(i=0;i<=5;i++){ + printf("table[%d]: %d\n",i,eht->table[i]); + } + for(i=1;i<=5;i++){ + printf("stack[%d]: %d\n",i,eht->stack[i]); + } +} + +/* ********************************************************************************************** +These functions are used for Image conversions and other +utility operations. Character type conversions are in uemf_utf.c +*********************************************************************************************** */ + +/** + \brief Make up an approximate dx array to pass to emrtext_set(), based on character height and weight. + + Take abs. value of character height, get width by multiplying by 0.6, and correct weight + approximately, with formula (measured on screen for one text line of Arial). + Caller is responsible for free() on the returned pointer. + + \return pointer to dx array + \param height character height (absolute value will be used) + \param weight LF_Weight Enumeration (character weight) + \param members Number of entries to put into dx + +*/ +uint32_t *dx_set( + int32_t height, + uint32_t weight, + uint32_t members + ){ + uint32_t i, width, *dx; + dx = (uint32_t *) malloc(members * sizeof(uint32_t)); + if(dx){ + if(U_FW_DONTCARE == weight)weight=U_FW_NORMAL; + width = (uint32_t) U_ROUND(((float) (height > 0 ? height : -height)) * 0.6 * (0.00024*(float) weight + 0.904)); + for ( i = 0; i < members; i++ ){ dx[i] = width; } + } + return(dx); +} + +/** + \brief Look up the properties (a bit map) of a type of EMR record. + Bits that may be set are defined in "Draw Properties" in uemf.h, they are U_DRAW_NOTEMPTY, etc.. + + \return bitmap of EMR record properties, or U_EMR_INVALID on error or release of all memory + \param type EMR record type. If U_EMR_INVALID release memory. (There is no U_EMR_INVALID EMR record type) + +*/ +uint32_t emr_properties(uint32_t type){ + static uint32_t *table=NULL; + uint32_t result = U_EMR_INVALID; // initialized to indicate an error (on a lookup) or nothing (on a memory release) + if(type == U_EMR_INVALID){ + if(table)free(table); + table=NULL; + } + else if(type>=1 && type<U_EMR_MAX){ + if(!table){ + table = (uint32_t *) malloc(sizeof(uint32_t)*(1 + U_EMR_MAX)); + if(!table)return(result); + // 0x80 0x40 0x20 0x10 0x08 0x04 0x02 0x01 + // Path properties (U_DRAW_*) TEXT ALTERS ONLYTO VISIBLE + // PATH FORCE CLOSED NOTEMPTY + table[ 0] = 0x00; // Does not map to any EMR record + table[ 1] = 0x80; // U_EMRHEADER 1 0 0 0 0 0 0 0 + table[ 2] = 0x83; // U_EMRPOLYBEZIER 1 0 0 0 0 0 1 1 + table[ 3] = 0x87; // U_EMRPOLYGON 1 0 0 0 0 1 1 1 + table[ 4] = 0x83; // U_EMRPOLYLINE 1 0 0 0 0 0 1 1 + table[ 5] = 0x8B; // U_EMRPOLYBEZIERTO 1 0 0 0 1 0 1 1 + table[ 6] = 0x8B; // U_EMRPOLYLINETO 1 0 0 0 1 0 1 1 + table[ 7] = 0x83; // U_EMRPOLYPOLYLINE 1 0 0 0 0 0 1 1 + table[ 8] = 0x87; // U_EMRPOLYPOLYGON 1 0 0 0 0 1 1 1 + table[ 9] = 0xA0; // U_EMRSETWINDOWEXTEX 1 0 1 0 0 0 0 0 + table[ 10] = 0xA0; // U_EMRSETWINDOWORGEX 1 0 1 0 0 0 0 0 + table[ 11] = 0xA0; // U_EMRSETVIEWPORTEXTEX 1 0 1 0 0 0 0 0 + table[ 12] = 0xA0; // U_EMRSETVIEWPORTORGEX 1 0 1 0 0 0 0 0 + table[ 13] = 0xA0; // U_EMRSETBRUSHORGEX 1 0 1 0 0 0 0 0 + table[ 14] = 0x82; // U_EMREOF 1 0 1 0 0 0 0 0 Force out any pending draw + table[ 15] = 0x82; // U_EMRSETPIXELV 1 0 0 0 0 0 1 0 + table[ 16] = 0xA0; // U_EMRSETMAPPERFLAGS 1 0 1 0 0 0 0 0 + table[ 17] = 0xA0; // U_EMRSETMAPMODE 1 0 1 0 0 0 0 0 + table[ 18] = 0x20; // U_EMRSETBKMODE 0 0 1 0 0 0 0 0 + table[ 19] = 0xA0; // U_EMRSETPOLYFILLMODE 1 0 1 0 0 0 0 0 + table[ 20] = 0xA0; // U_EMRSETROP2 1 0 1 0 0 0 0 0 + table[ 21] = 0xA0; // U_EMRSETSTRETCHBLTMODE 1 0 1 0 0 0 0 0 + table[ 22] = 0x20; // U_EMRSETTEXTALIGN 0 0 1 0 0 0 0 0 + table[ 23] = 0xA0; // U_EMRSETCOLORADJUSTMENT 1 0 1 0 0 0 0 0 + table[ 24] = 0x20; // U_EMRSETTEXTCOLOR 0 0 1 0 0 0 0 0 + table[ 25] = 0x20; // U_EMRSETBKCOLOR 0 0 1 0 0 0 0 0 + table[ 26] = 0xA0; // U_EMROFFSETCLIPRGN 1 0 1 0 0 0 0 0 + table[ 27] = 0x89; // U_EMRMOVETOEX 1 0 0 0 1 0 0 1 + table[ 28] = 0xA0; // U_EMRSETMETARGN 1 0 1 0 0 0 0 0 + table[ 29] = 0xA0; // U_EMREXCLUDECLIPRECT 1 0 1 0 0 0 0 0 + table[ 30] = 0xA0; // U_EMRINTERSECTCLIPRECT 1 0 1 0 0 0 0 0 + table[ 31] = 0xA0; // U_EMRSCALEVIEWPORTEXTEX 1 0 1 0 0 0 0 0 + table[ 32] = 0xA0; // U_EMRSCALEWINDOWEXTEX 1 0 1 0 0 0 0 0 + table[ 33] = 0xA0; // U_EMRSAVEDC 1 0 1 0 0 0 0 0 + table[ 34] = 0xA0; // U_EMRRESTOREDC 1 0 1 0 0 0 0 0 + table[ 35] = 0xA0; // U_EMRSETWORLDTRANSFORM 1 0 1 0 0 0 0 0 + table[ 36] = 0xA0; // U_EMRMODIFYWORLDTRANSFORM 1 0 1 0 0 0 0 0 + table[ 37] = 0x20; // U_EMRSELECTOBJECT 0 0 1 0 0 0 0 0 + table[ 38] = 0x20; // U_EMRCREATEPEN 0 0 1 0 0 0 0 0 + table[ 39] = 0x20; // U_EMRCREATEBRUSHINDIRECT 0 0 1 0 0 0 0 0 + table[ 40] = 0x20; // U_EMRDELETEOBJECT 0 0 1 0 0 0 0 0 + table[ 41] = 0x83; // U_EMRANGLEARC 1 0 0 0 0 0 1 1 + table[ 42] = 0x87; // U_EMRELLIPSE 1 0 0 0 0 1 1 1 + table[ 43] = 0x87; // U_EMRRECTANGLE 1 0 0 0 0 1 1 1 + table[ 44] = 0x87; // U_EMRROUNDRECT 1 0 0 0 0 1 1 1 + table[ 45] = 0x83; // U_EMRARC 1 0 0 0 0 0 1 1 + table[ 46] = 0x87; // U_EMRCHORD 1 0 0 0 0 1 1 1 + table[ 47] = 0x87; // U_EMRPIE 1 0 0 0 0 1 1 1 + table[ 48] = 0xA0; // U_EMRSELECTPALETTE 1 0 1 0 0 0 0 0 + table[ 49] = 0xA0; // U_EMRCREATEPALETTE 1 0 1 0 0 0 0 0 + table[ 50] = 0xA0; // U_EMRSETPALETTEENTRIES 1 0 1 0 0 0 0 0 + table[ 51] = 0xA0; // U_EMRRESIZEPALETTE 1 0 1 0 0 0 0 0 + table[ 52] = 0xA0; // U_EMRREALIZEPALETTE 1 0 1 0 0 0 0 0 + table[ 53] = 0x82; // U_EMREXTFLOODFILL 1 0 0 0 0 0 1 0 + table[ 54] = 0x8B; // U_EMRLINETO 1 0 0 0 1 0 1 1 + table[ 55] = 0x8B; // U_EMRARCTO 1 0 0 0 1 0 1 1 + table[ 56] = 0x83; // U_EMRPOLYDRAW 1 0 0 0 0 0 1 1 + table[ 57] = 0xA0; // U_EMRSETARCDIRECTION 1 0 1 0 0 0 0 0 + table[ 58] = 0xA0; // U_EMRSETMITERLIMIT 1 0 1 0 0 0 0 0 + table[ 59] = 0xE0; // U_EMRBEGINPATH 1 1 1 0 0 0 0 0 + table[ 60] = 0x80; // U_EMRENDPATH 1 0 0 0 0 0 0 0 + table[ 61] = 0x84; // U_EMRCLOSEFIGURE 1 0 0 0 0 1 0 0 + table[ 62] = 0x94; // U_EMRFILLPATH 1 0 0 1 0 1 0 0 + table[ 63] = 0x94; // U_EMRSTROKEANDFILLPATH 1 0 0 1 0 1 0 0 + table[ 64] = 0x90; // U_EMRSTROKEPATH 1 0 0 1 0 0 0 0 + table[ 65] = 0xA0; // U_EMRFLATTENPATH 1 0 1 0 0 0 0 0 + table[ 66] = 0xA0; // U_EMRWIDENPATH 1 0 1 0 0 0 0 0 + table[ 67] = 0xA0; // U_EMRSELECTCLIPPATH 1 0 1 0 0 0 0 0 + table[ 68] = 0xA0; // U_EMRABORTPATH 1 0 1 0 0 0 0 0 + table[ 69] = 0xA0; // U_EMRUNDEF69 1 0 1 0 0 0 0 0 + table[ 70] = 0x00; // U_EMRCOMMENT 0 0 0 0 0 0 0 0 + table[ 71] = 0x82; // U_EMRFILLRGN 1 0 0 0 0 0 1 0 + table[ 72] = 0x82; // U_EMRFRAMERGN 1 0 0 0 0 0 1 0 + table[ 73] = 0x82; // U_EMRINVERTRGN 1 0 0 0 0 0 1 0 + table[ 74] = 0x82; // U_EMRPAINTRGN 1 0 0 0 0 0 1 0 + table[ 75] = 0xA0; // U_EMREXTSELECTCLIPRGN 1 0 1 0 0 0 0 0 + table[ 76] = 0x82; // U_EMRBITBLT 1 0 0 0 0 0 1 0 + table[ 77] = 0x82; // U_EMRSTRETCHBLT 1 0 0 0 0 0 1 0 + table[ 78] = 0x82; // U_EMRMASKBLT 1 0 0 0 0 0 1 0 + table[ 79] = 0x82; // U_EMRPLGBLT 1 0 0 0 0 0 1 0 + table[ 80] = 0xA0; // U_EMRSETDIBITSTODEVICE 1 0 1 0 0 0 0 0 + table[ 81] = 0xA0; // U_EMRSTRETCHDIBITS 1 0 1 0 0 0 0 0 + table[ 82] = 0x20; // U_EMREXTCREATEFONTINDIRECTW 0 0 1 0 0 0 0 0 + table[ 83] = 0x02; // U_EMREXTTEXTOUTA 0 0 0 0 0 0 1 0 + table[ 84] = 0x02; // U_EMREXTTEXTOUTW 0 0 0 0 0 0 1 0 + table[ 85] = 0x83; // U_EMRPOLYBEZIER16 1 0 0 0 0 0 1 1 + table[ 86] = 0x83; // U_EMRPOLYGON16 1 0 0 0 0 0 1 1 + table[ 87] = 0x83; // U_EMRPOLYLINE16 1 0 0 0 0 0 1 1 + table[ 88] = 0x8B; // U_EMRPOLYBEZIERTO16 1 0 0 0 1 0 1 1 + table[ 89] = 0x8B; // U_EMRPOLYLINETO16 1 0 0 0 1 0 1 1 + table[ 90] = 0x83; // U_EMRPOLYPOLYLINE16 1 0 0 0 0 0 1 1 + table[ 91] = 0x87; // U_EMRPOLYPOLYGON16 1 0 0 0 0 1 1 1 + table[ 92] = 0x83; // U_EMRPOLYDRAW16 1 0 0 0 0 0 1 1 + table[ 93] = 0x80; // U_EMRCREATEMONOBRUSH 1 0 0 0 0 0 0 0 Not selected yet, so no change in drawing conditions + table[ 94] = 0x80; // U_EMRCREATEDIBPATTERNBRUSHPT 1 0 0 0 0 0 0 0 " + table[ 95] = 0x00; // U_EMREXTCREATEPEN 0 0 0 0 0 0 0 0 " + table[ 96] = 0x02; // U_EMRPOLYTEXTOUTA 0 0 0 0 0 0 1 0 + table[ 97] = 0x02; // U_EMRPOLYTEXTOUTW 0 0 0 0 0 0 1 0 + table[ 98] = 0xA0; // U_EMRSETICMMODE 1 0 1 0 0 0 0 0 + table[ 99] = 0xA0; // U_EMRCREATECOLORSPACE 1 0 1 0 0 0 0 0 + table[100] = 0xA0; // U_EMRSETCOLORSPACE 1 0 1 0 0 0 0 0 + table[101] = 0xA0; // U_EMRDELETECOLORSPACE 1 0 1 0 0 0 0 0 + table[102] = 0xA0; // U_EMRGLSRECORD 1 0 1 0 0 0 0 0 + table[103] = 0xA0; // U_EMRGLSBOUNDEDRECORD 1 0 1 0 0 0 0 0 + table[104] = 0xA0; // U_EMRPIXELFORMAT 1 0 1 0 0 0 0 0 + table[105] = 0xA0; // U_EMRDRAWESCAPE 1 0 1 0 0 0 0 0 + table[106] = 0xA0; // U_EMREXTESCAPE 1 0 1 0 0 0 0 0 + table[107] = 0xA0; // U_EMRUNDEF107 1 0 1 0 0 0 0 0 + table[108] = 0x02; // U_EMRSMALLTEXTOUT 0 0 0 0 0 0 1 0 + table[109] = 0xA0; // U_EMRFORCEUFIMAPPING 1 0 1 0 0 0 0 0 + table[110] = 0xA0; // U_EMRNAMEDESCAPE 1 0 1 0 0 0 0 0 + table[111] = 0xA0; // U_EMRCOLORCORRECTPALETTE 1 0 1 0 0 0 0 0 + table[112] = 0xA0; // U_EMRSETICMPROFILEA 1 0 1 0 0 0 0 0 + table[113] = 0xA0; // U_EMRSETICMPROFILEW 1 0 1 0 0 0 0 0 + table[114] = 0x82; // U_EMRALPHABLEND 1 0 0 0 0 0 1 0 + table[115] = 0xA0; // U_EMRSETLAYOUT 1 0 1 0 0 0 0 0 + table[116] = 0x82; // U_EMRTRANSPARENTBLT 1 0 0 0 0 0 1 0 + table[117] = 0xA0; // U_EMRUNDEF117 1 0 1 0 0 0 0 0 + table[118] = 0x82; // U_EMRGRADIENTFILL 1 0 0 0 0 0 1 0 + table[119] = 0xA0; // U_EMRSETLINKEDUFIS 1 0 1 0 0 0 0 0 + table[120] = 0x20; // U_EMRSETTEXTJUSTIFICATION 0 0 1 0 0 0 0 0 + table[121] = 0xA0; // U_EMRCOLORMATCHTOTARGETW 1 0 1 0 0 0 0 0 + table[122] = 0xA0; // U_EMRCREATECOLORSPACEW 1 0 1 0 0 0 0 0 + } + result = table[type]; + } + return(result); +} + +/** + \brief Derive from bounding rect, start and end radials, for arc, chord, or pie, the center, start, and end points, and the bounding rectangle. + + \return 0 on success, other values on errors. + \param rclBox bounding rectangle + \param ArcStart start of arc + \param ArcEnd end of arc + \param f1 1 if rotation angle >= 180, else 0 + \param f2 Rotation direction, 1 if counter clockwise, else 0 + \param center Center coordinates + \param start Start coordinates (point on the ellipse defined by rect) + \param end End coordinates (point on the ellipse defined by rect) + \param size W,H of the x,y axes of the bounding rectangle. +*/ +int emr_arc_points_common( + PU_RECTL rclBox, + PU_POINTL ArcStart, + PU_POINTL ArcEnd, + int *f1, + int f2, + PU_PAIRF center, + PU_PAIRF start, + PU_PAIRF end, + PU_PAIRF size + ){ + U_PAIRF estart; // EMF start position, defines a radial + U_PAIRF eend; // EMF end position, defines a radial + U_PAIRF vec_estart; // define a unit vector from the center to estart + U_PAIRF vec_eend; // define a unit vector from the center to eend + U_PAIRF radii; // x,y radii of ellipse + U_PAIRF ratio; // intermediate value + float scale, cross; + center->x = ((float)(rclBox->left + rclBox->right ))/2.0; + center->y = ((float)(rclBox->top + rclBox->bottom))/2.0; + size->x = (float)(rclBox->right - rclBox->left ); + size->y = (float)(rclBox->bottom - rclBox->top ); + estart.x = (float)(ArcStart->x); + estart.y = (float)(ArcStart->y); + eend.x = (float)(ArcEnd->x); + eend.y = (float)(ArcEnd->y); + radii.x = size->x/2.0; + radii.y = size->y/2.0; + + vec_estart.x = (estart.x - center->x); // initial vector, not unit length + vec_estart.y = (estart.y - center->y); + scale = sqrt(vec_estart.x*vec_estart.x + vec_estart.y*vec_estart.y); + if(!scale)return(1); // bogus record, has start at center + vec_estart.x /= scale; // now a unit vector + vec_estart.y /= scale; + + vec_eend.x = (eend.x - center->x); // initial vector, not unit length + vec_eend.y = (eend.y - center->y); + scale = sqrt(vec_eend.x*vec_eend.x + vec_eend.y*vec_eend.y); + if(!scale)return(2); // bogus record, has end at center + vec_eend.x /= scale; // now a unit vector + vec_eend.y /= scale; + + + // Find the intersection of the vectors with the ellipse. With no loss of generality + // we can translate the ellipse to the origin, then we just need to find tu (t a factor, u the unit vector) + // that also satisfies (x/Rx)^2 + (y/Ry)^2 = 1. x is t*(ux), y is t*(uy), where ux,uy are the x,y components + // of the unit vector. Substituting gives: + // (t*(ux)/Rx)^2 + (t*(uy)/Ry)^2 = 1 + // t^2 = 1/( (ux/Rx)^2 + (uy/Ry)^2 ) + // t = sqrt(1/( (ux/Rx)^2 + (uy/Ry)^2 )) + + ratio.x = vec_estart.x/radii.x; + ratio.y = vec_estart.y/radii.y; + ratio.x *= ratio.x; // we only use the square + ratio.y *= ratio.y; + scale = 1.0/sqrt(ratio.x + ratio.y); + start->x = center->x + scale * vec_estart.x; + start->y = center->y + scale * vec_estart.y; + + ratio.x = vec_eend.x/radii.x; + ratio.y = vec_eend.y/radii.y; + ratio.x *= ratio.x; // we only use the square + ratio.y *= ratio.y; + scale = 1.0/sqrt(ratio.x + ratio.y); + end->x = center->x + scale * vec_eend.x; + end->y = center->y + scale * vec_eend.y; + + //lastly figure out if the swept angle is >180 degrees or not, based on the direction of rotation + //and the two unit vectors. + + cross = vec_estart.x * vec_eend.y - vec_estart.y * vec_eend.x; + if(!f2){ // counter clockwise rotation + if(cross >=0){ *f1 = 1; } + else { *f1 = 0; } + } + else { + if(cross >=0){ *f1 = 0; } + else { *f1 = 1; } + } + + + return(0); +} + +/** + \brief Derive from an EMF arc, chord, or pie the center, start, and end points, and the bounding rectangle. + + \return 0 on success, other values on errors. + \param record U_EMRPIE, U_EMRCHORD, or _EMRARC record + \param f1 1 if rotation angle >= 180, else 0 + \param f2 Rotation direction, 1 if counter clockwise, else 0 + \param center Center coordinates + \param start Start coordinates (point on the ellipse defined by rect) + \param end End coordinates (point on the ellipse defined by rect) + \param size W,H of the x,y axes of the bounding rectangle. +*/ +int emr_arc_points( + PU_ENHMETARECORD record, + int *f1, + int f2, + PU_PAIRF center, + PU_PAIRF start, + PU_PAIRF end, + PU_PAIRF size + ){ + PU_EMRARC pEmr = (PU_EMRARC) (record); + return emr_arc_points_common(&(pEmr->rclBox), &(pEmr->ptlStart), &(pEmr->ptlEnd), f1, f2, center, start, end, size ); +} + +/** + \brief Convert a U_RGBA 32 bit pixmap to one of many different types of DIB pixmaps. + + Conversions to formats using color tables assume that the color table can hold every color + in the input image. If that assumption is false then the conversion will fail. Conversion + from 8 bit color to N bit colors (N<8) do so by shifting the appropriate number of bits. + + \return 0 on success, other values on errors. + \param px DIB pixel array + \param cbPx DIB pixel array size in bytes + \param ct DIB color table + \param numCt DIB color table number of entries + \param rgba_px U_RGBA pixel array (32 bits) + \param w Width of pixel array + \param h Height of pixel array + \param stride Row stride of input pixel array in bytes + \param colortype DIB BitCount Enumeration + \param use_ct If true use color table (only for 1-16 bit DIBs) + \param invert If DIB rows are in opposite order from RGBA rows +*/ +int RGBA_to_DIB( + char **px, + uint32_t *cbPx, + PU_RGBQUAD *ct, + int *numCt, + const char *rgba_px, + int w, + int h, + int stride, + uint32_t colortype, + int use_ct, + int invert + ){ + int bs; + int pad; + int i,j,k; + int istart, iend, iinc; + uint8_t r,g,b,a,tmp8; + char *pxptr; + const char *rptr; + int found; + int usedbytes; + U_RGBQUAD color; + PU_RGBQUAD lct; + int32_t index; + + *px=NULL; + *ct=NULL; + *numCt=0; + *cbPx=0; + // sanity checking + if(!w || !h || !stride || !colortype || !rgba_px)return(1); + if(use_ct && colortype >= U_BCBM_COLOR16)return(2); //color tables not used above 16 bit pixels + if(!use_ct && colortype < U_BCBM_COLOR16)return(3); //color tables mandatory for < 16 bit + + bs = colortype/8; + if(bs<1){ + bs=1; + usedbytes = (w*colortype + 7)/8; // width of line in fully and partially occupied bytes + } + else { + usedbytes = w*bs; + } + pad = UP4(usedbytes) - usedbytes; // DIB rows must be aligned on 4 byte boundaries, they are padded at the end to accomplish this.; + *cbPx = h * (usedbytes + pad); // Rows must start on a 4 byte boundary! + *px = (char *) malloc(*cbPx); + if(!px)return(4); + if(use_ct){ + *numCt = 1<< colortype; + if(*numCt >w*h)*numCt=w*h; + lct = (PU_RGBQUAD) malloc(*numCt * sizeof(U_RGBQUAD)); + if(!lct)return(5); + *ct = lct; + } + + if(invert){ + istart = h-1; + iend = -1; + iinc = -1; + } + else { + istart = 0; + iend = h; + iinc = 1; + } + + found = 0; + tmp8 = 0; + pxptr = *px; + for(i=istart; i!=iend; i+=iinc){ + rptr= rgba_px + i*stride; + for(j=0; j<w; j++){ + r = *rptr++; + g = *rptr++; + b = *rptr++; + a = *rptr++; + if(use_ct){ + color = U_BGRA(r,g,b,a); // color has order in memory: b,g,r,a + index = -1; + for(lct = *ct, k=0; k<found; k++,lct++){ // Is this color in the table (VERY inefficient if there are a lot of colors!!!) + if(*(uint32_t *)lct != *(uint32_t *) &color)continue; + index =k; + break; + } + if(index==-1){ // add a color + found++; + if(found > *numCt){ // More colors found than are supported by the color table + free(*ct); + free(*px); + *numCt=0; + *cbPx=0; + return(6); + } + index = found - 1; + *lct = color; + } + switch(colortype){ + case U_BCBM_MONOCHROME: // 2 colors. bmiColors array has two entries + tmp8 = tmp8 >> 1; // This seems wrong, as it fills from the top of each byte. But it works. + tmp8 |= index << 7; + if(!((j+1) % 8)){ + *pxptr++ = tmp8; + tmp8 = 0; + } + break; + case U_BCBM_COLOR4: // 2^4 colors. bmiColors array has 16 entries + tmp8 = tmp8 << 4; + tmp8 |= index; + if(!((j+1) % 2)){ + *pxptr++ = tmp8; + tmp8 = 0; + } + break; + case U_BCBM_COLOR8: // 2^8 colors. bmiColors array has 256 entries + tmp8 = index; + *pxptr++ = tmp8; + break; + case U_BCBM_COLOR16: // 2^16 colors. (Several different color methods)) + case U_BCBM_COLOR24: // 2^24 colors. bmiColors is not used. Pixels are U_RGBTRIPLE. + case U_BCBM_COLOR32: // 2^32 colors. bmiColors is not used. Pixels are U_RGBQUAD. + case U_BCBM_EXPLICIT: // Derinved from JPG or PNG compressed image or ? + default: + return(7); // This should not be possible, but might happen with memory corruption + } + } + else { + switch(colortype){ + case U_BCBM_COLOR16: // 2^16 colors. (Several different color methods)) + b /= 8; g /= 8; r /= 8; + // Do it in this way so that the bytes are always stored Little Endian + tmp8 = b; + tmp8 |= g<<5; // least significant 3 bits of green + *pxptr++ = tmp8; + tmp8 = g>>3; // most significant 2 bits of green (there are only 5 bits of data) + tmp8 |= r<<2; + *pxptr++ = tmp8; + break; + case U_BCBM_COLOR24: // 2^24 colors. bmiColors is not used. Pixels are U_RGBTRIPLE. + *pxptr++ = b; + *pxptr++ = g; + *pxptr++ = r; + break; + case U_BCBM_COLOR32: // 2^32 colors. bmiColors is not used. Pixels are U_RGBQUAD. + *pxptr++ = b; + *pxptr++ = g; + *pxptr++ = r; + *pxptr++ = a; + break; + case U_BCBM_MONOCHROME: // 2 colors. bmiColors array has two entries + case U_BCBM_COLOR4: // 2^4 colors. bmiColors array has 16 entries + case U_BCBM_COLOR8: // 2^8 colors. bmiColors array has 256 entries + case U_BCBM_EXPLICIT: // Derinved from JPG or PNG compressed image or ? + default: + return(7); // This should not be possible, but might happen with memory corruption + } + } + } + if( use_ct && colortype == U_BCBM_MONOCHROME && (j % 8) ){ + *pxptr++ = tmp8; // Write last few indices + tmp8 = 0; + } + if( use_ct && colortype == U_BCBM_COLOR4 && (j % 2) ){ + *pxptr++ = tmp8; // Write last few indices + tmp8 = 0; + } + if(pad){ + memset(pxptr,0,pad); // not strictly necessary, but set all bytes so that we can find important unset ones with valgrind + pxptr += pad; + } + } + return(0); +} + +/** + \brief Get the actual number of colors in the color table from the BitMapInfoHeader. + BitmapInfoHeader may list 0 for some types which implies the maximum value. + If the image is big enough, that is set by the bit count, as in 256 for an 8 + bit image. + If the image is smaller it is set by width * height. + Note, this may be called by WMF code, so it is not safe to assume the data is aligned. + + \return Number of entries in the color table. + \param Bmih char * pointer to the U_BITMAPINFOHEADER +*/ +int get_real_color_count( + const char *Bmih + ){ + int Colors, BitCount, Width, Height; + uint32_t utmp4; + uint16_t utmp2; + int32_t tmp4; + char *cBmih = (char *) Bmih; + memcpy(&utmp4, cBmih + offsetof(U_BITMAPINFOHEADER,biClrUsed), 4); Colors = utmp4; + memcpy(&utmp2, cBmih + offsetof(U_BITMAPINFOHEADER,biBitCount), 2); BitCount = utmp2; + memcpy(&tmp4, cBmih + offsetof(U_BITMAPINFOHEADER,biWidth), 4); Width = tmp4; + memcpy(&tmp4, cBmih + offsetof(U_BITMAPINFOHEADER,biHeight), 4); Height = tmp4; + return(get_real_color_icount(Colors, BitCount, Width, Height)); +} + +/** + \brief Get the actual number of colors in the color table from the ClrUsed, BitCount, Width, and Height. + BitmapInfoHeader may list 0 for some types which implies the maximum value. + If the image is big enough, that is set by the bit count, as in 256 for an 8 + bit image. + If the image is smaller it is set by width * height. + + \return Number of entries in the color table. + \param PU_BITMAPINFOHEADER pointer to to the U_BITMAPINFOHEADER +*/ +int get_real_color_icount( + int Colors, + int BitCount, + int Width, + int Height + ){ + int area = Width * Height; + if(area < 0){ area = -area; } /* Height might be negative */ + if(Colors == 0){ + if( BitCount == U_BCBM_MONOCHROME){ Colors = 2; } + else if(BitCount == U_BCBM_COLOR4 ){ Colors = 16; } + else if(BitCount == U_BCBM_COLOR8 ){ Colors = 256; } + if(Colors > area){ Colors = area; } + } + return(Colors); +} + + +/** + \brief Get the DIB parameters from the BMI of the record for use by DBI_to_RGBA() + + \return BI_Compression Enumeration. For anything other than U_BI_RGB values other than px may not be valid. + \param pEmr pointer to EMR record that has a U_BITMAPINFO and bitmap + \param offBitsSrc Offset to the bitmap + \param offBmiSrc Offset to the U_BITMAPINFO + \param px pointer to DIB pixel array in pEmr + \param ct pointer to DIB color table in pEmr + \param numCt DIB color table number of entries, for PNG or JPG returns the number of bytes in the image + \param width Width of pixel array + \param height Height of pixel array (always returned as a positive number) + \param colortype DIB BitCount Enumeration + \param invert If DIB rows are in opposite order from RGBA rows +*/ +int get_DIB_params( + void *pEmr, + uint32_t offBitsSrc, + uint32_t offBmiSrc, + const char **px, + const U_RGBQUAD **ct, + uint32_t *numCt, + uint32_t *width, + uint32_t *height, + uint32_t *colortype, + uint32_t *invert + ){ + uint32_t bic; + PU_BITMAPINFO Bmi = (PU_BITMAPINFO)((char *)pEmr + offBmiSrc); + PU_BITMAPINFOHEADER Bmih = &(Bmi->bmiHeader); + /* if biCompression is not U_BI_RGB some or all of the following might not hold real values */ + bic = Bmih->biCompression; + *width = Bmih->biWidth; + *colortype = Bmih->biBitCount; + if(Bmih->biHeight < 0){ + *height = -Bmih->biHeight; + *invert = 1; + } + else { + *height = Bmih->biHeight; + *invert = 0; + } + if(bic == U_BI_RGB){ + *numCt = get_real_color_count((const char *) Bmih); + if( numCt){ *ct = (PU_RGBQUAD) ((char *)Bmi + sizeof(U_BITMAPINFOHEADER)); } + else { *ct = NULL; } + } + else { + *numCt = Bmih->biSizeImage; + *ct = NULL; + } + *px = (char *)((char *)pEmr + offBitsSrc); + return(bic); +} + +/** + \brief Convert one of many different types of DIB pixmaps to an RGBA 32 bit pixmap. + + \return 0 on success, other values on errors. + \param px DIB pixel array + \param ct DIB color table + \param numCt DIB color table number of entries + \param rgba_px U_RGBA pixel array (32 bits), created by this routine, caller must free. + \param w Width of pixel array in the record + \param h Height of pixel array in the record + \param colortype DIB BitCount Enumeration + \param use_ct Kept for symmetry with RGBA_to_DIB, should be set to numCt + \param invert If DIB rows are in opposite order from RGBA rows +*/ +int DIB_to_RGBA( + const char *px, + const U_RGBQUAD *ct, + int numCt, + char **rgba_px, + int w, + int h, + uint32_t colortype, + int use_ct, + int invert + ){ + uint32_t cbRgba_px; + int stride; + int bs; + int pad; + int i,j; + int istart, iend, iinc; + uint8_t r,g,b,a,tmp8; + const char *pxptr; + char *rptr; + int usedbytes; + U_RGBQUAD color; + int32_t index; + + // sanity checking + if(!w || !h || !colortype || !px)return(1); + if(use_ct && colortype >= U_BCBM_COLOR16)return(2); //color tables not used above 16 bit pixels + if(!use_ct && colortype < U_BCBM_COLOR16)return(3); //color tables mandatory for < 16 bit + if(use_ct && !numCt)return(4); //color table not adequately described + + stride = w * 4; + cbRgba_px = stride * h; + bs = colortype/8; + if(bs<1){ + bs=1; + usedbytes = (w*colortype + 7)/8; // width of line in fully and partially occupied bytes + } + else { + usedbytes = w*bs; + } + pad = UP4(usedbytes) - usedbytes; // DIB rows must be aligned on 4 byte boundaries, they are padded at the end to accomplish this.; + *rgba_px = (char *) malloc(cbRgba_px); + if(!rgba_px)return(4); + + if(invert){ + istart = h-1; + iend = -1; + iinc = -1; + } + else { + istart = 0; + iend = h; + iinc = 1; + } + + pxptr = px; + tmp8 = 0; // silences a compiler warning, tmp8 always sets when j=0, so never used uninitialized + for(i=istart; i!=iend; i+=iinc){ + rptr= *rgba_px + i*stride; + for(j=0; j<w; j++){ + if(use_ct){ + switch(colortype){ + case U_BCBM_MONOCHROME: // 2 colors. bmiColors array has two entries + if(!(j % 8)){ tmp8 = *pxptr++; } + index = 0x80 & tmp8; // This seems wrong, as lowest position is top bit, but it works. + index = index >> 7; + tmp8 = tmp8 << 1; + break; + case U_BCBM_COLOR4: // 2^4 colors. bmiColors array has 16 entries + if(!(j % 2)){ tmp8 = *pxptr++; } + index = 0xF0 & tmp8; + index = index >> 4; + tmp8 = tmp8 << 4; + break; + case U_BCBM_COLOR8: // 2^8 colors. bmiColors array has 256 entries + index = (uint8_t) *pxptr++;; + break; + case U_BCBM_COLOR16: // 2^16 colors. (Several different color methods)) + case U_BCBM_COLOR24: // 2^24 colors. bmiColors is not used. Pixels are U_RGBTRIPLE. + case U_BCBM_COLOR32: // 2^32 colors. bmiColors is not used. Pixels are U_RGBQUAD. + case U_BCBM_EXPLICIT: // Derinved from JPG or PNG compressed image or ? + default: + return(7); // This should not be possible, but might happen with memory corruption + } + color = ct[index]; + b = U_BGRAGetB(color); + g = U_BGRAGetG(color); + r = U_BGRAGetR(color); + a = U_BGRAGetA(color); + } + else { + switch(colortype){ + case U_BCBM_COLOR16: // 2^16 colors. (Several different color methods)) + // Do it in this way because the bytes are always stored Little Endian + tmp8 = *pxptr++; + b = (0x1F & tmp8) <<3; // 5 bits of b into the top 5 of 8 + g = tmp8 >> 5; // least significant 3 bits of green + tmp8 = *pxptr++; + r = (0x7C & tmp8) << 1; // 5 bits of r into the top 5 of 8 + g |= (0x3 & tmp8) << 3; // most significant 2 bits of green (there are only 5 bits of data) + g = g << 3; // restore intensity (have lost 3 bits of accuracy) + a = 0; + break; + case U_BCBM_COLOR24: // 2^24 colors. bmiColors is not used. Pixels are U_RGBTRIPLE. + b = *pxptr++; + g = *pxptr++; + r = *pxptr++; + a = 0; + break; + case U_BCBM_COLOR32: // 2^32 colors. bmiColors is not used. Pixels are U_RGBQUAD. + b = *pxptr++; + g = *pxptr++; + r = *pxptr++; + a = *pxptr++; + break; + case U_BCBM_MONOCHROME: // 2 colors. bmiColors array has two entries + case U_BCBM_COLOR4: // 2^4 colors. bmiColors array has 16 entries + case U_BCBM_COLOR8: // 2^8 colors. bmiColors array has 256 entries + case U_BCBM_EXPLICIT: // Derinved from JPG or PNG compressed image or ? + default: + return(7); // This should not be possible, but might happen with memory corruption + } + } + *rptr++ = r; + *rptr++ = g; + *rptr++ = b; + *rptr++ = a; + } + for(j=0; j<pad; j++){ pxptr++; } // DIB rows are all 4 byte aligned + } + return(0); +} + +/** + \brief Extract a subset of an RGBA bitmap array. + Frees the incoming bitmap array IF a subset is extracted, otherwise it is left alone. + If the entire array is extracted it just returns the incoming pointer. + If the subset requested is partially outside of the bitmap the region is clipped to the + bitmap boundaries and extracted. This seems to be a (very) grey area in EMF files, and + even different Microsoft applications do not always do the same thing. For instance, + XP Preview gives some different images for EMR_BITBLT records than does the "import image" + (but not unpacked) view in PowerPoint. Since all of these states are probably best viewed + as undefined or errors we can only try to do something reasonable and not blow up when + encountering one. + + \return Pointer to the sub array on success, NULL otherwise. + \param rgba_px U_RGBA pixel array (32 bits), created by this routine, caller must free. + \param w Width of pixel array in the record + \param h Height of pixel array in the record + \param sl start left position in the pixel array in the record to start extracting + \param st start top position in the pixel array in the record to start extracting + \param eew Width of pixel array to extract + \param eeh Height of pixel array to extract +*/ +char *RGBA_to_RGBA( + char *rgba_px, + int w, + int h, + int sl, + int st, + int *eew, + int *eeh + ){ + int i; + char *sub; + char *sptr; + int ew = *eew; + int eh = *eeh; + + // sanity checking + if(w<=0 || h<=0 || ew<=0 || eh<=0 || !rgba_px)return(NULL); + + if(sl>w || st >h)return(NULL); // This is hopeless, the start point is outside of the array. + if(sl<0){ + if(sl+ew<=0)return(NULL); // This is hopeless, the start point is outside of the array. + ew += sl; + sl = 0; + } + if(st<0){ + if(st+eh<=0)return(NULL); // This is hopeless, the start point is outside of the array. + eh += st; + st = 0; + } + if(sl+ew > w)ew=w-sl; + if(st+eh > h)eh=h-st; + if(!sl && !st && (ew == w) && (eh == h)){ + sub = rgba_px; + } + else { + sptr = sub = malloc(ew*eh*4); + if(!sub)return(NULL); + for(i=st; i<st+eh; i++){ + memcpy(sptr,rgba_px + i*w*4 + sl*4,4*ew); + sptr += 4*ew; + } + free(rgba_px); + } + *eeh = eh; + *eew = ew; + return(sub); + } + + +/* ********************************************************************************************** +These functions are for setting up, appending to, and then tearing down an EMF structure, including +writing the final data structure out to a file. +*********************************************************************************************** */ + +/** + \brief Duplicate an EMR record. + \param emr record to duplicate +*/ +char *emr_dup( + const char *emr + ){ + char *dup; + int irecsize; + + if(!emr)return(NULL); + irecsize = ((PU_EMR)emr)->nSize; + dup=malloc(irecsize); + if(dup){ memcpy(dup,emr,irecsize); } + return(dup); +} + + +/** + \brief Start constructing an emf in memory. Supply the file name and initial size. + \return 0 for success, >=0 for failure. + \param name EMF filename (will be opened) + \param initsize Initialize EMF in memory to hold this many bytes + \param chunksize When needed increase EMF in memory by this number of bytes + \param et EMF in memory + + +*/ +int emf_start( + const char *name, + const uint32_t initsize, + const uint32_t chunksize, + EMFTRACK **et + ){ + FILE *fp; + EMFTRACK *etl=NULL; + + if(initsize < 1)return(1); + if(chunksize < 1)return(2); + if(!name)return(3); + etl = (EMFTRACK *) malloc(sizeof(EMFTRACK)); + if(!etl)return(4); + etl->buf = malloc(initsize); // no need to zero the memory + if(!etl->buf){ + free(etl); + return(5); + } + fp=emf_fopen(name,U_WRITE); + if(!fp){ + free(etl->buf); + free(etl); + return(6); + } + etl->fp = fp; + etl->allocated = initsize; + etl->used = 0; + etl->records = 0; + etl->PalEntries = 0; + etl->chunk = chunksize; + *et=etl; + return(0); +} + +/** + \brief Finalize the emf in memory and write it to the file. + \return 0 on success, >=1 on failure + \param et EMF in memory + \param eht EMF handle table (peak handle number needed) +*/ +int emf_finish( + EMFTRACK *et, + EMFHANDLES *eht + ){ + U_EMRHEADER *record; + + if(!et->fp)return(1); // This could happen if something stomps on memory, otherwise should be caught in emf_start + + // Set the header fields which were unknown up until this point + + record = (U_EMRHEADER *)et->buf; + record->nBytes = et->used; + record->nRecords = et->records; + record->nHandles = eht->peak + 1; + record->nPalEntries = et->PalEntries; + +#if U_BYTE_SWAP + //This is a Big Endian machine, EMF data must be Little Endian + U_emf_endian(et->buf,et->used,1); +#endif + + if(1 != fwrite(et->buf,et->used,1,et->fp))return(2); + (void) fclose(et->fp); + et->fp=NULL; + return(0); +} + +/** + \brief Release memory for an emf structure in memory. Call this after emf_finish(). + \return 0 on success, >=1 on failure + \param et EMF in memory +*/ +int emf_free( + EMFTRACK **et + ){ + EMFTRACK *etl; + if(!et)return(1); + etl=*et; + if(!etl)return(2); + free(etl->buf); + free(etl); + *et=NULL; + return(0); +} + +/** + \brief wrapper for fopen, works on any platform + \return 0 on success, >=1 on failure + \param filename file to open (either ASCII or UTF-8) + \param mode U_READ or U_WRITE (these map to "rb" and "wb") +*/ +FILE *emf_fopen( + const char *filename, + const int mode + ){ + FILE *fp = NULL; +#ifdef WIN32 + uint16_t *fn16; + uint16_t *md16; + if(mode == U_READ){ md16 = U_Utf8ToUtf16le("rb", 0, NULL); } + else { md16 = U_Utf8ToUtf16le("wb", 0, NULL); } + fn16 = U_Utf8ToUtf16le(filename, 0, NULL); + fp = _wfopen(fn16,md16); + free(fn16); + free(md16); +#else + if(mode == U_READ){ fp = fopen(filename,"rb"); } + else { fp = fopen(filename,"wb"); } +#endif + return(fp); +} + +/** + \brief Retrieve contents of an EMF file by name. + \return 0 on success, >=1 on failure + \param filename Name of file to open, including the path + \param contents Contents of the file. Buffer must be free()'d by caller. + \param length Number of bytes in Contents +*/ +int emf_readdata( + const char *filename, + char **contents, + size_t *length + ){ + FILE *fp; + int status=0; + + *contents=NULL; + fp=emf_fopen(filename,U_READ); + if(!fp){ status = 1; } + else { + // read the entire file into memory + fseek(fp, 0, SEEK_END); // move to end + *length = ftell(fp); + rewind(fp); + *contents = (char *) malloc(*length); + if(!*contents){ + status = 2; + } + else { + size_t inbytes = fread(*contents,*length,1,fp); + if(inbytes != 1){ + free(*contents); + status = 3; + } + else { +#if U_BYTE_SWAP + //This is a Big Endian machine, EMF data is Little Endian + U_emf_endian(*contents,*length,0); // LE to BE +#endif + } + } + fclose(fp); + } + return(status); +} + + +/** + \brief Append an EMF record to an emf in memory. This may reallocate buf memory. + \return 0 for success, >=1 for failure. + \param rec Record to append to EMF in memory + \param et EMF in memory + \param freerec If true, free rec after append +*/ +int emf_append( + U_ENHMETARECORD *rec, + EMFTRACK *et, + int freerec + ){ + size_t deficit; + +#ifdef U_VALGRIND + printf("\nbefore \n"); + printf(" probe %d\n",memprobe(rec, U_EMRSIZE(rec))); + printf("after \n"); +#endif + if(!rec)return(1); + if(!et)return(2); + if(rec->nSize + et->used > et->allocated){ + deficit = rec->nSize + et->used - et->allocated; + if(deficit < et->chunk)deficit = et->chunk; + et->allocated += deficit; + et->buf = realloc(et->buf,et->allocated); + if(!et->buf)return(3); + } + memcpy(et->buf + et->used, rec, rec->nSize); + et->used += rec->nSize; + et->records++; + if(rec->iType == U_EMR_EOF){ et->PalEntries = ((U_EMREOF *)rec)->cbPalEntries; } + if(freerec){ free(rec); } + return(0); +} + +/** + \brief Create a handle table. Entries filled with 0 are empty, entries >0 hold a handle. + \return 0 for success, >=1 for failure. + \param initsize Initialize with space for this number of handles + \param chunksize When needed increase space by this number of handles + \param eht EMF handle table +*/ +int emf_htable_create( + uint32_t initsize, + uint32_t chunksize, + EMFHANDLES **eht + ){ + EMFHANDLES *ehtl; + unsigned int i; + + if(initsize<1)return(1); + if(chunksize<1)return(2); + ehtl = (EMFHANDLES *) malloc(sizeof(EMFHANDLES)); + if(!ehtl)return(3); + ehtl->table = malloc(initsize * sizeof(uint32_t)); + if(!ehtl->table){ + free(ehtl); + return(4); + } + ehtl->stack = malloc(initsize * sizeof(uint32_t)); + if(!ehtl->stack){ + free(ehtl); + free(ehtl->table); + return(5); + } + memset(ehtl->table , 0, initsize * sizeof(uint32_t)); // zero all slots in the table + for(i=1; i<initsize; i++){ehtl->stack[i]=i;} // preset the stack + ehtl->allocated = initsize; + ehtl->chunk = chunksize; + ehtl->table[0] = 0; // This slot isn't actually ever used + ehtl->stack[0] = 0; // This stack position isn't actually ever used + ehtl->peak = 1; + ehtl->sptr = 1; + ehtl->top = 0; + *eht = ehtl; + return(0); +} + +/** + \brief Delete an entry from the handle table. Move it back onto the stack. The specified slot is filled with a 0. + \return 0 for success, >=1 for failure. + \param ih handle + \param eht EMF handle table + +*/ +int emf_htable_delete( + uint32_t *ih, + EMFHANDLES *eht + ){ + if(!eht)return(1); + if(!eht->table)return(2); + if(!eht->stack)return(3); + if(*ih < 1)return(4); // invalid handle + if(!eht->table[*ih])return(5); // requested table position was not in use + eht->table[*ih]=0; // remove handle from table + while(eht->top>0 && !eht->table[eht->top]){ // adjust top + eht->top--; + } + eht->sptr--; // adjust stack + eht->stack[eht->sptr]=*ih; // place handle on stack + *ih=0; // invalidate handle variable, so a second delete will of it is not possible + return(0); +} + +/** + \brief Returns the index of the first free slot. + Call realloc() if needed. The slot is set to handle (indicates occupied) and the peak value is adjusted. + \return 0 for success, >=1 for failure. + \param ih handle + \param eht EMF handle table +*/ +int emf_htable_insert( + uint32_t *ih, + EMFHANDLES *eht + ){ + unsigned int i; + size_t newsize; + + if(!eht)return(1); + if(!eht->table)return(2); + if(!eht->stack)return(3); + if(!ih)return(4); + if(eht->sptr >= eht->allocated - 1){ // need to reallocate + newsize=eht->allocated + eht->chunk; + eht->table = realloc(eht->table,newsize * sizeof(uint32_t)); + if(!eht->table)return(5); + memset(&eht->table[eht->allocated] , 0, eht->chunk * sizeof(uint32_t)); // zero all NEW slots in the table + + eht->stack = realloc(eht->stack,newsize * sizeof(uint32_t)); + if(!eht->stack)return(6); + for(i=eht->allocated; i<newsize;i++){ eht->stack[i] = i; } // init all NEW slots in the stack + eht->allocated = newsize; + } + *ih = eht->stack[eht->sptr]; // handle that is inserted + if(eht->table[*ih])return(7); + eht->table[*ih] = *ih; // handle goes into preexisting (but zero) slot in table + eht->stack[eht->sptr] = 0; + if(*ih > eht->top){ eht->top = *ih; } + if(eht->sptr > eht->peak){ eht->peak = eht->sptr; } + eht->sptr++; // next available handle + return(0); +} + +/** + \brief Free all memory in an htable. Sets the pointer to NULL. + \return 0 for success, >=1 for failure. + \param eht EMF handle table +*/ +int emf_htable_free( + EMFHANDLES **eht + ){ + EMFHANDLES *ehtl; + if(!eht)return(1); + ehtl = *eht; + if(!ehtl)return(2); + if(!ehtl->table)return(3); + if(!ehtl->stack)return(4); + free(ehtl->table); + free(ehtl->stack); + free(ehtl); + *eht=NULL; + return(0); +} + +/* ********************************************************************************************** +These functions create standard structures used in the EMR records. +*********************************************************************************************** */ + + +/** + \brief Set up fields for an EMR_HEADER from the physical device's width and height in mm and dots per millimeter. + Typically this is something like 216,279,47.244 (Letter paper, 1200 DPI = 47.244 DPmm) + \return 0 for success, >=1 for failure. + \param xmm Device width in millimeters + \param ymm Device height in millimeters + \param dpmm Dots per millimeter + \param szlDev Device size structure in pixels + \param szlMm Device size structure in mm +*/ +int device_size( + const int xmm, + const int ymm, + const float dpmm, + U_SIZEL *szlDev, + U_SIZEL *szlMm + ){ + if(xmm < 0 || ymm < 0 || dpmm < 0)return(1); + szlDev->cx = U_ROUND((float) xmm * dpmm); + szlDev->cy = U_ROUND((float) ymm * dpmm);; + szlMm->cx = xmm; + szlMm->cy = ymm; + return(0); +} + +/** + \brief Set up fields for an EMR_HEADER for drawing by physical size in mm and dots per millimeter. + Technically rclBounds is supposed to be the extent of the drawing within the EMF, but libUEMF has no way + of knowing this since it never actually draws anything. Instead this is set to the full drawing size. + Coordinates are inclusive inclusive, so 297 -> 0,29699. + \return 0 for success, >=1 for failure. + \param xmm Drawing width in millimeters + \param ymm Drawing height in millimeters + \param dpmm Dots per millimeter + \param rclBounds Drawing size structure in pixels + \param rclFrame Drawing size structure in mm +*/ +int drawing_size( + const int xmm, + const int ymm, + const float dpmm, + U_RECTL *rclBounds, + U_RECTL *rclFrame + ){ + if(xmm < 0 || ymm < 0 || dpmm < 0)return(1); + rclBounds->left = 0; + rclBounds->top = 0; + rclBounds->right = U_ROUND((float) xmm * dpmm) - 1; // because coordinate system is 0,0 in upper left, N,M in lower right + rclBounds->bottom = U_ROUND((float) ymm * dpmm) - 1; + rclFrame->left = 0; + rclFrame->top = 0; + rclFrame->right = U_ROUND((float) xmm * 100.) - 1; + rclFrame->bottom = U_ROUND((float) ymm * 100.) - 1; + return(0); +} + +/** + \brief Set a U_COLORREF value from separeate R,G,B values. + Or use macro directly: cr = U_RGB(r,g,b). + \param red Red component + \param green Green component + \param blue Blue component + +*/ +U_COLORREF colorref_set( + uint8_t red, + uint8_t green, + uint8_t blue + ){ + U_COLORREF cr = (U_COLORREF){red , green, blue, 0}; + return(cr); +} + +/** + \brief Set rect and rectl objects from Upper Left and Lower Right corner points. + \param ul upper left corner of rectangle + \param lr lower right corner of rectangle +*/ +U_RECTL rectl_set( + U_POINTL ul, + U_POINTL lr + ){ + U_RECTL rct; + rct.left = ul.x; + rct.top = ul.y; + rct.right = lr.x; + rct.bottom = lr.y; + return(rct); +} + +/** + \brief Set sizel objects with X,Y values. + \param x X coordinate + \param y Y coordinate +*/ +U_SIZEL sizel_set( + int32_t x, + int32_t y + ){ + U_SIZEL sz; + sz.cx = x; + sz.cy = y; + return(sz); +} + +/** + \brief Set pointl objects with X,Y values. + \param x X coordinate + \param y Y coordinate +*/ +U_POINTL point32_set( + int32_t x, + int32_t y + ){ + U_POINTL pt; + pt.x = x; + pt.y = y; + return(pt); +} + +/** + \brief Set point16 objects with 16 bit X,Y values. + \param x X coordinate + \param y Y coordinate +*/ +U_POINT16 point16_set( + int16_t x, + int16_t y + ){ + U_POINT16 pt; + pt.x = x; + pt.y = y; + return(pt); +} + +/** + \brief Find the bounding rectangle from a polyline of a given width. + \param count number of points in the polyline + \param pts the polyline + \param width width of drawn line + +*/ +U_RECT findbounds( + uint32_t count, + PU_POINT pts, + uint32_t width + ){ + U_RECT rect={INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN }; + unsigned int i; + + for(i=0; i<count;i++,pts++){ + if ( pts->x < rect.left ) rect.left = pts->x; + if ( pts->x > rect.right ) rect.right = pts->x; + if ( pts->y < rect.top ) rect.top = pts->y; + if ( pts->y > rect.bottom ) rect.bottom = pts->y; + } + if(width > 0){ + rect.left -= width; + rect.right += width; + rect.top += width; + rect.bottom -= width; + } + return(rect); +} + +/** + \brief Find the bounding rectangle from a polyline of a given width. + \param count number of points in the polyline + \param pts the polyline + \param width width of drawn line + +*/ +U_RECT findbounds16( + uint32_t count, + PU_POINT16 pts, + uint32_t width + ){ + U_RECT rect={INT16_MAX, INT16_MAX, INT16_MIN, INT16_MIN }; + unsigned int i; + + for(i=0; i<count;i++,pts++){ + if ( pts->x < rect.left ) rect.left = pts->x; + if ( pts->x > rect.right ) rect.right = pts->x; + if ( pts->y < rect.top ) rect.top = pts->y; + if ( pts->y > rect.bottom ) rect.bottom = pts->y; + } + if(width > 0){ + rect.left -= width; + rect.right += width; + rect.top += width; + rect.bottom -= width; + } + return(rect); +} +/** + \brief Construct a U_LOGBRUSH structure. + \return U_LOGBRUSH structure + \param lbStyle LB_Style Enumeration + \param lbColor Brush color + \param lbHatch HatchStyle Enumertaion +*/ +U_LOGBRUSH logbrush_set( + uint32_t lbStyle, + U_COLORREF lbColor, + int32_t lbHatch + ){ + U_LOGBRUSH lb; + lb.lbStyle = lbStyle; + lb.lbColor = lbColor; + lb.lbHatch = lbHatch; + return(lb); +} + +/** + \brief Construct a U_XFORM structure. + \return U_XFORM structure + \param eM11 Rotation Matrix element + \param eM12 Rotation Matrix element + \param eM21 Rotation Matrix element + \param eM22 Rotation Matrix element + \param eDx Translation element + \param eDy Translation element +*/ +U_XFORM xform_set( + U_FLOAT eM11, + U_FLOAT eM12, + U_FLOAT eM21, + U_FLOAT eM22, + U_FLOAT eDx, + U_FLOAT eDy + ){ + U_XFORM xform; + xform.eM11 = eM11; + xform.eM12 = eM12; + xform.eM21 = eM21; + xform.eM22 = eM22; + xform.eDx = eDx; + xform.eDy = eDy; + return(xform); +} + +/** + \brief Construct a U_XFORM structure. + \return U_XFORM structure + \param scale Scale factor + \param ratio Ratio of minor axis/major axis + \param rot Rotation angle in degrees, positive is counter clockwise from the x axis. + \param axisrot Angle in degrees defining the major axis before rotation, positive is counter clockwise from the x axis. + \param eDx Translation element + \param eDy Translation element + + Operation is: + 1 Conformal map of points based on scale, axis rotation, and axis ratio, + 2. Apply rotation + 3. Apply offset +*/ +U_XFORM xform_alt_set( + U_FLOAT scale, + U_FLOAT ratio, + U_FLOAT rot, + U_FLOAT axisrot, + U_FLOAT eDx, + U_FLOAT eDy + ){ + U_XFORM xform; + U_MAT2X2 mat1, mat2; + // angles are in degrees, must be in radians + rot *= (2.0 * U_PI)/360.0; + axisrot *= -(2.0 * U_PI)/360.0; + mat1.M11 = cos(rot); // set up the rotation matrix + mat1.M12 = -sin(rot); + mat1.M21 = sin(rot); + mat1.M22 = cos(rot); + if(ratio!=1.0){ // set scale/ellipticity matrix + mat2.M11 = scale*( cos(axisrot)*cos(axisrot) + ratio*sin(axisrot)*sin(axisrot) ); + mat2.M12 = mat2.M21 = scale*( sin(axisrot)*cos(axisrot) * (1.0 - ratio) ); + mat2.M22 = scale*( sin(axisrot)*sin(axisrot) + ratio*cos(axisrot)*cos(axisrot) ); + } + else { // when the ratio is 1.0 then the major axis angle is ignored and only scale matters + mat2.M11 = scale; + mat2.M12 = 0.0; + mat2.M21 = 0.0; + mat2.M22 = scale; + } + xform.eM11 = mat2.M11 * mat1.M11 + mat2.M12 * mat1.M21; + xform.eM12 = mat2.M11 * mat1.M12 + mat2.M12 * mat1.M22;; + xform.eM21 = mat2.M21 * mat1.M11 + mat2.M22 * mat1.M21; + xform.eM22 = mat2.M21 * mat1.M12 + mat2.M22 * mat1.M22; + xform.eDx = eDx; + xform.eDy = eDy; + return(xform); +} + + +/** + \brief Construct a U_LOGCOLORSPACEA structure. + \return U_LOGCOLORSPACEA structure + \param lcsCSType LCS_CSType Enumeration + \param lcsIntent LCS_Intent Enumeration + \param lcsEndpoints CIE XYZ color space endpoints + \param lcsGammaRGB Gamma For RGB + \param lcsFilename Could name an external color profile file, otherwise empty string +*/ +U_LOGCOLORSPACEA logcolorspacea_set( + int32_t lcsCSType, + int32_t lcsIntent, + U_CIEXYZTRIPLE lcsEndpoints, + U_LCS_GAMMARGB lcsGammaRGB, + char *lcsFilename + ){ + U_LOGCOLORSPACEA lcsa; + lcsa.lcsSignature = U_LCS_SIGNATURE; + lcsa.lcsVersion = U_LCS_SIGNATURE; + lcsa.lcsSize = sizeof(U_LOGCOLORSPACEA); + lcsa.lcsCSType = lcsCSType; + lcsa.lcsIntent = lcsIntent; + lcsa.lcsEndpoints = lcsEndpoints; + lcsa.lcsGammaRGB = lcsGammaRGB; + memset(lcsa.lcsFilename,0,U_MAX_PATH); // zero out the Filename field + strncpy(lcsa.lcsFilename,lcsFilename,U_MAX_PATH); + return(lcsa); +} + +/** + + \brief Construct a U_LOGCOLORSPACEW structure. + \return U_LOGCOLORSPACEW structure + \param lcsCSType LCS_CSType Enumeration + \param lcsIntent LCS_Intent Enumeration + \param lcsEndpoints CIE XYZ color space endpoints + \param lcsGammaRGB Gamma For RGB + \param lcsFilename Could name an external color profile file, otherwise empty string +*/ +U_LOGCOLORSPACEW logcolorspacew_set( + int32_t lcsCSType, + int32_t lcsIntent, + U_CIEXYZTRIPLE lcsEndpoints, + U_LCS_GAMMARGB lcsGammaRGB, + uint16_t *lcsFilename + ){ + U_LOGCOLORSPACEW lcsa; + lcsa.lcsSignature = U_LCS_SIGNATURE; + lcsa.lcsVersion = U_LCS_SIGNATURE; + lcsa.lcsSize = sizeof(U_LOGCOLORSPACEW); + lcsa.lcsCSType = lcsCSType; + lcsa.lcsIntent = lcsIntent; + lcsa.lcsEndpoints = lcsEndpoints; + lcsa.lcsGammaRGB = lcsGammaRGB; + wchar16strncpypad(lcsa.lcsFilename,lcsFilename,U_MAX_PATH); + return(lcsa); +} + +/** + + \brief Construct a U_PANOSE structure. + \return U_PANOSE structure + \param bFamilyType FamilyType Enumeration + \param bSerifStyle SerifType Enumeration + \param bWeight Weight Enumeration + \param bProportion Proportion Enumeration + \param bContrast Contrast Enumeration + \param bStrokeVariation StrokeVariation Enumeration + \param bArmStyle ArmStyle Enumeration + \param bLetterform Letterform Enumeration + \param bMidline Midline Enumeration + \param bXHeight XHeight Enumeration +*/ +U_PANOSE panose_set( + uint8_t bFamilyType, + uint8_t bSerifStyle, + uint8_t bWeight, + uint8_t bProportion, + uint8_t bContrast, + uint8_t bStrokeVariation, + uint8_t bArmStyle, + uint8_t bLetterform, + uint8_t bMidline, + uint8_t bXHeight + ){ + U_PANOSE panose; + panose.bFamilyType = bFamilyType; + panose.bSerifStyle = bSerifStyle; + panose.bWeight = bWeight; + panose.bProportion = bProportion; + panose.bContrast = bContrast; + panose.bStrokeVariation = bStrokeVariation; + panose.bArmStyle = bArmStyle; + panose.bLetterform = bLetterform; + panose.bMidline = bMidline; + panose.bXHeight = bXHeight; + return(panose); +} + +/** + \brief Construct a U_LOGFONT structure. + \return U_LOGFONT structure + \param lfHeight Height in Logical units + \param lfWidth Average Width in Logical units + \param lfEscapement Angle in 0.1 degrees betweem escapement vector and X axis + \param lfOrientation Angle in 0.1 degrees between baseline and X axis + \param lfWeight LF_Weight Enumeration + \param lfItalic Italics: 0 or 1 + \param lfUnderline Underline: 0 or 1 + \param lfStrikeOut Strikeout: 0 or 1 + \param lfCharSet LF_CharSet Enumeration + \param lfOutPrecision LF_OutPrecision Enumeration + \param lfClipPrecision LF_ClipPrecision Enumeration + \param lfQuality LF_Quality Enumeration + \param lfPitchAndFamily LF_PitchAndFamily Enumeration + \param lfFaceName Name of font. truncates at U_LF_FACESIZE, smaller must be null terminated + +*/ +U_LOGFONT logfont_set( + int32_t lfHeight, + int32_t lfWidth, + int32_t lfEscapement, + int32_t lfOrientation, + int32_t lfWeight, + uint8_t lfItalic, + uint8_t lfUnderline, + uint8_t lfStrikeOut, + uint8_t lfCharSet, + uint8_t lfOutPrecision, + uint8_t lfClipPrecision, + uint8_t lfQuality, + uint8_t lfPitchAndFamily, + uint16_t *lfFaceName + ){ + U_LOGFONT lf; + lf.lfHeight = lfHeight; + lf.lfWidth = lfWidth; + lf.lfEscapement = lfEscapement; + lf.lfOrientation = lfOrientation; + lf.lfWeight = lfWeight; + lf.lfItalic = lfItalic; + lf.lfUnderline = lfUnderline; + lf.lfStrikeOut = lfStrikeOut; + lf.lfCharSet = lfCharSet; + lf.lfOutPrecision = lfOutPrecision; + lf.lfClipPrecision = lfClipPrecision; + lf.lfQuality = lfQuality; + lf.lfPitchAndFamily = lfPitchAndFamily; + wchar16strncpypad(lf.lfFaceName, lfFaceName, U_LF_FACESIZE); // pad this one as the intial structure was not set to zero + return(lf); +} + + +/** + \brief Construct a U_LOGFONT_PANOSE structure. + \return U_LOGFONT_PANOSE structure + \param elfLogFont Basic font attributes + \param elfFullName Font full name, truncates at U_LF_FULLFACESIZE, smaller must be null terminated + \param elfStyle Font style, truncates at U_LF_FULLFACESIZE, smaller must be null terminated + \param elfStyleSize Font hinting starting at this point size, if 0, starts at Height + \param elfPanose Panose Object. If all zero, it is ignored. +*/ +U_LOGFONT_PANOSE logfont_panose_set( + U_LOGFONT elfLogFont, + uint16_t *elfFullName, + uint16_t *elfStyle, + uint32_t elfStyleSize, + U_PANOSE elfPanose + ){ + U_LOGFONT_PANOSE lfp; + memset(&lfp,0,sizeof(U_LOGFONT_PANOSE)); // all fields zero unless needed. Many should be ignored or must be 0. + wchar16strncpy(lfp.elfFullName, elfFullName, U_LF_FULLFACESIZE); + wchar16strncpy(lfp.elfStyle, elfStyle, U_LF_FACESIZE); + lfp.elfLogFont = elfLogFont; + lfp.elfStyleSize = elfStyleSize; + lfp.elfPanose = elfPanose; + return(lfp); +} + +/** + \brief Construct a U_BITMAPINFOHEADER structure. + \return U_BITMAPINFOHEADER structure + \param biWidth Bitmap width in pixels + \param biHeight Bitmap height in pixels + \param biPlanes Planes (must be 1) + \param biBitCount BitCount Enumeration + \param biCompression BI_Compression Enumeration + \param biSizeImage Size in bytes of image + \param biXPelsPerMeter X Resolution in pixels/meter + \param biYPelsPerMeter Y Resolution in pixels/meter + \param biClrUsed Number of bmciColors in U_BITMAPCOREINFO + \param biClrImportant Number of bmciColors needed (0 means all). +*/ +U_BITMAPINFOHEADER bitmapinfoheader_set( + int32_t biWidth, + int32_t biHeight, + uint16_t biPlanes, + uint16_t biBitCount, + uint32_t biCompression, + uint32_t biSizeImage, + int32_t biXPelsPerMeter, + int32_t biYPelsPerMeter, + U_NUM_RGBQUAD biClrUsed, + uint32_t biClrImportant + ){ + U_BITMAPINFOHEADER Bmi; + Bmi.biSize = sizeof(U_BITMAPINFOHEADER); + Bmi.biWidth = biWidth; + Bmi.biHeight = biHeight; + Bmi.biPlanes = biPlanes; + Bmi.biBitCount = biBitCount; + Bmi.biCompression = biCompression; + Bmi.biSizeImage = biSizeImage; + Bmi.biXPelsPerMeter = biXPelsPerMeter; + Bmi.biYPelsPerMeter = biYPelsPerMeter; + Bmi.biClrUsed = biClrUsed; + Bmi.biClrImportant = biClrImportant; + return(Bmi); +} + + +/** + \brief Allocate and construct a U_BITMAPINFO structure. + \return Pointer to a U_BITMAPINFO structure + \param BmiHeader Geometry and pixel properties + \param BmiColors Color table (must be NULL for some values of BmiHeader->biBitCount) +*/ +PU_BITMAPINFO bitmapinfo_set( + U_BITMAPINFOHEADER BmiHeader, + PU_RGBQUAD BmiColors + ){ + char *record; + int irecsize; + int cbColors, cbColors4, off; + + cbColors = 4*get_real_color_count((char *) &BmiHeader); + cbColors4 = UP4(cbColors); + irecsize = sizeof(U_BITMAPINFOHEADER) + cbColors4; + record = malloc(irecsize); + if(record){ + memcpy(record, &BmiHeader, sizeof(U_BITMAPINFOHEADER)); + if(cbColors){ + off = sizeof(U_BITMAPINFOHEADER); + memcpy(record + off, BmiColors, cbColors); + off += cbColors; + if(cbColors4 - cbColors){ memset(record + off, 0, cbColors4 - cbColors); } + } + } + return((PU_BITMAPINFO) record); +} + +/** + \brief Allocate and construct a U_EXTLOGPEN structure. + \return pointer to U_EXTLOGPEN structure, or NULL on error + \param elpPenStyle PenStyle Enumeration + \param elpWidth Width in logical units (elpPenStyle & U_PS_GEOMETRIC) or 1 (pixel) + \param elpBrushStyle LB_Style Enumeration + \param elpColor Pen color + \param elpHatch HatchStyle Enumeration + \param elpNumEntries Count of StyleEntry array + \param elpStyleEntry Array of StyleEntry (For user specified dot/dash patterns) +*/ +PU_EXTLOGPEN extlogpen_set( + uint32_t elpPenStyle, + uint32_t elpWidth, + uint32_t elpBrushStyle, + U_COLORREF elpColor, + int32_t elpHatch, + U_NUM_STYLEENTRY elpNumEntries, + U_STYLEENTRY *elpStyleEntry + ){ + int irecsize,szSyleArray; + char *record; + + if(elpNumEntries){ + if(!elpStyleEntry)return(NULL); + szSyleArray = elpNumEntries * sizeof(U_STYLEENTRY); + irecsize = sizeof(U_EXTLOGPEN) + szSyleArray - sizeof(U_STYLEENTRY); // first one is in the record + } + else { + szSyleArray = 0; + irecsize = sizeof(U_EXTLOGPEN); + } + record = malloc(irecsize); + if(record){ + ((PU_EXTLOGPEN) record)->elpPenStyle = elpPenStyle; + ((PU_EXTLOGPEN) record)->elpWidth = elpWidth; + ((PU_EXTLOGPEN) record)->elpBrushStyle = elpBrushStyle; + ((PU_EXTLOGPEN) record)->elpColor = elpColor; + ((PU_EXTLOGPEN) record)->elpHatch = elpHatch; + ((PU_EXTLOGPEN) record)->elpNumEntries = elpNumEntries; + if(elpNumEntries){ memcpy(((PU_EXTLOGPEN) record)->elpStyleEntry,elpStyleEntry,szSyleArray); } + else { memset(((PU_EXTLOGPEN) record)->elpStyleEntry,0,sizeof(U_STYLEENTRY)); } // not used, but this stops valgrind warnings + } + return((PU_EXTLOGPEN) record); +} + +/** + \brief Construct a U_LOGPEN structure. + \return U_LOGPEN structure + \param lopnStyle PenStyle Enumeration + \param lopnWidth Width of pen set by X, Y is ignored + \param lopnColor Pen color value + +*/ +U_LOGPEN logpen_set( + uint32_t lopnStyle, + U_POINT lopnWidth, + U_COLORREF lopnColor + ){ + U_LOGPEN lp; + lp.lopnStyle = lopnStyle; + lp.lopnWidth = lopnWidth; + lp.lopnColor = lopnColor; + return(lp); +} + +/** + \brief Construct a U_LOGPLTNTRY structure. + \return U_LOGPLTNTRY structure + \param peReserved Ignore + \param peRed Palette entry Red Intensity + \param peGreen Palette entry Green Intensity + \param peBlue Palette entry Blue Intensity +*/ +U_LOGPLTNTRY logpltntry_set( + uint8_t peReserved, + uint8_t peRed, + uint8_t peGreen, + uint8_t peBlue + ){ + U_LOGPLTNTRY lpny; + lpny.peReserved = peReserved; + lpny.peRed = peRed; + lpny.peGreen = peGreen; + lpny.peBlue = peBlue; + return(lpny); +} + +/** + \brief Allocate and construct a U_LOGPALETTE structure. + \return pointer to U_LOGPALETTE structure, or NULL on error. + \param palNumEntries Number of U_LOGPLTNTRY objects + \param palPalEntry array, PC_Entry Enumeration +*/ +PU_LOGPALETTE logpalette_set( + U_NUM_LOGPLTNTRY palNumEntries, + PU_LOGPLTNTRY *palPalEntry + ){ + PU_LOGPALETTE record; + int cbPalArray,irecsize; + + if(palNumEntries == 0 || !palPalEntry)return(NULL); + cbPalArray = palNumEntries * sizeof(U_LOGPLTNTRY); + irecsize = sizeof(U_LOGPALETTE) + cbPalArray - sizeof(U_LOGPLTNTRY); + record = (PU_LOGPALETTE) malloc(irecsize); + if(irecsize){ + record->palVersion = U_LP_VERSION; + record->palNumEntries = palNumEntries; + memcpy(record->palPalEntry,palPalEntry,cbPalArray); + } + return(record); +} + +/** + \brief Construct a U_RGNDATAHEADER structure. + \return U_RGNDATAHEADER structure + \param nCount Number of rectangles in region + \param rclBounds Region bounds +*/ +U_RGNDATAHEADER rgndataheader_set( + U_NUM_RECTL nCount, + U_RECTL rclBounds + ){ + U_RGNDATAHEADER rdh; + rdh.dwSize = U_RDH_OBJSIZE; + rdh.iType = U_RDH_RECTANGLES; + rdh.nCount = nCount; + rdh.nRgnSize = nCount * sizeof(U_RECTL); // Size in bytes of retangle array + rdh.rclBounds = rclBounds; + return(rdh); +} + +/** + \brief Allocate and construct a U_RGNDATA structure. + \return pointer to U_RGNDATA structure, or NULL on error. + \param rdh Data description + \param Buffer Array of U_RECTL elements +*/ +PU_RGNDATA rgndata_set( + U_RGNDATAHEADER rdh, + PU_RECTL Buffer + ){ + char *record; + int irecsize; + int szRgnArray,off; + + if(!Buffer || !rdh.nCount || !rdh.nRgnSize)return(NULL); + szRgnArray = rdh.nRgnSize; // size of the U_RECTL array + irecsize = sizeof(U_RGNDATA) + szRgnArray - sizeof(U_RECTL); // core + array - overlap + record = malloc(irecsize); + if(record){ + memcpy(record, &rdh, sizeof(U_RGNDATAHEADER)); + off = sizeof(U_RGNDATAHEADER); + memcpy(record + off, Buffer, szRgnArray); + } + return((PU_RGNDATA) record); +} + +/** + \brief Construct a U_COLORADJUSTMENT structure. + \return U_COLORADJUSTMENT structure + \param Size Size of this structure in bytes + \param Flags ColorAdjustment Enumeration + \param IlluminantIndex Illuminant Enumeration + \param RedGamma Red Gamma correction (range:2500:65000, 10000 is no correction) + \param GreenGamma Green Gamma correction (range:2500:65000, 10000 is no correction) + \param BlueGamma Blue Gamma correction (range:2500:65000, 10000 is no correction) + \param ReferenceBlack Values less than this are black (range:0:4000) + \param ReferenceWhite Values more than this are white (range:6000:10000) + \param Contrast Contrast adjustment (range:-100:100, 0 is no correction) + \param Brightness Brightness adjustment (range:-100:100, 0 is no correction) + \param Colorfulness Colorfulness adjustment (range:-100:100, 0 is no correction) + \param RedGreenTint Tine adjustment (range:-100:100, 0 is no correction) +*/ +U_COLORADJUSTMENT coloradjustment_set( + uint16_t Size, + uint16_t Flags, + uint16_t IlluminantIndex, + uint16_t RedGamma, + uint16_t GreenGamma, + uint16_t BlueGamma, + uint16_t ReferenceBlack, + uint16_t ReferenceWhite, + int16_t Contrast, + int16_t Brightness, + int16_t Colorfulness, + int16_t RedGreenTint + ){ + U_COLORADJUSTMENT ca; + ca.caSize = Size; + ca.caFlags = Flags; + ca.caIlluminantIndex = IlluminantIndex; + ca.caRedGamma = U_MNMX(RedGamma, U_RGB_GAMMA_MIN, U_RGB_GAMMA_MAX); + ca.caGreenGamma = U_MNMX(GreenGamma, U_RGB_GAMMA_MIN, U_RGB_GAMMA_MAX); + ca.caBlueGamma = U_MNMX(BlueGamma, U_RGB_GAMMA_MIN, U_RGB_GAMMA_MAX); + // Next one is different to eliminate compiler warning - U_R_B_MIN is 0 and unsigned + ca.caReferenceBlack = U_MAX( ReferenceBlack, U_REFERENCE_BLACK_MAX); + ca.caReferenceWhite = U_MNMX(ReferenceWhite, U_REFERENCE_WHITE_MIN, U_REFERENCE_WHITE_MAX); + ca.caContrast = U_MNMX(Contrast, U_COLOR_ADJ_MIN, U_COLOR_ADJ_MAX); + ca.caBrightness = U_MNMX(Brightness, U_COLOR_ADJ_MIN, U_COLOR_ADJ_MAX); + ca.caColorfulness = U_MNMX(Colorfulness, U_COLOR_ADJ_MIN, U_COLOR_ADJ_MAX); + ca.caRedGreenTint = U_MNMX(RedGreenTint, U_COLOR_ADJ_MIN, U_COLOR_ADJ_MAX); + return(ca); +} + +/** + \brief Construct a U_PIXELFORMATDESCRIPTOR structure. + \return U_PIXELFORMATDESCRIPTOR structure + \param dwFlags PFD_dwFlags Enumeration + \param iPixelType PFD_iPixelType Enumeration + \param cColorBits RGBA: total bits per pixel + \param cRedBits Red bits per pixel + \param cRedShift Red shift to data bits + \param cGreenBits Green bits per pixel + \param cGreenShift Green shift to data bits + \param cBlueBits Blue bits per pixel + \param cBlueShift Blue shift to data bits + \param cAlphaBits Alpha bits per pixel + \param cAlphaShift Alpha shift to data bits + \param cAccumBits Accumulator buffer, total bitplanes + \param cAccumRedBits Red accumulator buffer bitplanes + \param cAccumGreenBits Green accumulator buffer bitplanes + \param cAccumBlueBits Blue accumulator buffer bitplanes + \param cAccumAlphaBits Alpha accumulator buffer bitplanes + \param cDepthBits Depth of Z-buffer + \param cStencilBits Depth of stencil buffer + \param cAuxBuffers Depth of auxilliary buffers (not supported) + \param iLayerType PFD_iLayerType Enumeration, may be ignored + \param bReserved Bits 0:3/4:7 are number of Overlay/Underlay planes + \param dwLayerMask may be ignored + \param dwVisibleMask color or index of underlay plane + \param dwDamageMask may be ignored +*/ +U_PIXELFORMATDESCRIPTOR pixelformatdescriptor_set( + uint32_t dwFlags, + uint8_t iPixelType, + uint8_t cColorBits, + uint8_t cRedBits, + uint8_t cRedShift, + uint8_t cGreenBits, + uint8_t cGreenShift, + uint8_t cBlueBits, + uint8_t cBlueShift, + uint8_t cAlphaBits, + uint8_t cAlphaShift, + uint8_t cAccumBits, + uint8_t cAccumRedBits, + uint8_t cAccumGreenBits, + uint8_t cAccumBlueBits, + uint8_t cAccumAlphaBits, + uint8_t cDepthBits, + uint8_t cStencilBits, + uint8_t cAuxBuffers, + uint8_t iLayerType, + uint8_t bReserved, + uint32_t dwLayerMask, + uint32_t dwVisibleMask, + uint32_t dwDamageMask + ){ + U_PIXELFORMATDESCRIPTOR pfd; + pfd.nSize = sizeof(U_PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = dwFlags; + pfd.iPixelType = iPixelType; + pfd.cColorBits = cColorBits; + pfd.cRedBits = cRedBits; + pfd.cRedShift = cRedShift; + pfd.cGreenBits = cGreenBits; + pfd.cGreenShift = cGreenShift; + pfd.cBlueBits = cBlueBits; + pfd.cBlueShift = cBlueShift; + pfd.cAlphaBits = cAlphaBits; + pfd.cAlphaShift = cAlphaShift; + pfd.cAccumBits = cAccumBits; + pfd.cAccumRedBits = cAccumRedBits; + pfd.cAccumGreenBits = cAccumGreenBits; + pfd.cAccumBlueBits = cAccumBlueBits; + pfd.cAccumAlphaBits = cAccumAlphaBits; + pfd.cDepthBits = cDepthBits; + pfd.cStencilBits = cStencilBits; + pfd.cAuxBuffers = cAuxBuffers; + pfd.iLayerType = iLayerType; + pfd.bReserved = bReserved; + pfd.dwLayerMask = dwLayerMask; + pfd.dwVisibleMask = dwVisibleMask; + pfd.dwDamageMask = dwDamageMask; + return(pfd); +} + +/** + \brief Allocate and create a U_EMRTEXT structure followed by its variable pieces via a char* pointer. + Dx cannot be NULL, if the calling program has no appropriate values call dx_set() first. + \return char* pointer to U_EMRTEXT structure followed by its variable pieces, or NULL on error + \param ptlReference String start coordinates + \param NumString Number of characters in string, does NOT include a terminator + \param cbChar Number of bytes per character + \param String String to write + \param fOptions ExtTextOutOptions Enumeration + \param rcl (Optional, when fOptions & 7) grayed/clipping/opaque rectangle + \param Dx Character spacing array from the start of the RECORD +*/ +char *emrtext_set( + U_POINTL ptlReference, + U_NUM_STR NumString, + uint32_t cbChar, + void *String, + uint32_t fOptions, + U_RECTL rcl, + uint32_t *Dx + ){ + int irecsize,cbDxArray,cbString4,cbString,off; + char *record; + uint32_t *loffDx; + + if(!String)return(NULL); + if(!Dx)return(NULL); + cbString = cbChar * NumString; // size of the string in bytes + cbString4 = UP4(cbString); // size of the string buffer + cbDxArray = sizeof(uint32_t)*NumString; // size of Dx array storage + if(fOptions & U_ETO_PDY)cbDxArray += cbDxArray; // of the Dx buffer, here do both X and Y coordinates + irecsize = sizeof(U_EMRTEXT) + sizeof(uint32_t) + cbString4 + cbDxArray; // core structure + offDx + string buf + dx buf + if(!(fOptions & U_ETO_NO_RECT)){ irecsize += sizeof(U_RECTL); } // plus variable U_RECTL, when it is present + record = malloc(irecsize); + if(record){ + ((PU_EMRTEXT)record)->ptlReference = ptlReference; + ((PU_EMRTEXT)record)->nChars = NumString; + // pick up ((PU_EMRTEXT)record)->offString later + ((PU_EMRTEXT)record)->fOptions = fOptions; + off = sizeof(U_EMRTEXT); // location where variable pieces will start to be written + if(!(fOptions & U_ETO_NO_RECT)){ // variable field, may or may not be present + memcpy(record + off,&rcl, sizeof(U_RECTL)); + off += sizeof(U_RECTL); + } + loffDx = (uint32_t *)(record + off); // offDx will go here, but we do not know with what value yet + off += sizeof(uint32_t); + memcpy(record + off,String,cbString); // copy the string data to its buffer + ((PU_EMRTEXT)record)->offString = off; // now save offset in the structure + off += cbString; + if(cbString < cbString4){ + memset(record+off,0,cbString4-cbString); // keeps valgrind happy (initialize padding after string) + off += cbString4-cbString; + } + memcpy(record + off, Dx, cbDxArray); // copy the Dx data to its buffer + *loffDx = off; // now save offDx to the structure + } + return(record); +} + + + +/* ********************************************************************************************** +These functions are simpler or more convenient ways to generate the specified types of EMR records. +Each should be called in preference to the underlying "base" EMR function. +*********************************************************************************************** */ + + +/** + \brief Allocate and construct a U_EMRCOMMENT structure with a UTF8 string. + A U_EMRCOMMENT contains application specific data, and that may include contain null characters. This function may be used when the + comment only incluces UT8 text. + \return pointer to U_EMRCOMMENT structure, or NULL on error. + \param string UTF8 string to store in the comment + + +*/ +char *textcomment_set( + const char *string + ){ + if(!string)return(NULL); + return(U_EMRCOMMENT_set(1 + strlen(string),string)); +} + +/** + \brief Allocate and construct a U_EMRDELETEOBJECT structure and also delete the requested object from the table. + Use this function instead of calling U_EMRDELETEOBJECT_set() directly. + \return pointer to U_EMRDELETEOBJECT structure, or NULL on error. + \param ihObject Pointer to handle to delete. This value is set to 0 if the function succeeds. + \param eht EMF handle table + + Note that calling this function should always be conditional on the specifed object being defined. It is easy to + write a program where deleteobject_set() is called in a sequence where, at the time, we know that ihObject is defined. + Then a later modification, possibly quite far away in the code, causes it to be undefined. That distant change will + result in a failure when this function reutrns. That problem cannot be handled here because the only values which + may be returned are a valid U_EMRDELETEOBJECT record or a NULL, and other errors could result in the NULL. + So the object must be checked before the call. +*/ +char *deleteobject_set( + uint32_t *ihObject, + EMFHANDLES *eht + ){ + uint32_t saveObject=*ihObject; + if(emf_htable_delete(ihObject,eht))return(NULL); // invalid handle or other problem, cannot be deleted + return(U_EMRDELETEOBJECT_set(saveObject)); +} + +/** + \brief Allocate and construct a U_EMRSELECTOBJECT structure, checks that the handle specified is one that can actually be selected. + Use this function instead of calling U_EMRSELECTOBJECT_set() directly. + \return pointer to U_EMRSELECTOBJECT structure, or NULL on error. + \param ihObject handle to select + \param eht EMF handle table +*/ +char *selectobject_set( + uint32_t ihObject, + EMFHANDLES *eht + ){ + if(!(U_STOCK_OBJECT & ihObject)){ // not a stock object, those go straight through + if(ihObject > eht->top)return(NULL); // handle this high is not in the table + if(!eht->table[ihObject])return(NULL); // handle is not in the table, so not active, so cannot be selected + } + return(U_EMRSELECTOBJECT_set(ihObject)); +} + +/** + \brief Allocate and construct a U_EMREXTCREATEPEN structure, create a handle and return it. + Use this function instead of calling U_EMREXTCREATEPEN_set() directly. + \return pointer to U_EMREXTCREATEPEN structure, or NULL on error. + \param ihPen handle to be used by new object + \param eht EMF handle table + \param Bmi bitmapbuffer + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px pixel array (NULL if cbPx == 0) + \param elp Pen parameters (Size is Variable!!!!) +*/ +char *extcreatepen_set( + uint32_t *ihPen, + EMFHANDLES *eht, + PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px, + PU_EXTLOGPEN elp + ){ + if(emf_htable_insert(ihPen, eht))return(NULL); + return(U_EMREXTCREATEPEN_set(*ihPen, Bmi, cbPx, Px, elp )); +} + +/** + \brief Allocate and construct a U_EMRCREATEPEN structure, create a handle and returns it + Use this function instead of calling U_EMRCREATEPEN_set() directly. + \return pointer to U_EMRCREATEPEN structure, or NULL on error. + \param ihPen handle to be used by new object + \param eht EMF handle table + \param lopn Pen parameters +*/ +char *createpen_set( + uint32_t *ihPen, + EMFHANDLES *eht, + U_LOGPEN lopn + ){ + if(emf_htable_insert(ihPen, eht))return(NULL); + return(U_EMRCREATEPEN_set(*ihPen, lopn)); +} + +/** + \brief Allocate and construct a U_EMRCREATEBRUSHINDIRECT structure, create a handle and returns it + Use this function instead of calling U_EMRCREATEBRUSHINDIRECT_set() directly. + \return pointer to U_EMRCREATEBRUSHINDIRECT structure, or NULL on error. + \param ihBrush handle to be used by new object + \param eht EMF handle table + \param lb Brush parameters +*/ +char *createbrushindirect_set( + uint32_t *ihBrush, + EMFHANDLES *eht, + U_LOGBRUSH lb + ){ + if(emf_htable_insert(ihBrush, eht))return(NULL); + return(U_EMRCREATEBRUSHINDIRECT_set(*ihBrush, lb)); +} + +/** + \brief Allocate and construct a U_EMRCREATEDIBPATTERNBRUSHPT_set structure, create a handle and returns it + Use this function instead of calling U_EMRCREATEDIBPATTERNBRUSHPT_set() directly. + \return pointer to U_EMRCREATEDIBPATTERNBRUSHPT_set structure, or NULL on error. + \param ihBrush handle to be used by new object + \param eht EMF handle table + \param iUsage DIBColors enumeration + \param Bmi Bitmap info + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *createdibpatternbrushpt_set( + uint32_t *ihBrush, + EMFHANDLES *eht, + const uint32_t iUsage, + PU_BITMAPINFO Bmi, + const uint32_t cbPx, + const char *Px + + ){ + if(emf_htable_insert(ihBrush, eht))return(NULL); + return(U_EMRCREATEDIBPATTERNBRUSHPT_set(*ihBrush, iUsage, Bmi, cbPx, Px)); +} + +/** + \brief Allocate and construct a U_EMRCREATEMONOBRUSH_set structure, create a handle and returns it + Use this function instead of calling U_EMRCREATEMONOBRUSH_set() directly. + \return pointer to U_EMRCREATEMONOBRUSH_set structure, or NULL on error. + \param ihBrush handle to be used by new object + \param eht EMF handle table + \param iUsage DIBColors enumeration + \param Bmi Bitmap info + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *createmonobrush_set( + uint32_t *ihBrush, + EMFHANDLES *eht, + const uint32_t iUsage, + PU_BITMAPINFO Bmi, + const uint32_t cbPx, + const char *Px + + ){ + if(emf_htable_insert(ihBrush, eht))return(NULL); + return(U_EMRCREATEMONOBRUSH_set(*ihBrush, iUsage, Bmi, cbPx, Px)); +} + + +/** + \brief Allocate and construct a U_EMRCREATECOLORSPACE structure, create a handle and returns it + Use this function instead of calling U_EMRCREATECOLORSPACE_set() directly. + \return pointer to U_EMRCREATECOLORSPACE structure, or NULL on error. + \param ihCS ColorSpace handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param lcs ColorSpace parameters +*/ +char *createcolorspace_set( + uint32_t *ihCS, + EMFHANDLES *eht, + U_LOGCOLORSPACEA lcs + ){ + if(emf_htable_insert(ihCS, eht))return(NULL); + return(U_EMRCREATECOLORSPACE_set(*ihCS,lcs)); +} + +/** + \brief Allocate and construct a U_EMRCREATECOLORSPACEW structure, create a handle and returns it + Use this function instead of calling U_EMRCREATECOLORSPACEW_set() directly. + \return pointer to U_EMRCREATECOLORSPACEW structure, or NULL on error. + \param ihCS ColorSpace handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param lcs ColorSpace parameters + \param dwFlags If low bit set Data is present + \param cbData Number of bytes of theData field. + \param Data (Optional, dwFlags & 1) color profile data +*/ +char *createcolorspacew_set( + uint32_t *ihCS, + EMFHANDLES *eht, + U_LOGCOLORSPACEW lcs, + uint32_t dwFlags, + U_CBDATA cbData, + uint8_t *Data + ){ + if(emf_htable_insert(ihCS, eht))return(NULL); + return(U_EMRCREATECOLORSPACEW_set(*ihCS, lcs, dwFlags, cbData, Data)); +} + +/** + \brief Allocate and construct a U_EMREXTCREATEFONTINDIRECTW structure, create a handle and returns it + Use this function instead of calling U_EMREXTCREATEFONTINDIRECTW_set() directly. + \return pointer to U_EMREXTCREATEFONTINDIRECTW structure, or NULL on error. + \param ihFont Font handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param elf Pointer to Font parameters asPU_LOGFONT + \param elfw Pointer to Font parameters as U_LOGFONT_PANOSE +*/ +char *extcreatefontindirectw_set( + uint32_t *ihFont, + EMFHANDLES *eht, + const char *elf, + const char *elfw + ){ + if(emf_htable_insert(ihFont, eht))return(NULL); + return(U_EMREXTCREATEFONTINDIRECTW_set(*ihFont, elf, elfw)); +} + +/** + \brief Allocate and construct a U_EMRCREATEPALETTE structure, create a handle and returns it + Use this function instead of calling U_EMRCREATEPALETTE_set() directly. + \return pointer to U_EMRCREATEPALETTE structure, or NULL on error. + \param ihPal Palette handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param lgpl PaletteFont parameters +*/ +char *createpalette_set( + uint32_t *ihPal, + EMFHANDLES *eht, + U_LOGPALETTE lgpl + ){ + if(emf_htable_insert(ihPal, eht))return(NULL); + return(U_EMRCREATEPALETTE_set(*ihPal, lgpl)); +} + +/** + \brief Allocate and construct a U_EMRSETPALETTEENTRIES structure, create a handle and returns it + Use this function instead of calling U_EMRSETPALETTEENTRIES_set() directly. + \return pointer to U_EMRSETPALETTEENTRIES structure, or NULL on error. + \param ihPal Palette handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param iStart First Palette entry in selected object to set + \param cEntries Number of Palette entries in selected object to set + \param aPalEntries Values to set with +*/ +char *setpaletteentries_set( + uint32_t *ihPal, + EMFHANDLES *eht, + const uint32_t iStart, + const U_NUM_LOGPLTNTRY cEntries, + const PU_LOGPLTNTRY aPalEntries + ){ + if(emf_htable_insert(ihPal, eht))return(NULL); + return(U_EMRSETPALETTEENTRIES_set(*ihPal, iStart, cEntries, aPalEntries)); +} + +/** + \brief Allocate and construct a U_EMRFILLRGN structure, create a handle and returns it + Use this function instead of calling U_EMRFILLRGN_set() directly. + \return pointer to U_EMRFILLRGN structure, or NULL on error. + \param ihBrush Brush handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param rclBounds Bounding rectangle in device units + \param RgnData Pointer to a U_RGNDATA structure +*/ +char *fillrgn_set( + uint32_t *ihBrush, + EMFHANDLES *eht, + const U_RECTL rclBounds, + const PU_RGNDATA RgnData + ){ + if(emf_htable_insert(ihBrush, eht))return(NULL); + return(U_EMRFILLRGN_set(rclBounds, *ihBrush, RgnData)); +} + +/** + \brief Allocate and construct a U_EMRFRAMERGN structure, create a handle and returns it + Use this function instead of calling U_EMRFRAMERGN_set() directly. + \return pointer to U_EMRFRAMERGN structure, or NULL on error. + \param ihBrush Brush handle, will be created and returned + \param eht Pointer to structure holding all EMF handles + \param rclBounds Bounding rectangle in device units + \param szlStroke W & H of Brush stroke + \param RgnData Pointer to a U_RGNDATA structure +*/ +char *framergn_set( + uint32_t *ihBrush, + EMFHANDLES *eht, + const U_RECTL rclBounds, + const U_SIZEL szlStroke, + const PU_RGNDATA RgnData + ){ + if(emf_htable_insert(ihBrush, eht))return(NULL); + return(U_EMRFRAMERGN_set(rclBounds, *ihBrush, szlStroke, RgnData)); +} + +/** + \brief Allocate and construct an array of U_POINT objects which has been subjected to a U_XFORM + \returns pointer to an array of U_POINT structures. + \param points pointer to the source U_POINT structures + \param count number of members in points + \param xform U_XFORM to apply + + May also be used to modify U_RECT by doubling the count and casting the pointer. +*/ +PU_POINT points_transform(PU_POINT points, int count, U_XFORM xform){ + PU_POINT newpts; + int i; + float x,y; + newpts = (PU_POINT) malloc(count * sizeof(U_POINT)); + for(i=0; i<count; i++){ + x = (float) points[i].x; + y = (float) points[i].y; + newpts[i].x = U_ROUND(x * xform.eM11 + y * xform.eM21 + xform.eDx); + newpts[i].y = U_ROUND(x * xform.eM12 + y * xform.eM22 + xform.eDy); + } + return(newpts); +} + +/** + \brief Allocate and construct an array of U_POINT16 objects which has been subjected to a U_XFORM + \returns pointer to an array of U_POINT16 structures. + \param points pointer to the source U_POINT16 structures + \param count number of members in points + \param xform U_XFORM to apply + + Transformed src points {x0,y0} appear at {x0*xscale + x, y0*yscale + y} +*/ +PU_POINT16 point16_transform(PU_POINT16 points, int count, U_XFORM xform){ + PU_POINT16 newpts; + int i; + float x,y; + newpts = (PU_POINT16) malloc(count * sizeof(U_POINT16)); + for(i=0; i<count; i++){ + x = (float) points[i].x; + y = (float) points[i].y; + newpts[i].x = U_ROUND(x * xform.eM11 + y * xform.eM21 + xform.eDx); + newpts[i].y = U_ROUND(x * xform.eM12 + y * xform.eM22 + xform.eDy); + } + return(newpts); +} + +/** + \brief Allocate and construct an array of U_TRIVERTEX objects which has been subjected to a U_XFORM + \returns pointer to an array of U_TRIVERTEX structures. + \param tv pointer to the source U_TRIVERTEX structures + \param count number of members in points + \param xform U_XFORM to apply + + Transformed Trivertex points {x0,y0} appear at {x0*xscale + x, y0*yscale + y} +*/ +PU_TRIVERTEX trivertex_transform(PU_TRIVERTEX tv, int count, U_XFORM xform){ + PU_TRIVERTEX newtvs; + int i; + float x,y; + newtvs = (PU_TRIVERTEX) malloc(count * sizeof(U_TRIVERTEX)); + for(i=0; i<count; i++){ + x = (float) tv[i].x; + y = (float) tv[i].y; + newtvs[i] = tv[i]; + newtvs[i].x = U_ROUND(x * xform.eM11 + y * xform.eM21 + xform.eDx); + newtvs[i].y = U_ROUND(x * xform.eM12 + y * xform.eM22 + xform.eDy); + } + return(newtvs); +} + +/** + \brief Allocate and construct an array of U_POINT objects from a set of U_POINT16 objects + \returns pointer to an array of U_POINT structures. + \param points pointer to the source U_POINT16 structures + \param count number of members in points + +*/ +PU_POINT point16_to_point(PU_POINT16 points, int count){ + PU_POINT newpts; + int i; + newpts = (PU_POINT) malloc(count * sizeof(U_POINT)); + for(i=0; i<count; i++){ + newpts[i].x = points[i].x; + newpts[i].y = points[i].y; + } + return(newpts); +} + +/** + \brief Allocate and construct an array of U_POINT16 objects from a set of U_POINT objects + \returns pointer to an array of U_POINT16 structures. + \param points pointer to the source U_POINT structures + \param count number of members in points + + If a coordinate is out of range it saturates at boundary. +*/ +PU_POINT16 point_to_point16(PU_POINT points, int count){ + PU_POINT16 newpts; + int i; + newpts = (PU_POINT16) malloc(count * sizeof(U_POINT16)); + for(i=0; i<count; i++){ + newpts[i].x = U_MNMX(points[i].x, INT16_MIN, INT16_MAX); + newpts[i].y = U_MNMX(points[i].y, INT16_MIN, INT16_MAX); + } + return(newpts); +} + +// hide these from Doxygen +//! @cond +/* ********************************************************************************************** +These functions contain shared code used by various U_EMR*_set functions. These should NEVER be called +by end user code and to further that end prototypes are NOT provided and they are hidden from Doxygen. + + + These are (mostly) ordered by U_EMR_* index number. + For all _set functions the caller must eventually call free() on the returned pointer. + + CORE1(uint32_t iType, U_RECTL rclBounds, const uint32_t cptl, const U_POINTL *points){ + CORE2(uint32_t iType, U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts,const uint32_t cptl, const U_POINTL *points){ + CORE3(uint32_t iType, uint32_t iMode){ (generic 1 uint) + CORE4(uint32_t iType, U_RECTL rclBox){ + CORE5(uint32_t iType){ (generic noargs) + CORE6(uint32_t iType, U_RECTL rclBounds, const uint32_t cpts, const U_POINT16 *points){ (16bit form of CORE1) + CORE7(uint32_t iType, U_PAIR pair){ + CORE8(uint32_t iType, U_RECTL rclBounds, uint32_t iGraphicsMode, U_FLOAT exScale, U_FLOAT eyScale, PU_EMRTEXT emrtext){ + CORE9(uint32_t iType, U_RECTL rclBox, U_POINTL ptlStart, U_POINTL ptlEnd){ + CORE10(uint32_t iType, U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts,const uint32_t cpts, const U_POINT16 *points){ (16bit form of CORE2) + CORE11(uint32_t iType, PU_RGNDATA RgnData){ + CORE12(uint32_t iType, uint32_t ihBrush, uint32_t iUsage, PU_BITMAPINFO Bmi){ + CORE13(uint32_t iType, U_RECTL rclBounds, U_POINTL Dest, U_POINTL cDest, + U_POINTL Src, U_POINTL cSrc, U_XFORM xformSrc, U_COLORREF crBkColorSrc, uint32_t iUsageSrc, + uint32_t Data, PU_BITMAPINFO Bmi); +*********************************************************************************************** */ + + +// Functions with the same form starting with U_EMRPOLYBEZIER_set +char *U_EMR_CORE1(uint32_t iType, U_RECTL rclBounds, const uint32_t cptl, const U_POINTL *points){ + char *record; + int cbPoints; + int irecsize; + + cbPoints = sizeof(U_POINTL)*cptl; + irecsize = sizeof(U_EMRPOLYBEZIER) + cbPoints - sizeof(U_POINTL); // First instance is in struct + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRPOLYBEZIER) record)->rclBounds = rclBounds; + ((PU_EMRPOLYBEZIER) record)->cptl = cptl; + memcpy(((PU_EMRPOLYBEZIER) record)->aptl,points,cbPoints); + } + return(record); +} + +// Functions with the same form starting with U_EMR_POLYPOLYLINE +char *U_EMR_CORE2(uint32_t iType, U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts,const uint32_t cptl, const U_POINTL *points){ + char *record; + int cbPolys,cbPoints,off; + int irecsize; + + cbPoints = sizeof(U_POINTL)*cptl; + cbPolys = sizeof(uint32_t)*nPolys; + irecsize = sizeof(U_EMRPOLYPOLYLINE) + cbPoints + cbPolys - sizeof(uint32_t); // First instance of each is in struct + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRPOLYPOLYLINE) record)->rclBounds = rclBounds; + ((PU_EMRPOLYPOLYLINE) record)->nPolys = nPolys; + ((PU_EMRPOLYPOLYLINE) record)->cptl = cptl; + memcpy(((PU_EMRPOLYPOLYLINE) record)->aPolyCounts,aPolyCounts,cbPolys); + off = sizeof(U_EMRPOLYPOLYLINE) - 4 + cbPolys; + memcpy(record + off,points,cbPoints); + } + return(record); +} + +// Functions with the same form starting with U_EMR_SETMAPMODE_set +char *U_EMR_CORE3(uint32_t iType, uint32_t iMode){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRSETMAPMODE); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSETMAPMODE)record)->iMode = iMode; + } + return(record); +} + +// Functions taking a single U_RECT or U_RECTL, starting with U_EMRELLIPSE_set, also U_EMRFILLPATH, +char *U_EMR_CORE4(uint32_t iType, U_RECTL rclBox){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRELLIPSE); + record = malloc(irecsize); + memset(record,0,irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRELLIPSE)record)->rclBox = rclBox; // bounding rectangle in logical units + } + return(record); +} + +// Functions with the same form starting with U_EMRSETMETARGN_set +char *U_EMR_CORE5(uint32_t iType){ + char *record; + int irecsize = 8; + + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + } + return(record); +} + +// Functions with the same form starting with U_EMRPOLYBEZIER16_set +char *U_EMR_CORE6(uint32_t iType, U_RECTL rclBounds, const uint32_t cpts, const U_POINT16 *points){ + char *record; + int cbPoints,cbPoints4,off; + int irecsize; + + cbPoints = sizeof(U_POINT16)*cpts; + cbPoints4 = UP4(cbPoints); + off = sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(U_NUM_POINT16); // offset to the start of the variable region + irecsize = off + cbPoints4; // First instance is in struct + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRPOLYBEZIER16) record)->rclBounds = rclBounds; + ((PU_EMRPOLYBEZIER16) record)->cpts = cpts; + memcpy(record + off, points, cbPoints); + if(cbPoints < cbPoints4){ + off += cbPoints; + memset(record + off, 0, cbPoints4 - cbPoints); + } + } + return(record); +} + + +// Functions that take a single struct argument which contains two uint32_t, starting with U_EMRSETWINDOWEXTEX_set +// these all pass two 32 bit ints and are cast by the caller to U_PAIR +char *U_EMR_CORE7(uint32_t iType, U_PAIR pair){ + char *record; + int irecsize = sizeof(U_EMRGENERICPAIR); + + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRGENERICPAIR)record)->pair = pair; + } + return(record); +} + +// For U_EMREXTTEXTOUTA and U_EMREXTTEXTOUTW +char *U_EMR_CORE8( + uint32_t iType, + U_RECTL rclBounds, // Bounding rectangle in device units + uint32_t iGraphicsMode, // Graphics mode Enumeration + U_FLOAT exScale, // scale to 0.01 mm units ( only if iGraphicsMode & GM_COMPATIBLE) + U_FLOAT eyScale, // scale to 0.01 mm units ( only if iGraphicsMode & GM_COMPATIBLE) + PU_EMRTEXT emrtext // Text parameters + ){ + char *record; + int irecsize,cbString,cbString4,cbDx,cbEmrtext,cbEmrtextAll; + uint32_t *loffDx; + int csize; + + if( iType == U_EMR_EXTTEXTOUTA){ csize = 1; } // how many bytes per character + else if(iType == U_EMR_EXTTEXTOUTW){ csize = 2; } + else { return(NULL); } + + cbString = csize * emrtext->nChars; + cbString4 = UP4(cbString); // size of the string buffer + cbEmrtext = sizeof(U_EMRTEXT); // size of the constant part of the U_EMRTEXT structure + if(!(emrtext->fOptions & U_ETO_NO_RECT)){ cbEmrtext += sizeof(U_RECTL); } // plus the variable U_RECTL, when it is present + cbDx = emrtext->nChars * sizeof(int32_t); // size of Dx buffer + if(emrtext->fOptions & U_ETO_PDY)cbDx += cbDx; // size of Dx buffer when both x and y offsets are used + cbEmrtextAll = cbEmrtext + sizeof(uint32_t) + cbString4 + cbDx; // structure (+- rect) + offDx + string buf + dx buf + offDx + + /* adjust offset fields in emrtext to match the EMRTEXTOUT* field, currently they match EMRTEXT. + This works because the variable pieces have all been moved outside of the U_EMRTEXT and U_EMRTEXTOUTA strutures. + */ + ((PU_EMRTEXT)emrtext)->offString += sizeof(U_EMREXTTEXTOUTA) - sizeof(U_EMRTEXT); // adjust offString + loffDx = (uint32_t *)((char *)emrtext + cbEmrtext); + *loffDx += sizeof(U_EMREXTTEXTOUTA) - sizeof(U_EMRTEXT); + + // final record size is: U_EMREXTTEXTOUTA (includes constant part of U_EMRTEXT) + U_RECTL (if present) + offDx + dx buffer + string buffer + irecsize = sizeof(U_EMREXTTEXTOUTA) + cbEmrtextAll - sizeof(U_EMRTEXT); // do not count core emrtext strcture twice + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMREXTTEXTOUTA) record)->iGraphicsMode = iGraphicsMode; + ((PU_EMREXTTEXTOUTA) record)->rclBounds = rclBounds; + ((PU_EMREXTTEXTOUTA) record)->exScale = exScale; + ((PU_EMREXTTEXTOUTA) record)->eyScale = eyScale; + // copy the adjusted U_EMRTEXT into the emrtext part of the full record.. + memcpy(&(((PU_EMREXTTEXTOUTA) record)->emrtext), emrtext, cbEmrtextAll); + } + return(record); +} + +// Functions that take a rect and a pair of points, starting with U_EMRARC_set +char *U_EMR_CORE9(uint32_t iType, U_RECTL rclBox, U_POINTL ptlStart, U_POINTL ptlEnd){ + char *record; + int irecsize = sizeof(U_EMRARC); + + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRARC) record)->rclBox = rclBox; + ((PU_EMRARC) record)->ptlStart = ptlStart; + ((PU_EMRARC) record)->ptlEnd = ptlEnd; + } + return(record); +} + +// Functions with the same form starting with U_EMR_POLYPOLYLINE16 +char *U_EMR_CORE10(uint32_t iType, U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts,const uint32_t cpts, const U_POINT16 *points){ + char *record; + int cbPoints,cbPolys,off; + int irecsize; + + cbPolys = sizeof(uint32_t)*nPolys; + cbPoints = sizeof(U_POINT16)*cpts; + irecsize = sizeof(U_EMRPOLYPOLYLINE16) + cbPoints + cbPolys - sizeof(uint32_t); // First instance of each is in struct + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRPOLYPOLYLINE16) record)->rclBounds = rclBounds; + ((PU_EMRPOLYPOLYLINE16) record)->nPolys = nPolys; + ((PU_EMRPOLYPOLYLINE16) record)->cpts = cpts; + memcpy(((PU_EMRPOLYPOLYLINE16) record)->aPolyCounts,aPolyCounts,cbPolys); + off = sizeof(U_EMRPOLYPOLYLINE16) - 4 + cbPolys; + memcpy(record + off,points,cbPoints); + } + return(record); +} + +// common code for U_EMRINVERTRGN and U_EMRPAINTRGN, +char *U_EMR_CORE11(uint32_t iType, PU_RGNDATA RgnData){ + char *record; + int irecsize; + int cbRgns,cbRgns4,off; + + if(!RgnData)return(NULL); + cbRgns = ((PU_RGNDATAHEADER) RgnData)->nRgnSize; + cbRgns4 = UP4(cbRgns); + irecsize = sizeof(U_EMRINVERTRGN) + cbRgns4 - sizeof(U_RGNDATAHEADER); // core + array - overlap + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRINVERTRGN) record)->rclBounds = ((PU_RGNDATAHEADER) RgnData)->rclBounds; + ((PU_EMRINVERTRGN) record)->cbRgnData = cbRgns; + off = irecsize - cbRgns4; + memcpy(record + off, RgnData, cbRgns); + off += cbRgns; + if(cbRgns < cbRgns4){ memset(record + off,0, cbRgns4 - cbRgns); } // clear any unused bytes + } + return(record); +} + + +// common code for U_EMRCREATEMONOBRUSH_set and U_EMRCREATEDIBPATTERNBRUSHPT_set, +char *U_EMR_CORE12_set( + uint32_t iType, + uint32_t ihBrush, // Index to place object in EMF object table (this entry must not yet exist) + uint32_t iUsage, // DIBcolors Enumeration + PU_BITMAPINFO Bmi, // (Optional) bitmapbuffer (U_BITMAPINFO + pixel array) + const uint32_t cbPx, // Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + const char *Px // (Optional) bitmapbuffer (pixel array section ) + ){ + char *record; + int irecsize; + int cbImage,cbImage4,cbBmi,off; + + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + + irecsize = sizeof(U_EMRCREATEMONOBRUSH) + cbBmi + cbImage4; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRCREATEMONOBRUSH) record)->ihBrush = ihBrush; + ((PU_EMRCREATEMONOBRUSH) record)->iUsage = iUsage; + if(cbBmi){ + off = sizeof(U_EMRCREATEMONOBRUSH); + memcpy(record + off, Bmi, cbBmi); + ((PU_EMRCREATEMONOBRUSH) record)->offBmi = off; + ((PU_EMRCREATEMONOBRUSH) record)->cbBmi = cbBmi; + off += cbBmi; + memcpy(record + off, Px, cbPx); + ((PU_EMRCREATEMONOBRUSH) record)->offBits = off; + ((PU_EMRCREATEMONOBRUSH) record)->cbBits = cbImage; + } + else { + ((PU_EMRCREATEMONOBRUSH) record)->offBmi = 0; + ((PU_EMRCREATEMONOBRUSH) record)->cbBmi = 0; + ((PU_EMRCREATEMONOBRUSH) record)->offBits = 0; + ((PU_EMRCREATEMONOBRUSH) record)->cbBits = 0; + } + } + return(record); +} + +// common code for U_EMRBLEND_set and U_EMRTRANSPARENTBLT_set, +char *U_EMR_CORE13_set( + uint32_t iType, + U_RECTL rclBounds, // Bounding rectangle in device units + U_POINTL Dest, // Destination UL corner in logical units + U_POINTL cDest, // Destination width in logical units + U_POINTL Src, // Source UL corner in logical units + U_POINTL cSrc, // Src W & H in logical units + U_XFORM xformSrc, // Transform to apply to source + U_COLORREF crBkColorSrc, // Background color + uint32_t iUsageSrc, // DIBcolors Enumeration + uint32_t Data, // The meaning and type of this field varies, but it is always 4 bytes + const PU_BITMAPINFO Bmi, // (Optional) bitmapbuffer (U_BITMAPINFO section) + const uint32_t cbPx, // Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + char *Px // (Optional) bitmapbuffer (pixel array section ) + ){ + char *record; + int irecsize; + int cbImage,cbImage4,cbBmi,off; + + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + + irecsize = sizeof(U_EMRALPHABLEND) + cbBmi + cbImage4; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = iType; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRALPHABLEND) record)->rclBounds = rclBounds; + ((PU_EMRALPHABLEND) record)->Dest = Dest; + ((PU_EMRALPHABLEND) record)->cDest = cDest; + ((PU_EMRALPHABLEND) record)->Blend = *((PU_BLEND)&Data); + ((PU_EMRALPHABLEND) record)->Src = Src; + ((PU_EMRALPHABLEND) record)->xformSrc = xformSrc; + ((PU_EMRALPHABLEND) record)->crBkColorSrc = crBkColorSrc; + ((PU_EMRALPHABLEND) record)->iUsageSrc = iUsageSrc; + off = sizeof(U_EMRALPHABLEND); + APPEND_PXBMISRC(record, U_EMRALPHABLEND, cbBmi, Bmi, Px, cbImage, cbImage4); + ((PU_EMRALPHABLEND) record)->cSrc = cSrc; + } + return(record); +} +//! @endcond + +/* ********************************************************************************************** +These are the core EMR functions, each creates a particular type of record. +All return these records via a char* pointer, which is NULL if the call failed. +They are listed in order by the corresponding U_EMR_* index number. +*********************************************************************************************** */ + +// U_EMRHEADER_set 1 + +/** + \brief Allocate and construct a U_EMRHEADER record. + \return pointer to U_EMRHEADER record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param rclFrame Bounding rectangle in 0.01 mm units + \param pfmtDesc Pointer to a PixelFormatDescriptor + \param nDesc number of characters in Description, will include first three '\0' + \param Description Description, formatted like: text1\0text2\0\0 + \param szlDevice Reference device size in pixels + \param szlMillimeters Reference device size in 0.01 mm + \param bOpenGL nonZero if OpenGL commands are included +*/ +char *U_EMRHEADER_set( + const U_RECTL rclBounds, + const U_RECTL rclFrame, + U_PIXELFORMATDESCRIPTOR* const pfmtDesc, + U_CBSTR nDesc, + uint16_t* const Description, + const U_SIZEL szlDevice, + const U_SIZEL szlMillimeters, + const uint32_t bOpenGL + ){ + + char *record; + int cbPFD,cbDesc,cbDesc4; + uint32_t off; + int irecsize; + + if(pfmtDesc){ cbPFD = sizeof(U_PIXELFORMATDESCRIPTOR); } + else { cbPFD = 0; } + if(Description){ cbDesc = 2*nDesc; } // also copy the terminator. Size is in bytes + else { cbDesc = 0; } + cbDesc4 = UP4(cbDesc); + irecsize = sizeof(U_EMRHEADER) + cbPFD + cbDesc4; + record = malloc(irecsize); + if(record){ + off = sizeof(U_EMRHEADER); + ((PU_EMR) record)->iType = U_EMR_HEADER; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRHEADER) record)->rclBounds = rclBounds; + ((PU_EMRHEADER) record)->rclFrame = rclFrame; + ((PU_EMRHEADER) record)->dSignature = U_ENHMETA_SIGNATURE; + ((PU_EMRHEADER) record)->nVersion = U_ENHMETA_VERSION; + ((PU_EMRHEADER) record)->nBytes = 0; // Not known yet + ((PU_EMRHEADER) record)->nRecords = 0; // Not known yet + ((PU_EMRHEADER) record)->nHandles = 0; // Not known yet + ((PU_EMRHEADER) record)->sReserved = 0; // Must be 0 + ((PU_EMRHEADER) record)->nDescription = nDesc; + ((PU_EMRHEADER) record)->offDescription = 0; // may change below + ((PU_EMRHEADER) record)->nPalEntries = 0; // Not known yet + ((PU_EMRHEADER) record)->szlDevice = szlDevice; + ((PU_EMRHEADER) record)->szlMillimeters = szlMillimeters; + ((PU_EMRHEADER) record)->cbPixelFormat = cbPFD; + ((PU_EMRHEADER) record)->offPixelFormat = 0; // may change below + ((PU_EMRHEADER) record)->bOpenGL = bOpenGL; + ((PU_EMRHEADER) record)->szlMicrometers.cx = szlMillimeters.cx*1000; + ((PU_EMRHEADER) record)->szlMicrometers.cy = szlMillimeters.cy*1000; + if(cbDesc4){ + ((PU_EMRHEADER) record)->offDescription = off; + memcpy(record + off, Description, cbDesc); + off += cbDesc; + if(cbDesc < cbDesc4)memset(record + off,0,cbDesc4-cbDesc); // clear any unused bytes + off += cbDesc4 - cbDesc; + } + if(cbPFD){ + ((PU_EMRHEADER) record)->offPixelFormat = off; + memcpy(record+off,pfmtDesc,cbPFD); + } + } + return(record); +} + +// U_EMRPOLYBEZIER_set 2 +/** + \brief Allocate and construct a U_EMR_POLYBEZIER record. + \return pointer to U_EMR_POLYBEZIER record, or NULL on error. + \param rclBounds bounding rectangle in device units + \param cptl Number of points to draw + \param points array of points +*/ +char *U_EMRPOLYBEZIER_set( + const U_RECTL rclBounds, + const uint32_t cptl, + const U_POINTL *points + ){ + return(U_EMR_CORE1(U_EMR_POLYBEZIER, rclBounds, cptl, points)); +} + +// U_EMRPOLYGON_set 3 +/** + \brief Allocate and construct a U_EMR_POLYGON record. + \return pointer to U_EMR_POLYGON record, or NULL on error. + \param rclBounds bounding rectangle in device units + \param cptl Number of points to draw + \param points array of points +*/ +char *U_EMRPOLYGON_set( + const U_RECTL rclBounds, + const uint32_t cptl, + const U_POINTL *points + ){ + return(U_EMR_CORE1(U_EMR_POLYGON, rclBounds, cptl, points)); +} + +// U_EMRPOLYLINE_set 4 +/** + \brief Allocate and construct a U_EMR_POLYLINE record. + \return pointer to U_EMR_POLYLINE record, or NULL on error. + \param rclBounds bounding rectangle in device units + \param cptl Number of points to draw + \param points array of points +*/ +char *U_EMRPOLYLINE_set( + const U_RECTL rclBounds, + const uint32_t cptl, + const U_POINTL *points + ){ + return(U_EMR_CORE1(U_EMR_POLYLINE, rclBounds, cptl, points)); +} + +// U_EMRPOLYBEZIERTO_set 5 +/** + \brief Allocate and construct a U_EMR_POLYBEZIERTO record. + \return pointer to U_EMR_POLYBEZIERTO record, or NULL on error. + \param rclBounds bounding rectangle in device units + \param cptl Number of points to draw + \param points array of points +*/ +char *U_EMRPOLYBEZIERTO_set( + const U_RECTL rclBounds, + const uint32_t cptl, + const U_POINTL *points + ){ + return(U_EMR_CORE1(U_EMR_POLYBEZIERTO, rclBounds, cptl, points)); +} + +// U_EMRPOLYLINETO_set 6 +/** + \brief Allocate and construct a U_EMR_POLYLINETO record. + \return pointer to U_EMR_POLYLINETO record, or NULL on error. + \param rclBounds bounding rectangle in device units + \param cptl Number of points to draw + \param points array of points +*/ +char *U_EMRPOLYLINETO_set( + const U_RECTL rclBounds, + const uint32_t cptl, + const U_POINTL *points + ){ + return(U_EMR_CORE1(U_EMR_POLYLINETO, rclBounds, cptl, points)); +} + +// U_EMRPOLYPOLYLINE_set 7 +/** + \brief Allocate and construct a U_EMR_POLYPOLYLINE record. + \return pointer to U_EMR_POLYPOLYLINE record, or NULL on error. + \param rclBounds bounding rectangle in device units + \param nPolys Number of elements in aPolyCounts + \param aPolyCounts Number of points in each poly (sequential) + \param cptl Total number of points (over all poly) + \param points array of points +*/ +char *U_EMRPOLYPOLYLINE_set( + const U_RECTL rclBounds, + const uint32_t nPolys, + const uint32_t *aPolyCounts, + const uint32_t cptl, + const U_POINTL *points + ){ + return(U_EMR_CORE2(U_EMR_POLYPOLYLINE, rclBounds, nPolys, aPolyCounts,cptl, points)); +} + +// U_EMRPOLYPOLYGON_set 8 +/** + \brief Allocate and construct a U_EMR_POLYPOLYGON record. + \return pointer to U_EMR_POLYPOLYGON record, or NULL on error. + \param rclBounds bounding rectangle in device units + \param nPolys Number of elements in aPolyCounts + \param aPolyCounts Number of points in each poly (sequential) + \param cptl Total number of points (over all poly) + \param points array of points +*/ +char *U_EMRPOLYPOLYGON_set( + const U_RECTL rclBounds, + const uint32_t nPolys, + const uint32_t *aPolyCounts, + const uint32_t cptl, + const U_POINTL *points + ){ + return(U_EMR_CORE2(U_EMR_POLYPOLYGON, rclBounds, nPolys, aPolyCounts,cptl, points)); +} + +// U_EMRSETWINDOWEXTEX_set 9 +/** + \brief Allocate and construct a U_EMR_SETWINDOWEXTEX record. + \return pointer to U_EMR_SETWINDOWEXTEX record, or NULL on error. + \param szlExtent H & V extent in logical units +*/ +char *U_EMRSETWINDOWEXTEX_set( + const U_SIZEL szlExtent + ){ + U_PAIR temp; + temp.x = szlExtent.cx; + temp.y = szlExtent.cy; + return(U_EMR_CORE7(U_EMR_SETWINDOWEXTEX, temp)); +} + +// U_EMRSETWINDOWORGEX_set 10 +/** + \brief Allocate and construct a U_EMR_SETWINDOWORGEX record. + \return pointer to U_EMR_SETWINDOWORGEX record, or NULL on error. + \param ptlOrigin H & V origin in logical units +*/ +char *U_EMRSETWINDOWORGEX_set( + const U_POINTL ptlOrigin + ){ + return(U_EMR_CORE7(U_EMR_SETWINDOWORGEX, ptlOrigin)); // U_PAIR and U_POINTL are the same thing +} + +// U_EMRSETVIEWPORTEXTEX_set 11 +/** + \brief Allocate and construct a U_EMR_SETVIEWPORTEXTEX record. + \return pointer to U_EMR_SETVIEWPORTEXTEX record, or NULL on error. + \param szlExtent H & V extent in logical units +*/ +char *U_EMRSETVIEWPORTEXTEX_set( + const U_SIZEL szlExtent + ){ + U_PAIR temp; + temp.x = szlExtent.cx; + temp.y = szlExtent.cy; + return(U_EMR_CORE7(U_EMR_SETVIEWPORTEXTEX, temp)); +} + +// U_EMRSETVIEWPORTORGEX_set 12 +/** + \brief Allocate and construct a U_EMR_SETVIEWPORTORGEX record. + \return pointer to U_EMR_SETVIEWPORTORGEX record, or NULL on error. + \param ptlOrigin H & V origin in logical units +*/ +char *U_EMRSETVIEWPORTORGEX_set( + const U_POINTL ptlOrigin + ){ + return(U_EMR_CORE7(U_EMR_SETVIEWPORTORGEX, ptlOrigin)); // U_PAIR and U_POINTL are the same thing +} + +// U_EMRSETBRUSHORGEX_set 13 +/** + \brief Allocate and construct a U_EMR_SETBRUSHORGEX record. + \return pointer to U_EMR_SETBRUSHORGEX record, or NULL on error. + \param ptlOrigin H & V origin in logical units +*/ +char *U_EMRSETBRUSHORGEX_set( + const U_POINTL ptlOrigin + ){ + return(U_EMR_CORE7(U_EMR_SETBRUSHORGEX, *((PU_PAIR) & ptlOrigin))); +} + +// U_EMREOF_set 14 +/** + \brief Allocate and construct a U_EMR_EOF record. + \return pointer to U_EMR_EOF record, or NULL on error. + \param cbPalEntries Number of palette entries + \param PalEntries (optional) array of PalEntries + \param et tracking information, needed for nSizeLast calculation +*/ +char *U_EMREOF_set( + const U_CBPLENTRIES cbPalEntries, + const PU_LOGPLTNTRY PalEntries, + EMFTRACK *et + ){ + char *record; + char *ptr; + int irecsize; + int cbPals; // space allocated for Palette Entries + uint32_t off; + + if(cbPalEntries && !PalEntries)return(NULL); + if(!et)return(NULL); + cbPals = cbPalEntries * sizeof(U_LOGPLTNTRY); + irecsize = sizeof(U_EMREOF) + cbPals + sizeof(uint32_t); //invariant core, variable palette, palette byte count + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_EOF; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMREOF) record)->cbPalEntries = cbPalEntries; + ((PU_EMREOF) record)->offPalEntries = 0; // May be changed below + off = sizeof(U_EMREOF); //start of the variable region + if(cbPals){ + ((PU_EMREOF) record)->offPalEntries = off; + memcpy(record+off,PalEntries,cbPals); + off += cbPals; + } + ptr = record + off; + *(uint32_t *)ptr = irecsize + et->used; // EMREOF nSizeLast field, not at a fixed position, cannot be accessed by field name + } + et->PalEntries = cbPalEntries; + return(record); +} + + +// U_EMRSETPIXELV_set 15 +/** + \brief Allocate and construct a U_EMR_SETPIXELV record. + \return pointer to U_EMR_SETPIXELV record, or NULL on error. + \param ptlPixel Pixel coordinates (logical) + \param crColor Pixel color +*/ +char *U_EMRSETPIXELV_set( + const U_POINTL ptlPixel, + const U_COLORREF crColor + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRSETPIXELV); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_SETPIXELV; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSETPIXELV)record)->ptlPixel = ptlPixel; + ((PU_EMRSETPIXELV)record)->crColor = crColor; + } + return(record); +} + + +// U_EMRSETMAPPERFLAGS_set 16 +/** + \brief Allocate and construct a U_EMR_SETMAPPERFLAGS record. + \return pointer to U_EMR_SETMAPPERFLAGS record, or NULL on error. +*/ +char *U_EMRSETMAPPERFLAGS_set(void){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRSETMAPPERFLAGS); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_SETMAPPERFLAGS; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSETMAPPERFLAGS)record)->dwFlags = 1; + } + return(record); +} + +// U_EMRSETMAPMODE_set 17 +/** + \brief Allocate and construct a U_EMR_SETMAPMODE record. + \return pointer to U_EMR_SETMAPMODE record, or NULL on error. + \param iMode MapMode Enumeration +*/ +char *U_EMRSETMAPMODE_set( + const uint32_t iMode + ){ + return(U_EMR_CORE3(U_EMR_SETMAPMODE, iMode)); +} + +// U_EMRSETBKMODE_set 18 +/** + \brief Allocate and construct a U_EMR_SETBKMODE record. + \return pointer to U_EMR_SETBKMODE record, or NULL on error. + \param iMode BackgroundMode Enumeration +*/ +char *U_EMRSETBKMODE_set( + const uint32_t iMode + ){ + return(U_EMR_CORE3(U_EMR_SETBKMODE, iMode)); +} + +// U_EMRSETPOLYFILLMODE_set 19 +/** + \brief Allocate and construct a U_EMR_SETPOLYFILLMODE record. + \return pointer to U_EMR_SETPOLYFILLMODE record, or NULL on error. + \param iMode PolygonFillMode Enumeration +*/ +char *U_EMRSETPOLYFILLMODE_set( + const uint32_t iMode + ){ + return(U_EMR_CORE3(U_EMR_SETPOLYFILLMODE, iMode)); +} + +// U_EMRSETROP2_set 20 +/** + \brief Allocate and construct a U_EMR_SETROP2 record. + \return pointer to U_EMR_SETROP2 record, or NULL on error. + \param iMode RasterOperation2 Enumeration +*/ +char *U_EMRSETROP2_set( + const uint32_t iMode + ){ + return(U_EMR_CORE3(U_EMR_SETROP2, iMode)); +} + +// U_EMRSETSTRETCHBLTMODE_set 21 +/** + \brief Allocate and construct a U_EMR_SETSTRETCHBLTMODE record. + \return pointer to U_EMR_SETSTRETCHBLTMODE record, or NULL on error. + \param iMode StretchMode Enumeration +*/ +char *U_EMRSETSTRETCHBLTMODE_set( + const uint32_t iMode + ){ + return(U_EMR_CORE3(U_EMR_SETSTRETCHBLTMODE, iMode)); +} + +// U_EMRSETTEXTALIGN_set 22 +/** + \brief Allocate and construct a U_EMR_SETTEXTALIGN record. + \return pointer to U_EMR_SETTEXTALIGN record, or NULL on error. + \param iMode TextAlignment Enumeration +*/ +char *U_EMRSETTEXTALIGN_set( + const uint32_t iMode + ){ + return(U_EMR_CORE3(U_EMR_SETTEXTALIGN, iMode)); +} + +// U_EMRSETCOLORADJUSTMENT_set 23 +/** + \brief Allocate and construct a U_EMR_SETCOLORADJUSTMENT record. + \return pointer to U_EMR_SETCOLORADJUSTMENT record, or NULL on error. + \param ColorAdjustment Color Adjustment +*/ +char *U_EMRSETCOLORADJUSTMENT_set( + const U_COLORADJUSTMENT ColorAdjustment + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRSETCOLORADJUSTMENT); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_SETCOLORADJUSTMENT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSETCOLORADJUSTMENT) record)->ColorAdjustment = ColorAdjustment; + } + return(record); +} + +// U_EMRSETTEXTCOLOR_set 24 +/** + \brief Allocate and construct a U_EMR_SETTEXTCOLOR record. + \return pointer to U_EMR_SETTEXTCOLOR record, or NULL on error. + \param crColor Text Color +*/ +char *U_EMRSETTEXTCOLOR_set( + const U_COLORREF crColor + ){ + return(U_EMR_CORE3(U_EMR_SETTEXTCOLOR, *(uint32_t *) &crColor)); +} + +// U_EMRSETBKCOLOR_set 25 +/** + \brief Allocate and construct a U_EMR_SETBKCOLOR record. + \return pointer to U_EMR_SETBKCOLOR record, or NULL on error. + \param crColor Background Color +*/ +char *U_EMRSETBKCOLOR_set( + const U_COLORREF crColor + ){ + return(U_EMR_CORE3(U_EMR_SETBKCOLOR, *(uint32_t *) &crColor)); +} + +// U_EMROFFSETCLIPRGN_set 26 +/** + \brief Allocate and construct a U_EMR_OFFSETCLIPRGN record. + \return pointer to U_EMR_OFFSETCLIPRGN record, or NULL on error. + \param ptl Clipping region +*/ +char *U_EMROFFSETCLIPRGN_set( + const U_POINTL ptl + ){ + return(U_EMR_CORE7(U_EMR_OFFSETCLIPRGN, ptl)); +} + +// U_EMRMOVETOEX_set 27 +/** + \brief Allocate and construct a U_EMR_MOVETOEX record. + \return pointer to U_EMR_MOVETOEX record, or NULL on error. + \param ptl Point coordinates +*/ +char *U_EMRMOVETOEX_set( + const U_POINTL ptl + ){ + return(U_EMR_CORE7(U_EMR_MOVETOEX, ptl)); +} + +// U_EMRSETMETARGN_set 28 +/** + \brief Allocate and construct a U_EMR_SETMETARGN record. + \return pointer to U_EMR_SETMETARGN record, or NULL on error. +*/ +char *U_EMRSETMETARGN_set(void){ + return(U_EMR_CORE5(U_EMR_SETMETARGN)); +} + +// U_EMREXCLUDECLIPRECT_set 29 +/** + \brief Allocate and construct a U_EMR_EXCLUDECLIPRECT record. + \return pointer to U_EMR_EXCLUDECLIPRECT record, or NULL on error. + \param rclClip Clipping Region +*/ +char *U_EMREXCLUDECLIPRECT_set( + const U_RECTL rclClip + ){ + return(U_EMR_CORE4(U_EMR_EXCLUDECLIPRECT,rclClip)); +} + +// U_EMRINTERSECTCLIPRECT_set 30 +/** + \brief Allocate and construct a U_EMR_INTERSECTCLIPRECT record. + \return pointer to U_EMR_INTERSECTCLIPRECT record, or NULL on error. + \param rclClip Clipping Region +*/ +char *U_EMRINTERSECTCLIPRECT_set( + const U_RECTL rclClip + ){ + return(U_EMR_CORE4(U_EMR_INTERSECTCLIPRECT,rclClip)); +} + +// U_EMRSCALEVIEWPORTEXTEX_set 31 +/** + \brief Allocate and construct a U_EMR_SCALEVIEWPORTEXTEX record. + \return pointer to U_EMR_SCALEVIEWPORTEXTEX record, or NULL on error. + \param xNum Horizontal multiplier (!=0) + \param xDenom Horizontal divisor (!=0) + \param yNum Vertical multiplier (!=0) + \param yDenom Vertical divisor (!=0) +*/ +char *U_EMRSCALEVIEWPORTEXTEX_set( + const int32_t xNum, + const int32_t xDenom, + const int32_t yNum, + const int32_t yDenom + ){ + return(U_EMR_CORE4(U_EMR_SCALEVIEWPORTEXTEX,(U_RECTL){xNum,xDenom,yNum,yDenom})); +} + + +// U_EMRSCALEWINDOWEXTEX_set 32 +/** + \brief Allocate and construct a U_EMR_SCALEWINDOWEXTEX record. + \return pointer to U_EMR_SCALEWINDOWEXTEX record, or NULL on error. + \param xNum Horizontal multiplier (!=0) + \param xDenom Horizontal divisor (!=0) + \param yNum Vertical multiplier (!=0) + \param yDenom Vertical divisor (!=0) +*/ +char *U_EMRSCALEWINDOWEXTEX_set( + const int32_t xNum, + const int32_t xDenom, + const int32_t yNum, + const int32_t yDenom + ){ + return(U_EMR_CORE4(U_EMR_SCALEWINDOWEXTEX,(U_RECTL){xNum,xDenom,yNum,yDenom})); +} + +// U_EMRSAVEDC_set 33 +/** + \brief Allocate and construct a U_EMR_SAVEDC record. + \return pointer to U_EMR_SAVEDC record, or NULL on error. +*/ +char *U_EMRSAVEDC_set(void){ + return(U_EMR_CORE5(U_EMR_SAVEDC)); +} + +// U_EMRRESTOREDC_set 34 +/** + \brief Allocate and construct a U_EMR_RESTOREDC record. + \return pointer to U_EMR_RESTOREDC record, or NULL on error. + \param iRelative DC to restore. -1 is preceding +*/ +char *U_EMRRESTOREDC_set( + const int32_t iRelative + ){ + return(U_EMR_CORE3(U_EMR_RESTOREDC, (uint32_t) iRelative)); +} + +// U_EMRSETWORLDTRANSFORM_set 35 +/** + \brief Allocate and construct a U_EMR_SETWORLDTRANSFORM record. + \return pointer to U_EMR_SETWORLDTRANSFORM record, or NULL on error. + \param xform Transform to use +*/ +char *U_EMRSETWORLDTRANSFORM_set( + const U_XFORM xform + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRSETWORLDTRANSFORM); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_SETWORLDTRANSFORM; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSETWORLDTRANSFORM) record)->xform = xform; + } + return(record); +} + +// U_EMRMODIFYWORLDTRANSFORM_set 36 +/** + \brief Allocate and construct a U_EMR_MODIFYWORLDTRANSFORM record. + \return pointer to U_EMR_MODIFYWORLDTRANSFORM record, or NULL on error. + \param xform Transform to use + \param iMode ModifyWorldTransformMode Enumeration +*/ +char *U_EMRMODIFYWORLDTRANSFORM_set( + const U_XFORM xform, + const uint32_t iMode + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRMODIFYWORLDTRANSFORM); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_MODIFYWORLDTRANSFORM; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRMODIFYWORLDTRANSFORM) record)->xform = xform; + ((PU_EMRMODIFYWORLDTRANSFORM) record)->iMode = iMode; + } + return(record); +} + +// U_EMRSELECTOBJECT_set 37 +/** + \brief Allocate and construct a U_EMR_SELECTOBJECT record. + Use selectobject_set() instead of calling this function directly. + \return pointer to U_EMR_SELECTOBJECT record, or NULL on error. + \param ihObject Number of a stock or created object +*/ +char *U_EMRSELECTOBJECT_set( + const uint32_t ihObject + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRSELECTOBJECT); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_SELECTOBJECT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSELECTOBJECT) record)->ihObject = ihObject; // Index of object to SELECT + } + return(record); +} + +// U_EMRCREATEPEN_set 38 +/** + \brief Allocate and construct a U_EMR_CREATEPEN record. + Use createpen_set() instead of calling this function directly. + \return pointer to U_EMR_CREATEPEN record, or NULL on error. + \param ihPen Handle of created pen + \param lopn U_LOGPEN structure describing this pen +*/ +char *U_EMRCREATEPEN_set( + const uint32_t ihPen, + const U_LOGPEN lopn + ){ + char *record; + int irecsize=sizeof(U_EMRCREATEPEN); + + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_CREATEPEN; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRCREATEPEN) record)->ihPen = ihPen; + ((PU_EMRCREATEPEN) record)->lopn = lopn; + } + return(record); +} + +// U_EMRCREATEBRUSHINDIRECT_set 39 +/** + \brief Allocate and construct a U_EMR_CREATEBRUSHINDIRECT record. + Use createbrushindirect_set() instead of calling this function directly. + \return pointer to U_EMR_CREATEBRUSHINDIRECT record, or NULL on error. + \param ihBrush Index to place object in EMF object table (this entry must not yet exist) + \param lb Brush properties +*/ +char *U_EMRCREATEBRUSHINDIRECT_set( + const uint32_t ihBrush, + const U_LOGBRUSH lb + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRCREATEBRUSHINDIRECT); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_CREATEBRUSHINDIRECT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRCREATEBRUSHINDIRECT) record)->ihBrush = ihBrush; // Index to place object in EMF object table (this entry must not yet exist) + ((PU_EMRCREATEBRUSHINDIRECT) record)->lb = lb; + } + return(record); +} + +// U_EMRDELETEOBJECT_set 40 +/** + \brief Allocate and construct a U_EMR_DELETEOBJECT record. + Use deleteobject_set() instead of calling this function directly. + \return pointer to U_EMR_DELETEOBJECT record, or NULL on error. + \param ihObject Number of a stock or created object +*/ +char *U_EMRDELETEOBJECT_set( + const uint32_t ihObject + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRDELETEOBJECT); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_DELETEOBJECT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRDELETEOBJECT) record)->ihObject = ihObject; // Index of object to DELETE + } + return(record); +} + +// U_EMRANGLEARC_set 41 +/** + \brief Allocate and construct a U_EMR_ANGLEARC record. + \return pointer to U_EMR_ANGLEARC record, or NULL on error. + \param ptlCenter Center in logical units + \param nRadius Radius in logical units + \param eStartAngle Starting angle in degrees (counter clockwise from x axis) + \param eSweepAngle Sweep angle in degrees +*/ +char *U_EMRANGLEARC_set( + const U_POINTL ptlCenter, + const uint32_t nRadius, + const U_FLOAT eStartAngle, + const U_FLOAT eSweepAngle + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRANGLEARC); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_ANGLEARC; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRANGLEARC) record)->ptlCenter = ptlCenter; + ((PU_EMRANGLEARC) record)->nRadius = nRadius; + ((PU_EMRANGLEARC) record)->eStartAngle = eStartAngle; + ((PU_EMRANGLEARC) record)->eSweepAngle = eSweepAngle; + } + return(record); +} + +// U_EMRELLIPSE_set 42 +/** + \brief Allocate and construct a U_EMR_ELLIPSE record. + \return pointer to U_EMR_ELLIPSE record, or NULL on error. + \param rclBox bounding rectangle in logical units +*/ +char *U_EMRELLIPSE_set( + const U_RECTL rclBox + ){ + return(U_EMR_CORE4(U_EMR_ELLIPSE,rclBox)); +} + +// U_EMRRECTANGLE_set 43 +/** + \brief Allocate and construct a U_EMR_RECTANGLE record. + \return pointer to U_EMR_RECTANGLE record, or NULL on error. + \param rclBox bounding rectangle in logical units +*/ +char *U_EMRRECTANGLE_set( + const U_RECTL rclBox + ){ + return(U_EMR_CORE4(U_EMR_RECTANGLE,rclBox)); +} + +// U_EMRROUNDRECT_set 44 +/** + \brief Allocate and construct a U_EMR_ROUNDRECT record. + \return pointer to U_EMR_ROUNDRECT record, or NULL on error. + \param rclBox bounding rectangle in logical units + \param szlCorner W & H in logical units of ellipse used to round corner +*/ +char *U_EMRROUNDRECT_set( + const U_RECTL rclBox, + const U_SIZEL szlCorner + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRROUNDRECT); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_ROUNDRECT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRROUNDRECT) record)->rclBox = rclBox; + ((PU_EMRROUNDRECT) record)->szlCorner = szlCorner; + } + return(record); +} + +// U_EMRARC_set 45 +/** + \brief Allocate and construct a U_EMR_ARC record. + \return pointer to U_EMR_ARC record, or NULL on error. + \param rclBox bounding rectangle in logical units + \param ptlStart Start point in logical units + \param ptlEnd End point in logical units +*/ +char *U_EMRARC_set( + const U_RECTL rclBox, + const U_POINTL ptlStart, + const U_POINTL ptlEnd + ){ + return(U_EMR_CORE9(U_EMR_ARC,rclBox, ptlStart, ptlEnd)); +} + +// U_EMRCHORD_set 46 +/** + \brief Allocate and construct a U_EMR_CHORD record. + \return pointer to U_EMR_CHORD record, or NULL on error. + \param rclBox bounding rectangle in logical units + \param ptlStart Start point in logical units + \param ptlEnd End point in logical units +*/ +char *U_EMRCHORD_set( + const U_RECTL rclBox, + const U_POINTL ptlStart, + const U_POINTL ptlEnd + ){ + return(U_EMR_CORE9(U_EMR_CHORD,rclBox, ptlStart, ptlEnd)); +} + +// U_EMRPIE_set 47 +/** + \brief Allocate and construct a U_EMR_PIE record. + \return pointer to U_EMR_PIE record, or NULL on error. + \param rclBox bounding rectangle in logical units + \param ptlStart Start point in logical units + \param ptlEnd End point in logical units +*/ +char *U_EMRPIE_set( + const U_RECTL rclBox, + const U_POINTL ptlStart, + const U_POINTL ptlEnd + ){ + return(U_EMR_CORE9(U_EMR_PIE,rclBox, ptlStart, ptlEnd)); +} + +// U_EMRSELECTPALETTE_set 48 +/** + \brief Allocate and construct a U_EMR_SELECTPALETTE record. + \return pointer to U_EMR_SELECTPALETTE record, or NULL on error. + \param ihPal Index of a Palette object in the EMF object table +*/ +char *U_EMRSELECTPALETTE_set( + const uint32_t ihPal + ){ + return(U_EMR_CORE3(U_EMR_SELECTPALETTE, ihPal)); +} + +// U_EMRCREATEPALETTE_set 49 +/** + \brief Allocate and construct a U_EMR_CREATEPALETTE record. + Use createpalette_set() instead of calling this function directly. + \return pointer to U_EMR_CREATEPALETTE record, or NULL on error. + \param ihPal Index to place object in EMF object table (this entry must not yet exist) + \param lgpl Palette properties +*/ +char *U_EMRCREATEPALETTE_set( + const uint32_t ihPal, + const U_LOGPALETTE lgpl + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRCREATEPALETTE); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_CREATEPALETTE; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRCREATEPALETTE) record)->ihPal = ihPal; + ((PU_EMRCREATEPALETTE) record)->lgpl = lgpl; + } + return(record); +} + +// U_EMRSETPALETTEENTRIES_set 50 +/** + \brief Allocate and construct a U_EMR_SETPALETTEENTRIES record. + Use setpaletteentries_set() instead of calling this function directly. + \return pointer to U_EMR_SETPALETTEENTRIES record, or NULL on error. + \param ihPal Index of a Palette object in the EMF object table + \param iStart First Palette entry in selected object to set + \param cEntries Number of Palette entries in selected object to set + \param aPalEntries Values to set with +*/ +char *U_EMRSETPALETTEENTRIES_set( + const uint32_t ihPal, + const uint32_t iStart, + const U_NUM_LOGPLTNTRY cEntries, + const PU_LOGPLTNTRY aPalEntries + ){ + char *record; + int irecsize; + int cbPals; + + if(!aPalEntries)return(NULL); + cbPals = cEntries * sizeof(U_LOGPLTNTRY); + irecsize = sizeof(U_EMRSETPALETTEENTRIES) + cbPals - sizeof(U_LOGPLTNTRY); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_SETPALETTEENTRIES; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSETPALETTEENTRIES) record)->ihPal = ihPal; + ((PU_EMRSETPALETTEENTRIES) record)->iStart = iStart; + ((PU_EMRSETPALETTEENTRIES) record)->cEntries = cEntries; + memcpy(((PU_EMRSETPALETTEENTRIES) record)->aPalEntries, aPalEntries,cbPals); + } + return(record); +} + +// U_EMRRESIZEPALETTE_set 51 +/** + \brief Allocate and construct a U_EMR_RESIZEPALETTE record. + \return pointer to U_EMR_RESIZEPALETTE record, or NULL on error. + \param ihPal Index of a Palette object in the EMF object table + \param cEntries Number to expand or truncate the Palette entry list to +*/ +char *U_EMRRESIZEPALETTE_set( + const uint32_t ihPal, + const uint32_t cEntries + ){ + return(U_EMR_CORE7(U_EMR_RESIZEPALETTE, (U_PAIR){ihPal,cEntries})); +} + +// U_EMRREALIZEPALETTE_set 52 +/** + \brief Allocate and construct a U_EMR_REALIZEPALETTE record. + \return pointer to U_EMR_REALIZEPALETTE record, or NULL on error. +*/ +char *U_EMRREALIZEPALETTE_set(void){ + return(U_EMR_CORE5(U_EMR_REALIZEPALETTE)); +} + +// U_EMREXTFLOODFILL_set 53 +/** + \brief Allocate and construct a U_EMR_EXTFLOODFILL record. + \return pointer to U_EMR_EXTFLOODFILL record, or NULL on error. + \param ptlStart Start point in logical units + \param crColor Color to fill with + \param iMode FloodFill Enumeration +*/ +char *U_EMREXTFLOODFILL_set( + const U_POINTL ptlStart, + const U_COLORREF crColor, + const uint32_t iMode + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMREXTFLOODFILL); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_EXTFLOODFILL; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMREXTFLOODFILL) record)->ptlStart = ptlStart; + ((PU_EMREXTFLOODFILL) record)->crColor = crColor; + ((PU_EMREXTFLOODFILL) record)->iMode = iMode; + } + return(record); +} + +// U_EMRLINETO_set 54 +/** + \brief Allocate and construct a U_EMR_LINETO record. + \return pointer to U_EMR_LINETO record, or NULL on error. + \param ptl Point coordinates +*/ +char *U_EMRLINETO_set( + const U_POINTL ptl + ){ + return(U_EMR_CORE7(U_EMR_LINETO, ptl)); +} + +// U_EMRARCTO_set 55 +/** + \brief Allocate and construct a U_EMR_ARCTO record. + \return pointer to U_EMR_ARCTO record, or NULL on error. + \param rclBox bounding rectangle in logical units + \param ptlStart Start point in logical units + \param ptlEnd End point in logical units + + Note that the draw begins with a line from the current point to ptlStart, which is + not indicated in the Microsoft EMF documentation for this record. +*/ +char *U_EMRARCTO_set( + U_RECTL rclBox, + U_POINTL ptlStart, + U_POINTL ptlEnd + ){ + return(U_EMR_CORE9(U_EMR_ARCTO,rclBox, ptlStart, ptlEnd)); +} + +// U_EMRPOLYDRAW_set 56 +/** + \brief Allocate and construct a U_EMR_POLYDRAW record. + \return pointer to U_EMR_POLYDRAW record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param cptl Number of U_POINTL objects + \param aptl Array of U_POINTL objects + \param abTypes Array of Point Enumeration +*/ +char *U_EMRPOLYDRAW_set( + const U_RECTL rclBounds, + const U_NUM_POINTL cptl, + const U_POINTL *aptl, + const uint8_t *abTypes + ){ + char *record; + int irecsize; + int cbPoints, cbAbTypes, cbAbTypes4, off; + + if(!cptl || !aptl || !abTypes)return(NULL); + cbPoints = cptl * sizeof(U_POINTL); // space for aptl + cbAbTypes = cptl; // number of abTypes (also size, 1 byte each) + cbAbTypes4 = UP4(cbAbTypes); // space for abTypes + irecsize = sizeof(U_EMRPOLYDRAW) + cbPoints + cbAbTypes4 - sizeof(U_POINTL) - 1; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_POLYDRAW; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRPOLYDRAW) record)->rclBounds = rclBounds; + ((PU_EMRPOLYDRAW) record)->cptl = cptl; + off = sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(uint32_t); // offset to first variable part + memcpy(record+off,aptl,cbPoints); + off += cbPoints; + memcpy(record+off,abTypes,cbAbTypes); + off += cbAbTypes; + if(cbAbTypes4 > cbAbTypes){ memset(record+off,0,cbAbTypes4-cbAbTypes); } // keeps valgrind happy (initialize padding after byte array) + } + return(record); +} + +// U_EMRSETARCDIRECTION_set 57 +/** + \brief Allocate and construct a U_EMR_SETARCDIRECTION record. + \return pointer to U_EMR_SETARCDIRECTION record, or NULL on error. + \param iArcDirection ArcDirection Enumeration +*/ +char *U_EMRSETARCDIRECTION_set( + const uint32_t iArcDirection + ){ + return(U_EMR_CORE3(U_EMR_SETARCDIRECTION, iArcDirection)); +} + +// U_EMRSETMITERLIMIT_set 58 +/** + \brief Allocate and construct a U_EMR_SETMITERLIMIT record. + \return pointer to U_EMR_SETMITERLIMIT record, or NULL on error. + \param eMiterLimit MapMode Enumeration +*/ +char *U_EMRSETMITERLIMIT_set( + const uint32_t eMiterLimit + ){ + return(U_EMR_CORE3(U_EMR_SETMITERLIMIT, eMiterLimit)); +} + + +// U_EMRBEGINPATH_set 59 +/** + \brief Allocate and construct a U_EMR_BEGINPATH record. + \return pointer to U_EMR_BEGINPATH record, or NULL on error. +*/ +char *U_EMRBEGINPATH_set(void){ + return(U_EMR_CORE5(U_EMR_BEGINPATH)); +} + +// U_EMRENDPATH_set 60 +/** + \brief Allocate and construct a U_EMR_ENDPATH record. + \return pointer to U_EMR_ENDPATH record, or NULL on error. +*/ +char *U_EMRENDPATH_set(void){ + return(U_EMR_CORE5(U_EMR_ENDPATH)); +} + +// U_EMRCLOSEFIGURE_set 61 +/** + \brief Allocate and construct a U_EMR_CLOSEFIGURE record. + \return pointer to U_EMR_CLOSEFIGURE record, or NULL on error. +*/ +char *U_EMRCLOSEFIGURE_set(void){ + return(U_EMR_CORE5(U_EMR_CLOSEFIGURE)); +} + +// U_EMRFILLPATH_set 62 +/** + \brief Allocate and construct a U_EMR_FILLPATH record. + \return pointer to U_EMR_FILLPATH record, or NULL on error. + \param rclBox Bounding rectangle in device units + + U_EMR_FILLPATH closes the open figure before filling. +*/ +char *U_EMRFILLPATH_set( + const U_RECTL rclBox + ){ + return(U_EMR_CORE4(U_EMR_FILLPATH,rclBox)); +} + +// U_EMRSTROKEANDFILLPATH_set 63 +/** + \brief Allocate and construct a U_EMR_STROKEANDFILLPATH record. + \return pointer to U_EMR_STROKEANDFILLPATH record, or NULL on error. + \param rclBox Bounding rectangle in device units + + U_EMR_STROKEANDFILLPATH closes the open figure before filling and stroking. + There appears to be no way to fill an open path while stroking it, as any one + of U_EMRFILLPATH, U_EMRSTROKEPATH, or U_EMRSTROKEANDFILEPATH will "use up" the path, +*/ +char *U_EMRSTROKEANDFILLPATH_set( + const U_RECTL rclBox + ){ + return(U_EMR_CORE4(U_EMR_STROKEANDFILLPATH,rclBox)); +} + +// U_EMRSTROKEPATH_set 64 +/** + \brief Allocate and construct a U_EMR_STROKEPATH record. + \return pointer to U_EMR_STROKEPATH record, or NULL on error. + \param rclBox Bounding rectangle in device units + + U_EMR_STROKEPATH does NOT close the open figure before stroking it. +*/ +char *U_EMRSTROKEPATH_set( + const U_RECTL rclBox + ){ + return(U_EMR_CORE4(U_EMR_STROKEPATH,rclBox)); +} + +// U_EMRFLATTENPATH_set 65 +/** + \brief Allocate and construct a U_EMR_FLATTENPATH record. + \return pointer to U_EMR_FLATTENPATH record, or NULL on error. +*/ +char *U_EMRFLATTENPATH_set(void){ + return(U_EMR_CORE5(U_EMR_FLATTENPATH)); +} + +// U_EMRWIDENPATH_set 66 +/** + \brief Allocate and construct a U_EMR_WIDENPATH record. + \return pointer to U_EMR_WIDENPATH record, or NULL on error. +*/ +char *U_EMRWIDENPATH_set(void){ + return(U_EMR_CORE5(U_EMR_WIDENPATH)); +} + +// U_EMRSELECTCLIPPATH_set 67 +/** + \brief Allocate and construct a U_EMR_SELECTCLIPPATH record. + \return pointer to U_EMR_SELECTCLIPPATH record, or NULL on error. + \param iMode RegionMode Enumeration +*/ +char *U_EMRSELECTCLIPPATH_set( + const uint32_t iMode + ){ + return(U_EMR_CORE3(U_EMR_SELECTCLIPPATH, iMode)); +} + +// U_EMRABORTPATH_set 68 +/** + \brief Allocate and construct a U_EMR_ABORTPATH record. + \return pointer to U_EMR_ABORTPATH record, or NULL on error. +*/ +char *U_EMRABORTPATH_set(void){ + return(U_EMR_CORE5(U_EMR_ABORTPATH)); +} + +// U_EMRUNDEF69 69 + +// U_EMRCOMMENT_set 70 Comment (any binary data, interpretation is program specific) +/** + \brief Allocate and construct a U_EMR_COMMENT record. + \return pointer to U_EMR_COMMENT record, or NULL on error. + \param cbData Number of bytes in comment + \param Data Comment (any binary data, interpretation is program specific) +*/ +char *U_EMRCOMMENT_set( + const U_CBDATA cbData, + const char *Data + ){ + char *record; + unsigned int cbData4; + int irecsize; + + cbData4 = UP4(cbData); + irecsize = sizeof(U_EMR) + sizeof(U_CBDATA) + cbData4; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_COMMENT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRCOMMENT) record)->cbData = cbData; + memcpy(record + irecsize - cbData4,Data,cbData); + if(cbData4 > cbData)memset(record + irecsize - cbData4 + cbData,0,cbData4-cbData); // clear any unused bytes + } + return(record); +} + +// U_EMRFILLRGN_set 71 +/** + \brief Allocate and construct a U_EMR_FILLRGN record. + Use fillrgn_set() instead of calling this function directly. + \return pointer to U_EMR_FILLRGN record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param ihBrush Index of a Brush object in the EMF object table + \param RgnData Pointer to a U_RGNDATA structure +*/ +char *U_EMRFILLRGN_set( + const U_RECTL rclBounds, + const uint32_t ihBrush, + const PU_RGNDATA RgnData + ){ + char *record; + int irecsize; + int cbRgns,cbRgns4,off; + + if(!RgnData)return(NULL); + cbRgns = ((PU_RGNDATAHEADER) RgnData)->nRgnSize; + cbRgns4 = UP4(cbRgns); + irecsize = sizeof(U_EMRFILLRGN) + cbRgns4 - sizeof(U_RGNDATAHEADER); // core + array - overlap + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_FILLRGN; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRFILLRGN) record)->rclBounds = rclBounds; // or ??? ((PU_RGNDATAHEADER) RgnData)->rclBounds; + ((PU_EMRFILLRGN) record)->cbRgnData = cbRgns; + ((PU_EMRFILLRGN) record)->ihBrush = ihBrush; + off = irecsize - cbRgns4; + memcpy(record + off, RgnData, cbRgns); + off += cbRgns; + if(cbRgns4 > cbRgns){ memset(record + off,0,cbRgns4-cbRgns); } // clear any unused bytes + } + return(record); +} + +// U_EMRFRAMERGN_set 72 +/** + \brief Allocate and construct a U_EMR_FRAMERGN record. + Use framegrn_set() instead of calling this function directly. + \return pointer to U_EMR_FRAMERGN record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param ihBrush Index of a Brush object in the EMF object table + \param szlStroke W & H of Brush stroke + \param RgnData Pointer to a U_RGNDATA structure +*/ +char *U_EMRFRAMERGN_set( + const U_RECTL rclBounds, + const uint32_t ihBrush, + const U_SIZEL szlStroke, + const PU_RGNDATA RgnData + ){ + char *record; + int irecsize; + int cbRgns,cbRgns4,off; + + if(!RgnData)return(NULL); + cbRgns = ((PU_RGNDATAHEADER) RgnData)->nRgnSize; + cbRgns4 = UP4(cbRgns); + irecsize = sizeof(U_EMRFRAMERGN) + cbRgns4 - sizeof(U_RGNDATAHEADER); // core + array - overlap + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_FRAMERGN; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRFRAMERGN) record)->rclBounds = rclBounds; // or ??? ((PU_RGNDATAHEADER) RgnData)->rclBounds; + ((PU_EMRFRAMERGN) record)->cbRgnData = cbRgns; + ((PU_EMRFRAMERGN) record)->ihBrush = ihBrush; + ((PU_EMRFRAMERGN) record)->szlStroke = szlStroke; + off = irecsize - cbRgns4; + memcpy(record + off, RgnData, cbRgns); + off += cbRgns; + if(cbRgns4 > cbRgns){ memset(record + off,0,cbRgns4-cbRgns); } // clear any unused bytes + } + return(record); +} + +// U_EMRINVERTRGN_set 73 +/** + \brief Allocate and construct a U_EMR_INVERTRGN record. + \return pointer to U_EMR_INVERTRGN record, or NULL on error. + \param RgnData Variable size U_RGNDATA structure +*/ +char *U_EMRINVERTRGN_set( + const PU_RGNDATA RgnData + ){ + return(U_EMR_CORE11(U_EMR_INVERTRGN, RgnData)); +} + +// U_EMRPAINTRGN_set 74 +/** + \brief Allocate and construct a U_EMR_PAINTRGN record. + \return pointer to U_EMR_PAINTRGN record, or NULL on error. + \param RgnData Variable size U_RGNDATA structure +*/ +char *U_EMRPAINTRGN_set( + const PU_RGNDATA RgnData + ){ + return(U_EMR_CORE11(U_EMR_PAINTRGN, RgnData)); +} + +// U_EMREXTSELECTCLIPRGN_set 75 +/** + \brief Allocate and construct a U_EMR_EXTSELECTCLIPRGN record. + \return pointer to U_EMR_EXTSELECTCLIPRGN or NULL on error. + \param iMode RegionMode Enumeration + \param RgnData Variable size U_RGNDATA structure +*/ +char *U_EMREXTSELECTCLIPRGN_set( + const uint32_t iMode, + const PU_RGNDATA RgnData + ){ + char *record; + int irecsize; + int cbRgns,cbRgns4,off; + + if(!RgnData)return(NULL); + cbRgns = ((PU_RGNDATAHEADER) RgnData)->nRgnSize; + cbRgns4 = UP4(cbRgns); + irecsize = sizeof(U_EMREXTSELECTCLIPRGN) + cbRgns4 - sizeof(U_RGNDATAHEADER); // core + array - overlap + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_EXTSELECTCLIPRGN; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMREXTSELECTCLIPRGN) record)->cbRgnData = cbRgns; + ((PU_EMREXTSELECTCLIPRGN) record)->iMode = iMode; + off = irecsize - cbRgns4; + memcpy(record + off, RgnData, cbRgns); + off += cbRgns; + if(cbRgns4 > cbRgns){ memset(record + off,0,cbRgns4-cbRgns); } // clear any unused bytes + } + return(record); +} + +// U_EMRBITBLT_set 76 +/** + \brief Allocate and construct a U_EMR_BITBLT record. + \return pointer to U_EMR_BITBLT record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param Dest Destination UL corner in logical units + \param cDest Destination width in logical units + \param Src Source rectangle UL corner in logical units + \param xformSrc Source bitmap transform (world to page coordinates) + \param crBkColorSrc Source bitmap background color + \param iUsageSrc DIBcolors Enumeration + \param dwRop Ternary Raster Operation enumeration + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_EMRBITBLT_set( + const U_RECTL rclBounds, + const U_POINTL Dest, + const U_POINTL cDest, + const U_POINTL Src, + const U_XFORM xformSrc, + const U_COLORREF crBkColorSrc, + const uint32_t iUsageSrc, + const uint32_t dwRop, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px + ){ + char *record; + int irecsize; + int cbImage,cbImage4,cbBmi,off; + + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + + irecsize = sizeof(U_EMRBITBLT) + cbBmi + cbImage4; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_BITBLT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRBITBLT) record)->rclBounds = rclBounds; + ((PU_EMRBITBLT) record)->Dest = Dest; + ((PU_EMRBITBLT) record)->cDest = cDest; + ((PU_EMRBITBLT) record)->dwRop = dwRop; + ((PU_EMRBITBLT) record)->Src = Src; + ((PU_EMRBITBLT) record)->xformSrc = xformSrc; + ((PU_EMRBITBLT) record)->crBkColorSrc = crBkColorSrc; + ((PU_EMRBITBLT) record)->iUsageSrc = iUsageSrc; + off = sizeof(U_EMRBITBLT); + APPEND_PXBMISRC(record, U_EMRBITBLT, cbBmi, Bmi, Px, cbImage, cbImage4); + } + return(record); +} + +// U_EMRSTRETCHBLT_set 77 +/** + \brief Allocate and construct a U_EMR_STRETCHBLT record. + \return pointer to U_EMR_STRETCHBLT record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param Dest Destination UL corner in logical units + \param cDest Destination width in logical units + \param Src Source UL corner in logical units + \param cSrc Src W & H in logical units + \param xformSrc Transform to apply to source + \param crBkColorSrc Background color + \param iUsageSrc DIBcolors Enumeration + \param dwRop Ternary Raster Operation enumeration + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_EMRSTRETCHBLT_set( + const U_RECTL rclBounds, + const U_POINTL Dest, + const U_POINTL cDest, + const U_POINTL Src, + const U_POINTL cSrc, + const U_XFORM xformSrc, + const U_COLORREF crBkColorSrc, + const uint32_t iUsageSrc, + const uint32_t dwRop, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px + ){ + char *record; + int irecsize; + int cbImage,cbImage4,cbBmi,off; + + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + + irecsize = sizeof(U_EMRSTRETCHBLT) + cbBmi + cbImage4; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_STRETCHBLT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSTRETCHBLT) record)->rclBounds = rclBounds; + ((PU_EMRSTRETCHBLT) record)->Dest = Dest; + ((PU_EMRSTRETCHBLT) record)->cDest = cDest; + ((PU_EMRSTRETCHBLT) record)->dwRop = dwRop; + ((PU_EMRSTRETCHBLT) record)->Src = Src; + ((PU_EMRSTRETCHBLT) record)->xformSrc = xformSrc; + ((PU_EMRSTRETCHBLT) record)->crBkColorSrc = crBkColorSrc; + ((PU_EMRSTRETCHBLT) record)->iUsageSrc = iUsageSrc; + off = sizeof(U_EMRSTRETCHBLT); + APPEND_PXBMISRC(record, U_EMRSTRETCHBLT, cbBmi, Bmi, Px, cbImage, cbImage4); + ((PU_EMRSTRETCHBLT) record)->cSrc = cSrc; + } + return(record); +} + +// U_EMRMASKBLT_set 78 +/** + \brief Allocate and construct a U_EMR_MASKBLT record. + \return pointer to U_EMR_MASKBLT record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param Dest Destination UL corner in logical units + \param cDest Destination width in logical units + \param Src Source UL corner in logical units + \param xformSrc Transform to apply to source + \param crBkColorSrc Background color + \param iUsageSrc DIBcolors Enumeration + \param Mask Mask UL corner in logical units + \param iUsageMask DIBcolors Enumeration + \param dwRop Ternary Raster Operation enumeration + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) + \param MskBmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbMsk Size in bytes of mask array (row stride * height, there may be some padding at the end of each row) + \param Msk (Optional) bitmapbuffer (mask section ) +*/ +char *U_EMRMASKBLT_set( + const U_RECTL rclBounds, + const U_POINTL Dest, + const U_POINTL cDest, + const U_POINTL Src, + const U_XFORM xformSrc, + const U_COLORREF crBkColorSrc, + const uint32_t iUsageSrc, + const U_POINTL Mask, + const uint32_t iUsageMask, + const uint32_t dwRop, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px, + const PU_BITMAPINFO MskBmi, + const uint32_t cbMsk, + char *Msk + ){ + char *record; + int irecsize; + int cbImage,cbImage4,cbBmi,cbMskImage,cbMskImage4,cbMskBmi,off; + + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + SET_CB_FROM_PXBMI(Msk,MskBmi,cbMskImage,cbMskImage4,cbMskBmi,cbMsk); + + irecsize = sizeof(U_EMRMASKBLT) + cbBmi + cbImage4 + cbMskBmi + cbMskImage4; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_MASKBLT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRMASKBLT) record)->rclBounds = rclBounds; + ((PU_EMRMASKBLT) record)->Dest = Dest; + ((PU_EMRMASKBLT) record)->cDest = cDest; + ((PU_EMRMASKBLT) record)->dwRop = dwRop; + ((PU_EMRMASKBLT) record)->Src = Src; + ((PU_EMRMASKBLT) record)->xformSrc = xformSrc; + ((PU_EMRMASKBLT) record)->crBkColorSrc = crBkColorSrc; + ((PU_EMRMASKBLT) record)->iUsageSrc = iUsageSrc; + ((PU_EMRMASKBLT) record)->Mask = Mask; + ((PU_EMRMASKBLT) record)->iUsageMask = iUsageMask; + off = sizeof(U_EMRMASKBLT); + APPEND_PXBMISRC(record, U_EMRMASKBLT, cbBmi, Bmi, Px, cbImage, cbImage4); + APPEND_MSKBMISRC(record, U_EMRMASKBLT, cbMskBmi, MskBmi, Msk, cbMskImage, cbMskImage4); + } + return(record); +} + +// U_EMRPLGBLT_set 79 + +/** + \brief Allocate and construct a U_EMRPLGBLT record. + \return U_EMRPLGBLT record. + \param rclBounds Bounding rectangle in device units + \param aptlDst Defines parallelogram, UL, UR, LL corners, LR is derived (3 points) + \param Src Source UL corner in logical units + \param cSrc Source width in logical units + \param xformSrc Transform to apply to source + \param crBkColorSrc Background color + \param iUsageSrc DIBcolors Enumeration + \param Mask Mask UL corner in logical units + \param iUsageMask DIBcolors Enumeration + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) + \param MskBmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbMsk Size in bytes of mask array (row stride * height, there may be some padding at the end of each row) + \param Msk (Optional) bitmapbuffer (mask section ) +*/ +char *U_EMRPLGBLT_set( + const U_RECTL rclBounds, + const PU_POINTL aptlDst, + const U_POINTL Src, + const U_POINTL cSrc, + const U_XFORM xformSrc, + const U_COLORREF crBkColorSrc, + const uint32_t iUsageSrc, + const U_POINTL Mask, + const uint32_t iUsageMask, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px, + const PU_BITMAPINFO MskBmi, + const uint32_t cbMsk, + char *Msk + ){ + char *record; + int irecsize; + int cbImage,cbImage4,cbBmi,cbMskImage,cbMskImage4,cbMskBmi,off; + + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + SET_CB_FROM_PXBMI(Msk,MskBmi,cbMskImage,cbMskImage4,cbMskBmi,cbMsk); + + irecsize = sizeof(U_EMRPLGBLT) + cbBmi + cbImage4 + cbMskBmi + cbMskImage4; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_PLGBLT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRPLGBLT) record)->rclBounds = rclBounds; + memcpy(((PU_EMRPLGBLT) record)->aptlDst,aptlDst,3*sizeof(U_POINTL)); + ((PU_EMRPLGBLT) record)->Src = Src; + ((PU_EMRPLGBLT) record)->cSrc = cSrc; + ((PU_EMRPLGBLT) record)->xformSrc = xformSrc; + ((PU_EMRPLGBLT) record)->crBkColorSrc = crBkColorSrc; + ((PU_EMRPLGBLT) record)->iUsageSrc = iUsageSrc; + ((PU_EMRPLGBLT) record)->Mask = Mask; + ((PU_EMRPLGBLT) record)->iUsageMask = iUsageMask; + off = sizeof(U_EMRPLGBLT); + APPEND_PXBMISRC(record, U_EMRPLGBLT, cbBmi, Bmi, Px, cbImage, cbImage4); + APPEND_MSKBMISRC(record, U_EMRPLGBLT, cbMskBmi, MskBmi, Msk, cbMskImage, cbMskImage4); + } + return(record); +} + +// U_EMRSETDIBITSTODEVICE_set 80 +/** + \brief Allocate and construct a U_EMR_SETDIBITSTODEVICE record. + \return pointer to U_EMR_SETDIBITSTODEVICE record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param Dest Destination UL corner in logical units + \param Src Source UL corner in logical units + \param cSrc Source W & H in logical units + \param iUsageSrc DIBColors Enumeration + \param iStartScan First scan line + \param cScans Number of scan lines + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_EMRSETDIBITSTODEVICE_set( + const U_RECTL rclBounds, + const U_POINTL Dest, + const U_POINTL Src, + const U_POINTL cSrc, + const uint32_t iUsageSrc, + const uint32_t iStartScan, + const uint32_t cScans, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px + ){ + char *record; + int irecsize; + int cbImage,cbImage4,cbBmi,off; + + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + + irecsize = sizeof(U_EMRSETDIBITSTODEVICE) + cbBmi + cbImage4; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_SETDIBITSTODEVICE; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSETDIBITSTODEVICE) record)->rclBounds = rclBounds; + ((PU_EMRSETDIBITSTODEVICE) record)->Dest = Dest; + ((PU_EMRSETDIBITSTODEVICE) record)->Src = Src; + ((PU_EMRSETDIBITSTODEVICE) record)->cSrc = cSrc; + ((PU_EMRSETDIBITSTODEVICE) record)->iUsageSrc = iUsageSrc; + ((PU_EMRSETDIBITSTODEVICE) record)->iStartScan = iStartScan; + ((PU_EMRSETDIBITSTODEVICE) record)->cScans = cScans; + off = sizeof(U_EMRSETDIBITSTODEVICE); + APPEND_PXBMISRC(record, U_EMRSETDIBITSTODEVICE, cbBmi, Bmi, Px, cbImage, cbImage4); + } + return(record); +} + +// U_EMRSTRETCHDIBITS_set 81 +/** + \brief Allocate and construct a U_EMR_EMRSTRETCHDIBITS record. + \return pointer to U_EMR_EMRSTRETCHDIBITS record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param Dest Destination UL corner in logical units + \param cDest Destination W & H in logical units + \param Src Source UL corner in logical units + \param cSrc Source W & H in logical units + \param iUsageSrc DIBColors Enumeration + \param dwRop RasterOPeration Enumeration + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row STRIDE * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_EMRSTRETCHDIBITS_set( + const U_RECTL rclBounds, + const U_POINTL Dest, + const U_POINTL cDest, + const U_POINTL Src, + const U_POINTL cSrc, + const uint32_t iUsageSrc, + const uint32_t dwRop, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px + ){ + char *record; + int irecsize; + int cbImage,cbImage4,cbBmi,off; + + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + + irecsize = sizeof(U_EMRSTRETCHDIBITS) + cbBmi + cbImage4; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_STRETCHDIBITS; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSTRETCHDIBITS) record)->rclBounds = rclBounds; + ((PU_EMRSTRETCHDIBITS) record)->Dest = Dest; + ((PU_EMRSTRETCHDIBITS) record)->Src = Src; + ((PU_EMRSTRETCHDIBITS) record)->cSrc = cSrc; + ((PU_EMRSTRETCHDIBITS) record)->iUsageSrc = iUsageSrc; + ((PU_EMRSTRETCHDIBITS) record)->dwRop = dwRop; + ((PU_EMRSTRETCHDIBITS) record)->cDest = cDest; + off = sizeof(U_EMRSTRETCHDIBITS); + APPEND_PXBMISRC(record, U_EMRSTRETCHDIBITS, cbBmi, Bmi, Px, cbImage, cbImage4); + } + return(record); +} + +// U_EMREXTCREATEFONTINDIRECTW_set 82 +/** + \brief Allocate and construct a U_EMR_EXTCREATEFONTINDIRECTW record. + Use extcreatefontindirectw_set() instead of calling this function directly. + \return pointer to U_EMR_EXTCREATEFONTINDIRECTW record, or NULL on error. + \param ihFont Index of the font in the EMF object table + \param elf Font parameters as U_LOGFONT + \param elfw Font parameters as U_LOGFONT_PANOSE +*/ +char *U_EMREXTCREATEFONTINDIRECTW_set( + const uint32_t ihFont, + const char * elf, + const char * elfw + ){ + char *record; + const char *cptr; + int irecsize; + int cbLf,off; + + if((elf && elfw) || (!elf && !elfw))return(NULL); // ONE only must be passed + if(elf){ cbLf = sizeof(U_LOGFONT); cptr = elf; } + else { cbLf = sizeof(U_LOGFONT_PANOSE); cptr = elfw; } + + irecsize = sizeof(U_EMR) + sizeof(uint32_t) + cbLf; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_EXTCREATEFONTINDIRECTW; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMREXTCREATEFONTINDIRECTW) record)->ihFont = ihFont; + off = sizeof(U_EMR) + sizeof(uint32_t); + memcpy(record + off, cptr, cbLf); // No need to add padding for either structure + } + return(record); +} + +// U_EMREXTTEXTOUTA_set 83 +/** + \brief Allocate and construct a U_EMR_EXTTEXTOUTA record. + \return pointer to U_EMR_EXTTEXTOUTA record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param iGraphicsMode Graphics mode Enumeration + \param exScale scale to 0.01 mm units ( only if iGraphicsMode & GM_COMPATIBLE) + \param eyScale scale to 0.01 mm units ( only if iGraphicsMode & GM_COMPATIBLE) + \param emrtext Text parameters +*/ +char *U_EMREXTTEXTOUTA_set( + const U_RECTL rclBounds, + const uint32_t iGraphicsMode, + const U_FLOAT exScale, + const U_FLOAT eyScale, + const PU_EMRTEXT emrtext + ){ + return(U_EMR_CORE8(U_EMR_EXTTEXTOUTA,rclBounds, iGraphicsMode, exScale, eyScale,emrtext)); +} + +// U_EMREXTTEXTOUTW_set 84 +/** + \brief Allocate and construct a U_EMR_EXTTEXTOUTW record. + \return pointer to U_EMR_EXTTEXTOUTW record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param iGraphicsMode Graphics mode Enumeration + \param exScale scale to 0.01 mm units ( only if iGraphicsMode & GM_COMPATIBLE) + \param eyScale scale to 0.01 mm units ( only if iGraphicsMode & GM_COMPATIBLE) + \param emrtext Text parameters +*/ +char *U_EMREXTTEXTOUTW_set( + const U_RECTL rclBounds, + const uint32_t iGraphicsMode, + const U_FLOAT exScale, + const U_FLOAT eyScale, + const PU_EMRTEXT emrtext + ){ + return(U_EMR_CORE8(U_EMR_EXTTEXTOUTW,rclBounds, iGraphicsMode, exScale, eyScale,emrtext)); +} + +// U_EMRPOLYBEZIER16_set 85 +/** + \brief Allocate and construct a U_EMR_POLYBEZIER16 record. + \return pointer to U_EMR_POLYBEZIER16 record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param cpts Number of POINT16 in array + \param points Array of POINT16 +*/ +char *U_EMRPOLYBEZIER16_set( + const U_RECTL rclBounds, + const uint32_t cpts, + const U_POINT16 *points + ){ + return(U_EMR_CORE6(U_EMR_POLYBEZIER16, rclBounds, cpts, points)); +} + +// U_EMRPOLYGON16_set 86 +/** + \brief Allocate and construct a U_EMR_POLYGON16 record. + \return pointer to U_EMR_POLYGON16 record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param cpts Number of POINT16 in array + \param points Array of POINT16 +*/ +char *U_EMRPOLYGON16_set( + const U_RECTL rclBounds, + const uint32_t cpts, + const U_POINT16 *points + ){ + return(U_EMR_CORE6(U_EMR_POLYGON16, rclBounds, cpts, points)); +} + +// U_EMRPOLYLINE16_set 87 +/** + \brief Allocate and construct a U_EMR_POLYLINE16 record. + \return pointer to U_EMR_POLYLINE16 record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param cpts Number of POINT16 in array + \param points Array of POINT16 +*/ +char *U_EMRPOLYLINE16_set( + const U_RECTL rclBounds, + const uint32_t cpts, + const U_POINT16 *points + ){ + return(U_EMR_CORE6(U_EMR_POLYLINE16, rclBounds, cpts, points)); +} + +// U_EMRPOLYBEZIERTO16_set 88 +/** + \brief Allocate and construct a U_EMR_POLYBEZIERTO record. + \return pointer to U_EMR_POLYBEZIERTO record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param cpts Number of POINT16 in array + \param points Array of POINT16 +*/ +char *U_EMRPOLYBEZIERTO16_set( + const U_RECTL rclBounds, + const uint32_t cpts, + const U_POINT16 *points + ){ + return(U_EMR_CORE6(U_EMR_POLYBEZIERTO16, rclBounds, cpts, points)); +} + +// U_EMRPOLYLINETO16_set 89 +/** + \brief Allocate and construct a U_EMR_POLYLINETO record. + \return pointer to U_EMR_POLYLINETO record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param cpts Number of POINT16 in array + \param points Array of POINT16 +*/ +char *U_EMRPOLYLINETO16_set( + const U_RECTL rclBounds, + const uint32_t cpts, + const U_POINT16 *points + ){ + return(U_EMR_CORE6(U_EMR_POLYLINETO16, rclBounds, cpts, points)); +} + +// U_EMRPOLYPOLYLINE16_set 90 +/** + \brief Allocate and construct a U_EMR_POLYPOLYLINE16 record. + \return pointer to U_EMR_POLYPOLYLINE16 record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param nPolys Number of elements in aPolyCounts + \param aPolyCounts Number of points in each poly (sequential) + \param cpts Number of POINT16 in array + \param points Array of POINT16 +*/ +char *U_EMRPOLYPOLYLINE16_set( + const U_RECTL rclBounds, + const uint32_t nPolys, + const uint32_t *aPolyCounts, + const uint32_t cpts, + const U_POINT16 *points + ){ + return(U_EMR_CORE10(U_EMR_POLYPOLYLINE16, rclBounds, nPolys, aPolyCounts,cpts, points)); +} + +// U_EMRPOLYPOLYGON16_set 91 +/** + \brief Allocate and construct a U_EMR_POLYPOLYGON16 record. + \return pointer to U_EMR_POLYPOLYGON16 record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param nPolys Number of elements in aPolyCounts + \param aPolyCounts Number of points in each poly (sequential) + \param cpts Number of POINT16 in array + \param points Array of POINT16 +*/ +char *U_EMRPOLYPOLYGON16_set( + const U_RECTL rclBounds, + const uint32_t nPolys, + const uint32_t *aPolyCounts, + const uint32_t cpts, + const U_POINT16 *points + ){ + return(U_EMR_CORE10(U_EMR_POLYPOLYGON16, rclBounds, nPolys, aPolyCounts,cpts, points)); +} + + +// U_EMRPOLYDRAW16_set 92 +/** + \brief Allocate and construct a U_EMR_POLYDRAW16 record. + \return pointer to U_EMR_POLYDRAW16 record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param cpts Number of U_POINTL objects + \param aptl Array of U_POINTL objects + \param abTypes Array of Point Enumeration +*/ +char *U_EMRPOLYDRAW16_set( + const U_RECTL rclBounds, + const U_NUM_POINT16 cpts, + const U_POINT16 *aptl, + const uint8_t *abTypes + ){ + char *record; + int irecsize; + int cbPoints, cbAbTypes, cbAbTypes4, off; + + if(!cpts || !aptl || !abTypes)return(NULL); + cbPoints = cpts * sizeof(U_POINT16); // space for aptl + cbAbTypes = cpts; // number of abTypes (also size, 1 byte each) + cbAbTypes4 = UP4(cbAbTypes); // space for abTypes + irecsize = sizeof(U_EMRPOLYDRAW16) + cbPoints + cbAbTypes4 - sizeof(U_POINT16) - 1; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_POLYDRAW16; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRPOLYDRAW16) record)->rclBounds = rclBounds; + ((PU_EMRPOLYDRAW16) record)->cpts = cpts; + off = sizeof(U_EMR) + sizeof(U_RECTL) + sizeof(uint32_t); // offset to first variable part + memcpy(record+off,aptl,cbPoints); + off += cbPoints; + memcpy(record+off,abTypes,cbAbTypes); + off += cbAbTypes; + if(cbAbTypes4 > cbAbTypes){ memset(record+off,0,cbAbTypes4-cbAbTypes); } // keeps valgrind happy (initialize padding after byte array) + } + return(record); +} + +// U_EMRCREATEMONOBRUSH_set 93 +/** + \brief Allocate and construct a U_EMR_CREATEMONOBRUSH record. + \return pointer to U_EMR_CREATEMONOBRUSH record, or NULL on error. + \param ihBrush Index to place object in EMF object table (this entry must not yet exist) + \param iUsage DIBcolors Enumeration + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO + pixel array) + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_EMRCREATEMONOBRUSH_set( + const uint32_t ihBrush, + const uint32_t iUsage, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + const char *Px + ){ + return(U_EMR_CORE12_set(U_EMR_CREATEMONOBRUSH,ihBrush,iUsage,Bmi,cbPx,Px)); +} + +// U_EMRCREATEDIBPATTERNBRUSHPT_set 94 +/** + \brief Allocate and construct a U_EMR_CREATEDIBPATTERNBRUSHPT record. + Use createdibpatternbrushpt_set() instead of calling this function directly. + \return pointer to U_EMR_CREATEDIBPATTERNBRUSHPT record, or NULL on error. + \param ihBrush Index to place object in EMF object table (this entry must not yet exist) + \param iUsage DIBcolors Enumeration + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO + pixel array) + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_EMRCREATEDIBPATTERNBRUSHPT_set( + const uint32_t ihBrush, + const uint32_t iUsage, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + const char *Px + ){ + return(U_EMR_CORE12_set(U_EMR_CREATEDIBPATTERNBRUSHPT,ihBrush,iUsage,Bmi,cbPx,Px)); +} + + +// U_EMREXTCREATEPEN_set 95 +/** + \brief Allocate and construct a U_EMR_EXTCREATEPEN record. + Use extcreatepen_set() instead of calling this function directly. + \return pointer to U_EMR_EXTCREATEPEN record, or NULL on error. + \param ihPen ihPen Index to place object in EMF object table (this entry must not yet exist) + \param Bmi Bmi bitmapbuffer + \param cbPx cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px Px pixel array (NULL if cbPx == 0) + \param elp elp Pen parameters (Size is Variable!!!!) +*/ +char *U_EMREXTCREATEPEN_set( + const uint32_t ihPen, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px, + const PU_EXTLOGPEN elp + ){ + char *record; + int cbImage,cbImage4,cbBmi,off; + int irecsize,cbStyleArray,cbElp; + + if(!elp)return(NULL); + + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + + cbStyleArray = elp->elpNumEntries * sizeof(U_STYLEENTRY); // space actually used by penstyle entries + // EXTLOGPEN is already included in EMREXTCREATEPEN, including the possibly unused first penstyle entry + if(cbStyleArray){ + cbElp = sizeof(U_EXTLOGPEN) + cbStyleArray - sizeof(U_STYLEENTRY); // space actually used by elp + irecsize = sizeof(U_EMREXTCREATEPEN) + cbBmi + cbImage4 + cbStyleArray - sizeof(U_STYLEENTRY); + } + else { + cbElp = sizeof(U_EXTLOGPEN); // first U_STYLEENTRY is present but unused + irecsize = sizeof(U_EMREXTCREATEPEN) + cbBmi + cbImage4; + } + record = malloc(irecsize); + + if(record){ + ((PU_EMR) record)->iType = U_EMR_EXTCREATEPEN; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMREXTCREATEPEN) record)->ihPen = ihPen; + memcpy(&(((PU_EMREXTCREATEPEN) record)->elp),elp,cbElp); + if(cbStyleArray){ + off = sizeof(U_EMREXTCREATEPEN) + cbStyleArray - sizeof(U_STYLEENTRY); + } + else { + off = sizeof(U_EMREXTCREATEPEN); + } + // Cannot use APPEND_PXBMISRC here because there is no "Src" in the field names + if(cbBmi){ + memcpy(record + off, Bmi, cbBmi); + ((PU_EMREXTCREATEPEN) record)->offBmi = off; + ((PU_EMREXTCREATEPEN) record)->cbBmi = cbBmi; + off += cbBmi; + memcpy(record + off, Px, cbImage); + ((PU_EMREXTCREATEPEN) record)->offBits = off; + ((PU_EMREXTCREATEPEN) record)->cbBits = cbImage; + off += cbImage; + if(cbImage4 - cbImage){ memset(record + off, 0, cbImage4 - cbImage); } + } + else { + ((PU_EMREXTCREATEPEN) record)->cbBmi = 0; + ((PU_EMREXTCREATEPEN) record)->offBmi = 0; + ((PU_EMREXTCREATEPEN) record)->cbBits = 0; + ((PU_EMREXTCREATEPEN) record)->offBits = 0; + } + } + return(record); +} + +// U_EMRPOLYTEXTOUTA_set 96 NOT IMPLEMENTED, denigrated after Windows NT +// U_EMRPOLYTEXTOUTW_set 97 NOT IMPLEMENTED, denigrated after Windows NT + +// U_EMRSETICMMODE_set 98 +/** + \brief Allocate and construct a U_EMR_SETICMMODE record. + \return pointer to U_EMR_SETICMMODE record, or NULL on error. + \param iMode ICMMode Enumeration +*/ +char *U_EMRSETICMMODE_set( + const uint32_t iMode + ){ + return(U_EMR_CORE3(U_EMR_SETICMMODE, iMode)); +} + +// U_EMRCREATECOLORSPACE_set 99 +/** + \brief Allocate and construct a U_EMR_CREATECOLORSPACE record. + Use createcolorspace_set() instead of calling this function directly. + \return pointer to U_EMR_CREATECOLORSPACE record, or NULL on error. + \param ihCS Index to place object in EMF object table (this entry must not yet exist) + \param lcs ColorSpace parameters +*/ +char *U_EMRCREATECOLORSPACE_set( + const uint32_t ihCS, + const U_LOGCOLORSPACEA lcs + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRCREATECOLORSPACE); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_CREATECOLORSPACE; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRCREATECOLORSPACE) record)->ihCS = ihCS; + ((PU_EMRCREATECOLORSPACE) record)->lcs = lcs; + } + return(record); +} + +// U_EMRSETCOLORSPACE_set 100 +/** + \brief Allocate and construct a U_EMR_SETCOLORSPACE record. + \return pointer to U_EMR_SETCOLORSPACE record, or NULL on error. + \param ihCS Index of object in EMF object table +*/ +char *U_EMRSETCOLORSPACE_set( + const uint32_t ihCS + ){ + return(U_EMR_CORE3(U_EMR_SETCOLORSPACE, ihCS)); +} + +// U_EMRDELETECOLORSPACE_set 101 +/** + \brief Allocate and construct a U_EMR_DELETECOLORSPACE record. + \return pointer to U_EMR_DELETECOLORSPACE record, or NULL on error. + \param ihCS Index of object in EMF object table +*/ +char *U_EMRDELETECOLORSPACE_set( + const uint32_t ihCS + ){ + return(U_EMR_CORE3(U_EMR_DELETECOLORSPACE, ihCS)); +} + +// U_EMRGLSRECORD_set 102 Not implemented +// U_EMRGLSBOUNDEDRECORD_set 103 Not implemented +// U_EMRPIXELFORMAT_set 104 +/** + \brief Allocate and construct a U_EMR_PIXELFORMAT record. + \return pointer to U_EMR_PIXELFORMAT record, or NULL on error. + \param pfd PixelFormatDescriptor +*/ +char *U_EMRPIXELFORMAT_set( + const U_PIXELFORMATDESCRIPTOR pfd + ){ + char *record; + int irecsize; + + irecsize = sizeof(U_EMRPIXELFORMAT); + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_PIXELFORMAT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRPIXELFORMAT) record)->pfd = pfd; + } + return(record); +} +// U_EMRDRAWESCAPE_set 105 Not implemented +// U_EMREXTESCAPE_set 106 Not implemented +// U_EMRUNDEF107_set 107 Not implemented + +// U_EMRSMALLTEXTOUT_set 108 +/** + \brief Allocate and construct a U_EMR_SMALLTEXTOUT record. + \return pointer to U_EMR_SMALLTEXTOUT record, or NULL on error. + \param Dest Where to draw the text + \param cChars Characters in TextString (not null terminated) + \param fuOptions ExtTextOutOptions Enumeration + \param iGraphicsMode GraphicsMode Enumeration + \param exScale scale on X axis + \param eyScale scale on Y axis + \param rclBounds OPTIONAL Bounding rectangle (absent when: fuOPtions & ETO_NO_U_RECT) + \param TextString text to output (fuOptions & ETO_SMALL_CHARS ? 8 bit : 16 bit) +*/ +char *U_EMRSMALLTEXTOUT_set( + const U_POINTL Dest, + const U_NUM_STR cChars, + const uint32_t fuOptions, + const uint32_t iGraphicsMode, + const U_FLOAT exScale, + const U_FLOAT eyScale, + const U_RECTL rclBounds, + const char *TextString + ){ + char *record; + int irecsize,cbString,cbString4,cbRectl,off; + int csize; + + if( fuOptions & U_ETO_SMALL_CHARS ){ csize = 1; } // how many bytes per character + else { csize = 2; } + cbString = csize * cChars; // filled contents of the string buffer + cbString4 = UP4(cbString); // size of the variable string buffer + if(fuOptions & U_ETO_NO_RECT){ cbRectl = 0; } // size of the optional U_RECTL field + else { cbRectl = sizeof(U_RECTL); } + + irecsize = sizeof(U_EMRSMALLTEXTOUT) + cbString4 + cbRectl; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_SMALLTEXTOUT; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRSMALLTEXTOUT) record)->Dest = Dest; + ((PU_EMRSMALLTEXTOUT) record)->cChars = cChars; + ((PU_EMRSMALLTEXTOUT) record)->fuOptions = fuOptions; + ((PU_EMRSMALLTEXTOUT) record)->iGraphicsMode = iGraphicsMode; + ((PU_EMRSMALLTEXTOUT) record)->exScale = exScale; + ((PU_EMRSMALLTEXTOUT) record)->eyScale = eyScale; + off = sizeof(U_EMRSMALLTEXTOUT); //offset to the start of the variable fields + if(cbRectl){ + memcpy(record + off, &rclBounds, cbRectl); + off += cbRectl; + } + memcpy(record + off, TextString, cbString); + if(cbString < cbString4){ + off += cbString; + memset(record + off, 0, cbString4 - cbString); + } + } + return(record); +} + +// U_EMRFORCEUFIMAPPING_set 109 Not implemented +// U_EMRNAMEDESCAPE_set 110 Not implemented +// U_EMRCOLORCORRECTPALETTE_set 111 Not implemented +// U_EMRSETICMPROFILEA_set 112 Not implemented +// U_EMRSETICMPROFILEW_set 113 Not implemented + +// U_EMRALPHABLEND_set 114 +/** + \brief Allocate and construct a U_EMR_ALPHABLEND record. + \return pointer to U_EMR_ALPHABLEND record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param Dest Destination UL corner in logical units + \param cDest Destination width in logical units + \param Src Source UL corner in logical units + \param cSrc Src W & H in logical units + \param xformSrc Transform to apply to source + \param crBkColorSrc Background color + \param iUsageSrc DIBcolors Enumeration + \param Blend Blend function + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_EMRALPHABLEND_set( + const U_RECTL rclBounds, + const U_POINTL Dest, + const U_POINTL cDest, + const U_POINTL Src, + const U_POINTL cSrc, + const U_XFORM xformSrc, + const U_COLORREF crBkColorSrc, + const uint32_t iUsageSrc, + const U_BLEND Blend, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px + ){ + return(U_EMR_CORE13_set(U_EMR_ALPHABLEND,rclBounds,Dest,cDest,Src,cSrc,xformSrc,crBkColorSrc,iUsageSrc,*((uint32_t *) &Blend),Bmi,cbPx,Px)); +} + +// U_EMRSETLAYOUT_set 115 +/** + \brief Allocate and construct a U_EMR_SETLAYOUT record. + \return pointer to U_EMR_SETLAYOUT record, or NULL on error. + \param iMode Mirroring Enumeration +*/ +char *U_EMRSETLAYOUT_set(uint32_t iMode){ + return(U_EMR_CORE3(U_EMR_SETLAYOUT, iMode)); +} + +// U_EMRTRANSPARENTBLT_set 116 +/** + \brief Allocate and construct a U_EMR_TRANSPARENTBLT record. + \return pointer to U_EMR_TRANSPARENTBLT record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param Dest Destination UL corner in logical units + \param cDest Destination width in logical units + \param Src Source UL corner in logical units + \param cSrc Src W & H in logical units + \param xformSrc Transform to apply to source + \param crBkColorSrc Background color + \param iUsageSrc DIBcolors Enumeration + \param TColor Bitmap color to be treated as transparent + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_EMRTRANSPARENTBLT_set( + const U_RECTL rclBounds, + const U_POINTL Dest, + const U_POINTL cDest, + const U_POINTL Src, + const U_POINTL cSrc, + const U_XFORM xformSrc, + const U_COLORREF crBkColorSrc, + const uint32_t iUsageSrc, + const uint32_t TColor, + const PU_BITMAPINFO Bmi, + const uint32_t cbPx, + char *Px + ){ + return(U_EMR_CORE13_set(U_EMR_TRANSPARENTBLT,rclBounds,Dest,cDest,Src,cSrc,xformSrc,crBkColorSrc,iUsageSrc,TColor,Bmi,cbPx,Px)); +} +// U_EMRUNDEF117_set 117 Not implemented +// U_EMRGRADIENTFILL_set 118 +/** + \brief Allocate and construct a U_EMR_TRANSPARENTBLT record. + \return pointer to U_EMR_TRANSPARENTBLT record, or NULL on error. + \param rclBounds Bounding rectangle in device units + \param nTriVert Number of TriVertex objects in TriVert + \param nGradObj Number of gradient triangle/rectangle objects + \param ulMode Gradientfill Enumeration (determines Triangle/Rectangle) + \param TriVert Array of TriVertex objects + \param GradObj Array of gradient objects (each has 2 [rect] or 3 [triangle] indices into TriVert array) +*/ +char *U_EMRGRADIENTFILL_set( + const U_RECTL rclBounds, + const U_NUM_TRIVERTEX nTriVert, + const U_NUM_GRADOBJ nGradObj, + const uint32_t ulMode, + const PU_TRIVERTEX TriVert, + const uint32_t *GradObj + ){ + char *record; + unsigned int cbTriVert,cbGradObj,off; + int irecsize; + + cbTriVert = sizeof(U_TRIVERTEX) * nTriVert; // all of the cb's will be a multiple of 4 bytes + if( ulMode == U_GRADIENT_FILL_TRIANGLE){ cbGradObj = sizeof(U_GRADIENT3) * nGradObj; } + else if(ulMode == U_GRADIENT_FILL_RECT_H || + ulMode == U_GRADIENT_FILL_RECT_V){ cbGradObj = sizeof(U_GRADIENT4) * nGradObj; } + else { return(NULL); } + + irecsize = sizeof(U_EMRGRADIENTFILL) + cbTriVert + cbGradObj; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_GRADIENTFILL; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRGRADIENTFILL) record)->rclBounds = rclBounds; + ((PU_EMRGRADIENTFILL) record)->nTriVert = nTriVert; + ((PU_EMRGRADIENTFILL) record)->nGradObj = nGradObj; + ((PU_EMRGRADIENTFILL) record)->ulMode = ulMode; + off = sizeof(U_EMRGRADIENTFILL); // offset to TriVert field + memcpy(record + off, TriVert, cbTriVert); + off += cbTriVert; + memcpy(record + off, GradObj, cbGradObj); + } + return(record); +} + +// U_EMRSETLINKEDUFIS_set 119 Not implemented +// U_EMRSETTEXTJUSTIFICATION_set 120 Not implemented (denigrated) +// U_EMRCOLORMATCHTOTARGETW_set 121 Not implemented + +// U_EMRCREATECOLORSPACEW_set 122 +/** + \brief Allocate and construct a U_EMR_CREATECOLORSPACEW record. + Use createcolorspacew_set() instead of calling this function directly. + \return pointer to U_EMR_CREATECOLORSPACEW record, or NULL on error. + \param ihCS Index to place object in EMF object table (this entry must not yet exist) + \param lcs ColorSpace parameters + \param dwFlags If low bit set Data is present + \param cbData Number of bytes of theData field. + \param Data (Optional, dwFlags & 1) color profile data +*/ +char *U_EMRCREATECOLORSPACEW_set( + const uint32_t ihCS, + const U_LOGCOLORSPACEW lcs, + const uint32_t dwFlags, + const U_CBDATA cbData, + const uint8_t *Data + ){ + char *record; + unsigned int cbData4,off; + int irecsize; + + cbData4 = UP4(cbData); // buffer to hold Data + irecsize = sizeof(U_EMRCREATECOLORSPACEW) + cbData4; + record = malloc(irecsize); + if(record){ + ((PU_EMR) record)->iType = U_EMR_CREATECOLORSPACEW; + ((PU_EMR) record)->nSize = irecsize; + ((PU_EMRCREATECOLORSPACEW) record)->ihCS = ihCS; + ((PU_EMRCREATECOLORSPACEW) record)->lcs = lcs; + ((PU_EMRCREATECOLORSPACEW) record)->dwFlags = dwFlags; + ((PU_EMRCREATECOLORSPACEW) record)->cbData = cbData; + off = sizeof(U_EMR) + sizeof(uint32_t) + sizeof(U_LOGCOLORSPACEW) + sizeof(uint32_t) + sizeof(U_CBDATA); // offset to Data field + memcpy(record + off, Data, cbData); + if(cbData < cbData4){ + off += cbData; + memset(record + off,0,cbData4-cbData); + } + } + return(record); +} + + + +#ifdef __cplusplus +} +#endif diff --git a/src/libuemf/uemf.h b/src/libuemf/uemf.h new file mode 100644 index 000000000..f1211d63d --- /dev/null +++ b/src/libuemf/uemf.h @@ -0,0 +1,2892 @@ +/** + @file uemf.h Structures and functions prototypes for EMF files. + + EMF file Record structure information has been derived from Mingw, Wine, and libEMF header files, and from + Microsoft's EMF Information pdf, release date March 28,2012, link from here: + + http://msdn2.microsoft.com/en-us/library/cc230514.aspx + + If the direct link fails the document may be found + by searching for: "[MS-EMF]: Enhanced Metafile Format" + +*/ + +/* +File: uemf.h +Version: 0.0.19 +Date: 20-FEB-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifndef _UEMF_ +#define _UEMF_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdbool.h> +#include "uemf_utf.h" +#include "uemf_endian.h" + + + +// *********************************************************************************** +// defines not placed yet + +#define U_PAN_CULTURE_LATIN 0 + +#define U_SYSPAL_ERROR 0 +#define U_SYSPAL_STATIC 1 +#define U_SYSPAL_NOSTATIC 2 + +#define U_ELF_VENDOR_SIZE 4 + + +// *********************************************************************************** +// Value enumerations and other predefined constants, alphabetical order by group + + +/** \defgroup Font_struct_widths Font name and style widths in characters + For U_LOGFONT and U_LOGFONT_PANOSE, + @{ +*/ +#define U_LF_FACESIZE 32 //!< U_LOGFONT lfFaceName and U_LOGFONT_PANOSE elfStyle fields maximum width +#define U_LF_FULLFACESIZE 64 //!< U_LOGFONT_PANOSE elfFullName field maximum width +/** @} */ + +/** \defgroup U_EMR_Qualifiers RecordType Enumeration + (RecordType Enumeration) + For U_EMR iType field + @{ +*/ +#define U_EMR_HEADER 1 //!< U_EMRHEADER record +#define U_EMR_POLYBEZIER 2 //!< U_EMRPOLYBEZIER record +#define U_EMR_POLYGON 3 //!< U_EMRPOLYGON record +#define U_EMR_POLYLINE 4 //!< U_EMRPOLYLINE record +#define U_EMR_POLYBEZIERTO 5 //!< U_EMRPOLYBEZIERTO record +#define U_EMR_POLYLINETO 6 //!< U_EMRPOLYLINETO record +#define U_EMR_POLYPOLYLINE 7 //!< U_EMRPOLYPOLYLINE record +#define U_EMR_POLYPOLYGON 8 //!< U_EMRPOLYPOLYGON record +#define U_EMR_SETWINDOWEXTEX 9 //!< U_EMRSETWINDOWEXTEX record +#define U_EMR_SETWINDOWORGEX 10 //!< U_EMRSETWINDOWORGEX record +#define U_EMR_SETVIEWPORTEXTEX 11 //!< U_EMRSETVIEWPORTEXTEX record +#define U_EMR_SETVIEWPORTORGEX 12 //!< U_EMRSETVIEWPORTORGEX record +#define U_EMR_SETBRUSHORGEX 13 //!< U_EMRSETBRUSHORGEX record +#define U_EMR_EOF 14 //!< U_EMREOF record +#define U_EMR_SETPIXELV 15 //!< U_EMRSETPIXELV record +#define U_EMR_SETMAPPERFLAGS 16 //!< U_EMRSETMAPPERFLAGS record +#define U_EMR_SETMAPMODE 17 //!< U_EMRSETMAPMODE record +#define U_EMR_SETBKMODE 18 //!< U_EMRSETBKMODE record +#define U_EMR_SETPOLYFILLMODE 19 //!< U_EMRSETPOLYFILLMODE record +#define U_EMR_SETROP2 20 //!< U_EMRSETROP2 record +#define U_EMR_SETSTRETCHBLTMODE 21 //!< U_EMRSETSTRETCHBLTMODE record +#define U_EMR_SETTEXTALIGN 22 //!< U_EMRSETTEXTALIGN record +#define U_EMR_SETCOLORADJUSTMENT 23 //!< U_EMRSETCOLORADJUSTMENT record +#define U_EMR_SETTEXTCOLOR 24 //!< U_EMRSETTEXTCOLOR record +#define U_EMR_SETBKCOLOR 25 //!< U_EMRSETBKCOLOR record +#define U_EMR_OFFSETCLIPRGN 26 //!< U_EMROFFSETCLIPRGN record +#define U_EMR_MOVETOEX 27 //!< U_EMRMOVETOEX record +#define U_EMR_SETMETARGN 28 //!< U_EMRSETMETARGN record +#define U_EMR_EXCLUDECLIPRECT 29 //!< U_EMREXCLUDECLIPRECT record +#define U_EMR_INTERSECTCLIPRECT 30 //!< U_EMRINTERSECTCLIPRECT record +#define U_EMR_SCALEVIEWPORTEXTEX 31 //!< U_EMRSCALEVIEWPORTEXTEX record +#define U_EMR_SCALEWINDOWEXTEX 32 //!< U_EMRSCALEWINDOWEXTEX record +#define U_EMR_SAVEDC 33 //!< U_EMRSAVEDC record +#define U_EMR_RESTOREDC 34 //!< U_EMRRESTOREDC record +#define U_EMR_SETWORLDTRANSFORM 35 //!< U_EMRSETWORLDTRANSFORM record +#define U_EMR_MODIFYWORLDTRANSFORM 36 //!< U_EMRMODIFYWORLDTRANSFORM record +#define U_EMR_SELECTOBJECT 37 //!< U_EMRSELECTOBJECT record +#define U_EMR_CREATEPEN 38 //!< U_EMRCREATEPEN record +#define U_EMR_CREATEBRUSHINDIRECT 39 //!< U_EMRCREATEBRUSHINDIRECT record +#define U_EMR_DELETEOBJECT 40 //!< U_EMRDELETEOBJECT record +#define U_EMR_ANGLEARC 41 //!< U_EMRANGLEARC record +#define U_EMR_ELLIPSE 42 //!< U_EMRELLIPSE record +#define U_EMR_RECTANGLE 43 //!< U_EMRRECTANGLE record +#define U_EMR_ROUNDRECT 44 //!< U_EMRROUNDRECT record +#define U_EMR_ARC 45 //!< U_EMRARC record +#define U_EMR_CHORD 46 //!< U_EMRCHORD record +#define U_EMR_PIE 47 //!< U_EMRPIE record +#define U_EMR_SELECTPALETTE 48 //!< U_EMRSELECTPALETTE record +#define U_EMR_CREATEPALETTE 49 //!< U_EMRCREATEPALETTE record +#define U_EMR_SETPALETTEENTRIES 50 //!< U_EMRSETPALETTEENTRIES record +#define U_EMR_RESIZEPALETTE 51 //!< U_EMRRESIZEPALETTE record +#define U_EMR_REALIZEPALETTE 52 //!< U_EMRREALIZEPALETTE record +#define U_EMR_EXTFLOODFILL 53 //!< U_EMREXTFLOODFILL record +#define U_EMR_LINETO 54 //!< U_EMRLINETO record +#define U_EMR_ARCTO 55 //!< U_EMRARCTO record +#define U_EMR_POLYDRAW 56 //!< U_EMRPOLYDRAW record +#define U_EMR_SETARCDIRECTION 57 //!< U_EMRSETARCDIRECTION record +#define U_EMR_SETMITERLIMIT 58 //!< U_EMRSETMITERLIMIT record +#define U_EMR_BEGINPATH 59 //!< U_EMRBEGINPATH record +#define U_EMR_ENDPATH 60 //!< U_EMRENDPATH record +#define U_EMR_CLOSEFIGURE 61 //!< U_EMRCLOSEFIGURE record +#define U_EMR_FILLPATH 62 //!< U_EMRFILLPATH record +#define U_EMR_STROKEANDFILLPATH 63 //!< U_EMRSTROKEANDFILLPATH record +#define U_EMR_STROKEPATH 64 //!< U_EMRSTROKEPATH record +#define U_EMR_FLATTENPATH 65 //!< U_EMRFLATTENPATH record +#define U_EMR_WIDENPATH 66 //!< U_EMRWIDENPATH record +#define U_EMR_SELECTCLIPPATH 67 //!< U_EMRSELECTCLIPPATH record +#define U_EMR_ABORTPATH 68 //!< U_EMRABORTPATH record +#define U_EMR_UNDEF69 69 //!< U_EMRUNDEF69 record +#define U_EMR_COMMENT 70 //!< U_EMRCOMMENT record +#define U_EMR_FILLRGN 71 //!< U_EMRFILLRGN record +#define U_EMR_FRAMERGN 72 //!< U_EMRFRAMERGN record +#define U_EMR_INVERTRGN 73 //!< U_EMRINVERTRGN record +#define U_EMR_PAINTRGN 74 //!< U_EMRPAINTRGN record +#define U_EMR_EXTSELECTCLIPRGN 75 //!< U_EMREXTSELECTCLIPRGN record +#define U_EMR_BITBLT 76 //!< U_EMRBITBLT record +#define U_EMR_STRETCHBLT 77 //!< U_EMRSTRETCHBLT record +#define U_EMR_MASKBLT 78 //!< U_EMRMASKBLT record +#define U_EMR_PLGBLT 79 //!< U_EMRPLGBLT record +#define U_EMR_SETDIBITSTODEVICE 80 //!< U_EMRSETDIBITSTODEVICE record +#define U_EMR_STRETCHDIBITS 81 //!< U_EMRSTRETCHDIBITS record +#define U_EMR_EXTCREATEFONTINDIRECTW 82 //!< U_EMREXTCREATEFONTINDIRECTW record +#define U_EMR_EXTTEXTOUTA 83 //!< U_EMREXTTEXTOUTA record +#define U_EMR_EXTTEXTOUTW 84 //!< U_EMREXTTEXTOUTW record +#define U_EMR_POLYBEZIER16 85 //!< U_EMRPOLYBEZIER16 record +#define U_EMR_POLYGON16 86 //!< U_EMRPOLYGON16 record +#define U_EMR_POLYLINE16 87 //!< U_EMRPOLYLINE16 record +#define U_EMR_POLYBEZIERTO16 88 //!< U_EMRPOLYBEZIERTO16 record +#define U_EMR_POLYLINETO16 89 //!< U_EMRPOLYLINETO16 record +#define U_EMR_POLYPOLYLINE16 90 //!< U_EMRPOLYPOLYLINE16 record +#define U_EMR_POLYPOLYGON16 91 //!< U_EMRPOLYPOLYGON16 record +#define U_EMR_POLYDRAW16 92 //!< U_EMRPOLYDRAW16 record +#define U_EMR_CREATEMONOBRUSH 93 //!< U_EMRCREATEMONOBRUSH record +#define U_EMR_CREATEDIBPATTERNBRUSHPT 94 //!< U_EMRCREATEDIBPATTERNBRUSHPT record +#define U_EMR_EXTCREATEPEN 95 //!< U_EMREXTCREATEPEN record +#define U_EMR_POLYTEXTOUTA 96 //!< U_EMRPOLYTEXTOUTA record +#define U_EMR_POLYTEXTOUTW 97 //!< U_EMRPOLYTEXTOUTW record +#define U_EMR_SETICMMODE 98 //!< U_EMRSETICMMODE record +#define U_EMR_CREATECOLORSPACE 99 //!< U_EMRCREATECOLORSPACE record +#define U_EMR_SETCOLORSPACE 100 //!< U_EMRSETCOLORSPACE record +#define U_EMR_DELETECOLORSPACE 101 //!< U_EMRDELETECOLORSPACE record +#define U_EMR_GLSRECORD 102 //!< U_EMRGLSRECORD record +#define U_EMR_GLSBOUNDEDRECORD 103 //!< U_EMRGLSBOUNDEDRECORD record +#define U_EMR_PIXELFORMAT 104 //!< U_EMRPIXELFORMAT record +#define U_EMR_DRAWESCAPE 105 //!< U_EMRDRAWESCAPE record +#define U_EMR_EXTESCAPE 106 //!< U_EMREXTESCAPE record +#define U_EMR_UNDEF107 107 //!< U_EMRUNDEF107 record +#define U_EMR_SMALLTEXTOUT 108 //!< U_EMRSMALLTEXTOUT record +#define U_EMR_FORCEUFIMAPPING 109 //!< U_EMRFORCEUFIMAPPING record +#define U_EMR_NAMEDESCAPE 110 //!< U_EMRNAMEDESCAPE record +#define U_EMR_COLORCORRECTPALETTE 111 //!< U_EMRCOLORCORRECTPALETTE record +#define U_EMR_SETICMPROFILEA 112 //!< U_EMRSETICMPROFILEA record +#define U_EMR_SETICMPROFILEW 113 //!< U_EMRSETICMPROFILEW record +#define U_EMR_ALPHABLEND 114 //!< U_EMRALPHABLEND record +#define U_EMR_SETLAYOUT 115 //!< U_EMRSETLAYOUT record +#define U_EMR_TRANSPARENTBLT 116 //!< U_EMRTRANSPARENTBLT record +#define U_EMR_UNDEF117 117 //!< U_EMRUNDEF117 record +#define U_EMR_GRADIENTFILL 118 //!< U_EMRGRADIENTFILL record +#define U_EMR_SETLINKEDUFIS 119 //!< U_EMRSETLINKEDUFIS record +#define U_EMR_SETTEXTJUSTIFICATION 120 //!< U_EMRSETTEXTJUSTIFICATION record +#define U_EMR_COLORMATCHTOTARGETW 121 //!< U_EMRCOLORMATCHTOTARGETW record +#define U_EMR_CREATECOLORSPACEW 122 //!< U_EMRCREATECOLORSPACEW record + +#define U_EMR_MIN 1 //!< Minimum U_EMR_ value. +#define U_EMR_MAX 122 //!< Maximum U_EMR_ value. Not much beyond 104 is implemented + +#define U_EMR_INVALID 0xFFFFFFFF //!< Not any valid U_EMF_ value +/** @} */ + +/** \defgroup U_DRAW_PROPERTIES draw properties + Used in emr_properties() and wmr_properties. These are the bit definitions. + @{ +*/ +#define U_DRAW_NOTEMPTY 0x001 //!< Path has at least a MOVETO in it +#define U_DRAW_VISIBLE 0x002 //!< Path has at least a LINE in it +#define U_DRAW_CLOSED 0x004 //!< Path has been closed +#define U_DRAW_ONLYTO 0x008 //!< Path so far contains only *TO operations +#define U_DRAW_FORCE 0x010 //!< Path MUST be drawn +#define U_DRAW_ALTERS 0x020 //!< Alters draw parameters (pen, brush, coordinates...) +#define U_DRAW_PATH 0x040 //!< An explicit path is being used (with a BEGIN and END) +#define U_DRAW_TEXT 0x080 //!< Current record forces all pending text to be drawn first. +#define U_DRAW_OBJECT 0x100 //!< Creates an Object (only used in WMF) +#define U_DRAW_NOFILL 0x200 //!< Object is not fillable (lines and arc, only used in WMF) + +/** @} */ +/** \defgroup U_EMRSETARCDIRECTION_Qualifiers ArcDirection Enumeration + For U_EMRSETARCDIRECTION iArcDirection field + @{ +*/ +#define U_AD_COUNTERCLOCKWISE 1 +#define U_AD_CLOCKWISE 2 +/** @} */ + +/** \defgroup U_PANOSE_bArmStyle_Qualifiers ArmStyle Enumeration + For U_PANOSE bArmStyle field + @{ +*/ +#define U_PAN_STRAIGHT_ARMS_HORZ 2 +#define U_PAN_STRAIGHT_ARMS_WEDGE 3 +#define U_PAN_STRAIGHT_ARMS_VERT 4 +#define U_PAN_STRAIGHT_ARMS_SINGLE_SERIF 5 +#define U_PAN_STRAIGHT_ARMS_DOUBLE_SERIF 6 +#define U_PAN_BENT_ARMS_HORZ 7 +#define U_PAN_BENT_ARMS_WEDGE 8 +#define U_PAN_BENT_ARMS_VERT 9 +#define U_PAN_BENT_ARMS_SINGLE_SERIF 10 +#define U_PAN_BENT_ARMS_DOUBLE_SERIF 11 +/** @} */ + +/** \defgroup U_EMRSETBKMODE_iMode_Qualifiers BackgroundMode enumeration + For U_EMRSETBKMODE iMode field + @{ +*/ +#define U_TRANSPARENT 1 +#define U_OPAQUE 2 +/** @} */ + +/** \defgroup U_BITMAPINFOHEADER_biBitCount_Qualifiers BitCount Enumeration + For U_BITMAPINFOHEADER biBitCount field. + @{ +*/ +#define U_BCBM_EXPLICIT 0 //!< Derived from JPG or PNG compressed image or ? +#define U_BCBM_MONOCHROME 1 //!< 2 colors. bmiColors array has two entries +#define U_BCBM_COLOR4 4 //!< 2^4 colors. bmiColors array has 16 entries +#define U_BCBM_COLOR8 8 //!< 2^8 colors. bmiColors array has 256 entries +#define U_BCBM_COLOR16 16 //!< 2^16 colors. bmiColors is not used. Pixels are 5 bits B,G,R with 1 unused bit +#define U_BCBM_COLOR24 24 //!< 2^24 colors. bmiColors is not used. Pixels are U_RGBTRIPLE. +#define U_BCBM_COLOR32 32 //!< 2^32 colors. bmiColors is not used. Pixels are U_RGBQUAD. +/** @} */ + +/** \defgroup U_BITMAPINFOHEADER_biCompression_Qualifiers BI_Compression Enumeration + For U_BITMAPINFOHEADER biCompression field + @{ +*/ +#define U_BI_RGB 0 //!< This is the only one supported by UEMF at present +#define U_BI_RLE8 1 +#define U_BI_RLE4 2 +#define U_BI_BITFIELDS 3 +#define U_BI_JPEG 4 +#define U_BI_PNG 5 +/** @} */ + +/** \defgroup U_COLORADJUSTMENT_caFlags_Qualifiers ColorAdjustment Enumeration + For U_COLORADJUSTMENT caFlags field + @{ +*/ +#define U_CA_NEGATIVE 0x0001 +#define U_CA_LOG_FILTER 0x0002 +/** @} */ + +/** \defgroup U_EMRCOLORMATCHTOTARGETW_dwFlags_Qualifiers ColorMatchToTarget Enumeration + For U_EMRCOLORMATCHTOTARGETW dwFlags field + @{ +*/ +#define U_COLORMATCHTOTARGET_NOTEMBEDDED 0 +#define U_COLORMATCHTOTARGET_EMBEDDED 1 +/** @} */ + +/** \defgroup U_EMRCOLORMATCHTOTARGETW_dwAction_Qualifiers ColorSpace Enumeration + For U_EMRCOLORMATCHTOTARGETW dwAction field + @{ +*/ +#define U_CS_ENABLE 1 +#define U_CS_DISABLE 2 +#define U_CS_DELETE_TRANSFORM 3 +/** @} */ + +/** \defgroup U_PANOSE_bContrast_Qualifiers Contrast Enumeration + For U_PANOSE bContrast field + @{ +*/ +#define U_PAN_CONTRAST_NONE 2 +#define U_PAN_CONTRAST_VERY_LOW 3 +#define U_PAN_CONTRAST_LOW 4 +#define U_PAN_CONTRAST_MEDIUM_LOW 5 +#define U_PAN_CONTRAST_MEDIUM 6 +#define U_PAN_CONTRAST_MEDIUM_HIGH 7 +#define U_PAN_CONTRAST_HIGH 8 +#define U_PAN_CONTRAST_VERY_HIGH 9 +/** @} */ + +/** \defgroup U_DIBITS_iUsageSrc_Qualifiers DIBColors Enumeration + For U_EMRSETDIBITSTODEIVCE and U_EMRSTRETCHDIBITS iUsageSrc fields. + @{ +*/ +#define U_DIB_RGB_COLORS 0 +#define U_DIB_PAL_COLORS 1 +/** @} */ + +/** \defgroup U_EMRCOMMENT_TYPES Comment record types + For U_EMRCOMMENT_* cIdent fields + @{ +*/ +#define U_EMR_COMMENT_PUBLIC 0x43494447 +#define U_EMR_COMMENT_SPOOL 0x00000000 +#define U_EMR_COMMENT_SPOOLFONTDEF 0x544F4E46 +#define U_EMR_COMMENT_EMFPLUSRECORD 0x2B464D45 +/** @} */ + +/** \defgroup U_EMR_COMMENT_PUBLIC EMRComment Enumeration + For U_EMRCOMMENT_PUBLIC pcIdent fields + @{ +*/ +#define U_EMR_COMMENT_WINDOWS_METAFILE 0x80000001 +#define U_EMR_COMMENT_BEGINGROUP 0x00000002 +#define U_EMR_COMMENT_ENDGROUP 0x00000003 +#define U_EMR_COMMENT_MULTIFORMATS 0x40000004 +#define U_EMR_COMMENT_UNICODE_STRING 0x00000040 +#define U_EMR_COMMENT_UNICODE_END 0x00000080 +/** @} */ + +/** \defgroup U_EMRTEXT_foptions_Qualifiers ExtTextOutOptions Enumeration + For U_EMRTEXT foptions field + @{ +*/ +#define U_ETO_NONE 0x00000000 +#define U_ETO_GRAYED 0x00000001 +#define U_ETO_OPAQUE 0x00000002 +#define U_ETO_CLIPPED 0x00000004 +#define U_ETO_GLYPH_INDEX 0x00000010 +#define U_ETO_RTLREADING 0x00000080 +#define U_ETO_NO_RECT 0x00000100 +#define U_ETO_SMALL_CHARS 0x00000200 // For EMRSMALLTEXTOUT ONLY, does not affect EMRTEXTOUTA or EMRTEXTOUTW +#define U_ETO_NUMERICSLOCAL 0x00000400 +#define U_ETO_NUMERICSLATIN 0x00000800 +#define U_ETO_IGNORELANGUAGE 0x00001000 +#define U_ETO_PDY 0x00002000 +#define U_ETO_REVERSE_INDEX_MAP 0x00010000 +/** @} */ + +/** \defgroup U_PANOSE_bFamilyType_Qualifiers FamilyType Enumeration + For U_PANOSE bFamilyType field + @{ +*/ +#define U_PAN_FAMILY_TEXT_DISPLAY 2 +#define U_PAN_FAMILY_SCRIPT 3 +#define U_PAN_FAMILY_DECORATIVE 4 +#define U_PAN_FAMILY_PICTORIAL 5 +/** @} */ + +/** \defgroup U_EMREXTFLOODFILL_iMode_Qualifiers FloodFill Enumeration + For U_EMREXTFLOODFILL iMode field + @{ +*/ +#define U_FLOODFILLBORDER 0x00000000 /* Color specified must be the same as the border - brush fill stops at this color */ +#define U_FLOODFILLSURFACE 0x00000001 /* Color specified must be different from the border - brush fills only this color */ +/** @} */ + +/** \defgroup U_DESIGNVECTOR_Signature_Qualifiers Signature Enumeration + For U_DESIGNVECTOR Signature field + @{ +*/ +#define U_ENHMETA_SIGNATURE 0x464D4520 //!< also for U_EMRHEADER dSignature field +#define U_EPS_SIGNATURE 0x46535045 +/** @} */ + +/** \defgroup U_EMRGRADIENTFILL_ulMode_Qualifiers GradientFill Enumeration + For U_EMRGRADIENTFILL ulMode field + @{ +*/ +#define U_GRADIENT_FILL_RECT_H 0x00000000 +#define U_GRADIENT_FILL_RECT_V 0x00000001 +#define U_GRADIENT_FILL_TRIANGLE 0x00000002 +/** @} */ + +/** \defgroup U_EMREXTTEXTOUT_iGraphicsMode_Qualifiers GraphicsMode Enumeration + For U_EMREXTTEXTOUTA/U_EMREXTTEXTOUTW and all other iGraphicsMode fields + @{ +*/ +#define U_GM_COMPATIBLE 1 +#define U_GM_ADVANCED 2 +#define U_GM_LAST 2 +/** @} */ + +/** \defgroup U_LOGBRUSH_lbHatch_Qualifiers HatchStyle Enumeration + For U_LOGBRUSH lbHatch field + @{ +*/ +#define U_HS_HORIZONTAL 0 +#define U_HS_VERTICAL 1 +#define U_HS_FDIAGONAL 2 +#define U_HS_BDIAGONAL 3 +#define U_HS_CROSS 4 +#define U_HS_DIAGCROSS 5 +#define U_HS_SOLIDCLR 6 +#define U_HS_DITHEREDCLR 7 +#define U_HS_SOLIDTEXTCLR 8 +#define U_HS_DITHEREDTEXTCLR 9 +#define U_HS_SOLIDBKCLR 10 +#define U_HS_DITHEREDBKCLR 11 +/** @} */ + +/** \defgroup U_EMRSETICMMODE_iMode_Qualifiers ICMMode Enumeration + For EMF U_EMR_SETICMMODE iMode field + @{ +*/ +#define U_ICM_OFF 1 +#define U_ICM_ON 2 +#define U_ICM_QUERY 3 +/** @} */ + +/** \defgroup U_COLORADJUSTMENT_caIlluminantIndex_Qualifiers Illuminant Enumeration + For U_COLORADJUSTMENT caIlluminantIndex field + @{ +*/ +#define U_ILLUMINANT_DEVICE_DEFAULT 0 +#define U_ILLUMINANT_A 1 +#define U_ILLUMINANT_B 2 +#define U_ILLUMINANT_C 3 +#define U_ILLUMINANT_D50 4 +#define U_ILLUMINANT_D55 5 +#define U_ILLUMINANT_D65 6 +#define U_ILLUMINANT_D75 7 +#define U_ILLUMINANT_F2 8 +#define U_ILLUMINANT_MAX_INDEX ILLUMINANT_F2 +#define U_ILLUMINANT_TUNGSTEN ILLUMINANT_A +#define U_ILLUMINANT_DAYLIGHT ILLUMINANT_C +#define U_ILLUMINANT_FLUORESCENT ILLUMINANT_F2 +#define U_ILLUMINANT_NTSC ILLUMINANT_C +/** @} */ + +/** \defgroup U_LOGBRUSH_lbStyle_Qualifiers LB_Style Enumeration + For U_LOGBRUSH lbStyle field + @{ +*/ +#define U_BS_SOLID 0 +#define U_BS_NULL 1 +#define U_BS_HOLLOW 1 +#define U_BS_HATCHED 2 +#define U_BS_PATTERN 3 +#define U_BS_INDEXED 4 +#define U_BS_DIBPATTERN 5 +#define U_BS_DIBPATTERNPT 6 +#define U_BS_PATTERN8X8 7 +#define U_BS_DIBPATTERN8X8 8 +#define U_BS_MONOPATTERN 9 +/** @} */ + +/** \defgroup _LOGCOLORSPACE_lcsCSType_Qualifiers LCS_CSType Enumeration + For U_LOGCOLORSPACEA/U_LOGCOLORSPACEW lcsCSType field + @{ +*/ +#define U_LCS_CALIBRATED_RGB 0x00000000L +#define U_LCS_DEVICE_RGB 0x00000001L +#define U_LCS_DEVICE_CMYK 0x00000002L +/** @} */ + +/** \defgroup U_LOGCOLORSPACE_lcsIntent_Qualifiers LCS_Intent Enumeration + For U_LOGCOLORSPACEA/U_LOGCOLORSPACEW lcsIntent field + @{ +*/ +#define U_LCS_GM_BUSINESS 0x00000001L +#define U_LCS_GM_GRAPHICS 0x00000002L +#define U_LCS_GM_IMAGES 0x00000004L +#define U_LCS_GM_ABS_COLORIMETRIC 0x00000008L +/** @} */ + +/** \defgroup U_PANOSE_bLetterForm_Qualifiers Letterform Enumeration + For U_PANOSE bLetterForm field + @{ +*/ +#define U_PAN_LETT_NORMAL_COMPACT 2 +#define U_PAN_LETT_NORMAL_WEIGHTED 3 +#define U_PAN_LETT_NORMAL_BOXED 4 +#define U_PAN_LETT_NORMAL_FLATTENED 5 +#define U_PAN_LETT_NORMAL_ROUNDED 6 +#define U_PAN_LETT_NORMAL_OFF_CENTER 7 +#define U_PAN_LETT_NORMAL_SQUARE 8 +#define U_PAN_LETT_OBLIQUE_COMPACT 9 +#define U_PAN_LETT_OBLIQUE_WEIGHTED 10 +#define U_PAN_LETT_OBLIQUE_BOXED 11 +#define U_PAN_LETT_OBLIQUE_FLATTENED 12 +#define U_PAN_LETT_OBLIQUE_ROUNDED 13 +#define U_PAN_LETT_OBLIQUE_OFF_CENTER 14 +#define U_PAN_LETT_OBLIQUE_SQUARE 15 +/** @} */ + +/** \defgroup U_LOGFONT_lfWeight_Qualifiers LF_Weight Enumeration + For U_LOGFONT lfWeight field + @{ +*/ +#define U_FW_DONTCARE 0 +#define U_FW_THIN 100 +#define U_FW_EXTRALIGHT 200 +#define U_FW_ULTRALIGHT 200 +#define U_FW_LIGHT 300 +#define U_FW_NORMAL 400 +#define U_FW_REGULAR 400 +#define U_FW_MEDIUM 500 +#define U_FW_SEMIBOLD 600 +#define U_FW_DEMIBOLD 600 +#define U_FW_BOLD 700 +#define U_FW_EXTRABOLD 800 +#define U_FW_ULTRABOLD 800 +#define U_FW_HEAVY 900 +#define U_FW_BLACK 900 +/** @} */ + +/** \defgroup U_LOGFONT_lfItalic_Qualifiers LF_Italic Enumeration + For U_LOGFONT lfItalic field + @{ +*/ +#define U_FW_NOITALIC 0 +#define U_FW_ITALIC 1 +/** @} */ + +/** \defgroup U_LOGFONT_lfunderline_Qualifiers LF_Underline Enumeration + For U_LOGFONT lfunderline field + @{ +*/ +#define U_FW_NOUNDERLINE 0 +#define U_FW_UNDERLINE 1 +/** @} */ + +/** \defgroup U_LOGFONT_lfStrikeOut_Qualifiers LF_StrikeOut Enumeration + For U_LOGFONT lfStrikeOut field + @{ +*/ +#define U_FW_NOSTRIKEOUT 0 +#define U_FW_STRIKEOUT 1 +/** @} */ + +/** \defgroup U_LOGFONT_lfCharSet_Qualifiers LF_CharSet Enumeration + For U_LOGFONT lfCharSet field + @{ +*/ +#define U_ANSI_CHARSET (uint8_t)0 /* CP1252, ansi-0, iso8859-{1,15} */ +#define U_DEFAULT_CHARSET (uint8_t)1 +#define U_SYMBOL_CHARSET (uint8_t)2 +#define U_SHIFTJIS_CHARSET (uint8_t)128 /* CP932 */ +#define U_HANGEUL_CHARSET (uint8_t)129 /* CP949, ksc5601.1987-0 */ +#define U_HANGUL_CHARSET U_HANGEUL_CHARSET +#define U_GB2312_CHARSET (uint8_t)134 /* CP936, gb2312.1980-0 */ +#define U_CHINESEBIG5_CHARSET (uint8_t)136 /* CP950, big5.et-0 */ +#define U_GREEK_CHARSET (uint8_t)161 /* CP1253 */ +#define U_TURKISH_CHARSET (uint8_t)162 /* CP1254, -iso8859-9 */ +#define U_HEBREW_CHARSET (uint8_t)177 /* CP1255, -iso8859-8 */ +#define U_ARABIC_CHARSET (uint8_t)178 /* CP1256, -iso8859-6 */ +#define U_BALTIC_CHARSET (uint8_t)186 /* CP1257, -iso8859-13 */ +#define U_RUSSIAN_CHARSET (uint8_t)204 /* CP1251, -iso8859-5 */ +#define U_EE_CHARSET (uint8_t)238 /* CP1250, -iso8859-2 */ +#define U_EASTEUROPE_CHARSET U_EE_CHARSET +#define U_THAI_CHARSET (uint8_t)222 /* CP874, iso8859-11, tis620 */ +#define U_JOHAB_CHARSET (uint8_t)130 /* korean (johab) CP1361 */ +#define U_MAC_CHARSET (uint8_t)77 +#define U_OEM_CHARSET (uint8_t)255 +/* I don't know if the values of *_CHARSET macros are defined in Windows + * or if we can choose them as we want. -- srtxg + */ +#define U_VISCII_CHARSET (uint8_t)240 /* viscii1.1-1 */ +#define U_TCVN_CHARSET (uint8_t)241 /* tcvn-0 */ +#define U_KOI8_CHARSET (uint8_t)242 /* koi8-{r,u,ru} */ +#define U_ISO3_CHARSET (uint8_t)243 /* iso8859-3 */ +#define U_ISO4_CHARSET (uint8_t)244 /* iso8859-4 */ +#define U_ISO10_CHARSET (uint8_t)245 /* iso8859-10 */ +#define U_CELTIC_CHARSET (uint8_t)246 /* iso8859-14 */ +/** @} */ + +/** \defgroup U_LOGFONT_lfOutPrecision_Qualifiers LF_OutPrecision Enumeration + For U_LOGFONT lfOutPrecision field + @{ +*/ +#define U_OUT_DEFAULT_PRECIS 0 +#define U_OUT_STRING_PRECIS 1 +#define U_OUT_CHARACTER_PRECIS 2 +#define U_OUT_STROKE_PRECIS 3 +#define U_OUT_TT_PRECIS 4 +#define U_OUT_DEVICE_PRECIS 5 +#define U_OUT_RASTER_PRECIS 6 +#define U_OUT_TT_ONLY_PRECIS 7 +#define U_OUT_OUTLINE_PRECIS 8 +/** @} */ + +/** \defgroup U_LOGFONT_lfClipPrecision_Qualifiers LF_ClipPrecision Enumeration + For U_LOGFONT lfClipPrecision field + @{ +*/ +#define U_CLIP_DEFAULT_PRECIS 0x00 +#define U_CLIP_CHARACTER_PRECIS 0x01 +#define U_CLIP_STROKE_PRECIS 0x02 +#define U_CLIP_MASK 0x0F +#define U_CLIP_LH_ANGLES 0x10 +#define U_CLIP_TT_ALWAYS 0x20 +#define U_CLIP_EMBEDDED 0x80 +/** @} */ + +/** \defgroup U_LOGFONT_lfQuality_Qualifiers LF_Quality Enumeration + For For U_LOGFONT lfQuality field + @{ +*/ +#define U_DEFAULT_QUALITY 0 +#define U_DRAFT_QUALITY 1 +#define U_PROOF_QUALITY 2 +#define U_NONANTIALIASED_QUALITY 3 +#define U_ANTIALIASED_QUALITY 4 +/** @} */ + +/** \defgroup U_LOGFONT_lfPitchAndFamily_Qualifiers LF_PitchAndFamily Enumeration + For U_LOGFONT lfPitchAndFamily field + @{ +*/ +#define U_DEFAULT_PITCH 0x00 +#define U_FIXED_PITCH 0x01 +#define U_VARIABLE_PITCH 0x02 +#define U_MONO_FONT 0x08 +#define U_FF_DONTCARE 0x00 +#define U_FF_ROMAN 0x10 +#define U_FF_SWISS 0x20 +#define U_FF_MODERN 0x30 +#define U_FF_SCRIPT 0x40 +#define U_FF_DECORATIVE 0x50 +/** @} */ + +/** \defgroup U_EMRSETMAPMODE_iMode_Qualifiers MapMode Enumeration + For U_EMRSETMAPMODE iMode field + @{ +*/ +#define U_MM_TEXT 1 +#define U_MM_LOMETRIC 2 +#define U_MM_HIMETRIC 3 +#define U_MM_LOENGLISH 4 +#define U_MM_HIENGLISH 5 +#define U_MM_TWIPS 6 +#define U_MM_ISOTROPIC 7 +#define U_MM_ANISOTROPIC 8 +#define U_MM_MIN U_MM_TEXT +#define U_MM_MAX U_MM_ANISOTROPIC +#define U_MM_MAX_FIXEDSCALE U_MM_TWIPS +/** @} */ + + +/** \defgroup U_PANOSE_bMidline_Qualifiers MidLine Enumeration + For U_PANOSE bMidline field + @{ +*/ +#define U_PAN_MIDLINE_STANDARD_TRIMMED 2 +#define U_PAN_MIDLINE_STANDARD_POINTED 3 +#define U_PAN_MIDLINE_STANDARD_SERIFED 4 +#define U_PAN_MIDLINE_HIGH_TRIMMED 5 +#define U_PAN_MIDLINE_HIGH_POINTED 6 +#define U_PAN_MIDLINE_HIGH_SERIFED 7 +#define U_PAN_MIDLINE_CONSTANT_TRIMMED 8 +#define U_PAN_MIDLINE_CONSTANT_POINTED 9 +#define U_PAN_MIDLINE_CONSTANT_SERIFED 10 +#define U_PAN_MIDLINE_LOW_TRIMMED 11 +#define U_PAN_MIDLINE_LOW_POINTED 12 +#define U_PAN_MIDLINE_LOW_SERIFED 13 +/** @} */ + +/** \defgroup U_EMRSETLAYOUT_iMode_Qualifiers Mirroring Enumeration + For U_EMRSETLAYOUT iMode field + @{ +*/ +#define U_LAYOUT_LTR 0x00000000 +#define U_LAYOUT_RTL 0x00000001 +#define U_LAYOUT_BITMAPORIENTATIONPRESERVED 0x00000008 +#define U_NOMIRRORBITMAP 0x80000000 +/** @} */ + +/** \defgroup U_EMRMODIFYWORLDTRANSFORM_iMode_Qualifiers ModifyWorldTransformMode Enumeration + For U_EMRMODIFYWORLDTRANSFORM iMode + @{ +*/ +#define U_MWT_IDENTITY 1 +#define U_MWT_LEFTMULTIPLY 2 +#define U_MWT_RIGHTMULTIPLY 3 +#define U_MWT_MIN U_MWT_IDENTITY +#define U_MWT_MAX U_MWT_RIGHTMULTIPLY +/** @} */ + +/** \defgroup U_PANOSE_common_Qualifiers PanoseCommon Enumeration + Used by all PAN_* enumerations, but only defined once here. + See also U_PAN_ALL1 after the U_PANOSE structure + @{ +*/ +#define U_PAN_ANY 0 +#define U_PAN_NO_FIT 1 +/** @} */ + +/** \defgroup U_PANOSE_index PanoseIndex Enumeration + Fositions of each field in U_PANOSE structure. + @{ +*/ +#define U_PANOSE_COUNT 10 +#define U_PANOSE_FAMILYTYPE_INDEX 0 +#define U_PAN_SERIFSTYLE_INDEX 1 +#define U_PAN_WEIGHT_INDEX 2 +#define U_PAN_PROPORTION_INDEX 3 +#define U_PAN_CONTRAST_INDEX 4 +#define U_PAN_STROKEVARIATION_INDEX 5 +#define U_PAN_ARMSTYLE_INDEX 6 +#define U_PAN_LETTERFORM_INDEX 7 +#define U_PAN_MIDLINE_INDEX 8 +#define U_PAN_XHEIGHT_INDEX 9 +/** @} */ + +/** \defgroup U_*LOGPEN_elpPenStyle_Qualifiers PenStyle Enumeration + For U_LOGPEN lopnStyle and U_EXTLOGPEN elpPenStyle fields + @{ +*/ +#define U_PS_SOLID 0x00000000 +#define U_PS_DASH 0x00000001 //!< This only works when NO other U_PS is set. Line width is minimum no matter what pen is set to. +#define U_PS_DOT 0x00000002 //!< This only works when NO other U_PS is set. Line width is minimum no matter what pen is set to. +#define U_PS_DASHDOT 0x00000003 //!< This only works when NO other U_PS is set. Line width is minimum no matter what pen is set to. +#define U_PS_DASHDOTDOT 0x00000004 //!< This only works when NO other U_PS is set. Line width is minimum no matter what pen is set to. +#define U_PS_NULL 0x00000005 +#define U_PS_INSIDEFRAME 0x00000006 +#define U_PS_USERSTYLE 0x00000007 +#define U_PS_ALTERNATE 0x00000008 +#define U_PS_STYLE_MASK 0x0000000f + +#define U_PS_ENDCAP_ROUND 0x00000000 //!< These are only with U_PS_GEOMETRIC +#define U_PS_ENDCAP_SQUARE 0x00000100 +#define U_PS_ENDCAP_FLAT 0x00000200 +#define U_PS_ENDCAP_MASK 0x00000f00 + +#define U_PS_JOIN_ROUND 0x00000000 //!< These are only with U_PS_GEOMETRIC +#define U_PS_JOIN_BEVEL 0x00001000 +#define U_PS_JOIN_MITER 0x00002000 +#define U_PS_JOIN_MASK 0x0000f000 + +#define U_PS_COSMETIC 0x00000000 //!< width may only be 1 pixel. (If set higher it is still drawn as 1). +#define U_PS_GEOMETRIC 0x00010000 //!< width may be >1 pixel, but style may only be U_PS_SOLID or U_PS_NULL. +#define U_PS_TYPE_MASK 0x000f0000 +/** @} */ + +/** \defgroup U_PIXELFORMATDESCRIPTOR_dwFlags_Qualifiers PFD_dwFlags Enumeration + For U_PIXELFORMATDESCRIPTOR dwFlags field + @{ +*/ +#define U_PFD_DOUBLEBUFFER 0x00000001 +#define U_PFD_STEREO 0x00000002 +#define U_PFD_DRAW_TO_WINDOW 0x00000004 +#define U_PFD_DRAW_TO_BITMAP 0x00000008 +#define U_PFD_SUPPORT_GDI 0x00000010 +#define U_PFD_SUPPORT_OPENGL 0x00000020 +#define U_PFD_GENERIC_FORMAT 0x00000040 +#define U_PFD_NEED_PALETTE 0x00000080 +#define U_PFD_NEED_SYSTEM_PALETTE 0x00000100 +#define U_PFD_SWAP_EXCHANGE 0x00000200 +#define U_PFD_SWAP_COPY 0x00000400 +#define U_PFD_SWAP_LAYER_BUFFERS 0x00000800 +#define U_PFD_GENERIC_ACCELERATED 0x00001000 +/** @} */ + +/** \defgroup U_PIXELFORMATDESCRIPTOR_iLayerType_Qualifiers PFD_iLayerType Enumeration + For U_PIXELFORMATDESCRIPTOR iLayerType field + @{ +*/ +#define U_PFD_MAIN_PLANE 0 +#define U_PFD_OVERLAY_PLANE 1 +#define U_PFD_UNDERLAY_PLANE (-1) +/** @} */ + +/** \defgroup U_PIXELFORMATDESCRIPTOR_iPixelType_Qualifiers PFD_iPixelType Enumeration + For U_PIXELFORMATDESCRIPTOR iPixelType field + @{ +*/ +#define U_PFD_TYPE_RGBA 0 +#define U_PFD_TYPE_COLORINDEX 1 +/** @} */ + +/** \defgroup U_EMRPOLY_iMode_Qualifiers Point Enumeration + For U_EMRPOLYDRAW and U_EMRPOLAYDRAW16 abTypes fields. + @{ +*/ +#define U_PT_CLOSEFIGURE 0x0001 +#define U_PT_LINETO 0x0002 +#define U_PT_BEZIERTO 0x0004 +#define U_PT_MOVETO 0x0006 +/** @} */ + +/** \defgroup U_EMRSETPOLYFILLMODE_iMode_Qualifiers PolygonFillMode Enumeration + For U_EMRSETPOLYFILLMODE iMode field + @{ +*/ +#define U_ALTERNATE 1 +#define U_WINDING 2 +#define U_POLYFILL_LAST 2 +/** @} */ + +/** \defgroup U_BITMAPV5HEADER_bV5CSType_Qualifiers Profile Enumeration + For U_BITMAPV5HEADER bV5CSType field + @{ +*/ +#define U_PROFILE_LINKED 'LINK' +#define U_PROFILE_EMBEDDED 'MBED' +/** @} */ + +/** \defgroup U_PANOSE_bProportion_Qualifiers Proportion Enumeration + For U_PANOSE bProportion field + @{ +*/ +#define U_PAN_PROP_OLD_STYLE 2 +#define U_PAN_PROP_MODERN 3 +#define U_PAN_PROP_EVEN_WIDTH 4 +#define U_PAN_PROP_EXPANDED 5 +#define U_PAN_PROP_CONDENSED 6 +#define U_PAN_PROP_VERY_EXPANDED 7 +#define U_PAN_PROP_VERY_CONDENSED 8 +#define U_PAN_PROP_MONOSPACED 9 +/** @} */ + +/** \defgroup U_EMR_dwROP_Qualifiers Ternary Raster Operation enumeration + For U_EMR* dwROP fields. + + These codes specify: + 1. an order of operands (composed of various orders and combinations of: Dest, Src, Pen) + (There are 3, hence "Ternary Raster Operation") + 2. an order of operators to apply to the operands (composed of Not, Xor, Or, And) + Only a few of the more common operations are provided here. + When the Operation does not use a Src operand the corresponding source bitmap may be + omitted from the record. + + For more details see: + http://wiki.winehq.org/TernaryRasterOps + @{ +*/ +#define U_SRCCOPY 0xcc0020 +#define U_SRCPAINT 0xee0086 +#define U_SRCAND 0x8800c6 +#define U_SRCINVERT 0x660046 +#define U_SRCERASE 0x440328 +#define U_NOTSRCCOPY 0x330008 +#define U_NOTSRCERASE 0x1100a6 +#define U_MERGECOPY 0xc000ca +#define U_MERGEPAINT 0xbb0226 +#define U_PATCOPY 0xf00021 +#define U_PATPAINT 0xfb0a09 +#define U_PATINVERT 0x5a0049 +#define U_DSTINVERT 0x550009 +#define U_BLACKNESS 0x000042 +#define U_WHITENESS 0xff0062 +/** @} */ + +/** \defgroup U_EMRSETROP2_iMode_Qualifiers Binary Raster Operation Enumeration + For U_EMRSETROP2 iMode field + + These codes specify: + 1. an order of operands (composed of various orders and combinations of: Dest, Pen) + (There are 2, hence "Binary Raster Operation") + 2. an order of operators to apply to the operands (composed of Not, Xor, Or, And) + Only a few of the more common operations are provided here. + + The default is U_R2_COPYPEN. If this value is changed to something else all subsequenty + draw operations will use the altered logic. For instance, if it is set to U_R2_BLACK and + a red rectangle is drawn it will appear as a black rectangle. + + @{ +*/ +#define U_R2_BLACK 1 +#define U_R2_NOTMERGEPEN 2 +#define U_R2_MASKNOTPEN 3 +#define U_R2_NOTCOPYPEN 4 +#define U_R2_MASKPENNOT 5 +#define U_R2_NOT 6 +#define U_R2_XORPEN 7 +#define U_R2_NOTMASKPEN 8 +#define U_R2_MASKPEN 9 +#define U_R2_NOTXORPEN 10 +#define U_R2_NOP 11 +#define U_R2_MERGENOTPEN 12 +#define U_R2_COPYPEN 13 +#define U_R2_MERGEPENNOT 14 +#define U_R2_MERGEPEN 15 +#define U_R2_WHITE 16 +#define U_R2_LAST 16 +/** @} */ + +/** \defgroup U_EMRSELECTCLIP_iMode_Qualifiers RegionMode Enumeration + For U_EMRSELECTCLIPPATH and U_EMREXTSELECTCLIPRGN iMode field + @{ +*/ +#define U_RGN_AND 1 +#define U_RGN_OR 2 +#define U_RGN_XOR 3 +#define U_RGN_DIFF 4 +#define U_RGN_COPY 5 +#define U_RGN_MIN U_RGN_AND +#define U_RGN_MAX U_RGN_COPY +/** @} */ + +/** \defgroup U_PANOSE_bSerifStyle_Qualifiers SerifType Enumeration + For U_PANOSE bSerifStyle field + @{ +*/ +#define U_PAN_SERIF_COVE 2 +#define U_PAN_SERIF_OBTUSE_COVE 3 +#define U_PAN_SERIF_SQUARE_COVE 4 +#define U_PAN_SERIF_OBTUSE_SQUARE_COVE 5 +#define U_PAN_SERIF_SQUARE 6 +#define U_PAN_SERIF_THIN 7 +#define U_PAN_SERIF_BONE 8 +#define U_PAN_SERIF_EXAGGERATED 9 +#define U_PAN_SERIF_TRIANGLE 10 +#define U_PAN_SERIF_NORMAL_SANS 11 +#define U_PAN_SERIF_OBTUSE_SANS 12 +#define U_PAN_SERIF_PERP_SANS 13 +#define U_PAN_SERIF_FLARED 14 +#define U_PAN_SERIF_ROUNDED 15 +/** @} */ + +/** \defgroup U_EMRSELECTOBJECT_ihObject_Qualifiers StockObject Enumeration + For U_EMRSELECTOBJECT ihObject field. + @{ +*/ +#define U_STOCK_OBJECT 0x80000000 +#define U_WHITE_BRUSH 0x80000000 +#define U_LTGRAY_BRUSH 0x80000001 +#define U_GRAY_BRUSH 0x80000002 +#define U_DKGRAY_BRUSH 0x80000003 +#define U_BLACK_BRUSH 0x80000004 +#define U_NULL_BRUSH 0x80000005 +#define U_HOLLOW_BRUSH 0x80000005 +#define U_WHITE_PEN 0x80000006 +#define U_BLACK_PEN 0x80000007 +#define U_NULL_PEN 0x80000008 +#define U_OEM_FIXED_FONT 0x8000000A +#define U_ANSI_FIXED_FONT 0x8000000B +#define U_ANSI_VAR_FONT 0x8000000C +#define U_SYSTEM_FONT 0x8000000D +#define U_DEVICE_DEFAULT_FONT 0x8000000E +#define U_DEFAULT_PALETTE 0x8000000F +#define U_SYSTEM_FIXED_FONT 0x80000010 +#define U_DEFAULT_GUI_FONT 0x80000011 +#define U_STOCK_LAST 0x80000011 +/** @} */ + +/** \defgroup U_EMRSETSTRETCHBLTMODE_iMode_Qualifiers StretchMode Enumeration + For EMF U_EMRSETSTRETCHBLTMODE iMode field + @{ +*/ +#define U_BLACKONWHITE 1 +#define U_WHITEONBLACK 2 +#define U_COLORONCOLOR 3 +#define U_HALFTONE 4 +#define U_MAXSTRETCHBLTMODE 4 +#define U_STRETCH_ANDSCANS 1 +#define U_STRETCH_ORSCANS 2 +#define U_STRETCH_DELETESCANS 3 +#define U_STRETCH_HALFTONE 4 +/** @} */ + +/** \defgroup U_PANOSE_bStrokeVariation_Qualifiers StrokeVariation Enumeration + For U_PANOSE bStrokeVariation field + @{ +*/ +#define U_PAN_STROKE_GRADUAL_DIAG 2 +#define U_PAN_STROKE_GRADUAL_TRAN 3 +#define U_PAN_STROKE_GRADUAL_VERT 4 +#define U_PAN_STROKE_GRADUAL_HORZ 5 +#define U_PAN_STROKE_RAPID_VERT 6 +#define U_PAN_STROKE_RAPID_HORZ 7 +#define U_PAN_STROKE_INSTANT_VERT 8 +/** @} */ + +/** \defgroup U_EMRSETTEXTALIGN_iMode_Qualifiers TextAlignment Enumeration + For U_EMRSETTEXTALIGN iMode field + + Recall that EMF coordinates have UL closest to {0,0}, LR is below and to the right of UL and so has LARGER + {x,y} coordinates. In the following "TOP" is on the horizontal line defined by LR, as it has larger y coordinates, + which when viewing the EMF file, would actually be on the BOTTOM of the bounding rectangle. Similarly, left and right + are reversed. + + Microsoft documentation (WMF manual, section 2.1.2.3) says that the text starts on certain edges of the bounding rectangle. + That is apparently not true, whether the bounding rectangle is {0,0,-1,-1}, which is effectively no bounding rectangle, + or if a valid bounding rectangle is specified. In all cases the text (in Windows XP Preview) starts, has center at, or ends + at the center point. Vertical offsets seem to be defined analogously, but with respect to the height of the font. The bounding + rectangle defined for the U_EMRTEXT record appears to be ignored. + + Microsoft documentation (EMF manual,section 2.2.5) says that the same rectangle is used for "clipping or opaquing" by ExtTextOutA/W. + That does not seem to occur either. + + @{ +*/ +// Horizontal text flags +#define U_TA_DEFAULT 0x00 // default alignment +#define U_TA_NOUPDATECP 0x00 // Reference point does not move +#define U_TA_UPDATECP 0x01 // Reference point moves to end of next text drawn. +#define U_TA_LEFT 0x00 // Reference point is on left edge of bounding rectangle +#define U_TA_RIGHT 0x02 // Reference point is on right edge of bounding rectangle +#define U_TA_CENTER 0x06 // Reference point is on center vertical line of bounding rectangle +#define U_TA_TOP 0x00 // Reference point is on top edge of bounding rectangle +#define U_TA_BOTTOM 0x08 // Reference point is on bottom edge of bounding rectangle +#define U_TA_BASEBIT 0x10 // Reference point is on baseline of text if this bit is set, for 0x10 <-> 0x18 +#define U_TA_BASELINE 0x18 // Reference point is on baseline of text +#define U_TA_RTLREADING 0x100 // Set for Right to Left languages like Hebrew and Arabic +#define U_TA_MASK U_TA_BASELINE+U_TA_CENTER+U_TA_UPDATECP+U_TA_RTLREADING +// Vertical text flags +#define U_VTA_BASELINE U_TA_BASELINE // for vertical text +#define U_VTA_LEFT U_TA_BOTTOM +#define U_VTA_RIGHT U_TA_TOP +#define U_VTA_CENTER U_TA_CENTER +#define U_VTA_BOTTOM U_TA_RIGHT +#define U_VTA_TOP U_TA_LEFT +/** @} */ + +/** \defgroup U_PANOSE_bWeight_Qualifiers Weight Enumeration + For U_PANOSE bWeight field + @{ +*/ +#define U_PAN_WEIGHT_VERY_LIGHT 2 +#define U_PAN_WEIGHT_LIGHT 3 +#define U_PAN_WEIGHT_THIN 4 +#define U_PAN_WEIGHT_BOOK 5 +#define U_PAN_WEIGHT_MEDIUM 6 +#define U_PAN_WEIGHT_DEMI 7 +#define U_PAN_WEIGHT_BOLD 8 +#define U_PAN_WEIGHT_HEAVY 9 +#define U_PAN_WEIGHT_BLACK 10 +#define U_PAN_WEIGHT_NORD 11 +/** @} */ + +/** \defgroup U_PANOSE_bXHeight_Qualifiers XHeight Enumeration + For U_PANOSE bXHeight field + @{ +*/ +#define U_PAN_XHEIGHT_CONSTANT_SMALL 2 +#define U_PAN_XHEIGHT_CONSTANT_STANDARD 3 +#define U_PAN_XHEIGHT_CONSTANT_LARGE 4 +#define U_PAN_XHEIGHT_DUCKING_SMALL 5 +#define U_PAN_XHEIGHT_DUCKING_STANDARD 6 +#define U_PAN_XHEIGHT_DUCKING_LARGE 7 +/** @} */ + +/** \defgroup U_BLEND_Op_Qualifiers Blend Enumeration + For U_BLEND Op field + @{ +*/ +#define U_AC_SRC_GLOBAL 0 +#define U_AC_SRC_CONST 0 +#define U_AC_SRC_ALPHA 1 +/** @} */ + + +// *************************************************************************** +/** \defgroup Miscellaneous_values Miscellaneous Values + @{ +*/ +#define U_NONE 0 //!< Generic for nothing selected for all flag fields +#define U_PI 3.14159265358979323846 //!< pi +#define U_READ 1 +#define U_WRITE 0 +#define U_ENHMETA_VERSION 0x00010000 //!< U_EMRHEADER nVersion field +#define U_DV_SGNTR 0x08007664 //!< For U_DESIGNVECTOR Signature field +#define U_LP_VERSION 0x0300 //!< For U_LOGPALETTE palVersion field +#define U_RDH_RECTANGLES 1 //!< For U_RGNDATAHEADER iType field +#define U_RDH_OBJSIZE 0x20 //!< For U_RGNDATAHEADER dwSIze field +#define U_RGB_GAMMA_MIN (uint16_t)02500 //!< For U_COLORADJUSTMENT ca[Red|Green|Blue]Gamma fields +#define U_RGB_GAMMA_MAX (uint16_t)65000 //!< For U_COLORADJUSTMENT ca[Red|Green|Blue]Gamma fields +#define U_REFERENCE_WHITE_MIN (uint16_t)6000 //!< For U_COLORADJUSTMENT caReferenceWhite field +#define U_REFERENCE_WHITE_MAX (uint16_t)10000 //!< For U_COLORADJUSTMENT caReferenceWhite field +#define U_REFERENCE_BLACK_MIN (uint16_t)0 //!< For U_COLORADJUSTMENT caReferenceBlack field +#define U_REFERENCE_BLACK_MAX (uint16_t)4000 //!< For U_COLORADJUSTMENT caReferenceBlack field +#define U_COLOR_ADJ_MIN ((int16_t)-100) //!< For U_COLORADJUSTMENT ca[Contrast|Brightness|Colorfulness|RedGreenTint] fields +#define U_COLOR_ADJ_MAX (int16_t) 100 //!< For U_COLORADJUSTMENT ca[Contrast|Brightness|Colorfulness|RedGreenTint] fields +#define U_MAX_PATH 1024 //!< longest path name for a file +#define U_LCS_SIGNATURE 0x50534F43 //!< logColorSpace Signature +#define U_LCS_VERSION 0x400 //!< logColorSpace Version +#define U_REC_FREE 1 //!< used with emf_append +#define U_REC_KEEP 0 //!< used with emf_append +/** Solaris 8 has problems with round/roundf, just use this everywhere */ +#define U_ROUND(A) ( (A) > 0 ? floor((A)+0.5) : ( (A) < 0 ? -floor(-(A)+0.5) : (A) ) ) + +/** @} */ + +// *************************************************************************** +// Macros + +/** \defgroup Common_macros Common Macros + @{ +*/ +// Color U_BGR(A), byte order lo to hi: {B,G,R,A} corresponding to U_RGBQUAD. Set/Get Macros. +// These are used in EMF structures and the byte order must be the same in memory or on disk. +#define U_BGR(r,g,b) (U_RGBQUAD){b,g,r,0} //!< Set any BGR color with an {r,g,b} triplet +#define U_BGRA(r,g,b,a) (U_RGBQUAD){b,g,r,a} //!< Set any BGRA color with an {r,g,b,a} quad +#define U_WHITE U_BGR(255,255,255) //!< Set BGR white. +#define U_BLACK U_BGR(0,0,0) //!< Set BGR black. +#define U_BGRAGetR(rgb) (rgb.Red ) //!< Color BGR Get Red Macro. +#define U_BGRAGetG(rgb) (rgb.Green ) //!< Color BGR Get Green Macro. +#define U_BGRAGetB(rgb) (rgb.Blue ) //!< Color BGR Get Blue Macro. +#define U_BGRAGetA(rgb) (rgb.Reserved) //!< Color BGR Get A/reserved Macro. + +#define U_PALETTERGB(r,g,b) U_RGB(r,g,b,0x02)) //!< Set any Palette RGB color. +#define U_PALETTEINDEX(i) ((U_COLORREF)(0x01000000 | (uint16_t)(i)))\ + //!< Get RGB from Palette by index. + +// Color U_RGB(A), byte order lo to hi: {R,G,B,A} corresponding to U_COLORREF. Set/Get Macros. +// These are used in EMF structures and the byte order must be the same in memory or on disk. +// These MAY be used in PNG and other libraries if these enforce byte order in memory,otherwise +// U_swap4 may need to also be employed. + +/// Set any RGB color with an {r,g,b} triplet +#define U_RGB(r,g,b) (U_COLORREF){(uint8_t)(r), (uint8_t)(g), (uint8_t)(b), 0} +/// Set any RGBA color with an {r,g,b,a} quad +#define U_RGBA(r,g,b,a) (U_COLORREF){(uint8_t)(r), (uint8_t)(g), (uint8_t)(b), (uint8_t)(a)} +#define U_RGBAGetR(rgb) ((U_COLORREF)rgb).Red //!< Color RGB Get Red Macro. +#define U_RGBAGetG(rgb) ((U_COLORREF)rgb).Green //!< Color RGB Get Green Macro. +#define U_RGBAGetB(rgb) ((U_COLORREF)rgb).Blue //!< Color RGB Get Blue Macro. +#define U_RGBAGetA(rgb) ((U_COLORREF)rgb).Reserved //!< Color RGBA Get A/reserved Macro. + +// color type conversions +#define U_RGB2BGR(rgb) (U_RGBQUAD){ U_RGBAGetB(rgb),U_RGBAGetG(rgb),U_RGBAGetR(rgb),0} //!< Set any BGR color from an RGB color +#define U_BGR2RGB(rgb) (U_COLORREF){U_BGRAGetR(rgb),U_BGRAGetG(rgb),U_BGRAGetB(rgb),0} //!< Set any RGB color from an BGR color +#define U_RGBA2BGRA(rgb) (U_RGBQUAD){ U_RGBAGetB(rgb),U_RGBAGetG(rgb),U_RGBAGetR(rgb),U_RGBAGetA(rgb)} //!< Set any BGRA color from an RGBA color +#define U_BGRA2RGBA(rgb) (U_COLORREF){U_BGRAGetR(rgb),U_BGRAGetG(rgb),U_BGRAGetB(rgb),U_BGRAGetA(rgb)} //!< Set any RGBA color from an BGRA color + +// Color CMYK Get/Set Macros +#define U_CMYK(c,m,y,k)\ + ((COLOREF)((((uint8_t)(k)|((uint16_t)((uint8_t)(y))<<8))|(((uint32_t)(uint8_t)(m))<<16))|(((uint32_t)(uint8_t)(c))<<24))) \ + //!< Color CMYK Set Macro. +#define U_GetKValue(cmyk) ((uint8_t) (cmyk) ) //!< Color CMYK Get K Macro. +#define U_GetYValue(cmyk) ((uint8_t) ((cymk) >> 8)) //!< Color CMYK Get Y Macro. +#define U_GetMValue(cmyk) ((uint8_t) ((cymk) >> 16)) //!< Color CMYK Get M Macro. +#define U_GetCValue(cmyk) ((uint8_t) ((cymk) >> 24)) //!< Color CMYK Get C Macro. + +// Other macros +#define U_Gamma(A) (A < U_RGB_GAMMA_MIN ? U_RGB_GAMMA_MIN : (A > U_RGB_GAMMA_MAX ? U_RGB_GAMMA_MAX: A)) \ + //!< Gamma set Macro (enforce range). +#define U_PM(A,B) ((A)<-(B)?-(B):((A)>(B)?(B):(A))) //!< Plus/Minus Range Macro (B must be postitive!). +#define U_MNMX(A,B,C) ((A)<(B)?(B):((A)>(C)?(C):(A))) //!< Min/Max Range Macro (B <= A <= C). +#define U_MIN(A,B) ((A)>(B)?(B):(A)) //!< Minimum of A,B +#define U_MAX(A,B) ((A)>(B)?(A):(B)) //!< Maximum of A,B + +// basic EMR macros. +#define U_EMRTYPE(A) (((PU_EMR)A)->iType) //!< Get iType from U_EMR* record +#define U_EMRSIZE(A) (((PU_EMR)A)->nSize) //!< Get nSize from U_EMR* record + +// Utility macros +#define UP4(A) (4 * ((A + 3 ) / 4)) //!< Round up to nearest multiple of 4 + +/** @} */ + +typedef float U_FLOAT; + +typedef uint32_t U_CBBITS; // Describes byte count of TYPE +typedef uint32_t U_CBBITSMSK; +typedef uint32_t U_CBBITSSRC; +typedef uint32_t U_CBBMI; +typedef uint32_t U_CBBMIMSK; +typedef uint32_t U_CBBMISRC; +typedef uint32_t U_CBDATA; +typedef uint32_t U_CBNAME; +typedef uint32_t U_CBPLENTRIES; +typedef uint32_t U_CBPXLFMT; +typedef uint32_t U_CBRGNDATA; +typedef uint32_t U_CBSTR; // bytes in an 8 or 16 bit string + +typedef uint32_t U_OFFBITS; // Describes byte offset to TYPE, always measured from the start of the RECORD (not the struct) +typedef uint32_t U_OFFBITSMSK; +typedef uint32_t U_OFFBITSSRC; +typedef uint32_t U_OFFBMI; +typedef uint32_t U_OFFBMIMSK; +typedef uint32_t U_OFFBMISRC; +typedef uint32_t U_OFFDATA; +typedef uint32_t U_OFFDESC; +typedef uint32_t U_OFFDX; +typedef uint32_t U_OFFPLENTRIES; +typedef uint32_t U_OFFPXLFMT; +typedef uint32_t U_OFFSTR; // String of either 8 or 16 bit characters +typedef uint8_t U_DATA; // any binary sort of data, not otherwise classified. + +// "Types" For array components in structures, where not otherwise defined as a structure +typedef uint32_t U_FNTAXES; // Font Axes For U_DESIGNVECTOR +typedef uint32_t U_STYLEENTRY; // StyleEntry For U_EXTLOGPEN +typedef uint32_t U_POLYCOUNTS; // aPolyCounts For U_EMRPOLYPOLYLINE etc. + +// "Counts" for array components in structures +typedef uint32_t U_NUM_FNTAXES; // Number of U_FNTAXES +typedef uint32_t U_NUM_LOGPLTNTRY; // Number of U_LOGPLTENTRY +typedef uint32_t U_NUM_RECTL; // Number of U_RECTL +typedef uint32_t U_NUM_POINTL; // Number of U_POINTL +typedef uint32_t U_NUM_POINT16; // Number of U_POINT16 +typedef uint32_t U_NUM_STYLEENTRY; // Number of U_STYLEENTRY +typedef uint32_t U_NUM_POLYCOUNTS; // Number of U_POLYCOUNTS +typedef uint32_t U_NUM_EMRTEXT; // Number of U_EMRTEXT +typedef uint32_t U_NUM_STR; // Number of 8 or 16 bit characters in string +typedef uint32_t U_NUM_TRIVERTEX; // Number of U_TRIVERTEX +typedef uint32_t U_NUM_GRADOBJ; // Number of U_GRADIENT4 OR U_GRADIENT3 (determined at run time) +typedef uint32_t U_NUM_RGBQUAD; // Number of U_RGBQUAD (in bmciColors in U_BITMAPCOREINFO) + + +/** + \brief Pair of values indicating x and y sizes. + Microsoft name: SIZE Object + Microsoft name: SIZEL Object +*/ +typedef struct { + int32_t cx; //!< X size + int32_t cy; //!< Y size +} U_SIZE, U_SIZEL, *PU_SIZE, *PU_SIZEL; + +/** + \brief Used for any generic pair of floats + Microsoft name: (none) +*/ +typedef struct { + float x; //!< X value + float y; //!< Y value +} U_PAIRF, *PU_PAIRF; + +/** + \brief Used for any generic pair of uint32_t + Microsoft name: POINT Object +*/ +typedef struct { + int32_t x; //!< X value + int32_t y; //!< Y value +} U_PAIR, *PU_PAIR, U_POINT, *PU_POINT, U_POINTL, *PU_POINTL; + +/** + \brief Point type for 16 bit EMR drawing functions. + Microsoft name: POINTS Object + Microsoft name: POINTS16 Object +*/ +typedef struct { + int16_t x; //!< X size (16 bit) + int16_t y; //!< Y size (16 bit) +} U_POINT16, *PU_POINT16; + +/** + \brief Coordinates of the upper left, lower right corner. + Note that the coordinate system is 0,0 in the upper left corner + of the screen an N,M in the lower right corner. + Microsoft name: RECT Object +*/ +typedef struct { + int32_t left; //!< left coordinate + int32_t top; //!< top coordinate + int32_t right; //!< right coordinate + int32_t bottom; //!< bottom coordinate +} U_RECT, *PU_RECT, + U_RECTL, *PU_RECTL; + +#define U_RCL_DEF (U_RECTL){0,0,-1,-1} //!< Use this when no bounds are needed. + +/* ************************************************************ + EMF structures OTHER than those corresponding to complete U_EMR_* records + ************************************************************ */ + +/** + \brief For U_BITMAPINFO bmiColors field + NOTE that the color order is BGR, even though the name is RGB! + Microsoft name: RGBQUAD Object +*/ +typedef struct { + uint8_t Blue; //!< Blue color (0-255) + uint8_t Green; //!< Green color (0-255) + uint8_t Red; //!< Red color (0-255) + uint8_t Reserved; //!< Not used +} U_RGBQUAD, *PU_RGBQUAD; + +/** + \brief For U_BITMAPINFO crColor field + NOTE that the color order is RGB reserved, flipped around from the preceding. + Microsoft name: COLORREF Object +*/ +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 +} U_COLORREF, *PU_COLORREF; + +/** + \brief For U_POINT28_4 x and y fields. + Microsoft name: BitFIX28_4 Object. +*/ +typedef struct { + signed IntValue :28; //!< Signed integral bit field + unsigned FracValue :4; //!< Unsigned integral bit field +} U_BITFIX28_4, *PU_BITFIX28_4; + +/** + \brief For U_LCS_GAMMARGB lcsGamma* fields + Microsoft name:(unknown) Object +*/ +typedef struct { + unsigned ignoreHi :8; //!< not used + unsigned intPart :8; //!< integer part + unsigned fracPart :8; //!< fraction part + unsigned ignoreLo :8; //!< not used +} U_LCS_GAMMA, *PU_LCS_GAMMA; + +/** + \brief For U_LOGCOLORSPACEA and U_LOGCOLORSPACEW lcsGammaRGB field + Microsoft name:(unknown) Object +*/ +typedef struct { + U_LCS_GAMMA lcsGammaRed; //!< Red Gamma + U_LCS_GAMMA lcsGammaGreen; //!< Green Gamma + U_LCS_GAMMA lcsGammaBlue; //!< Blue Gamma +} U_LCS_GAMMARGB, *PU_LCS_GAMMARGB; + +/** + \brief For U_EMRSETOLORADJUSTMENT ColorAdjustment field + Note, range constants are: RGB_GAMMA_[MIN|MAX],REFERENCE_[WHITE|BLACK]_[MIN|MAX],COLOR_ADJ_[MIN|MAX] + Microsoft name: ColorAdjustment Object +*/ +typedef struct { + uint16_t caSize; //!< Size of this structure in bytes + uint16_t caFlags; //!< ColorAdjustment Enumeration + uint16_t caIlluminantIndex; //!< Illuminant Enumeration + uint16_t caRedGamma; //!< Red Gamma correction (range:2500:65000, 10000 is no correction) + uint16_t caGreenGamma; //!< Green Gamma correction (range:2500:65000, 10000 is no correction) + uint16_t caBlueGamma; //!< Blue Gamma correction (range:2500:65000, 10000 is no correction) + uint16_t caReferenceBlack; //!< Values less than this are black (range:0:4000) + uint16_t caReferenceWhite; //!< Values more than this are white (range:6000:10000) + int16_t caContrast; //!< Contrast adjustment (range:-100:100, 0 is no correction) + int16_t caBrightness; //!< Brightness adjustment (range:-100:100, 0 is no correction) + int16_t caColorfulness; //!< Colorfulness adjustment (range:-100:100, 0 is no correction) + int16_t caRedGreenTint; //!< Tine adjustment (range:-100:100, 0 is no correction) +} U_COLORADJUSTMENT, *PU_COLORADJUSTMENT; + +/** + \brief For ? (not implemented yet) + Microsoft name: DesignVector Object +*/ +typedef struct { + uint32_t Signature; //!< Must be 0x08007664 (AKA: DV_SGNTR) + U_NUM_FNTAXES NumAxes; //!< Number of elements in Values, 0-16 + U_FNTAXES Values[1]; //!< Optional. Array of font axes for opentype font +} U_DESIGNVECTOR,*PU_DESIGNVECTOR; + +/** + \brief For U_EMR_COMMENT_MULTIFORMATS record, where an array of these is used + Microsoft name: EmrFormat Object +*/ +typedef struct { + uint32_t signature; //!< FormatSignature Enumeration + uint32_t nVersion; //!< Must be 1 if signature is EPS, else ignored + U_CBDATA cbData; //!< Data size in bytes + U_OFFDATA offData; //!< Offset in bytes to the Data from the start of the RECORD +} U_EMRFORMAT, *PU_EMRFORMAT; + +/** + \brief For U_EMR[POLY]EXTTEXTOUT[A|W] emrtext field + Differs from implementation in Mingw and Wine in that the core struct has a fixed size. + Optional and movable components must be handled with offsets. + Microsoft name: EmrText Object + Following invariant core there may/must be: + U_RECTL rcl; (Optional, absent when fOptions & U_ETO_NO_RECT) grayed/clipping/opaque rectangle + + U_OFFDX offDx; (required) but position isn't static. Offset in bytes to the character spacing array measured + from the start of the RECORD, NOT from the start of this structure. + + The order of the next two may be reversed, they are found from their offsets. + + char string (required) String buffer holding nChars (padded to a multiple of 4 bytes in length). + + uint32_t Dx[1] (required) character spacing, array with one entry per glyph. + +*/ +typedef struct { + U_POINTL ptlReference; //!< String start coordinates + U_NUM_STR nChars; //!< Number of characters in the string + U_OFFSTR offString; //!< Offset in bytes to the string from the start of the RECORD + uint32_t fOptions; //!< ExtTextOutOptions Enumeration +} U_EMRTEXT, *PU_EMRTEXT; + +/** + \brief For U_EPS_DATA Points field + Microsoft name: Point28_4 Object +*/ +typedef struct { + U_BITFIX28_4 x; //!< X coordinate + U_BITFIX28_4 y; //!< Y coordinate +} U_POINT28_4, *PU_POINT28_4; + +/** + \brief For embedding EPS in EMF via U_EMRFORMAT offData array in U_EMR_COMMENT_MULTIFORMATS + Microsoft name: EpsData Object +*/ +typedef struct { + uint32_t sizeData; //!< Size in bytes of this object + uint32_t version; //!< Must be 1 + U_POINT28_4 Points[3]; //!< Defines parallelogram, UL, UR, LL corners, LR is derived. + U_RECTL PostScriptData; //!< Record may include optional clipping/opaque rectangle +} U_EPS_DATA, *PU_EPS_DATA; + +/** + \brief For GRADIENT_[TRIANGLE|U_RECT] + Microsoft name: TriVertex Object +*/ +typedef struct { + int32_t x; //!< X coord + int32_t y; //!< Y coord + uint16_t Red; //!< Red component + uint16_t Green; //!< Green component + uint16_t Blue; //!< Bule component + uint16_t Alpha; //!< Alpha Transparency +} U_TRIVERTEX, *PU_TRIVERTEX; + + +/** + \brief For U_EMRGRADIENTFILL GradObj field + + Gradient object notes. The next two structures are used to define the shape with reference to an existing array + of points stored in an array of TriVertex objects in the U_EMRGRADIENTFILL record. The tricky part + is that these two structures are different sizes. In some implementations (MingW) the array is cast to uint32_t + and basically the cast is then ignored. For libUEMF we leave this out of the structure entirely and get to it with offsets. + + Microsoft name: GradientTriangle Object +*/ +typedef struct { + uint32_t Vertex1; //!< Index of Vertex1 in an array of U_TRIVERTEX objects + uint32_t Vertex2; //!< Index of Vertex2 in an array of U_TRIVERTEX objects + uint32_t Vertex3; //!< Index of Vertex3 in an array of U_TRIVERTEX objects +} U_GRADIENT3, *PU_GRADIENT3; + +/** + \brief For U_EMRGRADIENTFILL GradObj field + Microsoft name: GradientRectangle Object +*/ +typedef struct { + uint32_t UpperLeft; //!< Index of UL corner in an array of U_TRIVERTEX objects + uint32_t LowerRight; //!< Index of LR corner in an array of U_TRIVERTEX objects +} U_GRADIENT4, *PU_GRADIENT4; + +/** + \brief For U_EMRCREATEBRUSHINDIRECT lb field + Microsoft name: LogBrushEx Object +*/ +typedef struct { //!< In MS documentation this is LogBrushEx Object + uint32_t lbStyle; //!< LB_Style Enumeration + U_COLORREF lbColor; //!< Brush color + uint32_t lbHatch; //!< HatchStyle Enumeration +} U_LOGBRUSH, *PU_LOGBRUSH; +typedef U_LOGBRUSH U_PATTERN, *PU_PATTERN; + +/** + \brief For U_LOGFONT_PANOSE elfLogFont field + Microsoft name: LogFont Object +*/ +typedef struct { + int32_t lfHeight; //!< Height in Logical units + int32_t lfWidth; //!< Average Width in Logical units + int32_t lfEscapement; //!< Angle in 0.1 degrees betweem escapement vector and X axis + int32_t lfOrientation; //!< Angle in 0.1 degrees between baseline and X axis + int32_t lfWeight; //!< LF_Weight Enumeration + uint8_t lfItalic; //!< LF_Italic Enumeration + uint8_t lfUnderline; //!< LF_Underline Enumeration + uint8_t lfStrikeOut; //!< LF_StrikeOut Enumeration + uint8_t lfCharSet; //!< LF_CharSet Enumeration + uint8_t lfOutPrecision; //!< LF_OutPrecision Enumeration + uint8_t lfClipPrecision; //!< LF_ClipPrecision Enumeration + uint8_t lfQuality; //!< LF_Quality Enumeration + uint8_t lfPitchAndFamily; //!< LF_PitchAndFamily Enumeration + uint16_t lfFaceName[U_LF_FACESIZE]; //!< Name of font. If <U_LF_FACESIZE chars must be null terminated +} U_LOGFONT, *PU_LOGFONT; + +/** + \brief For U_LOGFONT_PANOSE elfPanose field + Microsoft name: Panose Object +*/ +typedef struct { + uint8_t bFamilyType; //!< FamilyType Enumeration + uint8_t bSerifStyle; //!< SerifType Enumeration + uint8_t bWeight; //!< Weight Enumeration + uint8_t bProportion; //!< Proportion Enumeration + uint8_t bContrast; //!< Contrast Enumeration + uint8_t bStrokeVariation; //!< StrokeVariation Enumeration + uint8_t bArmStyle; //!< ArmStyle Enumeration + uint8_t bLetterform; //!< Letterform Enumeration + uint8_t bMidline; //!< Midline Enumeration + uint8_t bXHeight; //!< XHeight Enumeration +} U_PANOSE, *PU_PANOSE; + +#define U_PAN_ALL0 (U_PANOSE){0,0,0,0,0,0,0,0,0,0} // all U_PAN_ANY, have not seen this in an EMF file +#define U_PAN_ALL1 (U_PANOSE){1,1,1,1,1,1,1,1,1,1} // all U_PAN_NO_FIT, this is what createfont() would have made + +// Microsoft name: LogFontEx Object (not implemented) +// Microsoft name: LogFontExDv Object (not implemented) + +/** + \brief For U_EMREXTCREATEFONTINDIRECTW elfw field + Microsoft name: LogFont_Panose Object +*/ +typedef struct { + U_LOGFONT elfLogFont; //!< Basic font attributes + uint16_t elfFullName[U_LF_FULLFACESIZE]; //!< Font full name + uint16_t elfStyle[U_LF_FACESIZE]; //!< Font style (if <U_LF_FACESIZE characters, null terminate string) + uint32_t elfVersion; //!< Ignore + uint32_t elfStyleSize; //!< Font hinting starting at this point size, if 0, starts at Height + uint32_t elfMatch; //!< Ignore + uint32_t elfReserved; //!< Must be 0, Ignore + uint8_t elfVendorId[U_ELF_VENDOR_SIZE]; //!< Ignore + uint32_t elfCulture; //!< Must be 0, Ignore + U_PANOSE elfPanose; //!< Panose Object. If all zero, it is ignored. + uint16_t elfPadding; //!< Ignore +} U_LOGFONT_PANOSE, *PU_LOGFONT_PANOSE; + +/** + \brief For U_LOGPALETTE palPalEntry field(s) + Microsoft name: LogPaletteEntry Object +*/ +typedef struct { + uint8_t peReserved; //!< Ignore + uint8_t peRed; //!< Palette entry Red Intensity + uint8_t peGreen; //!< Palette entry Green Intensity + uint8_t peBlue; //!< Palette entry Blue Intensity +} U_LOGPLTNTRY, *PU_LOGPLTNTRY; + +/** + \brief For U_EMRCREATEPALETTE lgpl field + Microsoft name: LogPalette Object +*/ +typedef struct { + uint16_t palVersion; //!< Must be 0x0300 (AKA: U_LP_VERSION) + uint16_t palNumEntries; //!< Number of U_LOGPLTNTRY objects + U_LOGPLTNTRY palPalEntry[1]; //!< PC_Entry Enumeration +} U_LOGPALETTE, *PU_LOGPALETTE; + +/** + \brief For U_EMRCREATEPEN lopn field + Microsoft name: LogPen Object +*/ +typedef struct { + uint32_t lopnStyle; //!< PenStyle Enumeration + U_POINT lopnWidth; //!< Width of pen set by X, Y is ignored + U_COLORREF lopnColor; //!< Pen color value +} U_LOGPEN, *PU_LOGPEN; + +// Microsoft name: LogPenEx Object (not implemented) + +/** + \brief For U_EMRPIXELFORMAT pfd field + Microsoft name: PixelFormatDescriptor Object +*/ +typedef struct { + uint16_t nSize; //!< Structure size in bytes + uint16_t nVersion; //!< must be 1 + uint32_t dwFlags; //!< PFD_dwFlags Enumeration + uint8_t iPixelType; //!< PFD_iPixelType Enumeration + uint8_t cColorBits; //!< RGBA: total bits per pixel + uint8_t cRedBits; //!< Red bits per pixel + uint8_t cRedShift; //!< Red shift to data bits + uint8_t cGreenBits; //!< Green bits per pixel + uint8_t cGreenShift; //!< Green shift to data bits + uint8_t cBlueBits; //!< Blue bits per pixel + uint8_t cBlueShift; //!< Blue shift to data bits + uint8_t cAlphaBits; //!< Alpha bits per pixel + uint8_t cAlphaShift; //!< Alpha shift to data bits + uint8_t cAccumBits; //!< Accumulator buffer, total bitplanes + uint8_t cAccumRedBits; //!< Red accumulator buffer bitplanes + uint8_t cAccumGreenBits; //!< Green accumulator buffer bitplanes + uint8_t cAccumBlueBits; //!< Blue accumulator buffer bitplanes + uint8_t cAccumAlphaBits; //!< Alpha accumulator buffer bitplanes + uint8_t cDepthBits; //!< Depth of Z-buffer + uint8_t cStencilBits; //!< Depth of stencil buffer + uint8_t cAuxBuffers; //!< Depth of auxilliary buffers (not supported) + uint8_t iLayerType; //!< PFD_iLayerType Enumeration, may be ignored + uint8_t bReserved; //!< Bits 0:3/4:7 are number of Overlay/Underlay planes + uint32_t dwLayerMask; //!< may be ignored + uint32_t dwVisibleMask; //!< color or index of underlay plane + uint32_t dwDamageMask; //!< may be ignored +} U_PIXELFORMATDESCRIPTOR, *PU_PIXELFORMATDESCRIPTOR; + +/** + \brief For U_RGNDATA rdb field + Microsoft name: RegionDataHeader Object (RGNDATAHEADER) +*/ +typedef struct { + uint32_t dwSize; //!< Size in bytes, must be 0x20 (AKA: U_RDH_OBJSIZE) + uint32_t iType; //!< Must be 1 (AKA: U_RDH_RECTANGLES) + U_NUM_RECTL nCount; //!< Number of rectangles in region + uint32_t nRgnSize; //!< Size in bytes of rectangle buffer + U_RECTL rclBounds; //!< Region bounds +} U_RGNDATAHEADER,*PU_RGNDATAHEADER; + +/** + \brief For U_EMRFILLRGN RgnData field(s) + Microsoft name: RegionData Object +*/ +typedef struct { + U_RGNDATAHEADER rdh; //!< Data description + U_RECTL Buffer[1]; //!< Array of U_RECTL elements +} U_RGNDATA,*PU_RGNDATA; + +// Microsoft name: UniversalFontId Object (not implemented) + +/** + \brief For U_EMR[FILLRGN|STRETCHBLT|MASKBLT|PLGBLT] xformSrc field + Microsoft name: Xform Object +*/ +typedef struct { + U_FLOAT eM11; //!< Matrix element M11 + U_FLOAT eM12; //!< Matrix element M12 + U_FLOAT eM21; //!< Matrix element M21 + U_FLOAT eM22; //!< Matrix element M22 + U_FLOAT eDx; //!< X offset in logical units + U_FLOAT eDy; //!< Y offset in logical units +} U_XFORM , *PU_XFORM; + +/** + \brief For U_CIEXYZTRIPLE (all) fields + Microsoft name: CIEXYZ Object +*/ +typedef struct { + int32_t ciexyzX; //!< CIE color space X component + int32_t ciexyzY; //!< CIE color space Y component + int32_t ciexyzZ; //!< CIE color space Z component +} U_CIEXYZ, *PU_CIEXYZ; + +/** + \brief For U_LOGCOLORSPACEA and U_LOGCOLORSPACEW lcsEndpints field + defines a CIE colorspace + Microsoft name: CIEXYZTRIPLE Object +*/ +typedef struct { + U_CIEXYZ ciexyzRed; //!< CIE XYZ coord of red endpoint of colorspace + U_CIEXYZ ciexyzGreen; //!< CIE XYZ coord of green endpoint of colorspace + U_CIEXYZ ciexyzBlue; //!< CIE XYZ coord of blue endpoint of colorspace +} U_CIEXYZTRIPLE, *PU_CIEXYZTRIPLE; + +/** + \brief For U_EMRCREATECOLORSPACE lcs field + Microsoft name: LOGCOLORSPACEA Object +*/ +typedef struct { + uint32_t lcsSignature; //!< must be U_LCS_SIGNATURE + uint32_t lcsVersion; //!< must be U_LCS_VERSION + uint32_t lcsSize; //!< Size in bytes of this structure + int32_t lcsCSType; //!< LCS_CSType Enumeration + int32_t lcsIntent; //!< LCS_Intent Enumeration + U_CIEXYZTRIPLE lcsEndpoints; //!< CIE XYZ color space endpoints + U_LCS_GAMMARGB lcsGammaRGB; //!< Gamma For RGB + char lcsFilename[U_MAX_PATH]; //!< Names an external color profile file, otherwise empty string +} U_LOGCOLORSPACEA, *PU_LOGCOLORSPACEA; + +/** + \brief For U_EMRCREATECOLORSPACEW lcs field + Microsoft name: LOGCOLORSPACEW Object +*/ +typedef struct { + uint32_t lcsSignature; //!< must be U_LCS_SIGNATURE + uint32_t lcsVersion; //!< must be U_LCS_VERSION + uint32_t lcsSize; //!< Size in bytes of this structure + int32_t lcsCSType; //!< lcsCSType Enumeration + int32_t lcsIntent; //!< lcsIntent Enumeration + U_CIEXYZTRIPLE lcsEndpoints; //!< CIE XYZ color space endpoints + U_LCS_GAMMARGB lcsGammaRGB; //!< Gamma For RGB + uint16_t lcsFilename[U_MAX_PATH]; //!< Could name an external color profile file, otherwise empty string +} U_LOGCOLORSPACEW, *PU_LOGCOLORSPACEW; + +/** + \brief For U_EMREXTCREATEPEN lopn field + Microsoft name: EXTLOGPEN Object +*/ +typedef struct { + uint32_t elpPenStyle; //!< PenStyle Enumeration + uint32_t elpWidth; //!< Width in logical units (elpPenStyle & U_PS_GEOMETRIC) or 1 (pixel) + uint32_t elpBrushStyle; //!< LB_Style Enumeration + U_COLORREF elpColor; //!< Pen color + uint32_t elpHatch; //!< HatchStyle Enumeration + U_NUM_STYLEENTRY elpNumEntries; //!< Count of StyleEntry array + U_STYLEENTRY elpStyleEntry[1]; //!< Array of StyleEntry (For user specified dot/dash patterns) +} U_EXTLOGPEN, *PU_EXTLOGPEN; + +/** + \brief For U_BITMAPINFO bmiHeader field + Microsoft name: BITMAPINFOHEADER Object +*/ +typedef struct { + uint32_t biSize; //!< Structure size in bytes + int32_t biWidth; //!< Bitmap width in pixels + int32_t biHeight; //!< Bitmap height in pixels, may be negative. + //!< abs(biHeight) is bitmap height + //!< bitmap may appear in two orientations: + //!< biHeight > 0 origin is LL corner, may be compressed, this is height after decompression. + //!< biHeight < 0 origin is UL corner, may not be compressed + uint16_t biPlanes; //!< Planes (must be 1) + uint16_t biBitCount; //!< BitCount Enumeration (determines number of RBG colors) + uint32_t biCompression; //!< BI_Compression Enumeration + uint32_t biSizeImage; //!< Image size in bytes or 0 = "default size (calculated from geometry?)" + int32_t biXPelsPerMeter; //!< X Resolution in pixels/meter + int32_t biYPelsPerMeter; //!< Y Resolution in pixels/meter + U_NUM_RGBQUAD biClrUsed; //!< Number of bmciColors in U_BITMAPINFO/U_BITMAPCOREINFO that are used by the bitmap + uint32_t biClrImportant; //!< Number of bmciColors needed (0 means all). +} U_BITMAPINFOHEADER, *PU_BITMAPINFOHEADER; + +/** + \brief For U_EMR_* OffBmi* fields + Description of a Bitmap which in some cases is a Device Independent Bitmap (DIB) + Microsoft name: BITMAPINFO Object +*/ +typedef struct { + U_BITMAPINFOHEADER bmiHeader; //!< Geometry and pixel properties + U_RGBQUAD bmiColors[1]; //!< Color table. 24 bit images do not use color table values. +} U_BITMAPINFO, *PU_BITMAPINFO; + +/** + \brief U_EMRALPHABLEND Blend field +*/ +typedef struct { + uint8_t Operation; //!< Must be 0 + uint8_t Flags; //!< Must be 0 + uint8_t Global; //!< Alpha for whole thing if Op is U_AC_SRC_GLOBAL (AKA U_AC_SRC_GLOBAL) + uint8_t Op; //!< Blend Enumeration +} U_BLEND, *PU_BLEND; +# + +/** + General form of an EMF record. + Microsoft name: ENHMETARECORD Object + For generic cast of other U_EMR_* records +*/ +typedef struct { + uint32_t iType; //!< Type of EMR record + uint32_t nSize; //!< Size of entire record in bytes (multiple of 4). + uint32_t dParm[1]; //!< Data in record +} U_ENHMETARECORD, *PU_ENHMETARECORD; + +/** First two fields of all EMF records + For accessing iType and nSize files in all U_EMR* records + Microsoft name: EMR Object +*/ +typedef struct { + uint32_t iType; //!< Type of EMR record + uint32_t nSize; //!< Size of entire record in bytes (multiple of 4). +} U_EMR, *PU_EMR; + +typedef struct { + U_EMR emr; //!< U_EMR + U_PAIR pair; //!< pair of 32 bit values +} U_EMRGENERICPAIR, *PU_EMRGENERICPAIR; + + + + +// *********************************************************************************** +// The following have U_EMR_# records + +/* Index 1 */ +/** + \brief The firstU_ENHMETARECORD record in the metafile. + + Microsoft names instead: Header, HeaderExtension1, and HeaderExtension2 objects. These are + used nowhere else, so they are combined here, along with the first two fields which were not listed in the Header. + + Note also that three fields in this file (nBytes, nRecords, nHandles) must be (re)set after the entire EMF + is constructed, since typically they are not known until then. bOpenGL may or may not be knowable when this + header is written. + + Note also that rclBounds and rclFrame are supposed to be the region bounding the drawn content within the + EMF. This is generally smaller than the size from szlDevice. However, since libUEMF does not actually draw + anything it has no way of knowing what these values are. Instead when it creates a header it sets these to + match the szl* fields. +*/ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_RECTL rclFrame; //!< Bounding rectangle in 0.01 mm units + uint32_t dSignature; //!< FormatSignature Enumeration (must be U_ENHMETA_SIGNATURE) + uint32_t nVersion; //!< Must be U_ENHMETA_VERSION (0x00010000) + uint32_t nBytes; //!< Length in bytes of the Metafile + uint32_t nRecords; //!< Records in the Metafile + uint16_t nHandles; //!< Number of graphics objects used in the Metafile + uint16_t sReserved; //!< Must be 0 + uint32_t nDescription; //!< Characters in the Description field, 0 if no description + uint32_t offDescription; //!< Offset in bytes to Description field + uint32_t nPalEntries; //!< Number of Palette entries (in U_EMR_EOF record). + U_SIZEL szlDevice; //!< Reference device size in pixels + U_SIZEL szlMillimeters; //!< Reference device size in 0.01 mm + /** Fields for winver >= win95 */ + U_CBPXLFMT cbPixelFormat; //!< Size in bytes of PixelFormatDescriptor, 0 if no PFD + U_OFFPXLFMT offPixelFormat; //!< Offset in bytes to PixelFormatDescriptor from the start of the RECORD, 0 if no PFD + uint32_t bOpenGL; //!< nonZero if OpenGL commands are included + /** Fields for winver >= win98 */ + U_SIZEL szlMicrometers; //!< Size of the display device in micrometer + //!< Record may include optional Description, UTF-16BE string + //!< Record may include optional PxlFmtDescriptor, U_PIXELFORMATDESCRIPTOR +} U_EMRHEADER, *PU_EMRHEADER; + +/* Index 2,3,4,5,6*/ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< bounding rectangle in device units + U_NUM_POINTL cptl; //!< Number of points to draw + U_POINTL aptl[1]; //!< array of points +} U_EMRPOLYBEZIER, *PU_EMRPOLYBEZIER, + U_EMRPOLYGON, *PU_EMRPOLYGON, + U_EMRPOLYLINE, *PU_EMRPOLYLINE, + U_EMRPOLYBEZIERTO, *PU_EMRPOLYBEZIERTO, + U_EMRPOLYLINETO, *PU_EMRPOLYLINETO; + +/* Index 7,8 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< bounding rectangle in device units + U_NUM_POLYCOUNTS nPolys; //!< Number of elements in aPolyCounts + U_NUM_POINTL cptl; //!< Total number of points (over all poly) + U_POLYCOUNTS aPolyCounts[1]; //!< Number of points in each poly (sequential) +// This will appear somewhere but is not really part of the core structure. +// U_POINTL aptl[1]; //!< array of points +} U_EMRPOLYPOLYLINE, *PU_EMRPOLYPOLYLINE, + U_EMRPOLYPOLYGON, *PU_EMRPOLYPOLYGON; + +/* Index 9,11 (numbers interleave with next one) */ +typedef struct { + U_EMR emr; //!< U_EMR + U_SIZEL szlExtent; //!< H & V extent in logical units +} U_EMRSETWINDOWEXTEX, *PU_EMRSETWINDOWEXTEX, + U_EMRSETVIEWPORTEXTEX, *PU_EMRSETVIEWPORTEXTEX; + +/* Index 10,12,13 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_POINTL ptlOrigin; //!< H & V origin in logical units +} U_EMRSETWINDOWORGEX, *PU_EMRSETWINDOWORGEX, + U_EMRSETVIEWPORTORGEX, *PU_EMRSETVIEWPORTORGEX, + U_EMRSETBRUSHORGEX, *PU_EMRSETBRUSHORGEX; + +/* Index 14 +*/ +/** +This is a very odd structure because the nSizeLast follows an optional variable size field. Consequently +even though nSizeLast has a name it cannot actually be accessed by it! Following the core appear these fields: + + U_LOGPLTNTRY PalEntries[1]; Record may include optional array of PalEntries + + uint32_t nSizeLast; Mandatory, but position isn't fixed. Must have same value as emr.nSize in header record +*/ +typedef struct { + U_EMR emr; //!< U_EMR + U_CBPLENTRIES cbPalEntries; //!< Number of palette entries + U_OFFPLENTRIES offPalEntries; //!< Offset in bytes to array of palette entries +} U_EMREOF, *PU_EMREOF; + +/* Index 15 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_POINTL ptlPixel; //!< Pixel coordinates (logical) + U_COLORREF crColor; //!< Pixel color +} U_EMRSETPIXELV, *PU_EMRSETPIXELV; + +/* Index 16 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t dwFlags; //!< must be 1 +} U_EMRSETMAPPERFLAGS, *PU_EMRSETMAPPERFLAGS; + +/* Index 17,18,19,20,21,22,67,98,115 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t iMode; //!< enumeration varies with type +} U_EMRSETMAPMODE, *PU_EMRSETMAPMODE, //!< MapMode enumeration + U_EMRSETBKMODE, *PU_EMRSETBKMODE, //!< BackgroundMode Enumeration + U_EMRSETPOLYFILLMODE, *PU_EMRSETPOLYFILLMODE, //!< PolygonFillMode Enumeration + U_EMRSETROP2, *PU_EMRSETROP2, //!< Binary Raster Operation Enumeration + U_EMRSETSTRETCHBLTMODE, *PU_EMRSETSTRETCHBLTMODE, //!< StretchMode Enumeration + U_EMRSETTEXTALIGN, *PU_EMRSETTEXTALIGN, //!< TextAlignment enumeration + U_EMRSELECTCLIPPATH, *PU_EMRSELECTCLIPPATH, //!< RegionMode Enumeration + U_EMRSETICMMODE, *PU_EMRSETICMMODE, //!< ICMMode Enumeration + U_EMRSETLAYOUT, *PU_EMRSETLAYOUT; //!< Mirroring Enumeration + +/* Index 23 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_COLORADJUSTMENT ColorAdjustment; //!< Color Adjustment +} U_EMRSETCOLORADJUSTMENT, *PU_EMRSETCOLORADJUSTMENT; + +/* Index 24, 25 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_COLORREF crColor; //!< Color +} U_EMRSETTEXTCOLOR, *PU_EMRSETTEXTCOLOR, + U_EMRSETBKCOLOR, *PU_EMRSETBKCOLOR; + +/* Index 26 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_POINTL ptlOffset; //!< Clipping region +} U_EMROFFSETCLIPRGN, *PU_EMROFFSETCLIPRGN; + +/* Index 27, 54 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_POINTL ptl; //!< Point coordinates +} U_EMRMOVETOEX, *PU_EMRMOVETOEX, + U_EMRLINETO, *PU_EMRLINETO; + +/* Index 28,33,52,59,60,61,65,66,68 */ +typedef struct { + U_EMR emr; //!< U_EMR +} + U_EMRSETMETARGN, *PU_EMRSETMETARGN, + U_EMRSAVEDC, *PU_EMRSAVEDC, + U_EMRREALIZEPALETTE, *PU_EMRREALIZEPALETTE, + U_EMRBEGINPATH, *PU_EMRBEGINPATH, + U_EMRENDPATH, *PU_EMRENDPATH, + U_EMRCLOSEFIGURE, *PU_EMRCLOSEFIGURE, + U_EMRFLATTENPATH, *PU_EMRFLATTENPATH, + U_EMRWIDENPATH, *PU_EMRWIDENPATH, + U_EMRABORTPATH, *PU_EMRABORTPATH; + +/* Index 29,30 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclClip; //!< Clipping Region +} U_EMREXCLUDECLIPRECT, *PU_EMREXCLUDECLIPRECT, + U_EMRINTERSECTCLIPRECT, *PU_EMRINTERSECTCLIPRECT; + +/* Index 31,32 */ +typedef struct { + U_EMR emr; //!< U_EMR + int32_t xNum; //!< Horizontal multiplier (!=0) + int32_t xDenom; //!< Horizontal divisor (!=0) + int32_t yNum; //!< Vertical multiplier (!=0) + int32_t yDenom; //!< Vertical divisor (!=0) +} U_EMRSCALEVIEWPORTEXTEX, *PU_EMRSCALEVIEWPORTEXTEX, + U_EMRSCALEWINDOWEXTEX, *PU_EMRSCALEWINDOWEXTEX; + +/* Index 33 (see 28) */ + +/* Index 34 */ +typedef struct { + U_EMR emr; //!< U_EMR + int32_t iRelative; //!< DC to restore. -1 is preceding +} U_EMRRESTOREDC, *PU_EMRRESTOREDC; + +/* Index 35 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_XFORM xform; //!< Transform +} U_EMRSETWORLDTRANSFORM, *PU_EMRSETWORLDTRANSFORM; + +/* Index 36 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_XFORM xform; //!< Transform + uint32_t iMode; //!< ModifyWorldTransformMode Enumeration +} U_EMRMODIFYWORLDTRANSFORM, *PU_EMRMODIFYWORLDTRANSFORM; + +/* Index 37,40 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihObject; //!< Number of a stock or created object +} U_EMRDELETEOBJECT, *PU_EMRDELETEOBJECT, + U_EMRSELECTOBJECT, *PU_EMRSELECTOBJECT; + +/* Index 38 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihPen; //!< Index to place object in EMF object table (this entry must not yet exist) + U_LOGPEN lopn; //!< Pen properties +} U_EMRCREATEPEN, *PU_EMRCREATEPEN; + +/* Index 39 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihBrush; //!< Index to place object in EMF object table (this entry must not yet exist) + U_LOGBRUSH lb; //!< Brush properties +} U_EMRCREATEBRUSHINDIRECT, *PU_EMRCREATEBRUSHINDIRECT; + +/* Index 40 see 37 */ + +/* Index 41 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_POINTL ptlCenter; //!< Center in logical units + uint32_t nRadius; //!< Radius in logical units + U_FLOAT eStartAngle; //!< Starting angle in degrees (counter clockwise from x axis) + U_FLOAT eSweepAngle; //!< Sweep angle in degrees +} U_EMRANGLEARC, *PU_EMRANGLEARC; + +/* Index 42,43 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBox; //!< bounding rectangle in logical units +} U_EMRELLIPSE, *PU_EMRELLIPSE, + U_EMRRECTANGLE, *PU_EMRRECTANGLE; + +/* Index 44 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBox; //!< bounding rectangle in logical units + U_SIZEL szlCorner; //!< W & H in logical units of ellipse used to round corner +} U_EMRROUNDRECT, *PU_EMRROUNDRECT; + +/* Index 45, 46 ,47, 55 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBox; //!< bounding rectangle in logical units + U_POINTL ptlStart; //!< Start point in logical units + U_POINTL ptlEnd; //!< End point in logical units +} U_EMRARC, *PU_EMRARC, + U_EMRCHORD, *PU_EMRCHORD, + U_EMRPIE, *PU_EMRPIE, + U_EMRARCTO, *PU_EMRARCTO; + +/* Index 48 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihPal; //!< Index of a Palette object in the EMF object table +} U_EMRSELECTPALETTE, *PU_EMRSELECTPALETTE; + +/* Index 49 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihPal; //!< Index to place object in EMF object table (this entry must not yet exist) + U_LOGPALETTE lgpl; //!< Palette properties +} U_EMRCREATEPALETTE, *PU_EMRCREATEPALETTE; + +/* Index 50 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihPal; //!< Index of a Palette object in the EMF object table + uint32_t iStart; //!< First Palette entry in selected object to set + U_NUM_LOGPLTNTRY cEntries; //!< Number of Palette entries in selected object to set + U_LOGPLTNTRY aPalEntries[1]; //!< Values to set with +} U_EMRSETPALETTEENTRIES, *PU_EMRSETPALETTEENTRIES; + +/* Index 51 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihPal; //!< Index of a Palette object in the EMF object table + uint32_t cEntries; //!< Number to expand or truncate the Palette entry list to. +} U_EMRRESIZEPALETTE, *PU_EMRRESIZEPALETTE; + +/* Index 52 (see 28) */ + +/* Index 53 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_POINTL ptlStart; //!< Start point in logical units + U_COLORREF crColor; //!< Color to fill with + uint32_t iMode; //!< FloodFill Enumeration +} U_EMREXTFLOODFILL, *PU_EMREXTFLOODFILL; + +/* Index 54 (see 27) */ + +/* Index 55 (see 45) */ + +/* Index 56 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_NUM_POINTL cptl; //!< Number of U_POINTL objects + U_POINTL aptl[1]; //!< Array of U_POINTL objects + uint8_t abTypes[1]; //!< Array of Point Enumeration +} U_EMRPOLYDRAW, *PU_EMRPOLYDRAW; + +/* Index 57 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t iArcDirection; //!< ArcDirection Enumeration +} U_EMRSETARCDIRECTION, *PU_EMRSETARCDIRECTION; + +/* Index 58 +IMPORTANT!!!! The Microsoft structure uses a float for the miterlimit but the EMF file record +uses an unsigned int. The latter form is used in this structure. +*/ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t eMiterLimit; //!< Miter limit (max value of mitered length / line width) +} U_EMRSETMITERLIMIT, *PU_EMRSETMITERLIMIT; + +/* Index 59,60,61 (see 28) */ + +/* Index 62,63,64 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units +} U_EMRFILLPATH, *PU_EMRFILLPATH, + U_EMRSTROKEANDFILLPATH, *PU_EMRSTROKEANDFILLPATH, + U_EMRSTROKEPATH, *PU_EMRSTROKEPATH; + +/* Index 65,66 (see 28) */ +/* Index 67 (see 17) */ +/* Index 68 (see 28) */ +/* Index 69 (not a defined U_EMR record type ) */ + +/* Index 70 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_CBDATA cbData; //!< Number of bytes in comment + uint8_t Data[1]; //!< Comment (any binary data, interpretation is program specific) +} U_EMRCOMMENT, *PU_EMRCOMMENT; //!< AKA GDICOMMENT + +/* variant comment types */ +typedef struct { + U_EMR emr; //!< U_EMR + U_CBDATA cbData; //!< Number of bytes in comment + uint32_t cIdent; //!< Comment identifier, must be U_EMR_COMMENT_EMFPLUSRECORD + uint8_t Data[1]; //!< EMF Plus record +} U_EMRCOMMENT_EMFPLUS, *PU_EMRCOMMENT_EMFPLUS; //!< EMF Plus comment + +typedef struct { + U_EMR emr; //!< U_EMR + U_CBDATA cbData; //!< Number of bytes in comment + uint32_t cIdent; //!< Comment identifier, must be U_EMR_COMMENT_SPOOL + uint32_t esrIdent; //!< EMFSpoolRecordIdentifier, may be U_EMR_COMMENT_SPOOLFONTDEF + uint8_t Data[1]; //!< EMF Spool records +} U_EMRCOMMENT_SPOOL, *PU_EMRCOMMENT_SPOOL; //!< EMF Spool comment + +typedef struct { + U_EMR emr; //!< U_EMR + U_CBDATA cbData; //!< Number of bytes in comment + uint32_t cIdent; //!< Comment identifier, must be U_EMR_COMMENT_PUBLIC + uint32_t pcIdent; //!< Public Comment Identifier, from EMRComment Enumeration + uint8_t Data[1]; //!< Public comment data +} U_EMRCOMMENT_PUBLIC, *PU_EMRCOMMENT_PUBLIC; //!< EMF Public comment + +/* Index 71 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_CBRGNDATA cbRgnData; //!< Size in bytes of Region data + uint32_t ihBrush; //!< Index of a Brush object in the EMF object table + U_RGNDATA RgnData[1]; //!< Variable size U_RGNDATA structure +} U_EMRFILLRGN, *PU_EMRFILLRGN; + +/* Index 72 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_CBRGNDATA cbRgnData; //!< Size in bytes of Region data + uint32_t ihBrush; //!< Index of a Brush object in the EMF object table + U_SIZEL szlStroke; //!< W & H of Brush stroke + U_RGNDATA RgnData[1]; //!< Variable size U_RGNDATA structure +} U_EMRFRAMERGN, *PU_EMRFRAMERGN; + +/* Index 73,74 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_CBRGNDATA cbRgnData; //!< Size in bytes of Region data + U_RGNDATA RgnData[1]; //!< Variable size U_RGNDATA structure +} U_EMRINVERTRGN, *PU_EMRINVERTRGN, + U_EMRPAINTRGN, *PU_EMRPAINTRGN; + +/* Index 75 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_CBRGNDATA cbRgnData; //!< Size in bytes of Region data + uint32_t iMode; //!< RegionMode Enumeration + U_RGNDATA RgnData[1]; //!< Variable size U_RGNDATA structure +} U_EMREXTSELECTCLIPRGN, *PU_EMREXTSELECTCLIPRGN; + +/* Index 76 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_POINTL Dest; //!< Destination UL corner in logical units + U_POINTL cDest; //!< Destination width in logical units + uint32_t dwRop; //!< Ternary Raster Operation enumeration + U_POINTL Src; //!< Source retangle UL corner in logical units + U_XFORM xformSrc; //!< Source bitmap transform (world to page coordinates) + U_COLORREF crBkColorSrc; //!< Source bitmap background color + uint32_t iUsageSrc; //!< DIBcolors Enumeration + U_OFFBMISRC offBmiSrc; //!< Offset in bytes to U_BITMAPINFO (within bitmapbuffer) + U_CBBMISRC cbBmiSrc; //!< Size in bytes of U_BITMAPINFO + U_OFFBITSSRC offBitsSrc; //!< Offset in bytes to the bitmap (within bitmapbuffer) + U_CBBITS cbBitsSrc; //!< Size in bytes of bitmap + //!< Record may include optional bitmapbuffer +} U_EMRBITBLT, *PU_EMRBITBLT; + +/* Index 77 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_POINTL Dest; //!< Destination UL corner in logical units + U_POINTL cDest; //!< Destination width in logical units + uint32_t dwRop; //!< Ternary Raster Operation enumeration + U_POINTL Src; //!< Source UL corner in logical units + U_XFORM xformSrc; //!< Transform to apply to source + U_COLORREF crBkColorSrc; //!< Background color + uint32_t iUsageSrc; //!< DIBcolors Enumeration + U_OFFBMISRC offBmiSrc; //!< Offset in bytes to U_BITMAPINFO (within bitmapbuffer) + U_CBBMISRC cbBmiSrc; //!< Size in bytes of U_BITMAPINFO + U_OFFBITSSRC offBitsSrc; //!< Offset in bytes to the bitmap (within bitmapbuffer) + U_CBBITS cbBitsSrc; //!< Size in bytes of bitmap + U_POINTL cSrc; //!< Src W & H in logical units + //!< Record may include optional bitmapbuffer +} U_EMRSTRETCHBLT, *PU_EMRSTRETCHBLT; + +/* Index 78 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_POINTL Dest; //!< Destination UL corner in logical units + U_POINTL cDest; //!< Destination width in logical units + uint32_t dwRop; //!< Ternary Raster Operation enumeration + U_POINTL Src; //!< Source UL corner in logical units + U_XFORM xformSrc; //!< Transform to apply to source + U_COLORREF crBkColorSrc; //!< Background color + uint32_t iUsageSrc; //!< DIBcolors Enumeration + U_OFFBMISRC offBmiSrc; //!< Offset in bytes to U_BITMAPINFO (within srcbitmapbuffer) + U_CBBMISRC cbBmiSrc; //!< Size in bytes of U_BITMAPINFO + U_OFFBITSSRC offBitsSrc; //!< Offset in bytes to the src bitmap (within srcbitmapbuffer) + U_CBBITS cbBitsSrc; //!< Size in bytes of src bitmap + U_POINTL Mask; //!< Mask UL corner in logical units + uint32_t iUsageMask; //!< DIBcolors Enumeration + U_OFFBMIMSK offBmiMask; //!< Offset in bytes to U_BITMAPINFO (within maskbitmapbuffer) + U_CBBMIMSK cbBmiMask; //!< Size in bytes of U_BITMAPINFO + U_OFFBITSMSK offBitsMask; //!< Offset in bytes to the mask bitmap (within maskbitmapbuffer) + U_CBBITSMSK cbBitsMask; //!< Size in bytes of bitmap + //!< Record may include optional Source and mask bitmapbuffers +} U_EMRMASKBLT, *PU_EMRMASKBLT; + +/* Index 79 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_POINTL aptlDst[3]; //!< Defines parallelogram, UL, UR, LL corners, LR is derived. + U_POINTL Src; //!< Source UL corner in logical units + U_POINTL cSrc; //!< Src W & H in logical units + U_XFORM xformSrc; //!< Transform to apply to source + U_COLORREF crBkColorSrc; //!< Background color + uint32_t iUsageSrc; //!< DIBcolors Enumeration + U_OFFBMISRC offBmiSrc; //!< Offset in bytes to U_BITMAPINFO (within srcbitmapbuffer) + U_CBBMISRC cbBmiSrc; //!< Size in bytes of U_BITMAPINFO + U_OFFBITSSRC offBitsSrc; //!< Offset in bytes to the src bitmap (within srcbitmapbuffer) + U_CBBITS cbBitsSrc; //!< Size in bytes of src bitmap + U_POINTL Mask; //!< Mask UL corner in logical units + uint32_t iUsageMask; //!< DIBcolors Enumeration + U_OFFBMIMSK offBmiMask; //!< Offset in bytes to U_BITMAPINFO (within maskbitmapbuffer) + U_CBBMIMSK cbBmiMask; //!< Size in bytes of U_BITMAPINFO + U_OFFBITSMSK offBitsMask; //!< Offset in bytes to the mask bitmap (within maskbitmapbuffer) + U_CBBITSMSK cbBitsMask; //!< Size in bytes of bitmap + //!< Record may include optional Source and mask bitmapbuffers +} U_EMRPLGBLT, *PU_EMRPLGBLT; + +/* Index 80 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_POINTL Dest; //!< Destination UL corner in logical units + U_POINTL Src; //!< Source LL corner in logical units + U_POINTL cSrc; //!< Src W & H in logical units + U_OFFBMISRC offBmiSrc; //!< Offset in bytes to U_BITMAPINFO (within bitmapbuffer) + U_CBBMISRC cbBmiSrc; //!< Size in bytes of U_BITMAPINFO + U_OFFBITSSRC offBitsSrc; //!< Offset in bytes to bitmap + U_CBBITS cbBitsSrc; //!< Size in bytes of bitmap + uint32_t iUsageSrc; //!< DIBColors Enumeration + uint32_t iStartScan; //!< First scan line + uint32_t cScans; //!< Number of scan lines + //!< Record may includes optional bitmapbuffer +} U_EMRSETDIBITSTODEVICE, *PU_EMRSETDIBITSTODEVICE; + +/* Index 81 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_POINTL Dest; //!< Destination UL corner in logical units + U_POINTL Src; //!< Source UL corner in logical units + U_POINTL cSrc; //!< Source W & H in logical units + U_OFFBMISRC offBmiSrc; //!< Offset in bytes to U_BITMAPINFO (within bitmapbuffer) + U_CBBMISRC cbBmiSrc; //!< Size in bytes of U_BITMAPINFO + U_OFFBITSSRC offBitsSrc; //!< Offset in bytes to bitmap + U_CBBITS cbBitsSrc; //!< Size in bytes of bitmap + uint32_t iUsageSrc; //!< DIBColors Enumeration + uint32_t dwRop; //!< Ternary Raster Operation enumeration + U_POINTL cDest; //!< Destination W & H in logical units + //!< Record may includes optional bitmapbuffer +} U_EMRSTRETCHDIBITS, *PU_EMRSTRETCHDIBITS; + +/* Index 82 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihFont; //!< Index of the font in the EMF object table + U_LOGFONT_PANOSE elfw; //!< Font parameters, either U_LOGFONT or U_LOGFONT_PANOSE, the latter is bigger so use that type here +} U_EMREXTCREATEFONTINDIRECTW, *PU_EMREXTCREATEFONTINDIRECTW; + +/* Index 83,84 */ +/** +Variable and optional fields may follow core structure in record: + + U_RECTL rcl; absent when fOptions & U_ETO_NO_RECT) grayed/clipping/opaque rectangle + + U_OFFDX offDx; (required) Offset in bytes to the character spacing array from the start of the RECORD + + uint32_t Dx (optional) character spacing array (Required, but position is not static.) +*/ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + uint32_t iGraphicsMode; //!< GraphicsMode Enumeration + U_FLOAT exScale; //!< scale to 0.01 mm units ( only if iGraphicsMode & U_GM_COMPATIBLE) + U_FLOAT eyScale; //!< scale to 0.01 mm units ( only if iGraphicsMode & U_GM_COMPATIBLE) + U_EMRTEXT emrtext; //!< Text parameters +// U_RECTL rcl; absent when fOptions & U_ETO_NO_RECT) grayed/clipping/opaque rectangle +// U_OFFDX offDx; (required) but position isn't static. Offset in bytes to the character spacing array from the start of the RECORD +// Record may include optional Dx, character spacing, array of uint32_t +} U_EMREXTTEXTOUTA, *PU_EMREXTTEXTOUTA, + U_EMREXTTEXTOUTW, *PU_EMREXTTEXTOUTW; + +/* Index 85,86,87,88,89 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_NUM_POINT16 cpts; //!< Number of POINT16 in array + U_POINT16 apts[1]; //!< Array of POINT16 +} U_EMRPOLYBEZIER16,*PU_EMRPOLYBEZIER16, + U_EMRPOLYGON16,*PU_EMRPOLYGON16, + U_EMRPOLYLINE16,*PU_EMRPOLYLINE16, + U_EMRPOLYBEZIERTO16,*PU_EMRPOLYBEZIERTO16, + U_EMRPOLYLINETO16,*PU_EMRPOLYLINETO16; + +/* Index 90,91 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_NUM_POLYCOUNTS nPolys; //!< Number of elements in aPolyCounts + U_NUM_POINT16 cpts; //!< Total number of points (over all poly) + U_POLYCOUNTS aPolyCounts[1]; //!< Number of points in each poly (sequential) +// This will appear somewhere but is not really part of the core structure. +// U_POINT16 apts[1]; //!< array of point16 +} U_EMRPOLYPOLYLINE16,*PU_EMRPOLYPOLYLINE16, + U_EMRPOLYPOLYGON16,*PU_EMRPOLYPOLYGON16; + +/* Index 92 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_NUM_POINT16 cpts; //!< Total number of points (over all poly) + U_POINT16 apts[1]; //!< array of points + uint8_t abTypes[1]; //!< Array of Point Enumeration +} U_EMRPOLYDRAW16,*PU_EMRPOLYDRAW16; + +/* Index 93 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihBrush; //!< Index to place object in EMF object table (this entry must not yet exist) + uint32_t iUsage; //!< DIBcolors Enumeration + U_OFFBMI offBmi; //!< Offset in bytes to U_BITMAPINFO (within DIBbitmapbuffer) + U_CBBMI cbBmi; //!< Size in bytes of U_BITMAPINFO + U_OFFBITS offBits; //!< Offset in bytes to the DIB bitmap data (within DIBbitmapbuffer + U_CBBITS cbBits; //!< Size in bytes of DIB bitmap + //!< Record may include optional DIB bitmapbuffer +} U_EMRCREATEMONOBRUSH, *PU_EMRCREATEMONOBRUSH; + +/* Index 94 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihBrush; //!< Index to place object in EMF object table (this entry must not yet exist) + uint32_t iUsage; //!< DIBcolors Enumeration + U_OFFBMI offBmi; //!< Offset in bytes to U_BITMAPINFO (within DIB bitmapbuffer) + U_CBBMI cbBmi; //!< Size in bytes of U_BITMAPINFO + U_OFFBITS offBits; //!< Offset in bytes to the DIB bitmap data (within DIB bitmapbuffer + U_CBBITS cbBits; //!< Size in bytes of DIB bitmap + //!< Record may include optional DIB bitmapbuffer +} U_EMRCREATEDIBPATTERNBRUSHPT, *PU_EMRCREATEDIBPATTERNBRUSHPT; + +/* Index 95 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihPen; //!< Index to place object in EMF object table (this entry must not yet exist) + U_OFFBMI offBmi; //!< Offset in bytes to U_BITMAPINFO (within DIB bitmapbuffer) + U_CBBMI cbBmi; //!< Size in bytes of U_BITMAPINFO + U_OFFBITS offBits; //!< Offset in bytes to the DIB bitmap data (within DIB bitmapbuffer + U_CBBITS cbBits; //!< Size in bytes of DIB bitmap + U_EXTLOGPEN elp; //!< Pen parameters (Size is Variable!!!!) + //!< Record may include optional DIB bitmap +} U_EMREXTCREATEPEN, *PU_EMREXTCREATEPEN; + +/* Index 96.97 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + uint32_t iGraphicsMode; //!< GraphicsMode Enumeration + U_FLOAT exScale; //!< scale to 0.01 mm units ( only if iGraphicsMode & U_GM_COMPATIBLE) + U_FLOAT eyScale; //!< scale to 0.01 mm units ( only if iGraphicsMode & U_GM_COMPATIBLE) + U_NUM_EMRTEXT cStrings; //!< Number of U_EMRTEXT in array + U_EMRTEXT emrtext[1]; //!< Text parameters +} U_EMRPOLYTEXTOUTA, *PU_EMRPOLYTEXTOUTA, + U_EMRPOLYTEXTOUTW, *PU_EMRPOLYTEXTOUTW; + +/* Index 98 (see 17) */ + +/* Index 99 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihCS; //!< Index to place object in EMF object table (this entry must not yet exist) + U_LOGCOLORSPACEA lcs; //!< ColorSpace parameters +} U_EMRCREATECOLORSPACE, *PU_EMRCREATECOLORSPACE; + +/* Index 100,101 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihCS; //!< Index of object in EMF object table +} U_EMRDELETECOLORSPACE, *PU_EMRDELETECOLORSPACE, + U_EMRSETCOLORSPACE, *PU_EMRSETCOLORSPACE; + +/* Index 102 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_CBDATA cbData; //!< Size of OpenGL data in bytes + U_DATA Data[1]; //!< OpenGL data +} U_EMRGLSRECORD, *PU_EMRGLSRECORD; + +/* Index 103 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_CBDATA cbData; //!< Size of OpenGL data in bytes + U_DATA Data[1]; //!< OpenGL data +} U_EMRGLSBOUNDEDRECORD, *PU_EMRGLSBOUNDEDRECORD; + +/* Index 104 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_PIXELFORMATDESCRIPTOR pfd; //!< PixelFormatDescriptor +} U_EMRPIXELFORMAT, *PU_EMRPIXELFORMAT; + +/* Index 105 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_CBDATA cjIn; //!< Number of bytes to send to printer driver + U_DATA Data[1]; //!< Data to send +} U_EMRDRAWESCAPE, *PU_EMRDRAWESCAPE; + +/* Index 106 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_CBDATA cjIn; //!< Number of bytes to send to printer driver + U_DATA Data[1]; //!< Data to send +} U_EMREXTESCAPE, *PU_EMREXTESCAPE; + +/* Index 107 (not implemented ) */ + +/* Index 108 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_POINTL Dest; //!< Where to draw the text + U_NUM_STR cChars; //!< Characters in TextString (not null terminated) + uint32_t fuOptions; //!< ExtTextOutOptions Enumeration + uint32_t iGraphicsMode; //!< GraphicsMode Enumeration + U_FLOAT exScale; //!< scale on X axis + U_FLOAT eyScale; //!< scale on Y axis +//!< the tail end of this record is variable. +//!< U_RECTL rclBounds; Record may include optional Bounding rectangle (absent when: fuOPtions & ETO_NO_U_RECT) +//!< uint32_t TextString; text to output (fuOptions & ETO_SMALL_CHARS ? 8 bit : 16 bit) +} U_EMRSMALLTEXTOUT, *PU_EMRSMALLTEXTOUT; + +/* Index 109 (not implemented ) */ + +/* Index 110 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_CBDATA cbDriver; //!< Number of bytes in driver name (note, BYTES, not CHARACTERS) + U_CBDATA cbData; //!< Number of bytes in data + uint16_t Driver[1]; //!< Driver name in uint16_t characters, null terminated + uint8_t Data[1]; //!< Data for printer driver +} U_EMRNAMEDESCAPE, *PU_EMRNAMEDESCAPE; + +/* Index 111-113 (not implemented ) */ + +/* Index 114 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_POINTL Dest; //!< Destination UL corner in logical units + U_POINTL cDest; //!< Destination W & H in logical units + U_BLEND Blend; //!< Blend Function + U_POINTL Src; //!< Source UL corner in logical units + U_XFORM xformSrc; //!< Transform to apply to source + U_COLORREF crBkColorSrc; //!< Background color + uint32_t iUsageSrc; //!< DIBcolors Enumeration + U_OFFBMISRC offBmiSrc; //!< Offset in bytes to U_BITMAPINFO (within bitmapbuffer) + U_CBBMISRC cbBmiSrc; //!< Size in bytes of U_BITMAPINFO + U_OFFBITSSRC offBitsSrc; //!< Offset in bytes to the bitmap (within bitmapbuffer) + U_CBBITS cbBitsSrc; //!< Size in bytes of bitmap + U_POINTL cSrc; //!< Source W & H in logical units + //!< Record may include optional DIB bitmap +} U_EMRALPHABLEND, *PU_EMRALPHABLEND; + +/* Index 115 (see 17) */ + +/* Index 116 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_POINTL Dest; //!< Destination UL corner in logical units + U_POINTL cDest; //!< Destination W & H in logical units + uint32_t TColor; //!< Bitmap color to be treated as transparent + U_POINTL Src; //!< Source UL corner in logical units + U_XFORM xformSrc; //!< Transform to apply to source + U_COLORREF crBkColorSrc; //!< Background color + uint32_t iUsageSrc; //!< DIBcolors Enumeration + U_OFFBMISRC offBmiSrc; //!< Offset in bytes to U_BITMAPINFO (within bitmapbuffer) + U_CBBMISRC cbBmiSrc; //!< Size in bytes of U_BITMAPINFO + U_OFFBITSSRC offBitsSrc; //!< Offset in bytes to the bitmap (within bitmapbuffer) + U_CBBITS cbBitsSrc; //!< Size in bytes of bitmap + U_POINTL cSrc; //!< Source W & H in logical units + //!< Record may includes optional bitmapbuffer +} U_EMRTRANSPARENTBLT, *PU_EMRTRANSPARENTBLT; + +/* Index 117 (not a defined U_EMR record type ) */ + +/* Index 118 */ +typedef struct { + U_EMR emr; //!< U_EMR + U_RECTL rclBounds; //!< Bounding rectangle in device units + U_NUM_TRIVERTEX nTriVert; //!< Number of TriVertex objects + U_NUM_GRADOBJ nGradObj; //!< Number of gradient triangle/rectangle objects + uint32_t ulMode; //!< Gradientfill Enumeration (determines Triangle/Rectangle) +//parts that are required but which are not included in the core structure +// U_TRIVERTEX TriVert[1]; Array of TriVertex objects +// uint32_t GradObj[1]; Array of gradient objects (each has 2 or 3 indices into TriVert array) +} U_EMRGRADIENTFILL, *PU_EMRGRADIENTFILL; + +/* Index 119,120 (not implemented ) */ + +/* Index 121 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t dwAction; //!< ColorSpace Enumeration + uint32_t dwFlags; //!< ColorMatchToTarget Enumeration + U_CBNAME cbName; //!< Number of bytes in in UTF16 name of the color profile + U_CBDATA cbData; //!< Number of bytes of the target profile + uint8_t Data[1]; //!< Data of size cbName+cbData: Name in UTF16 then color profile data +} U_EMRCOLORMATCHTOTARGETW, *PU_EMRCOLORMATCHTOTARGETW; + +/* Index 122 */ +typedef struct { + U_EMR emr; //!< U_EMR + uint32_t ihCS; //!< Index of the logical color space object in the EMF object table + U_LOGCOLORSPACEW lcs; //!< Description of the color profile + uint32_t dwFlags; //!< If low bit set Data is present + U_CBDATA cbData; //!< Number of bytes of theData field. + uint8_t Data[1]; //!< (Optional, dwFlags & 1) color profile data +} U_EMRCREATECOLORSPACEW, *PU_EMRCREATECOLORSPACEW; + +// ************************************************************************************************ +// Utility function structures + +typedef struct { + FILE *fp; //!< Open file + size_t allocated; //!< Size of the buffer + size_t used; //!< Amount consumed + uint32_t records; //!< Number of records already contained + uint16_t ignore; //!< size padding,not used + uint32_t PalEntries; //!< Number of PalEntries (set from U_EMREOF) + uint32_t chunk; //!< Number of bytes to add when more space is needed + char *buf; //!< Buffer for constructing the EMF in memory +} EMFTRACK; + +/** + The various create functions need a place to put their handles, these are stored in the table below. + We don't actually do anything much with these handles, that is up to whatever program finally plays back the EMF, but + we do need to keep track of the numbers so that they are not accidentally reused. This structure is used for that, + and all *_set functions that touch a handle reference it. + + Stock objects are not used in this limited model, so libUEMF cannot detect if a handle is still in use. Nor can it + tell when a handle has been deselected (by selecting another handle for the same type of graphic object, and thus + made deleteable). End user code must keep track of this for itself. +*/ +typedef struct { + uint32_t *table; //!< Array Buffer for constructing the EMF in memory + uint32_t *stack; //!< handles are either on the stack or in the table + size_t allocated; //!< Slots in the buffer + size_t chunk; //!< Number to add if a realloc is required + uint32_t sptr; //!< Pointer to next available handle in the stack + uint32_t top; //!< Highest slot occupied (currently) + uint32_t peak; //!< Highest slot occupied (ever) +} EMFHANDLES; + +/** + 2 x 2 matrix, used by xform_alt_set() function. +*/ +typedef struct { + double M11; //!< Matrix element 1,1 + double M12; //!< Matrix element 1,2 + double M21; //!< Matrix element 2,1 + double M22; //!< Matrix element 2,2 +} U_MAT2X2, *PU_MAT2X2; + +// ************************************************************************************************ +// Prototypes + +int memprobe(const void *buf, size_t size); +void wchar8show(const char *src); +void wchar16show(const uint16_t *src); +void wchar32show(const uint32_t *src); +void wchartshow(const wchar_t *src); +void dumpeht(char *string, unsigned int *handle, EMFHANDLES *eht); + + +char *U_emr_names(unsigned int idx); +uint32_t *dx_set(int32_t height, uint32_t weight, uint32_t members); +uint32_t emr_properties(uint32_t type); +int emr_arc_points(PU_ENHMETARECORD record, int *f1, int f2, PU_PAIRF center, PU_PAIRF start, PU_PAIRF end, PU_PAIRF size); +int emr_arc_points_common(PU_RECTL rclBox, PU_POINTL ArcStart, PU_POINTL ArcEnd, + int *f1, int f2, PU_PAIRF center, PU_PAIRF start, PU_PAIRF end, PU_PAIRF size); +int get_real_color_count(const char *Bmih); +int get_real_color_icount(int Colors, int BitCount, int Width, int Height); +int RGBA_to_DIB(char **px, uint32_t *cbPx, PU_RGBQUAD *ct, int *numCt, + const char *rgba_px, int w, int h, int stride, uint32_t colortype, int use_ct, int invert); +int get_DIB_params( void *pEmr, uint32_t offBitsSrc, uint32_t offBmiSrc, + const char **px, const U_RGBQUAD **ct, uint32_t *numCt, + uint32_t *width, uint32_t *height, uint32_t *colortype, uint32_t *invert ); +int DIB_to_RGBA(const char *px, const U_RGBQUAD *ct, int numCt, + char **rgba_px, int w, int h, uint32_t colortype, int use_ct, int invert); +char *RGBA_to_RGBA(char *rgba_px, int w, int h, int sl, int st, int *ew, int *eh); + +int device_size(const int xmm, const int ymm, const float dpmm, U_SIZEL *szlDev, U_SIZEL *szlMm); +int drawing_size(const int xmm, const int yum, const float dpmm, U_RECTL *rclBounds, U_RECTL *rclFrame); + +int emf_start(const char *name, const uint32_t initsize, const uint32_t chunksize, EMFTRACK **et); +int emf_finish(EMFTRACK *et, EMFHANDLES *eht); +int emf_free(EMFTRACK **et); +int emf_append(U_ENHMETARECORD *rec, EMFTRACK *et, int freerec); +int emf_readdata(const char *filename, char **contents, size_t *length); +FILE *emf_fopen(const char *filename, const int mode); + + +/* use these instead*/ +int emf_htable_create(uint32_t initsize, uint32_t chunksize, EMFHANDLES **eht); +int emf_htable_delete(uint32_t *ih, EMFHANDLES *eht); +int emf_htable_insert(uint32_t *ih, EMFHANDLES *eht); +int emf_htable_free(EMFHANDLES **eht); +/* Deprecated forms */ +#define htable_create emf_htable_create +#define htable_delete emf_htable_delete +#define htable_insert emf_htable_insert +#define htable_free emf_htable_free + +U_RECTL rectl_set(U_POINTL ul, U_POINTL lr); +U_SIZEL sizel_set(int32_t x, int32_t y); +U_POINTL point32_set(int32_t x, int32_t y); +#define point_set point32_set +#define pointl_set point32_set +U_POINT16 point16_set(int16_t x, int16_t y); +U_PANOSE panose_set(uint8_t bFamilyType, uint8_t bSerifStyle, uint8_t bWeight, uint8_t bProportion, + uint8_t bContrast, uint8_t bStrokeVariation, uint8_t bArmStyle, uint8_t bLetterform, + uint8_t bMidline, uint8_t bXHeight ); +U_COLORREF colorref_set(uint8_t red, uint8_t green, uint8_t blue); +U_LOGBRUSH logbrush_set(uint32_t lbStyle, U_COLORREF lbColor, int32_t lbHatch); +U_XFORM xform_set(U_FLOAT eM11, U_FLOAT eM12, U_FLOAT eM21, U_FLOAT eM22, U_FLOAT eDx, U_FLOAT eDy); +U_XFORM xform_alt_set(U_FLOAT scale, U_FLOAT ratio, U_FLOAT rot, U_FLOAT axisrot, U_FLOAT eDx, U_FLOAT eDy); +U_LOGPEN logpen_set( uint32_t lopnStyle, U_POINT lopnWidth, U_COLORREF lopnColor ); +PU_EXTLOGPEN extlogpen_set(uint32_t elpPenStyle, uint32_t elpWidth, uint32_t elpBrushStyle, + U_COLORREF elpColor, int32_t elpHatch, U_NUM_STYLEENTRY elpNumEntries, U_STYLEENTRY *elpStyleEntry ); +U_LOGFONT_PANOSE logfont_panose_set(U_LOGFONT elfLogFont, uint16_t *elfFullName, + uint16_t *elfStyle, uint32_t elfStyleSize, U_PANOSE elfPanose); +U_LOGFONT logfont_set( int32_t lfHeight, int32_t lfWidth, int32_t lfEscapement, int32_t lfOrientation, + int32_t lfWeight, uint8_t lfItalic, uint8_t lfUnderline, uint8_t lfStrikeOut, + uint8_t lfCharSet, uint8_t lfOutPrecision, uint8_t lfClipPrecision, + uint8_t lfQuality, uint8_t lfPitchAndFamily, uint16_t *lfFaceName); +char *emrtext_set(U_POINTL ptlReference, U_NUM_STR NumString, uint32_t cbChar, void *String, uint32_t fOptions, U_RECTL rcl, uint32_t *Dx); +U_LOGPLTNTRY logpltntry_set(uint8_t peReserved,uint8_t peRed,uint8_t peGreen,uint8_t peBlue); +PU_LOGPALETTE logpalette_set(U_NUM_LOGPLTNTRY palNumEntries,PU_LOGPLTNTRY *palPalEntry); +U_RGNDATAHEADER rgndataheader_set( U_NUM_RECTL nCount, U_RECTL rcBound); +PU_RGNDATA rgndata_set( U_RGNDATAHEADER rdh, PU_RECTL Buffer); +U_BITMAPINFOHEADER bitmapinfoheader_set(int32_t biWidth, int32_t biHeight, + uint16_t biPlanes, uint16_t biBitCount, uint32_t biCompression, + uint32_t biSizeImage, int32_t biXPelsPerMeter, + int32_t biYPelsPerMeter, U_NUM_RGBQUAD biClrUsed, uint32_t biClrImportant); +PU_BITMAPINFO bitmapinfo_set(U_BITMAPINFOHEADER BmiHeader, PU_RGBQUAD BmiColors); +U_LOGCOLORSPACEA logcolorspacea_set(int32_t lcsCSType, int32_t lcsIntent, + U_CIEXYZTRIPLE lcsEndpoints, U_LCS_GAMMARGB lcsGammaRGB, char *lcsFilename); +U_LOGCOLORSPACEW logcolorspacew_set(int32_t lcsCSType, int32_t lcsIntent, + U_CIEXYZTRIPLE lcsEndpoints, U_LCS_GAMMARGB lcsGammaRGB, uint16_t *lcsFilename); +U_COLORADJUSTMENT coloradjustment_set(uint16_t Size, uint16_t Flags, uint16_t IlluminantIndex, + uint16_t RedGamma, uint16_t GreenGamma, uint16_t BlueGamma, + uint16_t ReferenceBlack, uint16_t ReferenceWhite, + int16_t Contrast, int16_t Brightness, int16_t Colorfulness, int16_t RedGreenTint); +U_PIXELFORMATDESCRIPTOR pixelformatdescriptor_set( uint32_t dwFlags, uint8_t iPixelType, uint8_t cColorBits, + uint8_t cRedBits, uint8_t cRedShift, + uint8_t cGreenBits, uint8_t cGreenShift, + uint8_t cBlueBits, uint8_t cBlueShift, + uint8_t cAlphaBits, uint8_t cAlphaShift, + uint8_t cAccumBits, uint8_t cAccumRedBits, uint8_t cAccumGreenBits, uint8_t cAccumBlueBits, + uint8_t cAccumAlphaBits, uint8_t cDepthBits, uint8_t cStencilBits, + uint8_t cAuxBuffers, uint8_t iLayerType, uint8_t bReserved, uint32_t dwLayerMask, + uint32_t dwVisibleMask, uint32_t dwDamageMask); + +PU_POINT points_transform(PU_POINT points, int count, U_XFORM xform); +PU_POINT16 point16_transform(PU_POINT16 points, int count, U_XFORM xform); +PU_TRIVERTEX trivertex_transform(PU_TRIVERTEX tv, int count, U_XFORM xform); +PU_POINT point16_to_point(PU_POINT16 points, int count); +PU_POINT16 point_to_point16(PU_POINT points, int count); + +U_RECT findbounds(uint32_t count, PU_POINT pts, uint32_t width); +U_RECT findbounds16(uint32_t count, PU_POINT16 pts, uint32_t width); +char *emr_dup(const char *emr); + +char *textcomment_set(const char *string); + +// These generate the handle and then call the underlying function +char *deleteobject_set(uint32_t *ihObject, EMFHANDLES *eht); +char *selectobject_set(uint32_t ihObject, EMFHANDLES *eht); +char *createpen_set(uint32_t *ihPen, EMFHANDLES *eht, U_LOGPEN lopn ); +char *extcreatepen_set(uint32_t *ihPen, EMFHANDLES *eht, + PU_BITMAPINFO Bmi, const uint32_t cbPx, char *Px, PU_EXTLOGPEN elp); +char *createbrushindirect_set(uint32_t *ihBrush, EMFHANDLES *eht, U_LOGBRUSH lb ); +char *createdibpatternbrushpt_set(uint32_t *ihBrush, EMFHANDLES *eht, uint32_t iUsage, + PU_BITMAPINFO Bmi, const uint32_t cbPx, const char *Px); +char *createmonobrush_set(uint32_t *ihBrush, EMFHANDLES *eht, uint32_t iUsage, + PU_BITMAPINFO Bmi, const uint32_t cbPx, const char *Px); +char *extcreatefontindirectw_set(uint32_t *ihFont, EMFHANDLES *eht, const char *elf, const char *elfw); +char *createpalette_set(uint32_t *ihPal, EMFHANDLES *eht, U_LOGPALETTE lgpl); +char *setpaletteentries_set(uint32_t *ihPal, EMFHANDLES *eht, uint32_t iStart, U_NUM_LOGPLTNTRY cEntries, PU_LOGPLTNTRY aPalEntries); +char *fillrgn_set(uint32_t *ihBrush, EMFHANDLES *eht, U_RECTL rclBounds,PU_RGNDATA RgnData); +char *framergn_set(uint32_t *ihBrush, EMFHANDLES *eht, U_RECTL rclBounds, U_SIZEL szlStroke, PU_RGNDATA RgnData); +char *createcolorspace_set(uint32_t *ihCS, EMFHANDLES *eht, U_LOGCOLORSPACEA lcs); +char *createcolorspacew_set(uint32_t *ihCS, EMFHANDLES *eht, U_LOGCOLORSPACEW lcs, uint32_t dwFlags, U_CBDATA cbData, uint8_t *Data); + +char *U_EMRHEADER_set( const U_RECTL rclBounds, const U_RECTL rclFrame, U_PIXELFORMATDESCRIPTOR* const pfmtDesc, + U_CBSTR nDesc, uint16_t* const Description, const U_SIZEL szlDevice, const U_SIZEL szlMillimeters, + const uint32_t bOpenGL); +char *U_EMRPOLYBEZIER_set( const U_RECTL rclBounds, const uint32_t count, const U_POINTL *points); +char *U_EMRPOLYGON_set( const U_RECTL rclBounds, const uint32_t count, const U_POINTL *points); +char *U_EMRPOLYLINE_set( const U_RECTL rclBounds, const uint32_t count, const U_POINTL *points); +char *U_EMRPOLYBEZIERTO_set(const U_RECTL rclBounds, const uint32_t count, const U_POINTL *points); +char *U_EMRPOLYLINETO_set( const U_RECTL rclBounds, const uint32_t count, const U_POINTL *points); + +char *U_EMRPOLYPOLYLINE_set(const U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts, + const uint32_t cptl, const U_POINTL *points); +char *U_EMRPOLYPOLYGON_set(const U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts, + const uint32_t cptl, const U_POINTL *points); +char *U_EMRSETWINDOWEXTEX_set(const U_SIZEL szlExtent); +char *U_EMRSETWINDOWORGEX_set(const U_POINTL ptlOrigin); +char *U_EMRSETVIEWPORTEXTEX_set(const U_SIZEL szlExtent); +char *U_EMRSETVIEWPORTORGEX_set(const U_POINTL ptlOrigin); +char *U_EMRSETBRUSHORGEX_set(const U_POINTL ptlOrigin); +char *U_EMREOF_set(const U_CBPLENTRIES cbPalEntries, const PU_LOGPLTNTRY PalEntries, EMFTRACK *et); +char *U_EMRSETPIXELV_set(const U_POINTL ptlPixel, const U_COLORREF crColor); +char *U_EMRSETMAPPERFLAGS_set(void); +char *U_EMRSETMAPMODE_set(const uint32_t iMode); +char *U_EMRSETBKMODE_set(const uint32_t iMode); +char *U_EMRSETPOLYFILLMODE_set(const uint32_t iMode); +char *U_EMRSETROP2_set(const uint32_t iMode); +char *U_EMRSETSTRETCHBLTMODE_set(const uint32_t iMode); +char *U_EMRSETTEXTALIGN_set(const uint32_t iMode); +char *U_EMRSETCOLORADJUSTMENT_set(const U_COLORADJUSTMENT ColorAdjustment); +char *U_EMRSETTEXTCOLOR_set(const U_COLORREF crColor); +char *U_EMRSETBKCOLOR_set(const U_COLORREF crColor); +char *U_EMROFFSETCLIPRGN_set(const U_POINTL ptl); +char *U_EMRMOVETOEX_set(const U_POINTL ptl); +char *U_EMRSETMETARGN_set(void); +char *U_EMREXCLUDECLIPRECT_set(const U_RECTL rclClip); +char *U_EMRINTERSECTCLIPRECT_set(const U_RECTL rclClip); +char *U_EMRSCALEVIEWPORTEXTEX_set(const int32_t xNum, const int32_t xDenom, const int32_t yNum, const int32_t yDenom); +char *U_EMRSCALEWINDOWEXTEX_set(const int32_t xNum, const int32_t xDenom, const int32_t yNum, const int32_t yDenom); +char *U_EMRSAVEDC_set(void); +char *U_EMRRESTOREDC_set(const int32_t iRelative); +char *U_EMRSETWORLDTRANSFORM_set(const U_XFORM xform); +char *U_EMRMODIFYWORLDTRANSFORM_set(const U_XFORM xform, const uint32_t iMode); +char *U_EMRSELECTOBJECT_set(const uint32_t ihObject); // better to call selectobject_set() +char *U_EMRCREATEPEN_set(const uint32_t ihPen, const U_LOGPEN lopn ); +char *U_EMRCREATEBRUSHINDIRECT_set(const uint32_t ihBrush, const U_LOGBRUSH lb); +char *U_EMRDELETEOBJECT_set(const uint32_t ihObject); // better to call deleteobject_set() +char *U_EMRANGLEARC_set(const U_POINTL ptlCenter, const uint32_t nRadius, const U_FLOAT eStartAngle, const U_FLOAT eSweepAngle); +char *U_EMRELLIPSE_set(const U_RECTL rclBox); +char *U_EMRRECTANGLE_set(const U_RECTL rclBox); +char *U_EMRROUNDRECT_set(const U_RECTL rclBox, const U_SIZEL szlCorner); +char *U_EMRARC_set(const U_RECTL rclBox, const U_POINTL ptlStart, const U_POINTL ptlEnd); +char *U_EMRCHORD_set(const U_RECTL rclBox, const U_POINTL ptlStart, const U_POINTL ptlEnd); +char *U_EMRPIE_set(const U_RECTL rclBox, const U_POINTL ptlStart, const U_POINTL ptlEnd); +char *U_EMRSELECTPALETTE_set(const uint32_t ihPal); +char *U_EMRCREATEPALETTE_set(const uint32_t ihPal, const U_LOGPALETTE lgpl); +char *U_EMRSETPALETTEENTRIES_set(const uint32_t ihPal, const uint32_t iStart, const U_NUM_LOGPLTNTRY cEntries, const PU_LOGPLTNTRY aPalEntries); +char *U_EMRRESIZEPALETTE_set(const uint32_t ihPal, const uint32_t cEntries); +char *U_EMRREALIZEPALETTE_set(void); +char *U_EMREXTFLOODFILL_set(const U_POINTL ptlStart, const U_COLORREF crColor, const uint32_t iMode); +char *U_EMRLINETO_set(const U_POINTL ptl); +char *U_EMRARCTO_set(const U_RECTL rclBox, const U_POINTL ptlStart, const U_POINTL ptlEnd); +char *U_EMRPOLYDRAW_set(const U_RECTL rclBounds,const U_NUM_POINTL cptl,const U_POINTL *aptl,const uint8_t *abTypes); +char *U_EMRSETARCDIRECTION_set(const uint32_t iArcDirection); +char *U_EMRSETMITERLIMIT_set(const uint32_t eMiterLimit); +char *U_EMRBEGINPATH_set(void); +char *U_EMRENDPATH_set(void); +char *U_EMRCLOSEFIGURE_set(void); +char *U_EMRFILLPATH_set(const U_RECTL rclBox); +char *U_EMRSTROKEANDFILLPATH_set(const U_RECTL rclBox); +char *U_EMRSTROKEPATH_set(const U_RECTL rclBox); +char *U_EMRFLATTENPATH_set(void); +char *U_EMRWIDENPATH_set(void); +char *U_EMRSELECTCLIPPATH_set(const uint32_t iMode); +char *U_EMRABORTPATH_set(void); +// EMR_ENDEF69 +char *U_EMRCOMMENT_set(const U_CBDATA cbData, const char *Data); +char *U_EMRFILLRGN_set(const U_RECTL rclBounds, const uint32_t ihBrush, const PU_RGNDATA RgnData); +char *U_EMRFRAMERGN_set(const U_RECTL rclBounds, const uint32_t ihBrush, const U_SIZEL szlStroke, const PU_RGNDATA RgnData); +char *U_EMRINVERTRGN_set(const PU_RGNDATA RgnData); +char *U_EMRPAINTRGN_set(const PU_RGNDATA RgnData); +char *U_EMREXTSELECTCLIPRGN_set(const uint32_t iMode, const PU_RGNDATA RgnData); +char *U_EMRBITBLT_set(const U_RECTL rclBounds, const U_POINTL Dest, const U_POINTL cDest, + const U_POINTL Src, const U_XFORM xformSrc, const U_COLORREF crBkColorSrc, + const uint32_t iUsageSrc, const uint32_t dwRop, + const PU_BITMAPINFO Bmi, const uint32_t cbPx, char *Px); +char *U_EMRSTRETCHBLT_set(U_RECTL rclBounds, U_POINTL Dest, U_POINTL cDest, + const U_POINTL Src, const U_POINTL cSrc, const U_XFORM xformSrc, const U_COLORREF crBkColorSrc, const uint32_t iUsageSrc, + const uint32_t dwRop, + const PU_BITMAPINFO Bmi, const uint32_t cbPx, char *Px); +char *U_EMRMASKBLT_set(U_RECTL rclBounds, U_POINTL Dest, U_POINTL cDest, + const U_POINTL Src, const U_XFORM xformSrc, const U_COLORREF crBkColorSrc, const uint32_t iUsageSrc, + const U_POINTL Mask, const uint32_t iUsageMask, + const uint32_t dwRop, + const PU_BITMAPINFO Bmi, const uint32_t cbPx, char *Px, + const PU_BITMAPINFO BmiMsk, const uint32_t cbMsk, char *Msk); +char *U_EMRPLGBLT_set(const U_RECTL rclBounds, const PU_POINTL aptlDst, + const U_POINTL Src, const U_POINTL cSrc, const U_XFORM xformSrc, const U_COLORREF crBkColorSrc, const uint32_t iUsageSrc, + const U_POINTL Mask, const uint32_t iUsageMask, + const PU_BITMAPINFO Bmi, const uint32_t cbPx, char *Px, + const PU_BITMAPINFO BmiMsk, const uint32_t cbMsk, char *Msk); +char *U_EMRSETDIBITSTODEVICE_set(const U_RECTL rclBounds, const U_POINTL Dest, + const U_POINTL Src, const U_POINTL cSrc, const uint32_t iUsageSrc, + const uint32_t iStartScan, const uint32_t cScans, + const PU_BITMAPINFO Bmi, const uint32_t cbPx, char *Px); +char *U_EMRSTRETCHDIBITS_set(const U_RECTL rclBounds, const U_POINTL Dest, const U_POINTL cDest, + const U_POINTL Src, const U_POINTL cSrc, const uint32_t iUsageSrc, + const uint32_t dwRop, + const PU_BITMAPINFO Bmi, const uint32_t cbPx, char *Px); +char *U_EMREXTCREATEFONTINDIRECTW_set( uint32_t ihFont, const char *elf, const char *elfw); +char *U_EMREXTTEXTOUTA_set(U_RECTL rclBounds, uint32_t iGraphicsMode, U_FLOAT exScale, U_FLOAT eyScale, PU_EMRTEXT emrtext); +char *U_EMREXTTEXTOUTW_set(U_RECTL rclBounds, uint32_t iGraphicsMode, U_FLOAT exScale, U_FLOAT eyScale, PU_EMRTEXT emrtext); +char *U_EMRPOLYBEZIER16_set(const U_RECTL rclBounds, const uint32_t cpts, const U_POINT16 *points); +char *U_EMRPOLYGON16_set(const U_RECTL rclBounds, const uint32_t cpts, const U_POINT16 *points); +char *U_EMRPOLYLINE16_set(const U_RECTL rclBounds, const uint32_t cpts, const U_POINT16 *points); +char *U_EMRPOLYBEZIERTO16_set(const U_RECTL rclBounds, const uint32_t cpts, const U_POINT16 *points); +char *U_EMRPOLYLINETO16_set(const U_RECTL rclBounds, const uint32_t cpts, const U_POINT16 *points); +char *U_EMRPOLYPOLYLINE16_set(const U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts,const uint32_t cpts, const U_POINT16 *points); +char *U_EMRPOLYPOLYGON16_set(const U_RECTL rclBounds, const uint32_t nPolys, const uint32_t *aPolyCounts,const uint32_t cpts, const U_POINT16 *points); +char *U_EMRPOLYDRAW16_set(const U_RECTL rclBounds,const U_NUM_POINT16 cpts, const U_POINT16 *aptl, const uint8_t *abTypes); +char *U_EMRCREATEMONOBRUSH_set(const uint32_t ihBrush, const uint32_t iUsage, + const PU_BITMAPINFO Bmi, const uint32_t cbPx, const char *Px); +char *U_EMRCREATEDIBPATTERNBRUSHPT_set(const uint32_t ihBrush, const uint32_t iUsage, + const PU_BITMAPINFO Bmi, const uint32_t cbPx, const char *Px); +char *U_EMREXTCREATEPEN_set(const uint32_t ihPen, const PU_BITMAPINFO Bmi, const uint32_t cbPx, char *Px, const PU_EXTLOGPEN elp ); +// U_EMRPOLYTEXTOUTA_set 96 NOT IMPLEMENTED, denigrated after Windows NT +// U_EMRPOLYTEXTOUTW_set 97 NOT IMPLEMENTED, denigrated after Windows NT +char *U_EMRSETICMMODE_set(const uint32_t iMode); +char *U_EMRCREATECOLORSPACE_set(const uint32_t ihCS, const U_LOGCOLORSPACEA lcs); +char *U_EMRSETCOLORSPACE_set(const uint32_t ihCS ); +char *U_EMRDELETECOLORSPACE_set(const uint32_t ihCS); +// U_EMRGLSRECORD_set 102 Not implemented +// U_EMRGLSBOUNDEDRECORD_set 103 Not implemented +char *U_EMRPIXELFORMAT_set(const U_PIXELFORMATDESCRIPTOR pfd); +char *U_EMRSMALLTEXTOUT_set(const U_POINTL Dest, const U_NUM_STR cChars, const uint32_t fuOptions, const uint32_t iGraphicsMode, + const U_FLOAT exScale, const U_FLOAT eyScale, const U_RECTL rclBounds, const char *TextString); +// U_EMRDRAWESCAPE_set 105 Not implemented +// U_EMREXTESCAPE_set 106 Not implemented +// U_EMRUNDEF107_set 107 Not implemented +// U_EMRSMALLTEXTOUT_set 108 +// U_EMRFORCEUFIMAPPING_set 109 Not implemented +// U_EMRNAMEDESCAPE_set 110 Not implemented +// U_EMRCOLORCORRECTPALETTE_set 111 Not implemented +// U_EMRSETICMPROFILEA_set 112 Not implemented +// U_EMRSETICMPROFILEW_set 113 Not implemented +char *U_EMRALPHABLEND_set(const U_RECTL rclBounds, const U_POINTL Dest, const U_POINTL cDest, + const U_POINTL Src, const U_POINTL cSrc, const U_XFORM xformSrc, + const U_COLORREF crBkColorSrc, const uint32_t iUsageSrc, + const U_BLEND Blend, + const PU_BITMAPINFO Bmi, const uint32_t cbPx, char *Px); +char *U_EMRSETLAYOUT_set(const uint32_t iMode); +char *U_EMRTRANSPARENTBLT_set(const U_RECTL rclBounds, const U_POINTL Dest, const U_POINTL cDest, + const U_POINTL Src, const U_POINTL cSrc, const U_XFORM xformSrc, + const U_COLORREF crBkColorSrc, const uint32_t iUsageSrc, const uint32_t TColor, + const PU_BITMAPINFO Bmi, const uint32_t cbPx, char *Px); +// U_EMRUNDEF117_set 117 Not implemented +char *U_EMRGRADIENTFILL_set(const U_RECTL rclBounds, const U_NUM_TRIVERTEX nTriVert, const U_NUM_GRADOBJ nGradObj, + const uint32_t ulMode, const PU_TRIVERTEX TriVert, const uint32_t *GradObj ); +// U_EMRSETLINKEDUFIS_set 119 Not implemented +// U_EMRSETTEXTJUSTIFICATION_set 120 Not implemented (denigrated) +// U_EMRCOLORMATCHTOTARGETW_set 121 Not implemented +char *U_EMRCREATECOLORSPACEW_set(const uint32_t ihCS, const U_LOGCOLORSPACEW lcs, const uint32_t dwFlags, + const U_CBDATA cbData, const uint8_t *Data); + +#ifdef __cplusplus +} +#endif + +#endif /* _UEMF_ */ diff --git a/src/libuemf/uemf_endian.c b/src/libuemf/uemf_endian.c new file mode 100644 index 000000000..cdae07a3d --- /dev/null +++ b/src/libuemf/uemf_endian.c @@ -0,0 +1,1783 @@ +/** + @file uemf_endian.c Functions for converting EMF records between Big Endian and Little Endian + + EMF files use Little Endian order. + On a Big Endian machine the data must be converted to/from Little Endian when it is writen to/read from a file. + On a Little Endian machine no conversion is required, but it is good to be able to test the routines on either platform. + When "torev" is true these routines convert from the native byte order to the reverse. + When "torev" is false these routines convert from the reverse byte order to the native. + Routines that do not use that variable swap byte order, and the way in which they do so does not depend + on the native byte order. + + The only function here which should be called directly is U_emf_endian(), and then,except for testing purposes, only on a BE machine. + + Many variables are initialized to zero even though they will always be set because + some versions of gcc give spurious "may be used uninitialized" warnings otherwise. +*/ + +/* +File: uemf_endian.h +Version: 0.0.12 +Date: 14-FEB-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "uemf.h" +#include "uemf_endian.h" + +// hide almost everuything in here from Doxygen +//! @cond + +/* ********************************************************************************************** + These functions convert standard objects used in the EMR records. +*********************************************************************************************** */ + +void U_swap2(void *ul, unsigned int count){ + uint8_t ctmp; + uint8_t *cl = (uint8_t *) ul; + for(; count; count--,cl+=2){ + ctmp = *cl; + *cl = *(cl+1); + *(cl+1) = ctmp; + } +} + +/* Note: U_swap4 is also used by uwmf_endian.c, in cases where the 32 bit data is not aligned on a 4 byte boundary */ +void U_swap4(void *ul, unsigned int count){ + uint8_t ctmp; + uint8_t *cl = (uint8_t *) ul; + for(; count; count--,cl+=4){ + ctmp = *(cl+0); + *(cl+0) = *(cl+3); + *(cl+3) = ctmp; + ctmp = *(cl+1); + *(cl+1) = *(cl+2); + *(cl+2) = ctmp; + } +} + +/** + U_COLORREF and U_RGBQUAD do NOT need to be swapped, they are always stored in memory in the proper order. +*/ + +/** + \brief Convert rect and rectl objects from Upper Left and Lower Right corner points. + \param rect U_RECTL object + \param count number to convert +*/ +void rectl_swap( + PU_RECTL rect, + unsigned int count + ){ + U_swap4(rect,4*count); +} + +/** + \brief Convert a U_SIZEL object. + \param sz U_SizeL object + \param count number to convert +*/ +void sizel_swap( + PU_SIZEL sz, + unsigned int count + ){ + U_swap4(sz,2*count); +} + +/** + \brief Convert a U_POINTL object + \param pt U_POINTL object + \param count number to convert +*/ +void pointl_swap( + PU_POINTL pt, + unsigned int count + ){ + U_swap4(pt,2*count); +} + +/** + \brief Convert a U_POINT16 object + \param pt U_POINT16 object + \param count number to convert +*/ +void point16_swap( + PU_POINT16 pt, + unsigned int count + ){ + U_swap2(pt,2*count); +} + + + +/** + \brief Convert a U_TRIVERTEX object. + \param tv U_TRIVERTEX object. + \param count number to convert +*/ +void trivertex_swap( + PU_TRIVERTEX tv, + unsigned int count + ){ + for(;count; count--, tv++){ + U_swap4(tv,2); /* x,y */ + U_swap2(&(tv->Red),4); /* Red, Green, Blue, Alpha */ + } +} + +/** + \brief Convert a U_GRADIENT3 object. + \param tv U_GRADIENT3 object. + \param count number to convert +*/ +void gradient3_swap( + PU_GRADIENT3 g3, + unsigned int count + ){ + U_swap4(g3,3*count); +} + +/** + \brief Convert a U_GRADIENT4 object. + \param tv U_GRADIENT4 object. + \param count number to convert +*/ +void gradient4_swap( + PU_GRADIENT4 g4, + unsigned int count + ){ + U_swap4(g4,2*count); //a gradient4 object has 2 int4's, NOT 4! +} + +/** + \brief Convert a U_LOGBRUSH object. + \param lb U_LOGBRUSH object. +*/ +void logbrush_swap( + PU_LOGBRUSH lb + ){ + U_swap4(&(lb->lbStyle),1); // lbStyle + // ordered bytes: lbColor + U_swap4(&(lb->lbHatch),1); // lbHatch +} + +/** + \brief Convert a U_XFORM object. + \param xform U_XFORM object +*/ +void xform_swap( + PU_XFORM xform + ){ + U_swap4(xform,6); +} + + +/** + \brief Convert a U_CIEXYZTRIPLE object + \param cie3 U_CIEXYZTRIPLE object +*/ +void ciexyztriple_swap( + PU_CIEXYZTRIPLE cie3 + ){ + U_swap4(cie3,9); +} +/** + \brief Convert a U_LOGCOLORSPACEA object. + \param lcsa U_LOGCOLORSPACEA object +*/ +void logcolorspacea_swap( + PU_LOGCOLORSPACEA lcsa + ){ + U_swap4(lcsa,5); // lcsSignature lcsVersion lcsSize lcsCSType lcsIntent + ciexyztriple_swap(&(lcsa->lcsEndpoints)); + // ordered bytes: lcsGammaRGB + // ordered bytes: lcsFilename +} + +/** + + \brief Convert a U_LOGCOLORSPACEW object. + \param lcsa U_LOGCOLORSPACEW object +*/ +void logcolorspacew_swap( + PU_LOGCOLORSPACEW lcsa + ){ + U_swap4(lcsa,5); // lcsSignature lcsVersion lcsSize lcsCSType lcsIntent + ciexyztriple_swap(&(lcsa->lcsEndpoints)); + // ordered bytes: lcsGammaRGB + // UTF-16LE, already in order: lcsFilename +} + + +/** + \brief Convert a U_LOGFONT object. + \param lf U_LOGFONT object +*/ +void logfont_swap( + PU_LOGFONT lf + ){ + U_swap4(lf,5); // lfHeight lfWidth lfEscapement lfOrientation lfWeight + // ordered bytes: lfItalic lfUnderline lfStrikeOut lfCharSet lfOutPrecision lfClipPrecision lfQuality lfPitchAndFamily + // UTF16-LE, already in order +} + +/** + \brief Convert a U_LOGFONT_PANOSE object. + \return U_LOGFONT_PANOSE object +*/ +void logfont_panose_swap( + PU_LOGFONT_PANOSE lfp + ){ + logfont_swap(&(lfp->elfLogFont)); // elfLogFont + // UTF-16LE, already in order: elfFullName + // UTF-16LE, already in order: elfStyle + U_swap4(&(lfp->elfVersion),4); // elfVersion elfStyleSize elfMatch elfReserved + // ordered bytes: elfVendorId + U_swap4(&(lfp->elfCulture),1); // elfCulture + // ordered bytes: elfPanose +} + +/** + \brief Convert a U_BITMAPINFOHEADER object. + \param Bmi U_BITMAPINFOHEADER object +*/ +void bitmapinfoheader_swap( + PU_BITMAPINFOHEADER Bmi + ){ + U_swap4(Bmi,3); // biSize biWidth biHeight + U_swap2(&(Bmi->biPlanes),2); // biPlanes biBitCount + U_swap4(&(Bmi->biCompression),6); // biCompression biSizeImage biXPelsPerMeter biYPelsPerMeter biClrUsed biClrImportant +} + + +/** + \brief Convert a Pointer to a U_BITMAPINFO object. + \param Bmi Pointer to a U_BITMAPINFO object +*/ +void bitmapinfo_swap( + PU_BITMAPINFO Bmi + ){ + bitmapinfoheader_swap(&(Bmi->bmiHeader)); // bmIHeader + // ordered bytes: bmiColors +} + +/** + \brief Convert a pointer to a U_EXTLOGPEN object. + \param elp PU_EXTLOGPEN object +*/ +void extlogpen_swap( + PU_EXTLOGPEN elp, + int torev + ){ + int count=0; + U_swap4(elp,3); // elpPenStyle elpWidth elpBrushStyle + // ordered bytes: elpColor + if(torev){ + count = elp->elpNumEntries; + } + U_swap4(&(elp->elpHatch),2); // elpHatch elpNumEntries + if(!torev){ + count = elp->elpNumEntries; + } + U_swap4(&(elp->elpStyleEntry),count); // elpStyleEntry[] +} + +/** + \brief Convert a U_LOGPEN object. + \param lp U_LOGPEN object + +*/ +void logpen_swap( + PU_LOGPEN lp + ){ + U_swap4(lp,1); // lopnStyle + pointl_swap(&(lp->lopnWidth),1); // lopnWidth + // ordered bytes: lopnColor +} + + +/** + \brief Convert a pointer to a U_LOGPALETTE object. + \param lp Pointer to a U_LOGPALETTE object. +*/ +void logpalette_swap( + PU_LOGPALETTE lp + ){ + U_swap2(lp,2); // palVersion palNumEntries + // ordered bytes: palPalEntry[] +} + +/** + \brief Convert a U_RGNDATAHEADER object. + \param rdh U_RGNDATAHEADER object +*/ +void rgndataheader_swap( + PU_RGNDATAHEADER rdh + ){ + U_swap4(rdh,4); // dwSize iType nCount nRgnSize + rectl_swap(&(rdh->rclBounds),1); // rclBounds +} + +/** + \brief Convert a pointer to a U_RGNDATA object. + \param rgd pointer to a U_RGNDATA object. +*/ +void rgndata_swap( + PU_RGNDATA rd + ){ + int count = rd->rdh.nCount; + rgndataheader_swap(&(rd->rdh)); + U_swap4(rd->Buffer,4*count); +} + +/** + \brief Convert a U_COLORADJUSTMENT object. + \param ca U_COLORADJUSTMENT object. +*/ +void coloradjustment_swap( + PU_COLORADJUSTMENT ca + ){ + U_swap2(ca,12); // caSize caFlags caIlluminantIndex caRedGamma caGreenGamma caBlueGamma caReferenceBlack caReferenceWhite caContrast caBrightness caColorfulness caRedGreenTint +} + +/** + \brief Convert a pointer to a U_PIXELFORMATDESCRIPTOR object. + \param pfd pointer to a U_PIXELFORMATDESCRIPTOR object. +*/ +void pixelformatdescriptor_swap( + PU_PIXELFORMATDESCRIPTOR pfd + ){ + U_swap2(pfd,2); // nSize nVersion + U_swap4(&(pfd->dwFlags),1); // dwFlags + // ordered bytes: iPixelType cColorBits cRedBits cRedShift cGreenBits cGreenShift cBlueBits cBlueShift cAlphaBits cAlphaShift cAccumBits cAccumRedBits cAccumGreenBits cAccumBlueBits cAccumAlphaBits cDepthBits cStencilBits cAuxBuffers iLayerType bReserved + U_swap4(&(pfd->dwLayerMask),3); // dwLayerMask dwVisibleMask dwDamageMask +} + +/** + \brief Convert a Pointer to a U_EMRTEXT record + \param pemt Pointer to a U_EMRTEXT record + \param record Pointer to the start of the record which contains this U_EMRTEXT + \param torev 1 for native to reversed, 0 for reversed to native +*/ +void emrtext_swap( + PU_EMRTEXT pemt, + char *record, + int torev + ){ + int off; + uint32_t count=0; + uint32_t offDx=0; + uint32_t fOptions=0; + pointl_swap(&(pemt->ptlReference),1); // ptlReference + if(torev){ + count = pemt->nChars; + fOptions = pemt->fOptions; + } + U_swap4(&(pemt->nChars),3); // nChars offString fOptions + if(!torev){ + count = pemt->nChars; + fOptions = pemt->fOptions; + } + off = sizeof(U_EMRTEXT); + if(!(fOptions & U_ETO_NO_RECT)){ + rectl_swap((PU_RECTL)((char *)pemt + off),1); // optional rectangle + off+=sizeof(U_RECTL); + } + if(torev){ + offDx = *(uint32_t *)((char *)pemt +off); + } + // ordered bytes OR UTF16-LE: the string at offString + U_swap4(((char *)pemt+off),1); // offDx + if(!torev){ + offDx = *(uint32_t *)((char *)pemt +off); + } + U_swap4((record+offDx),count); // Dx[], offset with respect to the Record, NOT the object +} + + + +/* ********************************************************************************************** +These functions contain shared code used by various U_EMR*_swap functions. These should NEVER be called +by end user code and to further that end prototypes are NOT provided and they are hidden from Doxygen. + + + These all have this form: + + void core1_swap(char *record, int torev){ + + but some do not actually use torev. + + + +*********************************************************************************************** */ + +// all core*_swap call this, U_EMRSETMARGN_swap and some others all it directly +// numbered as core5 to be consistent with uemf.c, but must appear before the others as there is no prototype +void core5_swap(char *record, int torev){ + torev = torev; // shuts up compiler warnings about unused parameters + PU_ENHMETARECORD pEMR = (PU_ENHMETARECORD)(record); + U_swap4(pEMR,2); // iType nSize +} + +// Functions with the same form starting with U_EMRPOLYBEZIER_swap +void core1_swap(char *record, int torev){ + int count=0; + PU_EMRPOLYLINETO pEmr = (PU_EMRPOLYLINETO) (record); + if(torev){ + count = pEmr->cptl; + } + core5_swap(record, torev); + rectl_swap(&(pEmr->rclBounds),1 ); // rclBounds + U_swap4(&(pEmr->cptl),1); // cptl + if(!torev){ + count = pEmr->cptl; + } + pointl_swap((pEmr->aptl),count); // aptl[] +} + +// Functions with the same form starting with U_EMRPOLYPOLYLINE_swap +void core2_swap(char *record, int torev){ + int count=0; + int nPolys=0; + PU_EMRPOLYPOLYLINE pEmr = (PU_EMRPOLYPOLYLINE) (record); + if(torev){ + count = pEmr->cptl; + nPolys = pEmr->nPolys; + } + core5_swap(record, torev); + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + U_swap4(&(pEmr->nPolys),2); // nPolys cptl + if(!torev){ + count = pEmr->cptl; + nPolys = pEmr->nPolys; + } + U_swap4(pEmr->aPolyCounts,nPolys); // aPolyCounts[] + pointl_swap((PU_POINT)(record + sizeof(U_EMRPOLYPOLYLINE) - 4 + sizeof(uint32_t)* nPolys), count); // paptl[] +} + + +// Functions with the same form starting with U_EMRSETMAPMODE_swap +void core3_swap(char *record, int torev){ + PU_EMRSETMAPMODE pEmr = (PU_EMRSETMAPMODE)(record); + core5_swap(record, torev); + U_swap4(&(pEmr->iMode),1); // iMode +} + +// Functions taking a single U_RECT or U_RECTL, starting with U_EMRELLIPSE_swap, also U_EMRFILLPATH_swap, +void core4_swap(char *record, int torev){ + PU_EMRELLIPSE pEmr = (PU_EMRELLIPSE)( record); + core5_swap(record, torev); + rectl_swap(&(pEmr->rclBox),1); // rclBox +} + +// Functions with the same form starting with U_EMRPOLYBEZIER16_swap +void core6_swap(char *record, int torev){ + int count=0; + PU_EMRPOLYBEZIER16 pEmr = (PU_EMRPOLYBEZIER16) (record); + if(torev){ + count = pEmr->cpts; + } + core5_swap(record, torev); + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + U_swap4(&(pEmr->cpts),1); // cpts + if(!torev){ + count = pEmr->cpts; + } + point16_swap((pEmr->apts),count); // apts[] +} + + +// Records with the same form starting with U_EMRSETWINDOWEXTEX_swap, that is, all with two uint32_t values after the emr +void core7_swap(char *record, int torev){ + PU_EMRGENERICPAIR pEmr = (PU_EMRGENERICPAIR) (record); + core5_swap(record, torev); + U_swap4(&(pEmr->pair),2); +} + +// For U_EMREXTTEXTOUTA and U_EMREXTTEXTOUTW, type=0 for the first one +void core8_swap(char *record, int torev){ + PU_EMREXTTEXTOUTA pEmr = (PU_EMREXTTEXTOUTA) (record); + emrtext_swap(&(pEmr->emrtext),record,torev); + core5_swap(record, torev); + U_swap4(&(pEmr->iGraphicsMode),1); // iGraphicsMode + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + U_swap4(&(pEmr->exScale),2); // exScale eyScale +} + +// Functions that take a rect and a pair of points, starting with U_EMRARC_swap +void core9_swap(char *record, int torev){ + PU_EMRARC pEmr = (PU_EMRARC) (record); + core5_swap(record, torev); + rectl_swap(&(pEmr->rclBox),1); // rclBox + U_swap4(&(pEmr->ptlStart),4); // ptlStart ptlEnd +} + +// Functions with the same form starting with U_EMRPOLYPOLYLINE16_swap +void core10_swap(char *record, int torev){ + int count=0; + int nPolys=0; + PU_EMRPOLYPOLYLINE16 pEmr = (PU_EMRPOLYPOLYLINE16) (record); + if(torev){ + count = pEmr->cpts; + nPolys = pEmr->nPolys; + } + core5_swap(record, torev); + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + U_swap4(&(pEmr->nPolys),2); // nPolys cpts + if(!torev){ + count = pEmr->cpts; + nPolys = pEmr->nPolys; + } + U_swap4(pEmr->aPolyCounts,nPolys); // aPolyCounts[] + point16_swap((PU_POINT16)(record + sizeof(U_EMRPOLYPOLYLINE16) - 4 + sizeof(uint32_t)* nPolys), count); // apts[] +} + +// Functions with the same form starting with U_EMRINVERTRGN_swap and U_EMRPAINTRGN_swap, +void core11_swap(char *record, int torev){ + int roff=0; + int nextroff=0; + int limit=0; + PU_EMRINVERTRGN pEmr = (PU_EMRINVERTRGN) (record); + roff = 0; + if(torev){ + limit = pEmr->emr.nSize; + nextroff = 0; + } + core5_swap(record, torev); + if(!torev){ + limit = pEmr->emr.nSize; + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + U_swap4(&(pEmr->cbRgnData),1); // cbRgnData + if(!torev){ + limit = pEmr->emr.nSize; + } + // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. + char *prd = (char *) &(pEmr->RgnData); + while(roff + 28 < limit){ // up to the end of the record + if(torev){ + nextroff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + rgndata_swap((PU_RGNDATA) (prd + roff)); + roff = nextroff; + } + else { + rgndata_swap((PU_RGNDATA) (prd + roff)); + roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + } + } +} + + +// common code for U_EMRCREATEMONOBRUSH_swap and U_EMRCREATEDIBPATTERNBRUSHPT_swap, +void core12_swap(char *record, int torev){ + PU_EMRCREATEMONOBRUSH pEmr = (PU_EMRCREATEMONOBRUSH) (record); + if(torev && pEmr->cbBmi){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmi)); // Bmi + } + core5_swap(record, torev); + U_swap4(&(pEmr->ihBrush),6); // ihBrush iUsage offBmi cbBmi offBits cbBits + // ordered bytes: bitmap (including 16 bit 5bit/channel color mode, which is done bytewise). + if(!torev && pEmr->cbBmi){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmi)); // Bmi + } +} + +// common code for U_EMRALPHABLEND_swap and U_EMRTRANSPARENTBLT_swap, +void core13_swap(char *record, int torev){ + PU_EMRALPHABLEND pEmr = (PU_EMRALPHABLEND) (record); + if(torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } + core5_swap(record, torev); + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + pointl_swap(&(pEmr->Dest),2); // Dest cDest + pointl_swap(&(pEmr->Dest),2); // Dest cDest + // ordered bytes: Blend + pointl_swap(&(pEmr->Src),2); // Src + xform_swap( &(pEmr->xformSrc)); // xformSrc + // ordered bytes: crBkColorSrc + U_swap4(&(pEmr->iUsageSrc),5); // iUsageSrc offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc + // ordered bytes: bitmap (including 16 bit 5bit/channel color mode, which is done bytewise). + if(!torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } +} + +/* ********************************************************************************************** +These are the core EMR functions, each creates a particular type of record. +All return these records via a char* pointer, which is NULL if the call failed. +They are listed in order by the corresponding U_EMR_* index number. +*********************************************************************************************** */ + +/** + All of the record level (hidden) functions have this form: + \brief Convert a pointer to a U_EMR_whatever record which has not been implemented. + \param record pointer to a buffer holding the EMR record + \param torev 1 for native to reversed, 0 for reversed to native +*/ +void U_EMRNOTIMPLEMENTED_swap(char *record, int torev){ + core5_swap(record, torev); + printf("WARNING: could not convert data in record type that has not been implemented!\n"); +} + +// U_EMRHEADER 1 +void U_EMRHEADER_swap(char *record, int torev){ + int nDesc,offDesc,nSize,cbPix,offPix; + PU_EMRHEADER pEmr = (PU_EMRHEADER)(record); + if(torev){ + nSize = pEmr->emr.nSize; + core5_swap(record, torev); + } + else { + core5_swap(record, torev); + nSize = pEmr->emr.nSize; + } + + rectl_swap(&(pEmr->rclBounds),2); // rclBounds rclFrame + U_swap4(&(pEmr->dSignature), 4); // dSignature nVersion nBytes nRecords + U_swap2(&(pEmr->nHandles), 2); // nHandlessReserved + if(torev){ + nDesc = pEmr->nDescription; + offDesc = pEmr->offDescription; + U_swap4(&(pEmr->nDescription), 3); // nDescription offDescription nPalEntries + } + else { + U_swap4(&(pEmr->nDescription), 3); // nDescription offDescription nPalEntries + nDesc = pEmr->nDescription; + offDesc = pEmr->offDescription; + } + // UTF16-LE Description + sizel_swap(&(pEmr->szlDevice), 2); // szlDevice szlMillimeters + if((nDesc && (offDesc >= 100)) || + (!offDesc && nSize >= 100) + ){ + if(torev){ + cbPix = pEmr->cbPixelFormat; + offPix = pEmr->offPixelFormat; + if(cbPix)pixelformatdescriptor_swap( (PU_PIXELFORMATDESCRIPTOR) (record + pEmr->offPixelFormat)); + U_swap4(&(pEmr->cbPixelFormat), 2); // cbPixelFormat offPixelFormat + } + else { + U_swap4(&(pEmr->cbPixelFormat), 2); // cbPixelFormat offPixelFormat + cbPix = pEmr->cbPixelFormat; + offPix = pEmr->offPixelFormat; + if(cbPix)pixelformatdescriptor_swap( (PU_PIXELFORMATDESCRIPTOR) (record + pEmr->offPixelFormat)); + } + U_swap4(&(pEmr->bOpenGL), 1); // bOpenGL + if((nDesc && (offDesc >= 108)) || + (cbPix && (offPix >=108)) || + (!offDesc && !cbPix && nSize >= 108) + ){ + sizel_swap(&(pEmr->szlMicrometers), 1); // szlMicrometers + } + } +} + +// U_EMRPOLYBEZIER 2 +void U_EMRPOLYBEZIER_swap(char *record, int torev){ + core1_swap(record, torev); +} + +// U_EMRPOLYGON 3 +void U_EMRPOLYGON_swap(char *record, int torev){ + core1_swap(record, torev); +} + + +// U_EMRPOLYLINE 4 +void U_EMRPOLYLINE_swap(char *record, int torev){ + core1_swap(record, torev); +} + +// U_EMRPOLYBEZIERTO 5 +void U_EMRPOLYBEZIERTO_swap(char *record, int torev){ + core1_swap(record, torev); +} + +// U_EMRPOLYLINETO 6 +void U_EMRPOLYLINETO_swap(char *record, int torev){ + core1_swap(record, torev); +} + +// U_EMRPOLYPOLYLINE 7 +void U_EMRPOLYPOLYLINE_swap(char *record, int torev){ + core2_swap(record, torev); +} + +// U_EMRPOLYPOLYGON 8 +void U_EMRPOLYPOLYGON_swap(char *record, int torev){ + core2_swap(record, torev); +} + +// U_EMRSETWINDOWEXTEX 9 +void U_EMRSETWINDOWEXTEX_swap(char *record, int torev){ + core7_swap(record, torev); +} + +// U_EMRSETWINDOWORGEX 10 +void U_EMRSETWINDOWORGEX_swap(char *record, int torev){ + core7_swap(record, torev); +} + +// U_EMRSETVIEWPORTEXTEX 11 +void U_EMRSETVIEWPORTEXTEX_swap(char *record, int torev){ + core7_swap(record, torev); +} + +// U_EMRSETVIEWPORTORGEX 12 +void U_EMRSETVIEWPORTORGEX_swap(char *record, int torev){ + core7_swap(record, torev); +} + +// U_EMRSETBRUSHORGEX 13 +void U_EMRSETBRUSHORGEX_swap(char *record, int torev){ + core7_swap(record, torev); +} + +// U_EMREOF 14 +void U_EMREOF_swap(char *record, int torev){ + int off=0; + int cbPalEntries=0; + core5_swap(record, torev); + PU_EMREOF pEmr = (PU_EMREOF)(record); + if(torev){ + cbPalEntries = pEmr->cbPalEntries; + if(cbPalEntries){ + logpalette_swap( (PU_LOGPALETTE)(record + pEmr->offPalEntries)); + } + } + U_swap4(&(pEmr->cbPalEntries),2); // cbPalEntries offPalEntries + if(!torev){ + cbPalEntries = pEmr->cbPalEntries; + if(cbPalEntries){ + logpalette_swap( (PU_LOGPALETTE)(record + pEmr->offPalEntries)); + } + } + off = sizeof(U_EMREOF) + 4 * cbPalEntries; + U_swap4(record + off,1); // nSizeLast +} + + +// U_EMRSETPIXELV 15 +void U_EMRSETPIXELV_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRSETPIXELV pEmr = (PU_EMRSETPIXELV)(record); + pointl_swap(&(pEmr->ptlPixel),1); // ptlPixel + // ordered bytes: crColor +} + + +// U_EMRSETMAPPERFLAGS 16 +void U_EMRSETMAPPERFLAGS_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRSETMAPPERFLAGS pEmr = (PU_EMRSETMAPPERFLAGS)(record); + U_swap4(&(pEmr->dwFlags),1); // dwFlags +} + + +// U_EMRSETMAPMODE 17 +void U_EMRSETMAPMODE_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRSETBKMODE 18 +void U_EMRSETBKMODE_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRSETPOLYFILLMODE 19 +void U_EMRSETPOLYFILLMODE_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRSETROP2 20 +void U_EMRSETROP2_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRSETSTRETCHBLTMODE 21 +void U_EMRSETSTRETCHBLTMODE_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRSETTEXTALIGN 22 +void U_EMRSETTEXTALIGN_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRSETCOLORADJUSTMENT 23 +void U_EMRSETCOLORADJUSTMENT_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRSETCOLORADJUSTMENT pEmr = (PU_EMRSETCOLORADJUSTMENT)(record); + coloradjustment_swap(&(pEmr->ColorAdjustment)); +} + +// U_EMRSETTEXTCOLOR 24 +void U_EMRSETTEXTCOLOR_swap(char *record, int torev){ + core5_swap(record, torev); + // ordered bytes: crColor +} + +// U_EMRSETBKCOLOR 25 +void U_EMRSETBKCOLOR_swap(char *record, int torev){ + core5_swap(record, torev); + // ordered bytes: crColor +} + +// U_EMROFFSETCLIPRGN 26 +void U_EMROFFSETCLIPRGN_swap(char *record, int torev){ + core7_swap(record, torev); +} + +// U_EMRMOVETOEX 27 +void U_EMRMOVETOEX_swap(char *record, int torev){ + core7_swap(record, torev); +} + +// U_EMRSETMETARGN 28 +void U_EMRSETMETARGN_swap(char *record, int torev){ + core5_swap(record, torev); +} + +// U_EMREXCLUDECLIPRECT 29 +void U_EMREXCLUDECLIPRECT_swap(char *record, int torev){ + core4_swap(record, torev); +} + +// U_EMRINTERSECTCLIPRECT 30 +void U_EMRINTERSECTCLIPRECT_swap(char *record, int torev){ + core4_swap(record, torev); +} + +// U_EMRSCALEVIEWPORTEXTEX 31 +void U_EMRSCALEVIEWPORTEXTEX_swap(char *record, int torev){ + core4_swap(record, torev); +} + + +// U_EMRSCALEWINDOWEXTEX 32 +void U_EMRSCALEWINDOWEXTEX_swap(char *record, int torev){ + core4_swap(record, torev); +} + +// U_EMRSAVEDC 33 +void U_EMRSAVEDC_swap(char *record, int torev){ + core5_swap(record, torev); +} + +// U_EMRRESTOREDC 34 +void U_EMRRESTOREDC_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRSETWORLDTRANSFORM 35 +void U_EMRSETWORLDTRANSFORM_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRSETWORLDTRANSFORM pEmr = (PU_EMRSETWORLDTRANSFORM)(record); + xform_swap(&(pEmr->xform)); +} + +// U_EMRMODIFYWORLDTRANSFORM 36 +void U_EMRMODIFYWORLDTRANSFORM_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRMODIFYWORLDTRANSFORM pEmr = (PU_EMRMODIFYWORLDTRANSFORM)(record); + xform_swap(&(pEmr->xform)); // xform + U_swap4(&(pEmr->iMode),1); // iMode +} + +// U_EMRSELECTOBJECT 37 +void U_EMRSELECTOBJECT_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRSELECTOBJECT pEmr = (PU_EMRSELECTOBJECT)(record); + U_swap4(&(pEmr->ihObject),1); // ihObject +} + +// U_EMRCREATEPEN 38 +void U_EMRCREATEPEN_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRCREATEPEN pEmr = (PU_EMRCREATEPEN)(record); + U_swap4(&(pEmr->ihPen),1); // ihPen + logpen_swap(&(pEmr->lopn)); // lopn +} + +// U_EMRCREATEBRUSHINDIRECT 39 +void U_EMRCREATEBRUSHINDIRECT_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRCREATEBRUSHINDIRECT pEmr = (PU_EMRCREATEBRUSHINDIRECT)(record); + U_swap4(&(pEmr->ihBrush),1); // ihBrush + logbrush_swap(&(pEmr->lb)); // lb +} + +// U_EMRDELETEOBJECT 40 +void U_EMRDELETEOBJECT_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRDELETEOBJECT pEmr = (PU_EMRDELETEOBJECT)(record); + U_swap4(&(pEmr->ihObject),1); // ihObject +} + +// U_EMRANGLEARC 41 +void U_EMRANGLEARC_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRANGLEARC pEmr = (PU_EMRANGLEARC)(record); + pointl_swap(&(pEmr->ptlCenter),1); // ptlCenter + U_swap4(&(pEmr->nRadius),3); // nRadius eStartAngle eSweepAngle +} + +// U_EMRELLIPSE 42 +void U_EMRELLIPSE_swap(char *record, int torev){ + core4_swap(record, torev); +} + +// U_EMRRECTANGLE 43 +void U_EMRRECTANGLE_swap(char *record, int torev){ + core4_swap(record, torev); +} + +// U_EMRROUNDRECT 44 +void U_EMRROUNDRECT_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRROUNDRECT pEmr = (PU_EMRROUNDRECT)(record); + rectl_swap(&(pEmr->rclBox),1); // rclBox + sizel_swap(&(pEmr->szlCorner), 1); // szlCorner +} + +// U_EMRARC 45 +void U_EMRARC_swap(char *record, int torev){ + core9_swap(record, torev); +} + +// U_EMRCHORD 46 +void U_EMRCHORD_swap(char *record, int torev){ + core9_swap(record, torev); +} + +// U_EMRPIE 47 +void U_EMRPIE_swap(char *record, int torev){ + core9_swap(record, torev); +} + +// U_EMRSELECTPALETTE 48 +void U_EMRSELECTPALETTE_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRCREATEPALETTE 49 +void U_EMRCREATEPALETTE_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRCREATEPALETTE pEmr = (PU_EMRCREATEPALETTE)(record); + U_swap4(&(pEmr->ihPal),1); // ihPal + logpalette_swap( (PU_LOGPALETTE)&(pEmr->lgpl) ); // lgpl +} + +// U_EMRSETPALETTEENTRIES 50 +void U_EMRSETPALETTEENTRIES_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRSETPALETTEENTRIES pEmr = (PU_EMRSETPALETTEENTRIES)(record); + U_swap4(&(pEmr->ihPal),3); // ihPal iStart cEntries + // ordered bytes: aPalEntries[] +} + +// U_EMRRESIZEPALETTE 51 +void U_EMRRESIZEPALETTE_swap(char *record, int torev){ + core7_swap(record, torev); +} + +// U_EMRREALIZEPALETTE 52 +void U_EMRREALIZEPALETTE_swap(char *record, int torev){ + core5_swap(record, torev); +} + +// U_EMREXTFLOODFILL 53 +void U_EMREXTFLOODFILL_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMREXTFLOODFILL pEmr = (PU_EMREXTFLOODFILL)(record); + pointl_swap(&(pEmr->ptlStart),1); // ptlStart + // ordered bytes: crColor + U_swap4(&(pEmr->iMode),1); // iMode +} + +// U_EMRLINETO 54 +void U_EMRLINETO_swap(char *record, int torev){ + core7_swap(record, torev); +} + +// U_EMRARCTO 55 +void U_EMRARCTO_swap(char *record, int torev){ + core9_swap(record, torev); +} + +// U_EMRPOLYDRAW 56 +void U_EMRPOLYDRAW_swap(char *record, int torev){ + int count=0; + core5_swap(record, torev); + PU_EMRPOLYDRAW pEmr = (PU_EMRPOLYDRAW)(record); + + if(torev){ + count = pEmr->cptl; + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + U_swap4(&(pEmr->cptl),1); // cptl + if(!torev){ + count = pEmr->cptl; + } + pointl_swap(pEmr->aptl,count); // aptl[] + U_swap4(pEmr->abTypes,count); // abTypes[] +} + +// U_EMRSETARCDIRECTION 57 +void U_EMRSETARCDIRECTION_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRSETMITERLIMIT 58 +void U_EMRSETMITERLIMIT_swap(char *record, int torev){ + core3_swap(record, torev); +} + + +// U_EMRBEGINPATH 59 +void U_EMRBEGINPATH_swap(char *record, int torev){ + core5_swap(record, torev); +} + +// U_EMRENDPATH 60 +void U_EMRENDPATH_swap(char *record, int torev){ + core5_swap(record, torev); +} + +// U_EMRCLOSEFIGURE 61 +void U_EMRCLOSEFIGURE_swap(char *record, int torev){ + core5_swap(record, torev); +} + +// U_EMRFILLPATH 62 +void U_EMRFILLPATH_swap(char *record, int torev){ + core4_swap(record, torev); +} + +// U_EMRSTROKEANDFILLPATH 63 +void U_EMRSTROKEANDFILLPATH_swap(char *record, int torev){ + core4_swap(record, torev); +} + +// U_EMRSTROKEPATH 64 +void U_EMRSTROKEPATH_swap(char *record, int torev){ + core4_swap(record, torev); +} + +// U_EMRFLATTENPATH 65 +void U_EMRFLATTENPATH_swap(char *record, int torev){ + core5_swap(record, torev); +} + +// U_EMRWIDENPATH 66 +void U_EMRWIDENPATH_swap(char *record, int torev){ + core5_swap(record, torev); +} + +// U_EMRSELECTCLIPPATH 67 +void U_EMRSELECTCLIPPATH_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRABORTPATH 68 +void U_EMRABORTPATH_swap(char *record, int torev){ + core5_swap(record, torev); +} + +// U_EMRUNDEF69 69 +#define U_EMRUNDEF69_swap U_EMRNOTIMPLEMENTED_swap + +// U_EMRCOMMENT 70 Comment (any binary data, interpretation is program specific) +void U_EMRCOMMENT_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRCOMMENT pEmr = (PU_EMRCOMMENT)(record); + U_swap4(&(pEmr->cbData),1); // cbData + // program specific data, presumably byte ordered, otherwise, not portable +} + +// U_EMRFILLRGN 71 +void U_EMRFILLRGN_swap(char *record, int torev){ + int roff=0; + int nextroff=0; + int limit=0; + roff=0; + PU_EMRFILLRGN pEmr = (PU_EMRFILLRGN)(record); + if(torev){ + limit = pEmr->emr.nSize; + nextroff = 0; + } + core5_swap(record, torev); + if(!torev){ + limit = pEmr->emr.nSize; + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + U_swap4(&(pEmr->cbRgnData),2); // cbRgnData ihBrush + // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. + char *prd = (char *) &(pEmr->RgnData); + while(roff + 28 < limit){ // up to the end of the record + if(torev){ + nextroff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + rgndata_swap((PU_RGNDATA) (prd + roff)); + roff = nextroff; + } + else { + rgndata_swap((PU_RGNDATA) (prd + roff)); + roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + } + } +} + +// U_EMRFRAMERGN 72 +void U_EMRFRAMERGN_swap(char *record, int torev){ + int roff=0; + int nextroff=0; + int limit=0; + PU_EMRFRAMERGN pEmr = (PU_EMRFRAMERGN)(record); + roff = 0; + if(torev){ + limit = pEmr->emr.nSize; + nextroff = 0; + } + core5_swap(record, torev); + if(!torev){ + limit = pEmr->emr.nSize; + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + U_swap4(&(pEmr->cbRgnData),2); // cbRgnData ihBrush + sizel_swap(&(pEmr->szlStroke), 2); // szlStroke + // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. + char *prd = (char *) &(pEmr->RgnData); + while(roff + 28 < limit){ // up to the end of the record + if(torev){ + nextroff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + rgndata_swap((PU_RGNDATA) (prd + roff)); + roff = nextroff; + } + else { + rgndata_swap((PU_RGNDATA) (prd + roff)); + roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + } + } +} + +// U_EMRINVERTRGN 73 +void U_EMRINVERTRGN_swap(char *record, int torev){ + core11_swap(record, torev); +} + +// U_EMRPAINTRGN 74 +void U_EMRPAINTRGN_swap(char *record, int torev){ + core11_swap(record, torev); +} + +// U_EMREXTSELECTCLIPRGN 75 +void U_EMREXTSELECTCLIPRGN_swap(char *record, int torev){ + int roff=0; + int nextroff=0; + int limit=0; + PU_EMREXTSELECTCLIPRGN pEmr = (PU_EMREXTSELECTCLIPRGN) (record); + roff = 0; + if(torev){ + limit = pEmr->emr.nSize; + nextroff = 0; + } + core5_swap(record, torev); + if(!torev){ + limit = pEmr->emr.nSize; + } + U_swap4(&(pEmr->cbRgnData),2); // cbRgnData iMode + // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. + char *prd = (char *) &(pEmr->RgnData); + nextroff = roff = 0; + while(roff + 16 < limit){ // up to the end of the record + if(torev){ + nextroff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + rgndata_swap((PU_RGNDATA) (prd + roff)); + roff = nextroff; + } + else { + rgndata_swap((PU_RGNDATA) (prd + roff)); + roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + } + } +} + +// U_EMRBITBLT 76 +void U_EMRBITBLT_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRBITBLT pEmr = (PU_EMRBITBLT) (record); + if(torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + pointl_swap(&(pEmr->Dest),2); // Dest cDest + U_swap4(&(pEmr->dwRop),1); // dwRop + pointl_swap(&(pEmr->Src),1); // Src + xform_swap(&(pEmr->xformSrc)); // xformSrc + // ordered bytes: crBkColorSrc + U_swap4(&(pEmr->iUsageSrc),5); // iUsageSrc offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc + // ordered bytes: bitmap (including 16 bit 5bit/channel color mode, which is done bytewise). + if(!torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } +} + +// U_EMRSTRETCHBLT 77 +void U_EMRSTRETCHBLT_swap(char *record, int torev){ + PU_EMRSTRETCHBLT pEmr = (PU_EMRSTRETCHBLT) (record); + core5_swap(record, torev); + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + pointl_swap(&(pEmr->Dest),2); // Dest cDest + U_swap4(&(pEmr->dwRop),1); // dwRop + pointl_swap(&(pEmr->Src),1); // Src + xform_swap(&(pEmr->xformSrc)); // xformSrc + // ordered bytes: crBkColorSrc + if(torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } + U_swap4(&(pEmr->iUsageSrc),5); // iUsageSrc offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc + pointl_swap(&(pEmr->cSrc),1); // cSrc + if(!torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } +} + +// U_EMRMASKBLT 78 +void U_EMRMASKBLT_swap(char *record, int torev){ + PU_EMRMASKBLT pEmr = (PU_EMRMASKBLT) (record); + core5_swap(record, torev); + if(torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } + if(torev && pEmr->cbBmiMask){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiMask)); + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + pointl_swap(&(pEmr->Dest),2); // Dest cDest + U_swap4(&(pEmr->dwRop),1); // dwRop + pointl_swap(&(pEmr->Src),1); // Src + xform_swap(&(pEmr->xformSrc)); // xformSrc + // ordered bytes: crBkColorSrc + U_swap4(&(pEmr->iUsageSrc),5); // iUsageSrc offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc + pointl_swap(&(pEmr->Mask),1); // Mask + U_swap4(&(pEmr->iUsageMask),5); // iUsageMask offBmiMask cbBmiMask offBitsMask cbBitsMask + if(!torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } + if(!torev && pEmr->cbBmiMask){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiMask)); + } +} + +// U_EMRPLGBLT 79 +void U_EMRPLGBLT_swap(char *record, int torev){ + PU_EMRPLGBLT pEmr = (PU_EMRPLGBLT) (record); + core5_swap(record, torev); + if(torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } + if(torev && pEmr->cbBmiMask){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiMask)); + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + pointl_swap(pEmr->aptlDst,3); // aptlDst[] + pointl_swap(&(pEmr->Src),2); // Src cSrc + xform_swap(&(pEmr->xformSrc)); // xformSrc + // ordered bytes: crBkColorSrc + U_swap4(&(pEmr->iUsageSrc),5); // iUsageSrc offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc + pointl_swap(&(pEmr->Mask),1); // Mask + U_swap4(&(pEmr->iUsageMask),5); // iUsageMask offBmiMask cbBmiMask offBitsMask cbBitsMask + if(!torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } + if(!torev && pEmr->cbBmiMask){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiMask)); + } +} + +// U_EMRSETDIBITSTODEVICE 80 +void U_EMRSETDIBITSTODEVICE_swap(char *record, int torev){ + PU_EMRSETDIBITSTODEVICE pEmr = (PU_EMRSETDIBITSTODEVICE) (record); + core5_swap(record, torev); + if(torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + pointl_swap(&(pEmr->Dest),1); // Dest + pointl_swap(&(pEmr->Src),2); // Src cSrc + U_swap4(&(pEmr->offBmiSrc),7); // offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc iUsageSrc iStartScan cScans + if(!torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } +} + +// U_EMRSTRETCHDIBITS 81 +void U_EMRSTRETCHDIBITS_swap(char *record, int torev){ + PU_EMRSTRETCHDIBITS pEmr = (PU_EMRSTRETCHDIBITS) (record); + core5_swap(record, torev); + if(torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + pointl_swap(&(pEmr->Dest),1); // Dest + pointl_swap(&(pEmr->Src),2); // Src cSrc + U_swap4(&(pEmr->offBmiSrc),6); // offBmiSrc cbBmiSrc offBitsSrc cbBitsSrc iUsageSrc dwRop + pointl_swap(&(pEmr->cDest),1); // cDest + if(!torev && pEmr->cbBmiSrc){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmiSrc)); + } +} + +// U_EMREXTCREATEFONTINDIRECTW_swap 82 +void U_EMREXTCREATEFONTINDIRECTW_swap(char *record, int torev){ + PU_EMREXTCREATEFONTINDIRECTW pEmr = (PU_EMREXTCREATEFONTINDIRECTW) (record); + if(torev){ + if(pEmr->emr.nSize == sizeof(U_EMREXTCREATEFONTINDIRECTW)){ + logfont_panose_swap(&(pEmr->elfw)); + } + else { + logfont_swap( (PU_LOGFONT) &(pEmr->elfw)); + } + } + core5_swap(record, torev); + if(!torev){ + if(pEmr->emr.nSize == sizeof(U_EMREXTCREATEFONTINDIRECTW)){ + logfont_panose_swap(&(pEmr->elfw)); + } + else { + logfont_swap( (PU_LOGFONT) &(pEmr->elfw)); + } + } + U_swap4(&(pEmr->ihFont),1); // ihFont +} + +// U_EMREXTTEXTOUTA 83 +void U_EMREXTTEXTOUTA_swap(char *record, int torev){ + core8_swap(record, torev); +} + +// U_EMREXTTEXTOUTW 84 +void U_EMREXTTEXTOUTW_swap(char *record, int torev){ + core8_swap(record, torev); +} + +// U_EMRPOLYBEZIER16 85 +/** + \brief Convert a pointer to a U_EMR_POLYBEZIER16 record. + \param record pointer to a buffer holding the EMR record +*/ +void U_EMRPOLYBEZIER16_swap(char *record, int torev){ + core6_swap(record, torev); +} + +// U_EMRPOLYGON16 86 +void U_EMRPOLYGON16_swap(char *record, int torev){ + core6_swap(record, torev); +} + +// U_EMRPOLYLINE16 87 +void U_EMRPOLYLINE16_swap(char *record, int torev){ + core6_swap(record, torev); +} + +// U_EMRPOLYBEZIERTO16 88 +void U_EMRPOLYBEZIERTO16_swap(char *record, int torev){ + core6_swap(record, torev); +} + +// U_EMRPOLYLINETO16 89 +/** + \brief Convert a pointer to a U_EMR_POLYLINETO16 record. + \param record pointer to a buffer holding the EMR record +*/ +void U_EMRPOLYLINETO16_swap(char *record, int torev){ + core6_swap(record, torev); +} + +// U_EMRPOLYPOLYLINE16 90 +void U_EMRPOLYPOLYLINE16_swap(char *record, int torev){ + core10_swap(record, torev); +} + +// U_EMRPOLYPOLYGON16 91 +void U_EMRPOLYPOLYGON16_swap(char *record, int torev){ + core10_swap(record, torev); +} + + +// U_EMRPOLYDRAW16 92 +void U_EMRPOLYDRAW16_swap(char *record, int torev){ + int count=0; + core5_swap(record, torev); + PU_EMRPOLYDRAW16 pEmr = (PU_EMRPOLYDRAW16)(record); + if(torev){ + count = pEmr->cpts; + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + U_swap4(&(pEmr->cpts),1); // cpts + if(!torev){ + count = pEmr->cpts; + } + point16_swap(pEmr->apts,count); // apts[] + U_swap4(pEmr->abTypes,count); // abTypes[] +} + +// U_EMRCREATEMONOBRUSH 93 +void U_EMRCREATEMONOBRUSH_swap(char *record, int torev){ + core12_swap(record, torev); +} + +// U_EMRCREATEDIBPATTERNBRUSHPT_swap 94 +void U_EMRCREATEDIBPATTERNBRUSHPT_swap(char *record, int torev){ + core12_swap(record, torev); +} + + +// U_EMREXTCREATEPEN 95 +void U_EMREXTCREATEPEN_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMREXTCREATEPEN pEmr = (PU_EMREXTCREATEPEN)(record); + if(torev && pEmr->cbBmi){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmi)); + } + U_swap4(&(pEmr->ihPen),5); // ihPen offBmi cbBmi offBits cbBits + if(!torev && pEmr->cbBmi){ + bitmapinfo_swap((PU_BITMAPINFO)(record + pEmr->offBmi)); + } + extlogpen_swap((PU_EXTLOGPEN) &(pEmr->elp), torev); +} + +// U_EMRPOLYTEXTOUTA 96 NOT IMPLEMENTED, denigrated after Windows NT +#define U_EMRPOLYTEXTOUTA_swap U_EMRNOTIMPLEMENTED_swap +// U_EMRPOLYTEXTOUTW 97 NOT IMPLEMENTED, denigrated after Windows NT +#define U_EMRPOLYTEXTOUTW_swap U_EMRNOTIMPLEMENTED_swap + +// U_EMRSETICMMODE 98 +void U_EMRSETICMMODE_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRCREATECOLORSPACE 99 +void U_EMRCREATECOLORSPACE_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRCREATECOLORSPACE pEmr = (PU_EMRCREATECOLORSPACE)(record); + U_swap4(&(pEmr->ihCS),1); // ihCS + logcolorspacea_swap(&(pEmr->lcs)); // lcs +} + +// U_EMRSETCOLORSPACE 100 +void U_EMRSETCOLORSPACE_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRDELETECOLORSPACE 101 +void U_EMRDELETECOLORSPACE_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRGLSRECORD 102 Not implemented +#define U_EMRGLSRECORD_swap U_EMRNOTIMPLEMENTED_swap +// U_EMRGLSBOUNDEDRECORD 103 Not implemented +#define U_EMRGLSBOUNDEDRECORD_swap U_EMRNOTIMPLEMENTED_swap + +// U_EMRPIXELFORMAT 104 +void U_EMRPIXELFORMAT_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRPIXELFORMAT pEmr = (PU_EMRPIXELFORMAT)(record); + pixelformatdescriptor_swap(&(pEmr->pfd)); // pfd +} + +// U_EMRDRAWESCAPE 105 Not implemented +#define U_EMRDRAWESCAPE_swap U_EMRNOTIMPLEMENTED_swap +// U_EMREXTESCAPE 106 Not implemented +#define U_EMREXTESCAPE_swap U_EMRNOTIMPLEMENTED_swap +// U_EMRUNDEF107 107 Not implemented +#define U_EMRUNDEF107_swap U_EMRNOTIMPLEMENTED_swap + +// U_EMRSMALLTEXTOUT 108 +void U_EMRSMALLTEXTOUT_swap(char *record, int torev){ + int roff=0; + int fuOptions=0; + core5_swap(record, torev); + PU_EMRSMALLTEXTOUT pEmr = (PU_EMRSMALLTEXTOUT)(record); + if(torev){ + fuOptions = pEmr->fuOptions; + } + pointl_swap(&(pEmr->Dest),1); // Dest + U_swap4(&(pEmr->cChars),5); // cChars fuOptions iGraphicsMode exScale eyScale + if(!torev){ + fuOptions = pEmr->fuOptions; + } + roff = sizeof(U_EMRSMALLTEXTOUT); // offset to the start of the variable fields + if(!(fuOptions & U_ETO_NO_RECT)){ + rectl_swap( (PU_RECTL) (record + roff),1); // rclBounds + } + // ordered bytes or UTF16-LE TextString +} + +// U_EMRFORCEUFIMAPPING 109 Not implemented +#define U_EMRFORCEUFIMAPPING_swap U_EMRNOTIMPLEMENTED_swap +// U_EMRNAMEDESCAPE 110 Not implemented +#define U_EMRNAMEDESCAPE_swap U_EMRNOTIMPLEMENTED_swap +// U_EMRCOLORCORRECTPALETTE 111 Not implemented +#define U_EMRCOLORCORRECTPALETTE_swap U_EMRNOTIMPLEMENTED_swap +// U_EMRSETICMPROFILEA 112 Not implemented +#define U_EMRSETICMPROFILEA_swap U_EMRNOTIMPLEMENTED_swap +// U_EMRSETICMPROFILEW 113 Not implemented +#define U_EMRSETICMPROFILEW_swap U_EMRNOTIMPLEMENTED_swap + +// U_EMRALPHABLEND 114 +void U_EMRALPHABLEND_swap(char *record, int torev){ + core13_swap(record, torev); +} + +// U_EMRSETLAYOUT 115 +void U_EMRSETLAYOUT_swap(char *record, int torev){ + core3_swap(record, torev); +} + +// U_EMRTRANSPARENTBLT 116 +void U_EMRTRANSPARENTBLT_swap(char *record, int torev){ + core13_swap(record, torev); +} + + +// U_EMRUNDEF117 117 Not implemented +#define U_EMRUNDEF117_swap U_EMRNOTIMPLEMENTED_swap +// U_EMRGRADIENTFILL 118 +void U_EMRGRADIENTFILL_swap(char *record, int torev){ + int nTriVert=0; + int nGradObj=0; + int ulMode=0; + core5_swap(record, torev); + PU_EMRGRADIENTFILL pEmr = (PU_EMRGRADIENTFILL)(record); + if(torev){ + nTriVert = pEmr->nTriVert; + nGradObj = pEmr->nGradObj; + ulMode = pEmr->ulMode; + } + rectl_swap(&(pEmr->rclBounds),1); // rclBounds + U_swap4(&(pEmr->nTriVert),3); // nTriVert nGradObj ulMode + if(!torev){ + nTriVert = pEmr->nTriVert; + nGradObj = pEmr->nGradObj; + ulMode = pEmr->ulMode; + } + record += sizeof(U_EMRGRADIENTFILL); + if(nTriVert){ + trivertex_swap((PU_TRIVERTEX)(record),nTriVert); // TriVert[] + } + record += nTriVert * sizeof(U_TRIVERTEX); + if(nGradObj){ + if( ulMode == U_GRADIENT_FILL_TRIANGLE){ + gradient3_swap((PU_GRADIENT3)(record), nGradObj); // GradObj[] + } + else if(ulMode == U_GRADIENT_FILL_RECT_H || ulMode == U_GRADIENT_FILL_RECT_V){ + gradient4_swap((PU_GRADIENT4)(record), nGradObj); // GradObj[] + } + } +} + +// U_EMRSETLINKEDUFIS 119 Not implemented +#define U_EMRSETLINKEDUFIS_swap U_EMRNOTIMPLEMENTED_swap +// U_EMRSETTEXTJUSTIFICATION120 Not implemented (denigrated) +#define U_EMRSETTEXTJUSTIFICATION_swap U_EMRNOTIMPLEMENTED_swap +// U_EMRCOLORMATCHTOTARGETW 121 Not implemented +#define U_EMRCOLORMATCHTOTARGETW_swap U_EMRNOTIMPLEMENTED_swap + +// U_EMRCREATECOLORSPACEW 122 +void U_EMRCREATECOLORSPACEW_swap(char *record, int torev){ + core5_swap(record, torev); + PU_EMRCREATECOLORSPACEW pEmr = (PU_EMRCREATECOLORSPACEW)(record); + U_swap4(&(pEmr->ihCS),1); // ihCS + logcolorspacew_swap(&(pEmr->lcs)); // lcs + U_swap4(&(pEmr->dwFlags),2); // dwFlags cbData + // ordered bytes: Data +} + +//! @endcond + + +/** + \brief Convert an entire EMF in memory from Big Endian to Little Endian. + \return 0 on failure, 1 on success + \param contents pointer to the buffer holding the entire EMF in memory + \param length number of bytes in the buffer + \param torev 1 for native to reversed, 0 for reversed to native + + Normally this would be called immediately before writing the data to a file + or immediately after reading the data from a file. +*/ +int U_emf_endian(char *contents, size_t length, int torev){ + size_t off; + uint32_t OK, recnum, iType; + char *record; + PU_ENHMETARECORD pEmr; + + record = contents; + OK = 1; + off = 0; + recnum = 0; + while(OK){ + if(record > contents + length){ // this is most likely a corrupt EMF + return(0); + } + + pEmr = (PU_ENHMETARECORD)(record); + + iType = pEmr->iType; + off = pEmr->nSize; + if(!torev){ + U_swap4(&iType,1); + U_swap4(&off,1); + } + + switch (iType) + { + case U_EMR_HEADER: U_EMRHEADER_swap(record, torev); break; + case U_EMR_POLYBEZIER: U_EMRPOLYBEZIER_swap(record, torev); break; + case U_EMR_POLYGON: U_EMRPOLYGON_swap(record, torev); break; + case U_EMR_POLYLINE: U_EMRPOLYLINE_swap(record, torev); break; + case U_EMR_POLYBEZIERTO: U_EMRPOLYBEZIERTO_swap(record, torev); break; + case U_EMR_POLYLINETO: U_EMRPOLYLINETO_swap(record, torev); break; + case U_EMR_POLYPOLYLINE: U_EMRPOLYPOLYLINE_swap(record, torev); break; + case U_EMR_POLYPOLYGON: U_EMRPOLYPOLYGON_swap(record, torev); break; + case U_EMR_SETWINDOWEXTEX: U_EMRSETWINDOWEXTEX_swap(record, torev); break; + case U_EMR_SETWINDOWORGEX: U_EMRSETWINDOWORGEX_swap(record, torev); break; + case U_EMR_SETVIEWPORTEXTEX: U_EMRSETVIEWPORTEXTEX_swap(record, torev); break; + case U_EMR_SETVIEWPORTORGEX: U_EMRSETVIEWPORTORGEX_swap(record, torev); break; + case U_EMR_SETBRUSHORGEX: U_EMRSETBRUSHORGEX_swap(record, torev); break; + case U_EMR_EOF: + U_EMREOF_swap(record, torev); + OK = 0; /* Exit triggered here */ + break; + case U_EMR_SETPIXELV: U_EMRSETPIXELV_swap(record, torev); break; + case U_EMR_SETMAPPERFLAGS: U_EMRSETMAPPERFLAGS_swap(record, torev); break; + case U_EMR_SETMAPMODE: U_EMRSETMAPMODE_swap(record, torev); break; + case U_EMR_SETBKMODE: U_EMRSETBKMODE_swap(record, torev); break; + case U_EMR_SETPOLYFILLMODE: U_EMRSETPOLYFILLMODE_swap(record, torev); break; + case U_EMR_SETROP2: U_EMRSETROP2_swap(record, torev); break; + case U_EMR_SETSTRETCHBLTMODE: U_EMRSETSTRETCHBLTMODE_swap(record, torev); break; + case U_EMR_SETTEXTALIGN: U_EMRSETTEXTALIGN_swap(record, torev); break; + case U_EMR_SETCOLORADJUSTMENT: U_EMRSETCOLORADJUSTMENT_swap(record, torev); break; + case U_EMR_SETTEXTCOLOR: U_EMRSETTEXTCOLOR_swap(record, torev); break; + case U_EMR_SETBKCOLOR: U_EMRSETBKCOLOR_swap(record, torev); break; + case U_EMR_OFFSETCLIPRGN: U_EMROFFSETCLIPRGN_swap(record, torev); break; + case U_EMR_MOVETOEX: U_EMRMOVETOEX_swap(record, torev); break; + case U_EMR_SETMETARGN: U_EMRSETMETARGN_swap(record, torev); break; + case U_EMR_EXCLUDECLIPRECT: U_EMREXCLUDECLIPRECT_swap(record, torev); break; + case U_EMR_INTERSECTCLIPRECT: U_EMRINTERSECTCLIPRECT_swap(record, torev); break; + case U_EMR_SCALEVIEWPORTEXTEX: U_EMRSCALEVIEWPORTEXTEX_swap(record, torev); break; + case U_EMR_SCALEWINDOWEXTEX: U_EMRSCALEWINDOWEXTEX_swap(record, torev); break; + case U_EMR_SAVEDC: U_EMRSAVEDC_swap(record, torev); break; + case U_EMR_RESTOREDC: U_EMRRESTOREDC_swap(record, torev); break; + case U_EMR_SETWORLDTRANSFORM: U_EMRSETWORLDTRANSFORM_swap(record, torev); break; + case U_EMR_MODIFYWORLDTRANSFORM: U_EMRMODIFYWORLDTRANSFORM_swap(record, torev); break; + case U_EMR_SELECTOBJECT: U_EMRSELECTOBJECT_swap(record, torev); break; + case U_EMR_CREATEPEN: U_EMRCREATEPEN_swap(record, torev); break; + case U_EMR_CREATEBRUSHINDIRECT: U_EMRCREATEBRUSHINDIRECT_swap(record, torev); break; + case U_EMR_DELETEOBJECT: U_EMRDELETEOBJECT_swap(record, torev); break; + case U_EMR_ANGLEARC: U_EMRANGLEARC_swap(record, torev); break; + case U_EMR_ELLIPSE: U_EMRELLIPSE_swap(record, torev); break; + case U_EMR_RECTANGLE: U_EMRRECTANGLE_swap(record, torev); break; + case U_EMR_ROUNDRECT: U_EMRROUNDRECT_swap(record, torev); break; + case U_EMR_ARC: U_EMRARC_swap(record, torev); break; + case U_EMR_CHORD: U_EMRCHORD_swap(record, torev); break; + case U_EMR_PIE: U_EMRPIE_swap(record, torev); break; + case U_EMR_SELECTPALETTE: U_EMRSELECTPALETTE_swap(record, torev); break; + case U_EMR_CREATEPALETTE: U_EMRCREATEPALETTE_swap(record, torev); break; + case U_EMR_SETPALETTEENTRIES: U_EMRSETPALETTEENTRIES_swap(record, torev); break; + case U_EMR_RESIZEPALETTE: U_EMRRESIZEPALETTE_swap(record, torev); break; + case U_EMR_REALIZEPALETTE: U_EMRREALIZEPALETTE_swap(record, torev); break; + case U_EMR_EXTFLOODFILL: U_EMREXTFLOODFILL_swap(record, torev); break; + case U_EMR_LINETO: U_EMRLINETO_swap(record, torev); break; + case U_EMR_ARCTO: U_EMRARCTO_swap(record, torev); break; + case U_EMR_POLYDRAW: U_EMRPOLYDRAW_swap(record, torev); break; + case U_EMR_SETARCDIRECTION: U_EMRSETARCDIRECTION_swap(record, torev); break; + case U_EMR_SETMITERLIMIT: U_EMRSETMITERLIMIT_swap(record, torev); break; + case U_EMR_BEGINPATH: U_EMRBEGINPATH_swap(record, torev); break; + case U_EMR_ENDPATH: U_EMRENDPATH_swap(record, torev); break; + case U_EMR_CLOSEFIGURE: U_EMRCLOSEFIGURE_swap(record, torev); break; + case U_EMR_FILLPATH: U_EMRFILLPATH_swap(record, torev); break; + case U_EMR_STROKEANDFILLPATH: U_EMRSTROKEANDFILLPATH_swap(record, torev); break; + case U_EMR_STROKEPATH: U_EMRSTROKEPATH_swap(record, torev); break; + case U_EMR_FLATTENPATH: U_EMRFLATTENPATH_swap(record, torev); break; + case U_EMR_WIDENPATH: U_EMRWIDENPATH_swap(record, torev); break; + case U_EMR_SELECTCLIPPATH: U_EMRSELECTCLIPPATH_swap(record, torev); break; + case U_EMR_ABORTPATH: U_EMRABORTPATH_swap(record, torev); break; + case U_EMR_UNDEF69: U_EMRUNDEF69_swap(record, torev); break; + case U_EMR_COMMENT: U_EMRCOMMENT_swap(record, torev); break; + case U_EMR_FILLRGN: U_EMRFILLRGN_swap(record, torev); break; + case U_EMR_FRAMERGN: U_EMRFRAMERGN_swap(record, torev); break; + case U_EMR_INVERTRGN: U_EMRINVERTRGN_swap(record, torev); break; + case U_EMR_PAINTRGN: U_EMRPAINTRGN_swap(record, torev); break; + case U_EMR_EXTSELECTCLIPRGN: U_EMREXTSELECTCLIPRGN_swap(record, torev); break; + case U_EMR_BITBLT: U_EMRBITBLT_swap(record, torev); break; + case U_EMR_STRETCHBLT: U_EMRSTRETCHBLT_swap(record, torev); break; + case U_EMR_MASKBLT: U_EMRMASKBLT_swap(record, torev); break; + case U_EMR_PLGBLT: U_EMRPLGBLT_swap(record, torev); break; + case U_EMR_SETDIBITSTODEVICE: U_EMRSETDIBITSTODEVICE_swap(record, torev); break; + case U_EMR_STRETCHDIBITS: U_EMRSTRETCHDIBITS_swap(record, torev); break; + case U_EMR_EXTCREATEFONTINDIRECTW: U_EMREXTCREATEFONTINDIRECTW_swap(record, torev); break; + case U_EMR_EXTTEXTOUTA: U_EMREXTTEXTOUTA_swap(record, torev); break; + case U_EMR_EXTTEXTOUTW: U_EMREXTTEXTOUTW_swap(record, torev); break; + case U_EMR_POLYBEZIER16: U_EMRPOLYBEZIER16_swap(record, torev); break; + case U_EMR_POLYGON16: U_EMRPOLYGON16_swap(record, torev); break; + case U_EMR_POLYLINE16: U_EMRPOLYLINE16_swap(record, torev); break; + case U_EMR_POLYBEZIERTO16: U_EMRPOLYBEZIERTO16_swap(record, torev); break; + case U_EMR_POLYLINETO16: U_EMRPOLYLINETO16_swap(record, torev); break; + case U_EMR_POLYPOLYLINE16: U_EMRPOLYPOLYLINE16_swap(record, torev); break; + case U_EMR_POLYPOLYGON16: U_EMRPOLYPOLYGON16_swap(record, torev); break; + case U_EMR_POLYDRAW16: U_EMRPOLYDRAW16_swap(record, torev); break; + case U_EMR_CREATEMONOBRUSH: U_EMRCREATEMONOBRUSH_swap(record, torev); break; + case U_EMR_CREATEDIBPATTERNBRUSHPT: U_EMRCREATEDIBPATTERNBRUSHPT_swap(record, torev); break; + case U_EMR_EXTCREATEPEN: U_EMREXTCREATEPEN_swap(record, torev); break; + case U_EMR_POLYTEXTOUTA: U_EMRPOLYTEXTOUTA_swap(record, torev); break; + case U_EMR_POLYTEXTOUTW: U_EMRPOLYTEXTOUTW_swap(record, torev); break; + case U_EMR_SETICMMODE: U_EMRSETICMMODE_swap(record, torev); break; + case U_EMR_CREATECOLORSPACE: U_EMRCREATECOLORSPACE_swap(record, torev); break; + case U_EMR_SETCOLORSPACE: U_EMRSETCOLORSPACE_swap(record, torev); break; + case U_EMR_DELETECOLORSPACE: U_EMRDELETECOLORSPACE_swap(record, torev); break; + case U_EMR_GLSRECORD: U_EMRGLSRECORD_swap(record, torev); break; + case U_EMR_GLSBOUNDEDRECORD: U_EMRGLSBOUNDEDRECORD_swap(record, torev); break; + case U_EMR_PIXELFORMAT: U_EMRPIXELFORMAT_swap(record, torev); break; + case U_EMR_DRAWESCAPE: U_EMRDRAWESCAPE_swap(record, torev); break; + case U_EMR_EXTESCAPE: U_EMREXTESCAPE_swap(record, torev); break; + case U_EMR_UNDEF107: U_EMRUNDEF107_swap(record, torev); break; + case U_EMR_SMALLTEXTOUT: U_EMRSMALLTEXTOUT_swap(record, torev); break; + case U_EMR_FORCEUFIMAPPING: U_EMRFORCEUFIMAPPING_swap(record, torev); break; + case U_EMR_NAMEDESCAPE: U_EMRNAMEDESCAPE_swap(record, torev); break; + case U_EMR_COLORCORRECTPALETTE: U_EMRCOLORCORRECTPALETTE_swap(record, torev); break; + case U_EMR_SETICMPROFILEA: U_EMRSETICMPROFILEA_swap(record, torev); break; + case U_EMR_SETICMPROFILEW: U_EMRSETICMPROFILEW_swap(record, torev); break; + case U_EMR_ALPHABLEND: U_EMRALPHABLEND_swap(record, torev); break; + case U_EMR_SETLAYOUT: U_EMRSETLAYOUT_swap(record, torev); break; + case U_EMR_TRANSPARENTBLT: U_EMRTRANSPARENTBLT_swap(record, torev); break; + case U_EMR_UNDEF117: U_EMRUNDEF117_swap(record, torev); break; + case U_EMR_GRADIENTFILL: U_EMRGRADIENTFILL_swap(record, torev); break; + case U_EMR_SETLINKEDUFIS: U_EMRSETLINKEDUFIS_swap(record, torev); break; + case U_EMR_SETTEXTJUSTIFICATION: U_EMRSETTEXTJUSTIFICATION_swap(record, torev); break; + case U_EMR_COLORMATCHTOTARGETW: U_EMRCOLORMATCHTOTARGETW_swap(record, torev); break; + case U_EMR_CREATECOLORSPACEW: U_EMRCREATECOLORSPACEW_swap(record, torev); break; + default: U_EMRNOTIMPLEMENTED_swap(record, torev); break; + } //end of switch + record += off; + recnum++; + } //end of while + + return(1); +} + +#ifdef __cplusplus +} +#endif diff --git a/src/libuemf/uemf_endian.h b/src/libuemf/uemf_endian.h new file mode 100644 index 000000000..9866aacaf --- /dev/null +++ b/src/libuemf/uemf_endian.h @@ -0,0 +1,37 @@ +/** + @file uemf_endian.h Prototype for function for converting EMF records between Big Endian and Little Endian +*/ + +/* +File: uemf_endian.h +Version: 0.0.3 +Date: 24-JUL-2012 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2012 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifndef _UEMF_ENDIAN_ +#define _UEMF_ENDIAN_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* There is no way for the preprocessor, in general, to figure out endianness. So the command line must define + WORDS_BIGENDIAN for a big endian machine. Otherwise we assume is is little endian. If it is something + else this code won't work in any case. */ +#ifdef WORDS_BIGENDIAN +#define U_BYTE_SWAP 1 +#else +#define U_BYTE_SWAP 0 +#endif + +// prototypes +int U_emf_endian(char *contents, size_t length, int torev); + +#ifdef __cplusplus +} +#endif + +#endif /* _UEMF_ENDIAN_ */ diff --git a/src/libuemf/uemf_print.c b/src/libuemf/uemf_print.c new file mode 100644 index 000000000..4ea9620cf --- /dev/null +++ b/src/libuemf/uemf_print.c @@ -0,0 +1,2381 @@ +/** + @file uemf_print.c Functions for printing EMF records +*/ + +/* +File: uemf_print.c +Version: 0.0.12 +Date: 04-FEB-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> /* for offsetof() macro */ +#include <string.h> +#include "uemf.h" + +/** + \brief Print some number of hex bytes + \param buf pointer to the first byte + \param num number of bytes +*/ +void hexbytes_print(uint8_t *buf,unsigned int num){ + for(; num; num--,buf++){ + printf("%2.2X",*buf); + } +} + +/* ********************************************************************************************** + These functions print standard objects used in the EMR records. + The low level ones do not append EOL. +*********************************************************************************************** */ + + + +/** + \brief Print a U_COLORREF object. + \param color U_COLORREF object +*/ +void colorref_print( + U_COLORREF color + ){ + printf("{%u,%u,%u} ",color.Red,color.Green,color.Blue); +} + + +/** + \brief Print a U_RGBQUAD object. + \param color U_RGBQUAD object +*/ +void rgbquad_print( + U_RGBQUAD color + ){ + printf("{%u,%u,%u,%u} ",color.Blue,color.Green,color.Red,color.Reserved); +} + +/** + \brief Print rect and rectl objects from Upper Left and Lower Right corner points. + \param rect U_RECTL object +*/ +void rectl_print( + U_RECTL rect + ){ + printf("{%d,%d,%d,%d} ",rect.left,rect.top,rect.right,rect.bottom); +} + +/** + \brief Print a U_SIZEL object. + \param sz U_SizeL object +*/ +void sizel_print( + U_SIZEL sz + ){ + printf("{%d,%d} ",sz.cx ,sz.cy); +} + +/** + \brief Print a U_POINTL object + \param pt U_POINTL object +*/ +void pointl_print( + U_POINTL pt + ){ + printf("{%d,%d} ",pt.x ,pt.y); +} + +/** + \brief Print a pointer to a U_POINT16 object + \param pt pointer to a U_POINT16 object + Warning - WMF data may contain unaligned U_POINT16, do not call + this routine with a pointer to such data! +*/ +void point16_print( + U_POINT16 pt + ){ + printf("{%d,%d} ",pt.x ,pt.y); +} + +/** + \brief Print a U_LCS_GAMMA object + \param lg U_LCS_GAMMA object +*/ +void lcs_gamma_print( + U_LCS_GAMMA lg + ){ + uint8_t tmp; + tmp = lg.ignoreHi; printf("ignoreHi:%u ",tmp); + tmp = lg.intPart ; printf("intPart :%u ",tmp); + tmp = lg.fracPart; printf("fracPart:%u ",tmp); + tmp = lg.ignoreLo; printf("ignoreLo:%u ",tmp); +} + +/** + \brief Print a U_LCS_GAMMARGB object + \param lgr U_LCS_GAMMARGB object +*/ +void lcs_gammargb_print( + U_LCS_GAMMARGB lgr + ){ + printf("lcsGammaRed:"); lcs_gamma_print(lgr.lcsGammaRed ); + printf("lcsGammaGreen:"); lcs_gamma_print(lgr.lcsGammaGreen); + printf("lcsGammaBlue:"); lcs_gamma_print(lgr.lcsGammaBlue ); +} + +/** + \brief Print a U_TRIVERTEX object. + \param tv U_TRIVERTEX object. +*/ +void trivertex_print( + U_TRIVERTEX tv + ){ + printf("{{%d,%d},{%u,%u,%u,%u}} ",tv.x,tv.y,tv.Red,tv.Green,tv.Blue,tv.Alpha); +} + +/** + \brief Print a U_GRADIENT3 object. + \param g3 U_GRADIENT3 object. +*/ +void gradient3_print( + U_GRADIENT3 g3 + ){ + printf("{%u,%u,%u} ",g3.Vertex1,g3.Vertex2,g3.Vertex3); +} + +/** + \brief Print a U_GRADIENT4 object. + \param g4 U_GRADIENT4 object. +*/ +void gradient4_print( + U_GRADIENT4 g4 + ){ + printf("{%u,%u} ",g4.UpperLeft,g4.LowerRight); +} + +/** + \brief Print a U_LOGBRUSH object. + \param lb U_LOGBRUSH object. +*/ +void logbrush_print( + U_LOGBRUSH lb + ){ + printf("lbStyle:0x%8.8X ", lb.lbStyle); + printf("lbColor:"); colorref_print(lb.lbColor); + printf("lbHatch:0x%8.8X ", lb.lbHatch); +} + +/** + \brief Print a U_XFORM object. + \param xform U_XFORM object +*/ +void xform_print( + U_XFORM xform + ){ + printf("{%f,%f.%f,%f,%f,%f} ",xform.eM11,xform.eM12,xform.eM21,xform.eM22,xform.eDx,xform.eDy); +} + +/** + \brief Print a U_CIEXYZ object + \param ciexyz U_CIEXYZ object +*/ +void ciexyz_print( + U_CIEXYZ ciexyz + ){ + printf("{%d,%d.%d} ",ciexyz.ciexyzX,ciexyz.ciexyzY,ciexyz.ciexyzZ); + +} + +/** + \brief Print a U_CIEXYZTRIPLE object + \param cie3 U_CIEXYZTRIPLE object +*/ +void ciexyztriple_print( + U_CIEXYZTRIPLE cie3 + ){ + printf("{Red:"); ciexyz_print(cie3.ciexyzRed ); + printf(", Green:"); ciexyz_print(cie3.ciexyzGreen); + printf(", Blue:"); ciexyz_print(cie3.ciexyzBlue ); + printf("} "); +} +/** + \brief Print a U_LOGCOLORSPACEA object. + \param lcsa U_LOGCOLORSPACEA object +*/ +void logcolorspacea_print( + U_LOGCOLORSPACEA lcsa + ){ + printf("lcsSignature:%u ",lcsa.lcsSignature); + printf("lcsVersion:%u ", lcsa.lcsVersion ); + printf("lcsSize:%u ", lcsa.lcsSize ); + printf("lcsCSType:%d ", lcsa.lcsCSType ); + printf("lcsIntent:%d ", lcsa.lcsIntent ); + printf("lcsEndpoints:"); ciexyztriple_print(lcsa.lcsEndpoints); + printf("lcsGammaRGB: "); lcs_gammargb_print(lcsa.lcsGammaRGB ); + printf("filename:%s ", lcsa.lcsFilename ); +} + +/** + + \brief Print a U_LOGCOLORSPACEW object. + \param lcsa U_LOGCOLORSPACEW object +*/ +void logcolorspacew_print( + U_LOGCOLORSPACEW lcsa + ){ + char *string; + printf("lcsSignature:%d ",lcsa.lcsSignature); + printf("lcsVersion:%d ", lcsa.lcsVersion ); + printf("lcsSize:%d ", lcsa.lcsSize ); + printf("lcsCSType:%d ", lcsa.lcsCSType ); + printf("lcsIntent:%d ", lcsa.lcsIntent ); + printf("lcsEndpoints:"); ciexyztriple_print(lcsa.lcsEndpoints); + printf("lcsGammaRGB: "); lcs_gammargb_print(lcsa.lcsGammaRGB ); + string = U_Utf16leToUtf8(lcsa.lcsFilename, U_MAX_PATH, NULL); + printf("filename:%s ", string ); + free(string); +} + +/** + \brief Print a U_PANOSE object. + \param panose U_PANOSE object +*/ +void panose_print( + U_PANOSE panose + ){ + printf("bFamilyType:%u ", panose.bFamilyType ); + printf("bSerifStyle:%u ", panose.bSerifStyle ); + printf("bWeight:%u ", panose.bWeight ); + printf("bProportion:%u ", panose.bProportion ); + printf("bContrast:%u ", panose.bContrast ); + printf("bStrokeVariation:%u ",panose.bStrokeVariation); + printf("bArmStyle:%u ", panose.bArmStyle ); + printf("bLetterform:%u ", panose.bLetterform ); + printf("bMidline:%u ", panose.bMidline ); + printf("bXHeight:%u ", panose.bXHeight ); +} + +/** + \brief Print a U_LOGFONT object. + \param lf U_LOGFONT object +*/ +void logfont_print( + U_LOGFONT lf + ){ + char *string; + printf("lfHeight:%d ", lf.lfHeight ); + printf("lfWidth:%d ", lf.lfWidth ); + printf("lfEscapement:%d ", lf.lfEscapement ); + printf("lfOrientation:%d ", lf.lfOrientation ); + printf("lfWeight:%d ", lf.lfWeight ); + printf("lfItalic:0x%2.2X ", lf.lfItalic ); + printf("lfUnderline:0x%2.2X ", lf.lfUnderline ); + printf("lfStrikeOut:0x%2.2X ", lf.lfStrikeOut ); + printf("lfCharSet:0x%2.2X ", lf.lfCharSet ); + printf("lfOutPrecision:0x%2.2X ", lf.lfOutPrecision ); + printf("lfClipPrecision:0x%2.2X ", lf.lfClipPrecision ); + printf("lfQuality:0x%2.2X ", lf.lfQuality ); + printf("lfPitchAndFamily:0x%2.2X ", lf.lfPitchAndFamily); + string = U_Utf16leToUtf8(lf.lfFaceName, U_LF_FACESIZE, NULL); + printf("lfFaceName:%s ", string ); + free(string); +} + +/** + \brief Print a U_LOGFONT_PANOSE object. + \return U_LOGFONT_PANOSE object +*/ +void logfont_panose_print( + U_LOGFONT_PANOSE lfp + ){ + char *string; + printf("elfLogFont:"); logfont_print(lfp.elfLogFont); + string = U_Utf16leToUtf8(lfp.elfFullName, U_LF_FULLFACESIZE, NULL); + printf("elfFullName:%s ", string ); + free(string); + string = U_Utf16leToUtf8(lfp.elfStyle, U_LF_FACESIZE, NULL); + printf("elfStyle:%s ", string ); + free(string); + printf("elfVersion:%u " ,lfp.elfVersion ); + printf("elfStyleSize:%u " ,lfp.elfStyleSize); + printf("elfMatch:%u " ,lfp.elfMatch ); + printf("elfReserved:%u " ,lfp.elfReserved ); + printf("elfVendorId:"); hexbytes_print((uint8_t *)lfp.elfVendorId,U_ELF_VENDOR_SIZE); printf(" "); + printf("elfCulture:%u " ,lfp.elfCulture ); + printf("elfPanose:"); panose_print(lfp.elfPanose); +} + +/** + \brief Print a pointer to U_BITMAPINFOHEADER object. + + This may be called indirectly from WMF _print routines, where problems could occur + if the data was passed as the struct or a pointer to the struct, as the struct may not + be aligned in memory. + + \returns Actual number of color table entries. + \param Bmih pointer to a U_BITMAPINFOHEADER object +*/ +int bitmapinfoheader_print( + const char *Bmih + ){ + uint32_t utmp4; + int32_t tmp4; + int16_t tmp2; + int Colors, BitCount, Width, Height, RealColors; + + /* DIB from a WMF may not be properly aligned on a 4 byte boundary, will be aligned on a 2 byte boundary */ + + memcpy(&utmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biSize), 4); printf("biSize:%u " ,utmp4 ); + memcpy(&tmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biWidth), 4); printf("biWidth:%d " ,tmp4 ); + Width = tmp4; + memcpy(&tmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biHeight), 4); printf("biHeight:%d " ,tmp4 ); + Height = tmp4; + memcpy(&tmp2, Bmih + offsetof(U_BITMAPINFOHEADER,biPlanes), 2); printf("biPlanes:%u " ,tmp2 ); + memcpy(&tmp2, Bmih + offsetof(U_BITMAPINFOHEADER,biBitCount), 2); printf("biBitCount:%u " ,tmp2 ); + BitCount = tmp2; + memcpy(&utmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biCompression), 4); printf("biCompression:%u " ,utmp4 ); + memcpy(&utmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biSizeImage), 4); printf("biSizeImage:%u " ,utmp4 ); + memcpy(&tmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biXPelsPerMeter), 4); printf("biXPelsPerMeter:%d " ,tmp4 ); + memcpy(&tmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biYPelsPerMeter), 4); printf("biYPelsPerMeter:%d " ,tmp4 ); + memcpy(&utmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biClrUsed), 4); printf("biClrUsed:%u " ,utmp4 ); + Colors = utmp4; + memcpy(&utmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biClrImportant), 4); printf("biClrImportant:%u " ,utmp4 ); + RealColors = get_real_color_icount(Colors, BitCount, Width, Height); + printf("ColorEntries:%d ",RealColors); + return(RealColors); +} + + +/** + \brief Print a Pointer to a U_BITMAPINFO object. + \param Bmi Pointer to a U_BITMAPINFO object + This may be called from WMF _print routines, where problems could occur + if the data was passed as the struct or a pointer to the struct, as the struct may not + be aligned in memory. +*/ +void bitmapinfo_print( + const char *Bmi + ){ + int i,k; + int ClrUsed; + U_RGBQUAD BmiColor; + printf("BmiHeader: "); + ClrUsed = bitmapinfoheader_print(Bmi + offsetof(U_BITMAPINFO,bmiHeader)); + if(ClrUsed){ + k= offsetof(U_BITMAPINFO,bmiColors); + for(i=0; i<ClrUsed; i++, k+= sizeof(U_RGBQUAD)){ + memcpy(&BmiColor, Bmi+k, sizeof(U_RGBQUAD)); + printf("%d:",i); rgbquad_print(BmiColor); + } + } +} + +/** + \brief Print a U_BLEND object. + \param blend a U_BLEND object +*/ +void blend_print( + U_BLEND blend + ){ + printf("Operation:%u " ,blend.Operation); + printf("Flags:%u " ,blend.Flags ); + printf("Global:%u " ,blend.Global ); + printf("Op:%u " ,blend.Op ); +} + +/** + \brief Print a pointer to a U_EXTLOGPEN object. + \param elp PU_EXTLOGPEN object +*/ +void extlogpen_print( + PU_EXTLOGPEN elp + ){ + unsigned i; + U_STYLEENTRY *elpStyleEntry; + printf("elpPenStyle:0x%8.8X " ,elp->elpPenStyle ); + printf("elpWidth:%u " ,elp->elpWidth ); + printf("elpBrushStyle:0x%8.8X " ,elp->elpBrushStyle); + printf("elpColor"); colorref_print(elp->elpColor); + printf("elpHatch:%d " ,elp->elpHatch ); + printf("elpNumEntries:%u " ,elp->elpNumEntries); + if(elp->elpNumEntries){ + printf("elpStyleEntry:"); + elpStyleEntry = (uint32_t *) elp->elpStyleEntry; + for(i=0;i<elp->elpNumEntries;i++){ + printf("%d:%u ",i,elpStyleEntry[i]); + } + } +} + +/** + \brief Print a U_LOGPEN object. + \param lp U_LOGPEN object + +*/ +void logpen_print( + U_LOGPEN lp + ){ + printf("lopnStyle:%u " ,lp.lopnStyle ); + printf("lopnWidth:"); pointl_print( lp.lopnWidth ); + printf("lopnColor:"); colorref_print(lp.lopnColor ); +} + +/** + \brief Print a U_LOGPLTNTRY object. + \param lpny Ignore U_LOGPLTNTRY object. +*/ +void logpltntry_print( + U_LOGPLTNTRY lpny + ){ + printf("peReserved:%u " ,lpny.peReserved ); + printf("peRed:%u " ,lpny.peRed ); + printf("peGreen:%u " ,lpny.peGreen ); + printf("peBlue:%u " ,lpny.peBlue ); +} + +/** + \brief Print a pointer to a U_LOGPALETTE object. + \param lp Pointer to a U_LOGPALETTE object. +*/ +void logpalette_print( + PU_LOGPALETTE lp + ){ + int i; + PU_LOGPLTNTRY palPalEntry; + printf("palVersion:%u ", lp->palVersion ); + printf("palNumEntries:%u ", lp->palNumEntries ); + if(lp->palNumEntries){ + palPalEntry = (PU_LOGPLTNTRY) &(lp->palPalEntry); + for(i=0;i<lp->palNumEntries;i++){ + printf("%d:",i); logpltntry_print(palPalEntry[i]); + } + } +} + +/** + \brief Print a U_RGNDATAHEADER object. + \param rdh U_RGNDATAHEADER object +*/ +void rgndataheader_print( + U_RGNDATAHEADER rdh + ){ + printf("dwSize:%u ", rdh.dwSize ); + printf("iType:%u ", rdh.iType ); + printf("nCount:%u ", rdh.nCount ); + printf("nRgnSize:%u ", rdh.nRgnSize ); + printf("rclBounds:"); rectl_print(rdh.rclBounds ); +} + +/** + \brief Print a pointer to a U_RGNDATA object. + \param rd pointer to a U_RGNDATA object. +*/ +void rgndata_print( + PU_RGNDATA rd + ){ + unsigned i; + PU_RECTL rects; + printf("rdh:"); rgndataheader_print(rd->rdh ); + if(rd->rdh.nCount){ + rects = (PU_RECTL) &(rd->Buffer); + for(i=0;i<rd->rdh.nCount;i++){ + printf("%d:",i); rectl_print(rects[i]); + } + } +} + +/** + \brief Print a U_COLORADJUSTMENT object. + \param ca U_COLORADJUSTMENT object. +*/ +void coloradjustment_print( + U_COLORADJUSTMENT ca + ){ + printf("caSize:%u " ,ca.caSize ); + printf("caFlags:%u " ,ca.caFlags ); + printf("caIlluminantIndex:%u " ,ca.caIlluminantIndex); + printf("caRedGamma:%u " ,ca.caRedGamma ); + printf("caGreenGamma:%u " ,ca.caGreenGamma ); + printf("caBlueGamma:%u " ,ca.caBlueGamma ); + printf("caReferenceBlack:%u " ,ca.caReferenceBlack ); + printf("caReferenceWhite:%u " ,ca.caReferenceWhite ); + printf("caContrast:%d " ,ca.caContrast ); + printf("caBrightness:%d " ,ca.caBrightness ); + printf("caColorfulness:%d " ,ca.caColorfulness ); + printf("caRedGreenTint:%d " ,ca.caRedGreenTint ); +} + +/** + \brief Print a U_PIXELFORMATDESCRIPTOR object. + \param pfd U_PIXELFORMATDESCRIPTOR object +*/ +void pixelformatdescriptor_print( + U_PIXELFORMATDESCRIPTOR pfd + ){ + printf("nSize:%u " ,pfd.nSize ); + printf("nVersion:%u " ,pfd.nVersion ); + printf("dwFlags:%u " ,pfd.dwFlags ); + printf("iPixelType:%u " ,pfd.iPixelType ); + printf("cColorBits:%u " ,pfd.cColorBits ); + printf("cRedBits:%u " ,pfd.cRedBits ); + printf("cRedShift:%u " ,pfd.cRedShift ); + printf("cGreenBits:%u " ,pfd.cGreenBits ); + printf("cGreenShift:%u " ,pfd.cGreenShift ); + printf("cBlueBits:%u " ,pfd.cBlueBits ); + printf("cBlueShift:%u " ,pfd.cBlueShift ); + printf("cAlphaBits:%u " ,pfd.cAlphaBits ); + printf("cAlphaShift:%u " ,pfd.cAlphaShift ); + printf("cAccumBits:%u " ,pfd.cAccumBits ); + printf("cAccumRedBits:%u " ,pfd.cAccumRedBits ); + printf("cAccumGreenBits:%u " ,pfd.cAccumGreenBits ); + printf("cAccumBlueBits:%u " ,pfd.cAccumBlueBits ); + printf("cAccumAlphaBits:%u " ,pfd.cAccumAlphaBits ); + printf("cDepthBits:%u " ,pfd.cDepthBits ); + printf("cStencilBits:%u " ,pfd.cStencilBits ); + printf("cAuxBuffers:%u " ,pfd.cAuxBuffers ); + printf("iLayerType:%u " ,pfd.iLayerType ); + printf("bReserved:%u " ,pfd.bReserved ); + printf("dwLayerMask:%u " ,pfd.dwLayerMask ); + printf("dwVisibleMask:%u " ,pfd.dwVisibleMask ); + printf("dwDamageMask:%u " ,pfd.dwDamageMask ); +} + +/** + \brief Print a Pointer to a U_EMRTEXT record + \param emt Pointer to a U_EMRTEXT record + \param record Pointer to the start of the record which contains this U_ERMTEXT + \param type 0 for 8 bit character, anything else for 16 +*/ +void emrtext_print( + const char *emt, + const char *record, + int type + ){ + unsigned i, off; + char *string; + PU_EMRTEXT pemt = (PU_EMRTEXT) emt; + // constant part + printf("ptlReference:"); pointl_print(pemt->ptlReference); + printf("nChars:%u " ,pemt->nChars ); + printf("offString:%u " ,pemt->offString ); + if(pemt->offString){ + if(!type){ + printf("string8:<%s> ",record + pemt->offString); + } + else { + string = U_Utf16leToUtf8((uint16_t *)(record + pemt->offString), pemt->nChars, NULL); + printf("string16:<%s> ",string); + free(string); + } + } + printf("fOptions:%u " ,pemt->fOptions ); + off = sizeof(U_EMRTEXT); + if(!(pemt->fOptions & U_ETO_NO_RECT)){ + printf("rcl"); rectl_print( *((U_RECTL *)(emt+off)) ); + off += sizeof(U_RECTL); + } + printf("offDx:%u " , *((U_OFFDX *)(emt+off)) ); off = *(U_OFFDX *)(emt+off); + printf("Dx:"); + for(i=0; i<pemt->nChars; i++, off+=sizeof(uint32_t)){ + printf("%d:", *((uint32_t *)(record+off)) ); + } +} + + + + +// hide these from Doxygen +//! @cond +/* ********************************************************************************************** +These functions contain shared code used by various U_EMR*_print functions. These should NEVER be called +by end user code and to further that end prototypes are NOT provided and they are hidden from Doxygen. + + + These are (mostly) ordered by U_EMR_* index number. + + The exceptions: + void core3_print(const char *name, const char *label, const char *contents) + void core7_print(const char *name, const char *field1, const char *field2, const char *contents) + void core8_print(const char *name, const char *contents, int type) + + +*********************************************************************************************** */ + + +// Functions with the same form starting with U_EMRPOLYBEZIER_print +void core1_print(const char *name, const char *contents){ + unsigned i; + (void) name; + PU_EMRPOLYLINETO pEmr = (PU_EMRPOLYLINETO) (contents); + printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); + printf(" cptl: %d\n",pEmr->cptl ); + printf(" Points: "); + for(i=0;i<pEmr->cptl; i++){ + printf("[%d]:",i); pointl_print(pEmr->aptl[i]); + } + printf("\n"); +} + +// Functions with the same form starting with U_EMRPOLYPOLYLINE_print +void core2_print(const char *name, const char *contents){ + unsigned i; + (void) name; + PU_EMRPOLYPOLYGON pEmr = (PU_EMRPOLYPOLYGON) (contents); + printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); + printf(" nPolys: %d\n",pEmr->nPolys ); + printf(" cptl: %d\n",pEmr->cptl ); + printf(" Counts: "); + for(i=0;i<pEmr->nPolys; i++){ + printf(" [%d]:%d ",i,pEmr->aPolyCounts[i] ); + } + printf("\n"); + PU_POINTL paptl = (PU_POINTL)((char *)pEmr->aPolyCounts + sizeof(uint32_t)* pEmr->nPolys); + printf(" Points: "); + for(i=0;i<pEmr->cptl; i++){ + printf(" [%d]:",i); pointl_print(paptl[i]); + } + printf("\n"); +} + + +// Functions with the same form starting with U_EMRSETMAPMODE_print +void core3_print(const char *name, const char *label, const char *contents){ + PU_EMRSETMAPMODE pEmr = (PU_EMRSETMAPMODE)(contents); + (void) name; + if(!strcmp(label,"crColor:")){ + printf(" %-15s ",label); colorref_print(*(U_COLORREF *)&(pEmr->iMode)); printf("\n"); + } + else if(!strcmp(label,"iMode:")){ + printf(" %-15s 0x%8.8X\n",label,pEmr->iMode ); + } + else { + printf(" %-15s %d\n",label,pEmr->iMode ); + } +} + +// Functions taking a single U_RECT or U_RECTL, starting with U_EMRELLIPSE_print, also U_EMRFILLPATH_print, +void core4_print(const char *name, const char *contents){ + PU_EMRELLIPSE pEmr = (PU_EMRELLIPSE)( contents); + (void) name; + printf(" rclBox: "); rectl_print(pEmr->rclBox); printf("\n"); +} + +// Functions with the same form starting with U_EMRPOLYBEZIER16_print +void core6_print(const char *name, const char *contents){ + unsigned i; + (void) name; + PU_EMRPOLYBEZIER16 pEmr = (PU_EMRPOLYBEZIER16) (contents); + printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); + printf(" cpts: %d\n",pEmr->cpts ); + printf(" Points: "); + PU_POINT16 papts = (PU_POINT16)(&(pEmr->apts)); + for(i=0; i<pEmr->cpts; i++){ + printf(" [%d]:",i); point16_print(papts[i]); + } + printf("\n"); +} + + +// Records with the same form starting with U_EMRSETWINDOWEXTEX_print +// CAREFUL, in the _set equivalents all functions with two uint32_t values are mapped here, and member names differ, consequently +// print routines must supply the names of the two arguments. These cannot be null. If the second one is +// empty the values are printed as a pair {x,y}, otherwise each is printed with its own label on a separate line. +void core7_print(const char *name, const char *field1, const char *field2, const char *contents){ + PU_EMRGENERICPAIR pEmr = (PU_EMRGENERICPAIR) (contents); + (void) name; + if(*field2){ + printf(" %-15s %d\n",field1,pEmr->pair.x); + printf(" %-15s %d\n",field2,pEmr->pair.y); + } + else { + printf(" %-15s {%d,%d}\n",field1,pEmr->pair.x,pEmr->pair.y); + } +} + +// For U_EMREXTTEXTOUTA and U_EMREXTTEXTOUTW, type=0 for the first one +void core8_print(const char *name, const char *contents, int type){ + PU_EMREXTTEXTOUTA pEmr = (PU_EMREXTTEXTOUTA) (contents); + (void) name; + printf(" iGraphicsMode: %u\n",pEmr->iGraphicsMode ); + printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); + printf(" exScale: %f\n",pEmr->exScale ); + printf(" eyScale: %f\n",pEmr->eyScale ); + printf(" emrtext: "); + emrtext_print(contents + sizeof(U_EMREXTTEXTOUTA) - sizeof(U_EMRTEXT),contents,type); + printf("\n"); +} + +// Functions that take a rect and a pair of points, starting with U_EMRARC_print +void core9_print(const char *name, const char *contents){ + PU_EMRARC pEmr = (PU_EMRARC) (contents); + (void) name; + printf(" rclBox: "); rectl_print(pEmr->rclBox); printf("\n"); + printf(" ptlStart: "); pointl_print(pEmr->ptlStart); printf("\n"); + printf(" ptlEnd: "); pointl_print(pEmr->ptlEnd); printf("\n"); +} + +// Functions with the same form starting with U_EMRPOLYPOLYLINE16_print +void core10_print(const char *name, const char *contents){ + unsigned i; + PU_EMRPOLYPOLYLINE16 pEmr = (PU_EMRPOLYPOLYLINE16) (contents); + (void) name; + printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); + printf(" nPolys: %d\n",pEmr->nPolys ); + printf(" cpts: %d\n",pEmr->cpts ); + printf(" Counts: "); + for(i=0;i<pEmr->nPolys; i++){ + printf(" [%d]:%d ",i,pEmr->aPolyCounts[i] ); + } + printf("\n"); + printf(" Points: "); + PU_POINT16 papts = (PU_POINT16)((char *)pEmr->aPolyCounts + sizeof(uint32_t)* pEmr->nPolys); + for(i=0; i<pEmr->cpts; i++){ + printf(" [%d]:",i); point16_print(papts[i]); + } + printf("\n"); + +} + +// Functions with the same form starting with U_EMRINVERTRGN_print and U_EMRPAINTRGN_print, +void core11_print(const char *name, const char *contents){ + unsigned i,roff; + PU_EMRINVERTRGN pEmr = (PU_EMRINVERTRGN) (contents); + (void) name; + printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); + printf(" cbRgnData: %d\n",pEmr->cbRgnData); + // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. + roff=0; + i=1; + char *prd = (char *) &(pEmr->RgnData); + while(roff + 28 < pEmr->emr.nSize){ // up to the end of the record + printf(" RegionData:%d",i); + rgndata_print((PU_RGNDATA) (prd + roff)); + roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + printf("\n"); + } +} + + +// common code for U_EMRCREATEMONOBRUSH_print and U_EMRCREATEDIBPATTERNBRUSHPT_print, +void core12_print(const char *name, const char *contents){ + PU_EMRCREATEMONOBRUSH pEmr = (PU_EMRCREATEMONOBRUSH) (contents); + (void) name; + printf(" ihBrush: %u\n",pEmr->ihBrush ); + printf(" iUsage : %u\n",pEmr->iUsage ); + printf(" offBmi : %u\n",pEmr->offBmi ); + printf(" cbBmi : %u\n",pEmr->cbBmi ); + if(pEmr->cbBmi){ + printf(" bitmap:"); + bitmapinfo_print(contents + pEmr->offBmi); + printf("\n"); + } + printf(" offBits: %u\n",pEmr->offBits ); + printf(" cbBits : %u\n",pEmr->cbBits ); +} + +// common code for U_EMRALPHABLEND_print and U_EMRTRANSPARENTBLT_print, +void core13_print(const char *name, const char *contents){ + PU_EMRALPHABLEND pEmr = (PU_EMRALPHABLEND) (contents); + (void) name; + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); + printf(" cDest: "); pointl_print(pEmr->cDest); printf("\n"); + printf(" Blend: "); blend_print(pEmr->Blend); printf("\n"); + printf(" Src: "); pointl_print(pEmr->Src); printf("\n"); + printf(" xformSrc: "); xform_print( pEmr->xformSrc); printf("\n"); + printf(" crBkColorSrc: "); colorref_print( pEmr->crBkColorSrc); printf("\n"); + printf(" iUsageSrc: %u\n",pEmr->iUsageSrc ); + printf(" offBmiSrc: %u\n",pEmr->offBmiSrc ); + printf(" cbBmiSrc: %u\n",pEmr->cbBmiSrc ); + if(pEmr->cbBmiSrc){ + printf(" bitmap:"); + bitmapinfo_print(contents + pEmr->offBmiSrc); + printf("\n"); + } + printf(" offBitsSrc: %u\n",pEmr->offBitsSrc ); + printf(" cbBitsSrc: %u\n",pEmr->cbBitsSrc ); +} +//! @endcond + +/* ********************************************************************************************** +These are the core EMR functions, each creates a particular type of record. +All return these records via a char* pointer, which is NULL if the call failed. +They are listed in order by the corresponding U_EMR_* index number. +*********************************************************************************************** */ + +/** + \brief Print a pointer to a U_EMR_whatever record which has not been implemented. + \param name name of this type of record + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRNOTIMPLEMENTED_print(const char *name, const char *contents){ + (void) name; + (void) contents; + printf(" Not Implemented!\n"); +} + +// U_EMRHEADER 1 +/** + \brief Print a pointer to a U_EMR_HEADER record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRHEADER_print(const char *contents){ + char *string; + int p1len; + + PU_EMRHEADER pEmr = (PU_EMRHEADER)(contents); + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" rclFrame: "); rectl_print( pEmr->rclFrame); printf("\n"); + printf(" dSignature: 0x%8.8X\n", pEmr->dSignature ); + printf(" nVersion: 0x%8.8X\n", pEmr->nVersion ); + printf(" nBytes: %d\n", pEmr->nBytes ); + printf(" nRecords: %d\n", pEmr->nRecords ); + printf(" nHandles: %d\n", pEmr->nHandles ); + printf(" sReserved: %d\n", pEmr->sReserved ); + printf(" nDescription: %d\n", pEmr->nDescription ); + printf(" offDescription: %d\n", pEmr->offDescription); + if(pEmr->offDescription){ + string = U_Utf16leToUtf8((uint16_t *)((char *) pEmr + pEmr->offDescription), pEmr->nDescription, NULL); + printf(" Desc. A: %s\n",string); + free(string); + p1len = 2 + 2*wchar16len((uint16_t *)((char *) pEmr + pEmr->offDescription)); + string = U_Utf16leToUtf8((uint16_t *)((char *) pEmr + pEmr->offDescription + p1len), pEmr->nDescription, NULL); + printf(" Desc. B: %s\n",string); + free(string); + } + printf(" nPalEntries: %d\n", pEmr->nPalEntries ); + printf(" szlDevice: {%d,%d} \n", pEmr->szlDevice.cx,pEmr->szlDevice.cy); + printf(" szlMillimeters: {%d,%d} \n", pEmr->szlMillimeters.cx,pEmr->szlMillimeters.cy); + if((pEmr->nDescription && (pEmr->offDescription >= 100)) || + (!pEmr->offDescription && pEmr->emr.nSize >= 100) + ){ + printf(" cbPixelFormat: %d\n", pEmr->cbPixelFormat ); + printf(" offPixelFormat: %d\n", pEmr->offPixelFormat); + if(pEmr->cbPixelFormat){ + printf(" PFD:"); + pixelformatdescriptor_print( *(PU_PIXELFORMATDESCRIPTOR) (contents + pEmr->offPixelFormat)); + printf("\n"); + } + printf(" bOpenGL: %d\n",pEmr->bOpenGL ); + if((pEmr->nDescription && (pEmr->offDescription >= 108)) || + (pEmr->cbPixelFormat && (pEmr->offPixelFormat >=108)) || + (!pEmr->offDescription && !pEmr->cbPixelFormat && pEmr->emr.nSize >= 108) + ){ + printf(" szlMicrometers: {%d,%d} \n", pEmr->szlMicrometers.cx,pEmr->szlMicrometers.cy); + } + } +} + +// U_EMRPOLYBEZIER 2 +/** + \brief Print a pointer to a U_EMR_POLYBEZIER record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYBEZIER_print(const char *contents){ + core1_print("U_EMRPOLYBEZIER", contents); +} + +// U_EMRPOLYGON 3 +/** + \brief Print a pointer to a U_EMR_POLYGON record. + \param contents pointer to a buffer holding all EMR records + */ +void U_EMRPOLYGON_print(const char *contents){ + core1_print("U_EMRPOLYGON", contents); +} + + +// U_EMRPOLYLINE 4 +/** + \brief Print a pointer to a U_EMR_POLYLINE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYLINE_print(const char *contents){ + core1_print("U_EMRPOLYLINE", contents); +} + +// U_EMRPOLYBEZIERTO 5 +/** + \brief Print a pointer to a U_EMR_POLYBEZIERTO record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYBEZIERTO_print(const char *contents){ + core1_print("U_EMRPOLYBEZIERTO", contents); +} + +// U_EMRPOLYLINETO 6 +/** + \brief Print a pointer to a U_EMR_POLYLINETO record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYLINETO_print(const char *contents){ + core1_print("U_EMRPOLYLINETO", contents); +} + +// U_EMRPOLYPOLYLINE 7 +/** + \brief Print a pointer to a U_EMR_POLYPOLYLINE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYPOLYLINE_print(const char *contents){ + core2_print("U_EMRPOLYPOLYLINE", contents); +} + +// U_EMRPOLYPOLYGON 8 +/** + \brief Print a pointer to a U_EMR_POLYPOLYGON record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYPOLYGON_print(const char *contents){ + core2_print("U_EMRPOLYPOLYGON", contents); +} + +// U_EMRSETWINDOWEXTEX 9 +/** + \brief Print a pointer to a U_EMR_SETWINDOWEXTEX record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETWINDOWEXTEX_print(const char *contents){ + core7_print("U_EMRSETWINDOWEXTEX", "szlExtent:","",contents); +} + +// U_EMRSETWINDOWORGEX 10 +/** + \brief Print a pointer to a U_EMR_SETWINDOWORGEX record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETWINDOWORGEX_print(const char *contents){ + core7_print("U_EMRSETWINDOWORGEX", "ptlOrigin:","",contents); +} + +// U_EMRSETVIEWPORTEXTEX 11 +/** + \brief Print a pointer to a U_EMR_SETVIEWPORTEXTEX record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETVIEWPORTEXTEX_print(const char *contents){ + core7_print("U_EMRSETVIEWPORTEXTEX", "szlExtent:","",contents); +} + +// U_EMRSETVIEWPORTORGEX 12 +/** + \brief Print a pointer to a U_EMR_SETVIEWPORTORGEX record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETVIEWPORTORGEX_print(const char *contents){ + core7_print("U_EMRSETVIEWPORTORGEX", "ptlOrigin:","",contents); +} + +// U_EMRSETBRUSHORGEX 13 +/** + \brief Print a pointer to a U_EMR_SETBRUSHORGEX record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETBRUSHORGEX_print(const char *contents){ + core7_print("U_EMRSETBRUSHORGEX", "ptlOrigin:","",contents); +} + +// U_EMREOF 14 +/** + \brief Print a pointer to a U_EMR_EOF record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMREOF_print(const char *contents){ + PU_EMREOF pEmr = (PU_EMREOF)(contents); + printf(" cbPalEntries: %u\n", pEmr->cbPalEntries ); + printf(" offPalEntries: %u\n", pEmr->offPalEntries); + if(pEmr->cbPalEntries){ + printf(" PE:"); + logpalette_print( (PU_LOGPALETTE)(contents + pEmr->offPalEntries)); + printf("\n"); + } +} + + +// U_EMRSETPIXELV 15 +/** + \brief Print a pointer to a U_EMR_SETPIXELV record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETPIXELV_print(const char *contents){ + PU_EMRSETPIXELV pEmr = (PU_EMRSETPIXELV)(contents); + printf(" ptlPixel: "); pointl_print( pEmr->ptlPixel); printf("\n"); + printf(" crColor: "); colorref_print(pEmr->crColor); printf("\n"); +} + + +// U_EMRSETMAPPERFLAGS 16 +/** + \brief Print a pointer to a U_EMR_SETMAPPERFLAGS record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETMAPPERFLAGS_print(const char *contents){ + PU_EMRSETMAPPERFLAGS pEmr = (PU_EMRSETMAPPERFLAGS)(contents); + printf(" dwFlags: %u\n",pEmr->dwFlags); +} + + +// U_EMRSETMAPMODE 17 +/** + \brief Print a pointer to a U_EMR_SETMAPMODE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETMAPMODE_print(const char *contents){ + core3_print("U_EMRSETMAPMODE", "iMode:", contents); +} + +// U_EMRSETBKMODE 18 +/** + \brief Print a pointer to a U_EMR_SETBKMODE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETBKMODE_print(const char *contents){ + core3_print("U_EMRSETBKMODE", "iMode:", contents); +} + +// U_EMRSETPOLYFILLMODE 19 +/** + \brief Print a pointer to a U_EMR_SETPOLYFILLMODE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETPOLYFILLMODE_print(const char *contents){ + core3_print("U_EMRSETPOLYFILLMODE", "iMode:", contents); +} + +// U_EMRSETROP2 20 +/** + \brief Print a pointer to a U_EMR_SETROP2 record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETROP2_print(const char *contents){ + core3_print("U_EMRSETROP2", "dwRop:", contents); +} + +// U_EMRSETSTRETCHBLTMODE 21 +/** + \brief Print a pointer to a U_EMR_SETSTRETCHBLTMODE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETSTRETCHBLTMODE_print(const char *contents){ + core3_print("U_EMRSETSTRETCHBLTMODE", "iMode:", contents); +} + +// U_EMRSETTEXTALIGN 22 +/** + \brief Print a pointer to a U_EMR_SETTEXTALIGN record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETTEXTALIGN_print(const char *contents){ + core3_print("U_EMRSETTEXTALIGN", "iMode:", contents); +} + +// U_EMRSETCOLORADJUSTMENT 23 +/** + \brief Print a pointer to a U_EMR_SETCOLORADJUSTMENT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETCOLORADJUSTMENT_print(const char *contents){ + PU_EMRSETCOLORADJUSTMENT pEmr = (PU_EMRSETCOLORADJUSTMENT)(contents); + printf(" ColorAdjustment:"); + coloradjustment_print(pEmr->ColorAdjustment); + printf("\n"); +} + +// U_EMRSETTEXTCOLOR 24 +/** + \brief Print a pointer to a U_EMR_SETTEXTCOLOR record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETTEXTCOLOR_print(const char *contents){ + core3_print("U_EMRSETTEXTCOLOR", "crColor:", contents); +} + +// U_EMRSETBKCOLOR 25 +/** + \brief Print a pointer to a U_EMR_SETBKCOLOR record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETBKCOLOR_print(const char *contents){ + core3_print("U_EMRSETBKCOLOR", "crColor:", contents); +} + +// U_EMROFFSETCLIPRGN 26 +/** + \brief Print a pointer to a U_EMR_OFFSETCLIPRGN record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMROFFSETCLIPRGN_print(const char *contents){ + core7_print("U_EMROFFSETCLIPRGN", "ptl:","",contents); +} + +// U_EMRMOVETOEX 27 +/** + \brief Print a pointer to a U_EMR_MOVETOEX record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRMOVETOEX_print(const char *contents){ + core7_print("U_EMRMOVETOEX", "ptl:","",contents); +} + +// U_EMRSETMETARGN 28 +/** + \brief Print a pointer to a U_EMR_SETMETARGN record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETMETARGN_print(const char *contents){ + (void) contents; +} + +// U_EMREXCLUDECLIPRECT 29 +/** + \brief Print a pointer to a U_EMR_EXCLUDECLIPRECT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMREXCLUDECLIPRECT_print(const char *contents){ + core4_print("U_EMREXCLUDECLIPRECT", contents); +} + +// U_EMRINTERSECTCLIPRECT 30 +/** + \brief Print a pointer to a U_EMR_INTERSECTCLIPRECT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRINTERSECTCLIPRECT_print(const char *contents){ + core4_print("U_EMRINTERSECTCLIPRECT", contents); +} + +// U_EMRSCALEVIEWPORTEXTEX 31 +/** + \brief Print a pointer to a U_EMR_SCALEVIEWPORTEXTEX record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSCALEVIEWPORTEXTEX_print(const char *contents){ + core4_print("U_EMRSCALEVIEWPORTEXTEX", contents); +} + + +// U_EMRSCALEWINDOWEXTEX 32 +/** + \brief Print a pointer to a U_EMR_SCALEWINDOWEXTEX record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSCALEWINDOWEXTEX_print(const char *contents){ + core4_print("U_EMRSCALEWINDOWEXTEX", contents); +} + +// U_EMRSAVEDC 33 +/** + \brief Print a pointer to a U_EMR_SAVEDC record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSAVEDC_print(const char *contents){ + (void) contents; +} + +// U_EMRRESTOREDC 34 +/** + \brief Print a pointer to a U_EMR_RESTOREDC record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRRESTOREDC_print(const char *contents){ + core3_print("U_EMRRESTOREDC", "iRelative:", contents); +} + +// U_EMRSETWORLDTRANSFORM 35 +/** + \brief Print a pointer to a U_EMR_SETWORLDTRANSFORM record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETWORLDTRANSFORM_print(const char *contents){ + PU_EMRSETWORLDTRANSFORM pEmr = (PU_EMRSETWORLDTRANSFORM)(contents); + printf(" xform:"); + xform_print(pEmr->xform); + printf("\n"); +} + +// U_EMRMODIFYWORLDTRANSFORM 36 +/** + \brief Print a pointer to a U_EMR_MODIFYWORLDTRANSFORM record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRMODIFYWORLDTRANSFORM_print(const char *contents){ + PU_EMRMODIFYWORLDTRANSFORM pEmr = (PU_EMRMODIFYWORLDTRANSFORM)(contents); + printf(" xform:"); + xform_print(pEmr->xform); + printf("\n"); + printf(" iMode: %u\n", pEmr->iMode ); +} + +// U_EMRSELECTOBJECT 37 +/** + \brief Print a pointer to a U_EMR_SELECTOBJECT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSELECTOBJECT_print(const char *contents){ + PU_EMRSELECTOBJECT pEmr = (PU_EMRSELECTOBJECT)(contents); + if(pEmr->ihObject & U_STOCK_OBJECT){ + printf(" StockObject: 0x%8.8X\n", pEmr->ihObject ); + } + else { + printf(" ihObject: %u\n", pEmr->ihObject ); + } +} + +// U_EMRCREATEPEN 38 +/** + \brief Print a pointer to a U_EMR_CREATEPEN record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRCREATEPEN_print(const char *contents){ + PU_EMRCREATEPEN pEmr = (PU_EMRCREATEPEN)(contents); + printf(" ihPen: %u\n", pEmr->ihPen ); + printf(" lopn: "); logpen_print(pEmr->lopn); printf("\n"); +} + +// U_EMRCREATEBRUSHINDIRECT 39 +/** + \brief Print a pointer to a U_EMR_CREATEBRUSHINDIRECT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRCREATEBRUSHINDIRECT_print(const char *contents){ + PU_EMRCREATEBRUSHINDIRECT pEmr = (PU_EMRCREATEBRUSHINDIRECT)(contents); + printf(" ihBrush: %u\n", pEmr->ihBrush ); + printf(" lb: "); logbrush_print(pEmr->lb); printf("\n"); +} + +// U_EMRDELETEOBJECT 40 +/** + \brief Print a pointer to a U_EMR_DELETEOBJECT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRDELETEOBJECT_print(const char *contents){ + PU_EMRDELETEOBJECT pEmr = (PU_EMRDELETEOBJECT)(contents); + printf(" ihObject: %u\n", pEmr->ihObject ); +} + +// U_EMRANGLEARC 41 +/** + \brief Print a pointer to a U_EMR_ANGLEARC record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRANGLEARC_print(const char *contents){ + PU_EMRANGLEARC pEmr = (PU_EMRANGLEARC)(contents); + printf(" ptlCenter: "), pointl_print(pEmr->ptlCenter ); printf("\n"); + printf(" nRadius: %u\n", pEmr->nRadius ); + printf(" eStartAngle: %f\n", pEmr->eStartAngle ); + printf(" eSweepAngle: %f\n", pEmr->eSweepAngle ); +} + +// U_EMRELLIPSE 42 +/** + \brief Print a pointer to a U_EMR_ELLIPSE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRELLIPSE_print(const char *contents){ + core4_print("U_EMRELLIPSE", contents); +} + +// U_EMRRECTANGLE 43 +/** + \brief Print a pointer to a U_EMR_RECTANGLE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRRECTANGLE_print(const char *contents){ + core4_print("U_EMRRECTANGLE", contents); +} + +// U_EMRROUNDRECT 44 +/** + \brief Print a pointer to a U_EMR_ROUNDRECT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRROUNDRECT_print(const char *contents){ + PU_EMRROUNDRECT pEmr = (PU_EMRROUNDRECT)(contents); + printf(" rclBox: "), rectl_print(pEmr->rclBox ); printf("\n"); + printf(" szlCorner: "), sizel_print(pEmr->szlCorner ); printf("\n"); +} + +// U_EMRARC 45 +/** + \brief Print a pointer to a U_EMR_ARC record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRARC_print(const char *contents){ + core9_print("U_EMRARC", contents); +} + +// U_EMRCHORD 46 +/** + \brief Print a pointer to a U_EMR_CHORD record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRCHORD_print(const char *contents){ + core9_print("U_EMRCHORD", contents); +} + +// U_EMRPIE 47 +/** + \brief Print a pointer to a U_EMR_PIE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPIE_print(const char *contents){ + core9_print("U_EMRPIE", contents); +} + +// U_EMRSELECTPALETTE 48 +/** + \brief Print a pointer to a U_EMR_SELECTPALETTE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSELECTPALETTE_print(const char *contents){ + core3_print("U_EMRSELECTPALETTE", "ihPal:", contents); +} + +// U_EMRCREATEPALETTE 49 +/** + \brief Print a pointer to a U_EMR_CREATEPALETTE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRCREATEPALETTE_print(const char *contents){ + PU_EMRCREATEPALETTE pEmr = (PU_EMRCREATEPALETTE)(contents); + printf(" ihPal: %u\n",pEmr->ihPal); + printf(" lgpl: "), logpalette_print( (PU_LOGPALETTE)&(pEmr->lgpl) ); printf("\n"); +} + +// U_EMRSETPALETTEENTRIES 50 +/** + \brief Print a pointer to a U_EMR_SETPALETTEENTRIES record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETPALETTEENTRIES_print(const char *contents){ + unsigned i; + PU_EMRSETPALETTEENTRIES pEmr = (PU_EMRSETPALETTEENTRIES)(contents); + printf(" ihPal: %u\n",pEmr->ihPal); + printf(" iStart: %u\n",pEmr->iStart); + printf(" cEntries: %u\n",pEmr->cEntries); + if(pEmr->cEntries){ + printf(" PLTEntries:"); + PU_LOGPLTNTRY aPalEntries = (PU_LOGPLTNTRY) &(pEmr->aPalEntries); + for(i=0; i<pEmr->cEntries; i++){ + printf("%d:",i); logpltntry_print(aPalEntries[i]); + } + printf("\n"); + } +} + +// U_EMRRESIZEPALETTE 51 +/** + \brief Print a pointer to a U_EMR_RESIZEPALETTE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRRESIZEPALETTE_print(const char *contents){ + core7_print("U_EMRRESIZEPALETTE", "ihPal:","cEntries",contents); +} + +// U_EMRREALIZEPALETTE 52 +/** + \brief Print a pointer to a U_EMR_REALIZEPALETTE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRREALIZEPALETTE_print(const char *contents){ + (void) contents; +} + +// U_EMREXTFLOODFILL 53 +/** + \brief Print a pointer to a U_EMR_EXTFLOODFILL record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMREXTFLOODFILL_print(const char *contents){ + PU_EMREXTFLOODFILL pEmr = (PU_EMREXTFLOODFILL)(contents); + printf(" ptlStart: "); pointl_print(pEmr->ptlStart); printf("\n"); + printf(" crColor: "); colorref_print(pEmr->crColor); printf("\n"); + printf(" iMode: %u\n",pEmr->iMode); +} + +// U_EMRLINETO 54 +/** + \brief Print a pointer to a U_EMR_LINETO record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRLINETO_print(const char *contents){ + core7_print("U_EMRLINETO", "ptl:","",contents); +} + +// U_EMRARCTO 55 +/** + \brief Print a pointer to a U_EMR_ARCTO record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRARCTO_print(const char *contents){ + core9_print("U_EMRARCTO", contents); +} + +// U_EMRPOLYDRAW 56 +/** + \brief Print a pointer to a U_EMR_POLYDRAW record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYDRAW_print(const char *contents){ + unsigned i; + PU_EMRPOLYDRAW pEmr = (PU_EMRPOLYDRAW)(contents); + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" cptl: %d\n",pEmr->cptl ); + printf(" Points: "); + for(i=0;i<pEmr->cptl; i++){ + printf(" [%d]:",i); + pointl_print(pEmr->aptl[i]); + } + printf("\n"); + printf(" Types: "); + for(i=0;i<pEmr->cptl; i++){ + printf(" [%d]:%u ",i,pEmr->abTypes[i]); + } + printf("\n"); +} + +// U_EMRSETARCDIRECTION 57 +/** + \brief Print a pointer to a U_EMR_SETARCDIRECTION record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETARCDIRECTION_print(const char *contents){ + core3_print("U_EMRSETARCDIRECTION","arcDirection:", contents); +} + +// U_EMRSETMITERLIMIT 58 +/** + \brief Print a pointer to a U_EMR_SETMITERLIMIT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETMITERLIMIT_print(const char *contents){ + core3_print("U_EMRSETMITERLIMIT", "eMiterLimit:", contents); +} + + +// U_EMRBEGINPATH 59 +/** + \brief Print a pointer to a U_EMR_BEGINPATH record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRBEGINPATH_print(const char *contents){ + (void) contents; +} + +// U_EMRENDPATH 60 +/** + \brief Print a pointer to a U_EMR_ENDPATH record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRENDPATH_print(const char *contents){ + (void) contents; +} + +// U_EMRCLOSEFIGURE 61 +/** + \brief Print a pointer to a U_EMR_CLOSEFIGURE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRCLOSEFIGURE_print(const char *contents){ + (void) contents; +} + +// U_EMRFILLPATH 62 +/** + \brief Print a pointer to a U_EMR_FILLPATH record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRFILLPATH_print(const char *contents){ + core4_print("U_EMRFILLPATH", contents); +} + +// U_EMRSTROKEANDFILLPATH 63 +/** + \brief Print a pointer to a U_EMR_STROKEANDFILLPATH record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSTROKEANDFILLPATH_print(const char *contents){ + core4_print("U_EMRSTROKEANDFILLPATH", contents); +} + +// U_EMRSTROKEPATH 64 +/** + \brief Print a pointer to a U_EMR_STROKEPATH record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSTROKEPATH_print(const char *contents){ + core4_print("U_EMRSTROKEPATH", contents); +} + +// U_EMRFLATTENPATH 65 +/** + \brief Print a pointer to a U_EMR_FLATTENPATH record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRFLATTENPATH_print(const char *contents){ + (void) contents; +} + +// U_EMRWIDENPATH 66 +/** + \brief Print a pointer to a U_EMR_WIDENPATH record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRWIDENPATH_print(const char *contents){ + (void) contents; +} + +// U_EMRSELECTCLIPPATH 67 +/** + \brief Print a pointer to a U_EMR_SELECTCLIPPATH record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSELECTCLIPPATH_print(const char *contents){ + core3_print("U_EMRSELECTCLIPPATH", "iMode:", contents); +} + +// U_EMRABORTPATH 68 +/** + \brief Print a pointer to a U_EMR_ABORTPATH record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRABORTPATH_print(const char *contents){ + (void) contents; +} + +// U_EMRUNDEF69 69 +#define U_EMRUNDEF69_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRUNDEF69",A) + +// U_EMRCOMMENT 70 Comment (any binary data, interpretation is program specific) +/** + \brief Print a pointer to a U_EMR_COMMENT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRCOMMENT_print(const char *contents){ + char *string; + char *src; + uint32_t cIdent,cbData; + PU_EMRCOMMENT pEmr = (PU_EMRCOMMENT)(contents); + + /* There are several different types of comments */ + + cbData = pEmr->cbData; + printf(" cbData: %d\n",cbData ); + src = (char *)(&pEmr->Data); // default + if(cbData >= 4){ + cIdent = *(uint32_t *)(src); + if( cIdent == U_EMR_COMMENT_PUBLIC ){ + printf(" cIdent: Public\n"); + PU_EMRCOMMENT_PUBLIC pEmrp = (PU_EMRCOMMENT_PUBLIC) pEmr; + printf(" pcIdent: %8.8x\n",pEmrp->pcIdent); + src = (char *)&(pEmrp->Data); + cbData -= 8; + } + else if(cIdent == U_EMR_COMMENT_SPOOL ){ + printf(" cIdent: Spool\n"); + PU_EMRCOMMENT_SPOOL pEmrs = (PU_EMRCOMMENT_SPOOL) pEmr; + printf(" esrIdent: %8.8x\n",pEmrs->esrIdent); + src = (char *)&(pEmrs->Data); + cbData -= 8; + } + else if(cIdent == U_EMR_COMMENT_EMFPLUSRECORD){ + printf(" cIdent: EMF+\n"); + PU_EMRCOMMENT_EMFPLUS pEmrpl = (PU_EMRCOMMENT_EMFPLUS) pEmr; + src = (char *)&(pEmrpl->Data); + cbData -= 4; + } + else { + printf(" cIdent: not (Public or Spool or EMF+)\n"); + } + } + if(cbData){ // The data may not be printable, but try it just in case + string = malloc(cbData + 1); + (void)strncpy(string, src, cbData); + string[cbData] = '\0'; // it might not be terminated - it might not even be text! + printf(" Data: <%s>\n",string); + free(string); + } + +} + +// U_EMRFILLRGN 71 +/** + \brief Print a pointer to a U_EMR_FILLRGN record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRFILLRGN_print(const char *contents){ + unsigned i,roff; + PU_EMRFILLRGN pEmr = (PU_EMRFILLRGN)(contents); + printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); + printf(" cbRgnData: %u\n",pEmr->cbRgnData); + printf(" ihBrush: %u\n",pEmr->ihBrush); + // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. + roff=0; + i=1; + char *prd = (char *) &(pEmr->RgnData); + while(roff + 28 < pEmr->emr.nSize){ // up to the end of the record + printf(" RegionData[%d]: ",i); rgndata_print((PU_RGNDATA) (prd + roff)); printf("\n"); + roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + } +} + +// U_EMRFRAMERGN 72 +/** + \brief Print a pointer to a U_EMR_FRAMERGN record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRFRAMERGN_print(const char *contents){ + unsigned i,roff; + PU_EMRFRAMERGN pEmr = (PU_EMRFRAMERGN)(contents); + printf(" rclBounds: "); rectl_print(pEmr->rclBounds); printf("\n"); + printf(" cbRgnData: %u\n",pEmr->cbRgnData); + printf(" ihBrush: %u\n",pEmr->ihBrush); + printf(" szlStroke: "), sizel_print(pEmr->szlStroke ); printf("\n"); + // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. + roff=0; + i=1; + char *prd = (char *) &(pEmr->RgnData); + while(roff + 28 < pEmr->emr.nSize){ // up to the end of the record + printf(" RegionData[%d]: ",i); rgndata_print((PU_RGNDATA) (prd + roff)); printf("\n"); + roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + } +} + +// U_EMRINVERTRGN 73 +/** + \brief Print a pointer to a U_EMR_INVERTRGN record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRINVERTRGN_print(const char *contents){ + core11_print("U_EMRINVERTRGN", contents); +} + +// U_EMRPAINTRGN 74 +/** + \brief Print a pointer to a U_EMR_PAINTRGN record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPAINTRGN_print(const char *contents){ + core11_print("U_EMRPAINTRGN", contents); +} + +// U_EMREXTSELECTCLIPRGN 75 +/** + \brief Print a pointer to a U_EMR_EXTSELECTCLIPRGN record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMREXTSELECTCLIPRGN_print(const char *contents){ + unsigned i,roff; + PU_EMREXTSELECTCLIPRGN pEmr = (PU_EMREXTSELECTCLIPRGN) (contents); + printf(" cbRgnData: %u\n",pEmr->cbRgnData); + printf(" iMode: %u\n",pEmr->iMode); + // This one is a pain since each RGNDATA may be a different size, so it isn't possible to index through them. + char *prd = (char *) &(pEmr->RgnData); + i=roff=0; + while(roff + 16 < pEmr->emr.nSize){ // stop at end of the record + printf(" RegionData[%d]: ",i++); rgndata_print((PU_RGNDATA) (prd + roff)); printf("\n"); + roff += (((PU_RGNDATA)prd)->rdh.dwSize + ((PU_RGNDATA)prd)->rdh.nRgnSize - 16); + } +} + +// U_EMRBITBLT 76 +/** + \brief Print a pointer to a U_EMR_BITBLT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRBITBLT_print(const char *contents){ + PU_EMRBITBLT pEmr = (PU_EMRBITBLT) (contents); + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); + printf(" cDest: "); pointl_print(pEmr->cDest); printf("\n"); + printf(" dwRop : %u\n", pEmr->dwRop ); + printf(" Src: "); pointl_print(pEmr->Src); printf("\n"); + printf(" xformSrc: "); xform_print( pEmr->xformSrc); printf("\n"); + printf(" crBkColorSrc: "); colorref_print( pEmr->crBkColorSrc); printf("\n"); + printf(" iUsageSrc: %u\n", pEmr->iUsageSrc ); + printf(" offBmiSrc: %u\n", pEmr->offBmiSrc ); + printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); + if(pEmr->cbBmiSrc){ + printf(" bitmap: "); + bitmapinfo_print(contents + pEmr->offBmiSrc); + printf("\n"); + } + printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); + printf(" cbBitsSrc: %u\n", pEmr->cbBitsSrc ); +} + +// U_EMRSTRETCHBLT 77 +/** + \brief Print a pointer to a U_EMR_STRETCHBLT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSTRETCHBLT_print(const char *contents){ + PU_EMRSTRETCHBLT pEmr = (PU_EMRSTRETCHBLT) (contents); + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); + printf(" cDest: "); pointl_print(pEmr->cDest); printf("\n"); + printf(" dwRop : %u\n", pEmr->dwRop ); + printf(" Src: "); pointl_print(pEmr->Src); printf("\n"); + printf(" xformSrc: "); xform_print( pEmr->xformSrc); printf("\n"); + printf(" crBkColorSrc: "); colorref_print( pEmr->crBkColorSrc); printf("\n"); + printf(" iUsageSrc: %u\n", pEmr->iUsageSrc ); + printf(" offBmiSrc: %u\n", pEmr->offBmiSrc ); + printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); + if(pEmr->cbBmiSrc){ + printf(" bitmap: "); + bitmapinfo_print(contents + pEmr->offBmiSrc); + printf("\n"); + } + printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); + printf(" cbBitsSrc: %u\n", pEmr->cbBitsSrc ); + printf(" cSrc: "); pointl_print(pEmr->cSrc); printf("\n"); +} + +// U_EMRMASKBLT 78 +/** + \brief Print a pointer to a U_EMR_MASKBLT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRMASKBLT_print(const char *contents){ + PU_EMRMASKBLT pEmr = (PU_EMRMASKBLT) (contents); + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); + printf(" cDest: "); pointl_print(pEmr->cDest); printf("\n"); + printf(" dwRop : %u\n", pEmr->dwRop ); + printf(" Src: "); pointl_print(pEmr->Src); printf("\n"); + printf(" xformSrc: "); xform_print( pEmr->xformSrc); printf("\n"); + printf(" crBkColorSrc: "); colorref_print( pEmr->crBkColorSrc); printf("\n"); + printf(" iUsageSrc: %u\n", pEmr->iUsageSrc ); + printf(" offBmiSrc: %u\n", pEmr->offBmiSrc ); + printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); + if(pEmr->cbBmiSrc){ + printf(" Src bitmap: "); + bitmapinfo_print(contents + pEmr->offBmiSrc); + printf("\n"); + } + printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); + printf(" cbBitsSrc: %u\n", pEmr->cbBitsSrc ); + printf(" Mask: "); pointl_print(pEmr->Mask); printf("\n"); + printf(" iUsageMask: %u\n", pEmr->iUsageMask ); + printf(" offBmiMask: %u\n", pEmr->offBmiMask ); + printf(" cbBmiMask: %u\n", pEmr->cbBmiMask ); + if(pEmr->cbBmiMask){ + printf(" Mask bitmap: "); + bitmapinfo_print(contents + pEmr->offBmiMask); + printf("\n"); + } + printf(" offBitsMask: %u\n", pEmr->offBitsMask ); + printf(" cbBitsMask: %u\n", pEmr->cbBitsMask ); +} + +// U_EMRPLGBLT 79 +/** + \brief Print a pointer to a U_EMR_PLGBLT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPLGBLT_print(const char *contents){ + PU_EMRPLGBLT pEmr = (PU_EMRPLGBLT) (contents); + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" aptlDst(UL): "); pointl_print(pEmr->aptlDst[0]); printf("\n"); + printf(" aptlDst(UR): "); pointl_print(pEmr->aptlDst[1]); printf("\n"); + printf(" aptlDst(LL): "); pointl_print(pEmr->aptlDst[2]); printf("\n"); + printf(" Src: "); pointl_print(pEmr->Src); printf("\n"); + printf(" cSrc: "); pointl_print(pEmr->cSrc); printf("\n"); + printf(" xformSrc: "); xform_print( pEmr->xformSrc); printf("\n"); + printf(" crBkColorSrc: "); colorref_print( pEmr->crBkColorSrc); printf("\n"); + printf(" iUsageSrc: %u\n", pEmr->iUsageSrc ); + printf(" offBmiSrc: %u\n", pEmr->offBmiSrc ); + printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); + if(pEmr->cbBmiSrc){ + printf(" Src bitmap: "); + bitmapinfo_print(contents + pEmr->offBmiSrc); + printf("\n"); + } + printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); + printf(" cbBitsSrc: %u\n", pEmr->cbBitsSrc ); + printf(" Mask: "); pointl_print(pEmr->Mask); printf("\n"); + printf(" iUsageMsk: %u\n", pEmr->iUsageMask ); + printf(" offBmiMask: %u\n", pEmr->offBmiMask ); + printf(" cbBmiMask: %u\n", pEmr->cbBmiMask ); + if(pEmr->cbBmiMask){ + printf(" Mask bitmap: "); + bitmapinfo_print(contents + pEmr->offBmiMask); + printf("\n"); + } + printf(" offBitsMask: %u\n", pEmr->offBitsMask ); + printf(" cbBitsMask: %u\n", pEmr->cbBitsMask ); +} + +// U_EMRSETDIBITSTODEVICE 80 +/** + \brief Print a pointer to a U_EMRSETDIBITSTODEVICE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETDIBITSTODEVICE_print(const char *contents){ + PU_EMRSETDIBITSTODEVICE pEmr = (PU_EMRSETDIBITSTODEVICE) (contents); + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); + printf(" Src: "); pointl_print(pEmr->Src); printf("\n"); + printf(" cSrc: "); pointl_print(pEmr->cSrc); printf("\n"); + printf(" offBmiSrc: %u\n", pEmr->offBmiSrc ); + printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); + if(pEmr->cbBmiSrc){ + printf(" Src bitmap: "); + bitmapinfo_print(contents + pEmr->offBmiSrc); + printf("\n"); + } + printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); + printf(" cbBitsSrc: %u\n", pEmr->cbBitsSrc ); + printf(" iUsageSrc: %u\n", pEmr->iUsageSrc ); + printf(" iStartScan: %u\n", pEmr->iStartScan ); + printf(" cScans : %u\n", pEmr->cScans ); +} + +// U_EMRSTRETCHDIBITS 81 +/** + \brief Print a pointer to a U_EMR_STRETCHDIBITS record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSTRETCHDIBITS_print(const char *contents){ + PU_EMRSTRETCHDIBITS pEmr = (PU_EMRSTRETCHDIBITS) (contents); + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); + printf(" Src: "); pointl_print(pEmr->Src); printf("\n"); + printf(" cSrc: "); pointl_print(pEmr->cSrc); printf("\n"); + printf(" offBmiSrc: %u\n", pEmr->offBmiSrc ); + printf(" cbBmiSrc: %u\n", pEmr->cbBmiSrc ); + if(pEmr->cbBmiSrc){ + printf(" Src bitmap: "); + bitmapinfo_print(contents + pEmr->offBmiSrc); + printf("\n"); + } + printf(" offBitsSrc: %u\n", pEmr->offBitsSrc ); + printf(" cbBitsSrc: %u\n", pEmr->cbBitsSrc ); + printf(" iUsageSrc: %u\n", pEmr->iUsageSrc ); + printf(" dwRop : %u\n", pEmr->dwRop ); + printf(" cDest: "); pointl_print(pEmr->cDest); printf("\n"); +} + +// U_EMREXTCREATEFONTINDIRECTW_print 82 +/** + \brief Print a pointer to a U_EMR_EXTCREATEFONTINDIRECTW record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMREXTCREATEFONTINDIRECTW_print(const char *contents){ + PU_EMREXTCREATEFONTINDIRECTW pEmr = (PU_EMREXTCREATEFONTINDIRECTW) (contents); + printf(" ihFont: %u\n",pEmr->ihFont ); + printf(" Font: "); + if(pEmr->emr.nSize == sizeof(U_EMREXTCREATEFONTINDIRECTW)){ // holds logfont_panose + logfont_panose_print(pEmr->elfw); + } + else { // holds logfont + logfont_print( *(PU_LOGFONT) &(pEmr->elfw)); + } + printf("\n"); +} + +// U_EMREXTTEXTOUTA 83 +/** + \brief Print a pointer to a U_EMR_EXTTEXTOUTA record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMREXTTEXTOUTA_print(const char *contents){ + core8_print("U_EMREXTTEXTOUTA", contents, 0); +} + +// U_EMREXTTEXTOUTW 84 +/** + \brief Print a pointer to a U_EMR_EXTTEXTOUTW record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMREXTTEXTOUTW_print(const char *contents){ + core8_print("U_EMREXTTEXTOUTW", contents, 1); +} + +// U_EMRPOLYBEZIER16 85 +/** + \brief Print a pointer to a U_EMR_POLYBEZIER16 record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYBEZIER16_print(const char *contents){ + core6_print("U_EMRPOLYBEZIER16", contents); +} + +// U_EMRPOLYGON16 86 +/** + \brief Print a pointer to a U_EMR_POLYGON16 record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYGON16_print(const char *contents){ + core6_print("U_EMRPOLYGON16", contents); +} + +// U_EMRPOLYLINE16 87 +/** + \brief Print a pointer to a U_EMR_POLYLINE16 record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYLINE16_print(const char *contents){ + core6_print("U_EMRPOLYLINE16", contents); +} + +// U_EMRPOLYBEZIERTO16 88 +/** + \brief Print a pointer to a U_EMR_POLYBEZIERTO16 record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYBEZIERTO16_print(const char *contents){ + core6_print("U_EMRPOLYBEZIERTO16", contents); +} + +// U_EMRPOLYLINETO16 89 +/** + \brief Print a pointer to a U_EMR_POLYLINETO16 record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYLINETO16_print(const char *contents){ + core6_print("U_EMRPOLYLINETO16", contents); +} + +// U_EMRPOLYPOLYLINE16 90 +/** + \brief Print a pointer to a U_EMR_POLYPOLYLINE16 record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYPOLYLINE16_print(const char *contents){ + core10_print("U_EMRPOLYPOLYLINE16", contents); +} + +// U_EMRPOLYPOLYGON16 91 +/** + \brief Print a pointer to a U_EMR_POLYPOLYGON16 record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYPOLYGON16_print(const char *contents){ + core10_print("U_EMRPOLYPOLYGON16", contents); +} + + +// U_EMRPOLYDRAW16 92 +/** + \brief Print a pointer to a U_EMR_POLYDRAW16 record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPOLYDRAW16_print(const char *contents){ + unsigned i; + PU_EMRPOLYDRAW16 pEmr = (PU_EMRPOLYDRAW16)(contents); + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" cpts: %d\n",pEmr->cpts ); + printf(" Points: "); + for(i=0;i<pEmr->cpts; i++){ + printf(" [%d]:",i); + point16_print(pEmr->apts[i]); + } + printf("\n"); + printf(" Types: "); + for(i=0;i<pEmr->cpts; i++){ + printf(" [%d]:%u ",i,pEmr->abTypes[i]); + } + printf("\n"); +} + +// U_EMRCREATEMONOBRUSH 93 +/** + \brief Print a pointer to a U_EMR_CREATEMONOBRUSH record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRCREATEMONOBRUSH_print(const char *contents){ + core12_print("U_EMRCREATEMONOBRUSH", contents); +} + +// U_EMRCREATEDIBPATTERNBRUSHPT_print 94 +/** + \brief Print a pointer to a U_EMR_CREATEDIBPATTERNBRUSHPT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRCREATEDIBPATTERNBRUSHPT_print(const char *contents){ + core12_print("U_EMRCREATEDIBPATTERNBRUSHPT", contents); +} + + +// U_EMREXTCREATEPEN 95 +/** + \brief Print a pointer to a U_EMR_EXTCREATEPEN record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMREXTCREATEPEN_print(const char *contents){ + PU_EMREXTCREATEPEN pEmr = (PU_EMREXTCREATEPEN)(contents); + printf(" ihPen: %u\n", pEmr->ihPen ); + printf(" offBmi: %u\n", pEmr->offBmi ); + printf(" cbBmi: %u\n", pEmr->cbBmi ); + if(pEmr->cbBmi){ + printf(" bitmap: "); + bitmapinfo_print(contents + pEmr->offBmi); + printf("\n"); + } + printf(" offBits: %u\n", pEmr->offBits ); + printf(" cbBits: %u\n", pEmr->cbBits ); + printf(" elp: "); extlogpen_print((PU_EXTLOGPEN) &(pEmr->elp)); printf("\n"); +} + +// U_EMRPOLYTEXTOUTA 96 NOT IMPLEMENTED, denigrated after Windows NT +#define U_EMRPOLYTEXTOUTA_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRPOLYTEXTOUTA",A) +// U_EMRPOLYTEXTOUTW 97 NOT IMPLEMENTED, denigrated after Windows NT +#define U_EMRPOLYTEXTOUTW_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRPOLYTEXTOUTW",A) + +// U_EMRSETICMMODE 98 +/** + \brief Print a pointer to a U_EMR_SETICMMODE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETICMMODE_print(const char *contents){ + core3_print("U_EMRSETICMMODE", "iMode:", contents); +} + +// U_EMRCREATECOLORSPACE 99 +/** + \brief Print a pointer to a U_EMR_CREATECOLORSPACE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRCREATECOLORSPACE_print(const char *contents){ + PU_EMRCREATECOLORSPACE pEmr = (PU_EMRCREATECOLORSPACE)(contents); + printf(" ihCS: %u\n", pEmr->ihCS ); + printf(" ColorSpace: "); logcolorspacea_print(pEmr->lcs); printf("\n"); +} + +// U_EMRSETCOLORSPACE 100 +/** + \brief Print a pointer to a U_EMR_SETCOLORSPACE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETCOLORSPACE_print(const char *contents){ + core3_print("U_EMRSETCOLORSPACE", "ihCS:", contents); +} + +// U_EMRDELETECOLORSPACE 101 +/** + \brief Print a pointer to a U_EMR_DELETECOLORSPACE record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRDELETECOLORSPACE_print(const char *contents){ + core3_print("U_EMRDELETECOLORSPACE", "ihCS:", contents); +} + +// U_EMRGLSRECORD 102 Not implemented +#define U_EMRGLSRECORD_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRGLSRECORD",A) +// U_EMRGLSBOUNDEDRECORD 103 Not implemented +#define U_EMRGLSBOUNDEDRECORD_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRGLSBOUNDEDRECORD",A) + +// U_EMRPIXELFORMAT 104 +/** + \brief Print a pointer to a U_EMR_PIXELFORMAT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRPIXELFORMAT_print(const char *contents){ + PU_EMRPIXELFORMAT pEmr = (PU_EMRPIXELFORMAT)(contents); + printf(" Pfd: "); pixelformatdescriptor_print(pEmr->pfd); printf("\n"); +} + +// U_EMRDRAWESCAPE 105 Not implemented +#define U_EMRDRAWESCAPE_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRDRAWESCAPE",A) +// U_EMREXTESCAPE 106 Not implemented +#define U_EMREXTESCAPE_print(A) U_EMRNOTIMPLEMENTED_print("U_EMREXTESCAPE",A) +// U_EMRUNDEF107 107 Not implemented +#define U_EMRUNDEF107_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRUNDEF107",A) + +// U_EMRSMALLTEXTOUT 108 +/** + \brief Print a pointer to a U_EMR_SMALLTEXTOUT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSMALLTEXTOUT_print(const char *contents){ + int roff; + char *string; + PU_EMRSMALLTEXTOUT pEmr = (PU_EMRSMALLTEXTOUT)(contents); + printf(" Dest: "); pointl_print(pEmr->Dest); printf("\n"); + printf(" cChars: %u\n", pEmr->cChars ); + printf(" fuOptions: 0x%8.8X\n", pEmr->fuOptions ); + printf(" iGraphicsMode: 0x%8.8X\n", pEmr->iGraphicsMode ); + printf(" exScale: %f\n", pEmr->exScale ); + printf(" eyScale: %f\n", pEmr->eyScale ); + roff = sizeof(U_EMRSMALLTEXTOUT); //offset to the start of the variable fields + if(!(pEmr->fuOptions & U_ETO_NO_RECT)){ + printf(" rclBounds: "); rectl_print( *(PU_RECTL) (contents + roff)); printf("\n"); + roff += sizeof(U_RECTL); + } + if(pEmr->fuOptions & U_ETO_SMALL_CHARS){ + printf(" Text8: <%.*s>\n",pEmr->cChars,contents+roff); /* May not be null terminated */ + } + else { + string = U_Utf16leToUtf8((uint16_t *)(contents+roff), pEmr->cChars, NULL); + printf(" Text16: <%s>\n",contents+roff); + free(string); + } +} + +// U_EMRFORCEUFIMAPPING 109 Not implemented +#define U_EMRFORCEUFIMAPPING_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRFORCEUFIMAPPING",A) +// U_EMRNAMEDESCAPE 110 Not implemented +#define U_EMRNAMEDESCAPE_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRNAMEDESCAPE",A) +// U_EMRCOLORCORRECTPALETTE 111 Not implemented +#define U_EMRCOLORCORRECTPALETTE_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRCOLORCORRECTPALETTE",A) +// U_EMRSETICMPROFILEA 112 Not implemented +#define U_EMRSETICMPROFILEA_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRSETICMPROFILEA",A) +// U_EMRSETICMPROFILEW 113 Not implemented +#define U_EMRSETICMPROFILEW_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRSETICMPROFILEW",A) + +// U_EMRALPHABLEND 114 +/** + \brief Print a pointer to a U_EMR_ALPHABLEND record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRALPHABLEND_print(const char *contents){ + core13_print("U_EMRALPHABLEND", contents); +} + +// U_EMRSETLAYOUT 115 +/** + \brief Print a pointer to a U_EMR_SETLAYOUT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRSETLAYOUT_print(const char *contents){ + core3_print("U_EMRSETLAYOUT", "iMode:", contents); +} + +// U_EMRTRANSPARENTBLT 116 +/** + \brief Print a pointer to a U_EMR_TRANSPARENTBLT record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRTRANSPARENTBLT_print(const char *contents){ + core13_print("U_EMRTRANSPARENTBLT", contents); +} + +// U_EMRUNDEF117 117 Not implemented +#define U_EMRUNDEF117_print(A) U_EMRNOTIMPLEMENTED_print("U_EMRUNDEF117",A) +// U_EMRGRADIENTFILL 118 +/** + \brief Print a pointer to a U_EMR_GRADIENTFILL record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRGRADIENTFILL_print(const char *contents){ + unsigned i; + PU_EMRGRADIENTFILL pEmr = (PU_EMRGRADIENTFILL)(contents); + printf(" rclBounds: "); rectl_print( pEmr->rclBounds); printf("\n"); + printf(" nTriVert: %u\n", pEmr->nTriVert ); + printf(" nGradObj: %u\n", pEmr->nGradObj ); + printf(" ulMode: %u\n", pEmr->ulMode ); + contents += sizeof(U_EMRGRADIENTFILL); + if(pEmr->nTriVert){ + printf(" TriVert: "); + for(i=0; i<pEmr->nTriVert; i++, contents+=sizeof(U_TRIVERTEX)){ + trivertex_print(*(PU_TRIVERTEX)(contents)); + } + printf("\n"); + } + if(pEmr->nGradObj){ + printf(" GradObj: "); + if( pEmr->ulMode == U_GRADIENT_FILL_TRIANGLE){ + for(i=0; i<pEmr->nGradObj; i++, contents+=sizeof(U_GRADIENT3)){ + gradient3_print(*(PU_GRADIENT3)(contents)); + } + } + else if(pEmr->ulMode == U_GRADIENT_FILL_RECT_H || + pEmr->ulMode == U_GRADIENT_FILL_RECT_V){ + for(i=0; i<pEmr->nGradObj; i++, contents+=sizeof(U_GRADIENT4)){ + gradient4_print(*(PU_GRADIENT4)(contents)); + } + } + else { printf("invalid ulMode value!"); } + printf("\n"); + } +} + +// U_EMRSETLINKEDUFIS 119 Not implemented +#define U_EMRSETLINKEDUFIS_print(A) U_EMRNOTIMPLEMENTED_print("U_EMR_SETLINKEDUFIS",A) +// U_EMRSETTEXTJUSTIFICATION120 Not implemented (denigrated) +#define U_EMRSETTEXTJUSTIFICATION_print(A) U_EMRNOTIMPLEMENTED_print("U_EMR_SETTEXTJUSTIFICATION",A) +// U_EMRCOLORMATCHTOTARGETW 121 Not implemented +#define U_EMRCOLORMATCHTOTARGETW_print(A) U_EMRNOTIMPLEMENTED_print("U_EMR_COLORMATCHTOTARGETW",A) + +// U_EMRCREATECOLORSPACEW 122 +/** + \brief Print a pointer to a U_EMR_CREATECOLORSPACEW record. + \param contents pointer to a buffer holding all EMR records +*/ +void U_EMRCREATECOLORSPACEW_print(const char *contents){ + unsigned i; + PU_EMRCREATECOLORSPACEW pEmr = (PU_EMRCREATECOLORSPACEW)(contents); + printf(" ihCS: %u\n", pEmr->ihCS ); + printf(" ColorSpace: "); logcolorspacew_print(pEmr->lcs); printf("\n"); + printf(" dwFlags: %u\n", pEmr->dwFlags ); + printf(" cbData: %u\n", pEmr->cbData ); + printf(" Data: "); + if(pEmr->dwFlags & 1){ + for(i=0; i<pEmr->cbData; i++){ + printf("[%d]:%2.2X ",i,pEmr->Data[i]); + } + } + printf("\n"); +} + +/** + \brief Print any record in an emf + \returns record length for a normal record, 0 for EMREOF, -1 for a bad record + \param contents pointer to a buffer holding all EMR records + \param blimit pointer to the byte after the last byte in the buffer holding all EMR records + \param recnum number of this record in contents + \param off offset to this record in contents +*/ +int U_emf_onerec_print(const char *contents, const char *blimit, int recnum, size_t off){ + PU_ENHMETARECORD lpEMFR = (PU_ENHMETARECORD)(contents + off); + unsigned size; + + printf("%-30srecord:%5d type:%3d offset:%8d size:%8d\n",U_emr_names(lpEMFR->iType),recnum,lpEMFR->iType,(int) off,lpEMFR->nSize); + size = lpEMFR->nSize; + contents += off; + + /* Check that the record size is OK, abort if not. + Pointer math might wrap, so check both sides of the range */ + if(size < sizeof(U_EMR) || + contents + size - 1 >= blimit || + contents + size - 1 < contents)return(-1); + + switch (lpEMFR->iType) + { + case U_EMR_HEADER: U_EMRHEADER_print(contents); break; + case U_EMR_POLYBEZIER: U_EMRPOLYBEZIER_print(contents); break; + case U_EMR_POLYGON: U_EMRPOLYGON_print(contents); break; + case U_EMR_POLYLINE: U_EMRPOLYLINE_print(contents); break; + case U_EMR_POLYBEZIERTO: U_EMRPOLYBEZIERTO_print(contents); break; + case U_EMR_POLYLINETO: U_EMRPOLYLINETO_print(contents); break; + case U_EMR_POLYPOLYLINE: U_EMRPOLYPOLYLINE_print(contents); break; + case U_EMR_POLYPOLYGON: U_EMRPOLYPOLYGON_print(contents); break; + case U_EMR_SETWINDOWEXTEX: U_EMRSETWINDOWEXTEX_print(contents); break; + case U_EMR_SETWINDOWORGEX: U_EMRSETWINDOWORGEX_print(contents); break; + case U_EMR_SETVIEWPORTEXTEX: U_EMRSETVIEWPORTEXTEX_print(contents); break; + case U_EMR_SETVIEWPORTORGEX: U_EMRSETVIEWPORTORGEX_print(contents); break; + case U_EMR_SETBRUSHORGEX: U_EMRSETBRUSHORGEX_print(contents); break; + case U_EMR_EOF: U_EMREOF_print(contents); size=0; break; + case U_EMR_SETPIXELV: U_EMRSETPIXELV_print(contents); break; + case U_EMR_SETMAPPERFLAGS: U_EMRSETMAPPERFLAGS_print(contents); break; + case U_EMR_SETMAPMODE: U_EMRSETMAPMODE_print(contents); break; + case U_EMR_SETBKMODE: U_EMRSETBKMODE_print(contents); break; + case U_EMR_SETPOLYFILLMODE: U_EMRSETPOLYFILLMODE_print(contents); break; + case U_EMR_SETROP2: U_EMRSETROP2_print(contents); break; + case U_EMR_SETSTRETCHBLTMODE: U_EMRSETSTRETCHBLTMODE_print(contents); break; + case U_EMR_SETTEXTALIGN: U_EMRSETTEXTALIGN_print(contents); break; + case U_EMR_SETCOLORADJUSTMENT: U_EMRSETCOLORADJUSTMENT_print(contents); break; + case U_EMR_SETTEXTCOLOR: U_EMRSETTEXTCOLOR_print(contents); break; + case U_EMR_SETBKCOLOR: U_EMRSETBKCOLOR_print(contents); break; + case U_EMR_OFFSETCLIPRGN: U_EMROFFSETCLIPRGN_print(contents); break; + case U_EMR_MOVETOEX: U_EMRMOVETOEX_print(contents); break; + case U_EMR_SETMETARGN: U_EMRSETMETARGN_print(contents); break; + case U_EMR_EXCLUDECLIPRECT: U_EMREXCLUDECLIPRECT_print(contents); break; + case U_EMR_INTERSECTCLIPRECT: U_EMRINTERSECTCLIPRECT_print(contents); break; + case U_EMR_SCALEVIEWPORTEXTEX: U_EMRSCALEVIEWPORTEXTEX_print(contents); break; + case U_EMR_SCALEWINDOWEXTEX: U_EMRSCALEWINDOWEXTEX_print(contents); break; + case U_EMR_SAVEDC: U_EMRSAVEDC_print(contents); break; + case U_EMR_RESTOREDC: U_EMRRESTOREDC_print(contents); break; + case U_EMR_SETWORLDTRANSFORM: U_EMRSETWORLDTRANSFORM_print(contents); break; + case U_EMR_MODIFYWORLDTRANSFORM: U_EMRMODIFYWORLDTRANSFORM_print(contents); break; + case U_EMR_SELECTOBJECT: U_EMRSELECTOBJECT_print(contents); break; + case U_EMR_CREATEPEN: U_EMRCREATEPEN_print(contents); break; + case U_EMR_CREATEBRUSHINDIRECT: U_EMRCREATEBRUSHINDIRECT_print(contents); break; + case U_EMR_DELETEOBJECT: U_EMRDELETEOBJECT_print(contents); break; + case U_EMR_ANGLEARC: U_EMRANGLEARC_print(contents); break; + case U_EMR_ELLIPSE: U_EMRELLIPSE_print(contents); break; + case U_EMR_RECTANGLE: U_EMRRECTANGLE_print(contents); break; + case U_EMR_ROUNDRECT: U_EMRROUNDRECT_print(contents); break; + case U_EMR_ARC: U_EMRARC_print(contents); break; + case U_EMR_CHORD: U_EMRCHORD_print(contents); break; + case U_EMR_PIE: U_EMRPIE_print(contents); break; + case U_EMR_SELECTPALETTE: U_EMRSELECTPALETTE_print(contents); break; + case U_EMR_CREATEPALETTE: U_EMRCREATEPALETTE_print(contents); break; + case U_EMR_SETPALETTEENTRIES: U_EMRSETPALETTEENTRIES_print(contents); break; + case U_EMR_RESIZEPALETTE: U_EMRRESIZEPALETTE_print(contents); break; + case U_EMR_REALIZEPALETTE: U_EMRREALIZEPALETTE_print(contents); break; + case U_EMR_EXTFLOODFILL: U_EMREXTFLOODFILL_print(contents); break; + case U_EMR_LINETO: U_EMRLINETO_print(contents); break; + case U_EMR_ARCTO: U_EMRARCTO_print(contents); break; + case U_EMR_POLYDRAW: U_EMRPOLYDRAW_print(contents); break; + case U_EMR_SETARCDIRECTION: U_EMRSETARCDIRECTION_print(contents); break; + case U_EMR_SETMITERLIMIT: U_EMRSETMITERLIMIT_print(contents); break; + case U_EMR_BEGINPATH: U_EMRBEGINPATH_print(contents); break; + case U_EMR_ENDPATH: U_EMRENDPATH_print(contents); break; + case U_EMR_CLOSEFIGURE: U_EMRCLOSEFIGURE_print(contents); break; + case U_EMR_FILLPATH: U_EMRFILLPATH_print(contents); break; + case U_EMR_STROKEANDFILLPATH: U_EMRSTROKEANDFILLPATH_print(contents); break; + case U_EMR_STROKEPATH: U_EMRSTROKEPATH_print(contents); break; + case U_EMR_FLATTENPATH: U_EMRFLATTENPATH_print(contents); break; + case U_EMR_WIDENPATH: U_EMRWIDENPATH_print(contents); break; + case U_EMR_SELECTCLIPPATH: U_EMRSELECTCLIPPATH_print(contents); break; + case U_EMR_ABORTPATH: U_EMRABORTPATH_print(contents); break; + case U_EMR_UNDEF69: U_EMRUNDEF69_print(contents); break; + case U_EMR_COMMENT: U_EMRCOMMENT_print(contents); break; + case U_EMR_FILLRGN: U_EMRFILLRGN_print(contents); break; + case U_EMR_FRAMERGN: U_EMRFRAMERGN_print(contents); break; + case U_EMR_INVERTRGN: U_EMRINVERTRGN_print(contents); break; + case U_EMR_PAINTRGN: U_EMRPAINTRGN_print(contents); break; + case U_EMR_EXTSELECTCLIPRGN: U_EMREXTSELECTCLIPRGN_print(contents); break; + case U_EMR_BITBLT: U_EMRBITBLT_print(contents); break; + case U_EMR_STRETCHBLT: U_EMRSTRETCHBLT_print(contents); break; + case U_EMR_MASKBLT: U_EMRMASKBLT_print(contents); break; + case U_EMR_PLGBLT: U_EMRPLGBLT_print(contents); break; + case U_EMR_SETDIBITSTODEVICE: U_EMRSETDIBITSTODEVICE_print(contents); break; + case U_EMR_STRETCHDIBITS: U_EMRSTRETCHDIBITS_print(contents); break; + case U_EMR_EXTCREATEFONTINDIRECTW: U_EMREXTCREATEFONTINDIRECTW_print(contents); break; + case U_EMR_EXTTEXTOUTA: U_EMREXTTEXTOUTA_print(contents); break; + case U_EMR_EXTTEXTOUTW: U_EMREXTTEXTOUTW_print(contents); break; + case U_EMR_POLYBEZIER16: U_EMRPOLYBEZIER16_print(contents); break; + case U_EMR_POLYGON16: U_EMRPOLYGON16_print(contents); break; + case U_EMR_POLYLINE16: U_EMRPOLYLINE16_print(contents); break; + case U_EMR_POLYBEZIERTO16: U_EMRPOLYBEZIERTO16_print(contents); break; + case U_EMR_POLYLINETO16: U_EMRPOLYLINETO16_print(contents); break; + case U_EMR_POLYPOLYLINE16: U_EMRPOLYPOLYLINE16_print(contents); break; + case U_EMR_POLYPOLYGON16: U_EMRPOLYPOLYGON16_print(contents); break; + case U_EMR_POLYDRAW16: U_EMRPOLYDRAW16_print(contents); break; + case U_EMR_CREATEMONOBRUSH: U_EMRCREATEMONOBRUSH_print(contents); break; + case U_EMR_CREATEDIBPATTERNBRUSHPT: U_EMRCREATEDIBPATTERNBRUSHPT_print(contents); break; + case U_EMR_EXTCREATEPEN: U_EMREXTCREATEPEN_print(contents); break; + case U_EMR_POLYTEXTOUTA: U_EMRPOLYTEXTOUTA_print(contents); break; + case U_EMR_POLYTEXTOUTW: U_EMRPOLYTEXTOUTW_print(contents); break; + case U_EMR_SETICMMODE: U_EMRSETICMMODE_print(contents); break; + case U_EMR_CREATECOLORSPACE: U_EMRCREATECOLORSPACE_print(contents); break; + case U_EMR_SETCOLORSPACE: U_EMRSETCOLORSPACE_print(contents); break; + case U_EMR_DELETECOLORSPACE: U_EMRDELETECOLORSPACE_print(contents); break; + case U_EMR_GLSRECORD: U_EMRGLSRECORD_print(contents); break; + case U_EMR_GLSBOUNDEDRECORD: U_EMRGLSBOUNDEDRECORD_print(contents); break; + case U_EMR_PIXELFORMAT: U_EMRPIXELFORMAT_print(contents); break; + case U_EMR_DRAWESCAPE: U_EMRDRAWESCAPE_print(contents); break; + case U_EMR_EXTESCAPE: U_EMREXTESCAPE_print(contents); break; + case U_EMR_UNDEF107: U_EMRUNDEF107_print(contents); break; + case U_EMR_SMALLTEXTOUT: U_EMRSMALLTEXTOUT_print(contents); break; + case U_EMR_FORCEUFIMAPPING: U_EMRFORCEUFIMAPPING_print(contents); break; + case U_EMR_NAMEDESCAPE: U_EMRNAMEDESCAPE_print(contents); break; + case U_EMR_COLORCORRECTPALETTE: U_EMRCOLORCORRECTPALETTE_print(contents); break; + case U_EMR_SETICMPROFILEA: U_EMRSETICMPROFILEA_print(contents); break; + case U_EMR_SETICMPROFILEW: U_EMRSETICMPROFILEW_print(contents); break; + case U_EMR_ALPHABLEND: U_EMRALPHABLEND_print(contents); break; + case U_EMR_SETLAYOUT: U_EMRSETLAYOUT_print(contents); break; + case U_EMR_TRANSPARENTBLT: U_EMRTRANSPARENTBLT_print(contents); break; + case U_EMR_UNDEF117: U_EMRUNDEF117_print(contents); break; + case U_EMR_GRADIENTFILL: U_EMRGRADIENTFILL_print(contents); break; + case U_EMR_SETLINKEDUFIS: U_EMRSETLINKEDUFIS_print(contents); break; + case U_EMR_SETTEXTJUSTIFICATION: U_EMRSETTEXTJUSTIFICATION_print(contents); break; + case U_EMR_COLORMATCHTOTARGETW: U_EMRCOLORMATCHTOTARGETW_print(contents); break; + case U_EMR_CREATECOLORSPACEW: U_EMRCREATECOLORSPACEW_print(contents); break; + default: U_EMRNOTIMPLEMENTED_print("?",contents); break; + } //end of switch + return(size); +} + + +#ifdef __cplusplus +} +#endif diff --git a/src/libuemf/uemf_print.h b/src/libuemf/uemf_print.h new file mode 100644 index 000000000..238e0e659 --- /dev/null +++ b/src/libuemf/uemf_print.h @@ -0,0 +1,169 @@ +/** + @file uemf_print.h Functions for printing records from EMF files. +*/ + +/* +File: uemf_print.h +Version: 0.0.5 +Date: 14-FEB-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifndef _UEMF_PRINT_ +#define _UEMF_PRINT_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* prototypes for objects used in EMR records */ +void hexbytes_print(uint8_t *buf,unsigned int num); +void colorref_print(U_COLORREF color); +void rgbquad_print(U_RGBQUAD color); +void rectl_print(U_RECTL rect); +void sizel_print(U_SIZEL sz); +void pointl_print(U_POINTL pt); +void point16_print(U_POINT16 pt); +void lcs_gamma_print(U_LCS_GAMMA lg); +void lcs_gammargb_print(U_LCS_GAMMARGB lgr); +void trivertex_print(U_TRIVERTEX tv); +void gradient3_print(U_GRADIENT3 g3); +void gradient4_print(U_GRADIENT4 g4); +void logbrush_print(U_LOGBRUSH lb); +void xform_print(U_XFORM xform); +void ciexyz_print(U_CIEXYZ ciexyz); +void ciexyztriple_print(U_CIEXYZTRIPLE cie3); +void logcolorspacea_print(U_LOGCOLORSPACEA lcsa); +void logcolorspacew_print(U_LOGCOLORSPACEW lcsa); +void panose_print(U_PANOSE panose); +void logfont_print(U_LOGFONT lf); +void logfont_panose_print(U_LOGFONT_PANOSE lfp); +void bitmapinfoheader_print(const char *Bmih); +void bitmapinfo_print(const char *Bmi); +void blend_print(U_BLEND blend); +void extlogpen_print(const PU_EXTLOGPEN elp); +void logpen_print(U_LOGPEN lp); +void logpltntry_print(U_LOGPLTNTRY lpny); +void logpalette_print(const PU_LOGPALETTE lp); +void rgndataheader_print(U_RGNDATAHEADER rdh); +void rgndata_print(const PU_RGNDATA rd); +void coloradjustment_print(U_COLORADJUSTMENT ca); +void pixelformatdescriptor_print(U_PIXELFORMATDESCRIPTOR pfd); +void emrtext_print(const char *emt, const char *record, int type); + +/* prototypes for EMR records */ +void U_EMRNOTIMPLEMENTED_print(const char *name, const char *contents, int recnum, int off); +void U_EMRHEADER_print(const char *contents, int recnum, int off); +void U_EMRPOLYBEZIER_print(const char *contents, int recnum, int off); +void U_EMRPOLYGON_print(const char *contents, int recnum, int off); +void U_EMRPOLYLINE_print(const char *contents, int recnum, int off); +void U_EMRPOLYBEZIERTO_print(const char *contents, int recnum, int off); +void U_EMRPOLYLINETO_print(const char *contents, int recnum, int off); +void U_EMRPOLYPOLYLINE_print(const char *contents, int recnum, int off); +void U_EMRPOLYPOLYGON_print(const char *contents, int recnum, int off); +void U_EMRSETWINDOWEXTEX_print(const char *contents, int recnum, int off); +void U_EMRSETWINDOWORGEX_print(const char *contents, int recnum, int off); +void U_EMRSETVIEWPORTEXTEX_print(const char *contents, int recnum, int off); +void U_EMRSETVIEWPORTORGEX_print(const char *contents, int recnum, int off); +void U_EMRSETBRUSHORGEX_print(const char *contents, int recnum, int off); +void U_EMREOF_print(const char *contents, int recnum, int off); +void U_EMRSETPIXELV_print(const char *contents, int recnum, int off); +void U_EMRSETMAPPERFLAGS_print(const char *contents, int recnum, int off); +void U_EMRSETMAPMODE_print(const char *contents, int recnum, int off); +void U_EMRSETBKMODE_print(const char *contents, int recnum, int off); +void U_EMRSETPOLYFILLMODE_print(const char *contents, int recnum, int off); +void U_EMRSETROP2_print(const char *contents, int recnum, int off); +void U_EMRSETSTRETCHBLTMODE_print(const char *contents, int recnum, int off); +void U_EMRSETTEXTALIGN_print(const char *contents, int recnum, int off); +void U_EMRSETCOLORADJUSTMENT_print(const char *contents, int recnum, int off); +void U_EMRSETTEXTCOLOR_print(const char *contents, int recnum, int off); +void U_EMRSETBKCOLOR_print(const char *contents, int recnum, int off); +void U_EMROFFSETCLIPRGN_print(const char *contents, int recnum, int off); +void U_EMRMOVETOEX_print(const char *contents, int recnum, int off); +void U_EMRSETMETARGN_print(const char *contents, int recnum, int off); +void U_EMREXCLUDECLIPRECT_print(const char *contents, int recnum, int off); +void U_EMRINTERSECTCLIPRECT_print(const char *contents, int recnum, int off); +void U_EMRSCALEVIEWPORTEXTEX_print(const char *contents, int recnum, int off); +void U_EMRSCALEWINDOWEXTEX_print(const char *contents, int recnum, int off); +void U_EMRSAVEDC_print(const char *contents, int recnum, int off); +void U_EMRRESTOREDC_print(const char *contents, int recnum, int off); +void U_EMRSETWORLDTRANSFORM_print(const char *contents, int recnum, int off); +void U_EMRMODIFYWORLDTRANSFORM_print(const char *contents, int recnum, int off); +void U_EMRSELECTOBJECT_print(const char *contents, int recnum, int off); +void U_EMRCREATEPEN_print(const char *contents, int recnum, int off); +void U_EMRCREATEBRUSHINDIRECT_print(const char *contents, int recnum, int off); +void U_EMRDELETEOBJECT_print(const char *contents, int recnum, int off); +void U_EMRANGLEARC_print(const char *contents, int recnum, int off); +void U_EMRELLIPSE_print(const char *contents, int recnum, int off); +void U_EMRRECTANGLE_print(const char *contents, int recnum, int off); +void U_EMRROUNDRECT_print(const char *contents, int recnum, int off); +void U_EMRARC_print(const char *contents, int recnum, int off); +void U_EMRCHORD_print(const char *contents, int recnum, int off); +void U_EMRPIE_print(const char *contents, int recnum, int off); +void U_EMRSELECTPALETTE_print(const char *contents, int recnum, int off); +void U_EMRCREATEPALETTE_print(const char *contents, int recnum, int off); +void U_EMRSETPALETTEENTRIES_print(const char *contents, int recnum, int off); +void U_EMRRESIZEPALETTE_print(const char *contents, int recnum, int off); +void U_EMRREALIZEPALETTE_print(const char *contents, int recnum, int off); +void U_EMREXTFLOODFILL_print(const char *contents, int recnum, int off); +void U_EMRLINETO_print(const char *contents, int recnum, int off); +void U_EMRARCTO_print(const char *contents, int recnum, int off); +void U_EMRPOLYDRAW_print(const char *contents, int recnum, int off); +void U_EMRSETARCDIRECTION_print(const char *contents, int recnum, int off); +void U_EMRSETMITERLIMIT_print(const char *contents, int recnum, int off); +void U_EMRBEGINPATH_print(const char *contents, int recnum, int off); +void U_EMRENDPATH_print(const char *contents, int recnum, int off); +void U_EMRCLOSEFIGURE_print(const char *contents, int recnum, int off); +void U_EMRFILLPATH_print(const char *contents, int recnum, int off); +void U_EMRSTROKEANDFILLPATH_print(const char *contents, int recnum, int off); +void U_EMRSTROKEPATH_print(const char *contents, int recnum, int off); +void U_EMRFLATTENPATH_print(const char *contents, int recnum, int off); +void U_EMRWIDENPATH_print(const char *contents, int recnum, int off); +void U_EMRSELECTCLIPPATH_print(const char *contents, int recnum, int off); +void U_EMRABORTPATH_print(const char *contents, int recnum, int off); +void U_EMRCOMMENT_print(const char *contents, int recnum, int off); +void U_EMRFILLRGN_print(const char *contents, int recnum, int off); +void U_EMRFRAMERGN_print(const char *contents, int recnum, int off); +void U_EMRINVERTRGN_print(const char *contents, int recnum, int off); +void U_EMRPAINTRGN_print(const char *contents, int recnum, int off); +void U_EMREXTSELECTCLIPRGN_print(const char *contents, int recnum, int off); +void U_EMRBITBLT_print(const char *contents, int recnum, int off); +void U_EMRSTRETCHBLT_print(const char *contents, int recnum, int off); +void U_EMRMASKBLT_print(const char *contents, int recnum, int off); +void U_EMRPLGBLT_print(const char *contents, int recnum, int off); +void U_EMRSETDIBITSTODEVICE_print(const char *contents, int recnum, int off); +void U_EMRSTRETCHDIBITS_print(const char *contents, int recnum, int off); +void U_EMREXTCREATEFONTINDIRECTW_print(const char *contents, int recnum, int off); +void U_EMREXTTEXTOUTA_print(const char *contents, int recnum, int off); +void U_EMREXTTEXTOUTW_print(const char *contents, int recnum, int off); +void U_EMRPOLYBEZIER16_print(const char *contents, int recnum, int off); +void U_EMRPOLYGON16_print(const char *contents, int recnum, int off); +void U_EMRPOLYLINE16_print(const char *contents, int recnum, int off); +void U_EMRPOLYBEZIERTO16_print(const char *contents, int recnum, int off); +void U_EMRPOLYLINETO16_print(const char *contents, int recnum, int off); +void U_EMRPOLYPOLYLINE16_print(const char *contents, int recnum, int off); +void U_EMRPOLYPOLYGON16_print(const char *contents, int recnum, int off); +void U_EMRPOLYDRAW16_print(const char *contents, int recnum, int off); +void U_EMRCREATEMONOBRUSH_print(const char *contents, int recnum, int off); +void U_EMRCREATEDIBPATTERNBRUSHPT_print(const char *contents, int recnum, int off); +void U_EMREXTCREATEPEN_print(const char *contents, int recnum, int off); +void U_EMRSETICMMODE_print(const char *contents, int recnum, int off); +void U_EMRCREATECOLORSPACE_print(const char *contents, int recnum, int off); +void U_EMRSETCOLORSPACE_print(const char *contents, int recnum, int off); +void U_EMRDELETECOLORSPACE_print(const char *contents, int recnum, int off); +void U_EMRPIXELFORMAT_print(const char *contents, int recnum, int off); +void U_EMRSMALLTEXTOUT_print(const char *contents, int recnum, int off); +void U_EMRALPHABLEND_print(const char *contents, int recnum, int off); +void U_EMRSETLAYOUT_print(const char *contents, int recnum, int off); +void U_EMRTRANSPARENTBLT_print(const char *contents, int recnum, int off); +void U_EMRGRADIENTFILL_print(const char *contents, int recnum, int off); +void U_EMRCREATECOLORSPACEW_print(const char *contents, int recnum, int off); +int U_emf_onerec_print(const char *contents, char *blimit, int recnum, int off); + +#ifdef __cplusplus +} +#endif + +#endif /* _UEMF_PRINT_ */ diff --git a/src/libuemf/uemf_utf.c b/src/libuemf/uemf_utf.c new file mode 100644 index 000000000..48d2510f0 --- /dev/null +++ b/src/libuemf/uemf_utf.c @@ -0,0 +1,552 @@ +/** + @file uemf_utf.c Functions for manipulating UTF and various types of text. + + + Compile with "U_VALGRIND" defined defined to enable code which lets valgrind check each record for + uninitialized data. + + Compile with "SOL8" defined for Solaris 8 or 9 (Sparc). +*/ + +/* +File: uemf_utf.c +Version: 0.0.4 +Date: 19-MAR-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <iconv.h> +#include <wchar.h> +#include <errno.h> +#include <string.h> +#include <limits.h> // for INT_MAX, INT_MIN +#include <math.h> // for U_ROUND() +#include "uemf_utf.h" + +/* Prototypes for functions used here and defined in uemf_endian.c, but which are not supposed +to be used in end user code. */ + +void U_swap2(void *ul, unsigned int count); + +/* ******************************************************************************************** */ + +/** \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 */ + +/* ********************************************************************************************** +These functions are used for development and debugging and should be be includied in production code. +*********************************************************************************************** */ + +/** + \brief Dump a UTF8 string. Not for use in production code. + \param src string to examine +*/ +void wchar8show( + const char *src + ){ + printf("char show\n"); + size_t srclen = 0; + while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } +} + +/** + \brief Dump a UTF16 string. Not for use in production code. + \param src string to examine +*/ +void wchar16show( + const uint16_t *src + ){ + printf("uint16_t show\n"); + size_t srclen = 0; + while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } +} + +/** + \brief Dump a UTF32 string. Not for use in production code. +*/ +void wchar32show( + const uint32_t *src + ){ + printf("uint32_t show\n"); + size_t srclen = 0; + while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } +} + +/** + \brief Dump a wchar_t string. Not for use in production code. + \param src string to examine +*/ +void wchartshow( + const wchar_t *src + ){ + uint32_t val; + printf("wchar_t show\n"); + size_t srclen = 0; + while(*src){ + val = *src; // because *src is wchar_t is not strictly an integer type, can cause warnings on next line + printf("%d %d %x\n",(int) srclen,val,val); + srclen++; + src++; + } +} + +/* ********************************************************************************************** +These functions are used for character type conversions, Image conversions, and other +utility operations +*********************************************************************************************** */ + +/** + \brief Find the number of (storage) characters in a 16 bit character string, not including terminator. + \param src string to examine +*/ +size_t wchar16len( + const uint16_t *src + ){ + size_t srclen = 0; + while(*src){ srclen++; src++; } + return(srclen); +} + +/** + \brief Find the number of (storage) characters in a 32 bit character string, not including terminator. + \param src string to examine +*/ +size_t wchar32len( + const uint32_t *src + ){ + size_t srclen = 0; + while(*src){ srclen++; src++; } + return(srclen); +} + +/** + \brief Strncpy for wchar16 (UTF16). + \param dst destination (already allocated) + \param src source + \param nchars number of characters to copy +*/ +void wchar16strncpy( + uint16_t *dst, + const uint16_t *src, + size_t nchars + ){ + for(;nchars;nchars--,dst++,src++){ + *dst = *src; + if(!*src)break; + } +} + +/** + \brief Fill the output string with N characters, if the input string is shorter than N, pad with nulls. + \param dst destination (already allocated) + \param src source + \param nchars number of characters to copy + +*/ +void wchar16strncpypad( + uint16_t *dst, + const uint16_t *src, + size_t nchars + ){ + for(;*src && nchars;nchars--,dst++,src++){ *dst = *src; } + for(;nchars;nchars--,dst++){ *dst = 0; } // Pad the remainder +} + +/* For the following converstion functions, remember that iconv() modifies ALL of its parameters, + so save a pointer to the destination buffer!!!! + It isn't clear that terminators are being + copied properly, so be sure allocated space is a bit larger and cleared. +*/ + +/** + \brief Convert a UTF32LE string to a UTF16LE string. + \returns pointer to new string or NULL if it fails + \param src wchar_t string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint16_t *U_Utf32leToUtf16le( + const uint32_t *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + size_t srclen,dstlen,status; + + if(max){ srclen = 4*max; } + else { srclen = 4 + 4*wchar32len(src); } //include terminator, length in BYTES + + dstlen = 2 + srclen; // this will always work, but may waste space + dst2 = dst = calloc(dstlen,1); // so there will be at least one terminator + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-16LE", "UTF-32LE"); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar16len((uint16_t *)dst2); + return((uint16_t *)dst2); +} + +/** + \brief Convert a UTF16LE string to a UTF32LE string. + \return pointer to new string or NULL if it fails + \param src UTF16LE string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint32_t *U_Utf16leToUtf32le( + const uint16_t *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = 2*max; } + else { srclen = 2*wchar16len(src)+2; } // include terminator, length in BYTES + dstlen = 2*(2 + srclen); // This should always work + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-32LE", "UTF-16LE"); + if ( conv == (iconv_t)-1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar32len((uint32_t *)dst2); + return((uint32_t *) dst2); +} + +/** + \brief Convert a Latin1 string to a UTF32LE string. + \return pointer to new string or NULL if it fails + \param src Latin1 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator + + + U_EMR_EXTTEXTOUTA records are "8 bit ASCII". In theory that is ASCII in an 8 + bit character, but numerous applications store Latin1 in them, and some + _may_ store UTF-8 in them. Since very vew Latin1 strings are valid UTF-8 strings, + call U_Utf8ToUtf32le first, and if it fails, then call this function. +*/ +uint32_t *U_Latin1ToUtf32le( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, length in BYTES + dstlen = sizeof(uint32_t)*(1 + srclen); // This should always work but might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-32LE", "LATIN1"); + if ( conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar32len((uint32_t *)dst2); + return((uint32_t *) dst2); +} + +/** + \brief Convert a UTF8 string to a UTF32LE string. + \return pointer to new string or NULL if it fails + \param src UTF8 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint32_t *U_Utf8ToUtf32le( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, length in BYTES + dstlen = sizeof(uint32_t)*(1 + srclen); // This should always work but might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-32LE", "UTF-8"); + if ( conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar32len((uint32_t *)dst2); + return((uint32_t *) dst2); +} + +/** + \brief Convert a UTF32LE string to a UTF8 string. + \return pointer to new string or NULL if it fails + \param src wchar_t string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +char *U_Utf32leToUtf8( + const uint32_t *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = 4*max; } + else { srclen = 4*(1 + wchar32len(src)); } //include terminator, length in BYTES + dstlen = 1 + srclen; // This should always work but might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-8", "UTF-32LE"); + if ( conv == (iconv_t)-1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=strlen(dst2); + return(dst2); +} + +/** + \brief Convert a UTF-8 string to a UTF16-LE string. + \return pointer to new string or NULL if it fails + \param src UTF8 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint16_t *U_Utf8ToUtf16le( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + size_t srclen,dstlen,status; + iconv_t conv; + + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, length in BYTES + dstlen = 2 * (1 + srclen); // this will always work, but may waste space + dst2 = dst =calloc(dstlen,1); // so there will always be a terminator + if(!dst)return(NULL); + conv = iconv_open("UTF-16LE", "UTF-8"); + if (conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar16len((uint16_t *)dst2); + return((uint16_t *)dst2); +} + +/** + \brief Convert a UTF16LE string to a UTF8 string. + \return pointer to new UTF8 string or NULL if it fails + \param src UTF16LE string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +char *U_Utf16leToUtf8( + const uint16_t *src, + size_t max, + size_t *len + ){ + char *dst, *dst2; + char *ret=NULL; + size_t srclen,dstlen,status; + if(max){ srclen = 2*max; } + else { srclen = 2*(1 +wchar16len(src)); } //include terminator, length in BYTES + dstlen = 1 + 2*srclen; // this will always work, but may waste space + // worst case is all glyphs (==max) need 4 UTF-8 encoded bytes + terminator. + dst2 = dst = (char *) calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-8", "UTF-16LE"); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status != (size_t) -1){ + if(len)*len=strlen(dst2); + ret=U_strdup(dst2); // make a string of exactly the right size + } + free(dst2); // free the one which was probably too big + return(ret); +} + +/** + \brief Convert a UTF16LE string to a LATIN1 string. + \return pointer to new UTF8 string or NULL if it fails + \param src UTF16LE string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +char *U_Utf16leToLatin1( + const uint16_t *src, + size_t max, + size_t *len + ){ + char *dst, *dst2; + char *ret=NULL; + size_t srclen,dstlen,status; + if(max){ srclen = 2*max; } + else { srclen = 2*(1 +wchar16len(src)); } //include terminator, length in BYTES + dstlen = 1 + srclen; // this will always work as latin1 is always 1 byte/character + ret = dst2 = dst = (char *) calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("LATIN1//TRANSLIT", "UTF-16LE"); // translate what can be, fill in with something close for the rest + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status != (size_t) -1){ + if(len)*len=strlen(dst2); + } + return(ret); +} +/** + \brief Put a single 16 bit character into UTF-16LE form. + + Used in conjunction with U_Utf16leEdit(), because the character + representation would otherwise be dependent on machine Endianness. + + \return UTF16LE representation of the character. + \param src 16 bit character + +*/ +uint16_t U_Utf16le(const uint16_t src){ + uint16_t dst=src; +#if U_BYTE_SWAP + U_swap2(&dst,1); +#endif + return(dst); +} + +/** + \brief Convert a UTF8 string to a Latin1 string. + \return pointer to new string or NULL if it fails + \param src Latin1 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator + + + WMF uses latin1, others UTF-8, only some utf-8 can be converted to latin1. + +*/ +char *U_Utf8ToLatin1( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + size_t srclen,dstlen,status; + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, length in BYTES + dstlen = (1 + srclen); // This should always work but might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("LATIN1//TRANSLIT", "UTF-8"); // translate what can be, fill in with something close for the rest + if ( conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=strlen(dst2); + return((char *) dst2); +} + +/** + \brief Convert a Latin1 string to a UTF8 string. + \return pointer to new string or NULL if it fails + \param src Latin1 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator + + + WMF uses latin1, others UTF-8, all Latin1 should be able to convert to utf-8. + +*/ +char *U_Latin1ToUtf8( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + size_t srclen,dstlen,status; + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, will waste some space + dstlen = (1 + 2*srclen); // This should always work because all latin1 convert to 1 or 2 byte UTF8, it might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-8", "LATIN1"); // everything should translate + if ( conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=strlen(dst2); + return((char *) dst2); +} + +/** + \brief Single character replacement in a UTF-16LE string. + + Used solely for the Description field which contains + embedded nulls, which makes it difficult to manipulate. Use some other character and then swap it. + + \return number of substitutions, or -1 if src is not defined + \param src UTF16LE string to edit + \param find character to replace + \param replace replacestitute character + +*/ +int U_Utf16leEdit( + uint16_t *src, + uint16_t find, + uint16_t replace + ){ + int count=0; + if(!src)return(-1); + while(*src){ + if(*src == find){ *src = replace; count++; } + src++; + } + return(count); +} + +/** + \brief strdup for when strict C99 compliance is enforced + \returns duplicate string or NULL on error + \param s string to duplicate +*/ +char *U_strdup(const char *s){ + char *news=NULL; + size_t slen; + if(s){ + slen = strlen(s) + 1; //include the terminator! + news = malloc(slen); + if(news){ + memcpy(news,s,slen); + } + } + return(news); + +} + + +#ifdef __cplusplus +} +#endif diff --git a/src/libuemf/uemf_utf.h b/src/libuemf/uemf_utf.h new file mode 100644 index 000000000..e42de23e6 --- /dev/null +++ b/src/libuemf/uemf_utf.h @@ -0,0 +1,53 @@ +/** + @file uemf_utf.h for manipulating UTF and various types of text. + +*/ + +/* +File: uemf_utf.h +Version: 0.0.1 +Date: 04-DEC-2012 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2012 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifndef _UEMF_UTF_ +#define _UEMF_UTF_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdbool.h> +#include "uemf_endian.h" + +void wchar8show(const char *src); +void wchar16show(const uint16_t *src); +void wchar32show(const uint32_t *src); +void wchartshow(const wchar_t *src); + +size_t wchar16len(const uint16_t *src); +size_t wchar32len(const uint32_t *src); +void wchar16strncpy(uint16_t *dst, const uint16_t *src, size_t nchars); +void wchar16strncpypad(uint16_t *dst, const uint16_t *src, size_t nchars); +uint16_t *U_Utf8ToUtf16le( const char *src, size_t max, size_t *len ); +uint32_t *U_Utf8ToUtf32le( const char *src, size_t max, size_t *len ); +uint32_t *U_Latin1ToUtf32le( const char *src, size_t max, size_t *len ); +uint16_t *U_Utf32leToUtf16le( const uint32_t *src, size_t max, size_t *len ); +char *U_Utf32leToUtf8( const uint32_t *src, size_t max, size_t *len ); +uint32_t *U_Utf16leToUtf32le( const uint16_t *src, size_t max, size_t *len ); +char *U_Utf16leToUtf8( const uint16_t *src, size_t max, size_t *len ); +char *U_Utf16leToLatin1( const uint16_t *src, size_t max, size_t *len ); +char *U_Utf8ToLatin1( const char *src, size_t max, size_t *len ); +char *U_Latin1ToUtf8( const char *src, size_t max, size_t *len ); +uint16_t U_Utf16le(const uint16_t src); +int U_Utf16leEdit( uint16_t *src, uint16_t find, uint16_t replace ); +char *U_strdup(const char *s); + +#ifdef __cplusplus +} +#endif + +#endif /* _UEMF_UTF_ */ diff --git a/src/libuemf/uwmf.c b/src/libuemf/uwmf.c new file mode 100644 index 000000000..9d916ecee --- /dev/null +++ b/src/libuemf/uwmf.c @@ -0,0 +1,6880 @@ +/** + @file uwmf.c Functions for manipulating WMF files and structures. + + [U_WMR*]_set all take data and return a pointer to memory holding the constructed record. + If something goes wrong a NULL pointer is returned. + [U_WMR*]_get takes a pointer to memory and returns the length of that record as well + as the values from it (in the provided fields, passed by reference.) + If something goes wrong, a size of 0 is returned. + + The _set material comes first, then all of the _get material. + + Compile with "U_VALGRIND" defined defined to enable code which lets valgrind check each record for + uninitialized data. + + Compile with "SOL8" defined for Solaris 8 or 9 (Sparc). +*/ + +/* +File: uwmf.c +Version: 0.0.11 +Date: 19-MAR-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> /* for offsetof() */ +#include <string.h> +#include <iconv.h> +#include <wchar.h> +#include <errno.h> +#include <string.h> +#include <limits.h> // for INT_MAX, INT_MIN +#include <math.h> // for U_ROUND() +#if 0 +#include <windef.h> //Not actually used, looking for collisions +#include <winnt.h> //Not actually used, looking for collisions +#include <wingdi.h> //Not actually used, looking for collisions +#endif +#include "uwmf.h" +#include "uwmf_endian.h" + +/** + \brief Look up the full numeric type of a WMR record by type. + + \return Full numeric value for this type of WMR record, Returns 0xFFFFFFFF if out of range. + \param idx WMR record type. + +*/ +uint32_t U_wmr_values(int idx){ + int ret; + int U_WMR_VALUES[256]={ + 0x0000, //!< U_WMR_EOF + 0x0201, //!< U_WMR_SETBKCOLOR + 0x0102, //!< U_WMR_SETBKMODE + 0x0103, //!< U_WMR_SETMAPMODE + 0x0104, //!< U_WMR_SETROP2 + 0x0105, //!< U_WMR_SETRELABS + 0x0106, //!< U_WMR_SETPOLYFILLMODE + 0x0107, //!< U_WMR_SETSTRETCHBLTMODE + 0x0108, //!< U_WMR_SETTEXTCHAREXTRA + 0x0209, //!< U_WMR_SETTEXTCOLOR + 0x020A, //!< U_WMR_SETTEXTJUSTIFICATION + 0x020B, //!< U_WMR_SETWINDOWORG + 0x020C, //!< U_WMR_SETWINDOWEXT + 0x020D, //!< U_WMR_SETVIEWPORTORG + 0x020E, //!< U_WMR_SETVIEWPORTEXT + 0x020F, //!< U_WMR_OFFSETWINDOWORG + 0x0410, //!< U_WMR_SCALEWINDOWEXT + 0x0211, //!< U_WMR_OFFSETVIEWPORTORG + 0x0412, //!< U_WMR_SCALEVIEWPORTEXT + 0x0213, //!< U_WMR_LINETO + 0x0214, //!< U_WMR_MOVETO + 0x0415, //!< U_WMR_EXCLUDECLIPRECT + 0x0416, //!< U_WMR_INTERSECTCLIPRECT + 0x0817, //!< U_WMR_ARC + 0x0418, //!< U_WMR_ELLIPSE + 0x0419, //!< U_WMR_FLOODFILL + 0x081A, //!< U_WMR_PIE + 0x041B, //!< U_WMR_RECTANGLE + 0x061C, //!< U_WMR_ROUNDRECT + 0x061D, //!< U_WMR_PATBLT + 0x001E, //!< U_WMR_SAVEDC + 0x041F, //!< U_WMR_SETPIXEL + 0x0220, //!< U_WMR_OFFSETCLIPRGN + 0x0521, //!< U_WMR_TEXTOUT + 0x0922, //!< U_WMR_BITBLT + 0x0B23, //!< U_WMR_STRETCHBLT + 0x0324, //!< U_WMR_POLYGON + 0x0325, //!< U_WMR_POLYLINE + 0x0626, //!< U_WMR_ESCAPE + 0x0127, //!< U_WMR_RESTOREDC + 0x0228, //!< U_WMR_FILLREGION + 0x0429, //!< U_WMR_FRAMEREGION + 0x012A, //!< U_WMR_INVERTREGION + 0x012B, //!< U_WMR_PAINTREGION + 0x012C, //!< U_WMR_SELECTCLIPREGION + 0x012D, //!< U_WMR_SELECTOBJECT + 0x012E, //!< U_WMR_SETTEXTALIGN + 0x062F, //!< U_WMR_DRAWTEXT + 0x0830, //!< U_WMR_CHORD + 0x0231, //!< U_WMR_SETMAPPERFLAGS + 0x0A32, //!< U_WMR_EXTTEXTOUT + 0x0D33, //!< U_WMR_SETDIBTODEV + 0x0234, //!< U_WMR_SELECTPALETTE + 0x0035, //!< U_WMR_REALIZEPALETTE + 0x0436, //!< U_WMR_ANIMATEPALETTE + 0x0037, //!< U_WMR_SETPALENTRIES + 0x0538, //!< U_WMR_POLYPOLYGON + 0x0139, //!< U_WMR_RESIZEPALETTE + 0x003A, //!< U_WMR_3A + 0x003B, //!< U_WMR_3B + 0x003C, //!< U_WMR_3C + 0x003D, //!< U_WMR_3D + 0x003E, //!< U_WMR_3E + 0x003F, //!< U_WMR_3F + 0x0940, //!< U_WMR_DIBBITBLT + 0x0B41, //!< U_WMR_DIBSTRETCHBLT + 0x0142, //!< U_WMR_DIBCREATEPATTERNBRUSH + 0x0F43, //!< U_WMR_STRETCHDIB + 0x0044, //!< U_WMR_44 + 0x0045, //!< U_WMR_45 + 0x0046, //!< U_WMR_46 + 0x0047, //!< U_WMR_47 + 0x0548, //!< U_WMR_EXTFLOODFILL + 0x0049, //!< U_WMR_49 + 0x004A, //!< U_WMR_4A + 0x004B, //!< U_WMR_4B + 0x014C, //!< U_WMR_4C + 0x014D, //!< U_WMR_4D + 0x004E, //!< U_WMR_4E + 0x004F, //!< U_WMR_4F + 0x0050, //!< U_WMR_50 + 0x0051, //!< U_WMR_51 + 0x0052, //!< U_WMR_52 + 0x0053, //!< U_WMR_53 + 0x0054, //!< U_WMR_54 + 0x0055, //!< U_WMR_55 + 0x0056, //!< U_WMR_56 + 0x0057, //!< U_WMR_57 + 0x0058, //!< U_WMR_58 + 0x0059, //!< U_WMR_59 + 0x005A, //!< U_WMR_5A + 0x005B, //!< U_WMR_5B + 0x005C, //!< U_WMR_5C + 0x005D, //!< U_WMR_5D + 0x005E, //!< U_WMR_5E + 0x005F, //!< U_WMR_5F + 0x0060, //!< U_WMR_60 + 0x0061, //!< U_WMR_61 + 0x0062, //!< U_WMR_62 + 0x0063, //!< U_WMR_63 + 0x0064, //!< U_WMR_64 + 0x0065, //!< U_WMR_65 + 0x0066, //!< U_WMR_66 + 0x0067, //!< U_WMR_67 + 0x0068, //!< U_WMR_68 + 0x0069, //!< U_WMR_69 + 0x006A, //!< U_WMR_6A + 0x006B, //!< U_WMR_6B + 0x006C, //!< U_WMR_6C + 0x006D, //!< U_WMR_6D + 0x006E, //!< U_WMR_6E + 0x006F, //!< U_WMR_6F + 0x0070, //!< U_WMR_70 + 0x0071, //!< U_WMR_71 + 0x0072, //!< U_WMR_72 + 0x0073, //!< U_WMR_73 + 0x0074, //!< U_WMR_74 + 0x0075, //!< U_WMR_75 + 0x0076, //!< U_WMR_76 + 0x0077, //!< U_WMR_77 + 0x0078, //!< U_WMR_78 + 0x0079, //!< U_WMR_79 + 0x007A, //!< U_WMR_7A + 0x007B, //!< U_WMR_7B + 0x007C, //!< U_WMR_7C + 0x007D, //!< U_WMR_7D + 0x007E, //!< U_WMR_7E + 0x007F, //!< U_WMR_7F + 0x0080, //!< U_WMR_80 + 0x0081, //!< U_WMR_81 + 0x0082, //!< U_WMR_82 + 0x0083, //!< U_WMR_83 + 0x0084, //!< U_WMR_84 + 0x0085, //!< U_WMR_85 + 0x0086, //!< U_WMR_86 + 0x0087, //!< U_WMR_87 + 0x0088, //!< U_WMR_88 + 0x0089, //!< U_WMR_89 + 0x008A, //!< U_WMR_8A + 0x008B, //!< U_WMR_8B + 0x008C, //!< U_WMR_8C + 0x008D, //!< U_WMR_8D + 0x008E, //!< U_WMR_8E + 0x008F, //!< U_WMR_8F + 0x0090, //!< U_WMR_90 + 0x0091, //!< U_WMR_91 + 0x0092, //!< U_WMR_92 + 0x0093, //!< U_WMR_93 + 0x0094, //!< U_WMR_94 + 0x0095, //!< U_WMR_95 + 0x0096, //!< U_WMR_96 + 0x0097, //!< U_WMR_97 + 0x0098, //!< U_WMR_98 + 0x0099, //!< U_WMR_99 + 0x009A, //!< U_WMR_9A + 0x009B, //!< U_WMR_9B + 0x009C, //!< U_WMR_9C + 0x009D, //!< U_WMR_9D + 0x009E, //!< U_WMR_9E + 0x009F, //!< U_WMR_9F + 0x00A0, //!< U_WMR_A0 + 0x00A1, //!< U_WMR_A1 + 0x00A2, //!< U_WMR_A2 + 0x00A3, //!< U_WMR_A3 + 0x00A4, //!< U_WMR_A4 + 0x00A5, //!< U_WMR_A5 + 0x00A6, //!< U_WMR_A6 + 0x00A7, //!< U_WMR_A7 + 0x00A8, //!< U_WMR_A8 + 0x00A9, //!< U_WMR_A9 + 0x00AA, //!< U_WMR_AA + 0x00AB, //!< U_WMR_AB + 0x00AC, //!< U_WMR_AC + 0x00AD, //!< U_WMR_AD + 0x00AE, //!< U_WMR_AE + 0x00AF, //!< U_WMR_AF + 0x00B0, //!< U_WMR_B0 + 0x00B1, //!< U_WMR_B1 + 0x00B2, //!< U_WMR_B2 + 0x00B3, //!< U_WMR_B3 + 0x00B4, //!< U_WMR_B4 + 0x00B5, //!< U_WMR_B5 + 0x00B6, //!< U_WMR_B6 + 0x00B7, //!< U_WMR_B7 + 0x00B8, //!< U_WMR_B8 + 0x00B9, //!< U_WMR_B9 + 0x00BA, //!< U_WMR_BA + 0x00BB, //!< U_WMR_BB + 0x00BC, //!< U_WMR_BC + 0x00BD, //!< U_WMR_BD + 0x00BE, //!< U_WMR_BE + 0x00BF, //!< U_WMR_BF + 0x00C0, //!< U_WMR_C0 + 0x00C1, //!< U_WMR_C1 + 0x00C2, //!< U_WMR_C2 + 0x00C3, //!< U_WMR_C3 + 0x00C4, //!< U_WMR_C4 + 0x00C5, //!< U_WMR_C5 + 0x00C6, //!< U_WMR_C6 + 0x00C7, //!< U_WMR_C7 + 0x00C8, //!< U_WMR_C8 + 0x00C9, //!< U_WMR_C9 + 0x00CA, //!< U_WMR_CA + 0x00CB, //!< U_WMR_CB + 0x00CC, //!< U_WMR_CC + 0x00CD, //!< U_WMR_CD + 0x00CE, //!< U_WMR_CE + 0x00CF, //!< U_WMR_CF + 0x00D0, //!< U_WMR_D0 + 0x00D1, //!< U_WMR_D1 + 0x00D2, //!< U_WMR_D2 + 0x00D3, //!< U_WMR_D3 + 0x00D4, //!< U_WMR_D4 + 0x00D5, //!< U_WMR_D5 + 0x00D6, //!< U_WMR_D6 + 0x00D7, //!< U_WMR_D7 + 0x00D8, //!< U_WMR_D8 + 0x00D9, //!< U_WMR_D9 + 0x00DA, //!< U_WMR_DA + 0x00DB, //!< U_WMR_DB + 0x00DC, //!< U_WMR_DC + 0x00DD, //!< U_WMR_DD + 0x00DE, //!< U_WMR_DE + 0x00DF, //!< U_WMR_DF + 0x00E0, //!< U_WMR_E0 + 0x00E1, //!< U_WMR_E1 + 0x00E2, //!< U_WMR_E2 + 0x00E3, //!< U_WMR_E3 + 0x00E4, //!< U_WMR_E4 + 0x00E5, //!< U_WMR_E5 + 0x00E6, //!< U_WMR_E6 + 0x00E7, //!< U_WMR_E7 + 0x00E8, //!< U_WMR_E8 + 0x00E9, //!< U_WMR_E9 + 0x00EA, //!< U_WMR_EA + 0x00EB, //!< U_WMR_EB + 0x00EC, //!< U_WMR_EC + 0x00ED, //!< U_WMR_ED + 0x00EE, //!< U_WMR_EE + 0x00EF, //!< U_WMR_EF + 0x01F0, //!< U_WMR_DELETEOBJECT + 0x00F1, //!< U_WMR_F1 + 0x00F2, //!< U_WMR_F2 + 0x00F3, //!< U_WMR_F3 + 0x00F4, //!< U_WMR_F4 + 0x00F5, //!< U_WMR_F5 + 0x00F6, //!< U_WMR_F6 + 0x00F7, //!< U_WMR_CREATEPALETTE + 0x00F8, //!< U_WMR_CREATEBRUSH + 0x01F9, //!< U_WMR_CREATEPATTERNBRUSH + 0x02FA, //!< U_WMR_CREATEPENINDIRECT + 0x02FB, //!< U_WMR_CREATEFONTINDIRECT + 0x02FC, //!< U_WMR_CREATEBRUSHINDIRECT + 0x02FD, //!< U_WMR_CREATEBITMAPINDIRECT + 0x06FE, //!< U_WMR_CREATEBITMAP + 0x06FF //!< U_WMR_CREATEREGION + }; + if(idx<U_WMR_MIN || idx > U_WMR_MAX){ ret = 0xFFFFFFFF; } + else { ret = U_WMR_VALUES[idx]; } + return(ret); +} + +/** + \brief Look up the name of the WMR record by type. Returns U_WMR_INVALID if out of range. + + \return name of the WMR record, "U_WMR_INVALID" if out of range. + \param idx WMR record type. + +*/ +char *U_wmr_names(int idx){ + int ret; + static char *U_WMR_NAMES[257]={ + "U_WMR_EOF", + "U_WMR_SETBKCOLOR", + "U_WMR_SETBKMODE", + "U_WMR_SETMAPMODE", + "U_WMR_SETROP2", + "U_WMR_SETRELABS", + "U_WMR_SETPOLYFILLMODE", + "U_WMR_SETSTRETCHBLTMODE", + "U_WMR_SETTEXTCHAREXTRA", + "U_WMR_SETTEXTCOLOR", + "U_WMR_SETTEXTJUSTIFICATION", + "U_WMR_SETWINDOWORG", + "U_WMR_SETWINDOWEXT", + "U_WMR_SETVIEWPORTORG", + "U_WMR_SETVIEWPORTEXT", + "U_WMR_OFFSETWINDOWORG", + "U_WMR_SCALEWINDOWEXT", + "U_WMR_OFFSETVIEWPORTORG", + "U_WMR_SCALEVIEWPORTEXT", + "U_WMR_LINETO", + "U_WMR_MOVETO", + "U_WMR_EXCLUDECLIPRECT", + "U_WMR_INTERSECTCLIPRECT", + "U_WMR_ARC", + "U_WMR_ELLIPSE", + "U_WMR_FLOODFILL", + "U_WMR_PIE", + "U_WMR_RECTANGLE", + "U_WMR_ROUNDRECT", + "U_WMR_PATBLT", + "U_WMR_SAVEDC", + "U_WMR_SETPIXEL", + "U_WMR_OFFSETCLIPRGN", + "U_WMR_TEXTOUT", + "U_WMR_BITBLT", + "U_WMR_STRETCHBLT", + "U_WMR_POLYGON", + "U_WMR_POLYLINE", + "U_WMR_ESCAPE", + "U_WMR_RESTOREDC", + "U_WMR_FILLREGION", + "U_WMR_FRAMEREGION", + "U_WMR_INVERTREGION", + "U_WMR_PAINTREGION", + "U_WMR_SELECTCLIPREGION", + "U_WMR_SELECTOBJECT", + "U_WMR_SETTEXTALIGN", + "U_WMR_DRAWTEXT", + "U_WMR_CHORD", + "U_WMR_SETMAPPERFLAGS", + "U_WMR_EXTTEXTOUT", + "U_WMR_SETDIBTODEV", + "U_WMR_SELECTPALETTE", + "U_WMR_REALIZEPALETTE", + "U_WMR_ANIMATEPALETTE", + "U_WMR_SETPALENTRIES", + "U_WMR_POLYPOLYGON", + "U_WMR_RESIZEPALETTE", + "U_WMR_3A", + "U_WMR_3B", + "U_WMR_3C", + "U_WMR_3D", + "U_WMR_3E", + "U_WMR_3F", + "U_WMR_DIBBITBLT", + "U_WMR_DIBSTRETCHBLT", + "U_WMR_DIBCREATEPATTERNBRUSH", + "U_WMR_STRETCHDIB", + "U_WMR_44", + "U_WMR_45", + "U_WMR_46", + "U_WMR_47", + "U_WMR_EXTFLOODFILL", + "U_WMR_49", + "U_WMR_4A", + "U_WMR_4B", + "U_WMR_4C", + "U_WMR_4D", + "U_WMR_4E", + "U_WMR_4F", + "U_WMR_50", + "U_WMR_51", + "U_WMR_52", + "U_WMR_53", + "U_WMR_54", + "U_WMR_55", + "U_WMR_56", + "U_WMR_57", + "U_WMR_58", + "U_WMR_59", + "U_WMR_5A", + "U_WMR_5B", + "U_WMR_5C", + "U_WMR_5D", + "U_WMR_5E", + "U_WMR_5F", + "U_WMR_60", + "U_WMR_61", + "U_WMR_62", + "U_WMR_63", + "U_WMR_64", + "U_WMR_65", + "U_WMR_66", + "U_WMR_67", + "U_WMR_68", + "U_WMR_69", + "U_WMR_6A", + "U_WMR_6B", + "U_WMR_6C", + "U_WMR_6D", + "U_WMR_6E", + "U_WMR_6F", + "U_WMR_70", + "U_WMR_71", + "U_WMR_72", + "U_WMR_73", + "U_WMR_74", + "U_WMR_75", + "U_WMR_76", + "U_WMR_77", + "U_WMR_78", + "U_WMR_79", + "U_WMR_7A", + "U_WMR_7B", + "U_WMR_7C", + "U_WMR_7D", + "U_WMR_7E", + "U_WMR_7F", + "U_WMR_80", + "U_WMR_81", + "U_WMR_82", + "U_WMR_83", + "U_WMR_84", + "U_WMR_85", + "U_WMR_86", + "U_WMR_87", + "U_WMR_88", + "U_WMR_89", + "U_WMR_8A", + "U_WMR_8B", + "U_WMR_8C", + "U_WMR_8D", + "U_WMR_8E", + "U_WMR_8F", + "U_WMR_90", + "U_WMR_91", + "U_WMR_92", + "U_WMR_93", + "U_WMR_94", + "U_WMR_95", + "U_WMR_96", + "U_WMR_97", + "U_WMR_98", + "U_WMR_99", + "U_WMR_9A", + "U_WMR_9B", + "U_WMR_9C", + "U_WMR_9D", + "U_WMR_9E", + "U_WMR_9F", + "U_WMR_A0", + "U_WMR_A1", + "U_WMR_A2", + "U_WMR_A3", + "U_WMR_A4", + "U_WMR_A5", + "U_WMR_A6", + "U_WMR_A7", + "U_WMR_A8", + "U_WMR_A9", + "U_WMR_AA", + "U_WMR_AB", + "U_WMR_AC", + "U_WMR_AD", + "U_WMR_AE", + "U_WMR_AF", + "U_WMR_B0", + "U_WMR_B1", + "U_WMR_B2", + "U_WMR_B3", + "U_WMR_B4", + "U_WMR_B5", + "U_WMR_B6", + "U_WMR_B7", + "U_WMR_B8", + "U_WMR_B9", + "U_WMR_BA", + "U_WMR_BB", + "U_WMR_BC", + "U_WMR_BD", + "U_WMR_BE", + "U_WMR_BF", + "U_WMR_C0", + "U_WMR_C1", + "U_WMR_C2", + "U_WMR_C3", + "U_WMR_C4", + "U_WMR_C5", + "U_WMR_C6", + "U_WMR_C7", + "U_WMR_C8", + "U_WMR_C9", + "U_WMR_CA", + "U_WMR_CB", + "U_WMR_CC", + "U_WMR_CD", + "U_WMR_CE", + "U_WMR_CF", + "U_WMR_D0", + "U_WMR_D1", + "U_WMR_D2", + "U_WMR_D3", + "U_WMR_D4", + "U_WMR_D5", + "U_WMR_D6", + "U_WMR_D7", + "U_WMR_D8", + "U_WMR_D9", + "U_WMR_DA", + "U_WMR_DB", + "U_WMR_DC", + "U_WMR_DD", + "U_WMR_DE", + "U_WMR_DF", + "U_WMR_E0", + "U_WMR_E1", + "U_WMR_E2", + "U_WMR_E3", + "U_WMR_E4", + "U_WMR_E5", + "U_WMR_E6", + "U_WMR_E7", + "U_WMR_E8", + "U_WMR_E9", + "U_WMR_EA", + "U_WMR_EB", + "U_WMR_EC", + "U_WMR_ED", + "U_WMR_EE", + "U_WMR_EF", + "U_WMR_DELETEOBJECT", + "U_WMR_F1", + "U_WMR_F2", + "U_WMR_F3", + "U_WMR_F4", + "U_WMR_F5", + "U_WMR_F6", + "U_WMR_CREATEPALETTE", + "U_WMR_CREATEBRUSH", + "U_WMR_CREATEPATTERNBRUSH", + "U_WMR_CREATEPENINDIRECT", + "U_WMR_CREATEFONTINDIRECT", + "U_WMR_CREATEBRUSHINDIRECT", + "U_WMR_CREATEBITMAPINDIRECT", + "U_WMR_CREATEBITMAP", + "U_WMR_CREATEREGION" + }; + if(idx<U_WMR_MIN || idx > U_WMR_MAX){ ret = 256; } + else { ret = idx; } + return(U_WMR_NAMES[ret]); +} + +/** + \brief Text description of Escape record type. + \return name of the WMR record, "UNKNOWN_ESCAPE" if out of range. + \param idx Escape record type. +*/ +char *U_wmr_escnames(int idx){ + char *name; + if(idx>=0 && idx <= 0x0023){ + switch(idx){ + case 0x0001: name = "NEWFRAME"; break; + case 0x0002: name = "ABORTDOC"; break; + case 0x0003: name = "NEXTBAND"; break; + case 0x0004: name = "SETCOLORTABLE"; break; + case 0x0005: name = "GETCOLORTABLE"; break; + case 0x0006: name = "FLUSHOUT"; break; + case 0x0007: name = "DRAFTMODE"; break; + case 0x0008: name = "QUERYESCSUPPORT"; break; + case 0x0009: name = "SETABORTPROC"; break; + case 0x000A: name = "STARTDOC"; break; + case 0x000B: name = "ENDDOC"; break; + case 0x000C: name = "GETPHYSPAGESIZE"; break; + case 0x000D: name = "GETPRINTINGOFFSET"; break; + case 0x000E: name = "GETSCALINGFACTOR"; break; + case 0x000F: name = "META_ESCAPE_ENHANCED_METAFILE"; break; + case 0x0010: name = "SETPENWIDTH"; break; + case 0x0011: name = "SETCOPYCOUNT"; break; + case 0x0012: name = "SETPAPERSOURCE"; break; + case 0x0013: name = "PASSTHROUGH"; break; + case 0x0014: name = "GETTECHNOLOGY"; break; + case 0x0015: name = "SETLINECAP"; break; + case 0x0016: name = "SETLINEJOIN"; break; + case 0x0017: name = "SETMITERLIMIT"; break; + case 0x0018: name = "BANDINFO"; break; + case 0x0019: name = "DRAWPATTERNRECT"; break; + case 0x001A: name = "GETVECTORPENSIZE"; break; + case 0x001B: name = "GETVECTORBRUSHSIZE"; break; + case 0x001C: name = "ENABLEDUPLEX"; break; + case 0x001D: name = "GETSETPAPERBINS"; break; + case 0x001E: name = "GETSETPRINTORIENT"; break; + case 0x001F: name = "ENUMPAPERBINS"; break; + case 0x0020: name = "SETDIBSCALING"; break; + case 0x0021: name = "EPSPRINTING"; break; + case 0x0022: name = "ENUMPAPERMETRICS"; break; + case 0x0023: name = "GETSETPAPERMETRICS"; break; + } + } + else if(idx == 0x0025){ name = "POSTSCRIPT_DATA"; } + else if(idx == 0x0026){ name = "POSTSCRIPT_IGNORE"; } + else if(idx == 0x002A){ name = "GETDEVICEUNITS"; } + else if(idx == 0x0100){ name = "GETEXTENDEDTEXTMETRICS"; } + else if(idx == 0x0102){ name = "GETPAIRKERNTABLE"; } + else if(idx == 0x0200){ name = "EXTTEXTOUT"; } + else if(idx == 0x0201){ name = "GETFACENAME"; } + else if(idx == 0x0202){ name = "DOWNLOADFACE"; } + else if(idx == 0x0801){ name = "METAFILE_DRIVER"; } + else if(idx == 0x0C01){ name = "QUERYDIBSUPPORT"; } + else if(idx == 0x1000){ name = "BEGIN_PATH"; } + else if(idx == 0x1001){ name = "CLIP_TO_PATH"; } + else if(idx == 0x1002){ name = "END_PATH"; } + else if(idx == 0x100E){ name = "OPEN_CHANNEL"; } + else if(idx == 0x100F){ name = "DOWNLOADHEADER"; } + else if(idx == 0x1010){ name = "CLOSE_CHANNEL"; } + else if(idx == 0x1013){ name = "POSTSCRIPT_PASSTHROUGH"; } + else if(idx == 0x1014){ name = "ENCAPSULATED_POSTSCRIPT";} + else if(idx == 0x1015){ name = "POSTSCRIPT_IDENTIFY"; } + else if(idx == 0x1016){ name = "POSTSCRIPT_INJECTION"; } + else if(idx == 0x1017){ name = "CHECKJPEGFORMAT"; } + else if(idx == 0x1018){ name = "CHECKPNGFORMAT"; } + else if(idx == 0x1019){ name = "GET_PS_FEATURESETTING"; } + else if(idx == 0x101A){ name = "MXDC_ESCAPE"; } + else if(idx == 0x11D8){ name = "SPCLPASSTHROUGH2"; } + else { name = "UNKNOWN_ESCAPE"; } + return(name); +} + +/* one prototype from uwmf_endian. Put it here because end user should never need to see it, so +not in uemf.h or uwmf_endian.h */ +void U_swap2(void *ul, unsigned int count); + +/** + \brief Derive from bounding box and start and end arc, for WMF arc, chord, or pie records, the center, start, and end points, and the bounding rectangle. + + \return 0 on success, other values on errors. + \param rclBox Bounding box of Arc + \param ArcStart Coordinates for Start of Arc + \param ArcEnd Coordinates for End of Arc + \param f1 1 if rotation angle >= 180, else 0 + \param f2 Rotation direction, 1 if counter clockwise, else 0 + \param center Center coordinates + \param start Start coordinates (point on the ellipse defined by rect) + \param end End coordinates (point on the ellipse defined by rect) + \param size W,H of the x,y axes of the bounding rectangle. +*/ +int wmr_arc_points( + U_RECT16 rclBox16, + U_POINT16 ArcStart16, + U_POINT16 ArcEnd16, + int *f1, + int f2, + PU_PAIRF center, + PU_PAIRF start, + PU_PAIRF end, + PU_PAIRF size + ){ + U_RECTL rclBox; + U_POINTL ArcStart,ArcEnd; + rclBox.left = rclBox16.left; + rclBox.top = rclBox16.top; + rclBox.right = rclBox16.right; + rclBox.bottom = rclBox16.bottom; + ArcStart.x = ArcStart16.x; + ArcStart.y = ArcStart16.y; + ArcEnd.x = ArcEnd16.x; + ArcEnd.y = ArcEnd16.y; + return emr_arc_points_common(&rclBox, &ArcStart, &ArcEnd, f1, f2, center, start, end, size); +} + +/** + \brief A U_RECT16 may have its values swapped, L<->R and T<->B, this extracts the leftmost as left, and so forth. + \param rc U_RECT156 binary contents of an WMF file + \param left the leftmost of rc.left and rc.right + \param top the topmost of rc.top and rc.bottom + \param right the rightmost of rc.left and rc.right + \param bottom the bottommost of rc.top and rc.bottom +*/ +void U_sanerect16(U_RECT16 rc, double *left, double *top, double *right, double *bottom){ + if(rc.left < rc.right) { *left = rc.left; *right = rc.right; } + else { *left = rc.right; *right = rc.left; } + if(rc.top < rc.bottom){ *top = rc.top; *bottom = rc.bottom; } + else{ *top = rc.bottom; *bottom = rc.top; } +} + +/* ********************************************************************************************** +These definitions are for code pieces that are used many times in the following implementation. These +definitions are not needed in end user code, so they are here rather than in uwmf.h. +*********************************************************************************************** */ + +/** + \brief Get record size in bytes from U_WMR* record, which may not be aligned + \return number of bytes in record. +*/ +uint32_t U_wmr_size(const U_METARECORD *record){ + uint32_t Size16; + memcpy(&Size16,record, 4); + return(2*Size16); +} + +#define SET_CB_FROM_PXBMI(A,B,C,D,E,F) /* A=Px, B=Bmi, C=cbImage, D=cbImage4, E=cbBmi, F=cbPx */ \ + if(A){\ + if(!B)return(NULL); /* size is derived from U_BITMAPINFO, but NOT from its size field, go figure*/ \ + C = F;\ + D = UP4(C); /* pixel array might not be a multiples of 4 bytes*/ \ + E = U_SIZE_BITMAPINFOHEADER + 4 * get_real_color_count((char *)&(B->bmiHeader)); /* bmiheader + colortable*/ \ + }\ + else { C = 0; D = 0; E=0; } + + +/** + \brief Create and return a U_FONT structure. + \return pointer to the created U_FONT structure. + \param Height Height in Logical units + \param Width Average Width in Logical units + \param Escapement Angle in 0.1 degrees betweem escapement vector and X axis + \param Orientation Angle in 0.1 degrees between baseline and X axis + \param Weight LF_Weight Enumeration + \param Italic LF_Italic Enumeration + \param Underline LF_Underline Enumeration + \param StrikeOut LF_StrikeOut Enumeration + \param CharSet LF_CharSet Enumeration + \param OutPrecision LF_OutPrecision Enumeration + \param ClipPrecision LF_ClipPrecision Enumeration + \param Quality LF_Quality Enumeration + \param PitchAndFamily LF_PitchAndFamily Enumeration + \param FaceName Name of font. ANSI Latin1, null terminated. +*/ +PU_FONT U_FONT_set( + int16_t Height, //!< Height in Logical units + int16_t Width, //!< Average Width in Logical units + int16_t Escapement, //!< Angle in 0.1 degrees betweem escapement vector and X axis + int16_t Orientation, //!< Angle in 0.1 degrees between baseline and X axis + int16_t Weight, //!< LF_Weight Enumeration + uint8_t Italic, //!< LF_Italic Enumeration + uint8_t Underline, //!< LF_Underline Enumeration + uint8_t StrikeOut, //!< LF_StrikeOut Enumeration + uint8_t CharSet, //!< LF_CharSet Enumeration + uint8_t OutPrecision, //!< LF_OutPrecision Enumeration + uint8_t ClipPrecision, //!< LF_ClipPrecision Enumeration + uint8_t Quality, //!< LF_Quality Enumeration + uint8_t PitchAndFamily, //!< LF_PitchAndFamily Enumeration + char *FaceName //!< Name of font. ANSI Latin1, null terminated. + ){ + PU_FONT font; + int slen = 1 + strlen(FaceName); /* include terminator */ + if(slen & 1)slen++; /* storage length even */ + font = (PU_FONT) calloc(1,slen + U_SIZE_FONT_CORE); /* use calloc to auto fill in terminating '\0'*/ + if(font){ + font->Height = Height; + font->Width = Width; + font->Escapement = Escapement; + font->Orientation = Orientation; + font->Weight = Weight; + font->Italic = Italic; + font->Underline = Underline; + font->StrikeOut = StrikeOut; + font->CharSet = CharSet; + font->OutPrecision = OutPrecision; + font->ClipPrecision = ClipPrecision; + font->Quality = Quality; + font->PitchAndFamily = PitchAndFamily; + strcpy((char *)&font->FaceName, FaceName); + } + return(font); +} + +/** + \brief Create and return a U_PLTENTRY structure. + \return the created U_PLTENTRY structure. + \param Color Color for the U_PLTENTRY +*/ +U_PLTNTRY U_PLTNTRY_set(U_COLORREF Color){ + U_PLTNTRY pe; + pe.Value = Color.Reserved; + pe.Blue = Color.Blue; + pe.Green = Color.Green; + pe.Red = Color.Red; + return(pe); +} + +/** + \brief Create and return a U_PALETTE structure. + \return pointer to the created U_PALETTE structure. + \param Start Either 0x0300 or an offset into the Palette table + \param NumEntries Number of U_LOGPLTNTRY objects + \param PalEntries Pointer to array of PaletteEntry Objects +*/ +PU_PALETTE U_PLTENTRY_set( + uint16_t Start, //!< Either 0x0300 or an offset into the Palette table + uint16_t NumEntries, //!< Number of U_LOGPLTNTRY objects + PU_PLTNTRY PalEntries //!< Pointer to array of PaletteEntry Objects + ){ + PU_PALETTE Palette = NULL; + if(NumEntries){ + Palette = malloc(4 + 4*NumEntries); + if(Palette){ + Palette->Start = Start; + Palette->NumEntries = NumEntries; + memcpy(&Palette->PalEntries, PalEntries, NumEntries*4); + } + } + return(Palette); +} + +/** + \brief Create and return a U_PEN structure. + \return the created U_PEN structure. + \param Style PenStyle Enumeration + \param Width Width of Pen + \param Color Pen Color. +*/ +U_PEN U_PEN_set( + uint16_t Style, //!< PenStyle Enumeration + uint16_t Width, //!< Width of Pen + U_COLORREF Color //!< Pen Color. + ){ + U_PEN p; + p.Style = Style; + p.Widthw[0] = Width; + p.Widthw[1] = 0; /* ignored */ + p.Color.Red = Color.Red; + p.Color.Green = Color.Green; + p.Color.Blue = Color.Blue; + p.Color.Reserved = Color.Reserved; + return(p); +} + +/** + \brief Create and return a U_RECT16 structure from Upper Left and Lower Right corner points. + \param ul upper left corner of rectangle + \param lr lower right corner of rectangle +*/ +U_RECT16 U_RECT16_set( + U_POINT16 ul, + U_POINT16 lr + ){ + U_RECT16 rect; + rect.left = ul.x; + rect.top = ul.y; + rect.right = lr.x; + rect.bottom = lr.y; + return(rect); +} + +/** + \brief Create and return a U_BITMAP16 structure + \return pointer to the U_BITMAP16 structure, or NULL on failure + \param Type bitmap Type (not described at all in the WMF PDF) + \param Width bitmap width in pixels. + \param Height bitmap height in scan lines. + \param LineN each array line in Bits is a multiple of this (4 for a DIB) + \param BitsPixel number of adjacent color bits on each plane (R bits + G bits + B bits ????) + \param Bits bitmap pixel data. Bytes contained = (((Width * BitsPixel + 15) >> 4) << 1) * Height +*/ +PU_BITMAP16 U_BITMAP16_set( + const int16_t Type, + const int16_t Width, + const int16_t Height, + const int16_t LineN, + const uint8_t BitsPixel, + const char *Bits + ){ + PU_BITMAP16 bm16; + uint32_t irecsize; + int cbBits,iHeight; + int usedbytes; + int16_t WidthBytes; // total bytes per scan line (used and padding). + + usedbytes = (Width * BitsPixel + 7)/8; // width of line in fully and partially occupied bytes + WidthBytes = (LineN * ((usedbytes + (LineN - 1) ) / LineN)); // Account for padding required by line alignment in the pixel array + + iHeight = (Height < 0 ? -Height : Height); /* DIB can use a negative height, but it does not look like a Bitmap16 object can */ + cbBits = WidthBytes * iHeight; + if(!Bits || cbBits<=0)return(NULL); + irecsize = U_SIZE_BITMAP16 + cbBits; + bm16 = (PU_BITMAP16) malloc(irecsize); + if(bm16){ + bm16->Type = Type; + bm16->Width = Width; + bm16->Height = iHeight; + bm16->WidthBytes = WidthBytes; + bm16->Planes = 1; + bm16->BitsPixel = BitsPixel; + memcpy((char *)bm16 + U_SIZE_BITMAP16,Bits,cbBits); + } + return(bm16); +} + +/** + \brief Create and return a U_SCAN structure + \return U_SCAN structure + \param count Number of entries in the ScanLines array + \param top Y coordinate of the top scanline + \param bottom Y coordinate of the bottom scanline + \param ScanLines Array of 16 bit left/right pairs, array has 2*count entries +*/ +PU_SCAN U_SCAN_set( + uint16_t count, //!< Number of entries in the ScanLines array + uint16_t top, //!< Y coordinate of the top scanline + uint16_t bottom, //!< Y coordinate of the bottom scanline + uint16_t *ScanLines //!< Array of 16 bit left/right pairs, array has 2*count entries + ){ + PU_SCAN scan=NULL; + int size = 6 + count*4; + scan = malloc(size); + if(scan){ + scan->count = count; + scan->top = top; + scan->bottom = bottom; + memcpy(&scan->ScanLines,ScanLines,4*count); + } + return(scan); +} + +/** + \brief Create and return a U_REGION structure + \return pointer to created U_REGION structure or NULL on error + \param Size aScans in bytes + regions size in bytes (size of this header plus all U_SCAN objects?) + \param sCount number of scan objects in region (docs say scanlines, but then no way to add sizes) + \param sMax largest number of points in any scan + \param sRect bounding rectangle + \param aScans series of U_SCAN objects to append. This is also an array of uint16_t, but should be handled as a bunch of U_SCAN objects tightly packed into the buffer. +*/ +PU_REGION U_REGION_set( + int16_t Size, //!< aScans in bytes + regions size in bytes (size of this header plus all U_SCAN objects?) + int16_t sCount, //!< number of scan objects in region (docs say scanlines, but then no way to add sizes) + int16_t sMax, //!< largest number of points in any scan + U_RECT16 sRect, //!< bounding rectangle + uint16_t *aScans //!< series of U_SCAN objects to append. This is also an array of uint16_t, but should be handled as a bunch of U_SCAN objects tightly packed into the buffer. + ){ + PU_REGION region=NULL; + char *psc; + int scansize,i,off; + psc = (char *)aScans; + for(scansize=i=0; i<sCount; i++){ + off = 6 + 4*(((PU_SCAN)psc)->count); + scansize += off; + psc += off; + } + region = malloc(U_SIZE_REGION + scansize); + if(region){ + region->ignore1 = 0; + region->Type = 0x0006; + region->ignore2 = 0; + region->Size = Size; + region->sCount = sCount; + region->sMax = sMax; + region->sRect = sRect; + memcpy(®ion->aScans,aScans,scansize); + } + return(region); +} + + +/** + \brief Create and return a U_WLOGBRUSH structure. + \return the created U_WLOGBRUSH structure. + \param Style BrushStyle Enumeration + \param Color Brush Color value + \param Hatch HatchStyle Enumeration +*/ +U_WLOGBRUSH U_WLOGBRUSH_set( + uint16_t Style, //!< BrushStyle Enumeration + U_COLORREF Color, //!< Brush Color value + uint16_t Hatch //!< HatchStyle Enumeration + ){ + U_WLOGBRUSH lb; + lb.Style = Style; + lb.Color.Red = Color.Red; + lb.Color.Green = Color.Green; + lb.Color.Blue = Color.Blue; + lb.Color.Reserved = Color.Reserved; + lb.Hatch = Hatch; + return(lb); +} + + +/** + \brief Create and return a U_PAIRF structure. + \return pointer to the created U_PAIRF structure. + \param x x value + \param y y value +*/ +PU_PAIRF U_PAIRF_set( + float x, //!< x value + float y //!< y value + ){ + PU_PAIRF pf=malloc(U_SIZE_PAIRF); + if(pf){ + pf->x = x; + pf->y = y; + } + return(pf); +} + +/* ********************************************************************************************** +These functions are used for Image conversions and other +utility operations. Character type conversions are in uwmf_utf.c +*********************************************************************************************** */ + +/** + \brief Calculate the int16_t checksum of the buffer for the number of positions specified. This is XOR of all values. + \return checksum + \param buf array of uint16_t values + \param count number of members in buf + +*/ +int16_t U_16_checksum(int16_t *buf, int count){ + int16_t result=0; + for(;count;count--){ + result ^= *buf++; + } + return(result); +} + +/** + \brief Dump a WMFHANDLES structure. Not for use in production code. + \param string Text to output before dumping eht structure + \param handle Handle + \param wht WMFHANDLES structure to dump +*/ +void dumpwht( + char *string, + unsigned int *handle, + WMFHANDLES *wht + ){ + uint32_t i; + printf("%s\n",string); + printf("lo: %d hi: %d peak: %d\n", wht->lolimit, wht->hilimit, wht->peak); + if(handle){ + printf("handle: %d \n",*handle); + } + for(i=0;i<=5;i++){ + printf("table[%d]: %d\n",i,wht->table[i]); + } +} + +/** + \brief Make up an approximate dx array to pass to U_WMREXTTEXTOUT_set(), based on character height and weight. + + Take abs. value of character height, get width by multiplying by 0.6, and correct weight + approximately, with formula (measured on screen for one text line of Arial). + Caller is responsible for free() on the returned pointer. + + \return pointer to dx array + \param height character height (absolute value will be used) + \param weight LF_Weight Enumeration (character weight) + \param members Number of entries to put into dx + +*/ +int16_t *dx16_set( + int32_t height, + uint32_t weight, + uint32_t members + ){ + uint32_t i, width; + int16_t *dx; + dx = (int16_t *) malloc(members * sizeof(int16_t)); + if(dx){ + if(U_FW_DONTCARE == weight)weight=U_FW_NORMAL; + width = (uint32_t) U_ROUND(((float) (height > 0 ? height : -height)) * 0.6 * (0.00024*(float) weight + 0.904)); + for ( i = 0; i < members; i++ ){ dx[i] = (width > INT16_MAX ? INT16_MAX : width); } + } + return(dx); +} +/** + \brief Look up the properties (a bit map) of a type of WMR record. + Bits that may be set are defined in "Draw Properties" in uemf.h, they are U_DRAW_NOTEMPTY, etc.. + \return bitmap of WMR record properties, or U_WMR_INVALID on error or release of all memory. + \param type WMR record type. If U_WMR_INVALID release memory. (There is no U_WMR_INVALID WMR record type) + +*/ +uint32_t U_wmr_properties(uint32_t type){ + static uint32_t *table=NULL; + uint32_t result = U_WMR_INVALID; // initialized to indicate an error (on a lookup) or nothing (on a memory release) + if(type == U_WMR_INVALID){ + if(table)free(table); + table=NULL; + } + else if(type<=U_WMR_MAX){ // type is uint so always >=0, no need to test U_WMR_MIN, which is 0. + if(!table){ + table = (uint32_t *) malloc(sizeof(uint32_t)*(1 + U_WMR_MAX)); + if(!table)return(result); + // 0x200 0x100 0x80 0x40 0x20 0x10 0x08 0x04 0x02 0x01 + // properties (U_DRAW_*) TEXT ALTERS ONLYTO VISIBLE + // NOFILL OBJECT PATH FORCE CLOSED NOTEMPTY + // Record Type + table[0x00] = 0x0A0; // U_WMREOF 0 0 1 0 1 0 0 0 0 0 Force out any pending draw + table[0x01] = 0x020; // U_WMRSETBKCOLOR 0 0 0 0 1 0 0 0 0 0 + table[0x02] = 0x020; // U_WMRSETBKMODE 0 0 0 0 1 0 0 0 0 0 + table[0x03] = 0x0A0; // U_WMRSETMAPMODE 0 0 1 0 1 0 0 0 0 0 + table[0x04] = 0x0A0; // U_WMRSETROP2 0 0 1 0 1 0 0 0 0 0 + table[0x05] = 0x000; // U_WMRSETRELABS 0 0 0 0 0 0 0 0 0 0 No idea what this is supposed to do + table[0x06] = 0x0A0; // U_WMRSETPOLYFILLMODE 0 0 1 0 1 0 0 0 0 0 + table[0x07] = 0x0A0; // U_WMRSETSTRETCHBLTMODE 0 0 1 0 1 0 0 0 0 0 + table[0x08] = 0x000; // U_WMRSETTEXTCHAREXTRA 0 0 0 0 0 0 0 0 0 0 + table[0x09] = 0x020; // U_WMRSETTEXTCOLOR 0 0 0 0 1 0 0 0 0 0 + table[0x0A] = 0x020; // U_WMRSETTEXTJUSTIFICATION 0 0 0 0 1 0 0 0 0 0 + table[0x0B] = 0x0A0; // U_WMRSETWINDOWORG 0 0 1 0 1 0 0 0 0 0 + table[0x0C] = 0x0A0; // U_WMRSETWINDOWEXT 0 0 1 0 1 0 0 0 0 0 + table[0x0D] = 0x0A0; // U_WMRSETVIEWPORTORG 0 0 1 0 1 0 0 0 0 0 + table[0x0E] = 0x0A0; // U_WMRSETVIEWPORTEXT 0 0 1 0 1 0 0 0 0 0 + table[0x0F] = 0x000; // U_WMROFFSETWINDOWORG 0 0 0 0 0 0 0 0 0 0 + table[0x10] = 0x000; // U_WMRSCALEWINDOWEXT 0 0 0 0 0 0 0 0 0 0 + table[0x11] = 0x0A0; // U_WMROFFSETVIEWPORTORG 0 0 1 0 1 0 0 0 0 0 + table[0x12] = 0x0A0; // U_WMRSCALEVIEWPORTEXT 0 0 1 0 1 0 0 0 0 0 + table[0x13] = 0x28B; // U_WMRLINETO 1 0 1 0 0 0 1 0 1 1 + table[0x14] = 0x289; // U_WMRMOVETO 1 0 1 0 0 0 1 0 0 1 + table[0x15] = 0x0A0; // U_WMREXCLUDECLIPRECT 0 0 1 0 1 0 0 0 0 0 + table[0x16] = 0x0A0; // U_WMRINTERSECTCLIPRECT 0 0 1 0 1 0 0 0 0 0 + table[0x17] = 0x283; // U_WMRARC 1 0 1 0 0 0 0 0 1 1 + table[0x18] = 0x087; // U_WMRELLIPSE 0 0 1 0 0 0 0 1 1 1 + table[0x19] = 0x082; // U_WMRFLOODFILL 0 0 1 0 0 0 0 0 1 0 + table[0x1A] = 0x087; // U_WMRPIE 0 0 1 0 0 0 0 1 1 1 + table[0x1B] = 0x087; // U_WMRRECTANGLE 0 0 1 0 0 0 0 1 1 1 + table[0x1C] = 0x087; // U_WMRROUNDRECT 0 0 1 0 0 0 0 1 1 1 + table[0x1D] = 0x000; // U_WMRPATBLT 0 0 1 0 0 0 0 1 1 1 + table[0x1E] = 0x0A0; // U_WMRSAVEDC 0 0 1 0 1 0 0 0 0 0 + table[0x1F] = 0x082; // U_WMRSETPIXEL 0 0 1 0 0 0 0 0 1 0 + table[0x20] = 0x0A0; // U_WMROFFSETCLIPRGN 0 0 1 0 1 0 0 0 0 0 + table[0x21] = 0x002; // U_WMRTEXTOUT 0 0 0 0 0 0 0 0 1 0 + table[0x22] = 0x082; // U_WMRBITBLT 0 0 1 0 0 0 0 0 1 0 + table[0x23] = 0x082; // U_WMRSTRETCHBLT 0 0 1 0 0 0 0 0 1 0 + table[0x24] = 0x083; // U_WMRPOLYGON 0 0 1 0 0 0 0 0 1 1 + table[0x25] = 0x283; // U_WMRPOLYLINE 1 0 1 0 0 0 0 0 1 1 + table[0x26] = 0x0A0; // U_WMRESCAPE 0 0 1 0 1 0 0 0 0 0 + table[0x27] = 0x0A0; // U_WMRRESTOREDC 0 0 1 0 1 0 0 0 0 0 + table[0x28] = 0x082; // U_WMRFILLREGION 0 0 1 0 0 0 0 0 1 0 + table[0x29] = 0x082; // U_WMRFRAMEREGION 0 0 1 0 0 0 0 0 1 0 + table[0x2A] = 0x082; // U_WMRINVERTREGION 0 0 1 0 0 0 0 0 1 0 + table[0x2B] = 0x082; // U_WMRPAINTREGION 0 0 1 0 0 0 0 0 1 0 + table[0x2C] = 0x0A0; // U_WMRSELECTCLIPREGION 0 0 1 0 1 0 0 0 0 0 + table[0x2D] = 0x020; // U_WMRSELECTOBJECT 0 0 0 0 1 0 0 0 0 0 + table[0x2E] = 0x020; // U_WMRSETTEXTALIGN 0 0 0 0 1 0 0 0 0 0 + table[0x2F] = 0x002; // U_WMRDRAWTEXT 0 0 0 0 0 0 0 0 1 0 no idea what this is supposed to do + table[0x30] = 0x087; // U_WMRCHORD 0 0 1 0 0 0 0 1 1 1 + table[0x31] = 0x0A0; // U_WMRSETMAPPERFLAGS 0 0 1 0 1 0 0 0 0 0 + table[0x32] = 0x002; // U_WMREXTTEXTOUT 0 0 0 0 0 0 0 0 1 0 + table[0x33] = 0x000; // U_WMRSETDIBTODEV 0 0 0 0 0 0 0 0 0 0 + table[0x34] = 0x0A0; // U_WMRSELECTPALETTE 0 0 1 0 1 0 0 0 0 0 + table[0x35] = 0x0A0; // U_WMRREALIZEPALETTE 0 0 1 0 1 0 0 0 0 0 + table[0x36] = 0x0A0; // U_WMRANIMATEPALETTE 0 0 1 0 1 0 0 0 0 0 + table[0x37] = 0x0A0; // U_WMRSETPALENTRIES 0 0 1 0 1 0 0 0 0 0 + table[0x38] = 0x087; // U_WMRPOLYPOLYGON 0 0 1 0 0 0 0 1 1 1 + table[0x39] = 0x0A0; // U_WMRRESIZEPALETTE 0 0 1 0 1 0 0 0 0 0 + table[0x3A] = 0x000; // U_WMR3A 0 0 0 0 0 0 0 0 0 0 + table[0x3B] = 0x000; // U_WMR3B 0 0 0 0 0 0 0 0 0 0 + table[0x3C] = 0x000; // U_WMR3C 0 0 0 0 0 0 0 0 0 0 + table[0x3D] = 0x000; // U_WMR3D 0 0 0 0 0 0 0 0 0 0 + table[0x3E] = 0x000; // U_WMR3E 0 0 0 0 0 0 0 0 0 0 + table[0x3F] = 0x000; // U_WMR3F 0 0 0 0 0 0 0 0 0 0 + table[0x40] = 0x0A0; // U_WMRDIBBITBLT 0 0 1 0 1 0 0 0 0 0 + table[0x41] = 0x0A0; // U_WMRDIBSTRETCHBLT 0 0 1 0 1 0 0 0 0 0 + table[0x42] = 0x080; // U_WMRDIBCREATEPATTERNBRUSH 0 0 1 0 0 0 0 0 0 0 Not selected yet, so no change in drawing conditions + table[0x43] = 0x0A0; // U_WMRSTRETCHDIB 0 0 1 0 1 0 0 0 0 0 + table[0x44] = 0x000; // U_WMR44 0 0 0 0 0 0 0 0 0 0 + table[0x45] = 0x000; // U_WMR45 0 0 0 0 0 0 0 0 0 0 + table[0x46] = 0x000; // U_WMR46 0 0 0 0 0 0 0 0 0 0 + table[0x47] = 0x000; // U_WMR47 0 0 0 0 0 0 0 0 0 0 + table[0x48] = 0x082; // U_WMREXTFLOODFILL 0 0 1 0 0 0 0 0 1 0 + table[0x49] = 0x000; // U_WMR49 0 0 0 0 0 0 0 0 0 0 + table[0x4A] = 0x000; // U_WMR4A 0 0 0 0 0 0 0 0 0 0 + table[0x4B] = 0x000; // U_WMR4B 0 0 0 0 0 0 0 0 0 0 + table[0x4C] = 0x000; // U_WMR4C 0 0 0 0 0 0 0 0 0 0 + table[0x4D] = 0x000; // U_WMR4D 0 0 0 0 0 0 0 0 0 0 + table[0x4E] = 0x000; // U_WMR4E 0 0 0 0 0 0 0 0 0 0 + table[0x4F] = 0x000; // U_WMR4F 0 0 0 0 0 0 0 0 0 0 + table[0x50] = 0x000; // U_WMR50 0 0 0 0 0 0 0 0 0 0 + table[0x51] = 0x000; // U_WMR51 0 0 0 0 0 0 0 0 0 0 + table[0x52] = 0x000; // U_WMR52 0 0 0 0 0 0 0 0 0 0 + table[0x53] = 0x000; // U_WMR53 0 0 0 0 0 0 0 0 0 0 + table[0x54] = 0x000; // U_WMR54 0 0 0 0 0 0 0 0 0 0 + table[0x55] = 0x000; // U_WMR55 0 0 0 0 0 0 0 0 0 0 + table[0x56] = 0x000; // U_WMR56 0 0 0 0 0 0 0 0 0 0 + table[0x57] = 0x000; // U_WMR57 0 0 0 0 0 0 0 0 0 0 + table[0x58] = 0x000; // U_WMR58 0 0 0 0 0 0 0 0 0 0 + table[0x59] = 0x000; // U_WMR59 0 0 0 0 0 0 0 0 0 0 + table[0x5A] = 0x000; // U_WMR5A 0 0 0 0 0 0 0 0 0 0 + table[0x5B] = 0x000; // U_WMR5B 0 0 0 0 0 0 0 0 0 0 + table[0x5C] = 0x000; // U_WMR5C 0 0 0 0 0 0 0 0 0 0 + table[0x5D] = 0x000; // U_WMR5D 0 0 0 0 0 0 0 0 0 0 + table[0x5E] = 0x000; // U_WMR5E 0 0 0 0 0 0 0 0 0 0 + table[0x5F] = 0x000; // U_WMR5F 0 0 0 0 0 0 0 0 0 0 + table[0x60] = 0x000; // U_WMR60 0 0 0 0 0 0 0 0 0 0 + table[0x61] = 0x000; // U_WMR61 0 0 0 0 0 0 0 0 0 0 + table[0x62] = 0x000; // U_WMR62 0 0 0 0 0 0 0 0 0 0 + table[0x63] = 0x000; // U_WMR63 0 0 0 0 0 0 0 0 0 0 + table[0x64] = 0x000; // U_WMR64 0 0 0 0 0 0 0 0 0 0 + table[0x65] = 0x000; // U_WMR65 0 0 0 0 0 0 0 0 0 0 + table[0x66] = 0x000; // U_WMR66 0 0 0 0 0 0 0 0 0 0 + table[0x67] = 0x000; // U_WMR67 0 0 0 0 0 0 0 0 0 0 + table[0x68] = 0x000; // U_WMR68 0 0 0 0 0 0 0 0 0 0 + table[0x69] = 0x000; // U_WMR69 0 0 0 0 0 0 0 0 0 0 + table[0x6A] = 0x000; // U_WMR6A 0 0 0 0 0 0 0 0 0 0 + table[0x6B] = 0x000; // U_WMR6B 0 0 0 0 0 0 0 0 0 0 + table[0x6C] = 0x000; // U_WMR6C 0 0 0 0 0 0 0 0 0 0 + table[0x6D] = 0x000; // U_WMR6D 0 0 0 0 0 0 0 0 0 0 + table[0x6E] = 0x000; // U_WMR6E 0 0 0 0 0 0 0 0 0 0 + table[0x6F] = 0x000; // U_WMR6F 0 0 0 0 0 0 0 0 0 0 + table[0x70] = 0x000; // U_WMR70 0 0 0 0 0 0 0 0 0 0 + table[0x71] = 0x000; // U_WMR71 0 0 0 0 0 0 0 0 0 0 + table[0x72] = 0x000; // U_WMR72 0 0 0 0 0 0 0 0 0 0 + table[0x73] = 0x000; // U_WMR73 0 0 0 0 0 0 0 0 0 0 + table[0x74] = 0x000; // U_WMR74 0 0 0 0 0 0 0 0 0 0 + table[0x75] = 0x000; // U_WMR75 0 0 0 0 0 0 0 0 0 0 + table[0x76] = 0x000; // U_WMR76 0 0 0 0 0 0 0 0 0 0 + table[0x77] = 0x000; // U_WMR77 0 0 0 0 0 0 0 0 0 0 + table[0x78] = 0x000; // U_WMR78 0 0 0 0 0 0 0 0 0 0 + table[0x79] = 0x000; // U_WMR79 0 0 0 0 0 0 0 0 0 0 + table[0x7A] = 0x000; // U_WMR7A 0 0 0 0 0 0 0 0 0 0 + table[0x7B] = 0x000; // U_WMR7B 0 0 0 0 0 0 0 0 0 0 + table[0x7C] = 0x000; // U_WMR7C 0 0 0 0 0 0 0 0 0 0 + table[0x7D] = 0x000; // U_WMR7D 0 0 0 0 0 0 0 0 0 0 + table[0x7E] = 0x000; // U_WMR7E 0 0 0 0 0 0 0 0 0 0 + table[0x7F] = 0x000; // U_WMR7F 0 0 0 0 0 0 0 0 0 0 + table[0x80] = 0x000; // U_WMR80 0 0 0 0 0 0 0 0 0 0 + table[0x81] = 0x000; // U_WMR81 0 0 0 0 0 0 0 0 0 0 + table[0x82] = 0x000; // U_WMR82 0 0 0 0 0 0 0 0 0 0 + table[0x83] = 0x000; // U_WMR83 0 0 0 0 0 0 0 0 0 0 + table[0x84] = 0x000; // U_WMR84 0 0 0 0 0 0 0 0 0 0 + table[0x85] = 0x000; // U_WMR85 0 0 0 0 0 0 0 0 0 0 + table[0x86] = 0x000; // U_WMR86 0 0 0 0 0 0 0 0 0 0 + table[0x87] = 0x000; // U_WMR87 0 0 0 0 0 0 0 0 0 0 + table[0x88] = 0x000; // U_WMR88 0 0 0 0 0 0 0 0 0 0 + table[0x89] = 0x000; // U_WMR89 0 0 0 0 0 0 0 0 0 0 + table[0x8A] = 0x000; // U_WMR8A 0 0 0 0 0 0 0 0 0 0 + table[0x8B] = 0x000; // U_WMR8B 0 0 0 0 0 0 0 0 0 0 + table[0x8C] = 0x000; // U_WMR8C 0 0 0 0 0 0 0 0 0 0 + table[0x8D] = 0x000; // U_WMR8D 0 0 0 0 0 0 0 0 0 0 + table[0x8E] = 0x000; // U_WMR8E 0 0 0 0 0 0 0 0 0 0 + table[0x8F] = 0x000; // U_WMR8F 0 0 0 0 0 0 0 0 0 0 + table[0x90] = 0x000; // U_WMR90 0 0 0 0 0 0 0 0 0 0 + table[0x91] = 0x000; // U_WMR91 0 0 0 0 0 0 0 0 0 0 + table[0x92] = 0x000; // U_WMR92 0 0 0 0 0 0 0 0 0 0 + table[0x93] = 0x000; // U_WMR93 0 0 0 0 0 0 0 0 0 0 + table[0x94] = 0x000; // U_WMR94 0 0 0 0 0 0 0 0 0 0 + table[0x95] = 0x000; // U_WMR95 0 0 0 0 0 0 0 0 0 0 + table[0x96] = 0x000; // U_WMR96 0 0 0 0 0 0 0 0 0 0 + table[0x97] = 0x000; // U_WMR97 0 0 0 0 0 0 0 0 0 0 + table[0x98] = 0x000; // U_WMR98 0 0 0 0 0 0 0 0 0 0 + table[0x99] = 0x000; // U_WMR99 0 0 0 0 0 0 0 0 0 0 + table[0x9A] = 0x000; // U_WMR9A 0 0 0 0 0 0 0 0 0 0 + table[0x9B] = 0x000; // U_WMR9B 0 0 0 0 0 0 0 0 0 0 + table[0x9C] = 0x000; // U_WMR9C 0 0 0 0 0 0 0 0 0 0 + table[0x9D] = 0x000; // U_WMR9D 0 0 0 0 0 0 0 0 0 0 + table[0x9E] = 0x000; // U_WMR9E 0 0 0 0 0 0 0 0 0 0 + table[0x9F] = 0x000; // U_WMR9F 0 0 0 0 0 0 0 0 0 0 + table[0xA0] = 0x000; // U_WMRA0 0 0 0 0 0 0 0 0 0 0 + table[0xA1] = 0x000; // U_WMRA1 0 0 0 0 0 0 0 0 0 0 + table[0xA2] = 0x000; // U_WMRA2 0 0 0 0 0 0 0 0 0 0 + table[0xA3] = 0x000; // U_WMRA3 0 0 0 0 0 0 0 0 0 0 + table[0xA4] = 0x000; // U_WMRA4 0 0 0 0 0 0 0 0 0 0 + table[0xA5] = 0x000; // U_WMRA5 0 0 0 0 0 0 0 0 0 0 + table[0xA6] = 0x000; // U_WMRA6 0 0 0 0 0 0 0 0 0 0 + table[0xA7] = 0x000; // U_WMRA7 0 0 0 0 0 0 0 0 0 0 + table[0xA8] = 0x000; // U_WMRA8 0 0 0 0 0 0 0 0 0 0 + table[0xA9] = 0x000; // U_WMRA9 0 0 0 0 0 0 0 0 0 0 + table[0xAA] = 0x000; // U_WMRAA 0 0 0 0 0 0 0 0 0 0 + table[0xAB] = 0x000; // U_WMRAB 0 0 0 0 0 0 0 0 0 0 + table[0xAC] = 0x000; // U_WMRAC 0 0 0 0 0 0 0 0 0 0 + table[0xAD] = 0x000; // U_WMRAD 0 0 0 0 0 0 0 0 0 0 + table[0xAE] = 0x000; // U_WMRAE 0 0 0 0 0 0 0 0 0 0 + table[0xAF] = 0x000; // U_WMRAF 0 0 0 0 0 0 0 0 0 0 + table[0xB0] = 0x000; // U_WMRB0 0 0 0 0 0 0 0 0 0 0 + table[0xB1] = 0x000; // U_WMRB1 0 0 0 0 0 0 0 0 0 0 + table[0xB2] = 0x000; // U_WMRB2 0 0 0 0 0 0 0 0 0 0 + table[0xB3] = 0x000; // U_WMRB3 0 0 0 0 0 0 0 0 0 0 + table[0xB4] = 0x000; // U_WMRB4 0 0 0 0 0 0 0 0 0 0 + table[0xB5] = 0x000; // U_WMRB5 0 0 0 0 0 0 0 0 0 0 + table[0xB6] = 0x000; // U_WMRB6 0 0 0 0 0 0 0 0 0 0 + table[0xB7] = 0x000; // U_WMRB7 0 0 0 0 0 0 0 0 0 0 + table[0xB8] = 0x000; // U_WMRB8 0 0 0 0 0 0 0 0 0 0 + table[0xB9] = 0x000; // U_WMRB9 0 0 0 0 0 0 0 0 0 0 + table[0xBA] = 0x000; // U_WMRBA 0 0 0 0 0 0 0 0 0 0 + table[0xBB] = 0x000; // U_WMRBB 0 0 0 0 0 0 0 0 0 0 + table[0xBC] = 0x000; // U_WMRBC 0 0 0 0 0 0 0 0 0 0 + table[0xBD] = 0x000; // U_WMRBD 0 0 0 0 0 0 0 0 0 0 + table[0xBE] = 0x000; // U_WMRBE 0 0 0 0 0 0 0 0 0 0 + table[0xBF] = 0x000; // U_WMRBF 0 0 0 0 0 0 0 0 0 0 + table[0xC0] = 0x000; // U_WMRC0 0 0 0 0 0 0 0 0 0 0 + table[0xC1] = 0x000; // U_WMRC1 0 0 0 0 0 0 0 0 0 0 + table[0xC2] = 0x000; // U_WMRC2 0 0 0 0 0 0 0 0 0 0 + table[0xC3] = 0x000; // U_WMRC3 0 0 0 0 0 0 0 0 0 0 + table[0xC4] = 0x000; // U_WMRC4 0 0 0 0 0 0 0 0 0 0 + table[0xC5] = 0x000; // U_WMRC5 0 0 0 0 0 0 0 0 0 0 + table[0xC6] = 0x000; // U_WMRC6 0 0 0 0 0 0 0 0 0 0 + table[0xC7] = 0x000; // U_WMRC7 0 0 0 0 0 0 0 0 0 0 + table[0xC8] = 0x000; // U_WMRC8 0 0 0 0 0 0 0 0 0 0 + table[0xC9] = 0x000; // U_WMRC9 0 0 0 0 0 0 0 0 0 0 + table[0xCA] = 0x000; // U_WMRCA 0 0 0 0 0 0 0 0 0 0 + table[0xCB] = 0x000; // U_WMRCB 0 0 0 0 0 0 0 0 0 0 + table[0xCC] = 0x000; // U_WMRCC 0 0 0 0 0 0 0 0 0 0 + table[0xCD] = 0x000; // U_WMRCD 0 0 0 0 0 0 0 0 0 0 + table[0xCE] = 0x000; // U_WMRCE 0 0 0 0 0 0 0 0 0 0 + table[0xCF] = 0x000; // U_WMRCF 0 0 0 0 0 0 0 0 0 0 + table[0xD0] = 0x000; // U_WMRD0 0 0 0 0 0 0 0 0 0 0 + table[0xD1] = 0x000; // U_WMRD1 0 0 0 0 0 0 0 0 0 0 + table[0xD2] = 0x000; // U_WMRD2 0 0 0 0 0 0 0 0 0 0 + table[0xD3] = 0x000; // U_WMRD3 0 0 0 0 0 0 0 0 0 0 + table[0xD4] = 0x000; // U_WMRD4 0 0 0 0 0 0 0 0 0 0 + table[0xD5] = 0x000; // U_WMRD5 0 0 0 0 0 0 0 0 0 0 + table[0xD6] = 0x000; // U_WMRD6 0 0 0 0 0 0 0 0 0 0 + table[0xD7] = 0x000; // U_WMRD7 0 0 0 0 0 0 0 0 0 0 + table[0xD8] = 0x000; // U_WMRD8 0 0 0 0 0 0 0 0 0 0 + table[0xD9] = 0x000; // U_WMRD9 0 0 0 0 0 0 0 0 0 0 + table[0xDA] = 0x000; // U_WMRDA 0 0 0 0 0 0 0 0 0 0 + table[0xDB] = 0x000; // U_WMRDB 0 0 0 0 0 0 0 0 0 0 + table[0xDC] = 0x000; // U_WMRDC 0 0 0 0 0 0 0 0 0 0 + table[0xDD] = 0x000; // U_WMRDD 0 0 0 0 0 0 0 0 0 0 + table[0xDE] = 0x000; // U_WMRDE 0 0 0 0 0 0 0 0 0 0 + table[0xDF] = 0x000; // U_WMRDF 0 0 0 0 0 0 0 0 0 0 + table[0xE0] = 0x000; // U_WMRE0 0 0 0 0 0 0 0 0 0 0 + table[0xE1] = 0x000; // U_WMRE1 0 0 0 0 0 0 0 0 0 0 + table[0xE2] = 0x000; // U_WMRE2 0 0 0 0 0 0 0 0 0 0 + table[0xE3] = 0x000; // U_WMRE3 0 0 0 0 0 0 0 0 0 0 + table[0xE4] = 0x000; // U_WMRE4 0 0 0 0 0 0 0 0 0 0 + table[0xE5] = 0x000; // U_WMRE5 0 0 0 0 0 0 0 0 0 0 + table[0xE6] = 0x000; // U_WMRE6 0 0 0 0 0 0 0 0 0 0 + table[0xE7] = 0x000; // U_WMRE7 0 0 0 0 0 0 0 0 0 0 + table[0xE8] = 0x000; // U_WMRE8 0 0 0 0 0 0 0 0 0 0 + table[0xE9] = 0x000; // U_WMRE9 0 0 0 0 0 0 0 0 0 0 + table[0xEA] = 0x000; // U_WMREA 0 0 0 0 0 0 0 0 0 0 + table[0xEB] = 0x000; // U_WMREB 0 0 0 0 0 0 0 0 0 0 + table[0xEC] = 0x000; // U_WMREC 0 0 0 0 0 0 0 0 0 0 + table[0xED] = 0x000; // U_WMRED 0 0 0 0 0 0 0 0 0 0 + table[0xEE] = 0x000; // U_WMREE 0 0 0 0 0 0 0 0 0 0 + table[0xEF] = 0x000; // U_WMREF 0 0 0 0 0 0 0 0 0 0 + table[0xF0] = 0x020; // U_WMRDELETEOBJECT 0 0 0 0 1 0 0 0 0 0 + table[0xF1] = 0x000; // U_WMRF1 0 0 0 0 0 0 0 0 0 0 + table[0xF2] = 0x000; // U_WMRF2 0 0 0 0 0 0 0 0 0 0 + table[0xF3] = 0x000; // U_WMRF3 0 0 0 0 0 0 0 0 0 0 + table[0xF4] = 0x000; // U_WMRF4 0 0 0 0 0 0 0 0 0 0 + table[0xF5] = 0x000; // U_WMRF5 0 0 0 0 0 0 0 0 0 0 + table[0xF6] = 0x000; // U_WMRF6 0 0 0 0 0 0 0 0 0 0 + table[0xF7] = 0x120; // U_WMRCREATEPALETTE 0 1 0 0 1 0 0 0 0 0 Not selected yet, so no change in drawing conditions + table[0xF8] = 0x120; // U_WMRCREATEBRUSH 0 1 0 0 1 0 0 0 0 0 " + table[0xF9] = 0x120; // U_WMRCREATEPATTERNBRUSH 0 1 0 0 1 0 0 0 0 0 " + table[0xFA] = 0x120; // U_WMRCREATEPENINDIRECT 0 1 0 0 1 0 0 0 0 0 " + table[0xFB] = 0x120; // U_WMRCREATEFONTINDIRECT 0 1 0 0 1 0 0 0 0 0 " + table[0xFC] = 0x120; // U_WMRCREATEBRUSHINDIRECT 0 1 0 0 1 0 0 0 0 0 " + table[0xFD] = 0x020; // U_WMRCREATEBITMAPINDIRECT 0 0 0 0 1 0 0 0 0 0 " + table[0xFE] = 0x020; // U_WMRCREATEBITMAP 0 0 0 0 1 0 0 0 0 0 " + table[0xFF] = 0x120; // U_WMRCREATEREGION 0 1 0 0 1 0 0 0 0 0 " + } + result = table[type]; + } + return(result); +} + +/* ********************************************************************************************** +These functions are for setting up, appending to, and then tearing down an WMF structure, including +writing the final data structure out to a file. +*********************************************************************************************** */ + +/** + \brief Duplicate an WMR record. + \param wmr record to duplicate +*/ +char *wmr_dup( + const char *wmr + ){ + char *dup; + uint32_t irecsize; + + if(!wmr)return(NULL); + memcpy(&irecsize,wmr,4); /* Size16_4 field is at offset 0 */ + irecsize *= 2; + dup=malloc(irecsize); + if(dup){ memcpy(dup,wmr,irecsize); } + return(dup); +} + + +/* some of these functions are identical to the emf ones, handled by defines in uemf.h,use the emf versions */ + +/** + \brief Start constructing an wmf in memory. Supply the file name and initial size. + \return 0 for success, >=0 for failure. + \param name WMF filename (will be opened) + \param initsize Initialize WMF in memory to hold this many bytes + \param chunksize When needed increase WMF in memory by this number of bytes + \param wt WMF in memory + + +*/ +int wmf_start( + const char *name, + const uint32_t initsize, + const uint32_t chunksize, + WMFTRACK **wt + ){ + FILE *fp; + WMFTRACK *wtl=NULL; + + if(initsize < 1)return(1); + if(chunksize < 1)return(2); + if(!name)return(3); + wtl = (WMFTRACK *) malloc(sizeof(WMFTRACK)); + if(!wtl)return(4); + wtl->buf = malloc(initsize); // no need to zero the memory + if(!wtl->buf){ + free(wtl); + return(5); + } + fp=wmf_fopen(name,U_WRITE); + if(!fp){ + free(wtl->buf); + free(wtl); + return(6); + } + wtl->fp = fp; + wtl->allocated = initsize; + wtl->used = 0; + wtl->records = 0; + wtl->PalEntries = 0; + wtl->chunk = chunksize; + wtl->largest = 0; /* only used by WMF */ + wtl->sumObjects = 0; /* only used by WMF */ + *wt=wtl; + return(0); +} + +/** + \brief Release memory for an wmf structure in memory. Call this after wmf_finish(). + \return 0 on success, >=1 on failure + \param wt WMF in memory +*/ +int wmf_free( + WMFTRACK **wt + ){ + WMFTRACK *wtl; + if(!wt)return(1); + wtl=*wt; + if(!wtl)return(2); + free(wtl->buf); + free(wtl); + *wt=NULL; + return(0); +} + +/** + \brief Finalize the emf in memory and write it to the file. + \return 0 on success, >=1 on failure + \param wt WMF in memory +*/ +int wmf_finish( + WMFTRACK *wt + ){ + char *record; + int off; + uint32_t tmp; + uint16_t tmp16; + + if(!wt->fp)return(1); // This could happen if something stomps on memory, otherwise should be caught in wmf_start + + // Set the header fields which were unknown up until this point + + + if(((PU_WMRPLACEABLE) wt->buf)->Key == 0x9AC6CDD7){ off = U_SIZE_WMRPLACEABLE; } + else { off = 0; } + + record = (wt->buf + off); + tmp = (wt->used)/2; + memcpy(record + offsetof(U_WMRHEADER,Sizew), &tmp, 4); /* 16 bit words in file. not aligned */ + tmp = (wt->largest)/2; + memcpy(record + offsetof(U_WMRHEADER,maxSize), &tmp, 4); /* 16 bit words in largest record, not aligned */ + if(wt->sumObjects > UINT16_MAX)return(3); + tmp16 = wt->sumObjects; + memcpy(record + offsetof(U_WMRHEADER,nObjects), &tmp16, 2); /* Total number of brushes, pens, and other graphics objects defined in this file */ + +#if U_BYTE_SWAP + //This is a Big Endian machine, WMF data must be Little Endian + U_wmf_endian(wt->buf,wt->used,1); +#endif + + (void) U_wmr_properties(U_WMR_INVALID); /* force the release of the lookup table memory, returned value is irrelevant */ + if(1 != fwrite(wt->buf,wt->used,1,wt->fp))return(2); + (void) fclose(wt->fp); + wt->fp=NULL; + return(0); +} + +/** + \brief Retrieve contents of an WMF file by name. + \return 0 on success, >=1 on failure + \param filename Name of file to open, including the path + \param contents Contents of the file. Buffer must be free()'d by caller. + \param length Number of bytes in Contents +*/ +int wmf_readdata( + const char *filename, + char **contents, + size_t *length + ){ + FILE *fp; + int status=0; + + *contents=NULL; + fp=wmf_fopen(filename,U_READ); + if(!fp){ status = 1; } + else { + // read the entire file into memory + fseek(fp, 0, SEEK_END); // move to end + *length = ftell(fp); + rewind(fp); + *contents = (char *) malloc(*length); + if(!*contents){ + status = 2; + } + else { + size_t inbytes = fread(*contents,*length,1,fp); + if(inbytes != 1){ + free(*contents); + status = 3; + } + else { +#if U_BYTE_SWAP + //This is a Big Endian machine, WMF data is Little Endian + U_wmf_endian(*contents,*length,0); // LE to BE +#endif + } + } + fclose(fp); + } + return(status); +} + +/** + \brief Append an WMF record to a wmf in memory. This may reallocate buf memory. + \return 0 for success, >=1 for failure. + \param rec Record to append to WMF in memory + \param wt WMF in memory + \param freerec If true, free rec after append +*/ +int wmf_append( + U_METARECORD *rec, + WMFTRACK *wt, + int freerec + ){ + size_t deficit; + uint32_t wp; + uint32_t size; + + size = U_wmr_size(rec); +#ifdef U_VALGRIND + printf("\nbefore \n"); + printf(" probe %d\n",memprobe(rec, size)); + printf("after \n"); +#endif + if(!rec)return(1); + if(!wt)return(2); + if(size + wt->used > wt->allocated){ + deficit = size + wt->used - wt->allocated; + if(deficit < wt->chunk)deficit = wt->chunk; + wt->allocated += deficit; + wt->buf = realloc(wt->buf,wt->allocated); + if(!wt->buf)return(3); + } + memcpy(wt->buf + wt->used, rec, size); + wt->used += size; + wt->records++; + if(wt->largest < size)wt->largest=size; + /* does the record create an object: brush, font, palette, pen, or region ? + Following EOF properties comes back as U_WMR_INVALID */ + wp = U_wmr_properties(U_WMRTYPE(rec)); + if((wp != U_WMR_INVALID) && (U_DRAW_OBJECT & wp))wt->sumObjects++; + if(freerec){ free(rec); } + return(0); +} + +/** + \brief Append an WMF header to a wmf in memory. This may reallocate buf memory. + WMF header is not a normal record, method used to figure out its size is different. + \return 0 for success, >=1 for failure. + \param rec header to append to WMF in memory + \param wt WMF in memory + \param freerec If true, free rec after append +*/ +int wmf_header_append( + PU_METARECORD rec, + WMFTRACK *wt, + int freerec + ){ + size_t deficit; + unsigned int hsize; + + hsize = (((PU_WMRPLACEABLE) rec)->Key == 0x9AC6CDD7 ? U_SIZE_WMRHEADER + U_SIZE_WMRPLACEABLE: U_SIZE_WMRHEADER); + +#ifdef U_VALGRIND + printf("\nbefore \n"); + printf(" probe %d\n",memprobe(rec, hsize)); + printf("after \n"); +#endif + if(!rec)return(1); + if(!wt)return(2); + if(U_wmr_size(rec) + wt->used > wt->allocated){ + deficit = hsize + wt->used - wt->allocated; + if(deficit < wt->chunk)deficit = wt->chunk; + wt->allocated += deficit; + wt->buf = realloc(wt->buf,wt->allocated); + if(!wt->buf)return(3); + } + memcpy(wt->buf + wt->used, rec, hsize); + wt->used += hsize; + /* do NOT increment records count, this is not strictly a record */ + if(wt->largest < hsize)wt->largest=hsize; + if(freerec){ free(rec); } + return(0); +} + +/** + \brief Create a handle table. Entries filled with 0 are empty, entries >0 hold a handle. + \return 0 for success, >=1 for failure. + \param initsize Initialize with space for this number of handles + \param chunksize When needed increase space by this number of handles + \param wht WMF handle table +*/ +int wmf_htable_create( + uint32_t initsize, + uint32_t chunksize, + WMFHANDLES **wht + ){ + WMFHANDLES *whtl; + + if(initsize<1)return(1); + if(chunksize<1)return(2); + whtl = (WMFHANDLES *) malloc(sizeof(WMFHANDLES)); + if(!whtl)return(3); + whtl->table = malloc(initsize * sizeof(uint32_t)); + if(!whtl->table){ + free(whtl); + return(4); + } + memset(whtl->table , 0, initsize * sizeof(uint32_t)); // zero all slots in the table + whtl->allocated = initsize; + whtl->chunk = chunksize; + whtl->table[0] = 0; // This slot isn't actually ever used + whtl->lolimit = 1; // first available table entry + whtl->hilimit = 0; // no entries in the table yet. + whtl->peak = 0; // no entries in the table ever + *wht = whtl; + return(0); +} + +/** + \brief Delete an entry from the handle table. Move it back onto the stack. The specified slot is filled with a 0. + \return 0 for success, >=1 for failure. + \param ih handle + \param wht WMF handle table + +*/ +int wmf_htable_delete( + uint32_t *ih, + WMFHANDLES *wht + ){ + if(!wht)return(1); + if(!wht->table)return(2); + if(*ih < 1)return(4); // invalid handle + if(!wht->table[*ih])return(5); // requested table position was not in use + wht->table[*ih]=0; // remove handle from table + while(wht->hilimit>0 && !wht->table[wht->hilimit]){ // adjust hilimit + wht->hilimit--; + } + if(*ih < wht->lolimit)wht->lolimit = *ih; // adjust lolimit + *ih=0; // invalidate handle variable, so a second delete will of it is not possible + return(0); +} + +/** + \brief Returns the index of the first free slot. + Call realloc() if needed. The slot is set to handle (indicates occupied) and the peak value is adjusted. + \return 0 for success, >=1 for failure. + \param ih handle + \param wht WMF handle table +*/ +int wmf_htable_insert( + uint32_t *ih, + WMFHANDLES *wht + ){ + size_t newsize; + + if(!wht)return(1); + if(!wht->table)return(2); + if(!ih)return(4); + if(wht->lolimit >= wht->allocated - 1){ // need to reallocate + newsize=wht->allocated + wht->chunk; + wht->table = realloc(wht->table,newsize * sizeof(uint32_t)); + if(!wht->table)return(5); + memset(&wht->table[wht->allocated] , 0, wht->chunk * sizeof(uint32_t)); // zero all NEW slots in the table + wht->allocated = newsize; + } + *ih = wht->lolimit; // handle that is inserted in first available slot + wht->table[*ih] = *ih; // handle goes into preexisting (but zero) slot in table, handle number is the same as the slot number + if(*ih > wht->hilimit){ wht->hilimit = *ih; } + if(*ih > wht->peak){ wht->peak = *ih; } + /* Find the next available slot, it will be at least one higher than the present position, and will have a zero in it. */ + wht->lolimit++; + while(wht->lolimit<= wht->hilimit && wht->table[wht->lolimit]){ wht->lolimit++; } + return(0); +} + +/** + \brief Free all memory in an htable. Sets the pointer to NULL. + \return 0 for success, >=1 for failure. + \param wht WMF handle table +*/ +int wmf_htable_free( + WMFHANDLES **wht + ){ + WMFHANDLES *whtl; + if(!wht)return(1); + whtl = *wht; + if(!whtl)return(2); + if(!whtl->table)return(3); + free(whtl->table); + free(whtl); + *wht=NULL; + return(0); +} + + +/* ********************************************************************************************** +These functions create standard structures used in the WMR records. +*********************************************************************************************** */ + +// hide these from Doxygen +//! @cond +/* ********************************************************************************************** +These functions contain shared code used by various U_WMR*_print functions. These should NEVER be called +by end user code and to further that end prototypes are NOT provided and they are hidden from Doxygen. +*********************************************************************************************** */ + +/* These definitons only used here */ +#define U_SIZE_WMR_NOARGS 6 +#define U_SIZE_WMR_1ARG16 8 +#define U_SIZE_WMR_2ARG16 10 +#define U_SIZE_WMR_3ARG16 12 +#define U_SIZE_WMR_4ARG16 14 +#define U_SIZE_WMR_5ARG16 16 +#define U_SIZE_WMR_6ARG16 18 +#define U_SIZE_WMR_8ARG16 22 + +char *U_WMRCORENONE_set(char *string){ + printf("unimplemented creator for:%s\n",string); + return(NULL); +} + +void U_WMRCORE_SETRECHEAD(char *record, uint32_t irecsize, int iType){ + uint32_t Size16; + Size16 = irecsize/2; + memcpy(record,&Size16,4); /*Size16_4 is at offset 0 in the record */ + ((PU_METARECORD) record)->iType = iType; + ((PU_METARECORD) record)->xb = U_WMR_XB_FROM_TYPE(iType); +} + +/* records that have no arguments */ +char *U_WMRCORE_NOARGS_set( + int iType +){ + char *record=NULL; + uint32_t irecsize; + irecsize = U_SIZE_METARECORD; + record = malloc(irecsize); + if(record)U_WMRCORE_SETRECHEAD(record,irecsize,iType); + return(record); +} + + +/* records like U_WMRFLOODFILL and others. all args are optional, Color is not */ +char *U_WMRCORE_1U16_CRF_2U16_set( + int iType, + uint16_t *arg1, + U_COLORREF Color, + uint16_t *arg2, + uint16_t *arg3 +){ + char *record=NULL; + uint32_t irecsize,off; + irecsize = U_SIZE_METARECORD + U_SIZE_COLORREF; + if(arg1)irecsize+=2; + if(arg2)irecsize+=2; + if(arg3)irecsize+=2; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,iType); + off = U_SIZE_METARECORD; + if(arg1){ memcpy(record + off, arg1, 2); off+=2; } + memcpy(record + off, &Color, 4); off+=4; + if(arg2){ memcpy(record + off, arg2, 2); off+=2; } + if(arg3){ memcpy(record + off, arg3, 2); } + } + return(record); +} + +/* records that have a single uint16_t argument like PU_WMRSETMAPMODE + May also be used with int16_t with appropriate casts */ +char *U_WMRCORE_1U16_set( + int iType, + uint16_t arg1 +){ + char *record=NULL; + uint32_t irecsize,off; + irecsize = U_SIZE_WMR_1ARG16; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,iType); + off = U_SIZE_METARECORD; + memcpy(record+off,&arg1,2); + } + return(record); +} + +/* records that have two uint16_t arguments like U_WMRSETBKMODE + May also be used with int16_t with appropriate casts */ +char *U_WMRCORE_2U16_set( + int iType, + uint16_t arg1, + uint16_t arg2 +){ + char *record=NULL; + uint32_t irecsize,off; + irecsize = U_SIZE_WMR_2ARG16; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,iType); + off = U_SIZE_METARECORD; + memcpy(record+off,&arg1,2); off+=2; + memcpy(record+off,&arg2,2); + } + return(record); +} + +/* records that have four uint16_t arguments like U_WMRSCALEWINDOWEXT + May also be used with int16_t with appropriate casts */ +char *U_WMRCORE_4U16_set( + int iType, + uint16_t arg1, + uint16_t arg2, + uint16_t arg3, + uint16_t arg4 +){ + char *record=NULL; + uint32_t irecsize, off; + irecsize = U_SIZE_WMR_4ARG16; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,iType); + off = U_SIZE_METARECORD; + memcpy(record+off,&arg1,2); off+=2; + memcpy(record+off,&arg2,2); off+=2; + memcpy(record+off,&arg3,2); off+=2; + memcpy(record+off,&arg4,2); + } + return(record); +} + +/* records that have five uint16_t arguments like U_WMRCREATEPENINDIRECT + May also be used with int16_t with appropriate casts */ +char *U_WMRCORE_5U16_set( + int iType, + uint16_t arg1, + uint16_t arg2, + uint16_t arg3, + uint16_t arg4, + uint16_t arg5 +){ + char *record=NULL; + uint32_t irecsize, off; + irecsize = U_SIZE_WMR_5ARG16; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,iType); + off = U_SIZE_METARECORD; + memcpy(record+off,&arg1,2); off+=2; + memcpy(record+off,&arg2,2); off+=2; + memcpy(record+off,&arg3,2); off+=2; + memcpy(record+off,&arg4,2); off+=2; + memcpy(record+off,&arg5,2); + } + return(record); +} + +/* records that have size uint16_t arguments like U_ROUNDREC + May also be used with int16_t with appropriate casts */ +char *U_WMRCORE_6U16_set( + int iType, + uint16_t arg1, + uint16_t arg2, + uint16_t arg3, + uint16_t arg4, + uint16_t arg5, + uint16_t arg6 +){ + char *record=NULL; + uint32_t irecsize, off; + irecsize = U_SIZE_WMR_6ARG16; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,iType); + off = U_SIZE_METARECORD; + memcpy(record+off,&arg1,2); off+=2; + memcpy(record+off,&arg2,2); off+=2; + memcpy(record+off,&arg3,2); off+=2; + memcpy(record+off,&arg4,2); off+=2; + memcpy(record+off,&arg5,2); off+=2; + memcpy(record+off,&arg6,2); + } + return(record); +} + +/* records that have eight uint16_t arguments like U_WMRARC + May also be used with int16_t with appropriate casts */ +char *U_WMRCORE_8U16_set( + int iType, + uint16_t arg1, + uint16_t arg2, + uint16_t arg3, + uint16_t arg4, + uint16_t arg5, + uint16_t arg6, + uint16_t arg7, + uint16_t arg8 +){ + char *record=NULL; + uint32_t irecsize, off; + irecsize = U_SIZE_WMR_8ARG16; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,iType); + off = U_SIZE_METARECORD; + memcpy(record+off,&arg1,2); off+=2; + memcpy(record+off,&arg2,2); off+=2; + memcpy(record+off,&arg3,2); off+=2; + memcpy(record+off,&arg4,2); off+=2; + memcpy(record+off,&arg5,2); off+=2; + memcpy(record+off,&arg6,2); off+=2; + memcpy(record+off,&arg7,2); off+=2; + memcpy(record+off,&arg8,2); + } + return(record); +} + +/* records that have + arg1 an (optional) (u)int16 + arg2 an (optional( (u)int16 + N16 number of (u)int16_t cells in array. may be zero + array of N16 (u)int16_t cells (or any structure that is 2N bytes in size), should be NULL if N16 is 0. + like U_WMRCREATEBRUSHINDIRECT with arg1=arg2=NULL +*/ +char *U_WMRCORE_2U16_N16_set( + int iType, + const uint16_t *arg1, + const uint16_t *arg2, + const uint16_t N16, + const void *array + ){ + char *record=NULL; + uint32_t irecsize, off; + irecsize = U_SIZE_METARECORD + N16*2; + if(arg1)irecsize += 2; + if(arg2)irecsize += 2; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,iType); + off = U_SIZE_METARECORD; + if(arg1){ memcpy(record+off,arg1,2); off+=2; } + if(arg2){ memcpy(record+off,arg2,2); off+=2; } + if(N16){ memcpy(record+off,array,2*N16); } + } + return(record); +} + + + +/* records that set a U_PALETTE , then a count and then a uint16_t list like U_WMRANIMATEPALETTE + May also be used with int16_t with appropriate casts */ +char *U_WMRCORE_PALETTE_set( + int iType, + PU_PALETTE Palette +){ + char *record=NULL; + uint32_t irecsize, off, nPE; + nPE = 4*Palette->NumEntries; + if(!nPE)return(NULL); /* What would it mean to load an empty palette??? */ + irecsize = U_SIZE_METARECORD + 2 + 2 + nPE; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,iType); + off = U_SIZE_METARECORD; + memcpy(record+off, &Palette->Start, 2); off+=2; + memcpy(record+off, &Palette->NumEntries, 2); off+=2; + memcpy(record+off, &Palette->PalEntries, nPE); off+=2; + } + return(record); +} + +//! @endcond + +/* ********************************************************************************************** +These functions are simpler or more convenient ways to generate the specified types of WMR records. +Each should be called in preference to the underlying "base" WMR function. +*********************************************************************************************** */ + + +/** + \brief Allocate and construct a U_WMRDELETEOBJECT record and also delete the requested object from the table. + Use this function instead of calling U_WMRDELETEOBJECT_set() directly. + Object Pointer in WMF (caller) is 0->N, but in htable it is 1->N+1, make that correction here. + \return pointer to the U_WMRDELETEOBJECT record, or NULL on error. + \param ihObject Pointer to handle to delete. This value is set to 0xFFFFFFFF if the function succeeds. + \param wht WMF handle table + + Note that calling this function should always be conditional on the specifed object being defined. It is easy to + write a program where deleteobject_set() is called in a sequence where, at the time, we know that ihObject is defined. + Then a later modification, possibly quite far away in the code, causes it to be undefined. That distant change will + result in a failure when this function reutrns. That problem cannot be handled here because the only values which + may be returned are a valid U_WMRDELETEOBJECT record or a NULL, and other errors could result in the NULL. + So the object must be checked before the call. +*/ +char *wdeleteobject_set( + uint32_t *ihObject, + WMFHANDLES *wht + ){ + uint32_t saveObject=*ihObject; /* caller 0->N */ + *ihObject += 1; /* caller 0->N --> 1->N+1 table*/ + if(wmf_htable_delete(ihObject,wht))return(NULL); /* invalid handle or other problem, cannot be deleted */ + *ihObject = 0xFFFFFFFF; /* EMF would have set to 0, but 0 is an allowed index in WMF */ + return(U_WMRDELETEOBJECT_set(saveObject)); /* caller 0->N */ +} + +/** + \brief Allocate and construct a U_WMRSELECTOBJECT record, checks that the handle specified is one that can actually be selected. + Use this function instead of calling U_WMRSELECTOBJECT_set() directly. + Object Pointer in WMF (caller) is 0->N, so is record, so no correction to 1->N+1 needed here. + \return pointer to the U_WMRSELECTOBJECT record, or NULL on error. + \param ihObject handle to select + \param wht WMF handle table +*/ +char *wselectobject_set( + uint32_t ihObject, + WMFHANDLES *wht + ){ + /* WMF has no stock objects! */ + if(ihObject > wht->hilimit)return(NULL); // handle this high is not in the table + /* caller uses 0->N, table uses 1->N+1 */ + if(!wht->table[ihObject+1])return(NULL); // handle is not in the table, so cannot be selected + /* file uses 0->N */ + return(U_WMRSELECTOBJECT_set(ihObject)); +} + +/** + \brief Allocate and construct a U_WMRCREATEPENINDIRECT record, create a handle and returns it + Use this function instead of calling U_WMRCREATEPENINDIRECT_set() directly. + Object Pointer in WMF (caller) is 0->N, but in htable it is 1->N+1, make that correction here. + \return pointer to the U_WMRCREATEPENINDIRECT record, or NULL on error. + \param ihPen handle to be used by new object + \param wht WMF handle table + \param pen Pen parameters (U_PEN) +*/ +char *wcreatepenindirect_set( + uint32_t *ihPen, + WMFHANDLES *wht, + U_PEN pen + ){ + if(wmf_htable_insert(ihPen, wht))return(NULL); + *ihPen -= 1; /* 1->N+1 --> 0->N */ + return(U_WMRCREATEPENINDIRECT_set(pen)); +} + +/** + \brief Allocate and construct a U_WMRCREATEBRUSHINDIRECT record, create a handle and returns it + Use this function instead of calling U_WMRCREATEBRUSHINDIRECT_set() directly. + Object Pointer in WMF (caller) is 0->N, but in htable it is 1->N+1, make that correction here. + \return pointer to the U_WMRCREATEBRUSHINDIRECT record, or NULL on error. + \param ihBrush handle to be used by new object + \param wht WMF handle table + \param lb Brush parameters +*/ +char *wcreatebrushindirect_set( + uint32_t *ihBrush, + WMFHANDLES *wht, + U_WLOGBRUSH lb + ){ + if(wmf_htable_insert(ihBrush, wht))return(NULL); + *ihBrush -= 1; /* 1->N+1 --> 0->N */ + return(U_WMRCREATEBRUSHINDIRECT_set(lb)); +} + +/** + \brief Allocate and construct a U_WMRDIBCREATEPATTERNBRUSH record from a DIB. + Use this function instead of calling U_WMRDIBCREATEPATTERNBRUSH_set() directly. + \return pointer to the U_WMRDIBCREATEPATTERNBRUSH record, or NULL on error. + \param ihBrush handle to be used by new object + \param wht WMF handle table + \param iUsage DIBColors enumeration + \param Bmi Bitmap info + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *wcreatedibpatternbrush_srcdib_set( + uint32_t *ihBrush, + WMFHANDLES *wht, + const uint32_t iUsage, + PU_BITMAPINFO Bmi, + const uint32_t cbPx, + const char *Px + + ){ + if(wmf_htable_insert(ihBrush, wht))return(NULL); + *ihBrush -= 1; /* 1->N+1 --> 0->N */ + return(U_WMRDIBCREATEPATTERNBRUSH_set(U_BS_DIBPATTERNPT, iUsage, Bmi, cbPx, Px,NULL)); +} + +/** + \brief Allocate and construct a U_WMRCREATEPATTERNBRUSH record from a U_BITMAP16 object. + Use this function instead of calling U_WMRCREATEPATTERNBRUSH_set() directly. + \return pointer to the U_WMRCREATEPATTERNBRUSH record, or NULL on error. + \param ihBrush handle to be used by new object + \param wht WMF handle table + \param iUsage DIBColors enumeration + \param Bm16 Pointer to a Bitmap16 object +*/ +char *wcreatedibpatternbrush_srcbm16_set( + uint32_t *ihBrush, + WMFHANDLES *wht, + const uint32_t iUsage, + PU_BITMAP16 Bm16 + ){ + if(wmf_htable_insert(ihBrush, wht))return(NULL); + *ihBrush -= 1; /* 1->N+1 --> 0->N */ + return(U_WMRDIBCREATEPATTERNBRUSH_set(U_BS_PATTERN, iUsage, NULL, 0, NULL, Bm16)); +} + +/** + \brief Allocate and construct a U_WMRCREATEPATTERNBRUSH record, create a handle and returns it + Use this function instead of calling U_WMRCREATEPATTERNBRUSH_set() directly. + Warning - application support for U_WMRCREATEPATTERNBRUSH is spotty, better to use U_WMRDIBCREATEPATTERNBRUSH. + \return pointer to the U_WMRCREATEPATTERNBRUSH record, or NULL on error. + \param ihBrush handle to be used by new object + \param wht WMF handle table + \param Bm16 Pointer to a Bitmap16 structure (only first 10 bytes are used). + \param Pattern Pointer to a byte array described by Bm16. (Pattern may be a pointer to the BM16 Bits field.) +*/ +char *wcreatepatternbrush_set( + uint32_t *ihBrush, + WMFHANDLES *wht, + PU_BITMAP16 Bm16, + char *Pattern + ){ + if(wmf_htable_insert(ihBrush, wht))return(NULL); + *ihBrush -= 1; /* 1->N+1 --> 0->N */ + return(U_WMRCREATEPATTERNBRUSH_set(Bm16, Pattern)); +} + +/** + \brief Allocate and construct a U_WMRCREATEFONTINDIRECT record, create a handle and returns it + Use this function instead of calling U_WMRCREATEFONTINDIRECT_set() directly. + Object Pointer in WMF (caller) is 0->N, but in htable it is 1->N+1, make that correction here. + \return pointer to the U_WMRCREATEFONTINDIRECT record, or NULL on error. + \param ihFont Font handle, will be created and returned + \param wht Pointer to structure holding all WMF handles + \param uf Pointer to Font parameters as PU_FONT +*/ +char *wcreatefontindirect_set( + uint32_t *ihFont, + WMFHANDLES *wht, + PU_FONT uf + ){ + if(wmf_htable_insert(ihFont, wht))return(NULL); + *ihFont -= 1; /* 1->N+1 --> 0->N */ + return(U_WMRCREATEFONTINDIRECT_set(uf)); +} + +/** + \brief Allocate and construct a U_WMRCREATEPALETTE record, create a handle and returns it + Use this function instead of calling U_WMRCREATEPALETTE_set() directly. + Object Pointer in WMF (caller) is 0->N, but in htable it is 1->N+1, make that correction here. + \return pointer to the U_WMRCREATEPALETTE record, or NULL on error. + \param ihPal Palette handle, will be created and returned + \param wht Pointer to structure holding all WMF handles + \param up Palette parameters +*/ +char *wcreatepalette_set( + uint32_t *ihPal, + WMFHANDLES *wht, + PU_PALETTE up + ){ + if(wmf_htable_insert(ihPal, wht))return(NULL); + *ihPal -= 1; /* 1->N+1 --> 0->N */ + return(U_WMRCREATEPALETTE_set(up)); +} + +/** + \brief Allocate and construct a U_WMRSETPALENTRIES record, create a handle and returns it + Use this function instead of calling U_WMRSETPALENTRIES_set() directly. + Object Pointer in WMF (caller) is 0->N, but in htable it is 1->N+1, make that correction here. + \return pointer to the U_WMRSETPALENTRIES record, or NULL on error. + \param ihPal Palette handle, will be created and returned + \param wht Pointer to structure holding all WMF handles + \param Palettes Values to set with +*/ +char *wsetpaletteentries_set( + uint32_t *ihPal, + WMFHANDLES *wht, + const PU_PALETTE Palettes + ){ + if(wmf_htable_insert(ihPal, wht))return(NULL); + *ihPal -= 1; /* 1->N+1 --> 0->N */ + return(U_WMRSETPALENTRIES_set(Palettes)); +} + +/** + \brief Allocate and construct a U_WMRCREATEREGION record, create a handle and returns it + Use this function instead of calling U_WMRCREATEREGION() directly. + Object Pointer in WMF (caller) is 0->N, but in htable it is 1->N+1, make that correction here. + \return pointer to the U_REGIONS record, or NULL on error. + \param ihReg Region handle, will be created and returned + \param wht Pointer to structure holding all WMF handles + \param Region Values to set with +*/ +char *wcreateregion_set( + uint32_t *ihReg, + WMFHANDLES *wht, + const PU_REGION Region + ){ + if(wmf_htable_insert(ihReg, wht))return(NULL); + *ihReg -= 1; /* 1->N+1 --> 0->N */ + return(U_WMRCREATEREGION_set(Region)); +} +/* A few escape functions are implemented, just those that set a state or a single value */ + +/** + \brief Allocate and construct the specified U_WMRESCAPE structure, create a handle and returns it + Use this function instead of calling U_WMRESCAPE_set() directly. + \return pointer to the U_WMRESCAPE structure, or NULL on error. +*/ +char *wbegin_path_set(void){ + return(U_WMRESCAPE_set(U_MFE_BEGIN_PATH,0,NULL)); +} + +/** + \brief Allocate and construct the specified U_WMRESCAPE structure, create a handle and returns it + Use this function instead of calling U_WMRESCAPE_set() directly. + \return pointer to the U_WMRESCAPE structure, or NULL on error. +*/ +char *wend_path_set(void){ + return(U_WMRESCAPE_set(U_MFE_END_PATH,0,NULL)); +} + +/** + \brief Allocate and construct the specified U_WMRESCAPE structure, create a handle and returns it + Use this function instead of calling U_WMRESCAPE_set() directly. + \return pointer to the U_WMRESCAPE structure, or NULL on error. + \param Type PostScriptCap Enumeration, anything else is an error +*/ +char *wlinecap_set( + int32_t Type + ){ + char *record =NULL; + if(Type == U_WPS_CAP_NOTSET || + Type == U_WPS_CAP_FLAT || + Type == U_WPS_CAP_ROUND || + Type == U_WPS_CAP_SQUARE){ record = U_WMRESCAPE_set(U_MFE_SETLINECAP,4,&Type); } + return(record); +} + +/** + \brief Allocate and construct the specified U_WMRESCAPE structure, create a handle and returns it + Use this function instead of calling U_WMRESCAPE_set() directly. + \return pointer to the U_WMRESCAPE structure, or NULL on error. + \param Type PostScriptCap Enumeration, anything else is an error +*/ +char *wlinejoin_set( + int32_t Type + ){ + char *record =NULL; + if(Type == U_WPS_JOIN_NOTSET || + Type == U_WPS_JOIN_MITER || + Type == U_WPS_JOIN_ROUND || + Type == U_WPS_JOIN_BEVEL){ record = U_WMRESCAPE_set(U_MFE_SETLINEJOIN,4,&Type); } + return(record); +} + +/** + \brief Allocate and construct the specified U_WMRESCAPE structure, create a handle and returns it + Use this function instead of calling U_WMRESCAPE_set() directly. + \return pointer to the U_WMRESCAPE structure, or NULL on error. + \param limit PostScriptCap Enumeration, anything else is an error +*/ +char *wmiterlimit_set( + int32_t limit + ){ + return(U_WMRESCAPE_set(U_MFE_SETMITERLIMIT,4,&limit)); +} + +/* ********************************************************************************************** +These are the core WMR functions, each creates a particular type of record. +All return these records via a char* pointer, which is NULL if the call failed. +They are listed in order by the corresponding U_WMR_* index number. +*********************************************************************************************** */ + +/** + \brief Set up fields for a (placeable) WMR_HEADER. Most of the fields are blank and are not set until all is written. + Typically values are something like (8.5,11.0), 1440 (Letter paper, 1440 DPI). + The scaled paper size must fit in the range 0<->32767 inclusive, because it must be represented by a signed 16bit number. + If the size + dpi result in out of range values a failure will result. + \return pointer to the WMF header record, or NULL on failure + \param size Pointer to page size (if NULL, not a placeable header) in inches. Values must be positive and scaled + \param dpi Logical units/inch. If 0 defaults to 1440. +*/ +char *U_WMRHEADER_set( + PU_PAIRF size, + unsigned int dpi + ){ + char *record=NULL; + uint32_t irecsize,off; + double xmax,ymax; + int16_t xm16,ym16; + irecsize = (size ? U_SIZE_WMRHEADER + U_SIZE_WMRPLACEABLE : U_SIZE_WMRHEADER); + record = calloc(1,irecsize); /* most will be zero*/ + off = 0; + if(record){ + if(size){ /* placeable */ + if(!dpi)dpi=1440; + xmax = U_ROUND((double) size->x * (double) dpi); + ymax = U_ROUND((double) size->y * (double) dpi); + if(xmax < 0 || ymax < 0 || xmax > 32767 || ymax > 32767){ + free(record); + return(NULL); + } + xm16 = xmax; + ym16 = ymax; + ((PU_WMRPLACEABLE) record)->Key = 0x9AC6CDD7; + ((PU_WMRPLACEABLE) record)->HWmf = 0; /* Manual says number of 16 bit words in record, but all WMF examples had it as 0 */ + ((PU_WMRPLACEABLE) record)->Dst.left = 0; + ((PU_WMRPLACEABLE) record)->Dst.top = 0; + ((PU_WMRPLACEABLE) record)->Dst.right = xm16; + ((PU_WMRPLACEABLE) record)->Dst.bottom = ym16; + ((PU_WMRPLACEABLE) record)->Inch = dpi; + ((PU_WMRPLACEABLE) record)->Reserved = 0; + ((PU_WMRPLACEABLE) record)->Checksum = U_16_checksum((int16_t *)record,10); + off = U_SIZE_WMRPLACEABLE; + } + ((PU_WMRHEADER) (record + off))->iType = 1; + ((PU_WMRHEADER) (record + off))->version = U_METAVERSION300; + ((PU_WMRHEADER) (record + off))->Size16w = U_SIZE_WMRHEADER/2; + } + return(record); +} + + +/** + \brief Allocate and construct a U_WMREOF record + \return pointer to the U_WMREOF record, or NULL on error. +*/ +char *U_WMREOF_set(void){ + return U_WMRCORE_NOARGS_set(U_WMR_EOF); +} + +/** + \brief Create and return a U_WMRSETBKCOLOR record + \return pointer to the U_WMRSETBKCOLOR record, or NULL on error + \param Color Background Color. +*/ +char *U_WMRSETBKCOLOR_set(U_COLORREF Color){ + return U_WMRCORE_1U16_CRF_2U16_set(U_WMR_SETBKCOLOR,NULL,Color,NULL,NULL); +} + +/** + \brief Create and return a U_WMRSETBKMODE record + \return pointer to the U_WMRSETBKMODE record, or NULL on error + \param Mode MixMode Enumeration +*/ +char *U_WMRSETBKMODE_set(uint16_t Mode){ + return U_WMRCORE_2U16_set(U_WMR_SETBKMODE, Mode, 0); +} + +/** + \brief Create and return a U_WMRSETMAPMODE record + \return pointer to the U_WMRSETMAPMODE record, or NULL on error + \param Mode MapMode Enumeration +*/ +char *U_WMRSETMAPMODE_set(uint16_t Mode){ + return U_WMRCORE_1U16_set(U_WMR_SETMAPMODE, Mode); +} + +/** + \brief Create and return a U_WMRSETROP2 record + \return pointer to the U_WMRSETROP2 record, or NULL on error + \param Mode Binary Raster Operation Enumeration +*/ +char *U_WMRSETROP2_set(uint16_t Mode){ + return U_WMRCORE_2U16_set(U_WMR_SETROP2, Mode, 0); +} + +/** + \brief Allocate and construct a U_WMRSETRELABS record + \return pointer to the U_WMRSETRELABS record, or NULL on error. +*/ +char *U_WMRSETRELABS_set(void){ + return U_WMRCORE_NOARGS_set(U_WMR_SETRELABS); +} + +/** + \brief Create and return a U_WMRSETPOLYFILLMODE record + \return pointer to the U_WMRSETPOLYFILLMODE record, or NULL on error + \param Mode PolyFillMode Enumeration +*/ +char *U_WMRSETPOLYFILLMODE_set(uint16_t Mode){ + return U_WMRCORE_2U16_set(U_WMR_SETPOLYFILLMODE, Mode, 0); +} + +/** + \brief Create and return a U_WMRSETSTRETCHBLTMODE record + \return pointer to the U_WMRSETSTRETCHBLTMODE record, or NULL on error + \param Mode StretchMode Enumeration +*/ +char *U_WMRSETSTRETCHBLTMODE_set(uint16_t Mode){ + return U_WMRCORE_2U16_set(U_WMR_SETSTRETCHBLTMODE, Mode, 0); +} + +/** + \brief Create and return a U_WMRSETTEXTCHAREXTRA record + \return pointer to the U_WMRSETTEXTCHAREXTRA record, or NULL on error + \param Mode Extra space in logical units to add to each character +*/ +char *U_WMRSETTEXTCHAREXTRA_set(uint16_t Mode){ + return U_WMRCORE_1U16_set(U_WMR_SETTEXTCHAREXTRA, Mode); +} + +/** + \brief Create and return a U_WMRSETTEXTCOLOR record + \return pointer to the U_WMRSETTEXTCOLOR record, or NULL on error + \param Color Text Color. +*/ +char *U_WMRSETTEXTCOLOR_set(U_COLORREF Color){ + return U_WMRCORE_1U16_CRF_2U16_set(U_WMR_SETTEXTCOLOR,NULL,Color,NULL,NULL); +} + +/** + \brief Create and return a U_WMRSETTEXTJUSTIFICATION record + \return pointer to the U_WMRSETTEXTJUSTIFICATION record, or NULL on error + \param Count Number of space characters in the line. + \param Extra Number of extra space characters to add to the line. +*/ +char *U_WMRSETTEXTJUSTIFICATION_set(uint16_t Count, uint16_t Extra){ + return U_WMRCORE_2U16_set(U_WMR_SETBKMODE, Count, Extra); +} + +/** + \brief Create and return a U_WMRSETWINDOWORG record + \return pointer to the U_WMRSETWINDOWORG record, or NULL on error + \param coord Window Origin. +*/ +char *U_WMRSETWINDOWORG_set(U_POINT16 coord){ + return U_WMRCORE_2U16_set(U_WMR_SETWINDOWORG, U_U16(coord.y), U_U16(coord.x)); +} + +/** + \brief Create and return a U_WMRSETWINDOWEXT record + \return pointer to the U_WMRSETWINDOWEXT record, or NULL on error + \param extent Window Extent. +*/ +char *U_WMRSETWINDOWEXT_set(U_POINT16 extent){ + return U_WMRCORE_2U16_set(U_WMR_SETWINDOWEXT, U_U16(extent.y), U_U16(extent.x)); +} + +/** + \brief Create and return a U_WMRSETVIEWPORTORG record + \return pointer to the U_WMRSETVIEWPORTORG record, or NULL on error + \param coord Viewport Origin. +*/ +char *U_WMRSETVIEWPORTORG_set(U_POINT16 coord){ + return U_WMRCORE_2U16_set(U_WMR_SETVIEWPORTORG, U_U16(coord.y), U_U16(coord.x)); +} + +/** + \brief Create and return a U_WMRSETVIEWPORTEXT record + \return pointer to the U_WMRSETVIEWPORTEXT record, or NULL on error + \param extent Viewport Extent. +*/ +char *U_WMRSETVIEWPORTEXT_set(U_POINT16 extent){ + return U_WMRCORE_2U16_set(U_WMR_SETWINDOWEXT, U_U16(extent.y), U_U16(extent.x)); +} + +/** + \brief Create and return a U_WMROFFSETWINDOWORG record + \return pointer to the U_WMROFFSETWINDOWORG record, or NULL on error + \param offset Window offset in device units. +*/ +char *U_WMROFFSETWINDOWORG_set(U_POINT16 offset){ + return U_WMRCORE_2U16_set(U_WMR_OFFSETWINDOWORG, U_U16(offset.y), U_U16(offset.x)); +} + +/** + \brief Create and return a U_WMRSCALEWINDOWEXT record + \return pointer to the U_WMRSCALEWINDOWEXT record, or NULL on error + \param Denom {X,Y} denominators. + \param Num {X,Y} numerators. +*/ +char *U_WMRSCALEWINDOWEXT_set(U_POINT16 Denom, U_POINT16 Num){ + return U_WMRCORE_4U16_set(U_WMR_SCALEWINDOWEXT, U_U16(Denom.y), U_U16(Num.y), U_U16(Denom.x), U_U16(Num.x)); +} + +/** + \brief Create and return a U_WMROFFSETVIEWPORTORG record + \return pointer to the U_WMROFFSETVIEWPORTORG record, or NULL on error + \param offset Viewport offset in device units. +*/ +char *U_WMROFFSETVIEWPORTORG_set(U_POINT16 offset){ + return U_WMRCORE_2U16_set(U_WMR_OFFSETVIEWPORTORG, U_U16(offset.y), U_U16(offset.x)); +} + +/** + \brief Create and return a U_WMRSCALEVIEWPORTEXT record + \return pointer to the U_WMRSCALEVIEWPORTEXT record, or NULL on error + \param Denom {X,Y} denominators. + \param Num {X,Y} numerators. +*/ +char *U_WMRSCALEVIEWPORTEXT_set(U_POINT16 Denom, U_POINT16 Num){ + return U_WMRCORE_4U16_set(U_WMR_SCALEVIEWPORTEXT, U_U16(Denom.y), U_U16(Num.y), U_U16(Denom.x), U_U16(Num.x)); +} + +/** + \brief Create and return a U_WMRLINETO record + \return pointer to the U_WMRLINETO record, or NULL on error + \param coord Draw line to {X,Y}. +*/ +char *U_WMRLINETO_set(U_POINT16 coord){ + return U_WMRCORE_2U16_set(U_WMR_LINETO, U_U16(coord.y), U_U16(coord.x)); +} + +/** + \brief Create and return a U_WMRMOVETO record + \return pointer to the U_WMRMOVETO record, or NULL on error + \param coord Move to {X,Y}. +*/ +char *U_WMRMOVETO_set(U_POINT16 coord){ + return U_WMRCORE_2U16_set(U_WMR_MOVETO, U_U16(coord.y), U_U16(coord.x)); +} + +/** + \brief Create and return a U_WMREXCLUDECLIPRECT record + \return pointer to the U_WMREXCLUDECLIPRECT record, or NULL on error + \param rect Exclude rect from clipping region. +*/ +char *U_WMREXCLUDECLIPRECT_set(U_RECT16 rect){ + return U_WMRCORE_4U16_set( + U_WMR_EXCLUDECLIPRECT, + U_U16(rect.bottom), + U_U16(rect.right), + U_U16(rect.top), + U_U16(rect.left) + ); +} + +/** + \brief Create and return a U_WMRINTERSECTCLIPRECT record + \return pointer to the U_WMRINTERSECTCLIPRECT record, or NULL on error + \param rect Clipping region is intersection of existing clipping region with rect. +*/ +char *U_WMRINTERSECTCLIPRECT_set(U_RECT16 rect){ + return U_WMRCORE_4U16_set( + U_WMR_INTERSECTCLIPRECT, + U_U16(rect.bottom), + U_U16(rect.right), + U_U16(rect.top), + U_U16(rect.left) + ); +} + +/** + \brief Create and return a U_WMRARC record + \return pointer to the U_WMRARC record, or NULL on error + \param StartArc Start of Arc + \param EndArc End of Arc + \param rect Bounding rectangle. +*/ +char *U_WMRARC_set(U_POINT16 StartArc, U_POINT16 EndArc, U_RECT16 rect){ + return U_WMRCORE_8U16_set( + U_WMR_ARC, + U_U16(EndArc.y), + U_U16(EndArc.x), + U_U16(StartArc.y), + U_U16(StartArc.x), + U_U16(rect.bottom), + U_U16(rect.right), + U_U16(rect.top), + U_U16(rect.left) + ); +} + +/** + \brief Create and return a U_WMRELLIPSE record + \return pointer to the U_WMRELLIPSE record, or NULL on error + \param rect Bounding rectangle for Ellipse. +*/ +char *U_WMRELLIPSE_set(U_RECT16 rect){ + return U_WMRCORE_4U16_set( + U_WMR_ELLIPSE, + U_U16(rect.bottom), + U_U16(rect.right), + U_U16(rect.top), + U_U16(rect.left) + ); +} + +/** + \brief Create and return a U_WMRFLOODFILL record + \return pointer to the U_WMRFLOODFILL record, or NULL on error + \param Mode FloodFill Enumeration. + \param Color Color to Fill with. + \param coord Location to start fill. +*/ +char *U_WMRFLOODFILL_set(uint16_t Mode, U_COLORREF Color, U_POINT16 coord){ + return U_WMRCORE_1U16_CRF_2U16_set( + U_WMR_FLOODFILL, + &Mode, + Color, + U_P16(coord.y), + U_P16(coord.x) + ); +} + +/** + \brief Create and return a U_WMRPIE record + \return pointer to the U_WMRPIE record, or NULL on error + \param Radial1 Start of Pie + \param Radial2 End of Pie + \param rect Bounding rectangle. +*/ +char *U_WMRPIE_set(U_POINT16 Radial1, U_POINT16 Radial2, U_RECT16 rect){ + return U_WMRCORE_8U16_set( + U_WMR_PIE, + U_U16(Radial2.y), + U_U16(Radial2.x), + U_U16(Radial1.y), + U_U16(Radial1.x), + U_U16(rect.bottom), + U_U16(rect.right), + U_U16(rect.top), + U_U16(rect.left) + ); +} + +/** + \brief Create and return a U_WMRRECTANGLE record + \return pointer to the U_WMRRECTANGLE record, or NULL on error + \param rect Boundaries. +*/ +char *U_WMRRECTANGLE_set(U_RECT16 rect){ + return U_WMRCORE_4U16_set( + U_WMR_RECTANGLE, + U_U16(rect.bottom), + U_U16(rect.right), + U_U16(rect.top), + U_U16(rect.left) + ); +} + +/** + \brief Create and return a U_WMRROUNDRECT record + \return pointer to the U_WMRROUNDRECT record, or NULL on error + \param Width Horizontal rounding length. + \param Height Vertical rounding length. + \param rect Boundaries. +*/ +char *U_WMRROUNDRECT_set(int16_t Width, int16_t Height, U_RECT16 rect){ + return U_WMRCORE_6U16_set( + U_WMR_ROUNDRECT, + U_U16(Height), + U_U16(Width), + U_U16(rect.bottom), + U_U16(rect.right), + U_U16(rect.top), + U_U16(rect.left) + ); +} + +/** + \brief Allocate and construct a U_WMRPATBLT record. + \return pointer to the U_WMRPATBLT record, or NULL on error. + \param Dst Destination UL corner in logical units + \param cwh W & H for Dst and Src in logical units + \param dwRop3 RasterOPeration Enumeration +*/ +char *U_WMRPATBLT_set( + U_POINT16 Dst, + U_POINT16 cwh, + uint32_t dwRop3 + ){ + char *record=NULL; + uint32_t irecsize; + PU_WMRPATBLT pmr; + + irecsize = U_SIZE_WMRPATBLT; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_PATBLT); + pmr = (PU_WMRPATBLT) record; + memcpy(pmr->rop3w, &dwRop3, 4); + pmr->Height = cwh.y; + pmr->Width = cwh.x; + pmr->yDst = Dst.y; + pmr->xDst = Dst.x; + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRSAVEDC record + \return pointer to the U_WMRSAVEDC record, or NULL on error. +*/ +char *U_WMRSAVEDC_set(void){ + return U_WMRCORE_NOARGS_set(U_WMR_SAVEDC); +} + +char *U_WMRSETPIXEL_set(U_COLORREF Color, U_POINT16 coord){ + return U_WMRCORE_1U16_CRF_2U16_set( + U_WMR_SETPIXEL, + NULL, + Color, + U_P16(coord.y), + U_P16(coord.x) + ); +} + +char *U_WMROFFSETCLIPRGN_set(U_POINT16 offset){ + return U_WMRCORE_2U16_set(U_WMR_OFFSETCLIPRGN, U_U16(offset.y), U_U16(offset.x)); +} + +/** + \brief Allocate and construct a U_WMRTEXTOUT record. + \return pointer to the U_WMRTEXTOUT record, or NULL on error. + \param Dst Destinationin logical units + \param string Null terminated string to write. The terminator is NOT placed in the record! +*/ +char *U_WMRTEXTOUT_set(U_POINT16 Dst, char *string){ + char *record=NULL; + uint32_t irecsize,off; + int L2; + int16_t Length; + irecsize = 2 + U_SIZE_METARECORD + 4; /* core + length + Dst */ + Length = strlen(string); + L2 = ( Length & 1 ? Length + 1 : Length); + irecsize += L2; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_TEXTOUT); + off = U_SIZE_METARECORD; + memcpy(record+off,&Length,2); off+=2; + memcpy(record+off,string,Length); off+=Length; + if(Length!=L2){ + memset(record+off,0,1); off+=1; + } + memcpy(record+off,&Dst.y,2); off+=2; + memcpy(record+off,&Dst.x,2); off+=2; + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRBITBLT record. + Note that unlike U_EMRBITBLT there is no scaling available - the Src and Dst + rectangles must be the same size. + \return pointer to the U_WMRBITBLT record, or NULL on error. + \param Dst Destination UL corner in logical units + \param cwh W & H for Dst and Src in logical units + \param Src Source UL corner in logical units + \param dwRop3 RasterOPeration Enumeration + \param Bm16 (Optional) bitmap16 object +*/ +char *U_WMRBITBLT_set( + U_POINT16 Dst, + U_POINT16 cwh, + U_POINT16 Src, + uint32_t dwRop3, + const PU_BITMAP16 Bm16 + ){ + char *record=NULL; + uint32_t irecsize; + int cbBm16,cbBm164,off; + PU_WMRBITBLT_PX pmr_px; + PU_WMRBITBLT_NOPX pmr_nopx; + + if(Bm16){ + cbBm16 = U_SIZE_BITMAP16 + (((Bm16->Width * Bm16->BitsPixel + 15) >> 4) << 1) * Bm16->Height; + cbBm164 = UP4(cbBm16); + irecsize = U_SIZE_WMRBITBLT_PX + cbBm164; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_BITBLT); + pmr_px = (PU_WMRBITBLT_PX) record; + memcpy(pmr_px->rop3w, &dwRop3, 4); + pmr_px->ySrc = Src.y; + pmr_px->xSrc = Src.x; + pmr_px->Height = cwh.y; + pmr_px->Width = cwh.x; + pmr_px->yDst = Dst.y; + pmr_px->xDst = Dst.x; + off = U_SIZE_WMRBITBLT_PX; + memcpy(record + off, Bm16, cbBm16); off += cbBm16; + if(cbBm164 - cbBm16)memset(record+off,0,cbBm164 - cbBm16); + } + } + else { + irecsize = U_SIZE_WMRBITBLT_NOPX; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_BITBLT); + pmr_nopx = (PU_WMRBITBLT_NOPX) record; + memcpy(pmr_nopx->rop3w, &dwRop3, 4); + pmr_nopx->ySrc = Src.y; + pmr_nopx->xSrc = Src.x; + pmr_nopx->Height = cwh.y; + pmr_nopx->Width = cwh.x; + pmr_nopx->ignore = 0; + pmr_nopx->yDst = Dst.y; + pmr_nopx->xDst = Dst.x; + } + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRSTRETCHBLT record. + \return pointer to the U_WMRSTRETCHBLT record, or NULL on error. + \param Dst Destination UL corner in logical units + \param cDst Destination W & H in logical units + \param Src Source UL corner in logical units + \param cSrc Source W & H in logical units + \param dwRop3 RasterOPeration Enumeration + \param Bm16 (Optional) bitmap16 object +*/ +char *U_WMRSTRETCHBLT_set( + U_POINT16 Dst, + U_POINT16 cDst, + U_POINT16 Src, + U_POINT16 cSrc, + uint32_t dwRop3, + const PU_BITMAP16 Bm16 + ){ + char *record=NULL; + uint32_t irecsize; + int cbBm16,cbBm164,off; + PU_WMRSTRETCHBLT_PX pmr_px; + PU_WMRSTRETCHBLT_NOPX pmr_nopx; + + if(Bm16){ + cbBm16 = U_SIZE_BITMAP16 + (((Bm16->Width * Bm16->BitsPixel + 15) >> 4) << 1) * Bm16->Height; + cbBm164 = UP4(cbBm16); + irecsize = U_SIZE_WMRSTRETCHBLT_PX + cbBm164; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_STRETCHBLT); + pmr_px = (PU_WMRSTRETCHBLT_PX) record; + memcpy(pmr_px->rop3w, &dwRop3, 4); + pmr_px->hSrc = cSrc.y; + pmr_px->wSrc = cSrc.x; + pmr_px->ySrc = Src.y; + pmr_px->xSrc = Src.x; + pmr_px->hDst = cDst.y; + pmr_px->wDst = cDst.x; + pmr_px->yDst = Dst.y; + pmr_px->xDst = Dst.x; + off = U_SIZE_WMRSTRETCHBLT_PX; + memcpy(record + off, Bm16, cbBm16); off += cbBm16; + if(cbBm164 - cbBm16)memset(record+off,0,cbBm164 - cbBm16); + } + } + else { + irecsize = U_SIZE_WMRSTRETCHBLT_NOPX; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_STRETCHBLT); + pmr_nopx = (PU_WMRSTRETCHBLT_NOPX) record; + memcpy(pmr_nopx->rop3w, &dwRop3, 4); + pmr_nopx->hSrc = cSrc.y; + pmr_nopx->wSrc = cSrc.x; + pmr_nopx->ySrc = Src.y; + pmr_nopx->xSrc = Src.x; + pmr_nopx->ignore = 0; + pmr_nopx->hDst = cDst.y; + pmr_nopx->wDst = cDst.x; + pmr_nopx->yDst = Dst.y; + pmr_nopx->xDst = Dst.x; + } + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRPOLYGON record. + \return pointer to the U_WMRPOLYGON record, or NULL on error. + \param Length Number of points in the Polygon + \param Data Array of Length points +*/ +char *U_WMRPOLYGON_set(uint16_t Length, const PU_POINT16 Data){ + return U_WMRCORE_2U16_N16_set(U_WMR_POLYGON, NULL, &Length, 2*Length, Data); +} + +/** + \brief Allocate and construct a U_WMRPOLYLINE record. + \return pointer to the U_WMRPOLYLINE record, or NULL on error. + \param Length Number of points in the Polyline + \param Data Array of Length points +*/ +char *U_WMRPOLYLINE_set(uint16_t Length, const PU_POINT16 Data){ + return U_WMRCORE_2U16_N16_set(U_WMR_POLYLINE, NULL, &Length, 2*Length, Data); +} + +/** + \brief Allocate and construct a U_WMRESCAPE record. + WARNING! Only three Escape record types are fully supported: SETLINECAP, SETLINEJOIN, SETMITERLIMIT. + Even these should not be set here directly, instead use the wsetlinecap_set(), wsetlinejoin_set(), + or wsetmiterlimit_set() functions. + Escape records created with this function, with the exception of the three named above, will not have + the byte orders in Data adjusted automatically. The user code must set Data to be little endian no + matter what the endianness of the current platform where the user code is running. + \return pointer to the U_WMRESCAPE record, or NULL on error. + \param Escape Escape function + \param Length Bytes in the Data + \param Data Array of Length bytes +*/ +char *U_WMRESCAPE_set(uint16_t Escape, uint16_t Length, const void *Data){ + return U_WMRCORE_2U16_N16_set(U_WMR_ESCAPE, &Escape, &Length, Length/2, Data); +} + +/** + \brief Allocate and construct a U_WMRRESTOREDC record + \return pointer to the U_WMRRESTOREDC record, or NULL on error. + \param DC Drawing Context to restore. (negative is relative to current, positive is absolute) +*/ +char *U_WMRRESTOREDC_set(int16_t DC){ + return U_WMRCORE_1U16_set(U_WMR_SETMAPMODE, DC); +} + +/** + \brief Allocate and construct a U_WMRFILLREGION record. + \return pointer to the U_WMRFILLREGION record, or NULL on error. + \param Region Region to fill + \param Brush Brush to fill with +*/ +char *U_WMRFILLREGION_set(uint16_t Region, uint16_t Brush){ + return U_WMRCORE_2U16_set(U_WMR_FILLREGION, Region, Brush); +} + +/** + \brief Allocate and construct a U_WMRFRAMEREGION record. + \return pointer to the U_WMRFRAMEREGION record, or NULL on error. + \param Region Index of region to frame in object table + \param Brush Index of brush to use in frame in object table + \param Height in logical units (of frame) + \param Width in logical units (of frame) +*/ +char *U_WMRFRAMEREGION_set(uint16_t Region, uint16_t Brush, int16_t Height, int16_t Width){ + return U_WMRCORE_4U16_set(U_WMR_FRAMEREGION, Region, Brush, U_U16(Height), U_U16(Width)); +} + +/** + \brief Allocate and construct a U_WMRINVERTREGION record. + \return pointer to the U_WMRINVERTREGION record, or NULL on error. + \param Region Index of region to invert. +*/ +char *U_WMRINVERTREGION_set(uint16_t Region){ + return U_WMRCORE_1U16_set(U_WMR_INVERTREGION, Region); +} + +/** + \brief Allocate and construct a U_WMRPAINTREGION record. + \return pointer to the U_WMRPAINTREGION record, or NULL on error. + \param Region Index of region to paint with the current Brush. +*/ +char *U_WMRPAINTREGION_set(uint16_t Region){ + return U_WMRCORE_1U16_set(U_WMR_PAINTREGION, Region); +} + +/** + \brief Allocate and construct a U_WMRSELECTCLIPREGION record. + \return pointer to the U_WMRSELECTCLIPREGION record, or NULL on error. + \param Region Index of region to become clipping region.. +*/ +char *U_WMRSELECTCLIPREGION_set(uint16_t Region){ + return U_WMRCORE_1U16_set(U_WMR_SELECTCLIPREGION, Region); +} + +/** + \brief Allocate and construct a U_WMRSELECTOBJECT record. + \return pointer to the U_WMRSELECTOBJECT record, or NULL on error. + \param object Index of object which is made active. +*/ +char *U_WMRSELECTOBJECT_set(uint16_t object){ + return U_WMRCORE_1U16_set(U_WMR_SELECTOBJECT, object); +} + +/** + \brief Allocate and construct a U_WMRSETTEXTALIGN record. + \return pointer to the U_WMRSETTEXTALIGN record, or NULL on error. + \param Mode TextAlignment Enumeration. +*/ +char *U_WMRSETTEXTALIGN_set(uint16_t Mode){ + return U_WMRCORE_2U16_set(U_WMR_SETTEXTALIGN, Mode, 0); +} + +/* in Wine, not in WMF PDF. */ + char *U_WMRDRAWTEXT_set(void){ /* in Wine, not in WMF PDF. */ + return U_WMRCORENONE_set("U_WMRDRAWTEXT"); +} + +/** + \brief Create and return a U_WMRCHORD record + \return pointer to the U_WMRCHORD record, or NULL on error + \param Radial1 Start of Chord + \param Radial2 End of Chord + \param rect Bounding rectangle. +*/ +char *U_WMRCHORD_set(U_POINT16 Radial1, U_POINT16 Radial2, U_RECT16 rect){ + return U_WMRCORE_8U16_set( + U_WMR_CHORD, + U_U16(Radial2.y), + U_U16(Radial2.x), + U_U16(Radial1.y), + U_U16(Radial1.x), + U_U16(rect.bottom), + U_U16(rect.right), + U_U16(rect.top), + U_U16(rect.left) + ); +} + +/** + \brief Allocate and construct a U_WMRSETMAPPERFLAGS record. + \return pointer to the U_WMRSETMAPPERFLAGS record, or NULL on error. + \param Mode If 1 bit set font mapper selects only matching aspect fonts. +*/ +char *U_WMRSETMAPPERFLAGS_set(uint32_t Mode){ + return U_WMRCORE_2U16_set(U_WMR_SETMAPPERFLAGS, 0xFFFF & Mode, Mode>>16); +} + +/** + \brief Allocate and construct a U_WMREXTTEXTOUT record. + \return pointer to the U_WMREXTTEXTOUT record, or NULL on error. + \param Dst {X,Y} coordinates where the string is to be written. + \param Length Stringlength in bytes + \param Opts ExtTextOutOptions Flags + \param string String to write (Latin1 encoding) + \param dx Kerning information. Must have same number of entries as Length. + \param rect Used when when U_ETO_OPAQUE or U_ETO_CLIPPED bits are set in Opts +*/ +char *U_WMREXTTEXTOUT_set(U_POINT16 Dst, int16_t Length, uint16_t Opts, + const char *string, int16_t *dx, U_RECT16 rect){ + + char *record=NULL; + uint32_t irecsize,off; + int slen; + irecsize = U_SIZE_METARECORD + 8; /* 8 = y,x,Length,Opts*/ + slen = ( Length & 1 ? Length + 1 : Length); + irecsize += slen; + if(dx)irecsize += 2*Length; + if(Opts & (U_ETO_OPAQUE | U_ETO_CLIPPED)){ + irecsize += U_SIZE_RECT16; + } + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_EXTTEXTOUT); + off = U_SIZE_METARECORD; + memcpy(record+off,&Dst.y,2); off+=2; + memcpy(record+off,&Dst.x,2); off+=2; + memcpy(record+off,&Length,2); off+=2; + memcpy(record+off,&Opts,2); off+=2; + if(Opts & (U_ETO_OPAQUE | U_ETO_CLIPPED)){ + memcpy(record+off,&rect.bottom,2); off+=2; + memcpy(record+off,&rect.right, 2); off+=2; + memcpy(record+off,&rect.top, 2); off+=2; + memcpy(record+off,&rect.left, 2); off+=2; + } + memcpy(record+off,string,strlen(string)); off+=Length; + if(Length!=slen){ + memset(record+off,0,1); off+=1; + } + if(dx){ + memcpy(record+off,dx,2*Length); + } + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRSETDIBTODEV record + \return pointer to the U_WMRSETDIBTODEV record, or NULL on error. +*/ +char *U_WMRSETDIBTODEV_set(void){ + return U_WMRCORENONE_set("U_WMRSETDIBTODEV"); +} + +/** + \brief Allocate and construct a U_WMRSELECTPALETTE record + \return pointer to the U_WMRSELECTPALETTE record, or NULL on error. + \param Palette Index of Palette to make active. +*/ +char *U_WMRSELECTPALETTE_set(uint16_t Palette){ + return U_WMRCORE_1U16_set(U_WMR_SELECTPALETTE, Palette); +} + +/** + \brief Allocate and construct a U_WMRREALIZEPALETTE record + \return pointer to the U_WMRREALIZEPALETTE record, or NULL on error. +*/ +char *U_WMRREALIZEPALETTE_set(void){ + return U_WMRCORE_NOARGS_set(U_WMR_REALIZEPALETTE); +} + +/** + \brief Allocate and construct a U_WMRSETPALENTRIES record + \return pointer to the U_WMRSETPALENTRIES record, or NULL on error. + \param Palette Redefines a set of RGB values for the current active Palette. +*/ +char *U_WMRANIMATEPALETTE_set(PU_PALETTE Palette){ + return U_WMRCORE_PALETTE_set(U_WMR_ANIMATEPALETTE, Palette); +} + +/** + \brief Allocate and construct a U_WMRSETPALENTRIES record + \return pointer to the U_WMRSETPALENTRIES record, or NULL on error. + \param Palette Defines a set of RGB values for the current active Palette. +*/ +char *U_WMRSETPALENTRIES_set(PU_PALETTE Palette){ + return U_WMRCORE_PALETTE_set(U_WMR_SETPALENTRIES, Palette); +} + +/** + \brief Allocate and construct a U_WMR_POLYPOLYGON record. + \return pointer to the U_WMR_POLYPOLYGON record, or NULL on error. + \param nPolys Number of elements in aPolyCounts + \param aPolyCounts Number of points in each poly (sequential) + \param Points array of points +*/ +char *U_WMRPOLYPOLYGON_set( + const uint16_t nPolys, + const uint16_t *aPolyCounts, + const PU_POINT16 Points + ){ + char *record; + uint32_t irecsize; + int i,cbPolys,cbPoints,off; + + cbPolys = sizeof(uint16_t)*nPolys; + for(i=cbPoints=0; i<nPolys; i++){ cbPoints += U_SIZE_POINT16*aPolyCounts[i]; } + + if(nPolys==0 || cbPoints==0)return(NULL); + + irecsize = U_SIZE_METARECORD + 2 + cbPolys + cbPoints; /* core WMR + nPolys + two array sizes in bytes */ + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_POLYPOLYGON); + off = U_SIZE_METARECORD; + memcpy(record + off, &nPolys, 2); off+=2; + memcpy(record + off, aPolyCounts, cbPolys); off+=cbPolys; + memcpy(record + off, Points, cbPoints); + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRRESIZEPALETTE record + \return pointer to the U_WMRRESIZEPALETTE record, or NULL on error. + \param Palette Changes the size of the currently active Palette. +*/ +char *U_WMRRESIZEPALETTE_set(uint16_t Palette){ + return U_WMRCORE_1U16_set(U_WMR_RESIZEPALETTE, Palette); +} + +char *U_WMR3A_set(void){ + return U_WMRCORENONE_set("U_WMR3A"); +} + +char *U_WMR3B_set(void){ + return U_WMRCORENONE_set("U_WMR3B"); +} + +char *U_WMR3C_set(void){ + return U_WMRCORENONE_set("U_WMR3C"); +} + +char *U_WMR3D_set(void){ + return U_WMRCORENONE_set("U_WMR3D"); +} + +char *U_WMR3E_set(void){ + return U_WMRCORENONE_set("U_WMR3E"); +} + +char *U_WMR3F_set(void){ + return U_WMRCORENONE_set("U_WMR3F"); +} + +// U_WMRDIBBITBLT_set +/** + \brief Allocate and construct a U_WMRDIBITBLT record. + \return pointer to the U_WMRDIBITBLT record, or NULL on error. + \param Dst Destination UL corner in logical units + \param Src Source UL corner in logical units + \param cwh W & H in logical units of Src and Dst + \param dwRop3 RasterOPeration Enumeration + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row STRIDE * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_WMRDIBBITBLT_set( + U_POINT16 Dst, + U_POINT16 cwh, + U_POINT16 Src, + uint32_t dwRop3, + const PU_BITMAPINFO Bmi, + uint32_t cbPx, + const char *Px + ){ + char *record=NULL; + uint32_t irecsize; + int cbImage,cbImage4,cbBmi,off; + PU_WMRDIBBITBLT_PX pmr_px; + PU_WMRDIBBITBLT_NOPX pmr_nopx; + + + if(Px && Bmi){ + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + irecsize = U_SIZE_WMRDIBBITBLT_PX + cbBmi + cbImage4; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_DIBBITBLT); + pmr_px = (PU_WMRDIBBITBLT_PX) record; + memcpy(pmr_px->rop3w, &dwRop3, 4); + pmr_px->ySrc = Src.y; + pmr_px->xSrc = Src.x; + pmr_px->Height = cwh.y; + pmr_px->Width = cwh.x; + pmr_px->yDst = Dst.y; + pmr_px->xDst = Dst.x; + off = U_SIZE_WMRDIBBITBLT_PX; + memcpy(record + off, Bmi, cbBmi); off += cbBmi; + memcpy(record + off, Px, cbPx); off += cbPx; + if(cbImage4 - cbImage)memset(record+off,0,cbImage4 - cbImage); + } + } + else if(!Px && !Bmi){ + irecsize = U_SIZE_WMRDIBBITBLT_NOPX; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_DIBBITBLT); + pmr_nopx = (PU_WMRDIBBITBLT_NOPX) record; + memcpy(pmr_nopx->rop3w, &dwRop3, 4); + pmr_nopx->ySrc = Src.y; + pmr_nopx->xSrc = Src.x; + pmr_nopx->ignore = 0; + pmr_nopx->Height = cwh.y; + pmr_nopx->Width = cwh.x; + pmr_nopx->yDst = Dst.y; + pmr_nopx->xDst = Dst.x; + } + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRSTRETCHDIB record. + \return pointer to the U_WMRSTRETCHDIB record, or NULL on error. + \param Dst Destination UL corner in logical units + \param cDst Destination W & H in logical units + \param Src Source UL corner in logical units + \param cSrc Source W & H in logical units + \param dwRop3 RasterOPeration Enumeration + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row STRIDE * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_WMRDIBSTRETCHBLT_set( + U_POINT16 Dst, + U_POINT16 cDst, + U_POINT16 Src, + U_POINT16 cSrc, + uint32_t dwRop3, + const PU_BITMAPINFO Bmi, + uint32_t cbPx, + const char *Px + ){ + char *record=NULL; + uint32_t irecsize; + int cbImage,cbImage4,cbBmi,off; + PU_WMRDIBSTRETCHBLT_PX pmr_px; + PU_WMRDIBSTRETCHBLT_NOPX pmr_nopx; + if(Px && Bmi){ + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + irecsize = U_SIZE_WMRDIBSTRETCHBLT_PX + cbBmi + cbImage4; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_DIBSTRETCHBLT); + pmr_px = (PU_WMRDIBSTRETCHBLT_PX) record; + memcpy(pmr_px->rop3w, &dwRop3, 4); + pmr_px->hSrc = cSrc.y; + pmr_px->wSrc = cSrc.x; + pmr_px->ySrc = Src.y; + pmr_px->xSrc = Src.x; + pmr_px->hDst = cDst.y; + pmr_px->wDst = cDst.x; + pmr_px->yDst = Dst.y; + pmr_px->xDst = Dst.x; + off = U_SIZE_WMRDIBSTRETCHBLT_PX; + memcpy(record + off, Bmi, cbBmi); off += cbBmi; + memcpy(record + off, Px, cbPx); off += cbPx; + if(cbImage4 - cbImage)memset(record+off,0,cbImage4 - cbImage); + } + } + else if(!Px && !Bmi){ + irecsize = U_SIZE_WMRDIBSTRETCHBLT_NOPX; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_DIBSTRETCHBLT); + pmr_nopx = (PU_WMRDIBSTRETCHBLT_NOPX) record; + memcpy(pmr_nopx->rop3w, &dwRop3, 4); + pmr_nopx->hSrc = cSrc.y; + pmr_nopx->wSrc = cSrc.x; + pmr_nopx->ySrc = Src.y; + pmr_nopx->xSrc = Src.x; + pmr_nopx->ignore = 0; + pmr_nopx->hDst = cDst.y; + pmr_nopx->wDst = cDst.x; + pmr_nopx->yDst = Dst.y; + pmr_nopx->xDst = Dst.x; + } + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRDIBCREATEPATTERNBRUSH record. + Accepts an image as either a DIB (Bmi/CbPx/Px defined) or a Bitmap16 (Bm16 defined). + \return pointer to the U_WMRDIBCREATEPATTERNBRUSH record, or NULL on error. + \param Style BrushStyle Enumeration + \param iUsage DIBcolors Enumeration + \param Bm16 pointer to U_BITMAP16 object for Style U_BS_PATTERN only + \param cbPx Size in bytes of pixel array (row stride * height, there may be some padding at the end of each row), for use with Bmi + \param Px bitmap buffer, for use with Bmi + \param Bmi pointer to U_BITMAPINFO for all Style OTHER than U_BS_PATTERN +*/ +char *U_WMRDIBCREATEPATTERNBRUSH_set( + const uint16_t Style, + const uint16_t iUsage, + PU_BITMAPINFO Bmi, + const uint32_t cbPx, + const char *Px, + PU_BITMAP16 Bm16 + ){ + char *record=NULL; + uint32_t irecsize; + int cbImage,cbImage4,cbBmi,cbBm16,cbBm164,off; + + if(Style==U_BS_PATTERN && Bm16){ + cbBm16 = U_SIZE_BITMAP16 + (((Bm16->Width * Bm16->BitsPixel + 15) >> 4) << 1) * Bm16->Height; + cbBm164 = UP4(cbBm16); + irecsize = U_SIZE_WMRDIBCREATEPATTERNBRUSH + cbBm164; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_DIBCREATEPATTERNBRUSH); + off = U_SIZE_METARECORD; + memcpy(record + off, &Style, 2); off+=2; + memcpy(record + off, &iUsage, 2); off+=2; + memcpy(record + off, Bm16, cbBm16); off += cbBm16; + if(cbBm164 - cbBm16)memset(record+off,0,cbBm164 - cbBm16); + } + } + else if(Bmi){ + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + irecsize = U_SIZE_WMRDIBCREATEPATTERNBRUSH + cbBmi + cbImage4; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_DIBCREATEPATTERNBRUSH); + off = U_SIZE_METARECORD; + memcpy(record + off, &Style, 2); off+=2; + memcpy(record + off, &iUsage, 2); off+=2; + memcpy(record + off, Bmi, cbBmi); off += cbBmi; + memcpy(record + off, Px, cbImage); off += cbImage; + if(cbImage4 - cbImage)memset(record + off, 0, cbImage4 - cbImage); + } + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRSTRETCHDIB record. + \return pointer to the U_WMRSTRETCHDIB record, or NULL on error. + \param Dst Destination UL corner in logical units + \param cDst Destination W & H in logical units + \param Src Source UL corner in logical units + \param cSrc Source W & H in logical units + \param cUsage DIBColors Enumeration + \param dwRop3 RasterOPeration Enumeration + \param Bmi (Optional) bitmapbuffer (U_BITMAPINFO section) + \param cbPx Size in bytes of pixel array (row STRIDE * height, there may be some padding at the end of each row) + \param Px (Optional) bitmapbuffer (pixel array section ) +*/ +char *U_WMRSTRETCHDIB_set( + U_POINT16 Dst, + U_POINT16 cDst, + U_POINT16 Src, + U_POINT16 cSrc, + uint16_t cUsage, + uint32_t dwRop3, + const PU_BITMAPINFO Bmi, + uint32_t cbPx, + const char *Px + ){ + char *record; + uint32_t irecsize; + int cbImage,cbImage4,cbBmi,off; + PU_WMRSTRETCHDIB pmr; + + SET_CB_FROM_PXBMI(Px,Bmi,cbImage,cbImage4,cbBmi,cbPx); + + irecsize = U_SIZE_WMRSTRETCHDIB + cbBmi + cbImage4; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_STRETCHDIB); + pmr = (PU_WMRSTRETCHDIB) record; + memcpy(pmr->rop3w, &dwRop3, 4); + pmr->cUsage = cUsage; + pmr->hSrc = cSrc.y; + pmr->wSrc = cSrc.x; + pmr->ySrc = Src.y; + pmr->xSrc = Src.x; + pmr->hDst = cDst.y; + pmr->wDst = cDst.x; + pmr->yDst = Dst.y; + pmr->xDst = Dst.x; + off = U_SIZE_WMRSTRETCHDIB; + if(cbBmi){ + memcpy(record + off, Bmi, cbBmi); off += cbBmi; + memcpy(record + off, Px, cbPx); off += cbPx; + if(cbImage4 - cbImage)memset(record+off,0,cbImage4 - cbImage); + } + } + return(record); +} + +char *U_WMR44_set(void){ + return U_WMRCORENONE_set("U_WMR44"); +} + +char *U_WMR45_set(void){ + return U_WMRCORENONE_set("U_WMR45"); +} + +char *U_WMR46_set(void){ + return U_WMRCORENONE_set("U_WMR46"); +} + +char *U_WMR47_set(void){ + return U_WMRCORENONE_set("U_WMR47"); +} + +/** + \brief Create and return a U_WMREXTFLOODFILL record + \return pointer to the U_WMREXTFLOODFILL record, or NULL on error + \param Mode FloodFill Enumeration. + \param Color Color to Fill with. + \param coord Location to start fill. +*/ +char *U_WMREXTFLOODFILL_set(uint16_t Mode, U_COLORREF Color, U_POINT16 coord){ + return U_WMRCORE_1U16_CRF_2U16_set( + U_WMR_EXTFLOODFILL, + &Mode, + Color, + U_P16(coord.y), + U_P16(coord.x) + ); +} + +char *U_WMR49_set(void){ + return U_WMRCORENONE_set("U_WMR49"); +} + +char *U_WMR4A_set(void){ + return U_WMRCORENONE_set("U_WMR4A"); +} + +char *U_WMR4B_set(void){ + return U_WMRCORENONE_set("U_WMR4B"); +} + +char *U_WMR4C_set(void){ + return U_WMRCORENONE_set("U_WMRRESETDOC"); +} + +char *U_WMR4D_set(void){ + return U_WMRCORENONE_set("U_WMRSTARTDOC"); +} + +char *U_WMR4E_set(void){ + return U_WMRCORENONE_set("U_WMR4E"); +} + +char *U_WMR4F_set(void){ + return U_WMRCORENONE_set("U_WMRSTARTPAGE"); +} + +char *U_WMR50_set(void){ + return U_WMRCORENONE_set("U_WMRENDPAGE"); +} + +char *U_WMR51_set(void){ + return U_WMRCORENONE_set("U_WMR51"); +} + +char *U_WMRABORTDOC_set(void){ + return U_WMRCORENONE_set("U_WMRABORTDOC"); +} + +char *U_WMR53_set(void){ + return U_WMRCORENONE_set("U_WMR53"); +} + +char *U_WMR54_set(void){ + return U_WMRCORENONE_set("U_WMR54"); +} + +char *U_WMR55_set(void){ + return U_WMRCORENONE_set("U_WMR55"); +} + +char *U_WMR56_set(void){ + return U_WMRCORENONE_set("U_WMR56"); +} + +char *U_WMR57_set(void){ + return U_WMRCORENONE_set("U_WMR57"); +} + +char *U_WMR58_set(void){ + return U_WMRCORENONE_set("U_WMR58"); +} + +char *U_WMR59_set(void){ + return U_WMRCORENONE_set("U_WMR59"); +} + +char *U_WMR5A_set(void){ + return U_WMRCORENONE_set("U_WMR5A"); +} + +char *U_WMR5B_set(void){ + return U_WMRCORENONE_set("U_WMR5B"); +} + +char *U_WMR5C_set(void){ + return U_WMRCORENONE_set("U_WMR5C"); +} + +char *U_WMR5D_set(void){ + return U_WMRCORENONE_set("U_WMR5D"); +} + +char *U_WMR5E_set(void){ + return U_WMRCORENONE_set("U_WMRENDDOC"); +} + +char *U_WMR5F_set(void){ + return U_WMRCORENONE_set("U_WMR5F"); +} + +char *U_WMR60_set(void){ + return U_WMRCORENONE_set("U_WMR60"); +} + +char *U_WMR61_set(void){ + return U_WMRCORENONE_set("U_WMR61"); +} + +char *U_WMR62_set(void){ + return U_WMRCORENONE_set("U_WMR62"); +} + +char *U_WMR63_set(void){ + return U_WMRCORENONE_set("U_WMR63"); +} + +char *U_WMR64_set(void){ + return U_WMRCORENONE_set("U_WMR64"); +} + +char *U_WMR65_set(void){ + return U_WMRCORENONE_set("U_WMR65"); +} + +char *U_WMR66_set(void){ + return U_WMRCORENONE_set("U_WMR66"); +} + +char *U_WMR67_set(void){ + return U_WMRCORENONE_set("U_WMR67"); +} + +char *U_WMR68_set(void){ + return U_WMRCORENONE_set("U_WMR68"); +} + +char *U_WMR69_set(void){ + return U_WMRCORENONE_set("U_WMR69"); +} + +char *U_WMR6A_set(void){ + return U_WMRCORENONE_set("U_WMR6A"); +} + +char *U_WMR6B_set(void){ + return U_WMRCORENONE_set("U_WMR6B"); +} + +char *U_WMR6C_set(void){ + return U_WMRCORENONE_set("U_WMR6C"); +} + +char *U_WMR6D_set(void){ + return U_WMRCORENONE_set("U_WMR6D"); +} + +char *U_WMR6E_set(void){ + return U_WMRCORENONE_set("U_WMR6E"); +} + +char *U_WMR6F_set(void){ + return U_WMRCORENONE_set("U_WMR6F"); +} + +char *U_WMR70_set(void){ + return U_WMRCORENONE_set("U_WMR70"); +} + +char *U_WMR71_set(void){ + return U_WMRCORENONE_set("U_WMR71"); +} + +char *U_WMR72_set(void){ + return U_WMRCORENONE_set("U_WMR72"); +} + +char *U_WMR73_set(void){ + return U_WMRCORENONE_set("U_WMR73"); +} + +char *U_WMR74_set(void){ + return U_WMRCORENONE_set("U_WMR74"); +} + +char *U_WMR75_set(void){ + return U_WMRCORENONE_set("U_WMR75"); +} + +char *U_WMR76_set(void){ + return U_WMRCORENONE_set("U_WMR76"); +} + +char *U_WMR77_set(void){ + return U_WMRCORENONE_set("U_WMR77"); +} + +char *U_WMR78_set(void){ + return U_WMRCORENONE_set("U_WMR78"); +} + +char *U_WMR79_set(void){ + return U_WMRCORENONE_set("U_WMR79"); +} + +char *U_WMR7A_set(void){ + return U_WMRCORENONE_set("U_WMR7A"); +} + +char *U_WMR7B_set(void){ + return U_WMRCORENONE_set("U_WMR7B"); +} + +char *U_WMR7C_set(void){ + return U_WMRCORENONE_set("U_WMR7C"); +} + +char *U_WMR7D_set(void){ + return U_WMRCORENONE_set("U_WMR7D"); +} + +char *U_WMR7E_set(void){ + return U_WMRCORENONE_set("U_WMR7E"); +} + +char *U_WMR7F_set(void){ + return U_WMRCORENONE_set("U_WMR7F"); +} + +char *U_WMR80_set(void){ + return U_WMRCORENONE_set("U_WMR80"); +} + +char *U_WMR81_set(void){ + return U_WMRCORENONE_set("U_WMR81"); +} + +char *U_WMR82_set(void){ + return U_WMRCORENONE_set("U_WMR82"); +} + +char *U_WMR83_set(void){ + return U_WMRCORENONE_set("U_WMR83"); +} + +char *U_WMR84_set(void){ + return U_WMRCORENONE_set("U_WMR84"); +} + +char *U_WMR85_set(void){ + return U_WMRCORENONE_set("U_WMR85"); +} + +char *U_WMR86_set(void){ + return U_WMRCORENONE_set("U_WMR86"); +} + +char *U_WMR87_set(void){ + return U_WMRCORENONE_set("U_WMR87"); +} + +char *U_WMR88_set(void){ + return U_WMRCORENONE_set("U_WMR88"); +} + +char *U_WMR89_set(void){ + return U_WMRCORENONE_set("U_WMR89"); +} + +char *U_WMR8A_set(void){ + return U_WMRCORENONE_set("U_WMR8A"); +} + +char *U_WMR8B_set(void){ + return U_WMRCORENONE_set("U_WMR8B"); +} + +char *U_WMR8C_set(void){ + return U_WMRCORENONE_set("U_WMR8C"); +} + +char *U_WMR8D_set(void){ + return U_WMRCORENONE_set("U_WMR8D"); +} + +char *U_WMR8E_set(void){ + return U_WMRCORENONE_set("U_WMR8E"); +} + +char *U_WMR8F_set(void){ + return U_WMRCORENONE_set("U_WMR8F"); +} + +char *U_WMR90_set(void){ + return U_WMRCORENONE_set("U_WMR90"); +} + +char *U_WMR91_set(void){ + return U_WMRCORENONE_set("U_WMR91"); +} + +char *U_WMR92_set(void){ + return U_WMRCORENONE_set("U_WMR92"); +} + +char *U_WMR93_set(void){ + return U_WMRCORENONE_set("U_WMR93"); +} + +char *U_WMR94_set(void){ + return U_WMRCORENONE_set("U_WMR94"); +} + +char *U_WMR95_set(void){ + return U_WMRCORENONE_set("U_WMR95"); +} + +char *U_WMR96_set(void){ + return U_WMRCORENONE_set("U_WMR96"); +} + +char *U_WMR97_set(void){ + return U_WMRCORENONE_set("U_WMR97"); +} + +char *U_WMR98_set(void){ + return U_WMRCORENONE_set("U_WMR98"); +} + +char *U_WMR99_set(void){ + return U_WMRCORENONE_set("U_WMR99"); +} + +char *U_WMR9A_set(void){ + return U_WMRCORENONE_set("U_WMR9A"); +} + +char *U_WMR9B_set(void){ + return U_WMRCORENONE_set("U_WMR9B"); +} + +char *U_WMR9C_set(void){ + return U_WMRCORENONE_set("U_WMR9C"); +} + +char *U_WMR9D_set(void){ + return U_WMRCORENONE_set("U_WMR9D"); +} + +char *U_WMR9E_set(void){ + return U_WMRCORENONE_set("U_WMR9E"); +} + +char *U_WMR9F_set(void){ + return U_WMRCORENONE_set("U_WMR9F"); +} + +char *U_WMRA0_set(void){ + return U_WMRCORENONE_set("U_WMRA0"); +} + +char *U_WMRA1_set(void){ + return U_WMRCORENONE_set("U_WMRA1"); +} + +char *U_WMRA2_set(void){ + return U_WMRCORENONE_set("U_WMRA2"); +} + +char *U_WMRA3_set(void){ + return U_WMRCORENONE_set("U_WMRA3"); +} + +char *U_WMRA4_set(void){ + return U_WMRCORENONE_set("U_WMRA4"); +} + +char *U_WMRA5_set(void){ + return U_WMRCORENONE_set("U_WMRA5"); +} + +char *U_WMRA6_set(void){ + return U_WMRCORENONE_set("U_WMRA6"); +} + +char *U_WMRA7_set(void){ + return U_WMRCORENONE_set("U_WMRA7"); +} + +char *U_WMRA8_set(void){ + return U_WMRCORENONE_set("U_WMRA8"); +} + +char *U_WMRA9_set(void){ + return U_WMRCORENONE_set("U_WMRA9"); +} + +char *U_WMRAA_set(void){ + return U_WMRCORENONE_set("U_WMRAA"); +} + +char *U_WMRAB_set(void){ + return U_WMRCORENONE_set("U_WMRAB"); +} + +char *U_WMRAC_set(void){ + return U_WMRCORENONE_set("U_WMRAC"); +} + +char *U_WMRAD_set(void){ + return U_WMRCORENONE_set("U_WMRAD"); +} + +char *U_WMRAE_set(void){ + return U_WMRCORENONE_set("U_WMRAE"); +} + +char *U_WMRAF_set(void){ + return U_WMRCORENONE_set("U_WMRAF"); +} + +char *U_WMRB0_set(void){ + return U_WMRCORENONE_set("U_WMRB0"); +} + +char *U_WMRB1_set(void){ + return U_WMRCORENONE_set("U_WMRB1"); +} + +char *U_WMRB2_set(void){ + return U_WMRCORENONE_set("U_WMRB2"); +} + +char *U_WMRB3_set(void){ + return U_WMRCORENONE_set("U_WMRB3"); +} + +char *U_WMRB4_set(void){ + return U_WMRCORENONE_set("U_WMRB4"); +} + +char *U_WMRB5_set(void){ + return U_WMRCORENONE_set("U_WMRB5"); +} + +char *U_WMRB6_set(void){ + return U_WMRCORENONE_set("U_WMRB6"); +} + +char *U_WMRB7_set(void){ + return U_WMRCORENONE_set("U_WMRB7"); +} + +char *U_WMRB8_set(void){ + return U_WMRCORENONE_set("U_WMRB8"); +} + +char *U_WMRB9_set(void){ + return U_WMRCORENONE_set("U_WMRB9"); +} + +char *U_WMRBA_set(void){ + return U_WMRCORENONE_set("U_WMRBA"); +} + +char *U_WMRBB_set(void){ + return U_WMRCORENONE_set("U_WMRBB"); +} + +char *U_WMRBC_set(void){ + return U_WMRCORENONE_set("U_WMRBC"); +} + +char *U_WMRBD_set(void){ + return U_WMRCORENONE_set("U_WMRBD"); +} + +char *U_WMRBE_set(void){ + return U_WMRCORENONE_set("U_WMRBE"); +} + +char *U_WMRBF_set(void){ + return U_WMRCORENONE_set("U_WMRBF"); +} + +char *U_WMRC0_set(void){ + return U_WMRCORENONE_set("U_WMRC0"); +} + +char *U_WMRC1_set(void){ + return U_WMRCORENONE_set("U_WMRC1"); +} + +char *U_WMRC2_set(void){ + return U_WMRCORENONE_set("U_WMRC2"); +} + +char *U_WMRC3_set(void){ + return U_WMRCORENONE_set("U_WMRC3"); +} + +char *U_WMRC4_set(void){ + return U_WMRCORENONE_set("U_WMRC4"); +} + +char *U_WMRC5_set(void){ + return U_WMRCORENONE_set("U_WMRC5"); +} + +char *U_WMRC6_set(void){ + return U_WMRCORENONE_set("U_WMRC6"); +} + +char *U_WMRC7_set(void){ + return U_WMRCORENONE_set("U_WMRC7"); +} + +char *U_WMRC8_set(void){ + return U_WMRCORENONE_set("U_WMRC8"); +} + +char *U_WMRC9_set(void){ + return U_WMRCORENONE_set("U_WMRC9"); +} + +char *U_WMRCA_set(void){ + return U_WMRCORENONE_set("U_WMRCA"); +} + +char *U_WMRCB_set(void){ + return U_WMRCORENONE_set("U_WMRCB"); +} + +char *U_WMRCC_set(void){ + return U_WMRCORENONE_set("U_WMRCC"); +} + +char *U_WMRCD_set(void){ + return U_WMRCORENONE_set("U_WMRCD"); +} + +char *U_WMRCE_set(void){ + return U_WMRCORENONE_set("U_WMRCE"); +} + +char *U_WMRCF_set(void){ + return U_WMRCORENONE_set("U_WMRCF"); +} + +char *U_WMRD0_set(void){ + return U_WMRCORENONE_set("U_WMRD0"); +} + +char *U_WMRD1_set(void){ + return U_WMRCORENONE_set("U_WMRD1"); +} + +char *U_WMRD2_set(void){ + return U_WMRCORENONE_set("U_WMRD2"); +} + +char *U_WMRD3_set(void){ + return U_WMRCORENONE_set("U_WMRD3"); +} + +char *U_WMRD4_set(void){ + return U_WMRCORENONE_set("U_WMRD4"); +} + +char *U_WMRD5_set(void){ + return U_WMRCORENONE_set("U_WMRD5"); +} + +char *U_WMRD6_set(void){ + return U_WMRCORENONE_set("U_WMRD6"); +} + +char *U_WMRD7_set(void){ + return U_WMRCORENONE_set("U_WMRD7"); +} + +char *U_WMRD8_set(void){ + return U_WMRCORENONE_set("U_WMRD8"); +} + +char *U_WMRD9_set(void){ + return U_WMRCORENONE_set("U_WMRD9"); +} + +char *U_WMRDA_set(void){ + return U_WMRCORENONE_set("U_WMRDA"); +} + +char *U_WMRDB_set(void){ + return U_WMRCORENONE_set("U_WMRDB"); +} + +char *U_WMRDC_set(void){ + return U_WMRCORENONE_set("U_WMRDC"); +} + +char *U_WMRDD_set(void){ + return U_WMRCORENONE_set("U_WMRDD"); +} + +char *U_WMRDE_set(void){ + return U_WMRCORENONE_set("U_WMRDE"); +} + +char *U_WMRDF_set(void){ + return U_WMRCORENONE_set("U_WMRDF"); +} + +char *U_WMRE0_set(void){ + return U_WMRCORENONE_set("U_WMRE0"); +} + +char *U_WMRE1_set(void){ + return U_WMRCORENONE_set("U_WMRE1"); +} + +char *U_WMRE2_set(void){ + return U_WMRCORENONE_set("U_WMRE2"); +} + +char *U_WMRE3_set(void){ + return U_WMRCORENONE_set("U_WMRE3"); +} + +char *U_WMRE4_set(void){ + return U_WMRCORENONE_set("U_WMRE4"); +} + +char *U_WMRE5_set(void){ + return U_WMRCORENONE_set("U_WMRE5"); +} + +char *U_WMRE6_set(void){ + return U_WMRCORENONE_set("U_WMRE6"); +} + +char *U_WMRE7_set(void){ + return U_WMRCORENONE_set("U_WMRE7"); +} + +char *U_WMRE8_set(void){ + return U_WMRCORENONE_set("U_WMRE8"); +} + +char *U_WMRE9_set(void){ + return U_WMRCORENONE_set("U_WMRE9"); +} + +char *U_WMREA_set(void){ + return U_WMRCORENONE_set("U_WMREA"); +} + +char *U_WMREB_set(void){ + return U_WMRCORENONE_set("U_WMREB"); +} + +char *U_WMREC_set(void){ + return U_WMRCORENONE_set("U_WMREC"); +} + +char *U_WMRED_set(void){ + return U_WMRCORENONE_set("U_WMRED"); +} + +char *U_WMREE_set(void){ + return U_WMRCORENONE_set("U_WMREE"); +} + +char *U_WMREF_set(void){ + return U_WMRCORENONE_set("U_WMREF"); +} + +/** + \brief Create and return a U_WMRDELETEOBJECT record + \return pointer to the U_WMRDELETEOBJECT record, or NULL on error + \param object Index of object to delete. +*/ +char *U_WMRDELETEOBJECT_set(uint16_t object){ + return U_WMRCORE_1U16_set(U_WMR_DELETEOBJECT, object); +} + +char *U_WMRF1_set(void){ + return U_WMRCORENONE_set("U_WMRF1"); +} + +char *U_WMRF2_set(void){ + return U_WMRCORENONE_set("U_WMRF2"); +} + +char *U_WMRF3_set(void){ + return U_WMRCORENONE_set("U_WMRF3"); +} + +char *U_WMRF4_set(void){ + return U_WMRCORENONE_set("U_WMRF4"); +} + +char *U_WMRF5_set(void){ + return U_WMRCORENONE_set("U_WMRF5"); +} + +char *U_WMRF6_set(void){ + return U_WMRCORENONE_set("U_WMRF6"); +} + +/** + \brief Create and return a U_WMRCREATEPALETTE record + \return pointer to the U_WMRCREATEPALETTE record, or NULL on error + \param Palette Create a Palette object. +*/ +char *U_WMRCREATEPALETTE_set(PU_PALETTE Palette){ + return U_WMRCORE_PALETTE_set(U_WMR_CREATEPALETTE, Palette); +} + +char *U_WMRF8_set(void){ + return U_WMRCORENONE_set("U_WMRF8"); +} + +/** + \brief Allocate and construct a U_WMRCREATEPATTERNBRUSH record. + Warning - application support for U_WMRCREATEPATTERNBRUSH is spotty, better to use U_WMRDIBCREATEPATTERNBRUSH. + \return pointer to the U_WMRCREATEPATTERNBRUSH record, or NULL on error. + \param Bm16 Pointer to a Bitmap16 Object, only the first 10 bytes are used. + \param Pattern byte array pattern, described by Bm16, for brush +*/ +char *U_WMRCREATEPATTERNBRUSH_set( + PU_BITMAP16 Bm16, + char *Pattern + ){ + char *record; + uint32_t irecsize,off,cbPat; + if(!Bm16 || !Pattern)return(NULL); + + cbPat = (((Bm16->Width * Bm16->BitsPixel + 15) >> 4) << 1) * Bm16->Height; + irecsize = U_SIZE_METARECORD + 14 + 4 + 18 + cbPat; /* core WMR + truncated Bm16 + pattern */ + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_CREATEPATTERNBRUSH); + off = U_SIZE_METARECORD; + memcpy(record + off, Bm16, 14); off+=14; /* Truncated bitmap16 object*/ + memset(record + off, 0, 4); off+=4; /* 4 bytes of its "bits", which are ignored */ + memset(record + off, 0, 18); off+=18; /* 18 bytes of zero, which are ignored */ + memcpy(record + off, Pattern, cbPat); /* The pattern array */ + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRCREATEPENINDIRECT record. + \return pointer to the U_WMRCREATEPENINDIRECT record, or NULL on error. + \param pen Parameters of the pen object to create. +*/ +char *U_WMRCREATEPENINDIRECT_set(U_PEN pen){ + return U_WMRCORE_2U16_N16_set(U_WMR_CREATEPENINDIRECT, NULL, NULL, U_SIZE_PEN/2, &pen); +} + +/** + \brief Allocate and construct a U_WMRCREATEFONTINDIRECT record. + \return pointer to the U_WMRCREATEFONTINDIRECT record, or NULL on error. + \param font Parameters of the font object to create. +*/ +char *U_WMRCREATEFONTINDIRECT_set(PU_FONT font){ + char *record=NULL; + uint32_t irecsize,off,flen; + flen = 1 + strlen((char *)font->FaceName); /* include the null terminator in the count */ + if(flen & 1) flen++; /* make the allocation end line up at an even byte */ + irecsize = U_SIZE_METARECORD + U_SIZE_FONT_CORE + flen; + record = calloc(1,irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_CREATEFONTINDIRECT); + off = U_SIZE_METARECORD; + memcpy(record+off,font,U_SIZE_FONT_CORE + flen); + } + return(record); +} + +/** + \brief Allocate and construct a U_WMRCREATEBRUSHINDIRECT record. + \return pointer to the U_WMRCREATEBRUSHINDIRECT record, or NULL on error. + \param brush Parameters of the brush object to create. +*/ +char *U_WMRCREATEBRUSHINDIRECT_set(U_WLOGBRUSH brush){ + return U_WMRCORE_2U16_N16_set(U_WMR_CREATEBRUSHINDIRECT, NULL, NULL, U_SIZE_WLOGBRUSH/2, &brush); +} + + /* in Wine, not in WMF PDF */ + char *U_WMRCREATEBITMAPINDIRECT_set(void){ + return U_WMRCORENONE_set("U_WMRCREATEBITMAPINDIRECT"); +} + + /* in Wine, not in WMF PDF */ + char *U_WMRCREATEBITMAP_set(void){ + return U_WMRCORENONE_set("U_WMRCREATEBITMAP"); +} + +/** + \brief Allocate and construct a U_WMRCREATEREGION record. + \return pointer to the U_WMRCREATEREGION record, or NULL on error. + \param region Parameters of the region object to create. +*/ +char *U_WMRCREATEREGION_set(PU_REGION region){ + char *record=NULL; + uint32_t irecsize,off; + irecsize = U_SIZE_METARECORD + region->Size; + record = malloc(irecsize); + if(record){ + U_WMRCORE_SETRECHEAD(record,irecsize,U_WMR_CREATEREGION); + off = U_SIZE_METARECORD; + memcpy(record+off,region,region->Size); + } + return(record); +} + + +/* all of the *_set are above, all of the *_get are below */ + +/* ********************************************************************************************** +These functions are used for Image conversions and other +utility operations. Character type conversions are in uwmf_utf.c +*********************************************************************************************** */ + + +/** + \brief Make up an approximate dx array to pass to U_WMREXTTEXTOUT_get(), based on character height and weight. + + Take abs. value of character height, get width by multiplying by 0.6, and correct weight + approximately, with formula (measured on screen for one text line of Arial). + Caller is responsible for free() on the returned pointer. + + \return pointer to dx array + \param height character height (absolute value will be used) + \param weight LF_Weight Enumeration (character weight) + \param members Number of entries to put into dx + +*/ +int16_t *dx16_get( + int32_t height, + uint32_t weight, + uint32_t members + ){ + uint32_t i, width; + int16_t *dx; + dx = (int16_t *) malloc(members * sizeof(int16_t)); + if(dx){ + if(U_FW_DONTCARE == weight)weight=U_FW_NORMAL; + width = (uint32_t) U_ROUND(((float) (height > 0 ? height : -height)) * 0.6 * (0.00024*(float) weight + 0.904)); + for ( i = 0; i < members; i++ ){ dx[i] = (width > INT16_MAX ? INT16_MAX : width); } + } + return(dx); +} + +/** + \brief Return the size of a WMF record, or 0 if it is found to be invalid. + A valid record will have a size that does not cause it to extend + beyond the end of data in memory. + A valid record will not be smaller than the smallest possible WMF record. + \return size of the record in bytes, 0 on failure + \param contents record to extract data from + \param blimit one byte past the last WMF record in memory. +*/ +size_t U_WMRRECSAFE_get( + const char *contents, + const char *blimit + ){ + size_t size=0; + uint32_t Size16; + memcpy(&Size16, contents + offsetof(U_METARECORD,Size16_4), 4); + size = 2*Size16; + /* Record is not self consistent - described size past the end of WMF in memory */ + if(size < U_SIZE_METARECORD || + contents + size - 1 >= blimit || + contents + size - 1 < contents)size=0; + return(size); +} + + +/* ********************************************************************************************** +These functions create standard structures used in the WMR records. +*********************************************************************************************** */ + +// hide these from Doxygen +//! @cond +/* ********************************************************************************************** +These functions contain shared code used by various U_WMR*_get functions. These should NEVER be called +by end user code and to further that end prototypes are NOT provided and they are hidden from Doxygen. +*********************************************************************************************** */ + +int U_WMRCORENONE_get(char *string){ + printf("unimplemented creator for:%s\n",string); + return(0); +} + +/* Returns the record size in bytes for a valid record, or 0 for an invalid record. + A valid record's size is at least as large as the minimum size passed in through minsize. + Use U_WMRRECSAFE_get() to check if the record extends too far in memory. +*/ +int U_WMRCORE_RECSAFE_get( + const char *contents, + int minsize +){ + int size=0; + uint32_t Size16; + memcpy(&Size16, contents + offsetof(U_METARECORD,Size16_4),4); + size = 2*Size16; + if(size < minsize)size=0; + return(size); +} + + +/* records like U_WMRFLOODFILL and others. all args are optional, Color is not */ +int U_WMRCORE_1U16_CRF_2U16_get( + const char *contents, + int minsize, + uint16_t *arg1, + PU_COLORREF Color, + uint16_t *arg2, + uint16_t *arg3 + ){ + int size = U_WMRCORE_RECSAFE_get(contents, minsize); + int irecsize,off; + irecsize = U_SIZE_METARECORD + U_SIZE_COLORREF; + if(arg1)irecsize+=2; + if(arg2)irecsize+=2; + if(arg3)irecsize+=2; + off = U_SIZE_METARECORD; + if(arg1){ memcpy(arg1, contents + off, 2); off+=2; } + memcpy(Color, contents + off, 4); off+=4; + if(arg2){ memcpy(arg2, contents + off, 2); off+=2; } + if(arg3){ memcpy(arg3, contents + off, 2); off+=2; } + return(size); +} + +/* records that have a single uint16_t argument like PU_WMRSETMAPMODE + May also be used with int16_t with appropriate casts */ +int U_WMRCORE_1U16_get( + const char *contents, + int minsize, + uint16_t *arg1 + ){ + int size = U_WMRCORE_RECSAFE_get(contents, minsize); + int off = U_SIZE_METARECORD; + if(!size)return(0); + memcpy(arg1, contents + off, 2); + return(size); +} + +/* records that have two uint16_t arguments like U_WMRSETBKMODE + May also be used with int16_t with appropriate casts */ +int U_WMRCORE_2U16_get( + const char *contents, + int minsize, + uint16_t *arg1, + uint16_t *arg2 + ){ + int size = U_WMRCORE_RECSAFE_get(contents, minsize); + int off = U_SIZE_METARECORD; + memcpy(arg1, contents + off, 2); off+=2; + memcpy(arg2, contents + off, 2); + return(size); +} + +/* records that have four uint16_t arguments like U_WMRSCALEWINDOWEXT + May also be used with int16_t with appropriate casts */ +int U_WMRCORE_4U16_get( + const char *contents, + int minsize, + uint16_t *arg1, + uint16_t *arg2, + uint16_t *arg3, + uint16_t *arg4 + ){ + int size = U_WMRCORE_RECSAFE_get(contents, minsize); + int off = U_SIZE_METARECORD; + if(!size)return(0); + memcpy(arg1, contents + off, 2); off+=2; + memcpy(arg2, contents + off, 2); off+=2; + memcpy(arg3, contents + off, 2); off+=2; + memcpy(arg4, contents + off, 2); + return(size); +} + +/* records that have five uint16_t arguments like U_WMRCREATEPENINDIRECT + May also be used with int16_t with appropriate casts */ +int U_WMRCORE_5U16_get( + const char *contents, + int minsize, + uint16_t *arg1, + uint16_t *arg2, + uint16_t *arg3, + uint16_t *arg4, + uint16_t *arg5 + ){ + int size = U_WMRCORE_RECSAFE_get(contents, minsize); + int off = U_SIZE_METARECORD; + if(!size)return(0); + memcpy(arg1, contents + off, 2); off+=2; + memcpy(arg2, contents + off, 2); off+=2; + memcpy(arg3, contents + off, 2); off+=2; + memcpy(arg4, contents + off, 2); off+=2; + memcpy(arg5, contents + off, 2); + return(size); +} + +/* records that have six uint16_t arguments like U_ROUNDREC + May also be used with int16_t with appropriate casts */ +int U_WMRCORE_6U16_get( + const char *contents, + int minsize, + uint16_t *arg1, + uint16_t *arg2, + uint16_t *arg3, + uint16_t *arg4, + uint16_t *arg5, + uint16_t *arg6 + ){ + int size = U_WMRCORE_RECSAFE_get(contents, minsize); + int off = U_SIZE_METARECORD; + if(!size)return(0); + memcpy(arg1, contents + off, 2); off+=2; + memcpy(arg2, contents + off, 2); off+=2; + memcpy(arg3, contents + off, 2); off+=2; + memcpy(arg4, contents + off, 2); off+=2; + memcpy(arg5, contents + off, 2); off+=2; + memcpy(arg6, contents + off, 2); + return(size); +} + +/* records that have eight uint16_t arguments like U_WMRARC + May also be used with int16_t with appropriate casts */ +int U_WMRCORE_8U16_get( + const char *contents, + int minsize, + uint16_t *arg1, + uint16_t *arg2, + uint16_t *arg3, + uint16_t *arg4, + uint16_t *arg5, + uint16_t *arg6, + uint16_t *arg7, + uint16_t *arg8 + ){ + int size = U_WMRCORE_RECSAFE_get(contents, minsize); + int off = U_SIZE_METARECORD; + if(!size)return(0); + memcpy(arg1, contents + off, 2); off+=2; + memcpy(arg2, contents + off, 2); off+=2; + memcpy(arg3, contents + off, 2); off+=2; + memcpy(arg4, contents + off, 2); off+=2; + memcpy(arg5, contents + off, 2); off+=2; + memcpy(arg6, contents + off, 2); off+=2; + memcpy(arg7, contents + off, 2); off+=2; + memcpy(arg8, contents + off, 2); + return(size); +} + +/* records that have + arg1 an (optional) (u)int16 + arg2 an (optional( (u)int16 + array of data cells or just a bunch of data. Passed as a char because the structures in the WMF in memory may + not be aligned properly for those structures. Caller has to take them apart - carefully. + like U_WMRCREATEBRUSHINDIRECT with arg1=arg2=NULL +*/ +int U_WMRCORE_2U16_N16_get( + const char *contents, + int minsize, + uint16_t *arg1, + uint16_t *arg2, + const char **array + ){ + int size = U_WMRCORE_RECSAFE_get(contents, minsize); + int off = U_SIZE_METARECORD; + if(!size)return(0); + if(arg1){ memcpy(arg1, contents + off, 2); off+=2; } + if(arg2){ memcpy(arg2, contents + off, 2); off+=2; } + *array = (contents + off); + return(size); +} + + + +/* records that get a U_PALETTE like U_WMRANIMATEPALETTE. Fills in the first two fields of U_PALETTE only, and returns + returns a separateepointer to the PalEntries[] array. This pointer is most likely not aligned with the data. + */ +int U_WMRCORE_PALETTE_get( + const char *contents, + int minsize, + PU_PALETTE Palette, + const char **PalEntries + ){ + int size = U_WMRCORE_RECSAFE_get(contents, minsize); + if(!size)return(0); + contents += offsetof(U_WMRANIMATEPALETTE, Palette); + memset(Palette, 0, (U_SIZE_PALETTE)); + memcpy(Palette, contents, (U_SIZE_PALETTE)); + *PalEntries = (contents + offsetof(U_PALETTE, PalEntries)); + return(size); +} + +//! @endcond + +/** + \brief Return parameters from a bitmapcoreheader. + All are returned as 32 bit integers, regardless of their internal representation. + + \param BmiCh char * pointer to a U_BITMAPCOREHEADER. Note, data may not be properly aligned. + \param Size_4 size of the coreheader in bytes + \param Width; Width of pixel array + \param Height; Height of pixel array + \param BitCount Pixel Format (BitCount Enumeration) +*/ +void U_BITMAPCOREHEADER_get( + const char *BmiCh, + int32_t *Size, + int32_t *Width, + int32_t *Height, + int32_t *BitCount + ){ + uint32_t utmp4; + uint16_t utmp2; + memcpy(&utmp4, BmiCh + offsetof(U_BITMAPCOREHEADER,Size_4), 4); *Size = utmp4; + memcpy(&utmp2, BmiCh + offsetof(U_BITMAPCOREHEADER,Width), 2); *Width = utmp2; + memcpy(&utmp2, BmiCh + offsetof(U_BITMAPCOREHEADER,Height), 2); *Height = utmp2; + memcpy(&utmp2, BmiCh + offsetof(U_BITMAPCOREHEADER,BitCount), 2); *BitCount = utmp2; +} + +/** + \brief Return parameters from a bitinfoheader. + All are returned as 32 bit integers, regardless of their internal representation. + + \param Bmih char * pointer to a U_BITMAPINFOHEADER. Note, data may not be properly aligned. + \param Size Structure size in bytes + \param Width Bitmap width in pixels + \param Height Bitmap height in pixels, may be negative. + \param Planes Planes (must be 1) + \param BitCount BitCount Enumeration (determines number of RBG colors) + \param Compression BI_Compression Enumeration + \param SizeImage Image size in bytes or 0 = "default size (calculated from geometry?)" + \param XPelsPerMeter X Resolution in pixels/meter + \param YPelsPerMeter Y Resolution in pixels/meter + \param ClrUsed Number of bmciColors in U_BITMAPINFO/U_BITMAPCOREINFO that are used by the bitmap + \param ClrImportant Number of bmciColors needed (0 means all). + + +*/ +void U_BITMAPINFOHEADER_get( + const char *Bmih, + uint32_t *Size, + int32_t *Width, + int32_t *Height, + uint32_t *Planes, + uint32_t *BitCount, + uint32_t *Compression, + uint32_t *SizeImage, + int32_t *XPelsPerMeter, + int32_t *YPelsPerMeter, + uint32_t *ClrUsed, + uint32_t *ClrImportant + ){ + int32_t tmp4; + uint32_t utmp4; + uint16_t utmp2; + + memcpy(&utmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biSize ), 4); *Size = utmp4; + memcpy( &tmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biWidth ), 4); *Width = tmp4; + memcpy( &tmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biHeight ), 4); *Height = tmp4; + memcpy(&utmp2, Bmih + offsetof(U_BITMAPINFOHEADER,biPlanes ), 2); *Planes = utmp2; + memcpy(&utmp2, Bmih + offsetof(U_BITMAPINFOHEADER,biBitCount ), 2); *BitCount = utmp2; + memcpy(&utmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biCompression ), 4); *Compression = utmp4; + memcpy(&utmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biSizeImage ), 4); *SizeImage = utmp4; + memcpy( &tmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biXPelsPerMeter), 4); *XPelsPerMeter = tmp4; + memcpy( &tmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biYPelsPerMeter), 4); *YPelsPerMeter = tmp4; + memcpy(&utmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biClrUsed ), 4); *ClrUsed = utmp4; + memcpy(&utmp4, Bmih + offsetof(U_BITMAPINFOHEADER,biClrImportant ), 4); *ClrImportant = utmp4; +} + +/** + \brief Assume a packed DIB and get the parameters from it, use by DBI_to_RGBA() + + \return BI_Compression Enumeration. For anything other than U_BI_RGB values other than px may not be valid. + \param dib pointer to the start of the DIB in the record + \param px pointer to DIB pixel array + \param ct pointer to DIB color table + \param numCt DIB color table number of entries, for PNG or JPG returns the number of bytes in the image + \param width Width of pixel array + \param height Height of pixel array (always returned as a positive number) + \param colortype DIB BitCount Enumeration + \param invert If DIB rows are in opposite order from RGBA rows +*/ +int wget_DIB_params( + const char *dib, + const char **px, + const U_RGBQUAD **ct, + int32_t *numCt, + int32_t *width, + int32_t *height, + int32_t *colortype, + int32_t *invert + ){ + uint32_t bic; + int32_t Size; + bic = U_BI_RGB; // this information is not in the coreheader; + U_BITMAPCOREHEADER_get(dib, &Size, width, height, colortype); + if(Size != 0xC ){ //BitmapCoreHeader + /* if biCompression is not U_BI_RGB some or all of the following might not hold real values. + Ignore most of the information returned from the bitmapinfoheader. + */ + uint32_t uig4; + int32_t ig4; + U_BITMAPINFOHEADER_get(dib, &uig4, width, height,&uig4, (uint32_t *) colortype, &bic, &uig4, &ig4, &ig4,&uig4, &uig4); + } + if(*height < 0){ + *height = -*height; + *invert = 1; + } + else { + *invert = 0; + } + *px = dib + U_SIZE_BITMAPINFOHEADER; + if(bic == U_BI_RGB){ + *numCt = get_real_color_count(dib); + if(*numCt){ + *ct = (PU_RGBQUAD) (dib + U_SIZE_BITMAPINFOHEADER); + *px += U_SIZE_COLORREF * (*numCt); + } + else { *ct = NULL; } + } + else { + memcpy(numCt, dib + offsetof(U_BITMAPINFOHEADER,biSizeImage), 4); + *ct = NULL; + } + return(bic); +} + + + +/* ********************************************************************************************** +These are the core WMR functions, each extracts data from a particular type of record. +In general routines fill in structures which have been passed in by the caller, and zero them +if that (optional) structure is not present. +Because the WMF records may not be aligned they are generally copied into the supplied + aligned structs, so that the caller may retrieve fields with the usual sorts of + structure operations: Struct.field or (*Struct)->field. +A few routines return pointers to data regions in the record. +They are listed in order by the corresponding U_WMR_* index number. +*********************************************************************************************** */ + +/** + \brief Get data from a (placeable) WMR_HEADER. + \return size of the record in bytes, 0 on failure + \param contents record to extract data from + \param blimit one byte past the last WMF record in memory. + \param Placeable U_WMRPLACEABLE data, if any + \param Header U_WMRHEADER data, if any +*/ +int wmfheader_get( + const char *contents, + const char *blimit, + PU_WMRPLACEABLE Placeable, + PU_WMRHEADER Header + ){ + uint32_t Key; + int size=0; + if(!Placeable || !Header || contents + 4 >= blimit)return(0); + memcpy(&Key, contents + offsetof(U_WMRPLACEABLE,Key), 4); + if(Key == 0x9AC6CDD7){ + size += U_SIZE_WMRPLACEABLE; + if(contents + size >= blimit)return(0); + memcpy(Placeable, contents, U_SIZE_WMRPLACEABLE); + contents += U_SIZE_WMRPLACEABLE; + } + else { + memset(Placeable, 0, U_SIZE_WMRPLACEABLE); + } + if(contents + size + U_SIZE_WMRHEADER >= blimit)return(0); + size += 2* (*(uint16_t *)(contents + offsetof(U_WMRHEADER,Size16w))); + if(contents + size >= blimit)return(0); + memcpy(Header, contents, U_SIZE_WMRHEADER); + return(size); +} + + +/** + \brief Get data from a U_WMREOF record + \return size of record in bytes, or 0 on error + \param contents record to extract data from +*/ +int U_WMREOF_get( + const char *contents + ){ + return(U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMREOF))); +} + +/** + \brief Retrieve values from a U_WMRSETBKCOLOR record + \return length of the U_WMRSETBKCOLOR record, or NULL on error + \param contents record to extract data from + \param Color Background Color. +*/ +int U_WMRSETBKCOLOR_get( + const char *contents, + PU_COLORREF Color + ){ + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRSETBKCOLOR)); + if(!size)return(0); + memcpy(Color,contents + offsetof(U_WMRSETBKCOLOR,Color),U_SIZE_COLORREF); + return(size); +} + +/** + \brief Retrieve values from a U_WMRSETBKMODE record + \return length of the U_WMRSETBKMODE record, or NULL on error + \param contents record to extract data from + \param Mode MixMode Enumeration +*/ +int U_WMRSETBKMODE_get( + const char *contents, + uint16_t *Mode + ){ + return(U_WMRCORE_1U16_get(contents, (U_SIZE_WMRSETBKMODE), Mode)); +} + +/** + \brief Retrieve values from a U_WMRSETMAPMODE record + \return length of the U_WMRSETMAPMODE record, or NULL on error + \param contents record to extract data from + \param Mode MapMode Enumeration +*/ +int U_WMRSETMAPMODE_get( + const char *contents, + uint16_t *Mode + ){ + return(U_WMRCORE_1U16_get(contents, (U_SIZE_WMRSETMAPMODE), Mode)); +} + +/** + \brief Retrieve values from a U_WMRSETROP2 record + \return length of the U_WMRSETROP2 record, or NULL on error + \param contents record to extract data from + \param Mode Binary Raster Operation Enumeration +*/ +int U_WMRSETROP2_get( + const char *contents, + uint16_t *Mode + ){ + return(U_WMRCORE_1U16_get(contents, (U_SIZE_WMRSETROP2), Mode)); +} + +/** + \brief Get data from a U_WMRSETRELABS record + \return length of the U_WMRSETRELABS record in bytes, or 0 on error + \param contents record to extract data from +*/ +int U_WMRSETRELABS_get( + const char *contents + ){ + return(U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRSETRELABS))); +} + +/** + \brief Retrieve values from a U_WMRSETPOLYFILLMODE record + \return length of the U_WMRSETPOLYFILLMODE record, or NULL on error + \param contents record to extract data from + \param Mode PolyFillMode Enumeration +*/ +int U_WMRSETPOLYFILLMODE_get( + const char *contents, + uint16_t *Mode + ){ + return(U_WMRCORE_1U16_get(contents, (U_SIZE_WMRSETPOLYFILLMODE), Mode)); +} + +/** + \brief Retrieve values from a U_WMRSETSTRETCHBLTMODE record + \return length of the U_WMRSETSTRETCHBLTMODE record, or NULL on error + \param contents record to extract data from + \param Mode StretchMode Enumeration +*/ +int U_WMRSETSTRETCHBLTMODE_get( + const char *contents, + uint16_t *Mode + ){ + return(U_WMRCORE_1U16_get(contents, (U_SIZE_WMRSETSTRETCHBLTMODE), Mode)); +} + +/** + \brief Retrieve values from a U_WMRSETTEXTCHAREXTRA record + \return length of the U_WMRSETTEXTCHAREXTRA record, or NULL on error + \param contents record to extract data from + \param Mode Extra space in logical units to add to each character +*/ +int U_WMRSETTEXTCHAREXTRA_get( + const char *contents, + uint16_t *Mode + ){ + return(U_WMRCORE_1U16_get(contents, (U_SIZE_WMRSETTEXTCHAREXTRA), Mode)); +} + +/** + \brief Retrieve values from a U_WMRSETTEXTCOLOR record + \return length of the U_WMRSETTEXTCOLOR record, or NULL on error + \param contents record to extract data from + \param Color Text Color. +*/ +int U_WMRSETTEXTCOLOR_get( + const char *contents, + PU_COLORREF Color + ){ + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRSETTEXTCOLOR)); + if(!size)return(0); + memcpy(Color,contents + offsetof(U_WMRSETTEXTCOLOR,Color),U_SIZE_COLORREF); + return(size); +} + +/** + \brief Retrieve values from a U_WMRSETTEXTJUSTIFICATION record + \return length of the U_WMRSETTEXTJUSTIFICATION record, or NULL on error + \param contents record to extract data from + \param Count Number of space characters in the line. + \param Extra Number of extra space characters to add to the line. +*/ +int U_WMRSETTEXTJUSTIFICATION_get( + const char *contents, + uint16_t *Count, + uint16_t *Extra + ){ + return(U_WMRCORE_2U16_get(contents, (U_SIZE_WMRSETTEXTJUSTIFICATION), Count, Extra)); +} + +/** + \brief Retrieve values from a U_WMRSETWINDOWORG record + \return length of the U_WMRSETWINDOWORG record, or NULL on error + \param contents record to extract data from + \param coord Window Origin. +*/ +int U_WMRSETWINDOWORG_get( + const char *contents, + PU_POINT16 coord + ){ + return(U_WMRCORE_2U16_get(contents, (U_SIZE_WMRSETWINDOWORG), U_P16(coord->y), U_P16(coord->x))); +} + +/** + \brief Retrieve values from a U_WMRSETWINDOWEXT record + \return length of the U_WMRSETWINDOWEXT record, or NULL on error + \param contents record to extract data from + \param extent Window Extent. +*/ +int U_WMRSETWINDOWEXT_get( + const char *contents, + PU_POINT16 extent + ){ + return(U_WMRCORE_2U16_get(contents, (U_SIZE_WMRSETWINDOWEXT), U_P16(extent->y), U_P16(extent->x))); +} + +/** + \brief Retrieve values from a U_WMRSETVIEWPORTORG record + \return length of the U_WMRSETVIEWPORTORG record, or NULL on error + \param contents record to extract data from + \param coord Viewport Origin. +*/ +int U_WMRSETVIEWPORTORG_get( + const char *contents, + PU_POINT16 coord + ){ + return(U_WMRCORE_2U16_get(contents, (U_SIZE_WMRSETVIEWPORTORG), U_P16(coord->y), U_P16(coord->x))); + +} + +/** + \brief Retrieve values from a U_WMRSETVIEWPORTEXT record + \return length of the U_WMRSETVIEWPORTEXT record, or NULL on error + \param contents record to extract data from + \param extent Viewport Extent. +*/ +int U_WMRSETVIEWPORTEXT_get( + const char *contents, + PU_POINT16 extent + ){ + return(U_WMRCORE_2U16_get(contents, (U_SIZE_WMRSETVIEWPORTEXT), U_P16(extent->y), U_P16(extent->x))); +} + +/** + \brief Retrieve values from a U_WMROFFSETWINDOWORG record + \return length of the U_WMROFFSETWINDOWORG record, or NULL on error + \param contents record to extract data from + \param offset Window offset in device units. +*/ +int U_WMROFFSETWINDOWORG_get( + const char *contents, + PU_POINT16 offset + ){ + return(U_WMRCORE_2U16_get(contents, (U_SIZE_WMROFFSETWINDOWORG), U_P16(offset->y), U_P16(offset->x))); +} + +/** + \brief Retrieve values from a U_WMRSCALEWINDOWEXT record + \return length of the U_WMRSCALEWINDOWEXT record, or NULL on error + \param contents record to extract data from + \param Denom {X,Y} denominators. + \param Num {X,Y} numerators. +*/ +int U_WMRSCALEWINDOWEXT_get( + const char *contents, + PU_POINT16 Denom, + PU_POINT16 Num + ){ + return(U_WMRCORE_4U16_get(contents, (U_SIZE_WMRSCALEWINDOWEXT), U_P16(Denom->y), U_P16(Denom->x), U_P16(Num->y), U_P16(Num->x))); +} + +/** + \brief Retrieve values from a U_WMROFFSETVIEWPORTORG record + \return length of the U_WMROFFSETVIEWPORTORG record, or NULL on error + \param contents record to extract data from + \param offset Viewport offset in device units. +*/ +int U_WMROFFSETVIEWPORTORG_get( + const char *contents, + PU_POINT16 offset + ){ + return(U_WMRCORE_2U16_get(contents, (U_SIZE_WMROFFSETVIEWPORTORG), U_P16(offset->y), U_P16(offset->x))); +} + +/** + \brief Retrieve values from a U_WMRSCALEVIEWPORTEXT record + \return length of the U_WMRSCALEVIEWPORTEXT record, or NULL on error + \param contents record to extract data from + \param Denom {X,Y} denominators. + \param Num {X,Y} numerators. +*/ +int U_WMRSCALEVIEWPORTEXT_get( + const char *contents, + PU_POINT16 Denom, + PU_POINT16 Num + ){ + return(U_WMRCORE_4U16_get(contents, (U_SIZE_WMRSCALEVIEWPORTEXT), U_P16(Denom->y), U_P16(Denom->x), U_P16(Num->y), U_P16(Num->x))); +} + +/** + \brief Retrieve values from a U_WMRLINETO record + \return length of the U_WMRLINETO record, or NULL on error + \param contents record to extract data from + \param coord Draw line to {X,Y}. +*/ +int U_WMRLINETO_get( + const char *contents, + PU_POINT16 coord + ){ + return(U_WMRCORE_2U16_get(contents, (U_SIZE_WMRLINETO), U_P16(coord->y), U_P16(coord->x))); +} + +/** + \brief Retrieve values from a U_WMRMOVETO record + \return length of the U_WMRMOVETO record, or NULL on error + \param contents record to extract data from + \param coord Move to {X,Y}. +*/ +int U_WMRMOVETO_get( + const char *contents, + PU_POINT16 coord + ){ + return(U_WMRCORE_2U16_get(contents, (U_SIZE_WMRMOVETO), U_P16(coord->y), U_P16(coord->x))); +} + +/** + \brief Retrieve values from a U_WMREXCLUDECLIPRECT record + \return length of the U_WMREXCLUDECLIPRECT record, or NULL on error + \param contents record to extract data from + \param rect Exclude rect from clipping region. +*/ +int U_WMREXCLUDECLIPRECT_get( + const char *contents, + PU_RECT16 rect + ){ + return(U_WMRCORE_4U16_get(contents, (U_SIZE_WMREXCLUDECLIPRECT), U_P16(rect->bottom), U_P16(rect->right), U_P16(rect->top), U_P16(rect->left))); +} + +/** + \brief Retrieve values from a U_WMRINTERSECTCLIPRECT record + \return length of the U_WMRINTERSECTCLIPRECT record, or NULL on error + \param contents record to extract data from + \param rect Clipping region is intersection of existing clipping region with rect. +*/ +int U_WMRINTERSECTCLIPRECT_get( + const char *contents, + PU_RECT16 rect + ){ + return(U_WMRCORE_4U16_get(contents, (U_SIZE_WMRINTERSECTCLIPRECT), U_P16(rect->bottom), U_P16(rect->right), U_P16(rect->top), U_P16(rect->left))); +} + +/** + \brief Retrieve values from a U_WMRARC record + \return length of the U_WMRARC record, or NULL on error + \param contents record to extract data from + \param StartArc Start of Arc + \param EndArc End of Arc + \param rect Bounding rectangle. +*/ +int U_WMRARC_get( + const char *contents, + PU_POINT16 StartArc, + PU_POINT16 EndArc, + PU_RECT16 rect + ){ + return U_WMRCORE_8U16_get( + contents, + (U_SIZE_WMRARC), + U_P16(EndArc->y), + U_P16(EndArc->x), + U_P16(StartArc->y), + U_P16(StartArc->x), + U_P16(rect->bottom), + U_P16(rect->right), + U_P16(rect->top), + U_P16(rect->left) + ); +} + +/** + \brief Retrieve values from a U_WMRELLIPSE record + \return length of the U_WMRELLIPSE record, or NULL on error + \param contents record to extract data from + \param rect Bounding rectangle for Ellipse. +*/ +int U_WMRELLIPSE_get( + const char *contents, + PU_RECT16 rect + ){ + return U_WMRCORE_4U16_get( + contents, + (U_SIZE_WMRELLIPSE), + U_P16(rect->bottom), + U_P16(rect->right), + U_P16(rect->top), + U_P16(rect->left) + ); +} + +/** + \brief Retrieve values from a U_WMRFLOODFILL record + \return length of the U_WMRFLOODFILL record, or NULL on error + \param contents record to extract data from + \param Mode FloodFill Enumeration. + \param Color Color to Fill with. + \param coord Location to start fill. +*/ +int U_WMRFLOODFILL_get( + const char *contents, + uint16_t *Mode, + PU_COLORREF Color, + PU_POINT16 coord + ){ + return U_WMRCORE_1U16_CRF_2U16_get( + contents, + (U_SIZE_WMRFLOODFILL), + Mode, + Color, + U_P16(coord->y), + U_P16(coord->x) + ); +} + +/** + \brief Retrieve values from a U_WMRPIE record + \return length of the U_WMRPIE record, or NULL on error + \param contents record to extract data from + \param Radial1 Start of Pie + \param Radial2 End of Pie + \param rect Bounding rectangle. +*/ +int U_WMRPIE_get( + const char *contents, + PU_POINT16 Radial1, + PU_POINT16 Radial2, + PU_RECT16 rect + ){ + return U_WMRCORE_8U16_get( + contents, + (U_SIZE_WMRPIE), + U_P16(Radial2->y), + U_P16(Radial2->x), + U_P16(Radial1->y), + U_P16(Radial1->x), + U_P16(rect->bottom), + U_P16(rect->right), + U_P16(rect->top), + U_P16(rect->left) + ); +} + +/** + \brief Retrieve values from a U_WMRRECTANGLE record + \return length of the U_WMRRECTANGLE record, or NULL on error + \param contents record to extract data from + \param rect Boundaries. +*/ +int U_WMRRECTANGLE_get( + const char *contents, + PU_RECT16 rect + ){ + return U_WMRCORE_4U16_get( + contents, + (U_SIZE_WMRRECTANGLE), + U_P16(rect->bottom), + U_P16(rect->right), + U_P16(rect->top), + U_P16(rect->left) + ); +} + +/** + \brief Retrieve values from a U_WMRROUNDRECT record + \return length of the U_WMRROUNDRECT record, or NULL on error + \param contents record to extract data from + \param Width Horizontal rounding length. + \param Height Vertical rounding length. + \param rect Boundaries. +*/ +int U_WMRROUNDRECT_get( + const char *contents, + int16_t *Width, + int16_t *Height, + PU_RECT16 rect + ){ + return U_WMRCORE_6U16_get( + contents, + (U_SIZE_WMRROUNDRECT), + U_PP16(Height), + U_PP16(Width), + U_P16(rect->bottom), + U_P16(rect->right), + U_P16(rect->top), + U_P16(rect->left) + ); +} + +/** + \brief Get data from a U_WMRPATBLT record. + \return length of the U_WMRPATBLT record in bytes, or 0 on error + \param contents record to extract data from + \param Dst Destination UL corner in logical units + \param cwh W & H for Dst and Src in logical units + \param dwRop3 RasterOPeration Enumeration +*/ +int U_WMRPATBLT_get( + const char *contents, + PU_POINT16 Dst, + PU_POINT16 cwh, + uint32_t *dwRop3 + ){ + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRPATBLT)); + if(!size)return(0); + memcpy(dwRop3, ( contents + offsetof(U_WMRPATBLT, rop3w)), 4); + cwh->y = *(int16_t *)( contents + offsetof(U_WMRPATBLT, Height )); + cwh->x = *(int16_t *)( contents + offsetof(U_WMRPATBLT, Width )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRPATBLT, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRPATBLT, xDst )); + return(size); +} + +/** + \brief Get data from a U_WMRSAVEDC record + \return length of the U_WMRSAVEDC record in bytes, or 0 on error + \param contents record to extract data from +*/ +int U_WMRSAVEDC_get( + const char *contents + ){ + return(U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRSAVEDC))); +} + +int U_WMRSETPIXEL_get( + const char *contents, + PU_COLORREF Color, + PU_POINT16 coord){ + return U_WMRCORE_1U16_CRF_2U16_get( + contents, + (U_SIZE_WMRSETPIXEL), + NULL, + Color, + U_P16(coord->y), + U_P16(coord->x) + ); +} + +int U_WMROFFSETCLIPRGN_get( + const char *contents, + PU_POINT16 offset + ){ + return U_WMRCORE_2U16_get(contents, (U_SIZE_WMROFFSETCLIPRGN), U_P16(offset->y), U_P16(offset->x)); +} + +/** + \brief Get data from a U_WMRTEXTOUT record + \return length of the U_WMRTEXTOUT record in bytes, or 0 on error + \param contents record to extract data from + \param Dst coordinates where text will be written + \param Length Number of characters in string. + \param string Pointer to string in WMF buffer in memory. This text is generally NOT null terminated!!! +*/ +int U_WMRTEXTOUT_get( + const char *contents, + PU_POINT16 Dst, + int16_t *Length, + const char **string + ){ + int16_t L2; + int off; + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRPATBLT)); + if(!size)return(0); + *Length = *(int16_t *)(contents + offsetof(U_WMRTEXTOUT, Length)); + *string = contents + offsetof(U_WMRTEXTOUT, String); /* May not be null terminated!!! */ + L2 = *Length; + if(L2 & 1)L2++; + off = U_SIZE_METARECORD + 2 + L2; + memcpy(&Dst->y, contents + off, 2); off+=2; + memcpy(&Dst->x, contents + off, 2); off+=2; + return(size); +} + +/** + \brief Get data from a U_WMRBITBLT record. + Note that unlike U_EMRBITBLT there is no scaling available - the Src and Dst + rectangles must be the same size. + \return length of the U_WMRBITBLT record in bytes, or 0 on error + \param contents record to extract data from + \param Dst Destination UL corner in logical units + \param cwh W & H for Dst and Src in logical units + \param Src Source UL corner in logical units + \param dwRop3 RasterOPeration Enumeration + \param Bm16 bitmap16 object (fields in it are all 0 if no bitmap is used) + \param px pointer to bitmap in memory, or NULL if not used +*/ +int U_WMRBITBLT_get( + const char *contents, + PU_POINT16 Dst, + PU_POINT16 cwh, + PU_POINT16 Src, + uint32_t *dwRop3, + PU_BITMAP16 Bm16, + const char **px + ){ + uint8_t xb; + uint32_t size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRBITBLT_NOPX)); + if(!size)return(0); + xb = *(uint8_t *)( contents + offsetof(U_METARECORD, xb)); + if(U_TEST_NOPXB(size,xb)){ /* no bitmap */ + memcpy(dwRop3, ( contents + offsetof(U_WMRBITBLT_NOPX, rop3w)), 4); + Src->y = *(int16_t *)( contents + offsetof(U_WMRBITBLT_NOPX, ySrc )); + Src->x = *(int16_t *)( contents + offsetof(U_WMRBITBLT_NOPX, xSrc )); + cwh->y = *(int16_t *)( contents + offsetof(U_WMRBITBLT_NOPX, Height )); + cwh->x = *(int16_t *)( contents + offsetof(U_WMRBITBLT_NOPX, Width )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRBITBLT_NOPX, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRBITBLT_NOPX, xDst )); + memset(Bm16, 0, U_SIZE_BITMAP16); + *px = NULL; + } + else { /* yes bitmap */ + memcpy(dwRop3, ( contents + offsetof(U_WMRBITBLT_PX, rop3w)), 4); + Src->y = *(int16_t *)( contents + offsetof(U_WMRBITBLT_PX, ySrc )); + Src->x = *(int16_t *)( contents + offsetof(U_WMRBITBLT_PX, xSrc )); + cwh->y = *(int16_t *)( contents + offsetof(U_WMRBITBLT_PX, Height )); + cwh->x = *(int16_t *)( contents + offsetof(U_WMRBITBLT_PX, Width )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRBITBLT_PX, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRBITBLT_PX, xDst )); + memcpy(Bm16, ( contents + offsetof(U_WMRBITBLT_PX, bitmap)), U_SIZE_BITMAP16); + *px = ( contents + offsetof(U_WMRBITBLT_PX, bitmap) + U_SIZE_BITMAP16); + } + return(size); +} + +/** + \brief Get data from a U_WMRSTRETCHBLT record. + \return length of the U_WMRSTRETCHBLT record in bytes, or 0 on error + \param contents record to extract data from + \param Dst Destination UL corner in logical units + \param cDst Destination W & H in logical units + \param Src Source UL corner in logical units + \param cSrc Source W & H in logical units + \param dwRop3 RasterOPeration Enumeration + \param Bm16 bitmap16 object (fields in it are all 0 if no bitmap is used) + \param px pointer to bitmap in memory, or NULL if not used +*/ +int U_WMRSTRETCHBLT_get( + const char *contents, + PU_POINT16 Dst, + PU_POINT16 cDst, + PU_POINT16 Src, + PU_POINT16 cSrc, + uint32_t *dwRop3, + PU_BITMAP16 Bm16, + const char **px + ){ + uint8_t xb; + uint32_t size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRSTRETCHBLT_NOPX)); + if(!size)return(0); + xb = *(uint8_t *)( contents + offsetof(U_METARECORD, xb)); + if(U_TEST_NOPXB(size,xb)){ /* no bitmap */ + memcpy(dwRop3, ( contents + offsetof(U_WMRSTRETCHBLT_NOPX, rop3w)), 4); + cSrc->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_NOPX, hSrc )); + cSrc->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_NOPX, wSrc )); + Src->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_NOPX, ySrc )); + Src->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_NOPX, xSrc )); + cDst->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_NOPX, hDst )); + cDst->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_NOPX, wDst )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_NOPX, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_NOPX, xDst )); + memset(Bm16, 0, U_SIZE_BITMAP16); + *px = NULL; + } + else { /* yes bitmap */ + memcpy(dwRop3, ( contents + offsetof(U_WMRSTRETCHBLT_PX, rop3w)), 4); + cSrc->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_PX, hSrc )); + cSrc->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_PX, wSrc )); + Src->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_PX, ySrc )); + Src->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_PX, xSrc )); + cDst->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_PX, hDst )); + cDst->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_PX, wDst )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_PX, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHBLT_PX, xDst )); + memcpy(Bm16, ( contents + offsetof(U_WMRSTRETCHBLT_PX, bitmap)), U_SIZE_BITMAP16); + *px = ( contents + offsetof(U_WMRSTRETCHBLT_PX, bitmap) + U_SIZE_BITMAP16); + } + return(size); +} + +/** + \brief Get data from a U_WMRPOLYGON record. + \return length of the U_WMRPOLYGON record in bytes, or 0 on error + \param contents record to extract data from + \param Length Number of points in the Polygon + \param Data pointer to array of U_POINT16 in memory. Pointer may not be aligned properly for structures. +*/ +int U_WMRPOLYGON_get( + const char *contents, + uint16_t *Length, + const char **Data + ){ + return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRPOLYGON), NULL, Length, Data); +} + +/** + \brief Get data from a U_WMRPOLYLINE record. + \return length of the U_WMRPOLYLINE record in bytes, or 0 on error + \param contents record to extract data from + \param Length Number of points in the Polyline + \param Data pointer to array of U_POINT16 in memory. Pointer may not be aligned properly for structures. +*/ +int U_WMRPOLYLINE_get( + const char *contents, + uint16_t *Length, + const char **Data + ){ + return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRPOLYLINE), NULL, Length, Data); +} + +/** + \brief Get data from a U_WMRESCAPE record. + WARNING! Only three Escape record types are fully supported: SETLINECAP, SETLINEJOIN, SETMITERLIMIT. + Even these should not be set here directly, instead use the wsetlinecap_get(), wsetlinejoin_get(), + or wsetmiterlimit_get() functions. + Escape records created with this function, with the exception of the three named above, will not have + the byte orders in Data adjusted automatically. The user code must set Data to be little endian no + matter what the endianness of the current platform where the user code is running. + \return length of the U_WMRESCAPE record in bytes, or 0 on error + \param contents record to extract data from + \param Escape Escape function + \param Length Bytes in the Data + \param Data Array of Length bytes +*/ +int U_WMRESCAPE_get( + const char *contents, + uint16_t *Escape, + uint16_t *Length, + const char **Data + ){ + return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRESCAPE), Escape, Length, Data); +} + +/** + \brief Get data from a U_WMRRESTOREDC record + \return length of the U_WMRRESTOREDC record in bytes, or 0 on error + \param contents record to extract data from + \param DC DC to restore (relative if negative, absolute if positive) +*/ +int U_WMRRESTOREDC_get( + const char *contents, + int16_t *DC + ){ + return U_WMRCORE_1U16_get(contents, (U_SIZE_WMRRESTOREDC), (uint16_t *)DC); // signed, but it is just a memcpy, so this works +} + +/** + \brief Get data from a U_WMRFILLREGION record. + \return length of the U_WMRFILLREGION record in bytes, or 0 on error + \param contents record to extract data from + \param Region Region to fill + \param Brush Brush to fill with +*/ +int U_WMRFILLREGION_get( + const char *contents, + uint16_t *Region, + uint16_t *Brush + ){ + return U_WMRCORE_2U16_get(contents, (U_SIZE_WMRFILLREGION), Region, Brush); +} + +/** + \brief Get data from a U_WMRFRAMEREGION record. + \return length of the U_WMRFRAMEREGION record in bytes, or 0 on error + \param contents record to extract data from + \param Region Index of region to frame in object table + \param Brush Index of brush to use in frame in object table + \param Height in logical units (of frame) + \param Width in logical units (of frame) +*/ +int U_WMRFRAMEREGION_get( + const char *contents, + uint16_t *Region, + uint16_t *Brush, + int16_t *Height, + int16_t *Width + ){ + return U_WMRCORE_4U16_get(contents, (U_SIZE_WMRFRAMEREGION), Region, Brush, U_PP16(Height), U_PP16(Width)); +} + +/** + \brief Get data from a U_WMRINVERTREGION record. + \return length of the U_WMRINVERTREGION record in bytes, or 0 on error + \param contents record to extract data from + \param Region Index of region to invert. +*/ +int U_WMRINVERTREGION_get( + const char *contents, + uint16_t *Region + ){ + return U_WMRCORE_1U16_get(contents, (U_SIZE_WMRINVERTREGION), Region); +} + +/** + \brief Get data from a U_WMRPAINTREGION record. + \return length of the U_WMRPAINTREGION record in bytes, or 0 on error + \param contents record to extract data from + \param Region Index of region to paint with the current Brush. +*/ +int U_WMRPAINTREGION_get( + const char *contents, + uint16_t *Region + ){ + return U_WMRCORE_1U16_get(contents, (U_SIZE_WMRPAINTREGION), Region); +} + +/** + \brief Get data from a U_WMRSELECTCLIPREGION record. + \return length of the U_WMRSELECTCLIPREGION record in bytes, or 0 on error + \param contents record to extract data from + \param Region Index of region to become clipping region.. +*/ +int U_WMRSELECTCLIPREGION_get( + const char *contents, + uint16_t *Region + ){ + return U_WMRCORE_1U16_get(contents, (U_SIZE_WMRSELECTCLIPREGION), Region); + +} + +/** + \brief Get data from a U_WMRSELECTOBJECT record. + \return length of the U_WMRSELECTOBJECT record in bytes, or 0 on error + \param contents record to extract data from + \param Object Index of object which is made active. +*/ +int U_WMRSELECTOBJECT_get( + const char *contents, + uint16_t *Object + ){ + return U_WMRCORE_1U16_get(contents, (U_SIZE_WMRSELECTOBJECT), Object); +} + +/** + \brief Get data from a U_WMRSETTEXTALIGN record. + \return length of the U_WMRSETTEXTALIGN record in bytes, or 0 on error + \param contents record to extract data from + \param Mode TextAlignment Enumeration. +*/ +int U_WMRSETTEXTALIGN_get( + const char *contents, + uint16_t *Mode + ){ + return U_WMRCORE_1U16_get(contents, (U_SIZE_WMRSETTEXTALIGN), Mode); +} + +/* in Wine, not in WMF PDF. */ +int U_WMRDRAWTEXT_get(void){ /* in Wine, not in WMF PDF. */ + return U_WMRCORENONE_get("U_WMRDRAWTEXT"); +} + +/** + \brief Retrieve values from a U_WMRCHORD record + \return length of the U_WMRCHORD record, or NULL on error + \param contents record to extract data from + \param Radial1 Start of Chord + \param Radial2 End of Chord + \param rect Bounding rectangle. +*/ +int U_WMRCHORD_get( + const char *contents, + PU_POINT16 Radial1, + PU_POINT16 Radial2, + PU_RECT16 rect + ){ + return U_WMRCORE_8U16_get( + contents, + (U_SIZE_WMRCHORD), + U_P16(Radial2->y), + U_P16(Radial2->x), + U_P16(Radial1->y), + U_P16(Radial1->x), + U_P16(rect->bottom), + U_P16(rect->right), + U_P16(rect->top), + U_P16(rect->left) + ); +} + +/** + \brief Get data from a U_WMRSETMAPPERFLAGS record. + \return length of the U_WMRSETMAPPERFLAGS record in bytes, or 0 on error + \param contents record to extract data from + \param Mode If 1 bit set font mapper selects only matching aspect fonts. +*/ +int U_WMRSETMAPPERFLAGS_get( + const char *contents, + uint32_t *Mode + ){ + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRSETMAPPERFLAGS)); + if(!size)return(0); + memcpy(Mode, contents + U_SIZE_METARECORD, 4); + return(size); +} + +/** + \brief Get data from a U_WMREXTTEXTOUT record. + \return length of the U_WMREXTTEXTOUT record in bytes, or 0 on error + \param contents record to extract data from + \param Dst {X,Y} coordinates where the string is to be written. + \param Length Stringlength in bytes + \param Opts ExtTextOutOptions Flags + \param string String to write (Latin1 encoding) + \param dx Kerning information. Must have same number of entries as Length. + \param rect Used when when U_ETO_OPAQUE or U_ETO_CLIPPED bits are set in Opts +*/ +int U_WMREXTTEXTOUT_get( + const char *contents, + PU_POINT16 Dst, + int16_t *Length, + uint16_t *Opts, + const char **string, + const int16_t **dx, + PU_RECT16 rect + ){ + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMREXTTEXTOUT)); + int off = U_SIZE_METARECORD; + if(!size)return(0); + Dst->y = *(int16_t *)( contents + offsetof(U_WMREXTTEXTOUT, y )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMREXTTEXTOUT, x )); + *Length = *(int16_t *)( contents + offsetof(U_WMREXTTEXTOUT, Length )); + *Opts = *(uint16_t *)(contents + offsetof(U_WMREXTTEXTOUT, Opts )); + off = U_SIZE_WMREXTTEXTOUT; + if(*Opts & (U_ETO_OPAQUE | U_ETO_CLIPPED)){ memcpy(rect, (contents + off), U_SIZE_RECT16); off += U_SIZE_RECT16; } + else { memset(rect, 0, U_SIZE_RECT16); } + *string = (contents + off); + off += 2*((*Length +1)/2); + if(*Length){ *dx = (int16_t *)(contents + off); } + else { *dx = NULL; } + return(size); +} + +/** + \brief Get data from a U_WMRSETDIBTODEV record + \return length of the U_WMRSETDIBTODEV record in bytes, or 0 on error + \param contents record to extract data from + \param Dst UL corner of Dst rect in logical units + \param cwh Width and Height in logical units + \param Src UL corner of Src rect in logical units + \param cUsage ColorUsage enumeration + \param ScanCount Number of scan lines in Src + \param StartScan First Scan line in Src + \param dib DeviceIndependentBitmap object +*/ +int U_WMRSETDIBTODEV_get( + const char *contents, + PU_POINT16 Dst, + PU_POINT16 cwh, + PU_POINT16 Src, + uint16_t *cUsage, + uint16_t *ScanCount, + uint16_t *StartScan, + const char **dib + ){ + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRSETDIBTODEV)); + if(!size)return(0); + *cUsage = *(uint16_t *)(contents + offsetof(U_WMRSETDIBTODEV, cUsage )); + *ScanCount = *(uint16_t *)(contents + offsetof(U_WMRSETDIBTODEV, ScanCount )); + *StartScan = *(uint16_t *)(contents + offsetof(U_WMRSETDIBTODEV, StartScan )); + Src->y = *(int16_t *)( contents + offsetof(U_WMRSETDIBTODEV, ySrc )); + Src->x = *(int16_t *)( contents + offsetof(U_WMRSETDIBTODEV, xSrc )); + cwh->y = *(int16_t *)( contents + offsetof(U_WMRSETDIBTODEV, Height )); + cwh->x = *(int16_t *)( contents + offsetof(U_WMRSETDIBTODEV, Width )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRSETDIBTODEV, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRSETDIBTODEV, xDst )); + *dib = ( contents + offsetof(U_WMRSETDIBTODEV, dib )); + return(size); +} + +/** + \brief Get data from a U_WMRSELECTPALETTE record + \return length of the U_WMRSELECTPALETTE record in bytes, or 0 on error + \param contents record to extract data from + \param Palette Index of Palette to make active. +*/ +int U_WMRSELECTPALETTE_get( + const char *contents, + uint16_t *Palette + ){ + return U_WMRCORE_1U16_get(contents, (U_SIZE_WMRSELECTPALETTE), Palette); +} + +/** + \brief Get data from a U_WMRREALIZEPALETTE record + \return length of the U_WMRREALIZEPALETTE record in bytes, or 0 on error + \param contents record to extract data from +*/ +int U_WMRREALIZEPALETTE_get( + const char *contents + ){ + return U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRREALIZEPALETTE)); +} + +/** + \brief Get data from a U_WMRSETPALENTRIES record + \return length of the U_WMRSETPALENTRIES record in bytes, or 0 on error + \param contents record to extract data from + \param Palette Redefines a set of RGB values for the current active Palette. + \param PalEntries Array of Palette Entries +*/ +int U_WMRANIMATEPALETTE_get( + const char *contents, + PU_PALETTE Palette, + const char **PalEntries + ){ + return U_WMRCORE_PALETTE_get(contents, (U_SIZE_WMRANIMATEPALETTE), Palette, PalEntries); +} + +/** + \brief Get data from a U_WMRSETPALENTRIES record + \return length of the U_WMRSETPALENTRIES record in bytes, or 0 on error + \param contents record to extract data from + \param Palette Defines a set of RGB values for the current active Palette. + \param PalEntries Array of Palette Entries +*/ +int U_WMRSETPALENTRIES_get( + const char *contents, + PU_PALETTE Palette, + const char **PalEntries + ){ + return U_WMRCORE_PALETTE_get(contents, (U_SIZE_WMRSETPALENTRIES), Palette, PalEntries); +} + +/** + \brief Get data from a U_WMR_POLYPOLYGON record. + \return length of the U_WMR_POLYPOLYGON record in bytes, or 0 on error + \param contents record to extract data from + \param nPolys Number of elements in aPolyCounts + \param aPolyCounts Number of points in each poly (sequential) + \param Points pointer to array of U_POINT16 in memory. Probably not aligned. +*/ +int U_WMRPOLYPOLYGON_get( + const char *contents, + uint16_t *nPolys, + const uint16_t **aPolyCounts, + const char **Points + ){ + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRPOLYPOLYGON)); + if(!size)return(0); + contents += offsetof(U_WMRPOLYPOLYGON, PPolygon); + memcpy(nPolys, contents + offsetof(U_POLYPOLYGON, nPolys), 2); + *aPolyCounts = (uint16_t *)(contents + offsetof(U_POLYPOLYGON, aPolyCounts)); + *Points = (contents + offsetof(U_POLYPOLYGON, aPolyCounts) + *nPolys*2); + return(size); +} + +/** + \brief Get data from a U_WMRRESIZEPALETTE record + \return length of the U_WMRRESIZEPALETTE record in bytes, or 0 on error + \param contents record to extract data from + \param Palette Changes the size of the currently active Palette. +*/ +int U_WMRRESIZEPALETTE_get( + const char *contents, + uint16_t *Palette + ){ + return U_WMRCORE_1U16_get(contents, (U_SIZE_WMRRESIZEPALETTE), Palette); +} + +int U_WMR3A_get(void){ + return U_WMRCORENONE_get("U_WMR3A"); +} + +int U_WMR3B_get(void){ + return U_WMRCORENONE_get("U_WMR3B"); +} + +int U_WMR3C_get(void){ + return U_WMRCORENONE_get("U_WMR3C"); +} + +int U_WMR3D_get(void){ + return U_WMRCORENONE_get("U_WMR3D"); +} + +int U_WMR3E_get(void){ + return U_WMRCORENONE_get("U_WMR3E"); +} + +int U_WMR3F_get(void){ + return U_WMRCORENONE_get("U_WMR3F"); +} + +// U_WMRDIBBITBLT_get +/** + \brief Get data from a U_WMRDIBITBLT record. + \return length of the U_WMRDIBITBLT record in bytes, or 0 on error + \param contents record to extract data from + \param Dst Destination UL corner in logical units + \param Src Source UL corner in logical units + \param cwh W & H in logical units of Src and Dst + \param dwRop3 RasterOPeration Enumeration + \param dib pointer to dib in WMF in memory. Most likely not aligned. +*/ +int U_WMRDIBBITBLT_get( + const char *contents, + PU_POINT16 Dst, + PU_POINT16 cwh, + PU_POINT16 Src, + uint32_t *dwRop3, + const char **dib + ){ + uint8_t xb; + uint32_t size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRDIBBITBLT_NOPX)); + if(!size)return(0); + xb = *(uint8_t *)( contents + offsetof(U_METARECORD, xb)); + if(U_TEST_NOPXB(size,xb)){ /* no bitmap */ + memcpy(dwRop3, ( contents + offsetof(U_WMRDIBBITBLT_NOPX, rop3w)), 4); + Src->y = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_NOPX, ySrc )); + Src->x = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_NOPX, xSrc )); + cwh->y = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_NOPX, Height )); + cwh->x = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_NOPX, Width )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_NOPX, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_NOPX, xDst )); + *dib = NULL; + } + else { /* yes bitmap */ + memcpy(dwRop3, ( contents + offsetof(U_WMRDIBBITBLT_PX, rop3w)), 4); + Src->y = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_PX, ySrc )); + Src->x = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_PX, xSrc )); + cwh->y = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_PX, Height )); + cwh->x = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_PX, Width )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_PX, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRDIBBITBLT_PX, xDst )); + *dib = ( contents + offsetof(U_WMRDIBBITBLT_PX, dib )); + } + return(size); +} + +/** + \brief Get data from a U_WMRSTRETCHDIB record. + \return length of the U_WMRSTRETCHDIB record in bytes, or 0 on error + \param contents record to extract data from + \param Dst Destination UL corner in logical units + \param cDst Destination W & H in logical units + \param Src Source UL corner in logical units + \param cSrc Source W & H in logical units + \param dwRop3 RasterOPeration Enumeration + \param dib pointer to dib in WMF in memory. Most likely not aligned. +*/ +int U_WMRDIBSTRETCHBLT_get( + const char *contents, + PU_POINT16 Dst, + PU_POINT16 cDst, + PU_POINT16 Src, + PU_POINT16 cSrc, + uint32_t *dwRop3, + const char **dib + ){ + uint8_t xb; + uint32_t size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRDIBSTRETCHBLT_NOPX)); + if(!size)return(0); + xb = *(uint8_t *)( contents + offsetof(U_METARECORD, xb)); + if(U_TEST_NOPXB(size,xb)){ /* no bitmap */ + memcpy(dwRop3 , ( contents + offsetof(U_WMRDIBSTRETCHBLT_NOPX, rop3w)), 4); + Src->y = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_NOPX, ySrc )); + Src->x = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_NOPX, xSrc )); + cSrc->y = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_NOPX, hSrc )); + cSrc->x = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_NOPX, wSrc )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_NOPX, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_NOPX, xDst )); + cDst->y = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_NOPX, hDst )); + cDst->x = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_NOPX, wDst )); + *dib = NULL; + } + else { /* yes bitmap */ + memcpy(dwRop3 , ( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, rop3w)), 4); + Src->y = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, ySrc )); + Src->x = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, xSrc )); + cSrc->y = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, hSrc )); + cSrc->x = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, wSrc )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, xDst )); + cDst->y = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, hDst )); + cDst->x = *(int16_t *)( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, wDst )); + *dib = ( contents + offsetof(U_WMRDIBSTRETCHBLT_PX, dib )); + } + return(size); +} + +/** + \brief Get data from a U_WMRDIBCREATEPATTERNBRUSH record. + Returns an image as either a DIB (Bmi/CbPx/Px defined) or a Bitmap16 (Bm16 defined). + \return length of the U_WMRDIBCREATEPATTERNBRUSH record in bytes, or 0 on error + \param contents record to extract data from + \param Style BrushStyle Enumeration + \param cUsage DIBcolors Enumeration + \param Bm16 pointer to a U_BITMAP16 in WMF in memory. Most likely not aligned. NULL if dib is used instead. + \param dib pointer to a dib in WMF in memory. Most likely not aligned. NULL if Bm16 is used instead. + */ +int U_WMRDIBCREATEPATTERNBRUSH_get( + const char *contents, + uint16_t *Style, + uint16_t *cUsage, + const char **Bm16, + const char **dib + ){ + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRDIBCREATEPATTERNBRUSH)); + if(!size)return(0); + + *Style = *(uint16_t *)(contents + offsetof(U_WMRDIBCREATEPATTERNBRUSH, Style )); + *cUsage = *(uint16_t *)(contents + offsetof(U_WMRDIBCREATEPATTERNBRUSH, cUsage )); + if(*Style == U_BS_PATTERN){ + *Bm16 = (contents + offsetof(U_WMRDIBCREATEPATTERNBRUSH, Src)); + *dib = NULL; + /* The WMF spec says that Style == U_BS_PATTERN _SHOULD_ be a bitmap16. + However there are instances when it is actually a DIB. U_WMRDIBCREATEPATTERNBRUSH_get + tries to detect this by looking for bogus values when the BM16 is interpreted as such, + and if it finds them, then it returns a dib instead. + */ + U_BITMAP16 TmpBm16; + memcpy(&TmpBm16, *Bm16, U_SIZE_BITMAP16); + if(TmpBm16.Width <= 0 || TmpBm16.Height <= 0 || TmpBm16.Planes != 1 || TmpBm16.BitsPixel == 0){ + *Bm16 = NULL; + *dib = (contents + offsetof(U_WMRDIBCREATEPATTERNBRUSH, Src)); + } + } + else { /* from DIB */ + *Bm16 = NULL; + *dib = (contents + offsetof(U_WMRDIBCREATEPATTERNBRUSH, Src)); + } + return(size); +} + +/** + \brief Get data from a U_WMRSTRETCHDIB record. + \return length of the U_WMRSTRETCHDIB record in bytes, or 0 on error + \param contents record to extract data from + \param Dst Destination UL corner in logical units + \param cDst Destination W & H in logical units + \param Src Source UL corner in logical units + \param cSrc Source W & H in logical units + \param cUsage DIBColors Enumeration + \param dwRop3 RasterOPeration Enumeration + \param dib (Optional) device independent bitmap +*/ +int U_WMRSTRETCHDIB_get( + const char *contents, + PU_POINT16 Dst, + PU_POINT16 cDst, + PU_POINT16 Src, + PU_POINT16 cSrc, + uint16_t *cUsage, + uint32_t *dwRop3, + const char **dib + ){ + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRSTRETCHDIB)); + if(!size)return(0); + + memcpy(dwRop3, ( contents + offsetof(U_WMRSTRETCHDIB, rop3w)), 4); + *cUsage = *(uint16_t *)( contents + offsetof(U_WMRSTRETCHDIB, cUsage )); + cSrc->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHDIB, hSrc )); + cSrc->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHDIB, wSrc )); + Src->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHDIB, ySrc )); + Src->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHDIB, xSrc )); + cDst->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHDIB, hDst )); + cDst->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHDIB, wDst )); + Dst->y = *(int16_t *)( contents + offsetof(U_WMRSTRETCHDIB, yDst )); + Dst->x = *(int16_t *)( contents + offsetof(U_WMRSTRETCHDIB, xDst )); + *dib = ( contents + offsetof(U_WMRSTRETCHDIB, dib )); + return(size); +} + +int U_WMR44_get(void){ + return U_WMRCORENONE_get("U_WMR44"); +} + +int U_WMR45_get(void){ + return U_WMRCORENONE_get("U_WMR45"); +} + +int U_WMR46_get(void){ + return U_WMRCORENONE_get("U_WMR46"); +} + +int U_WMR47_get(void){ + return U_WMRCORENONE_get("U_WMR47"); +} + +/** + \brief Retrieve values from a U_WMREXTFLOODFILL record + \return length of the U_WMREXTFLOODFILL record, or NULL on error + \param contents record to extract data from + \param Mode FloodFill Enumeration. + \param Color Color to Fill with. + \param coord Location to start fill. +*/ +int U_WMREXTFLOODFILL_get( + const char *contents, + uint16_t *Mode, + PU_COLORREF Color, + PU_POINT16 coord + ){ + return U_WMRCORE_1U16_CRF_2U16_get( + contents, + (U_SIZE_WMREXTFLOODFILL), + Mode, + Color, + U_P16(coord->y), + U_P16(coord->x) + ); +} + +int U_WMR49_get(void){ + return U_WMRCORENONE_get("U_WMR49"); +} + +int U_WMR4A_get(void){ + return U_WMRCORENONE_get("U_WMR4A"); +} + +int U_WMR4B_get(void){ + return U_WMRCORENONE_get("U_WMR4B"); +} + +int U_WMR4C_get(void){ + return U_WMRCORENONE_get("U_WMRRESETDOC"); +} + +int U_WMR4D_get(void){ + return U_WMRCORENONE_get("U_WMRSTARTDOC"); +} + +int U_WMR4E_get(void){ + return U_WMRCORENONE_get("U_WMR4E"); +} + +int U_WMR4F_get(void){ + return U_WMRCORENONE_get("U_WMRSTARTPAGE"); +} + +int U_WMR50_get(void){ + return U_WMRCORENONE_get("U_WMRENDPAGE"); +} + +int U_WMR51_get(void){ + return U_WMRCORENONE_get("U_WMR51"); +} + +int U_WMRABORTDOC_get(void){ + return U_WMRCORENONE_get("U_WMRABORTDOC"); +} + +int U_WMR53_get(void){ + return U_WMRCORENONE_get("U_WMR53"); +} + +int U_WMR54_get(void){ + return U_WMRCORENONE_get("U_WMR54"); +} + +int U_WMR55_get(void){ + return U_WMRCORENONE_get("U_WMR55"); +} + +int U_WMR56_get(void){ + return U_WMRCORENONE_get("U_WMR56"); +} + +int U_WMR57_get(void){ + return U_WMRCORENONE_get("U_WMR57"); +} + +int U_WMR58_get(void){ + return U_WMRCORENONE_get("U_WMR58"); +} + +int U_WMR59_get(void){ + return U_WMRCORENONE_get("U_WMR59"); +} + +int U_WMR5A_get(void){ + return U_WMRCORENONE_get("U_WMR5A"); +} + +int U_WMR5B_get(void){ + return U_WMRCORENONE_get("U_WMR5B"); +} + +int U_WMR5C_get(void){ + return U_WMRCORENONE_get("U_WMR5C"); +} + +int U_WMR5D_get(void){ + return U_WMRCORENONE_get("U_WMR5D"); +} + +int U_WMR5E_get(void){ + return U_WMRCORENONE_get("U_WMRENDDOC"); +} + +int U_WMR5F_get(void){ + return U_WMRCORENONE_get("U_WMR5F"); +} + +int U_WMR60_get(void){ + return U_WMRCORENONE_get("U_WMR60"); +} + +int U_WMR61_get(void){ + return U_WMRCORENONE_get("U_WMR61"); +} + +int U_WMR62_get(void){ + return U_WMRCORENONE_get("U_WMR62"); +} + +int U_WMR63_get(void){ + return U_WMRCORENONE_get("U_WMR63"); +} + +int U_WMR64_get(void){ + return U_WMRCORENONE_get("U_WMR64"); +} + +int U_WMR65_get(void){ + return U_WMRCORENONE_get("U_WMR65"); +} + +int U_WMR66_get(void){ + return U_WMRCORENONE_get("U_WMR66"); +} + +int U_WMR67_get(void){ + return U_WMRCORENONE_get("U_WMR67"); +} + +int U_WMR68_get(void){ + return U_WMRCORENONE_get("U_WMR68"); +} + +int U_WMR69_get(void){ + return U_WMRCORENONE_get("U_WMR69"); +} + +int U_WMR6A_get(void){ + return U_WMRCORENONE_get("U_WMR6A"); +} + +int U_WMR6B_get(void){ + return U_WMRCORENONE_get("U_WMR6B"); +} + +int U_WMR6C_get(void){ + return U_WMRCORENONE_get("U_WMR6C"); +} + +int U_WMR6D_get(void){ + return U_WMRCORENONE_get("U_WMR6D"); +} + +int U_WMR6E_get(void){ + return U_WMRCORENONE_get("U_WMR6E"); +} + +int U_WMR6F_get(void){ + return U_WMRCORENONE_get("U_WMR6F"); +} + +int U_WMR70_get(void){ + return U_WMRCORENONE_get("U_WMR70"); +} + +int U_WMR71_get(void){ + return U_WMRCORENONE_get("U_WMR71"); +} + +int U_WMR72_get(void){ + return U_WMRCORENONE_get("U_WMR72"); +} + +int U_WMR73_get(void){ + return U_WMRCORENONE_get("U_WMR73"); +} + +int U_WMR74_get(void){ + return U_WMRCORENONE_get("U_WMR74"); +} + +int U_WMR75_get(void){ + return U_WMRCORENONE_get("U_WMR75"); +} + +int U_WMR76_get(void){ + return U_WMRCORENONE_get("U_WMR76"); +} + +int U_WMR77_get(void){ + return U_WMRCORENONE_get("U_WMR77"); +} + +int U_WMR78_get(void){ + return U_WMRCORENONE_get("U_WMR78"); +} + +int U_WMR79_get(void){ + return U_WMRCORENONE_get("U_WMR79"); +} + +int U_WMR7A_get(void){ + return U_WMRCORENONE_get("U_WMR7A"); +} + +int U_WMR7B_get(void){ + return U_WMRCORENONE_get("U_WMR7B"); +} + +int U_WMR7C_get(void){ + return U_WMRCORENONE_get("U_WMR7C"); +} + +int U_WMR7D_get(void){ + return U_WMRCORENONE_get("U_WMR7D"); +} + +int U_WMR7E_get(void){ + return U_WMRCORENONE_get("U_WMR7E"); +} + +int U_WMR7F_get(void){ + return U_WMRCORENONE_get("U_WMR7F"); +} + +int U_WMR80_get(void){ + return U_WMRCORENONE_get("U_WMR80"); +} + +int U_WMR81_get(void){ + return U_WMRCORENONE_get("U_WMR81"); +} + +int U_WMR82_get(void){ + return U_WMRCORENONE_get("U_WMR82"); +} + +int U_WMR83_get(void){ + return U_WMRCORENONE_get("U_WMR83"); +} + +int U_WMR84_get(void){ + return U_WMRCORENONE_get("U_WMR84"); +} + +int U_WMR85_get(void){ + return U_WMRCORENONE_get("U_WMR85"); +} + +int U_WMR86_get(void){ + return U_WMRCORENONE_get("U_WMR86"); +} + +int U_WMR87_get(void){ + return U_WMRCORENONE_get("U_WMR87"); +} + +int U_WMR88_get(void){ + return U_WMRCORENONE_get("U_WMR88"); +} + +int U_WMR89_get(void){ + return U_WMRCORENONE_get("U_WMR89"); +} + +int U_WMR8A_get(void){ + return U_WMRCORENONE_get("U_WMR8A"); +} + +int U_WMR8B_get(void){ + return U_WMRCORENONE_get("U_WMR8B"); +} + +int U_WMR8C_get(void){ + return U_WMRCORENONE_get("U_WMR8C"); +} + +int U_WMR8D_get(void){ + return U_WMRCORENONE_get("U_WMR8D"); +} + +int U_WMR8E_get(void){ + return U_WMRCORENONE_get("U_WMR8E"); +} + +int U_WMR8F_get(void){ + return U_WMRCORENONE_get("U_WMR8F"); +} + +int U_WMR90_get(void){ + return U_WMRCORENONE_get("U_WMR90"); +} + +int U_WMR91_get(void){ + return U_WMRCORENONE_get("U_WMR91"); +} + +int U_WMR92_get(void){ + return U_WMRCORENONE_get("U_WMR92"); +} + +int U_WMR93_get(void){ + return U_WMRCORENONE_get("U_WMR93"); +} + +int U_WMR94_get(void){ + return U_WMRCORENONE_get("U_WMR94"); +} + +int U_WMR95_get(void){ + return U_WMRCORENONE_get("U_WMR95"); +} + +int U_WMR96_get(void){ + return U_WMRCORENONE_get("U_WMR96"); +} + +int U_WMR97_get(void){ + return U_WMRCORENONE_get("U_WMR97"); +} + +int U_WMR98_get(void){ + return U_WMRCORENONE_get("U_WMR98"); +} + +int U_WMR99_get(void){ + return U_WMRCORENONE_get("U_WMR99"); +} + +int U_WMR9A_get(void){ + return U_WMRCORENONE_get("U_WMR9A"); +} + +int U_WMR9B_get(void){ + return U_WMRCORENONE_get("U_WMR9B"); +} + +int U_WMR9C_get(void){ + return U_WMRCORENONE_get("U_WMR9C"); +} + +int U_WMR9D_get(void){ + return U_WMRCORENONE_get("U_WMR9D"); +} + +int U_WMR9E_get(void){ + return U_WMRCORENONE_get("U_WMR9E"); +} + +int U_WMR9F_get(void){ + return U_WMRCORENONE_get("U_WMR9F"); +} + +int U_WMRA0_get(void){ + return U_WMRCORENONE_get("U_WMRA0"); +} + +int U_WMRA1_get(void){ + return U_WMRCORENONE_get("U_WMRA1"); +} + +int U_WMRA2_get(void){ + return U_WMRCORENONE_get("U_WMRA2"); +} + +int U_WMRA3_get(void){ + return U_WMRCORENONE_get("U_WMRA3"); +} + +int U_WMRA4_get(void){ + return U_WMRCORENONE_get("U_WMRA4"); +} + +int U_WMRA5_get(void){ + return U_WMRCORENONE_get("U_WMRA5"); +} + +int U_WMRA6_get(void){ + return U_WMRCORENONE_get("U_WMRA6"); +} + +int U_WMRA7_get(void){ + return U_WMRCORENONE_get("U_WMRA7"); +} + +int U_WMRA8_get(void){ + return U_WMRCORENONE_get("U_WMRA8"); +} + +int U_WMRA9_get(void){ + return U_WMRCORENONE_get("U_WMRA9"); +} + +int U_WMRAA_get(void){ + return U_WMRCORENONE_get("U_WMRAA"); +} + +int U_WMRAB_get(void){ + return U_WMRCORENONE_get("U_WMRAB"); +} + +int U_WMRAC_get(void){ + return U_WMRCORENONE_get("U_WMRAC"); +} + +int U_WMRAD_get(void){ + return U_WMRCORENONE_get("U_WMRAD"); +} + +int U_WMRAE_get(void){ + return U_WMRCORENONE_get("U_WMRAE"); +} + +int U_WMRAF_get(void){ + return U_WMRCORENONE_get("U_WMRAF"); +} + +int U_WMRB0_get(void){ + return U_WMRCORENONE_get("U_WMRB0"); +} + +int U_WMRB1_get(void){ + return U_WMRCORENONE_get("U_WMRB1"); +} + +int U_WMRB2_get(void){ + return U_WMRCORENONE_get("U_WMRB2"); +} + +int U_WMRB3_get(void){ + return U_WMRCORENONE_get("U_WMRB3"); +} + +int U_WMRB4_get(void){ + return U_WMRCORENONE_get("U_WMRB4"); +} + +int U_WMRB5_get(void){ + return U_WMRCORENONE_get("U_WMRB5"); +} + +int U_WMRB6_get(void){ + return U_WMRCORENONE_get("U_WMRB6"); +} + +int U_WMRB7_get(void){ + return U_WMRCORENONE_get("U_WMRB7"); +} + +int U_WMRB8_get(void){ + return U_WMRCORENONE_get("U_WMRB8"); +} + +int U_WMRB9_get(void){ + return U_WMRCORENONE_get("U_WMRB9"); +} + +int U_WMRBA_get(void){ + return U_WMRCORENONE_get("U_WMRBA"); +} + +int U_WMRBB_get(void){ + return U_WMRCORENONE_get("U_WMRBB"); +} + +int U_WMRBC_get(void){ + return U_WMRCORENONE_get("U_WMRBC"); +} + +int U_WMRBD_get(void){ + return U_WMRCORENONE_get("U_WMRBD"); +} + +int U_WMRBE_get(void){ + return U_WMRCORENONE_get("U_WMRBE"); +} + +int U_WMRBF_get(void){ + return U_WMRCORENONE_get("U_WMRBF"); +} + +int U_WMRC0_get(void){ + return U_WMRCORENONE_get("U_WMRC0"); +} + +int U_WMRC1_get(void){ + return U_WMRCORENONE_get("U_WMRC1"); +} + +int U_WMRC2_get(void){ + return U_WMRCORENONE_get("U_WMRC2"); +} + +int U_WMRC3_get(void){ + return U_WMRCORENONE_get("U_WMRC3"); +} + +int U_WMRC4_get(void){ + return U_WMRCORENONE_get("U_WMRC4"); +} + +int U_WMRC5_get(void){ + return U_WMRCORENONE_get("U_WMRC5"); +} + +int U_WMRC6_get(void){ + return U_WMRCORENONE_get("U_WMRC6"); +} + +int U_WMRC7_get(void){ + return U_WMRCORENONE_get("U_WMRC7"); +} + +int U_WMRC8_get(void){ + return U_WMRCORENONE_get("U_WMRC8"); +} + +int U_WMRC9_get(void){ + return U_WMRCORENONE_get("U_WMRC9"); +} + +int U_WMRCA_get(void){ + return U_WMRCORENONE_get("U_WMRCA"); +} + +int U_WMRCB_get(void){ + return U_WMRCORENONE_get("U_WMRCB"); +} + +int U_WMRCC_get(void){ + return U_WMRCORENONE_get("U_WMRCC"); +} + +int U_WMRCD_get(void){ + return U_WMRCORENONE_get("U_WMRCD"); +} + +int U_WMRCE_get(void){ + return U_WMRCORENONE_get("U_WMRCE"); +} + +int U_WMRCF_get(void){ + return U_WMRCORENONE_get("U_WMRCF"); +} + +int U_WMRD0_get(void){ + return U_WMRCORENONE_get("U_WMRD0"); +} + +int U_WMRD1_get(void){ + return U_WMRCORENONE_get("U_WMRD1"); +} + +int U_WMRD2_get(void){ + return U_WMRCORENONE_get("U_WMRD2"); +} + +int U_WMRD3_get(void){ + return U_WMRCORENONE_get("U_WMRD3"); +} + +int U_WMRD4_get(void){ + return U_WMRCORENONE_get("U_WMRD4"); +} + +int U_WMRD5_get(void){ + return U_WMRCORENONE_get("U_WMRD5"); +} + +int U_WMRD6_get(void){ + return U_WMRCORENONE_get("U_WMRD6"); +} + +int U_WMRD7_get(void){ + return U_WMRCORENONE_get("U_WMRD7"); +} + +int U_WMRD8_get(void){ + return U_WMRCORENONE_get("U_WMRD8"); +} + +int U_WMRD9_get(void){ + return U_WMRCORENONE_get("U_WMRD9"); +} + +int U_WMRDA_get(void){ + return U_WMRCORENONE_get("U_WMRDA"); +} + +int U_WMRDB_get(void){ + return U_WMRCORENONE_get("U_WMRDB"); +} + +int U_WMRDC_get(void){ + return U_WMRCORENONE_get("U_WMRDC"); +} + +int U_WMRDD_get(void){ + return U_WMRCORENONE_get("U_WMRDD"); +} + +int U_WMRDE_get(void){ + return U_WMRCORENONE_get("U_WMRDE"); +} + +int U_WMRDF_get(void){ + return U_WMRCORENONE_get("U_WMRDF"); +} + +int U_WMRE0_get(void){ + return U_WMRCORENONE_get("U_WMRE0"); +} + +int U_WMRE1_get(void){ + return U_WMRCORENONE_get("U_WMRE1"); +} + +int U_WMRE2_get(void){ + return U_WMRCORENONE_get("U_WMRE2"); +} + +int U_WMRE3_get(void){ + return U_WMRCORENONE_get("U_WMRE3"); +} + +int U_WMRE4_get(void){ + return U_WMRCORENONE_get("U_WMRE4"); +} + +int U_WMRE5_get(void){ + return U_WMRCORENONE_get("U_WMRE5"); +} + +int U_WMRE6_get(void){ + return U_WMRCORENONE_get("U_WMRE6"); +} + +int U_WMRE7_get(void){ + return U_WMRCORENONE_get("U_WMRE7"); +} + +int U_WMRE8_get(void){ + return U_WMRCORENONE_get("U_WMRE8"); +} + +int U_WMRE9_get(void){ + return U_WMRCORENONE_get("U_WMRE9"); +} + +int U_WMREA_get(void){ + return U_WMRCORENONE_get("U_WMREA"); +} + +int U_WMREB_get(void){ + return U_WMRCORENONE_get("U_WMREB"); +} + +int U_WMREC_get(void){ + return U_WMRCORENONE_get("U_WMREC"); +} + +int U_WMRED_get(void){ + return U_WMRCORENONE_get("U_WMRED"); +} + +int U_WMREE_get(void){ + return U_WMRCORENONE_get("U_WMREE"); +} + +int U_WMREF_get(void){ + return U_WMRCORENONE_get("U_WMREF"); +} + +/** + \brief Get data from a U_WMRDELETEOBJECT record. + \return length of the U_WMRDELETEOBJECT record in bytes, or 0 on error + \param contents record to extract data from + \param Object Index of object which is made active. +*/ +int U_WMRDELETEOBJECT_get( + const char *contents, + uint16_t *Object + ){ + return U_WMRCORE_1U16_get(contents, (U_SIZE_WMRDELETEOBJECT), Object); +} + +int U_WMRF1_get(void){ + return U_WMRCORENONE_get("U_WMRF1"); +} + +int U_WMRF2_get(void){ + return U_WMRCORENONE_get("U_WMRF2"); +} + +int U_WMRF3_get(void){ + return U_WMRCORENONE_get("U_WMRF3"); +} + +int U_WMRF4_get(void){ + return U_WMRCORENONE_get("U_WMRF4"); +} + +int U_WMRF5_get(void){ + return U_WMRCORENONE_get("U_WMRF5"); +} + +int U_WMRF6_get(void){ + return U_WMRCORENONE_get("U_WMRF6"); +} + +/** + \brief Retrieve values from a U_WMRCREATEPALETTE record + \return length of the U_WMRCREATEPALETTE record, or NULL on error + \param contents record to extract data from + \param Palette Create a Palette object. + \param PalEntries Array of Palette Entries +*/ +int U_WMRCREATEPALETTE_get( + const char *contents, + PU_PALETTE Palette, + const char **PalEntries + ){ + return U_WMRCORE_PALETTE_get(contents, (U_SIZE_WMRCREATEPALETTE), Palette, PalEntries); +} + +int U_WMRF8_get(void){ + return U_WMRCORENONE_get("U_WMRF8"); +} + +/** + \brief Get data from a U_WMRCREATEPATTERNBRUSH record. + Warning - application support for U_WMRCREATEPATTERNBRUSH is spotty, better to use U_WMRDIBCREATEPATTERNBRUSH. + \return length of the U_WMRCREATEPATTERNBRUSH record in bytes, or 0 on error + \param contents record to extract data from + \param Bm16 truncated Bitmap16 structure from record, only tge first 14 bytes hold data. + \param pasize Number of bytes in Pattern + \param Pattern byte array pattern, described by Bm16, for brush +*/ +int U_WMRCREATEPATTERNBRUSH_get( + const char *contents, + PU_BITMAP16 Bm16, + int *pasize, + const char **Pattern + ){ + int off = U_SIZE_METARECORD; + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRSETDIBTODEV)); + if(!size)return(0); + memset(Bm16, 0, U_SIZE_BITMAP16); + memcpy(Bm16, contents + off, 14); /* BM16 is truncated in this record type */ + *pasize = (((Bm16->Width * Bm16->BitsPixel + 15) >> 4) << 1) * Bm16->Height; + off += 36; /* skip [truncated bitmap16 object and 18 bytes of reserved */ + *Pattern = (contents + off); + return(size); +} + +/** + \brief Get data from a U_WMRCREATEPENINDIRECT record. + \return length of the U_WMRCREATEPENINDIRECT record in bytes, or 0 on error + \param contents record to extract data from + \param pen pointer to a U_PEN object to fill. +*/ +int U_WMRCREATEPENINDIRECT_get( + const char *contents, + PU_PEN pen + ){ + int size = U_WMRCORE_RECSAFE_get(contents, (U_SIZE_WMRCREATEPENINDIRECT)); + if(!size)return(0); + memcpy(pen, contents + offsetof(U_WMRCREATEPENINDIRECT, pen), U_SIZE_PEN); + return(size); +} + +/** + \brief Get data from a U_WMRCREATEFONTINDIRECT record. + \return length of the U_WMRCREATEFONTINDIRECT record in bytes, or 0 on error + \param contents record to extract data from + \param font pointer to array of U_FONT structure in memory. Pointer may not be aligned properly for structure. +*/ +int U_WMRCREATEFONTINDIRECT_get( + const char *contents, + const char **font + ){ + return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRCREATEFONTINDIRECT), NULL, NULL, font); +} + +/** + \brief Get data from a U_WMRCREATEBRUSHINDIRECT record. + \return length of the U_WMRCREATEBRUSHINDIRECT record in bytes, or 0 on error + \param contents record to extract data from + \param brush pointer to U_WLOGBRUSH structure in memory. Pointer may not be aligned properly for structure. +*/ +int U_WMRCREATEBRUSHINDIRECT_get( + const char *contents, + const char **brush + ){ + return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRCREATEBRUSHINDIRECT), NULL, NULL, brush); +} + + /* in Wine, not in WMF PDF */ +int U_WMRCREATEBITMAPINDIRECT_get(void){ + return U_WMRCORENONE_get("U_WMRCREATEBITMAPINDIRECT"); +} + + /* in Wine, not in WMF PDF */ +int U_WMRCREATEBITMAP_get(void){ + return U_WMRCORENONE_get("U_WMRCREATEBITMAP"); +} + +/** + \brief Get data from a U_WMRCREATEREGION record. + \return length of the U_WMRCREATEREGION record in bytes, or 0 on error + \param contents record to extract data from + \param Region pointer to U_REGION structure in memory. Pointer may not be aligned properly for structure. +*/ +int U_WMRCREATEREGION_get( + const char *contents, + const char **Region + ){ + return U_WMRCORE_2U16_N16_get(contents, (U_SIZE_WMRCREATEREGION), NULL, NULL, Region); +} + + + +#ifdef __cplusplus +} +#endif diff --git a/src/libuemf/uwmf.h b/src/libuemf/uwmf.h new file mode 100644 index 000000000..a97648eb1 --- /dev/null +++ b/src/libuemf/uwmf.h @@ -0,0 +1,2492 @@ +/** + @file uwmf.h Structures and functions prototypes for WMF files. + + WMF file Record structure information has been derived from Mingw and Wine header files, and from + Microsoft's WMF Information pdf, release date July 5,2012, link from here: + + http://msdn2.microsoft.com/en-us/library/250370.aspx + + If the direct link fails the document may be found + by searching for: "[MS-WMF]: Windows Metafile Format" + + *********************************** IMPORTANT!!! ********************************************** + WMF is a 16 bit file type that has some 32 bit integers embedded in it. In + a few cases these 32 bit fields are not aligned in the structures defined in uwmf.h, but + in most cases they are. So when creating the individual WMF records the functions in + uwmf.c can usually use a regular assignment operation for the 32 bit fields. However, once the + records are part of a WMF file in memory there is no guaranty that any 32 bit type will be correctly + aligned. Similarly, many WMF structures contain embedded other structures which would "naturally" + be passed by pointer, but since their alignment may not be what malloc() would have created for that + type, the outcome of that operation is not defined by the C standard. (Per Eric Sosman, section + 6.3.2.3p7 of the standard.) + + For this reason, the _print, _swap and any read operations must pass structures with unknown alignment + as a (char *), and pull out the data using memcpy() or some equivalent + that will not segfault when it tries to read a 32 bit value that is not aligned + on a 4 byte boundary. Failure to do so will result in nonportable code. You have been warned! + + Problem areas: + The Size16_4 field of all WMF records may NOT be assumed to 4 byte aligned. + DIB's U_BITMAPINFOHEADER 32 bit fields may not be aligned. + *********************************** IMPORTANT!!! ********************************************** + +*/ + +/* +File: uwmf.h +Version: 0.0.8 +Date: 27-FEB-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifndef _UWMF_ +#define _UWMF_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include "uemf.h" /* many structures/defs in common, pull in the EMF ones as a basis */ +#include "uemf_utf.h" +#include "uwmf_endian.h" + + + + + +// *********************************************************************************** +// Value enumerations and other predefined constants, alphabetical order by group + +/** RecordType Enumeration WMF PDF 2.1.1.1 + \defgroup U_WMR_RecordTypes WMR Record types + @{ +*/ +enum U_WMR_TYPES{ + U_WMR_EOF, //!< 0x0000 U_WMREOF record + U_WMR_SETBKCOLOR, //!< 0x0201 U_WMRSETBKCOLOR record + U_WMR_SETBKMODE, //!< 0x0102 U_WMRSETBKMODE record + U_WMR_SETMAPMODE, //!< 0x0103 U_WMRSETMAPMODE record + U_WMR_SETROP2, //!< 0x0104 U_WMRSETROP2 record + U_WMR_SETRELABS, //!< 0x0105 U_WMRSETRELABS record + U_WMR_SETPOLYFILLMODE, //!< 0x0106 U_WMRSETPOLYFILLMODE record + U_WMR_SETSTRETCHBLTMODE, //!< 0x0107 U_WMRSETSTRETCHBLTMODE record + U_WMR_SETTEXTCHAREXTRA, //!< 0x0108 U_WMRSETTEXTCHAREXTRA record + U_WMR_SETTEXTCOLOR, //!< 0x0209 U_WMRSETTEXTCOLOR record + U_WMR_SETTEXTJUSTIFICATION, //!< 0x020A U_WMRSETTEXTJUSTIFICATION record + U_WMR_SETWINDOWORG, //!< 0x020B U_WMRSETWINDOWORG record + U_WMR_SETWINDOWEXT, //!< 0x020C U_WMRSETWINDOWEXT record + U_WMR_SETVIEWPORTORG, //!< 0x020D U_WMRSETVIEWPORTORG record + U_WMR_SETVIEWPORTEXT, //!< 0x020E U_WMRSETVIEWPORTEXT record + U_WMR_OFFSETWINDOWORG, //!< 0x020F U_WMROFFSETWINDOWORG record + U_WMR_SCALEWINDOWEXT, //!< 0x0410 U_WMRSCALEWINDOWEXT record + U_WMR_OFFSETVIEWPORTORG, //!< 0x0211 U_WMROFFSETVIEWPORTORG record + U_WMR_SCALEVIEWPORTEXT, //!< 0x0412 U_WMRSCALEVIEWPORTEXT record + U_WMR_LINETO, //!< 0x0213 U_WMRLINETO record + U_WMR_MOVETO, //!< 0x0214 U_WMRMOVETO record + U_WMR_EXCLUDECLIPRECT, //!< 0x0415 U_WMREXCLUDECLIPRECT record + U_WMR_INTERSECTCLIPRECT, //!< 0x0416 U_WMRINTERSECTCLIPRECT record + U_WMR_ARC, //!< 0x0817 U_WMRARC record + U_WMR_ELLIPSE, //!< 0x0418 U_WMRELLIPSE record + U_WMR_FLOODFILL, //!< 0x0419 U_WMRFLOODFILL record + U_WMR_PIE, //!< 0x081A U_WMRPIE record + U_WMR_RECTANGLE, //!< 0x041B U_WMRRECTANGLE record + U_WMR_ROUNDRECT, //!< 0x061C U_WMRROUNDRECT record + U_WMR_PATBLT, //!< 0x061D U_WMRPATBLT record + U_WMR_SAVEDC, //!< 0x001E U_WMRSAVEDC record + U_WMR_SETPIXEL, //!< 0x041F U_WMRSETPIXEL record + U_WMR_OFFSETCLIPRGN, //!< 0x0220 U_WMROFFSETCLIPRGN record + U_WMR_TEXTOUT, //!< 0x0521 U_WMRTEXTOUT record + U_WMR_BITBLT, //!< 0x0922 U_WMRBITBLT record + U_WMR_STRETCHBLT, //!< 0x0B23 U_WMRSTRETCHBLT record + U_WMR_POLYGON, //!< 0x0324 U_WMRPOLYGON record + U_WMR_POLYLINE, //!< 0x0325 U_WMRPOLYLINE record + U_WMR_ESCAPE, //!< 0x0626 U_WMRESCAPE record + U_WMR_RESTOREDC, //!< 0x0127 U_WMRRESTOREDC record + U_WMR_FILLREGION, //!< 0x0228 U_WMRFILLREGION record + U_WMR_FRAMEREGION, //!< 0x0429 U_WMRFRAMEREGION record + U_WMR_INVERTREGION, //!< 0x012A U_WMRINVERTREGION record + U_WMR_PAINTREGION, //!< 0x012B U_WMRPAINTREGION record + U_WMR_SELECTCLIPREGION, //!< 0x012C U_WMRSELECTCLIPREGION record + U_WMR_SELECTOBJECT, //!< 0x012D U_WMRSELECTOBJECT record + U_WMR_SETTEXTALIGN, //!< 0x012E U_WMRSETTEXTALIGN record + U_WMR_DRAWTEXT, //!< 0x062F U_WMRDRAWTEXT record + U_WMR_CHORD, //!< 0x0830 U_WMRCHORD record + U_WMR_SETMAPPERFLAGS, //!< 0x0231 U_WMRSETMAPPERFLAGS record + U_WMR_EXTTEXTOUT, //!< 0x0A32 U_WMREXTTEXTOUT record + U_WMR_SETDIBTODEV, //!< 0x0D33 U_WMRSETDIBTODEV record + U_WMR_SELECTPALETTE, //!< 0x0234 U_WMRSELECTPALETTE record + U_WMR_REALIZEPALETTE, //!< 0x0035 U_WMRREALIZEPALETTE record + U_WMR_ANIMATEPALETTE, //!< 0x0436 U_WMRANIMATEPALETTE record + U_WMR_SETPALENTRIES, //!< 0x0037 U_WMRSETPALENTRIES record + U_WMR_POLYPOLYGON, //!< 0x0538 U_WMRPOLYPOLYGON record + U_WMR_RESIZEPALETTE, //!< 0x0139 U_WMRRESIZEPALETTE record + U_WMR_3A, //!< 0x003A U_WMR3A record + U_WMR_3B, //!< 0x003B U_WMR3B record + U_WMR_3C, //!< 0x003C U_WMR3C record + U_WMR_3D, //!< 0x003D U_WMR3D record + U_WMR_3E, //!< 0x003E U_WMR3E record + U_WMR_3F, //!< 0x003F U_WMR3F record + U_WMR_DIBBITBLT, //!< 0x0940 U_WMRDIBBITBLT record + U_WMR_DIBSTRETCHBLT, //!< 0x0B41 U_WMRDIBSTRETCHBLT record + U_WMR_DIBCREATEPATTERNBRUSH, //!< 0x0142 U_WMRDIBCREATEPATTERNBRUSH record + U_WMR_STRETCHDIB, //!< 0x0F43 U_WMRSTRETCHDIB record + U_WMR_44, //!< 0x0044 U_WMR44 record + U_WMR_45, //!< 0x0045 U_WMR45 record + U_WMR_46, //!< 0x0046 U_WMR46 record + U_WMR_47, //!< 0x0047 U_WMR47 record + U_WMR_EXTFLOODFILL, //!< 0x0548 U_WMREXTFLOODFILL record + U_WMR_49, //!< 0x0049 U_WMR49 record + U_WMR_4A, //!< 0x004A U_WMR4A record + U_WMR_4B, //!< 0x004B U_WMR4B record + U_WMR_4C, //!< 0x014C U_WMR4C record + U_WMR_4D, //!< 0x014D U_WMR4D record + U_WMR_4E, //!< 0x004E U_WMR4E record + U_WMR_4F, //!< 0x004F U_WMR4F record + U_WMR_50, //!< 0x0050 U_WMR50 record + U_WMR_51, //!< 0x0051 U_WMR51 record + U_WMR_52, //!< 0x0052 U_WMR52 record + U_WMR_53, //!< 0x0053 U_WMR53 record + U_WMR_54, //!< 0x0054 U_WMR54 record + U_WMR_55, //!< 0x0055 U_WMR55 record + U_WMR_56, //!< 0x0056 U_WMR56 record + U_WMR_57, //!< 0x0057 U_WMR57 record + U_WMR_58, //!< 0x0058 U_WMR58 record + U_WMR_59, //!< 0x0059 U_WMR59 record + U_WMR_5A, //!< 0x005A U_WMR5A record + U_WMR_5B, //!< 0x005B U_WMR5B record + U_WMR_5C, //!< 0x005C U_WMR5C record + U_WMR_5D, //!< 0x005D U_WMR5D record + U_WMR_5E, //!< 0x005E U_WMR5E record + U_WMR_5F, //!< 0x005F U_WMR5F record + U_WMR_60, //!< 0x0060 U_WMR60 record + U_WMR_61, //!< 0x0061 U_WMR61 record + U_WMR_62, //!< 0x0062 U_WMR62 record + U_WMR_63, //!< 0x0063 U_WMR63 record + U_WMR_64, //!< 0x0064 U_WMR64 record + U_WMR_65, //!< 0x0065 U_WMR65 record + U_WMR_66, //!< 0x0066 U_WMR66 record + U_WMR_67, //!< 0x0067 U_WMR67 record + U_WMR_68, //!< 0x0068 U_WMR68 record + U_WMR_69, //!< 0x0069 U_WMR69 record + U_WMR_6A, //!< 0x006A U_WMR6A record + U_WMR_6B, //!< 0x006B U_WMR6B record + U_WMR_6C, //!< 0x006C U_WMR6C record + U_WMR_6D, //!< 0x006D U_WMR6D record + U_WMR_6E, //!< 0x006E U_WMR6E record + U_WMR_6F, //!< 0x006F U_WMR6F record + U_WMR_70, //!< 0x0070 U_WMR70 record + U_WMR_71, //!< 0x0071 U_WMR71 record + U_WMR_72, //!< 0x0072 U_WMR72 record + U_WMR_73, //!< 0x0073 U_WMR73 record + U_WMR_74, //!< 0x0074 U_WMR74 record + U_WMR_75, //!< 0x0075 U_WMR75 record + U_WMR_76, //!< 0x0076 U_WMR76 record + U_WMR_77, //!< 0x0077 U_WMR77 record + U_WMR_78, //!< 0x0078 U_WMR78 record + U_WMR_79, //!< 0x0079 U_WMR79 record + U_WMR_7A, //!< 0x007A U_WMR7A record + U_WMR_7B, //!< 0x007B U_WMR7B record + U_WMR_7C, //!< 0x007C U_WMR7C record + U_WMR_7D, //!< 0x007D U_WMR7D record + U_WMR_7E, //!< 0x007E U_WMR7E record + U_WMR_7F, //!< 0x007F U_WMR7F record + U_WMR_80, //!< 0x0080 U_WMR80 record + U_WMR_81, //!< 0x0081 U_WMR81 record + U_WMR_82, //!< 0x0082 U_WMR82 record + U_WMR_83, //!< 0x0083 U_WMR83 record + U_WMR_84, //!< 0x0084 U_WMR84 record + U_WMR_85, //!< 0x0085 U_WMR85 record + U_WMR_86, //!< 0x0086 U_WMR86 record + U_WMR_87, //!< 0x0087 U_WMR87 record + U_WMR_88, //!< 0x0088 U_WMR88 record + U_WMR_89, //!< 0x0089 U_WMR89 record + U_WMR_8A, //!< 0x008A U_WMR8A record + U_WMR_8B, //!< 0x008B U_WMR8B record + U_WMR_8C, //!< 0x008C U_WMR8C record + U_WMR_8D, //!< 0x008D U_WMR8D record + U_WMR_8E, //!< 0x008E U_WMR8E record + U_WMR_8F, //!< 0x008F U_WMR8F record + U_WMR_90, //!< 0x0090 U_WMR90 record + U_WMR_91, //!< 0x0091 U_WMR91 record + U_WMR_92, //!< 0x0092 U_WMR92 record + U_WMR_93, //!< 0x0093 U_WMR93 record + U_WMR_94, //!< 0x0094 U_WMR94 record + U_WMR_95, //!< 0x0095 U_WMR95 record + U_WMR_96, //!< 0x0096 U_WMR96 record + U_WMR_97, //!< 0x0097 U_WMR97 record + U_WMR_98, //!< 0x0098 U_WMR98 record + U_WMR_99, //!< 0x0099 U_WMR99 record + U_WMR_9A, //!< 0x009A U_WMR9A record + U_WMR_9B, //!< 0x009B U_WMR9B record + U_WMR_9C, //!< 0x009C U_WMR9C record + U_WMR_9D, //!< 0x009D U_WMR9D record + U_WMR_9E, //!< 0x009E U_WMR9E record + U_WMR_9F, //!< 0x009F U_WMR9F record + U_WMR_A0, //!< 0x00A0 U_WMRA0 record + U_WMR_A1, //!< 0x00A1 U_WMRA1 record + U_WMR_A2, //!< 0x00A2 U_WMRA2 record + U_WMR_A3, //!< 0x00A3 U_WMRA3 record + U_WMR_A4, //!< 0x00A4 U_WMRA4 record + U_WMR_A5, //!< 0x00A5 U_WMRA5 record + U_WMR_A6, //!< 0x00A6 U_WMRA6 record + U_WMR_A7, //!< 0x00A7 U_WMRA7 record + U_WMR_A8, //!< 0x00A8 U_WMRA8 record + U_WMR_A9, //!< 0x00A9 U_WMRA9 record + U_WMR_AA, //!< 0x00AA U_WMRAA record + U_WMR_AB, //!< 0x00AB U_WMRAB record + U_WMR_AC, //!< 0x00AC U_WMRAC record + U_WMR_AD, //!< 0x00AD U_WMRAD record + U_WMR_AE, //!< 0x00AE U_WMRAE record + U_WMR_AF, //!< 0x00AF U_WMRAF record + U_WMR_B0, //!< 0x00B0 U_WMRB0 record + U_WMR_B1, //!< 0x00B1 U_WMRB1 record + U_WMR_B2, //!< 0x00B2 U_WMRB2 record + U_WMR_B3, //!< 0x00B3 U_WMRB3 record + U_WMR_B4, //!< 0x00B4 U_WMRB4 record + U_WMR_B5, //!< 0x00B5 U_WMRB5 record + U_WMR_B6, //!< 0x00B6 U_WMRB6 record + U_WMR_B7, //!< 0x00B7 U_WMRB7 record + U_WMR_B8, //!< 0x00B8 U_WMRB8 record + U_WMR_B9, //!< 0x00B9 U_WMRB9 record + U_WMR_BA, //!< 0x00BA U_WMRBA record + U_WMR_BB, //!< 0x00BB U_WMRBB record + U_WMR_BC, //!< 0x00BC U_WMRBC record + U_WMR_BD, //!< 0x00BD U_WMRBD record + U_WMR_BE, //!< 0x00BE U_WMRBE record + U_WMR_BF, //!< 0x00BF U_WMRBF record + U_WMR_C0, //!< 0x00C0 U_WMRC0 record + U_WMR_C1, //!< 0x00C1 U_WMRC1 record + U_WMR_C2, //!< 0x00C2 U_WMRC2 record + U_WMR_C3, //!< 0x00C3 U_WMRC3 record + U_WMR_C4, //!< 0x00C4 U_WMRC4 record + U_WMR_C5, //!< 0x00C5 U_WMRC5 record + U_WMR_C6, //!< 0x00C6 U_WMRC6 record + U_WMR_C7, //!< 0x00C7 U_WMRC7 record + U_WMR_C8, //!< 0x00C8 U_WMRC8 record + U_WMR_C9, //!< 0x00C9 U_WMRC9 record + U_WMR_CA, //!< 0x00CA U_WMRCA record + U_WMR_CB, //!< 0x00CB U_WMRCB record + U_WMR_CC, //!< 0x00CC U_WMRCC record + U_WMR_CD, //!< 0x00CD U_WMRCD record + U_WMR_CE, //!< 0x00CE U_WMRCE record + U_WMR_CF, //!< 0x00CF U_WMRCF record + U_WMR_D0, //!< 0x00D0 U_WMRD0 record + U_WMR_D1, //!< 0x00D1 U_WMRD1 record + U_WMR_D2, //!< 0x00D2 U_WMRD2 record + U_WMR_D3, //!< 0x00D3 U_WMRD3 record + U_WMR_D4, //!< 0x00D4 U_WMRD4 record + U_WMR_D5, //!< 0x00D5 U_WMRD5 record + U_WMR_D6, //!< 0x00D6 U_WMRD6 record + U_WMR_D7, //!< 0x00D7 U_WMRD7 record + U_WMR_D8, //!< 0x00D8 U_WMRD8 record + U_WMR_D9, //!< 0x00D9 U_WMRD9 record + U_WMR_DA, //!< 0x00DA U_WMRDA record + U_WMR_DB, //!< 0x00DB U_WMRDB record + U_WMR_DC, //!< 0x00DC U_WMRDC record + U_WMR_DD, //!< 0x00DD U_WMRDD record + U_WMR_DE, //!< 0x00DE U_WMRDE record + U_WMR_DF, //!< 0x00DF U_WMRDF record + U_WMR_E0, //!< 0x00E0 U_WMRE0 record + U_WMR_E1, //!< 0x00E1 U_WMRE1 record + U_WMR_E2, //!< 0x00E2 U_WMRE2 record + U_WMR_E3, //!< 0x00E3 U_WMRE3 record + U_WMR_E4, //!< 0x00E4 U_WMRE4 record + U_WMR_E5, //!< 0x00E5 U_WMRE5 record + U_WMR_E6, //!< 0x00E6 U_WMRE6 record + U_WMR_E7, //!< 0x00E7 U_WMRE7 record + U_WMR_E8, //!< 0x00E8 U_WMRE8 record + U_WMR_E9, //!< 0x00E9 U_WMRE9 record + U_WMR_EA, //!< 0x00EA U_WMREA record + U_WMR_EB, //!< 0x00EB U_WMREB record + U_WMR_EC, //!< 0x00EC U_WMREC record + U_WMR_ED, //!< 0x00ED U_WMRED record + U_WMR_EE, //!< 0x00EE U_WMREE record + U_WMR_EF, //!< 0x00EF U_WMREF record + U_WMR_DELETEOBJECT, //!< 0x01F0 U_WMRDELETEOBJECT record + U_WMR_F1, //!< 0x00F1 U_WMRF1 record + U_WMR_F2, //!< 0x00F2 U_WMRF2 record + U_WMR_F3, //!< 0x00F3 U_WMRF3 record + U_WMR_F4, //!< 0x00F4 U_WMRF4 record + U_WMR_F5, //!< 0x00F5 U_WMRF5 record + U_WMR_F6, //!< 0x00F6 U_WMRF6 record + U_WMR_CREATEPALETTE, //!< 0x00F7 U_WMRCREATEPALETTE record + U_WMR_F8 , //!< 0x00F8 U_WMRF8 record + U_WMR_CREATEPATTERNBRUSH, //!< 0x01F9 U_WMRCREATEPATTERNBRUSH record + U_WMR_CREATEPENINDIRECT, //!< 0x02FA U_WMRCREATEPENINDIRECT record + U_WMR_CREATEFONTINDIRECT, //!< 0x02FB U_WMRCREATEFONTINDIRECT record + U_WMR_CREATEBRUSHINDIRECT, //!< 0x02FC U_WMRCREATEBRUSHINDIRECT record + U_WMR_CREATEBITMAPINDIRECT, //!< 0x02FD U_WMRCREATEBITMAPINDIRECT record + U_WMR_CREATEBITMAP, //!< 0x06FE U_WMRCREATEBITMAP record + U_WMR_CREATEREGION, //!< 0x06FF U_WMRCREATEREGION record +}; +/** @} */ +#define U_WMR_MIN 0 //!< Minimum U_WMR_ value. +#define U_WMR_MAX 255 //!< Maximum U_WMR_ value. +#define U_WMR_MASK 0xFF //!< Mask for enumerator (lower) byte +#define U_WMR_INVALID 0xFFFFFFFF //!< Not any valid U_EMF_ valuee + + +/** BinaryRasterOperation Enumeration WMF PDF 2.1.1.2 + + Same as U_EMRSETROP2 in uemf.h +*/ + +/** BitCount Enumeration WMF PDF 2.1.1.3 + \defgroup AltBitCount_Qualifiers Alternate names for the values under U_BITMAPINFOHEADER_biBitCount_Qualifiers in uemf.h + @{ +*/ +#define BI_BITCOUNT_0 U_BCBM_EXPLICIT //!< Derived from JPG or PNG compressed image or ? +#define BI_BITCOUNT_1 U_BCBM_MONOCHROME //!< 2 colors. bmiColors array has two entries +#define BI_BITCOUNT_2 U_BCBM_COLOR4 //!< 2^4 colors. bmiColors array has 16 entries +#define BI_BITCOUNT_3 U_BCBM_COLOR8 //!< 2^8 colors. bmiColors array has 256 entries +#define BI_BITCOUNT_4 U_BCBM_COLOR16 //!< 2^16 colors. bmiColors is not used. Pixels are 5 bits B,G,R with 1 unused bit +#define BI_BITCOUNT_5 U_BCBM_COLOR24 //!< 2^24 colors. bmiColors is not used. Pixels are U_RGBTRIPLE. +#define BI_BITCOUNT_6 U_BCBM_COLOR32 //!< 2^32 colors. bmiColors is not used. Pixels are U_RGBQUAD. +/** @} */ + +/** BrushStyle Enumeration WMF PDF 2.1.1.4 + Same as "LB_Style Enumeration" in uemf.h +*/ + +/** CharacterSet Enumeration WMF PDF 2.1.1.5 + Same as "LF_CharSet Enumeration" in uemf.h +*/ + +/** ColorUsage Enumeration WMF PDF 2.1.1.6 + Same as "DIBColors Enumeration" in uemf.h, with one addition + \defgroup Extra_iUsageSrc_Qualifiers Extra DIBColors Enumeration + For cUsage fields in various DIB related records. + @{ +*/ +#define U_DIB_PAL_INDICES 2 //!< No color table, pixels are logical palette indices. +/** @} */ + +/** Compression Enumeration WMF PDF 2.1.1.7 + Same as "BI_Compression Enumeration" in uemf.h with these additions + \defgroup ExtraU_BITMAPINFOHEADER_biCompression_Qualifiers Extra BI_Compression Enumeration, none are implemented + @{ +*/ +#define U_BI_CMYK 0x000B //!< CMYK uncompressed +#define U_BI_CMYKRLE8 0x000C //!< CMYK RLE8 compression +#define U_BI_CMYKRLE4 = 0x000D //!< CMYK RLE4 compression +/** @} */ + +/** FamilyFont enumeration WMF PDF 2.1.1.8 + Only used in a PitchAndFamily object, defined there +*/ + +/** Floodfill enumeration WMF PDF 2.1.1.9 + Same as "FloodFill Enumeration" in uemf.h +*/ + +/** FontQuality enumeration WMF PDF 2.1.1.10 + Same as "LF_Quality Enumeration" in uemf.h +*/ + +/** GamutMappingIntent enumeration WMF PDF 2.1.1.11 + Same as "LCS_Intent Enumeration" in uemf.h +*/ + +/** HatchStyle enumeration WMF PDF 2.1.1.12 + Same as "HatchStyle Enumeration" in uemf.h +*/ + +/** Layout enumeration WMF PDF 2.1.1.13 + Same as "Mirroring Enumeration" in uemf.h +*/ + +/** LogicalColorSpace Enumeration WMF PDF 2.1.1.14 + Not used presently, applies in BitmapV4Header + @{ +*/ +//!< #define U_LCS_CALIBRATED_RGB 0x00000000 calibrated RGB +#define U_LCS_sRGB 0x73524742 //!< ASCII for "sRGB" +#define U_LCS_WINDOWS_COLOR_SPACE 0x57696E20 //!< ASCII for "Win " +/** @} */ + +/** LogicalColorSpaceV5 Enumeration WMF PDF 2.1.1.15 + Same as "Profile Enumeration" in uemf.h +*/ + +/** MapMode Enumeration WMF PDF 2.1.1.16 + Same as "MapMode Enumeration" in uemf.h +*/ + +/** MetaFilesEscape Enumeration WMF PDF 2.1.1.17 + \defgroup MFEscape_Qualifiers Metafile Escape record types + For U_WMRESCAPE eFunc field + @{ +*/ +#define U_MFE_NEWFRAME 0x0001 //!< NEWFRAME escape type +#define U_MFE_ABORTDOC 0x0002 //!< ABORTDOC escape type +#define U_MFE_NEXTBAND 0x0003 //!< NEXTBAND escape type +#define U_MFE_SETCOLORTABLE 0x0004 //!< SETCOLORTABLE escape type +#define U_MFE_GETCOLORTABLE 0x0005 //!< GETCOLORTABLE escape type +#define U_MFE_FLUSHOUT 0x0006 //!< FLUSHOUT escape type +#define U_MFE_DRAFTMODE 0x0007 //!< DRAFTMODE escape type +#define U_MFE_QUERYESCSUPPORT 0x0008 //!< QUERYESCSUPPORT escape type +#define U_MFE_SETABORTPROC 0x0009 //!< SETABORTPROC escape type +#define U_MFE_STARTDOC 0x000A //!< STARTDOC escape type +#define U_MFE_ENDDOC 0x000B //!< ENDDOC escape type +#define U_MFE_GETPHYSPAGESIZE 0x000C //!< GETPHYSPAGESIZE escape type +#define U_MFE_GETPRINTINGOFFSET 0x000D //!< GETPRINTINGOFFSET escape type +#define U_MFE_GETSCALINGFACTOR 0x000E //!< GETSCALINGFACTOR escape type +#define U_MFE_META_ESCAPE_ENHANCED_METAFILE 0x000F //!< META_ESCAPE_ENHANCED_METAFILE escape type +#define U_MFE_SETPENWIDTH 0x0010 //!< SETPENWIDTH escape type +#define U_MFE_SETCOPYCOUNT 0x0011 //!< SETCOPYCOUNT escape type +#define U_MFE_SETPAPERSOURCE 0x0012 //!< SETPAPERSOURCE escape type +#define U_MFE_PASSTHROUGH 0x0013 //!< PASSTHROUGH escape type +#define U_MFE_GETTECHNOLOGY 0x0014 //!< GETTECHNOLOGY escape type +#define U_MFE_SETLINECAP 0x0015 //!< SETLINECAP escape type +#define U_MFE_SETLINEJOIN 0x0016 //!< SETLINEJOIN escape type +#define U_MFE_SETMITERLIMIT 0x0017 //!< SETMITERLIMIT escape type +#define U_MFE_BANDINFO 0x0018 //!< BANDINFO escape type +#define U_MFE_DRAWPATTERNRECT 0x0019 //!< DRAWPATTERNRECT escape type +#define U_MFE_GETVECTORPENSIZE 0x001A //!< GETVECTORPENSIZE escape type +#define U_MFE_GETVECTORBRUSHSIZE 0x001B //!< GETVECTORBRUSHSIZE escape type +#define U_MFE_ENABLEDUPLEX 0x001C //!< ENABLEDUPLEX escape type +#define U_MFE_GETSETPAPERBINS 0x001D //!< GETSETPAPERBINS escape type +#define U_MFE_GETSETPRINTORIENT 0x001E //!< GETSETPRINTORIENT escape type +#define U_MFE_ENUMPAPERBINS 0x001F //!< ENUMPAPERBINS escape type +#define U_MFE_SETDIBSCALING 0x0020 //!< SETDIBSCALING escape type +#define U_MFE_EPSPRINTING 0x0021 //!< EPSPRINTING escape type +#define U_MFE_ENUMPAPERMETRICS 0x0022 //!< ENUMPAPERMETRICS escape type +#define U_MFE_GETSETPAPERMETRICS 0x0023 //!< GETSETPAPERMETRICS escape type +#define U_MFE_POSTSCRIPT_DATA 0x0025 //!< POSTSCRIPT_DATA escape type +#define U_MFE_POSTSCRIPT_IGNORE 0x0026 //!< POSTSCRIPT_IGNORE escape type +#define U_MFE_GETDEVICEUNITS 0x002A //!< GETDEVICEUNITS escape type +#define U_MFE_GETEXTENDEDTEXTMETRICS 0x0100 //!< GETEXTENDEDTEXTMETRICS escape type +#define U_MFE_GETPAIRKERNTABLE 0x0102 //!< GETPAIRKERNTABLE escape type +#define U_MFE_EXTTEXTOUT 0x0200 //!< EXTTEXTOUT escape type +#define U_MFE_GETFACENAME 0x0201 //!< GETFACENAME escape type +#define U_MFE_DOWNLOADFACE 0x0202 //!< DOWNLOADFACE escape type +#define U_MFE_METAFILE_DRIVER 0x0801 //!< METAFILE_DRIVER escape type +#define U_MFE_QUERYDIBSUPPORT 0x0C01 //!< QUERYDIBSUPPORT escape type +#define U_MFE_BEGIN_PATH 0x1000 //!< BEGIN_PATH escape type +#define U_MFE_CLIP_TO_PATH 0x1001 //!< CLIP_TO_PATH escape type +#define U_MFE_END_PATH 0x1002 //!< END_PATH escape type +#define U_MFE_OPEN_CHANNEL 0x100E //!< OPEN_CHANNEL escape type +#define U_MFE_DOWNLOADHEADER 0x100F //!< DOWNLOADHEADER escape type +#define U_MFE_CLOSE_CHANNEL 0x1010 //!< CLOSE_CHANNEL escape type +#define U_MFE_POSTSCRIPT_PASSTHROUGH 0x1013 //!< POSTSCRIPT_PASSTHROUGH escape type +#define U_MFE_ENCAPSULATED_POSTSCRIPT 0x1014 //!< ENCAPSULATED_POSTSCRIPT escape type +#define U_MFE_POSTSCRIPT_IDENTIFY 0x1015 //!< POSTSCRIPT_IDENTIFY escape type +#define U_MFE_POSTSCRIPT_INJECTION 0x1016 //!< POSTSCRIPT_INJECTION escape type +#define U_MFE_CHECKJPEGFORMAT 0x1017 //!< CHECKJPEGFORMAT escape type +#define U_MFE_CHECKPNGFORMAT 0x1018 //!< CHECKPNGFORMAT escape type +#define U_MFE_GET_PS_FEATURESETTING 0x1019 //!< GET_PS_FEATURESETTING escape type +#define U_MFE_MXDC_ESCAPE 0x101A //!< MXDC_ESCAPE escape type +#define U_MFE_SPCLPASSTHROUGH2 0x11D8 //!< SPCLPASSTHROUGH2 escape type +/** @} */ + +/** MetafileType Enumeration WMF PDF 2.1.1.18 + @{ +*/ +#define U_MEMORYMETAFILE 0x0001 //!< memory metafile (never used by libUWMF) +#define U_DISKMETAFILE 0x0002 //!< disk metafile (always used by libUWMF) +/** @} */ + +/** MetafileVersion Enumeration WMF PDF 2.1.1.19 + @{ +*/ + +#define U_METAVERSION100 0x0100 //!< DIBs not allowed +#define U_METAVERSION300 0x0300 //!< DIBs allowed +/** @} */ + +/** MixMode Enumeration WMF PDF 2.1.1.20 + Same as "BackgroundMode Enumeration" in uemf.h +*/ + +/** OutPrecision Enumeration WMF PDF 2.1.1.21 + Same as "LF_OutPrecision Enumeration" in uemf.h +*/ + +/** PaletteEntryFlag Enumeration WMF PDF 2.1.1.22 + @{ +*/ +#define U_PC_RESERVED 0x01 //!< used for animation +#define U_PC_EXPLICIT 0x02 //!< low order word is palette index +#define U_PC_NOCOLLAPSE 0x04 //!< store as new color in palette, do not match to existing color +/** @} */ + +/** PenStyle Enumeration WMF PDF 2.1.1.23 + Same as "PenStyle Enumeration" in uemf.h + EXCEPT no values >0xFFFF are used, in particular there is no U_PS_GEOMETRIC (ie, all are U_PS_COSMETIC). + Apparently because there is no U_PS_GEOMETRIC, U_PS_JOIN* and U_PS_ENDCAP* are also ignored by XP SP3 Preview + (which defaults to a rounded cap) and PowerPoint 2003 (which defaults to square cap). The behavior + was the same when escape records for JOIN and ENDCAP are used. Bottom line, WMF line formatting seems + to be very hit and miss from application to application. +*/ + +/** PitchFont Enumeration WMF PDF 2.1.1.24 + These are only used in PitchAndFamily object, defined there. +*/ + +/** PolyFillMode Enumeration WMF PDF 2.1.1.25 + These are the first twp emtries in "PolygonFillMode Enumeration" in uemf.h + +*/ + +/** PostScriptCap Enumeration WMF PDF 2.1.1.26 + These are used in Escape Cap + @{ +*/ +#define U_WPS_CAP_NOTSET -2 +#define U_WPS_CAP_FLAT 0 +#define U_WPS_CAP_ROUND 1 +#define U_WPS_CAP_SQUARE 2 +/** @} */ + +/** PostScriptClipping Enumeration WMF PDF 2.1.1.27 + PostFeatureSetting Enumeration WMF PDF 2.1.1.28 + + These are used by postscript drivers, not supported by libUWEMF. +*/ + +/** PostScrioptJoin Enumeration WMF PDF 2.1.1.29 + These are used in Escape Cap + @{ +*/ +#define U_WPS_JOIN_NOTSET -2 +#define U_WPS_JOIN_MITER 0 +#define U_WPS_JOIN_ROUND 1 +#define U_WPS_JOIN_BEVEL 2 +/** @} */ + +/** StretchMode Enumeration WMF PDF 2.1.1.30 + Same as "StretchMode Enumeration" in uemf.h + +*/ + +/** TernaryRasterOperation Enumeration WMF PDF 2.1.1.31 + Same as "Ternary Raster Operation Enumeration" in uemf.h + Only partially supported in libUWMF.h +*/ + +/** ClipPrecision Flags WMF PDF 2.1.2.1 + Same as "LF_ClipPrecision Enumeration" in uemf.h +*/ + +/** ExtTextOutOptions Flags WMF PDF 2.1.2.2 + These are a subset of "ExtTextOutOptions Enumeration" in uemf.h + Not defined for WMF: U_ETO_NONE, U_ETO_GRAYED, U_ETO_NORECT, + U_ETO_SMALL_CHARS,U_ETO_IGNORELANGUAGE,U_ETO_REVERSE_INDEX_MAP + Defined for WMF: U_ETO_OPAQUE, U_ETO_CLIPPED, U_ETO_GLYPH_INDEX, + U_ETO_RTLREADING,_ETO_NUMERICSLOCAL,U_ETO_NUMERICSLATIN, + U_ETO_PDY +*/ + +/** TextAlignment Enumeration WMF PDF 2.1.2.3 + VertialTextAlignment Enumeration WMF PDF 2.1.2.4 + These are both in "TextAlignment Enumeration" in uemf.h +*/ + + + +// *************************************************************************** +// Miscellaneous Values +/** TextAlignmentMode Flags WMF PDF 2.1.2.3 + VerticalTextAlignmentMode Flags WMF PDF 2.1.2.4 + Same as "TextAlignment Enumeration" in uemf.h +*/ + +/** \defgroup MinimumRecord_sizes Size in bytes of core record of each type. + These are USUALLY not the same + as the corresponding struct, so in general it is unsafe to use sizeof() with this code. + Always use the U_SIZE_x instead!!!! + Note that some records may actually be much, much longer than their minimum as they include strings, + bitmaps, and such. + @{ +*/ +/* Record sizeof (+ same, X differs) */ +#define U_SIZE_PAIRF 8 /* + 8 this might be different on 64 bit platform */ +#define U_SIZE_COLORREF 4 /* + 4 */ +#define U_SIZE_BRUSH 8 /* + 8 */ +#define U_SIZE_FONT 19 /* + 20 */ +#define U_SIZE_FONT_CORE 18 /* Minus the FaceName part */ +#define U_SIZE_PLTNTRY 4 /* + 4 */ +#define U_SIZE_PALETTE 8 /* + 8 */ +#define U_SIZE_PEN 10 /* + 10 */ +#define U_SIZE_POINT16 4 /* + 4 */ +#define U_SIZE_RECT16 8 /* + 8 */ +#define U_SIZE_REGION 20 /* X 22 20 is minums the variable part */ +#define U_SIZE_BITMAP16 10 /* + 10 */ +#define U_SIZE_BITMAPCOREHEADER 12 /* + 12 */ +#define U_SIZE_BITMAPINFOHEADER 40 /* + 40 */ +#define U_SIZE_BITMAPV4HEADER 108 /* ? 108 not tested */ +#define U_SIZE_BITMAPV5HEADER 124 /* ? 124 not tested */ +#define U_SIZE_WLOGBRUSH 8 /* + 8 */ +#define U_SIZE_POLYPOLYGON 4 /* + 4 */ +#define U_SIZE_SCAN 8 /* + 8 */ +#define U_SIZE_METARECORD 6 /* X 8 */ +#define U_SIZE_WMRPLACEABLE 22 /* X 24 */ +#define U_SIZE_WMRHEADER 18 /* X 20 */ +#define U_SIZE_WMREOF 6 /* X 8 */ +#define U_SIZE_WMRSETRELABS 6 /* X 8 */ +#define U_SIZE_WMRSAVEDC 6 /* X 8 */ +#define U_SIZE_WMRRESTOREDC 8 /* * 8 */ +#define U_SIZE_WMRREALIZEPALETTE 6 /* X 8 */ +#define U_SIZE_WMRSETBKCOLOR 10 /* X 12 */ +#define U_SIZE_WMRSETTEXTCOLOR 10 /* X 12 */ +#define U_SIZE_WMRSETBKMODE 8 /* X 12 last 2 bytes are optional */ +#define U_SIZE_WMRSETROP2 8 /* X 12 last 2 bytes are optional */ +#define U_SIZE_WMRSETPOLYFILLMODE 8 /* X 12 last 2 bytes are optional */ +#define U_SIZE_WMRSETSTRETCHBLTMODE 8 /* X 12 last 2 bytes are optional */ +#define U_SIZE_WMRSETTEXTALIGN 8 /* X 12 last 2 bytes are optional */ +#define U_SIZE_WMRSETMAPMODE 8 /* + 8 */ +#define U_SIZE_WMRSETTEXTCHAREXTRA 8 /* + 8 */ +#define U_SIZE_WMRSETTEXTJUSTIFICATION 10 /* X 12 */ +#define U_SIZE_WMRSETWINDOWORG 10 /* X 12 */ +#define U_SIZE_WMRSETWINDOWEXT 10 /* X 12 */ +#define U_SIZE_WMRSETVIEWPORTORG 10 /* X 12 */ +#define U_SIZE_WMRSETVIEWPORTEXT 10 /* X 12 */ +#define U_SIZE_WMROFFSETWINDOWORG 10 /* X 12 */ +#define U_SIZE_WMROFFSETVIEWPORTORG 10 /* X 12 */ +#define U_SIZE_WMRLINETO 10 /* X 12 */ +#define U_SIZE_WMRMOVETO 10 /* X 12 */ +#define U_SIZE_WMROFFSETCLIPRGN 10 /* X 12 */ +#define U_SIZE_WMRSCALEWINDOWEXT 14 /* X 16 */ +#define U_SIZE_WMRSCALEVIEWPORTEXT 14 /* X 16 */ +#define U_SIZE_WMREXCLUDECLIPRECT 14 /* X 16 */ +#define U_SIZE_WMRINTERSECTCLIPRECT 14 /* X 16 */ +#define U_SIZE_WMRARC 22 /* X 24 */ +#define U_SIZE_WMRELLIPSE 14 /* X 16 */ +#define U_SIZE_WMRRECTANGLE 14 /* X 16 */ +#define U_SIZE_WMRFLOODFILL 16 /* + 16 */ +#define U_SIZE_WMREXTFLOODFILL 16 /* + 16 */ +#define U_SIZE_WMRSETPIXEL 14 /* X 16 */ +#define U_SIZE_WMRPIE 22 /* X 24 */ +#define U_SIZE_WMRCHORD 22 /* X 24 */ +#define U_SIZE_WMRROUNDRECT 18 /* X 20 */ +#define U_SIZE_WMRPATBLT 18 /* X 20 */ +#define U_SIZE_WMRTEXTOUT 8 /* X 12 (not including String,y,x) */ +#define U_SIZE_WMRBITBLT_NOPX 24 /* + 24 */ +#define U_SIZE_WMRBITBLT_PX 22 /* X 32 */ +#define U_SIZE_WMRSTRETCHBLT_NOPX 28 /* + 28 */ +#define U_SIZE_WMRSTRETCHBLT_PX 26 /* X 36 */ +#define U_SIZE_WMRPOLYGON 10 /* X 12 */ +#define U_SIZE_WMRPOLYLINE 10 /* X 12 */ +#define U_SIZE_WMRESCAPE 10 /* X 12 Data field could be completely absent */ +#define U_SIZE_WMRFILLREGION 10 /* X 12 */ +#define U_SIZE_WMRFRAMEREGION 14 /* X 16 */ +#define U_SIZE_WMRINVERTREGION 8 /* + 8 */ +#define U_SIZE_WMRPAINTREGION 8 /* + 8 */ +#define U_SIZE_WMRSELECTCLIPREGION 8 /* + 8 */ +#define U_SIZE_WMRSELECTOBJECT 8 /* + 8 */ +#define U_SIZE_WMRSELECTPALETTE 8 /* + 8 */ +#define U_SIZE_WMRRESIZEPALETTE 8 /* + 8 */ +#define U_SIZE_WMRDELETEOBJECT 8 /* + 8 */ +#define U_SIZE_WMRDRAWTEXT 6 /* X 8 */ +#define U_SIZE_WMRCREATEBITMAPINDIRECT 6 /* X 8 */ +#define U_SIZE_WMRCREATEBITMAP 6 /* X 8 */ +#define U_SIZE_WMRSETMAPPERFLAGS 10 /* X 12 */ +#define U_SIZE_WMREXTTEXTOUT 14 /* X 16 */ +#define U_SIZE_WMRSETDIBTODEV 22 /* X 28 */ +#define U_SIZE_WMRANIMATEPALETTE 14 /* X 16 */ +#define U_SIZE_WMRSETPALENTRIES 14 /* X 16 */ +#define U_SIZE_WMRCREATEPALETTE 14 /* X 16 */ +#define U_SIZE_WMRPOLYPOLYGON 10 /* X 12 */ +#define U_SIZE_WMRDIBBITBLT_NOPX 24 /* + 24 */ +#define U_SIZE_WMRDIBBITBLT_PX 22 /* X 24 */ +#define U_SIZE_WMRDIBSTRETCHBLT_NOPX 28 /* + 28 */ +#define U_SIZE_WMRDIBSTRETCHBLT_PX 26 /* X 28 */ +#define U_SIZE_WMRDIBCREATEPATTERNBRUSH 10 /* X 12 */ +#define U_SIZE_WMRSTRETCHDIB 28 /* X 32 */ +#define U_SIZE_WMRCREATEPATTERNBRUSH 6 /* X 8 */ +#define U_SIZE_WMRCREATEPENINDIRECT 16 /* + 16 */ +#define U_SIZE_WMRCREATEFONTINDIRECT 26 /* X 28 */ +#define U_SIZE_WMRCREATEBRUSHINDIRECT 14 /* X 16 */ +#define U_SIZE_WMRCREATEREGION 26 /* X 28 */ +/** @} */ + + +// *************************************************************************** +// Macros + +/** \defgroup Common_macros Common Macros + @{ +*/ +/* Because Size16_4 may not be aligned no tests should dereference it directly from a pointer. +in NOPX tests cast causes uint8_t to promote to uint32_t, without it c++ compiler complains about +comparison of int with unsigned int */ +#define U_TEST_NOPX2(A,B) (A == (uint32_t) (B + 3)) /* A is Size16_4 (extracted and aligned), B = xb true if no bitmap associated with the structure, used with some BLT records*/ +#define U_TEST_NOPXB(A,B) (A/2 == (uint32_t) (B + 3)) /* A is Size16_4(extracted and aligned)*2, B - xb, true if no bitmap associated with the structure, used with some BLT records*/ +#define U_WMRTYPE(A) (((PU_METARECORD)A)->iType) //!< Get iType from U_WMR* record +#define U_WMRXB(A) (((PU_METARECORD)A)->xb) //!< Get xb from U_WMR* record +#define U_WMR_XB_FROM_TYPE(A) ((uint8_t) (U_wmr_values(A)>>8)) //!< Get xb from type value +#define U_U16(A) (*(uint16_t *)&A) /* interpret a 16 bit type as uint16_t */ +#define U_P16(A) ( (uint16_t *)&A) /* pass any 16 bit type as a pointer to a uint16_t */ +#define U_PP16(A) ( (uint16_t *) A) /* pass any pointer to a 16 bit type as a pointer to a uint16_t */ + +/** @} */ + +/* ************************************************************ + WMF structures OTHER than those corresponding to complete U_WMR_* records + ************************************************************ */ + +/** Brush Object WMF PDF 2.2.1.1 + + Documentation is muddy, bColor and bHatch fields have different meanings depending on + the value of bStyle. Unclear if bHatch bytes are present in some cases from the + documentation. + + style Color Data + U_BS_SOLID ColorRef Object Not used (bytes present???) + U_BS_NULL ignored ignored (bytes present???). + U_BS_PATTERN ignored Bitmap16 object holding patern + U_BS_DIBPATTERNPT ColorUsage Enum DIB object + U_BS_HATCHED ColorRef Object HatchStyle Enumeration +*/ + +typedef struct { + uint16_t Style; //!< BrushStyle enumeration + U_COLORREF Color; //!< Brush Color value, 32 bit value is not aligned. + uint8_t Data[1]; //!< Brush pattern information, variable size and format +} U_BRUSH, *PU_BRUSH; + + +/** Font Object WMF PDF 2.2.1.2 + Warning, only pass by pointer, passing by value will will truncate in Facename! +*/ +typedef struct { + int16_t Height; //!< Height in Logical units + int16_t Width; //!< Average Width in Logical units + int16_t Escapement; //!< Angle in 0.1 degrees betweem escapement vector and X axis + int16_t Orientation; //!< Angle in 0.1 degrees between baseline and X axis + int16_t Weight; //!< LF_Weight Enumeration + uint8_t Italic; //!< LF_Italic Enumeration + uint8_t Underline; //!< LF_Underline Enumeration + uint8_t StrikeOut; //!< LF_StrikeOut Enumeration + uint8_t CharSet; //!< LF_CharSet Enumeration + uint8_t OutPrecision; //!< LF_OutPrecision Enumeration + uint8_t ClipPrecision; //!< LF_ClipPrecision Enumeration + uint8_t Quality; //!< LF_Quality Enumeration + uint8_t PitchAndFamily; //!< LF_PitchAndFamily Enumeration + uint8_t FaceName[1]; //!< Name of font. ANSI Latin1, null terminated. +} U_FONT, *PU_FONT; + +/** PaletteEntry Object WMF PDF 2.2.2.13 + Note, NOT compatiable with U_LOGPLTNTRY + Out of PDF order because needed for next struture. +*/ +typedef struct { + uint8_t Value; //!< 0 or PaletteEntryFlag Enumeration + uint8_t Blue; //!< Palette entry Blue Intensity + uint8_t Green; //!< Palette entry Green Intensity + uint8_t Red; //!< Palette entry Red Intensity +} U_PLTNTRY, *PU_PLTNTRY; + +/** Palette Object WMF PDF 2.2.1.3 + NOT same as "LogPalette Object" in uemf.h because Palette Entries have reversed colors. + Values for palVersion are expanded + + Start must be 0x0300 (as for EMF) with U_WMRCREATEPALETTE but is an offset + for U_WMRSETPALENTRIES and U_ANIMATEPALETTE +*/ +typedef struct { + uint16_t Start; //!< Either 0x0300 or an offset into the Palette table + uint16_t NumEntries; //!< Number of U_LOGPLTNTRY objects + U_PLTNTRY PalEntries[1]; //!< Array of PaletteEntry Objects +} U_PALETTE, *PU_PALETTE; + +/** Pen Object WMF PDF 2.2.1.4 +*/ +typedef struct { + uint16_t Style; //!< PenStyle Enumeration + uint16_t Widthw[2]; //!< reassemble/store the Pen Width in object dimensions using Widthw, the 32 bit value is not aligned + U_COLORREF Color; //!< Pen Color, the 32 bit value is not aligned. +} U_PEN, *PU_PEN; + +/** Rect Object WMF PDF 2.2.2.18 + \brief Coordinates of the upper left, lower right corner. + Note that the coordinate system is 0,0 in the upper left corner + of the screen an N,M in the lower right corner. + Microsoft name: RECT Object COLLIDES with EMF Rect Object. + + This one is out of order because it is needed early. +*/ +typedef struct { + int16_t left; //!< left coordinate + int16_t top; //!< top coordinate + int16_t right; //!< right coordinate + int16_t bottom; //!< bottom coordinate +} U_RECT16, *PU_RECT16; + +#define U_RCL16_DEF (U_RECT16){0,0,-1,-1} //!< Use this when no bounds are needed. + +/** Region Object WMF PDF 2.2.1.5 +*/ +typedef struct { + uint16_t ignore1; //!< unused value + uint16_t Type; //!< must be 0x0006. + uint16_t ignore2; //!< unused value + int16_t Size; //!< aScans in bytes + regions size in bytes (size of this header plus all U_SCAN objects?) + int16_t sCount; //!< number of scanlines in region + int16_t sMax; //!< largest number of points in any scan + U_RECT16 sRect; //!< bounding rectangle + uint16_t aScans[1]; //!< series of appended U_SCAN objects +} U_REGION, *PU_REGION; + +/** Bitmap16 Object WMF PDF 2.2.2.1 + + The U_BITMAP16 core is always followed by + uint8_t Bits[1]; //!< bitmap pixel data. Bytes contained = (((Width * BitsPixel + 15) >> 4) << 1) * Height + Note that in U_WMRCREATEPATTERNBRUSH Bits is always [4]. + +*/ +typedef struct { + int16_t Type; //!< "bitmap type" MS PDF does not define this field beyond this. + int16_t Width; //!< bitmap width in pixels. + int16_t Height; //!< bitmap height in scan lines. + int16_t WidthBytes; //!< bytes per scan line. + uint8_t Planes; //!< must be 1. + uint8_t BitsPixel; //!< number of adjacent color bits on each plane (R bits + G bits + B bits ????) +} U_BITMAP16, *PU_BITMAP16; + +/** BitmapCoreHeader Object WMF PDF 2.2.2.2 +*/ +typedef struct { + uint16_t Size_4[2]; //!< size of U_BITMAPCOREHEADER in bytes. + uint16_t Width; //!< DIB width in pixels. + uint16_t Height; //!< DIB height in pixels. + uint16_t Planes; //!< must be 1 + uint16_t BitCount; //!< Pixel Format (BitCount Enumeration) +} U_BITMAPCOREHEADER, *PU_BITMAPCOREHEADER; + + +/** BitmapInfoHeader Object WMF PDF 2.2.2.3 + same as "BITMAPINFOHEADER Object" in uemf.h + use U_BITMAPINFOHEADER +*/ + +/** BitmapV4Header Object WMF PDF 2.2.2.4 +*/ +typedef struct { + uint32_t bV4Size; + int32_t bV4Width; + int32_t bV4Height; + uint16_t bV4Planes; + uint16_t bV4BitCount; + uint32_t bV4Compression; + uint32_t bV4SizeImage; + int32_t bV4XPelsPerMeter; + int32_t bV4YPelsPerMeter; + uint32_t bV4ClrUsed; + uint32_t bV4ClrImportant; + uint32_t bV4RedMask; + uint32_t bV4GreenMask; + uint32_t bV4BlueMask; + uint32_t bV4AlphaMask; + uint32_t bV4CSType; + U_CIEXYZTRIPLE bV4EndPoints; + uint32_t bV4GammaRed; + uint32_t bV4GammaGreen; + uint32_t bV4GammaBlue; +} U_BITMAPV4HEADER, *PU_BITMAPV4HEADER; //!< For ? + + +/** BitmapV5Header Object WMF PDF 2.2.2.5 +*/ +typedef struct { + uint32_t bV5Size; + int32_t bV5Width; + int32_t bV5Height; + uint16_t bV5Planes; + uint16_t bV5BitCount; + uint32_t bV5Compression; + uint32_t bV5SizeImage; + int32_t bV5XPelsPerMeter; + int32_t bV5YPelsPerMeter; + uint32_t bV5ClrUsed; + uint32_t bV5ClrImportant; + uint32_t bV5RedMask; + uint32_t bV5GreenMask; + uint32_t bV5BlueMask; + uint32_t bV5AlphaMask; + uint32_t bV5CSType; + U_CIEXYZTRIPLE bV5Endpoints; + uint32_t bV5GammaRed; + uint32_t bV5GammaGreen; + uint32_t bV5GammaBlue; + uint32_t bV5Intent; + uint32_t bV5ProfileData; + uint32_t bV5ProfileSize; + uint32_t bV5Reserved; +} U_BITMAPV5HEADER, *PU_BITMAPV5HEADER; //!< For ? + + + +/** CIEXYZ Object WMF PDF 2.2.2.6 + Same as "CIEXYZ Object" in uemf.h +*/ + +/** CIEXYZTriple Object WMF PDF 2.2.2.7 + Same as "CIEXYZTRIPLE Object" in uemf.h +*/ + +/** ColorRef Object WMF PDF 2.2.2.8 + Same as "COLORREF Object" in uemf.h +*/ + +/** DeviceIndependentBitmap Object WMF PDF 2.2.2.9 +This "object" has an organization, but not one that can be easily expressed with a C struct. It consists of +three parts, all of which have variable size: + + DIBHeaderInfo BitmapCoreHeader or BitmapInfoHeader Object + Colors Array of RGBQuad Objects or uint16_t that make a color table, as determined from the DIBHeaderInfo field. + BitMapBuffer Array of bytes containing the image. + +*/ + +/** WLogBrush Object WMF PDF 2.2.2.10 + Not compatible with EMF LogBrush object! + + style Color Hatch + U_BS_SOLID ColorRef Object Not used (bytes present???) + U_BS_NULL ignored ignored (bytes present???). + U_BS_PATTERN ignored not used (Action is not strictly defined) + U_BS_DIBPATTERN ignored not used (Action is not strictly defined) + U_BS_DIBPATTERNPT ignored not used (Action is not strictly defined) + U_BS_HATCHED ColorRef Object HatchStyle Enumeration +*/ +typedef struct { + uint16_t Style; //!< BrushStyle Enumeration + U_COLORREF Color; //!< Brush Color value, 32 bit value is not aligned. + uint16_t Hatch; //!< HatchStyle Enumeration +} U_WLOGBRUSH, *PU_WLOGBRUSH; + +/** LogColorSpace Object WMF PDF 2.2.2.11 + Same as "LOGCOLORSPACEA Object" in uemf.h + use U_LOGCOLORSPACEA +*/ + +/** LogColorSpaceW Object WMF PDF 2.2.2.12 + Same as "LOGCOLORSPACEW Object" in uemf.h + use U_LOGCOLORSPACEW +*/ + + +/** PaletteEntry Object WMF PDF 2.2.2.13 + moved up before Palette Object */ + +/** PitchAndFamily Enumerations WMF PDF 2.2.2.14 + Same as "LF_PitchAndFamily Enumeration" in uemf.h +*/ + +/** PointL Object WMF PDF 2.2.2.15 + Same as "Point Object" in uemf.h +*/ + +/** PointS Object WMF PDF 2.2.2.16 + Same as "POINTS Object" in uemf.h +*/ + +/** PolyPolygon Object WMF PDF 2.2.2.17 + There is an array "aPoints" of uint16_t after aPolyCounts that holds the coordinates. + Presumably it is in order [x1,y1],[x2,y2],etc. The documentation does not say, it might have + y then x. + aPoints starts at aPolyCounts[nPolys] +*/ +typedef struct { + uint16_t nPolys; //!< Number of polygons + uint16_t aPolyCounts[1]; //!< Number of points in each polygon (sequential) +} U_POLYPOLYGON, *PU_POLYPOLYGON; + +/** Rect Object WMF PDF 2.2.2.18 + This one is out of order, had to be created much earlier than this +*/ + +/** RectL Object WMF PDF 2.2.2.19 + Same as "RECT Object" in uemf.h +*/ + +/** RGBQuad Object WMF PDF 2.2.2.20 + Same as "RGBQUAD Object" in uemf.h +*/ + +/** Scan Object WMF PDF 2.2.2.21 + Field "count2" must follow ScanLines, but it cannot be placed into the struct. It is + an uint16_t value which must be the same as count. +*/ +typedef struct { + uint16_t count; //!< Number of entries in the ScanLines array + uint16_t top; //!< Y coordinate of the top scanline + uint16_t bottom; //!< Y coordinate of the bottom scanline + uint16_t ScanLines[1]; //!< Array of 16 bit left/right pairs +} U_SCAN, *PU_SCAN; + +/** SizeL Object WMF PDF 2.2.2.22 + Same as "SIZEL Object" in uemf.h +*/ + + +/** First three fields of MOST WMF records (not WMR_HEADER and WMR_PLACEABLE!) + Should only used for accessing size and type fields. + NOT used as a prefix like U_EMR in uemf.h because it may cause alignment issues. + Microsoft name: WMF Object +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type +} U_METARECORD, *PU_METARECORD; + +/** WMF PDF 2.3.2.3 META_PLACEABLE + If present this must immediately precede the header. + It is not enumerated as an WMR record type. + This only ever occurs at the start of a WMF file, so the two uint32_t values will always be aligned. +*/ +typedef struct { + uint32_t Key; //!< MUST be 0x9AC6CDD7 + uint16_t HWmf; //!< 0. (Always. Manual says total number of 16bit words in record, but no examples found like that) + U_RECT16 Dst; //!< Destination bounding box in logical units + uint16_t Inch; //!< Logical units/inch (convention if not specified: 1440 logical units/inch) + uint32_t Reserved; //!< must be 0 + uint16_t Checksum; //!< Checksum of preceding 10 16 bit values +} U_WMRPLACEABLE, *PU_WMRPLACEABLE; + +/** WMF PDF 2.3.2.2 META_HEADER +*/ +typedef struct { + uint8_t iType; //!< RecordType enumeration, must be 1 + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t Size16w; //!< Total number of 16bit words in record + uint16_t version; //!< Metafile version enumeration + uint16_t Sizew[2]; //!< reassemble/store the Size (16 bit words in entire file) using Sizew, the 32 bit value is not aligned + uint16_t nObjects; //!< Total number of brushes, pens, and other graphics objects defined in this file + uint32_t maxSize; //!< Largest record in file, in number of 16bit words (This uint32_t is aligned) + uint16_t nMembers; //!< Unused, should be 0 +} U_WMRHEADER, *PU_WMRHEADER; + + +// *********************************************************************************** +// The following structures correspond to U_WMR_# records + +/* Index 00 U_WMREOF WMF PDF 2.3.2.1 META_EOF */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type +} U_WMREOF, *PU_WMREOF, + U_WMRSETRELABS, *PU_WMRSETRELABS, + U_WMRSAVEDC, *PU_WMRSAVEDC, + U_WMRREALIZEPALETTE, *PU_WMRREALIZEPALETTE; + +/* Index 01 U_WMRSETBKCOLOR WMF PDF 2.3.5.14 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + U_COLORREF Color; //!< Color value, the 32 bit value is not aligned. +} U_WMRSETBKCOLOR, *PU_WMRSETBKCOLOR, + U_WMRSETTEXTCOLOR, *PU_WMRSETTEXTCOLOR; + +/* Index 02 U_WMRSETBKMODE WMF PDF 2.3.5.15 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t Mode; //!< Various Enumeraton. + uint16_t Reserved; //!< Ignore (ALSO OPTIONAL - FIELD MAY NOT BE PRESENT!!!!) +} U_WMRSETBKMODE, *PU_WMRSETBKMODE, //!< MixMode Enumeration. + U_WMRSETROP2, *PU_WMRSETROP2, //!< Binary Raster Operation Enumeration. + U_WMRSETPOLYFILLMODE, *PU_WMRSETPOLYFILLMODE, //!< PolyFillMode Enumeration. + U_WMRSETSTRETCHBLTMODE, *PU_WMRSETSTRETCHBLTMODE, //!< StretchMode Enumeration + U_WMRSETTEXTALIGN, *PU_WMRSETTEXTALIGN; //!< TextAlignment Enumeration. + +/* Index 03 U_WMRSETMAPMODE WMF PDF 2.3.5.17 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t Mode; //!< Various Enumeraton and other +} U_WMRSETMAPMODE, *PU_WMRSETMAPMODE, //!< MapMode Enumeration. + U_WMRSETTEXTCHAREXTRA, *PU_WMRSETTEXTCHAREXTRA; //!< Extra space in logical units to add to each character + +/* Index 04 U_WMRSETROP2 WMF PDF 2.3.5.22 See Index 02 */ + +/* Index 05 U_WMRSETRELABS WMF PDF 2.3.5.21 See Index 00*/ + +/* Index 06 U_WMRSETPOLYFILLMODE WMF PDF 2.3.5.20 See Index 02 + Index 07 U_WMRSETSTRETCHBLTMODE WMF PDF 2.3.5.23 */ + +/* Index 08 U_WMRSETTEXTCHAREXTRA WMF PDF 2.3.5.25 See Index 03*/ + +/* Index 09 U_WMRSETTEXTCOLOR WMF PDF 2.3.5.26 see Index 01 */ + +/* Index 0A U_WMRSETTEXTJUSTIFICATION WMF PDF 2.3.5.27 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t Count; //!< Number of space characters in the line + uint16_t Extra; //!< Number of extra space characters to add to the line +} U_WMRSETTEXTJUSTIFICATION, *PU_WMRSETTEXTJUSTIFICATION; + +/* Index 0B U_WMRSETWINDOWORG WMF PDF 2.3.5.31 + Index 0C U_WMRSETWINDOWEXT WMF PDF 2.3.5.30 + Index 0D U_WMRSETVIEWPORTORG WMF PDF 2.3.5.29 + Index 0E U_WMRSETVIEWPORTEXT WMF PDF 2.3.5.28 + Index 0F U_WMROFFSETWINDOWORG WMF PDF 2.3.5.7 + Index 13 U_WMRLINETO WMF PDF 2.3.3.10 + Index 14 U_WMRMOVETO WMF PDF 2.3.3.4 +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t y; //!< Y value (note order!) + int16_t x; //!< X value +} U_WMRSETWINDOWORG, *PU_WMRSETWINDOWORG, //!< Window X,Y origin + U_WMRSETWINDOWEXT, *PU_WMRSETWINDOWEXT, //!< Window X,Y extent + U_WMRSETVIEWPORTORG, *PU_WMRSETVIEWPORTORG, //!< Viewport X,Y origin + U_WMRSETVIEWPORTEXT, *PU_WMRSETVIEWPORTEXT, //!< Viewport X,Y extent + U_WMROFFSETWINDOWORG, *PU_WMROFFSETWINDOWORG, //!< Window X,Y offset in device units + U_WMROFFSETVIEWPORTORG, *PU_WMROFFSETVIEWPORTORG, //!< Viewport X,Y offset in device units + U_WMRLINETO, *PU_WMRLINETO, //!< Endpoint X,Y in logical units + U_WMRMOVETO, *PU_WMRMOVETO, //!< Destination X,Y in logical units + U_WMROFFSETCLIPRGN, *PU_WMROFFSETCLIPRGN; //!< ClipRegion X,Y offset in logical units + +/* Index 10 U_WMRSCALEWINDOWEXT WMF PDF 2.3.5.13 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t yDenom; //!< Y denominator + int16_t yNum; //!< Y numerator + int16_t xDenom; //!< X denominator + int16_t xNum; //!< X numerator +} U_WMRSCALEWINDOWEXT, *PU_WMRSCALEWINDOWEXT, + U_WMRSCALEVIEWPORTEXT, *PU_WMRSCALEVIEWPORTEXT; + +/* Index 11 U_WMROFFSETVIEWPORTORG WMF PDF 2.3.5.6 see Index 0B */ + +/* Index 12 U_WMRSCALEVIEWPORTEXT WMF PDF 2.3.5.12 see Index 10 */ + +/* Index 13 U_WMRLINETO WMF PDF 2.3.3.10 see index 0B + Index 14 U_WMRMOVETO WMF PDF 2.3.5.4 */ + +/* Index 15 U_WMREXCLUDECLIPRECT WMF PDF 2.3.5.2 + Index 16 U_WMRINTERSECTCLIPRECT WMF PDF 2.3.5.3 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t Bottom; //!< Coordinates in logical units + int16_t Right; //!< Coordinates in logical units + int16_t Top; //!< Coordinates in logical units + int16_t Left; //!< Coordinates in logical units +} U_WMREXCLUDECLIPRECT, *PU_WMREXCLUDECLIPRECT, + U_WMRINTERSECTCLIPRECT, *PU_WMRINTERSECTCLIPRECT; + +/* Index 17 U_WMRARC WMF PDF 2.3.3.1 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t yEndArc; //!< Coordinates in logical units + int16_t xEndArc; //!< Coordinates in logical units + int16_t yStartArc; //!< Coordinates in logical units + int16_t xStartArc; //!< Coordinates in logical units + int16_t Bottom; //!< Coordinates in logical units + int16_t Right; //!< Coordinates in logical units + int16_t Top; //!< Coordinates in logical units + int16_t Left; //!< Coordinates in logical units +} U_WMRARC, *PU_WMRARC; + +/* Index 18 U_WMRELLIPSE WMF PDF 2.3.3.3 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t Bottom; //!< Coordinates in logical units + int16_t Right; //!< Coordinates in logical units + int16_t Top; //!< Coordinates in logical units + int16_t Left; //!< Coordinates in logical units +} U_WMRELLIPSE, *PU_WMRELLIPSE, + U_WMRRECTANGLE, *PU_WMRRECTANGLE; + +/* Index 19 U_WMRFLOODFILL WMF PDF 2.3.3.7 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t Mode; //!< FloodFill Enumeration + U_COLORREF Color; //!< Color + int16_t y; //!< Y + int16_t x; //!< X +} U_WMRFLOODFILL, *PU_WMRFLOODFILL, + U_WMREXTFLOODFILL, *PU_WMREXTFLOODFILL; + +/* Index 1A U_WMRPIE WMF PDF 2.3.3.13 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t yRadial2; //!< in logical units + int16_t xRadial2; //!< in logical units + int16_t yRadial1; //!< in logical units + int16_t xRadial1; //!< in logical units + int16_t Bottom; //!< in logical units + int16_t Right; //!< in logical units + int16_t Top; //!< in logical units + int16_t Left; //!< in logical units +} U_WMRPIE, *PU_WMRPIE, + U_WMRCHORD, *PU_WMRCHORD; + +/* Index 1B U_WMRRECTANGLE WMF PDF 2.3.3.17 See Index 18 */ + +/* Index 1C U_WMRROUNDRECT WMF PDF 2.3.3.18 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t Height; //!< in logical units (rounded corner) + int16_t Width; //!< in logical units (rounded corner) + int16_t Bottom; //!< in logical units + int16_t Right; //!< in logical units + int16_t Top; //!< in logical units + int16_t Left; //!< in logical units +} U_WMRROUNDRECT, *PU_WMRROUNDRECT; + +/* Index 1D U_WMRPATBLT WMF PDF 2.3.3.12 +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t rop3w[2]; //!< reassemble/store the rop3 Ternary raster operation using rop3w, as the 32 bit value is not aligned + int16_t Height; //!< in logical units (of Rect to Fill) + int16_t Width; //!< in logical units (of Rect to Fill) + int16_t yDst; //!< in logical units (UL corner to fill) + int16_t xDst; //!< in logical units (UL corner to fill) +} U_WMRPATBLT, *PU_WMRPATBLT; + +/* Index 1E U_WMRSAVEDC WMF PDF 2.3.5.11 See Index 00*/ + +/* Index 1F U_WMRSETPIXEL WMF PDF 2.3.3.19 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + U_COLORREF Color; //!< Color + int16_t y; //!< Y + int16_t x; //!< X +} U_WMRSETPIXEL, *PU_WMRSETPIXEL; + +/* Index 20 U_WMROFFSETCLIPRGN WMF PDF 2.3.5.5 See Index 0B*/ + +/* Index 21 U_WMRTEXTOUT WMF PDF 2.3.3.20 + Also part of the record, but at variable positions + int16_t y; start position + int16_t x; start position +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t Length; //!< Stringlength in bytes + uint8_t String; //!< String to write, storage area must be 2n bytes. +} U_WMRTEXTOUT, *PU_WMRTEXTOUT; + +/* Index 22 U_WMRBITBLT WMF PDF 2.3.1.1 + This is a variable structure the core, invariant part extends to xSrc. + + if RecordSize == ((xb) + 3) then there is no bitmap and use the _NOPX form, otherwise use the _PX form + Use Macro U_TEST_NOPX2 + +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t rop3w[2]; //!< reassemble/store the Ternary raster operation rop3 value using rop3w, the 32 bit value is not aligned. + int16_t ySrc; //!< in logical units (UL corner of Src rect) + int16_t xSrc; //!< in logical units (UL corner of Src rect) + int16_t ignore; //!< ignore + int16_t Height; //!< in logical units (of Src and Dst rects) + int16_t Width; //!< in logical units (of Src and Dst rects) + int16_t yDst; //!< in logical units (UL corner of Dst rect) + int16_t xDst; //!< in logical units (UL corner of Dst rect) +} U_WMRBITBLT_NOPX, *PU_WMRBITBLT_NOPX; + +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t rop3w[2]; //!< reassemble/store the Ternary raster operation rop3 value using rop3w, the 32 bit value is not aligned. + int16_t ySrc; //!< in logical units (UL corner of Src rect) + int16_t xSrc; //!< in logical units (UL corner of Src rect) + int16_t Height; //!< in logical units (of Src and Dst rects) + int16_t Width; //!< in logical units (of Src and Dst rects) + int16_t yDst; //!< in logical units (UL corner of Dst rect) + int16_t xDst; //!< in logical units (UL corner of Dst rect) + U_BITMAP16 bitmap; //!< Src bitmap +} U_WMRBITBLT_PX, *PU_WMRBITBLT_PX; + + +/* Index 23 U_WMRSTRETCHBLT WMF PDF 2.3.1.5 + This is a variable structure the core, invariant part extends to xSrc. + + if RecordSize == ((xb) + 3) then there is no bitmap and use the _NOPX form, otherwise use the _PX form + Use Macro U_TEST_NOPX2. +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t rop3w[2]; //!< reassemble/store the Ternary raster operation rop3 value using rop3w, the 32 bit value is not aligned. + int16_t hSrc; //!< Height in logical units of Src rect + int16_t wSrc; //!< Wdith in logical units of Dst rect + int16_t ySrc; //!< in logical units (UL corner of Src rect) + int16_t xSrc; //!< in logical units (UL corner of Src rect) + int16_t ignore; //!< ignored + int16_t hDst; //!< Height in logical units of Dst rect + int16_t wDst; //!< Wdith in logical units of Dst rect + int16_t yDst; //!< in logical units (UL corner of Dst rect) + int16_t xDst; //!< in logical units (UL corner of Dst rect) +} U_WMRSTRETCHBLT_NOPX, *PU_WMRSTRETCHBLT_NOPX; + +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t rop3w[2]; //!< reassemble/store the Ternary raster operation rop3 value using rop3w, the 32 bit value is not aligned. + int16_t hSrc; //!< Height in logical units of Src rect + int16_t wSrc; //!< Wdith in logical units of Dst rect + int16_t ySrc; //!< in logical units (UL corner of Src rect) + int16_t xSrc; //!< in logical units (UL corner of Src rect) + int16_t hDst; //!< Height in logical units of Dst rect + int16_t wDst; //!< Wdith in logical units of Dst rect + int16_t yDst; //!< in logical units (UL corner of Dst rect) + int16_t xDst; //!< in logical units (UL corner of Dst rect) + U_BITMAP16 bitmap; //!< Src bitmap +} U_WMRSTRETCHBLT_PX, *PU_WMRSTRETCHBLT_PX; + +/* Index 24 U_WMRPOLYGON WMF PDF 2.3.3.15 + Index 25 U_WMRPOLYLINE WMF PDF 2.3.3.14 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t nPoints; //!< Number of points in aPoints + U_POINT16 aPoints[1]; //!< Array of points +} U_WMRPOLYGON, *PU_WMRPOLYGON, + U_WMRPOLYLINE, *PU_WMRPOLYLINE; + +/* Index 26 U_WMRESCAPE WMF PDF 2.3.6.1 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t eFunc; //!< Escape function + uint16_t nBytes; //!< bytes in the data array + uint8_t Data[1]; //!< data array +} U_WMRESCAPE, *PU_WMRESCAPE; + +/* Index 27 U_WMRRESTOREDC WMF PDF 2.3.5.10*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t DC; //!< DC to restore (negative is relative to current, positive is absolute) +} U_WMRRESTOREDC, *PU_WMRRESTOREDC; + +/* Index 28 U_WMRFILLREGION WMF PDF 2.3.3.6 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t Region; //!< Index of region to fill in object table + uint16_t Brush; //!< Index of brush to use in object table +} U_WMRFILLREGION, *PU_WMRFILLREGION; + +/* Index 29 U_WMRFRAMEREGION WMF PDF 2.3.3.8 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t Region; //!< Index of region to frame in object table + uint16_t Brush; //!< Index of brush to use in frame in object table + int16_t Height; //!< in logical units (of frame) + int16_t Width; //!< in logical units (of frame) +} U_WMRFRAMEREGION, *PU_WMRFRAMEREGION; + +/* Index 2A U_WMRINVERTREGION WMF PDF 2.3.3.9 + Index 2B U_WMRPAINTREGION WMF PDF 2.3.3.11 + Index 2C U_WMRSELECTCLIPREGION WMF PDF 2.3.4.9 + Index 2D U_WMRSELECTOBJECT WMF PDF 2.3.4.10 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t index; //!< (usually) index of region/object in object table +} U_WMRINVERTREGION, *PU_WMRINVERTREGION, //!< invert region + U_WMRPAINTREGION, *PU_WMRPAINTREGION, //!< paint region + U_WMRSELECTCLIPREGION, *PU_WMRSELECTCLIPREGION, //!< select as clip region + U_WMRSELECTOBJECT, *PU_WMRSELECTOBJECT, //!< select object + U_WMRSELECTPALETTE, *PU_WMRSELECTPALETTE, //!< select palette object + U_WMRRESIZEPALETTE, *PU_WMRRESIZEPALETTE, //!< resize the system palette to "index" + U_WMRDELETEOBJECT, *PU_WMRDELETEOBJECT; //!< delete object + +/* Index 2E U_WMRSETTEXTALIGN WMF PDF 2.3.5.24 See Index 02 */ + +/* Index 2F U_WMRDRAWTEXT in Wine, not in WMF PDF. + no documentation found, this part must be correct */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type +} U_WMRDRAWTEXT, *PU_WMRDRAWTEXT, + U_WMRCREATEBITMAPINDIRECT, *PU_WMRCREATEBITMAPINDIRECT, + U_WMRCREATEBITMAP, *PU_WMRCREATEBITMAP; + +/* Index 30 U_WMRCHORD WMF PDF 2.3.3.2 See Index 1A */ + +/* Index 31 U_WMRSETMAPPERFLAGS WMF PDF 2.3.5.18 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t valuew[2]; //!< if 1 bit set font mapper selects only matching aspect fonts. reassemble/store the value using valuew, the 32 bit value is not aligned. +} U_WMRSETMAPPERFLAGS, *PU_WMRSETMAPPERFLAGS; + +/* Index 32 U_WMREXTTEXTOUT WMF PDF 2.3.3.5 + Variable size structure. Common part is shown. + + U_RECT16 Rect; Only present when U_ETO_OPAQUE or U_ETO_CLIPPED bits are set in Opts + uint8_t String; String to write, storage area must be 2n bytes. + int16_t Dx; Kerning information. Must have same number of entries as Length. + Dx is present when + 2*Size16_4[2] -14 - 2*((Length + 1)/2)) - 8*(Opts & (U_ETO_OPAQUE | U_ETO_CLIPPED)) == 2*Length +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + int16_t y; //!< in logical units (draw point) + int16_t x; //!< in logical units (draw point) + int16_t Length; //!< Stringlength in bytes + uint16_t Opts; //!< ExtTextOutOptions Flags +} U_WMREXTTEXTOUT, *PU_WMREXTTEXTOUT; + +/* Index 33 U_WMRSETDIBTODEV WMF PDF 2.3.1.4 + Constant part of record is showon. It is followed by a + DeviceIndependentBitmap Object +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t cUsage; //!< ColorUsage enumeration + uint16_t ScanCount; //!< Number of scan lines in Src + uint16_t StartScan; //!< First Scan line in Src + int16_t ySrc; //!< in logical units (UL corner of Src rect) + int16_t xSrc; //!< in logical units (UL corner of Src rect) + int16_t Height; //!< in logical units (of Src and Dst) + int16_t Width; //!< in logical units (of Src and Dst) + int16_t yDst; //!< in logical units (UL corner of Dst rect) + int16_t xDst; //!< in logical units (UL corner of Dst rect) + uint8_t dib[1]; //!< DeviceIndependentBitmap object +} U_WMRSETDIBTODEV, *PU_WMRSETDIBTODEV; + +/* Index 34 U_WMRSELECTPALETTE WMF PDF 2.3.4.11 See Index 2A */ + +/* Index 35 U_WMRREALIZEPALETTE WMF PDF 2.3.5.8 See Index 00 */ + +/* Index 36 U_WMRANIMATEPALETTE WMF PDF 2.3.5.1 + Index 37 U_WMRSETPALENTRIES WMF PDF 2.3.5.19 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + U_PALETTE Palette; //!< Palette object +} U_WMRANIMATEPALETTE, *PU_WMRANIMATEPALETTE, + U_WMRSETPALENTRIES, *PU_WMRSETPALENTRIES, + U_WMRCREATEPALETTE, *PU_WMRCREATEPALETTE; + +/* Index 38 U_WMRPOLYPOLYGON WMF PDF 2.3.3.16 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + U_POLYPOLYGON PPolygon; //!< PolyPolygon object (size is variable!) +} U_WMRPOLYPOLYGON, *PU_WMRPOLYPOLYGON; + +/* Index 39 U_WMRRESIZEPALETTE WMF PDF 2.3.5.9 See Index 2A */ + +/* Index 40 U_WMRDIBBITBLT WMF PDF 2.3.1.2 + The PX form is a variable structure the core, invariant part extends to xDst, and that is + followed by a DeviceInvariantBitmap object which starts at "dib". + The NOPX form is a constant structure. + + if RecordSize == ((xb) + 3) then there is no bitmap and use the _NOPX form, otherwise use the _PX form + Use Macro U_TEST_NOPX2. +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t rop3w[2]; //!< reassemble/store the Ternary raster operation rop3 value using rop3w, the 32 bit value is not aligned. + int16_t ySrc; //!< in logical units (UL corner of Src rect) + int16_t xSrc; //!< in logical units (UL corner of Src rect) + uint16_t ignore; //!< ignore + int16_t Height; //!< in logical units (of Src and Dst) + int16_t Width; //!< in logical units (of Src and Dst) + int16_t yDst; //!< in logical units (UL corner of Dst rect) + int16_t xDst; //!< in logical units (UL corner of Dst rect) +} U_WMRDIBBITBLT_NOPX, *PU_WMRDIBBITBLT_NOPX; + +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t rop3w[2]; //!< reassemble/store the Ternary raster operation rop3 value using rop3w, the 32 bit value is not aligned. + int16_t ySrc; //!< in logical units (UL corner of Src rect) + int16_t xSrc; //!< in logical units (UL corner of Src rect) + int16_t Height; //!< in logical units (of Src and Dst) + int16_t Width; //!< in logical units (of Src and Dst) + int16_t yDst; //!< in logical units (UL corner of Dst rect) + int16_t xDst; //!< in logical units (UL corner of Dst rect) + uint8_t dib[1]; //!< DeviceIndependentBitmap object +} U_WMRDIBBITBLT_PX, *PU_WMRDIBBITBLT_PX; + +/* Index 41 U_WMRDIBSTRETCHBLT WMF PDF 2.3.1.3 + The PX form is a variable structure the core, invariant part extends to xDst, and that is + followed by a DeviceInvariantBitmap object which starts at "dib". + The NOPX form is a constant structure. + + if RecordSize == ((xb) + 3) then there is no bitmap and use the _NOPX form, otherwise use the _PX form + Use Macro U_TEST_NOPX2. +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t rop3w[2]; //!< reassemble/store the Ternary raster operation rop3 value using rop3w, the 32 bit value is not aligned. + int16_t hSrc; //!< in logical units (of Src) + int16_t wSrc; //!< in logical units (of Src) + int16_t ySrc; //!< in logical units (UL corner of Src rect) + int16_t xSrc; //!< in logical units (UL corner of Src rect) + uint16_t ignore; //!< ignore + int16_t hDst; //!< in logical units (of Dst) + int16_t wDst; //!< in logical units (of Dst) + int16_t yDst; //!< in logical units (UL corner of Dst rect) + int16_t xDst; //!< in logical units (UL corner of Dst rect) +} U_WMRDIBSTRETCHBLT_NOPX, *PU_WMRDIBSTRETCHBLT_NOPX; + +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t rop3w[2]; //!< reassemble/store the Ternary raster operation rop3 value using rop3w, the 32 bit value is not aligned. + int16_t hSrc; //!< in logical units (of Src) + int16_t wSrc; //!< in logical units (of Src) + int16_t ySrc; //!< in logical units (UL corner of Src rect) + int16_t xSrc; //!< in logical units (UL corner of Src rect) + int16_t hDst; //!< in logical units (of Dst) + int16_t wDst; //!< in logical units (of Dst) + int16_t yDst; //!< in logical units (UL corner of Dst rect) + int16_t xDst; //!< in logical units (UL corner of Dst rect) + uint8_t dib[1]; //!< DeviceIndependentBitmap object +} U_WMRDIBSTRETCHBLT_PX, *PU_WMRDIBSTRETCHBLT_PX; + + +/* Index 42 U_WMRDIBCREATEPATTERNBRUSH WMF PDF 2.3.4.8 + + style cUsage Brush created + U_BS_SOLID like U_BS_DIBPATTERNPT + U_BS_NULL like U_BS_DIBPATTERNPT + U_BS_HATCHED like U_BS_DIBPATTERNPT + U_BS_DIBPATTERNPT ColorUsage enumer. U_BS_DIBPATTERNPT brush from DIB in Src + U_BS_PATTERN ColorUsage enumer. U_BS_PATTERN brush from Bitmap16 object in Src +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t Style; //!< BrushStyle Enumeration + uint16_t cUsage; //!< See table above + uint8_t Src[1]; //!< DeviceIndependentBitmap or Bitmap16 object +} U_WMRDIBCREATEPATTERNBRUSH, *PU_WMRDIBCREATEPATTERNBRUSH; + +/* Index 43 U_WMRSTRETCHDIB WMF PDF 2.3.1.6 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + uint16_t rop3w[2]; //!< reassemble/store the Ternary raster operation rop3 value using rop3w, the 32 bit value is not aligned. + uint16_t cUsage; //!< ColorUsage enumeration + int16_t hSrc; //!< in logical units (of Src) + int16_t wSrc; //!< in logical units (of Src) + int16_t ySrc; //!< in logical units (UL corner of Src rect) + int16_t xSrc; //!< in logical units (UL corner of Src rect) + int16_t hDst; //!< in logical units (of Dst) + int16_t wDst; //!< in logical units (of Dst) + int16_t yDst; //!< in logical units (UL corner of Dst rect) + int16_t xDst; //!< in logical units (UL corner of Dst rect) + uint8_t dib[1]; //!< DeviceIndependentBitmap object +} U_WMRSTRETCHDIB, *PU_WMRSTRETCHDIB; + +/* Index 48 U_WMREXTFLOODFILL WMF PDF 2.3.3.4 See Index 19*/ +/* Index 4C U_WMR4C */ +/* Index 4D U_WMR4D */ +/* Index 4F U_WMR4F */ +/* Index 50 U_WMR50 */ +/* Index 52 U_WMR52 */ +/* Index 5E U_WMR5E */ +/* Index 5F U_WMR5F */ +/* Index 60 U_WMR60 */ +/* Index 61 U_WMR61 */ +/* Index 62 U_WMR62 */ +/* Index 63 U_WMR63 */ +/* Index 64 U_WMR64 */ +/* Index 65 U_WMR65 */ +/* Index 66 U_WMR66 */ +/* Index 67 U_WMR67 */ +/* Index 68 U_WMR68 */ +/* Index 69 U_WMR69 */ +/* Index 6A U_WMR6A */ +/* Index 6B U_WMR6B */ +/* Index 6C U_WMR6C */ +/* Index 6D U_WMR6D */ +/* Index 6E U_WMR6E */ +/* Index 6F U_WMR6F */ +/* Index 70 U_WMR70 */ +/* Index 71 U_WMR71 */ +/* Index 72 U_WMR72 */ +/* Index 73 U_WMR73 */ +/* Index 74 U_WMR74 */ +/* Index 75 U_WMR75 */ +/* Index 76 U_WMR76 */ +/* Index 77 U_WMR77 */ +/* Index 78 U_WMR78 */ +/* Index 79 U_WMR79 */ +/* Index 7A U_WMR7A */ +/* Index 7B U_WMR7B */ +/* Index 7C U_WMR7C */ +/* Index 7D U_WMR7D */ +/* Index 7E U_WMR7E */ +/* Index 7F U_WMR7F */ +/* Index 80 U_WMR80 */ +/* Index 81 U_WMR81 */ +/* Index 82 U_WMR82 */ +/* Index 83 U_WMR83 */ +/* Index 84 U_WMR84 */ +/* Index 85 U_WMR85 */ +/* Index 86 U_WMR86 */ +/* Index 87 U_WMR87 */ +/* Index 88 U_WMR88 */ +/* Index 89 U_WMR89 */ +/* Index 8A U_WMR8A */ +/* Index 8B U_WMR8B */ +/* Index 8C U_WMR8C */ +/* Index 8D U_WMR8D */ +/* Index 8E U_WMR8E */ +/* Index 8F U_WMR8F */ +/* Index 90 U_WMR90 */ +/* Index 91 U_WMR91 */ +/* Index 92 U_WMR92 */ +/* Index 93 U_WMR93 */ +/* Index 94 U_WMR94 */ +/* Index 95 U_WMR95 */ +/* Index 96 U_WMR96 */ +/* Index 97 U_WMR97 */ +/* Index 98 U_WMR98 */ +/* Index 99 U_WMR99 */ +/* Index 9A U_WMR9A */ +/* Index 9B U_WMR9B */ +/* Index 9C U_WMR9C */ +/* Index 9D U_WMR9D */ +/* Index 9E U_WMR9E */ +/* Index 9F U_WMR9F */ +/* Index A0 U_WMRA0 */ +/* Index A1 U_WMRA1 */ +/* Index A2 U_WMRA2 */ +/* Index A3 U_WMRA3 */ +/* Index A4 U_WMRA4 */ +/* Index A5 U_WMRA5 */ +/* Index A6 U_WMRA6 */ +/* Index A7 U_WMRA7 */ +/* Index A8 U_WMRA8 */ +/* Index A9 U_WMRA9 */ +/* Index AA U_WMRAA */ +/* Index AB U_WMRAB */ +/* Index AC U_WMRAC */ +/* Index AD U_WMRAD */ +/* Index AE U_WMRAE */ +/* Index AF U_WMRAF */ +/* Index B0 U_WMRB0 */ +/* Index B1 U_WMRB1 */ +/* Index B2 U_WMRB2 */ +/* Index B3 U_WMRB3 */ +/* Index B4 U_WMRB4 */ +/* Index B5 U_WMRB5 */ +/* Index B6 U_WMRB6 */ +/* Index B7 U_WMRB7 */ +/* Index B8 U_WMRB8 */ +/* Index B9 U_WMRB9 */ +/* Index BA U_WMRBA */ +/* Index BB U_WMRBB */ +/* Index BC U_WMRBC */ +/* Index BD U_WMRBD */ +/* Index BE U_WMRBE */ +/* Index BF U_WMRBF */ +/* Index C0 U_WMRC0 */ +/* Index C1 U_WMRC1 */ +/* Index C2 U_WMRC2 */ +/* Index C3 U_WMRC3 */ +/* Index C4 U_WMRC4 */ +/* Index C5 U_WMRC5 */ +/* Index C6 U_WMRC6 */ +/* Index C7 U_WMRC7 */ +/* Index C8 U_WMRC8 */ +/* Index C9 U_WMRC9 */ +/* Index CA U_WMRCA */ +/* Index CB U_WMRCB */ +/* Index CC U_WMRCC */ +/* Index CD U_WMRCD */ +/* Index CE U_WMRCE */ +/* Index CF U_WMRCF */ +/* Index D0 U_WMRD0 */ +/* Index D1 U_WMRD1 */ +/* Index D2 U_WMRD2 */ +/* Index D3 U_WMRD3 */ +/* Index D4 U_WMRD4 */ +/* Index D5 U_WMRD5 */ +/* Index D6 U_WMRD6 */ +/* Index D7 U_WMRD7 */ +/* Index D8 U_WMRD8 */ +/* Index D9 U_WMRD9 */ +/* Index DA U_WMRDA */ +/* Index DB U_WMRDB */ +/* Index DC U_WMRDC */ +/* Index DD U_WMRDD */ +/* Index DE U_WMRDE */ +/* Index DF U_WMRDF */ +/* Index E0 U_WMRE0 */ +/* Index E1 U_WMRE1 */ +/* Index E2 U_WMRE2 */ +/* Index E3 U_WMRE3 */ +/* Index E4 U_WMRE4 */ +/* Index E5 U_WMRE5 */ +/* Index E6 U_WMRE6 */ +/* Index E7 U_WMRE7 */ +/* Index E8 U_WMRE8 */ +/* Index E9 U_WMRE9 */ +/* Index EA U_WMREA */ +/* Index EB U_WMREB */ +/* Index EC U_WMREC */ +/* Index ED U_WMRED */ +/* Index EE U_WMREE */ +/* Index EF U_WMREF */ +/* Index F0 U_WMRDELETEOBJECT WMF PDF 2.3.4.7 See Index 2A */ +/* Index F1 U_WMRF1 */ +/* Index F2 U_WMRF2 */ +/* Index F3 U_WMRF3 */ +/* Index F4 U_WMRF4 */ +/* Index F5 U_WMRF5 */ + +/* Index F7 U_WMRCREATEPALETTE WMF PDF 2.3.4.3 See Index 36*/ + +/* Index F8 U_WMRF8 */ +/* Index F9 U_WMRCREATEPATTERNBRUSH WMF PDF 2.3.4.4 + + This one is peculiar... + + After the core structure there is: + + 1. A truncated U_BITMAP16. Only the first 14 bytes are present, and the last 4 bytes (bits section) are ignored.\ + 2. 18 zero bytes (reserved) + 3. A pattern. The pattern is a byte array whose size is set by the fields in the U_BITMAP16 structure as follows: + + (((Width * BitsPixel + 15) >> 4) << 1) * Height + + brush created is BS_PATTERN + +*/ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type +} U_WMRCREATEPATTERNBRUSH, *PU_WMRCREATEPATTERNBRUSH; + +/* Index FA U_WMRCREATEPENINDIRECT WMF PDF 2.3.4.5 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + U_PEN pen; //!< Pen Object +} U_WMRCREATEPENINDIRECT, *PU_WMRCREATEPENINDIRECT; + +/* Index FB U_WMRCREATEFONTINDIRECT WMF PDF 2.3.4.2 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + U_FONT font; //!< Font Object +} U_WMRCREATEFONTINDIRECT, *PU_WMRCREATEFONTINDIRECT; + +/* Index FC U_WMRCREATEBRUSHINDIRECT WMF PDF 2.3.4.1 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + U_WLOGBRUSH brush; //!< WLogBrush Object +} U_WMRCREATEBRUSHINDIRECT, *PU_WMRCREATEBRUSHINDIRECT; + +/* Index FD U_WMRCREATEBITMAPINDIRECT in Wine, not in WMF PDF see index 2F */ + +/* Index FE U_WMRCREATEBITMAP in Wine, not in WMF PDF see index 2F */ + +/* Index FF U_WMRCREATEREGION WMF PDF 2.3.4.6 */ +typedef struct { + uint16_t Size16_4[2]; //!< Total number of 16bit words in record + uint8_t iType; //!< RecordType enumeration + uint8_t xb; //!< Extra high order byte associated with record type + U_REGION region; //!< Region Object +} U_WMRCREATEREGION, *PU_WMRCREATEREGION; + + + +// ************************************************************************************************ +// Utility function structures + +typedef struct { + FILE *fp; //!< Open file + size_t allocated; //!< Size of the buffer + size_t used; //!< Amount consumed + uint32_t records; //!< Number of records already contained + uint16_t ignore; //!< size padding,not used + uint32_t PalEntries; //!< Number of PalEntries (set from U_EMREOF) + uint32_t chunk; //!< Number of bytes to add when more space is needed + char *buf; //!< Buffer for constructing the EMF in memory + uint32_t largest; //!< Largest record size, in bytes (used by WMF, not by EMF) + uint32_t sumObjects; //!< Number of objects created (used by WMF, not by EMF) +} WMFTRACK; + +/** + The various create functions need a place to put their handles, these are stored in the table below. + We don't actually do anything much with these handles, that is up to whatever program finally plays back the WMF, but + we do need to keep track of the numbers so that they are not accidentally reused. (Also WMF files have rules + about how object handles must be numbered, for instance, the lowest possible number must always be used. These + are different from EMF object handles.) This structure is used for staying in conformance with these rules. + + There are no stock objects in WMF files. +*/ +typedef struct { + uint32_t *table; //!< Array Buffer for constructing the WMF in memory + size_t allocated; //!< Slots in the buffer + size_t chunk; //!< Number to add if a realloc is required + uint32_t lolimit; //!< Lowest unoccupied table slot, may be a hole created by a deleteobject. + uint32_t hilimit; //!< Highest table slot occupied (currently) + uint32_t peak; //!< Highest table slot occupied (ever) +} WMFHANDLES; + +// ************************************************************************************************ +// Prototypes (_set first, then _get) +char *wmr_dup(const char *wmr); +int wmf_start(const char *name, uint32_t initsize, uint32_t chunksize, WMFTRACK **wt); +int wmf_free(WMFTRACK **wt); +int wmf_finish(WMFTRACK *wt); +int wmf_append(PU_METARECORD rec, WMFTRACK *wt, int freerec); +int wmf_header_append(PU_METARECORD rec,WMFTRACK *et, int freerec); +int wmf_readdata(const char *filename, char **contents, size_t*length); +#define wmf_fopen emf_fopen +int wmf_htable_create(uint32_t initsize, uint32_t chunksize, WMFHANDLES **wht); +int wmf_htable_delete(uint32_t *ih, WMFHANDLES *wht); +int wmf_htable_insert(uint32_t *ih, WMFHANDLES *wht); +int wmf_htable_free(WMFHANDLES **wht); +int16_t U_16_checksum(int16_t *buf, int count); +int16_t *dx16_set( int32_t height, uint32_t weight, uint32_t members); +uint32_t U_wmr_properties(uint32_t type); + +uint32_t U_wmr_size(const U_METARECORD *record); +uint32_t U_wmr_values(int idx); +char *U_wmr_names(int idx); +char *U_wmr_escnames(int idx); + +void U_sanerect16(U_RECT16 rc, double *left, double *top, double *right, double *bottom); + + +PU_FONT U_FONT_set(int16_t Height, int16_t Width, int16_t Escapement, int16_t Orientation, + int16_t Weight, uint8_t Italic, uint8_t Underline, uint8_t StrikeOut, + uint8_t CharSet, uint8_t OutPrecision, uint8_t ClipPrecision, + uint8_t Quality, uint8_t PitchAndFamily, char *FaceName); +U_PLTNTRY U_PLTNTRY_set(U_COLORREF Color); +PU_PALETTE U_PLTENTRY_set(uint16_t Start, uint16_t NumEntries, PU_PLTNTRY Entries); +U_PEN U_PEN_set(uint16_t Style, uint16_t Width, U_COLORREF Color); +U_RECT16 U_RECT16_set(U_POINT16 ul,U_POINT16 lr); +PU_BITMAP16 U_BITMAP16_set(const int16_t Type, const int16_t Width, const int16_t Height, + const int16_t LineN, const uint8_t BitsPixel, const char *Bits); +PU_SCAN U_SCAN_set(uint16_t count, uint16_t top, uint16_t bottom, uint16_t *ScanLines); +PU_REGION U_REGION_set(int16_t Size, int16_t sCount, int16_t sMax, U_RECT16 sRect, uint16_t *aScans); +U_WLOGBRUSH U_WLOGBRUSH_set(uint16_t Style, U_COLORREF Color, uint16_t Hatch); +PU_PAIRF U_PAIRF_set(float x, float y); + +char *wdeleteobject_set(uint32_t *ihObject, WMFHANDLES *wht); +char *wselectobject_set(uint32_t ihObject, WMFHANDLES *wht ); +char *wcreatepenindirect_set(uint32_t *ihPen, WMFHANDLES *wht, U_PEN pen); +char *wcreatebrushindirect_set(uint32_t *ihBrush, WMFHANDLES *wht, U_WLOGBRUSH lb); +char *wcreatedibpatternbrush_srcdib_set(uint32_t *ihBrush, WMFHANDLES *wht, + uint32_t iUsage, const PU_BITMAPINFO Bmi, uint32_t cbPx, const char *Px); +char *wcreatedibpatternbrush_srcbm16_set(uint32_t *ihBrush, WMFHANDLES *wht, + uint32_t iUsage, const PU_BITMAP16 Bm16); +char *wcreatepatternbrush_set(uint32_t *ihBrush, WMFHANDLES *wht, PU_BITMAP16 Bm16, char *Pattern); +char *wcreatefontindirect_set(uint32_t *ihFont, WMFHANDLES *wht, PU_FONT uf); +char *wcreatepalette_set(uint32_t *ihPal, WMFHANDLES *wht, PU_PALETTE up); +char *wsetpaletteentries_set(uint32_t *ihPal, WMFHANDLES *wht, const PU_PALETTE Palletes); +char *wcreateregion_set(uint32_t *ihReg, WMFHANDLES *wht, const PU_REGION Region); +char *wbegin_path_set(void); +char *wend_path_set(void); +char *wlinecap_set(int32_t Type); +char *wlinejoin_set(int32_t Type); +char *wmiterlimit_set(int32_t limit); + + +char *U_WMRHEADER_set(PU_PAIRF size,unsigned int dpi); +char *U_WMREOF_set(void); +char *U_WMRSETBKCOLOR_set(U_COLORREF Color); +char *U_WMRSETBKMODE_set(uint16_t Mode); +char *U_WMRSETMAPMODE_set(uint16_t Mode); +char *U_WMRSETROP2_set(uint16_t Mode); +char *U_WMRSETRELABS_set(void); +char *U_WMRSETPOLYFILLMODE_set(uint16_t Mode); +char *U_WMRSETSTRETCHBLTMODE_set(uint16_t Mode); +char *U_WMRSETTEXTCHAREXTRA_set(uint16_t Mode); +char *U_WMRSETTEXTCOLOR_set(U_COLORREF Color); +char *U_WMRSETTEXTJUSTIFICATION_set(uint16_t Count, uint16_t Extra); +char *U_WMRSETWINDOWORG_set(U_POINT16 coord); +char *U_WMRSETWINDOWEXT_set(U_POINT16 extent); +char *U_WMRSETVIEWPORTORG_set(U_POINT16 coord); +char *U_WMRSETVIEWPORTEXT_set(U_POINT16 extent); +char *U_WMROFFSETWINDOWORG_set(U_POINT16 offset); +char *U_WMRSCALEWINDOWEXT_set(U_POINT16 Denom, U_POINT16 Num); +char *U_WMROFFSETVIEWPORTORG_set(U_POINT16 offset); +char *U_WMRSCALEVIEWPORTEXT_set(U_POINT16 Denom, U_POINT16 Num); +char *U_WMRLINETO_set(U_POINT16 coord); +char *U_WMRMOVETO_set(U_POINT16 coord); +char *U_WMREXCLUDECLIPRECT_set(U_RECT16 rect); +char *U_WMRINTERSECTCLIPRECT_set(U_RECT16 rect); +char *U_WMRARC_set(U_POINT16 StartArc, U_POINT16 EndArc, U_RECT16 rect); +char *U_WMRELLIPSE_set(U_RECT16 rect); +char *U_WMRFLOODFILL_set(uint16_t Mode, U_COLORREF Color, U_POINT16 coord); +char *U_WMRPIE_set(U_POINT16 Radial1, U_POINT16 Radial2, U_RECT16 rect); +char *U_WMRRECTANGLE_set(U_RECT16 rect); +char *U_WMRROUNDRECT_set(int16_t Width, int16_t Height, U_RECT16 rect); +char *U_WMRPATBLT_set(U_POINT16 Dst, U_POINT16 cwh, uint32_t dwRop3); +char *U_WMRSAVEDC_set(void); +char *U_WMRSETPIXEL_set(U_COLORREF Color, U_POINT16 coord); +char *U_WMROFFSETCLIPRGN_set(U_POINT16 offset); +char *U_WMRTEXTOUT_set(U_POINT16 Dst, char *string); +char *U_WMRBITBLT_set(U_POINT16 Dst, U_POINT16 cwh, U_POINT16 Src, + uint32_t dwRop3, const PU_BITMAP16 Bm16); +char *U_WMRSTRETCHBLT_set(U_POINT16 Dst, U_POINT16 cDst, U_POINT16 Src, + U_POINT16 cSrc, uint32_t dwRop3, const PU_BITMAP16 Bm16); +char *U_WMRPOLYGON_set(uint16_t Length, const PU_POINT16 Data); +char *U_WMRPOLYLINE_set(uint16_t Length, const PU_POINT16 Data); +char *U_WMRESCAPE_set(uint16_t Escape, uint16_t Length, const void *Data); +char *U_WMRRESTOREDC_set(int16_t DC); +char *U_WMRFILLREGION_set(uint16_t Region, uint16_t Brush); +char *U_WMRFRAMEREGION_set(uint16_t Region, uint16_t Brush, int16_t Height, int16_t Width); +char *U_WMRINVERTREGION_set(uint16_t Region); +char *U_WMRPAINTREGION_set(uint16_t Region); +char *U_WMRSELECTCLIPREGION_set(uint16_t Region); +char *U_WMRSELECTOBJECT_set(uint16_t object); +char *U_WMRSETTEXTALIGN_set(uint16_t Mode); +char *U_WMRDRAWTEXT_set(void); /* in Wine, not in WMF PDF. */ +char *U_WMRCHORD_set(U_POINT16 Radial1, U_POINT16 Radial2, U_RECT16 rect); +char *U_WMRSETMAPPERFLAGS_set(uint32_t Mode); +char *U_WMREXTTEXTOUT_set(U_POINT16 Dst, int16_t Length, uint16_t Opts, const char *string, int16_t *dx, U_RECT16 rect); +char *U_WMRSETDIBTODEV_set(void); +char *U_WMRSELECTPALETTE_set(uint16_t Palette); +char *U_WMRREALIZEPALETTE_set(void); +char *U_WMRANIMATEPALETTE_set(PU_PALETTE Palette); +char *U_WMRSETPALENTRIES_set(PU_PALETTE Palette); +char *U_WMRPOLYPOLYGON_set(const uint16_t, const uint16_t *aPolyCounts, const PU_POINT16 points); +char *U_WMRRESIZEPALETTE_set(uint16_t Palette); +char *U_WMR3A_set(void); +char *U_WMR3B_set(void); +char *U_WMR3C_set(void); +char *U_WMR3D_set(void); +char *U_WMR3E_set(void); +char *U_WMR3F_set(void); +char *U_WMRDIBBITBLT_set(U_POINT16 Dst, U_POINT16 cwh, U_POINT16 Src, + uint32_t dwRop3, const PU_BITMAPINFO Bmi, uint32_t cbPx, const char *Px); +char *U_WMRDIBSTRETCHBLT_set(U_POINT16 Dst, U_POINT16 cDst, U_POINT16 Src, + U_POINT16 cSrc, uint32_t dwRop3, const PU_BITMAPINFO Bmi, uint32_t cbPx, const char *Px); +char *U_WMRDIBCREATEPATTERNBRUSH_set(const uint16_t Style, const uint16_t iUsage, + PU_BITMAPINFO Bmi, uint32_t cbPx, const char *Px, PU_BITMAP16 Bm16); +char *U_WMRSTRETCHDIB_set(U_POINT16 Dest, U_POINT16 cDest, U_POINT16 Src, U_POINT16 cSrc, + const uint16_t cUsage, uint32_t dwRop3, const PU_BITMAPINFO Bmi, uint32_t cbPx, const char *Px); +char *U_WMR44_set(void); +char *U_WMR45_set(void); +char *U_WMR46_set(void); +char *U_WMR47_set(void); +char *U_WMREXTFLOODFILL_set(uint16_t Mode, U_COLORREF Color, U_POINT16 coord); +char *U_WMR49_set(void); +char *U_WMR4A_set(void); +char *U_WMR4B_set(void); +char *U_WMR4C_set(void); +char *U_WMR4D_set(void); +char *U_WMR4E_set(void); +char *U_WMR4F_set(void); +char *U_WMR50_set(void); +char *U_WMR51_set(void); +char *U_WMRABORTDOC_set(void); +char *U_WMR53_set(void); +char *U_WMR54_set(void); +char *U_WMR55_set(void); +char *U_WMR56_set(void); +char *U_WMR57_set(void); +char *U_WMR58_set(void); +char *U_WMR59_set(void); +char *U_WMR5A_set(void); +char *U_WMR5B_set(void); +char *U_WMR5C_set(void); +char *U_WMR5D_set(void); +char *U_WMR5E_set(void); +char *U_WMR5F_set(void); +char *U_WMR60_set(void); +char *U_WMR61_set(void); +char *U_WMR62_set(void); +char *U_WMR63_set(void); +char *U_WMR64_set(void); +char *U_WMR65_set(void); +char *U_WMR66_set(void); +char *U_WMR67_set(void); +char *U_WMR68_set(void); +char *U_WMR69_set(void); +char *U_WMR6A_set(void); +char *U_WMR6B_set(void); +char *U_WMR6C_set(void); +char *U_WMR6D_set(void); +char *U_WMR6E_set(void); +char *U_WMR6F_set(void); +char *U_WMR70_set(void); +char *U_WMR71_set(void); +char *U_WMR72_set(void); +char *U_WMR73_set(void); +char *U_WMR74_set(void); +char *U_WMR75_set(void); +char *U_WMR76_set(void); +char *U_WMR77_set(void); +char *U_WMR78_set(void); +char *U_WMR79_set(void); +char *U_WMR7A_set(void); +char *U_WMR7B_set(void); +char *U_WMR7C_set(void); +char *U_WMR7D_set(void); +char *U_WMR7E_set(void); +char *U_WMR7F_set(void); +char *U_WMR80_set(void); +char *U_WMR81_set(void); +char *U_WMR82_set(void); +char *U_WMR83_set(void); +char *U_WMR84_set(void); +char *U_WMR85_set(void); +char *U_WMR86_set(void); +char *U_WMR87_set(void); +char *U_WMR88_set(void); +char *U_WMR89_set(void); +char *U_WMR8A_set(void); +char *U_WMR8B_set(void); +char *U_WMR8C_set(void); +char *U_WMR8D_set(void); +char *U_WMR8E_set(void); +char *U_WMR8F_set(void); +char *U_WMR90_set(void); +char *U_WMR91_set(void); +char *U_WMR92_set(void); +char *U_WMR93_set(void); +char *U_WMR94_set(void); +char *U_WMR95_set(void); +char *U_WMR96_set(void); +char *U_WMR97_set(void); +char *U_WMR98_set(void); +char *U_WMR99_set(void); +char *U_WMR9A_set(void); +char *U_WMR9B_set(void); +char *U_WMR9C_set(void); +char *U_WMR9D_set(void); +char *U_WMR9E_set(void); +char *U_WMR9F_set(void); +char *U_WMRA0_set(void); +char *U_WMRA1_set(void); +char *U_WMRA2_set(void); +char *U_WMRA3_set(void); +char *U_WMRA4_set(void); +char *U_WMRA5_set(void); +char *U_WMRA6_set(void); +char *U_WMRA7_set(void); +char *U_WMRA8_set(void); +char *U_WMRA9_set(void); +char *U_WMRAA_set(void); +char *U_WMRAB_set(void); +char *U_WMRAC_set(void); +char *U_WMRAD_set(void); +char *U_WMRAE_set(void); +char *U_WMRAF_set(void); +char *U_WMRB0_set(void); +char *U_WMRB1_set(void); +char *U_WMRB2_set(void); +char *U_WMRB3_set(void); +char *U_WMRB4_set(void); +char *U_WMRB5_set(void); +char *U_WMRB6_set(void); +char *U_WMRB7_set(void); +char *U_WMRB8_set(void); +char *U_WMRB9_set(void); +char *U_WMRBA_set(void); +char *U_WMRBB_set(void); +char *U_WMRBC_set(void); +char *U_WMRBD_set(void); +char *U_WMRBE_set(void); +char *U_WMRBF_set(void); +char *U_WMRC0_set(void); +char *U_WMRC1_set(void); +char *U_WMRC2_set(void); +char *U_WMRC3_set(void); +char *U_WMRC4_set(void); +char *U_WMRC5_set(void); +char *U_WMRC6_set(void); +char *U_WMRC7_set(void); +char *U_WMRC8_set(void); +char *U_WMRC9_set(void); +char *U_WMRCA_set(void); +char *U_WMRCB_set(void); +char *U_WMRCC_set(void); +char *U_WMRCD_set(void); +char *U_WMRCE_set(void); +char *U_WMRCF_set(void); +char *U_WMRD0_set(void); +char *U_WMRD1_set(void); +char *U_WMRD2_set(void); +char *U_WMRD3_set(void); +char *U_WMRD4_set(void); +char *U_WMRD5_set(void); +char *U_WMRD6_set(void); +char *U_WMRD7_set(void); +char *U_WMRD8_set(void); +char *U_WMRD9_set(void); +char *U_WMRDA_set(void); +char *U_WMRDB_set(void); +char *U_WMRDC_set(void); +char *U_WMRDD_set(void); +char *U_WMRDE_set(void); +char *U_WMRDF_set(void); +char *U_WMRE0_set(void); +char *U_WMRE1_set(void); +char *U_WMRE2_set(void); +char *U_WMRE3_set(void); +char *U_WMRE4_set(void); +char *U_WMRE5_set(void); +char *U_WMRE6_set(void); +char *U_WMRE7_set(void); +char *U_WMRE8_set(void); +char *U_WMRE9_set(void); +char *U_WMREA_set(void); +char *U_WMREB_set(void); +char *U_WMREC_set(void); +char *U_WMRED_set(void); +char *U_WMREE_set(void); +char *U_WMREF_set(void); +char *U_WMRDELETEOBJECT_set(uint16_t object); +char *U_WMRF1_set(void); +char *U_WMRF2_set(void); +char *U_WMRF3_set(void); +char *U_WMRF4_set(void); +char *U_WMRF5_set(void); +char *U_WMRF6_set(void); +char *U_WMRCREATEPALETTE_set(PU_PALETTE Palette); +char *U_WMRF8_set(void); +char *U_WMRCREATEPATTERNBRUSH_set(PU_BITMAP16 Bm16, char *Pattern); +char *U_WMRCREATEPENINDIRECT_set(U_PEN pen); +char *U_WMRCREATEFONTINDIRECT_set(PU_FONT font); +char *U_WMRCREATEBRUSHINDIRECT_set(U_WLOGBRUSH brush); +char *U_WMRCREATEBITMAPINDIRECT_set(void); /* in Wine, not in WMF PDF*/ +char *U_WMRCREATEBITMAP_set(void); /* in Wine, not in WMF PDF */ +char *U_WMRCREATEREGION_set(PU_REGION region); + +int16_t *dx16_get( int32_t height, uint32_t weight, uint32_t members); +size_t U_WMRRECSAFE_get(const char *contents, const char *blimit); +int wmfheader_get(const char *contents, const char *blimit, PU_WMRPLACEABLE Placeable, PU_WMRHEADER Header); +int wmr_arc_points(U_RECT16 rclBox, U_POINT16 ArcStart, U_POINT16 ArcEnd, + int *f1, int f2, PU_PAIRF center, PU_PAIRF start, PU_PAIRF end, PU_PAIRF size ); +void U_BITMAPINFOHEADER_get(const char *Bmih, uint32_t *Size, int32_t *Width, int32_t *Height, + uint32_t *Planes, uint32_t *BitCount, uint32_t *Compression, uint32_t *SizeImage, + int32_t *XPelsPerMeter, int32_t *YPelsPerMeter, uint32_t *ClrUsed, uint32_t *ClrImportant); +void U_BITMAPCOREHEADER_get(const char *BmiCh, int32_t *Size, int32_t *Width, int32_t *Height, int32_t *BitCount); +int wget_DIB_params(const char *dib, const char **px, const U_RGBQUAD **ct, int32_t *numCt, + int32_t *width, int32_t *height, int32_t *colortype, int32_t *invert); +int U_WMREOF_get(const char *contents); +int U_WMRSETBKCOLOR_get(const char *contents, PU_COLORREF Color); +int U_WMRSETBKMODE_get(const char *contents, uint16_t *Mode); +int U_WMRSETMAPMODE_get(const char *contents, uint16_t *Mode); +int U_WMRSETROP2_get(const char *contents, uint16_t *Mode); +int U_WMRSETRELABS_get(const char *contents); +int U_WMRSETPOLYFILLMODE_get(const char *contents, uint16_t *Mode); +int U_WMRSETSTRETCHBLTMODE_get(const char *contents, uint16_t *Mode); +int U_WMRSETTEXTCHAREXTRA_get(const char *contents, uint16_t *Mode); +int U_WMRSETTEXTCOLOR_get(const char *contents, PU_COLORREF Color); +int U_WMRSETTEXTJUSTIFICATION_get(const char *contents, uint16_t *Count, uint16_t *Extra); +int U_WMRSETWINDOWORG_get(const char *contents, PU_POINT16 coord); +int U_WMRSETWINDOWEXT_get(const char *contents, PU_POINT16 extent); +int U_WMRSETVIEWPORTORG_get(const char *contents, PU_POINT16 coord); +int U_WMRSETVIEWPORTEXT_get(const char *contents, PU_POINT16 extent); +int U_WMROFFSETWINDOWORG_get(const char *contents, PU_POINT16 offset); +int U_WMRSCALEWINDOWEXT_get(const char *contents, PU_POINT16 Denom, PU_POINT16 Num); +int U_WMROFFSETVIEWPORTORG_get(const char *contents, PU_POINT16 offset); +int U_WMRSCALEVIEWPORTEXT_get(const char *contents, PU_POINT16 Denom, PU_POINT16 Num); +int U_WMRLINETO_get(const char *contents, PU_POINT16 coord); +int U_WMRMOVETO_get(const char *contents, PU_POINT16 coord); +int U_WMREXCLUDECLIPRECT_get(const char *contents, PU_RECT16 rect); +int U_WMRINTERSECTCLIPRECT_get(const char *contents, PU_RECT16 rect); +int U_WMRARC_get(const char *contents, PU_POINT16 StartArc, PU_POINT16 EndArc, PU_RECT16 rect); +int U_WMRELLIPSE_get(const char *contents, PU_RECT16 rect); +int U_WMRFLOODFILL_get(const char *contents, uint16_t *Mode, PU_COLORREF Color, PU_POINT16 coord); +int U_WMRPIE_get(const char *contents, PU_POINT16 Radial1, PU_POINT16 Radial2, PU_RECT16 rect); +int U_WMRRECTANGLE_get(const char *contents, PU_RECT16 rect); +int U_WMRROUNDRECT_get(const char *contents, int16_t *Width, int16_t *Height, PU_RECT16 rect); +int U_WMRPATBLT_get(const char *contents, PU_POINT16 Dst, PU_POINT16 cwh, uint32_t *dwRop3); +int U_WMRSAVEDC_get(const char *contents); +int U_WMRSETPIXEL_get(const char *contents, PU_COLORREF Color, PU_POINT16 coord); +int U_WMROFFSETCLIPRGN_get(const char *contents, PU_POINT16 offset); +int U_WMRTEXTOUT_get(const char *contents, PU_POINT16 Dst, int16_t *Length, const char **string); +int U_WMRBITBLT_get(const char *contents, PU_POINT16 Dst, PU_POINT16 cwh, PU_POINT16 Src, uint32_t *dwRop3, PU_BITMAP16 Bm16, const char **px); +int U_WMRSTRETCHBLT_get(const char *contents, PU_POINT16 Dst, PU_POINT16 cDst, PU_POINT16 Src, PU_POINT16 cSrc, uint32_t *dwRop3, PU_BITMAP16 Bm16, const char **px); +int U_WMRPOLYGON_get(const char *contents, uint16_t *Length, const char **Data); +int U_WMRPOLYLINE_get(const char *contents, uint16_t *Length, const char **Data); +int U_WMRESCAPE_get(const char *contents, uint16_t *Escape, uint16_t *Length, const char **Data); +int U_WMRRESTOREDC_get(const char *contents, int16_t *DC); +int U_WMRFILLREGION_get(const char *contents, uint16_t *Region, uint16_t *Brush); +int U_WMRFRAMEREGION_get(const char *contents, uint16_t *Region, uint16_t *Brush, int16_t *Height, int16_t *Width); +int U_WMRINVERTREGION_get(const char *contents, uint16_t *Region); +int U_WMRPAINTREGION_get(const char *contents, uint16_t *Region); +int U_WMRSELECTCLIPREGION_get(const char *contents, uint16_t *Region); +int U_WMRSELECTOBJECT_get(const char *contents, uint16_t *Object); +int U_WMRSETTEXTALIGN_get(const char *contents, uint16_t *Mode); +int U_WMRDRAWTEXT_get(void); /* in Wine, not in WMF PDF. */ +int U_WMRCHORD_get(const char *contents, PU_POINT16 Radial1, PU_POINT16 Radial2, PU_RECT16 rect); +int U_WMRSETMAPPERFLAGS_get(const char *contents, uint32_t *Mode); +int U_WMREXTTEXTOUT_get(const char *contents, PU_POINT16 Dst, int16_t *Length, uint16_t *Opts, const char **string, const int16_t **dx, PU_RECT16 rect); +int U_WMRSETDIBTODEV_get(const char *contents, PU_POINT16 Dst, PU_POINT16 cwh, PU_POINT16 Src, uint16_t *cUsage, uint16_t *ScanCount, uint16_t *StartScan, const char **dib); +int U_WMRSELECTPALETTE_get(const char *contents, uint16_t *Palette); +int U_WMRREALIZEPALETTE_get(const char *contents); +int U_WMRANIMATEPALETTE_get(const char *contents, PU_PALETTE Palette, const char **PalEntries); +int U_WMRSETPALENTRIES_get(const char *contents, PU_PALETTE Palette, const char **PalEntries); +int U_WMRPOLYPOLYGON_get(const char *contents, uint16_t *nPolys, const uint16_t **aPolyCounts, const char **Points); +int U_WMRRESIZEPALETTE_get(const char *contents, uint16_t *Palette); +int U_WMR3A_get(void); +int U_WMR3B_get(void); +int U_WMR3C_get(void); +int U_WMR3D_get(void); +int U_WMR3E_get(void); +int U_WMR3F_get(void); +int U_WMRDIBBITBLT_get(const char *contents, PU_POINT16 Dst, PU_POINT16 cwh, PU_POINT16 Src, uint32_t *dwRop3, const char **dib); +int U_WMRDIBSTRETCHBLT_get(const char *contents, PU_POINT16 Dst, PU_POINT16 cDst, PU_POINT16 Src, PU_POINT16 cSrc, uint32_t *dwRop3, const char **dib); +int U_WMRDIBCREATEPATTERNBRUSH_get(const char *contents, uint16_t *Style, uint16_t *cUsage, const char **Bm16, const char **dib); +int U_WMRSTRETCHDIB_get(const char *contents, PU_POINT16 Dst, PU_POINT16 cDst, PU_POINT16 Src, PU_POINT16 cSrc, uint16_t *cUsage, uint32_t *dwRop3, const char **dib); +int U_WMR44_get(void); +int U_WMR45_get(void); +int U_WMR46_get(void); +int U_WMR47_get(void); +int U_WMREXTFLOODFILL_get(const char *contents, uint16_t *Mode, PU_COLORREF Color, PU_POINT16 coord); +int U_WMR49_get(void); +int U_WMR4A_get(void); +int U_WMR4B_get(void); +int U_WMR4C_get(void); +int U_WMR4D_get(void); +int U_WMR4E_get(void); +int U_WMR4F_get(void); +int U_WMR50_get(void); +int U_WMR51_get(void); +int U_WMRABORTDOC_get(void); +int U_WMR53_get(void); +int U_WMR54_get(void); +int U_WMR55_get(void); +int U_WMR56_get(void); +int U_WMR57_get(void); +int U_WMR58_get(void); +int U_WMR59_get(void); +int U_WMR5A_get(void); +int U_WMR5B_get(void); +int U_WMR5C_get(void); +int U_WMR5D_get(void); +int U_WMR5E_get(void); +int U_WMR5F_get(void); +int U_WMR60_get(void); +int U_WMR61_get(void); +int U_WMR62_get(void); +int U_WMR63_get(void); +int U_WMR64_get(void); +int U_WMR65_get(void); +int U_WMR66_get(void); +int U_WMR67_get(void); +int U_WMR68_get(void); +int U_WMR69_get(void); +int U_WMR6A_get(void); +int U_WMR6B_get(void); +int U_WMR6C_get(void); +int U_WMR6D_get(void); +int U_WMR6E_get(void); +int U_WMR6F_get(void); +int U_WMR70_get(void); +int U_WMR71_get(void); +int U_WMR72_get(void); +int U_WMR73_get(void); +int U_WMR74_get(void); +int U_WMR75_get(void); +int U_WMR76_get(void); +int U_WMR77_get(void); +int U_WMR78_get(void); +int U_WMR79_get(void); +int U_WMR7A_get(void); +int U_WMR7B_get(void); +int U_WMR7C_get(void); +int U_WMR7D_get(void); +int U_WMR7E_get(void); +int U_WMR7F_get(void); +int U_WMR80_get(void); +int U_WMR81_get(void); +int U_WMR82_get(void); +int U_WMR83_get(void); +int U_WMR84_get(void); +int U_WMR85_get(void); +int U_WMR86_get(void); +int U_WMR87_get(void); +int U_WMR88_get(void); +int U_WMR89_get(void); +int U_WMR8A_get(void); +int U_WMR8B_get(void); +int U_WMR8C_get(void); +int U_WMR8D_get(void); +int U_WMR8E_get(void); +int U_WMR8F_get(void); +int U_WMR90_get(void); +int U_WMR91_get(void); +int U_WMR92_get(void); +int U_WMR93_get(void); +int U_WMR94_get(void); +int U_WMR95_get(void); +int U_WMR96_get(void); +int U_WMR97_get(void); +int U_WMR98_get(void); +int U_WMR99_get(void); +int U_WMR9A_get(void); +int U_WMR9B_get(void); +int U_WMR9C_get(void); +int U_WMR9D_get(void); +int U_WMR9E_get(void); +int U_WMR9F_get(void); +int U_WMRA0_get(void); +int U_WMRA1_get(void); +int U_WMRA2_get(void); +int U_WMRA3_get(void); +int U_WMRA4_get(void); +int U_WMRA5_get(void); +int U_WMRA6_get(void); +int U_WMRA7_get(void); +int U_WMRA8_get(void); +int U_WMRA9_get(void); +int U_WMRAA_get(void); +int U_WMRAB_get(void); +int U_WMRAC_get(void); +int U_WMRAD_get(void); +int U_WMRAE_get(void); +int U_WMRAF_get(void); +int U_WMRB0_get(void); +int U_WMRB1_get(void); +int U_WMRB2_get(void); +int U_WMRB3_get(void); +int U_WMRB4_get(void); +int U_WMRB5_get(void); +int U_WMRB6_get(void); +int U_WMRB7_get(void); +int U_WMRB8_get(void); +int U_WMRB9_get(void); +int U_WMRBA_get(void); +int U_WMRBB_get(void); +int U_WMRBC_get(void); +int U_WMRBD_get(void); +int U_WMRBE_get(void); +int U_WMRBF_get(void); +int U_WMRC0_get(void); +int U_WMRC1_get(void); +int U_WMRC2_get(void); +int U_WMRC3_get(void); +int U_WMRC4_get(void); +int U_WMRC5_get(void); +int U_WMRC6_get(void); +int U_WMRC7_get(void); +int U_WMRC8_get(void); +int U_WMRC9_get(void); +int U_WMRCA_get(void); +int U_WMRCB_get(void); +int U_WMRCC_get(void); +int U_WMRCD_get(void); +int U_WMRCE_get(void); +int U_WMRCF_get(void); +int U_WMRD0_get(void); +int U_WMRD1_get(void); +int U_WMRD2_get(void); +int U_WMRD3_get(void); +int U_WMRD4_get(void); +int U_WMRD5_get(void); +int U_WMRD6_get(void); +int U_WMRD7_get(void); +int U_WMRD8_get(void); +int U_WMRD9_get(void); +int U_WMRDA_get(void); +int U_WMRDB_get(void); +int U_WMRDC_get(void); +int U_WMRDD_get(void); +int U_WMRDE_get(void); +int U_WMRDF_get(void); +int U_WMRE0_get(void); +int U_WMRE1_get(void); +int U_WMRE2_get(void); +int U_WMRE3_get(void); +int U_WMRE4_get(void); +int U_WMRE5_get(void); +int U_WMRE6_get(void); +int U_WMRE7_get(void); +int U_WMRE8_get(void); +int U_WMRE9_get(void); +int U_WMREA_get(void); +int U_WMREB_get(void); +int U_WMREC_get(void); +int U_WMRED_get(void); +int U_WMREE_get(void); +int U_WMREF_get(void); +int U_WMRDELETEOBJECT_get(const char *contents, uint16_t *Object); +int U_WMRF1_get(void); +int U_WMRF2_get(void); +int U_WMRF3_get(void); +int U_WMRF4_get(void); +int U_WMRF5_get(void); +int U_WMRF6_get(void); +int U_WMRCREATEPALETTE_get(const char *contents, PU_PALETTE Palette, const char **PalEntries); +int U_WMRF8_get(void); +int U_WMRCREATEPATTERNBRUSH_get(const char *contents, PU_BITMAP16 Bm16, int *pasize, const char **Pattern); +int U_WMRCREATEPENINDIRECT_get(const char *contents, PU_PEN pen); +int U_WMRCREATEFONTINDIRECT_get(const char *contents, const char **font); +int U_WMRCREATEBRUSHINDIRECT_get(const char *contents, const char **brush); +int U_WMRCREATEBITMAPINDIRECT_get(void); +int U_WMRCREATEBITMAP_get(void); +int U_WMRCREATEREGION_get(const char *contents, const char **Region); + + +#ifdef __cplusplus +} +#endif + +#endif /* _UWMF_ */ diff --git a/src/libuemf/uwmf_endian.c b/src/libuemf/uwmf_endian.c new file mode 100644 index 000000000..e14c7aa77 --- /dev/null +++ b/src/libuemf/uwmf_endian.c @@ -0,0 +1,1772 @@ +/** + @file uwmf_endian.c Functions for Swaping WMF records +*/ + +/* +File: uwmf_endian.c +Version: 0.1.2 +Date: 18-FEB-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2012 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> /* for offsetof() */ +#include <string.h> +#include "uwmf.h" +#include "uwmf_endian.h" + +// hide almost everything in this file from Doxygen +//! @cond +/* Prototypes for functions used here and defined in uemf_endian.c, but which are not supposed +to be used in end user code. */ + +void U_swap2(void *ul, unsigned int count); +void U_swap4(void *ul, unsigned int count); +void bitmapinfo_swap(char *Bmi); + +/* ********************************************************************************************** + These functions Swap standard objects used in the WMR records. + The low level ones do not append EOL. +*********************************************************************************************** */ + +/** + \brief Swap U_BITMAP16 object + \param b U_BITMAP16 object +*/ +void bitmap16_swap( + char *b + ){ + U_swap2(b,4); /* Type, Width, Height, WidthBytes */ + /* Planes and BitsPixel are bytes, so no swap needed */ + /* Bits[] pixel data should already be in order */ +} + +/** + \brief Swap a U_BRUSH object. + \param b U_BRUSH object. + style bColor bHatch + U_BS_SOLID ColorRef Object Not used (bytes present???) + U_BS_NULL ignored ignored (bytes present???). + U_BS_PATTERN ignored Bitmap16 object holding patern + U_BS_DIBPATTERNPT ColorUsage Enum DIB object + U_BS_HATCHED ColorRef Object HatchStyle Enumeration +*/ +void brush_swap( + char *b, + int torev + ){ + int Style; + if(torev){ Style = *(uint16_t *)(b + offsetof(U_BRUSH,Style)); } + U_swap2(b + offsetof(U_BRUSH,Style),1); + if(!torev){ Style = *(uint16_t *)(b + offsetof(U_BRUSH,Style)); } + /* Color is already in the right order */ + switch(Style){ + case U_BS_SOLID: + /* no/ignored data field */ + break; + case U_BS_NULL: + /* no/ignored data field */ + break; + case U_BS_PATTERN: + bitmap16_swap(b + offsetof(U_BRUSH,Data)); + break; + case U_BS_DIBPATTERNPT: + bitmapinfo_swap(b + offsetof(U_BRUSH,Data)); + break; + case U_BS_HATCHED: + /* no/ignored data field */ + break; + } +} + +/** + \brief Swap a U_FONT object from pointer. + \param lf U_FONT object +*/ +void font_swap( + char *f + ){ + U_swap2(f + offsetof(U_FONT,Height),5); /*Height, Width, Escapement, Orientation, Weight */ + /* Other fields are single bytes */ +} + +/** + \brief Swap a pointer to a U_PALETTE object. + \param lp Pointer to a U_PALETTE object. +*/ +void palette_swap( + char *p + ){ + U_swap2(p + offsetof(U_PALETTE,Start),2); /* Start, NumEntries*/ + /* PalEntries[1] is byte ordered, so no need to swap */ +} + +/** + \brief Swap a U_PEN object. + \param p U_PEN object +*/ +void pen_swap( + char *p + ){ + U_swap2(p + offsetof(U_PEN,Style),3); /* Style,Widthw[0],Widthw[1] */ + /* Color already in order */ +} + +/* there are no + void rect16_ltrb_swap() + void rect16_brtl_swap() +because rectangles are swapped using U_swap2 as an array of 4 int16 values. +*/ + + +/** + \brief Swap U_REGION object + \param rect U_REGION object + \param torev + PARTIAL IMPLEMENTATION +*/ +void region_swap( + char *reg, + int torev + ){ + int Size; + if(torev){ Size = *(int16_t *)(reg + offsetof(U_REGION,Size)); } + U_swap2(reg,10); /* ignore1 through sRrect*/ + if(!torev){ Size = *(int16_t *)(reg + offsetof(U_REGION,Size)); } + U_swap2(reg + U_SIZE_REGION, (Size - U_SIZE_REGION)/2); /* aScans */ +} + + +/** + \brief Swap U_BITMAPCOREHEADER object + \param ch U_BITMAPCOREHEADER object +*/ +void bitmapcoreheader_swap( + char *ch + ){ + U_swap4(ch + offsetof(U_BITMAPCOREHEADER,Size_4), 1); /* Size_4, may not be aligned */ + U_swap2(ch + offsetof(U_BITMAPCOREHEADER,Width),4); /* Width, Height, Planes, BitCount, */ +} + +/** LogBrushW Object WMF PDF 2.2.2.10 + \brief Swap a U_WLOGBRUSH object. + \param lb U_WLOGBRUSH object. + + style Color Hatch + U_BS_SOLID ColorRef Object Not used (bytes present???) + U_BS_NULL ignored ignored (bytes present???). + U_BS_PATTERN ignored not used (Action is not strictly defined) + U_BS_DIBPATTERN ignored not used (Action is not strictly defined) + U_BS_DIBPATTERNPT ignored not used (Action is not strictly defined) + U_BS_HATCHED ColorRef Object HatchStyle Enumeration +*/ +void wlogbrush_swap( + char *lb + ){ + U_swap2(lb + offsetof(U_WLOGBRUSH,Style),1); + /* Color is already in order */ + U_swap2(lb + offsetof(U_WLOGBRUSH,Hatch),1); +} + +/** + \brief Swap U_POLYPOLY object from pointer + \param pp PU_POLYPOLY object +*/ +void polypolygon_swap( + char *pp, + int torev + ){ + int i,totPoints; + uint16_t nPolys; + uint16_t *aPolyCounts; + if(torev){ nPolys = *(uint16_t *)(pp + offsetof(U_POLYPOLYGON, nPolys)); } + U_swap2(pp + offsetof(U_POLYPOLYGON, nPolys),1); + if(!torev){ nPolys = *(uint16_t *)(pp + offsetof(U_POLYPOLYGON, nPolys)); } + aPolyCounts = (uint16_t *)(pp + offsetof(U_POLYPOLYGON, aPolyCounts)); + if(torev){ + for(totPoints=0,i=0;i<nPolys; i++){ totPoints += aPolyCounts[i]; } + } + U_swap2(aPolyCounts,nPolys); + if(!torev){ + for(totPoints=0,i=0;i<nPolys; i++){ totPoints += aPolyCounts[i]; } + } + U_swap2(&(aPolyCounts[nPolys]),2*totPoints); /* 2 coords/ point */ +} + +/** + \brief Swap U_SCAN object + \param pp U_SCAN object +*/ +void scan_swap( + char *sc, + int torev + ){ + int count; + if(torev){ count = *(uint16_t *)sc; } + U_swap2(sc,3); /*count, top, bottom */ + if(!torev){ count = *(uint16_t *)sc; } + U_swap2(sc + offsetof(U_SCAN,ScanLines),count); +} + +/** + \brief Swap a summary of a DIB header + A DIB header in an WMF may be either a BitmapCoreHeader or BitmapInfoHeader. + \param dh void pointer to DIB header +*/ +void dibheader_swap( + char *dh, + int torev + ){ + int Size; + memcpy(&Size, dh, 4); /* may not be aligned */ + if(!torev)U_swap4(&Size,1); + if(Size == 0xC){ + bitmapcoreheader_swap(dh); + } + else { + bitmapinfo_swap(dh); + } +} + +/** + \brief Swap WMF header object + \param head uint8_t pointer to header + \returns size of entire header structure + + If the header is preceded by a placeable struture, Swap that as well. +*/ +int wmfheader_swap( + char *contents, + int torev + ){ + uint32_t Key,Size16w; + int size=0; + Key=*(uint32_t *)(contents + offsetof(U_WMRPLACEABLE,Key)); + if(!torev)U_swap4(&Key,1); + if(Key == 0x9AC6CDD7){ + U_swap4(contents + offsetof(U_WMRPLACEABLE,Key ),1); + U_swap2(contents + offsetof(U_WMRPLACEABLE,HWmf ),1); + U_swap2(contents + offsetof(U_WMRPLACEABLE,Dst ),4); + U_swap2(contents + offsetof(U_WMRPLACEABLE,Inch ),1); + U_swap4(contents + offsetof(U_WMRPLACEABLE,Reserved),1); + U_swap2(contents + offsetof(U_WMRPLACEABLE,Checksum),1); + contents += U_SIZE_WMRPLACEABLE; + size += U_SIZE_WMRPLACEABLE; + } + if(torev){ Size16w = *(uint16_t *)(contents + offsetof(U_WMRHEADER,Size16w)); } + U_swap2(contents + offsetof(U_WMRHEADER,Size16w),2);/* Size16w, Version */ + if(!torev){ Size16w = *(uint16_t *)(contents + offsetof(U_WMRHEADER,Size16w)); } + U_swap4(contents + offsetof(U_WMRHEADER,Sizew ),1);/* Sizew */ + U_swap2(contents + offsetof(U_WMRHEADER,nObjects),1);/* nObjects */ + U_swap4(contents + offsetof(U_WMRHEADER,maxSize ),1);/* maxSize */ + U_swap2(contents + offsetof(U_WMRHEADER,nMembers),1);/* nMembers */ + size += 2*Size16w; + return(size); +} + + + +// hide these from Doxygen +//! @cond +/* ********************************************************************************************** +These functions contain shared code used by various U_WMR*_Swap functions. These should NEVER be called +by end user code and to further that end prototypes are NOT provided and they are hidden from Doxygen. +*********************************************************************************************** */ + +/* Size16 EVERY record type should call this, directly or indirectly*/ +void U_WMRCORE_SIZE16_swap(char *record, int torev){ + torev = torev; // shuts up compiler warnings about unused parameters + U_swap4(record, 1); /* Size16_4 is at offset 0 in U_METARECORD */ +} + + +/* Size16, move to data, Single 32bit followed by array of N16 U_POINT16 */ +void U_WMRCORE_U32_N16_swap(char *record, int N16, int torev){ + int off=U_SIZE_METARECORD; + U_WMRCORE_SIZE16_swap(record, torev); + U_swap4(record + off, 1); off+=4; + U_swap2(record + off, N16); +} + +/* Single 16bit nPoints followed by array of nPoints U_POINT16 */ +void U_WMRCORE_U16_N16_swap(char *record, int torev){ + int nPoints; + U_WMRCORE_SIZE16_swap(record, torev); + if(torev){ nPoints = *(uint16_t *)(record + offsetof(U_WMRPOLYGON,nPoints)); } + U_swap2(record + offsetof(U_WMRPOLYGON,nPoints), 1); + if(!torev){ nPoints = *(uint16_t *)(record + offsetof(U_WMRPOLYGON,nPoints)); } + U_swap2(record + offsetof(U_WMRPOLYGON,aPoints), 2*nPoints); +} + +/* all records that specify palette objects */ +void U_WMRCORE_PALETTE_swap(char *record, int torev){ + torev = torev; // shuts up compiler warnings about unused parameters + U_WMRCORE_SIZE16_swap(record, torev); + palette_swap(record + offsetof(U_WMRANIMATEPALETTE,Palette)); +} + +/* all records that have N int16 values, unconditionally swapped */ +void U_WMRCORE_N16_swap(char *record, int N16, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); + U_swap2(record+U_SIZE_METARECORD, N16); +} + + +/* like floodfill */ +void U_WMRCORE_U16_CR_2U16_swap(char *record, int torev){ + int off = U_SIZE_METARECORD; + U_WMRCORE_SIZE16_swap(record, torev); + U_swap2(record+off, 1); off += 2 + sizeof(U_COLORREF); + U_swap2(record+off, 2); +} + + +//! @endcond + +/* ********************************************************************************************** +These are the core WMR functions, each creates a particular type of record. +All return these records via a char* pointer, which is NULL if the call failed. +They are listed in order by the corresponding U_EMR_* index number. +*********************************************************************************************** */ + +/** + \brief Swap a pointer to a U_WMR_whatever record which has not been implemented. + \param name name of this type of record + \param contents pointer to a buffer holding all EMR records + \param recnum number of this record in contents + \param off offset to this record in contents +*/ +void U_WMRNOTIMPLEMENTED_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); +} + +void U_WMREOF_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); +} + +void U_WMRSETBKCOLOR_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); +} + +void U_WMRSETBKMODE_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRSETMAPMODE_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRSETROP2_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRSETRELABS_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); +} + +void U_WMRSETPOLYFILLMODE_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRSETSTRETCHBLTMODE_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRSETTEXTCHAREXTRA_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRSETTEXTCOLOR_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); +} + +void U_WMRSETTEXTJUSTIFICATION_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMRSETWINDOWORG_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMRSETWINDOWEXT_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMRSETVIEWPORTORG_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMRSETVIEWPORTEXT_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMROFFSETWINDOWORG_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMRSCALEWINDOWEXT_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,4,torev); +} + +void U_WMROFFSETVIEWPORTORG_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMRSCALEVIEWPORTEXT_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,4,torev); +} + +void U_WMRLINETO_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMRMOVETO_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMREXCLUDECLIPRECT_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,4,torev); +} + +void U_WMRINTERSECTCLIPRECT_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,4,torev); +} + +void U_WMRARC_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record, 8, torev); +} + +void U_WMRELLIPSE_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,4,torev); +} + +void U_WMRFLOODFILL_swap(char *record, int torev){ + U_WMRCORE_U16_CR_2U16_swap(record, torev); +} + +void U_WMRPIE_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record, 8, torev); +} + +void U_WMRRECTANGLE_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,4,torev); +} + +void U_WMRROUNDRECT_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,6,torev); +} + +void U_WMRPATBLT_swap(char *record, int torev){ + U_WMRCORE_U32_N16_swap(record,4,torev); +} + +void U_WMRSAVEDC_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); +} + +void U_WMRSETPIXEL_swap(char *record, int torev){ + int off = U_SIZE_METARECORD + sizeof(U_COLORREF); + U_WMRCORE_SIZE16_swap(record, torev); + U_swap2(record+off, 2); +} + +void U_WMROFFSETCLIPRGN_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMRTEXTOUT_swap(char *record, int torev){ + int L2; + if(torev){ L2 = *(int16_t *)(record + U_SIZE_METARECORD); } /* Length field */ + U_WMRCORE_N16_swap(record,1,torev); /* Length */ + if(!torev){ L2 = *(int16_t *)(record + U_SIZE_METARECORD); } /* Length field */ + /* string is in bytes, do not swap that */ + U_swap2(record + U_SIZE_WMRTEXTOUT + L2, 2); /* y,x */ +} + +void U_WMRBITBLT_swap(char *record, int torev){ + uint32_t Size16; + uint8_t xb; + memcpy(&Size16, record + offsetof(U_METARECORD,Size16_4), 4); + if(!torev)U_swap4(&Size16,1); + xb = *(uint8_t *)(record + offsetof(U_METARECORD,xb)); + if(U_TEST_NOPX2(Size16,xb)){ /* no bitmap */ + U_WMRCORE_U32_N16_swap(record,7,torev); + } + else { /* yes bitmap */ + U_WMRCORE_U32_N16_swap(record,6,torev); + bitmap16_swap(record + offsetof(U_WMRBITBLT_PX,bitmap)); + } +} + +void U_WMRSTRETCHBLT_swap(char *record, int torev){ + uint32_t Size16; + uint8_t xb; + memcpy(&Size16, record + offsetof(U_METARECORD,Size16_4), 4); + if(!torev)U_swap4(&Size16,1); + xb = *(uint8_t *)(record + offsetof(U_METARECORD,xb)); + if(U_TEST_NOPX2(Size16,xb)){ /* no bitmap */ + U_WMRCORE_U32_N16_swap(record,9,torev); + } + else { /* yes bitmap */ + U_WMRCORE_U32_N16_swap(record,8,torev); + bitmap16_swap(record + offsetof(U_WMRSTRETCHBLT_PX,bitmap)); + } +} + +void U_WMRPOLYGON_swap(char *record, int torev){ + U_WMRCORE_U16_N16_swap(record, torev); +} + +void U_WMRPOLYLINE_swap(char *record, int torev){ + U_WMRCORE_U16_N16_swap(record, torev); +} + +void U_WMRESCAPE_swap(char *record, int torev){ + uint16_t eFunc; + + if(torev){ eFunc = *(uint16_t *)(record + offsetof(U_WMRESCAPE,eFunc)); } + U_WMRCORE_N16_swap(record,2,torev); + if(!torev){ eFunc = *(uint16_t *)(record + offsetof(U_WMRESCAPE,eFunc)); } + /* Handle data swapping for three types only, anything else end user code must handle */ + if((eFunc == U_MFE_SETLINECAP) || (eFunc == U_MFE_SETLINEJOIN) || (eFunc == U_MFE_SETMITERLIMIT)){ + U_swap4(record + offsetof(U_WMRESCAPE, Data),1); + } +} + +void U_WMRRESTOREDC_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); +} + +void U_WMRFILLREGION_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,2,torev); +} + +void U_WMRFRAMEREGION_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,4,torev); +} + +void U_WMRINVERTREGION_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRPAINTREGION_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRSELECTCLIPREGION_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRSELECTOBJECT_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRSETTEXTALIGN_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRDRAWTEXT_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCHORD_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record, 8, torev); +} + +void U_WMRSETMAPPERFLAGS_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMREXTTEXTOUT_swap(char *record, int torev){ + int off,Length,Len2,Opts; + U_swap4(record + offsetof(U_WMREXTTEXTOUT,Size16_4),1); + if(torev){ + Length = *(int16_t *)( record + offsetof(U_WMREXTTEXTOUT,Length)); + Opts = *(uint16_t *)(record + offsetof(U_WMREXTTEXTOUT,Opts)); + } + U_swap2(record + offsetof(U_WMREXTTEXTOUT,y), 4); /* y,x,Length,Opts*/ + if(!torev){ + Length = *(int16_t *)( record + offsetof(U_WMREXTTEXTOUT,Length)); + Opts = *(uint16_t *)(record + offsetof(U_WMREXTTEXTOUT,Opts)); + } + off = U_SIZE_WMREXTTEXTOUT; + if(Opts & (U_ETO_OPAQUE | U_ETO_CLIPPED)){ + U_swap2(record + off,4); off += 8; + } + Len2 = (Length & 1 ? Length + 1 : Length); + off += Len2; /* no need to swap string, it is a byte array */ + U_swap2(record+off,Length); /* swap the dx array */ +} + +void U_WMRSETDIBTODEV_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record, 9, torev); + dibheader_swap(record + offsetof(U_WMRSETDIBTODEV,dib), torev); +} + +void U_WMRSELECTPALETTE_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRREALIZEPALETTE_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); +} + +void U_WMRANIMATEPALETTE_swap(char *record, int torev){ + U_WMRCORE_PALETTE_swap(record, torev); +} + +void U_WMRSETPALENTRIES_swap(char *record, int torev){ + U_WMRCORE_PALETTE_swap(record, torev); +} + +void U_WMRPOLYPOLYGON_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); + polypolygon_swap(record + offsetof(U_WMRPOLYPOLYGON,PPolygon), torev); +} + +void U_WMRRESIZEPALETTE_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMR3A_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR3B_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR3C_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR3D_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR3E_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR3F_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRDIBBITBLT_swap(char *record, int torev){ + uint32_t Size16; + uint8_t xb; + memcpy(&Size16, record + offsetof(U_METARECORD,Size16_4), 4); + if(!torev)U_swap4(&Size16,1); + xb = *(uint8_t *)(record + offsetof(U_METARECORD,xb)); + if(U_TEST_NOPX2(Size16,xb)){ /* no bitmap */ + U_WMRCORE_U32_N16_swap(record,7,torev); + } + else { /* yes bitmap */ + U_WMRCORE_U32_N16_swap(record,6,torev); + dibheader_swap(record + offsetof(U_WMRDIBBITBLT_PX,dib), torev); + } +} + +void U_WMRDIBSTRETCHBLT_swap(char *record, int torev){ + uint32_t Size16; + uint8_t xb; + memcpy(&Size16, record + offsetof(U_METARECORD,Size16_4), 4); + if(!torev)U_swap4(&Size16,1); + xb = *(uint8_t *)(record + offsetof(U_METARECORD,xb)); + if(U_TEST_NOPX2(Size16,xb)){ /* no bitmap */ + U_WMRCORE_U32_N16_swap(record,9,torev); + } + else { /* yes bitmap */ + U_WMRCORE_U32_N16_swap(record,8,torev); + dibheader_swap(record + offsetof(U_WMRDIBSTRETCHBLT_PX,dib), torev); + } +} + +void U_WMRDIBCREATEPATTERNBRUSH_swap(char *record, int torev){ + int Style; + if(torev){ Style = *(uint16_t *)(record + offsetof(U_WMRDIBCREATEPATTERNBRUSH,Style)); } + U_WMRCORE_N16_swap(record,2,torev); /* Style and cUsage */ + if(!torev){ Style = *(uint16_t *)(record + offsetof(U_WMRDIBCREATEPATTERNBRUSH,Style)); } + if(Style == U_BS_PATTERN){ + bitmap16_swap(record + offsetof(U_WMRDIBCREATEPATTERNBRUSH,Src)); + } + else { + dibheader_swap(record + offsetof(U_WMRDIBCREATEPATTERNBRUSH,Src), torev); + } +} + +void U_WMRSTRETCHDIB_swap(char *record, int torev){ + torev = torev; + U_WMRCORE_U32_N16_swap(record,9,torev); + dibheader_swap(record + offsetof(U_WMRSTRETCHDIB,dib), torev); +} + +void U_WMR44_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR45_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR46_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR47_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMREXTFLOODFILL_swap(char *record, int torev){ + U_WMRCORE_U16_CR_2U16_swap(record, torev); +} + +void U_WMR49_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR4A_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR4B_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR4C_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR4D_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR4E_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR4F_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR50_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR51_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR52_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR53_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR54_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR55_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR56_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR57_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR58_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR59_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR5A_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR5B_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR5C_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR5D_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR5E_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR5F_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR60_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR61_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR62_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR63_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR64_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR65_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR66_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR67_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR68_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR69_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR6A_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR6B_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR6C_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR6D_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR6E_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR6F_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR70_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR71_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR72_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR73_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR74_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR75_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR76_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR77_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR78_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR79_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR7A_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR7B_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR7C_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR7D_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR7E_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR7F_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR80_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR81_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR82_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR83_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR84_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR85_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR86_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR87_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR88_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR89_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR8A_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR8B_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR8C_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR8D_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR8E_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR8F_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR90_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR91_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR92_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR93_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR94_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR95_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR96_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR97_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR98_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR99_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR9A_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR9B_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR9C_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR9D_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR9E_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMR9F_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRA0_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRA1_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRA2_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRA3_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRA4_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRA5_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRA6_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRA7_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRA8_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRA9_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRAA_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRAB_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRAC_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRAD_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRAE_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRAF_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRB0_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRB1_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRB2_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRB3_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRB4_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRB5_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRB6_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRB7_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRB8_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRB9_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRBA_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRBB_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRBC_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRBD_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRBE_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRBF_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRC0_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRC1_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRC2_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRC3_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRC4_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRC5_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRC6_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRC7_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRC8_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRC9_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCA_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCB_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCC_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCD_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCE_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCF_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRD0_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRD1_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRD2_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRD3_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRD4_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRD5_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRD6_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRD7_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRD8_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRD9_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRDA_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRDB_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRDC_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRDD_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRDE_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRDF_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRE0_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRE1_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRE2_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRE3_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRE4_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRE5_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRE6_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRE7_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRE8_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRE9_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMREA_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMREB_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMREC_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRED_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMREE_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMREF_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRDELETEOBJECT_swap(char *record, int torev){ + U_WMRCORE_N16_swap(record,1,torev); +} + +void U_WMRF1_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRF2_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRF3_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRF4_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRF5_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRF6_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCREATEPALETTE_swap(char *record, int torev){ + U_WMRCORE_PALETTE_swap(record, torev); +} + +void U_WMRF8_swap(char *record, int torev){ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCREATEPATTERNBRUSH_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); + bitmap16_swap(record + U_SIZE_METARECORD); + /* pattern array byte order already correct? */ +} + +void U_WMRCREATEPENINDIRECT_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); + pen_swap(record + offsetof(U_WMRCREATEPENINDIRECT,pen)); +} + +void U_WMRCREATEFONTINDIRECT_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); + font_swap(record + offsetof(U_WMRCREATEFONTINDIRECT,font)); +} + +void U_WMRCREATEBRUSHINDIRECT_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); + wlogbrush_swap(record + offsetof(U_WMRCREATEBRUSHINDIRECT,brush)); +} + +void U_WMRCREATEBITMAPINDIRECT_swap(char *record, int torev){ /* in Wine, not in WMF PDF */ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCREATEBITMAP_swap(char *record, int torev){ /* in Wine, not in WMF PDF */ + U_WMRNOTIMPLEMENTED_swap(record, torev); +} + +void U_WMRCREATEREGION_swap(char *record, int torev){ + U_WMRCORE_SIZE16_swap(record, torev); + region_swap(record + offsetof(U_WMRCREATEREGION,region), torev); +} +//! @endcond + +/** + \brief Convert an entire WMF in memory from Big Endian to Little Endian (or vice versa). + \return 0 on failure, 1 on success + \param contents pointer to the buffer holding the entire EMF in memory + \param length number of bytes in the buffer + \param torev 1 for native to reversed, 0 for reversed to native + + Normally this would be called immediately before writing the data to a file + or immediately after reading the data from a file. +*/ +int U_wmf_endian(char *contents, size_t length, int torev){ + size_t off; + uint32_t OK, Size16; + uint8_t iType; + char *record; + int recnum, offset; + + record = contents; + off = wmfheader_swap(record,torev); fflush(stdout); /* WMF header is not a normal record, handle it separately */ + record += off; + offset = off; + OK = 1; + recnum = 1; /* used when debugging */ + + while(OK){ + if(record > contents + length){ return(0); } // this is most likely a corrupt WMF + + memcpy(&Size16, record + offsetof(U_METARECORD,Size16_4), 4); /* This may not be aligned */ + if(!torev){ U_swap4(&Size16,1); } + iType = *(uint8_t *)(record + offsetof(U_METARECORD,iType)); + +//printf("DEBUG U_wmf_endian before switch record:%d offset:%d type:%d name:%s Size16:%d\n",recnum,offset,iType,U_wmr_names(iType),Size16);fflush(stdout); + switch (iType) + { + case U_WMR_EOF: U_WMREOF_swap(record, torev); OK=0; break; + case U_WMR_SETBKCOLOR: U_WMRSETBKCOLOR_swap(record, torev); break; + case U_WMR_SETBKMODE: U_WMRSETBKMODE_swap(record, torev); break; + case U_WMR_SETMAPMODE: U_WMRSETMAPMODE_swap(record, torev); break; + case U_WMR_SETROP2: U_WMRSETROP2_swap(record, torev); break; + case U_WMR_SETRELABS: U_WMRSETRELABS_swap(record, torev); break; + case U_WMR_SETPOLYFILLMODE: U_WMRSETPOLYFILLMODE_swap(record, torev); break; + case U_WMR_SETSTRETCHBLTMODE: U_WMRSETSTRETCHBLTMODE_swap(record, torev); break; + case U_WMR_SETTEXTCHAREXTRA: U_WMRSETTEXTCHAREXTRA_swap(record, torev); break; + case U_WMR_SETTEXTCOLOR: U_WMRSETTEXTCOLOR_swap(record, torev); break; + case U_WMR_SETTEXTJUSTIFICATION: U_WMRSETTEXTJUSTIFICATION_swap(record, torev); break; + case U_WMR_SETWINDOWORG: U_WMRSETWINDOWORG_swap(record, torev); break; + case U_WMR_SETWINDOWEXT: U_WMRSETWINDOWEXT_swap(record, torev); break; + case U_WMR_SETVIEWPORTORG: U_WMRSETVIEWPORTORG_swap(record, torev); break; + case U_WMR_SETVIEWPORTEXT: U_WMRSETVIEWPORTEXT_swap(record, torev); break; + case U_WMR_OFFSETWINDOWORG: U_WMROFFSETWINDOWORG_swap(record, torev); break; + case U_WMR_SCALEWINDOWEXT: U_WMRSCALEWINDOWEXT_swap(record, torev); break; + case U_WMR_OFFSETVIEWPORTORG: U_WMROFFSETVIEWPORTORG_swap(record, torev); break; + case U_WMR_SCALEVIEWPORTEXT: U_WMRSCALEVIEWPORTEXT_swap(record, torev); break; + case U_WMR_LINETO: U_WMRLINETO_swap(record, torev); break; + case U_WMR_MOVETO: U_WMRMOVETO_swap(record, torev); break; + case U_WMR_EXCLUDECLIPRECT: U_WMREXCLUDECLIPRECT_swap(record, torev); break; + case U_WMR_INTERSECTCLIPRECT: U_WMRINTERSECTCLIPRECT_swap(record, torev); break; + case U_WMR_ARC: U_WMRARC_swap(record, torev); break; + case U_WMR_ELLIPSE: U_WMRELLIPSE_swap(record, torev); break; + case U_WMR_FLOODFILL: U_WMRFLOODFILL_swap(record, torev); break; + case U_WMR_PIE: U_WMRPIE_swap(record, torev); break; + case U_WMR_RECTANGLE: U_WMRRECTANGLE_swap(record, torev); break; + case U_WMR_ROUNDRECT: U_WMRROUNDRECT_swap(record, torev); break; + case U_WMR_PATBLT: U_WMRPATBLT_swap(record, torev); break; + case U_WMR_SAVEDC: U_WMRSAVEDC_swap(record, torev); break; + case U_WMR_SETPIXEL: U_WMRSETPIXEL_swap(record, torev); break; + case U_WMR_OFFSETCLIPRGN: U_WMROFFSETCLIPRGN_swap(record, torev); break; + case U_WMR_TEXTOUT: U_WMRTEXTOUT_swap(record, torev); break; + case U_WMR_BITBLT: U_WMRBITBLT_swap(record, torev); break; + case U_WMR_STRETCHBLT: U_WMRSTRETCHBLT_swap(record, torev); break; + case U_WMR_POLYGON: U_WMRPOLYGON_swap(record, torev); break; + case U_WMR_POLYLINE: U_WMRPOLYLINE_swap(record, torev); break; + case U_WMR_ESCAPE: U_WMRESCAPE_swap(record, torev); break; + case U_WMR_RESTOREDC: U_WMRRESTOREDC_swap(record, torev); break; + case U_WMR_FILLREGION: U_WMRFILLREGION_swap(record, torev); break; + case U_WMR_FRAMEREGION: U_WMRFRAMEREGION_swap(record, torev); break; + case U_WMR_INVERTREGION: U_WMRINVERTREGION_swap(record, torev); break; + case U_WMR_PAINTREGION: U_WMRPAINTREGION_swap(record, torev); break; + case U_WMR_SELECTCLIPREGION: U_WMRSELECTCLIPREGION_swap(record, torev); break; + case U_WMR_SELECTOBJECT: U_WMRSELECTOBJECT_swap(record, torev); break; + case U_WMR_SETTEXTALIGN: U_WMRSETTEXTALIGN_swap(record, torev); break; + case U_WMR_DRAWTEXT: U_WMRDRAWTEXT_swap(record, torev); break; + case U_WMR_CHORD: U_WMRCHORD_swap(record, torev); break; + case U_WMR_SETMAPPERFLAGS: U_WMRSETMAPPERFLAGS_swap(record, torev); break; + case U_WMR_EXTTEXTOUT: U_WMREXTTEXTOUT_swap(record, torev); break; + case U_WMR_SETDIBTODEV: U_WMRSETDIBTODEV_swap(record, torev); break; + case U_WMR_SELECTPALETTE: U_WMRSELECTPALETTE_swap(record, torev); break; + case U_WMR_REALIZEPALETTE: U_WMRREALIZEPALETTE_swap(record, torev); break; + case U_WMR_ANIMATEPALETTE: U_WMRANIMATEPALETTE_swap(record, torev); break; + case U_WMR_SETPALENTRIES: U_WMRSETPALENTRIES_swap(record, torev); break; + case U_WMR_POLYPOLYGON: U_WMRPOLYPOLYGON_swap(record, torev); break; + case U_WMR_RESIZEPALETTE: U_WMRRESIZEPALETTE_swap(record, torev); break; + case U_WMR_3A: U_WMR3A_swap(record, torev); break; + case U_WMR_3B: U_WMR3B_swap(record, torev); break; + case U_WMR_3C: U_WMR3C_swap(record, torev); break; + case U_WMR_3D: U_WMR3D_swap(record, torev); break; + case U_WMR_3E: U_WMR3E_swap(record, torev); break; + case U_WMR_3F: U_WMR3F_swap(record, torev); break; + case U_WMR_DIBBITBLT: U_WMRDIBBITBLT_swap(record, torev); break; + case U_WMR_DIBSTRETCHBLT: U_WMRDIBSTRETCHBLT_swap(record, torev); break; + case U_WMR_DIBCREATEPATTERNBRUSH: U_WMRDIBCREATEPATTERNBRUSH_swap(record, torev); break; + case U_WMR_STRETCHDIB: U_WMRSTRETCHDIB_swap(record, torev); break; + case U_WMR_44: U_WMR44_swap(record, torev); break; + case U_WMR_45: U_WMR45_swap(record, torev); break; + case U_WMR_46: U_WMR46_swap(record, torev); break; + case U_WMR_47: U_WMR47_swap(record, torev); break; + case U_WMR_EXTFLOODFILL: U_WMREXTFLOODFILL_swap(record, torev); break; + case U_WMR_49: U_WMR49_swap(record, torev); break; + case U_WMR_4A: U_WMR4A_swap(record, torev); break; + case U_WMR_4B: U_WMR4B_swap(record, torev); break; + case U_WMR_4C: U_WMR4C_swap(record, torev); break; + case U_WMR_4D: U_WMR4D_swap(record, torev); break; + case U_WMR_4E: U_WMR4E_swap(record, torev); break; + case U_WMR_4F: U_WMR4F_swap(record, torev); break; + case U_WMR_50: U_WMR50_swap(record, torev); break; + case U_WMR_51: U_WMR51_swap(record, torev); break; + case U_WMR_52: U_WMR52_swap(record, torev); break; + case U_WMR_53: U_WMR53_swap(record, torev); break; + case U_WMR_54: U_WMR54_swap(record, torev); break; + case U_WMR_55: U_WMR55_swap(record, torev); break; + case U_WMR_56: U_WMR56_swap(record, torev); break; + case U_WMR_57: U_WMR57_swap(record, torev); break; + case U_WMR_58: U_WMR58_swap(record, torev); break; + case U_WMR_59: U_WMR59_swap(record, torev); break; + case U_WMR_5A: U_WMR5A_swap(record, torev); break; + case U_WMR_5B: U_WMR5B_swap(record, torev); break; + case U_WMR_5C: U_WMR5C_swap(record, torev); break; + case U_WMR_5D: U_WMR5D_swap(record, torev); break; + case U_WMR_5E: U_WMR5E_swap(record, torev); break; + case U_WMR_5F: U_WMR5F_swap(record, torev); break; + case U_WMR_60: U_WMR60_swap(record, torev); break; + case U_WMR_61: U_WMR61_swap(record, torev); break; + case U_WMR_62: U_WMR62_swap(record, torev); break; + case U_WMR_63: U_WMR63_swap(record, torev); break; + case U_WMR_64: U_WMR64_swap(record, torev); break; + case U_WMR_65: U_WMR65_swap(record, torev); break; + case U_WMR_66: U_WMR66_swap(record, torev); break; + case U_WMR_67: U_WMR67_swap(record, torev); break; + case U_WMR_68: U_WMR68_swap(record, torev); break; + case U_WMR_69: U_WMR69_swap(record, torev); break; + case U_WMR_6A: U_WMR6A_swap(record, torev); break; + case U_WMR_6B: U_WMR6B_swap(record, torev); break; + case U_WMR_6C: U_WMR6C_swap(record, torev); break; + case U_WMR_6D: U_WMR6D_swap(record, torev); break; + case U_WMR_6E: U_WMR6E_swap(record, torev); break; + case U_WMR_6F: U_WMR6F_swap(record, torev); break; + case U_WMR_70: U_WMR70_swap(record, torev); break; + case U_WMR_71: U_WMR71_swap(record, torev); break; + case U_WMR_72: U_WMR72_swap(record, torev); break; + case U_WMR_73: U_WMR73_swap(record, torev); break; + case U_WMR_74: U_WMR74_swap(record, torev); break; + case U_WMR_75: U_WMR75_swap(record, torev); break; + case U_WMR_76: U_WMR76_swap(record, torev); break; + case U_WMR_77: U_WMR77_swap(record, torev); break; + case U_WMR_78: U_WMR78_swap(record, torev); break; + case U_WMR_79: U_WMR79_swap(record, torev); break; + case U_WMR_7A: U_WMR7A_swap(record, torev); break; + case U_WMR_7B: U_WMR7B_swap(record, torev); break; + case U_WMR_7C: U_WMR7C_swap(record, torev); break; + case U_WMR_7D: U_WMR7D_swap(record, torev); break; + case U_WMR_7E: U_WMR7E_swap(record, torev); break; + case U_WMR_7F: U_WMR7F_swap(record, torev); break; + case U_WMR_80: U_WMR80_swap(record, torev); break; + case U_WMR_81: U_WMR81_swap(record, torev); break; + case U_WMR_82: U_WMR82_swap(record, torev); break; + case U_WMR_83: U_WMR83_swap(record, torev); break; + case U_WMR_84: U_WMR84_swap(record, torev); break; + case U_WMR_85: U_WMR85_swap(record, torev); break; + case U_WMR_86: U_WMR86_swap(record, torev); break; + case U_WMR_87: U_WMR87_swap(record, torev); break; + case U_WMR_88: U_WMR88_swap(record, torev); break; + case U_WMR_89: U_WMR89_swap(record, torev); break; + case U_WMR_8A: U_WMR8A_swap(record, torev); break; + case U_WMR_8B: U_WMR8B_swap(record, torev); break; + case U_WMR_8C: U_WMR8C_swap(record, torev); break; + case U_WMR_8D: U_WMR8D_swap(record, torev); break; + case U_WMR_8E: U_WMR8E_swap(record, torev); break; + case U_WMR_8F: U_WMR8F_swap(record, torev); break; + case U_WMR_90: U_WMR90_swap(record, torev); break; + case U_WMR_91: U_WMR91_swap(record, torev); break; + case U_WMR_92: U_WMR92_swap(record, torev); break; + case U_WMR_93: U_WMR93_swap(record, torev); break; + case U_WMR_94: U_WMR94_swap(record, torev); break; + case U_WMR_95: U_WMR95_swap(record, torev); break; + case U_WMR_96: U_WMR96_swap(record, torev); break; + case U_WMR_97: U_WMR97_swap(record, torev); break; + case U_WMR_98: U_WMR98_swap(record, torev); break; + case U_WMR_99: U_WMR99_swap(record, torev); break; + case U_WMR_9A: U_WMR9A_swap(record, torev); break; + case U_WMR_9B: U_WMR9B_swap(record, torev); break; + case U_WMR_9C: U_WMR9C_swap(record, torev); break; + case U_WMR_9D: U_WMR9D_swap(record, torev); break; + case U_WMR_9E: U_WMR9E_swap(record, torev); break; + case U_WMR_9F: U_WMR9F_swap(record, torev); break; + case U_WMR_A0: U_WMRA0_swap(record, torev); break; + case U_WMR_A1: U_WMRA1_swap(record, torev); break; + case U_WMR_A2: U_WMRA2_swap(record, torev); break; + case U_WMR_A3: U_WMRA3_swap(record, torev); break; + case U_WMR_A4: U_WMRA4_swap(record, torev); break; + case U_WMR_A5: U_WMRA5_swap(record, torev); break; + case U_WMR_A6: U_WMRA6_swap(record, torev); break; + case U_WMR_A7: U_WMRA7_swap(record, torev); break; + case U_WMR_A8: U_WMRA8_swap(record, torev); break; + case U_WMR_A9: U_WMRA9_swap(record, torev); break; + case U_WMR_AA: U_WMRAA_swap(record, torev); break; + case U_WMR_AB: U_WMRAB_swap(record, torev); break; + case U_WMR_AC: U_WMRAC_swap(record, torev); break; + case U_WMR_AD: U_WMRAD_swap(record, torev); break; + case U_WMR_AE: U_WMRAE_swap(record, torev); break; + case U_WMR_AF: U_WMRAF_swap(record, torev); break; + case U_WMR_B0: U_WMRB0_swap(record, torev); break; + case U_WMR_B1: U_WMRB1_swap(record, torev); break; + case U_WMR_B2: U_WMRB2_swap(record, torev); break; + case U_WMR_B3: U_WMRB3_swap(record, torev); break; + case U_WMR_B4: U_WMRB4_swap(record, torev); break; + case U_WMR_B5: U_WMRB5_swap(record, torev); break; + case U_WMR_B6: U_WMRB6_swap(record, torev); break; + case U_WMR_B7: U_WMRB7_swap(record, torev); break; + case U_WMR_B8: U_WMRB8_swap(record, torev); break; + case U_WMR_B9: U_WMRB9_swap(record, torev); break; + case U_WMR_BA: U_WMRBA_swap(record, torev); break; + case U_WMR_BB: U_WMRBB_swap(record, torev); break; + case U_WMR_BC: U_WMRBC_swap(record, torev); break; + case U_WMR_BD: U_WMRBD_swap(record, torev); break; + case U_WMR_BE: U_WMRBE_swap(record, torev); break; + case U_WMR_BF: U_WMRBF_swap(record, torev); break; + case U_WMR_C0: U_WMRC0_swap(record, torev); break; + case U_WMR_C1: U_WMRC1_swap(record, torev); break; + case U_WMR_C2: U_WMRC2_swap(record, torev); break; + case U_WMR_C3: U_WMRC3_swap(record, torev); break; + case U_WMR_C4: U_WMRC4_swap(record, torev); break; + case U_WMR_C5: U_WMRC5_swap(record, torev); break; + case U_WMR_C6: U_WMRC6_swap(record, torev); break; + case U_WMR_C7: U_WMRC7_swap(record, torev); break; + case U_WMR_C8: U_WMRC8_swap(record, torev); break; + case U_WMR_C9: U_WMRC9_swap(record, torev); break; + case U_WMR_CA: U_WMRCA_swap(record, torev); break; + case U_WMR_CB: U_WMRCB_swap(record, torev); break; + case U_WMR_CC: U_WMRCC_swap(record, torev); break; + case U_WMR_CD: U_WMRCD_swap(record, torev); break; + case U_WMR_CE: U_WMRCE_swap(record, torev); break; + case U_WMR_CF: U_WMRCF_swap(record, torev); break; + case U_WMR_D0: U_WMRD0_swap(record, torev); break; + case U_WMR_D1: U_WMRD1_swap(record, torev); break; + case U_WMR_D2: U_WMRD2_swap(record, torev); break; + case U_WMR_D3: U_WMRD3_swap(record, torev); break; + case U_WMR_D4: U_WMRD4_swap(record, torev); break; + case U_WMR_D5: U_WMRD5_swap(record, torev); break; + case U_WMR_D6: U_WMRD6_swap(record, torev); break; + case U_WMR_D7: U_WMRD7_swap(record, torev); break; + case U_WMR_D8: U_WMRD8_swap(record, torev); break; + case U_WMR_D9: U_WMRD9_swap(record, torev); break; + case U_WMR_DA: U_WMRDA_swap(record, torev); break; + case U_WMR_DB: U_WMRDB_swap(record, torev); break; + case U_WMR_DC: U_WMRDC_swap(record, torev); break; + case U_WMR_DD: U_WMRDD_swap(record, torev); break; + case U_WMR_DE: U_WMRDE_swap(record, torev); break; + case U_WMR_DF: U_WMRDF_swap(record, torev); break; + case U_WMR_E0: U_WMRE0_swap(record, torev); break; + case U_WMR_E1: U_WMRE1_swap(record, torev); break; + case U_WMR_E2: U_WMRE2_swap(record, torev); break; + case U_WMR_E3: U_WMRE3_swap(record, torev); break; + case U_WMR_E4: U_WMRE4_swap(record, torev); break; + case U_WMR_E5: U_WMRE5_swap(record, torev); break; + case U_WMR_E6: U_WMRE6_swap(record, torev); break; + case U_WMR_E7: U_WMRE7_swap(record, torev); break; + case U_WMR_E8: U_WMRE8_swap(record, torev); break; + case U_WMR_E9: U_WMRE9_swap(record, torev); break; + case U_WMR_EA: U_WMREA_swap(record, torev); break; + case U_WMR_EB: U_WMREB_swap(record, torev); break; + case U_WMR_EC: U_WMREC_swap(record, torev); break; + case U_WMR_ED: U_WMRED_swap(record, torev); break; + case U_WMR_EE: U_WMREE_swap(record, torev); break; + case U_WMR_EF: U_WMREF_swap(record, torev); break; + case U_WMR_DELETEOBJECT: U_WMRDELETEOBJECT_swap(record, torev); break; + case U_WMR_F1: U_WMRF1_swap(record, torev); break; + case U_WMR_F2: U_WMRF2_swap(record, torev); break; + case U_WMR_F3: U_WMRF3_swap(record, torev); break; + case U_WMR_F4: U_WMRF4_swap(record, torev); break; + case U_WMR_F5: U_WMRF5_swap(record, torev); break; + case U_WMR_F6: U_WMRF6_swap(record, torev); break; + case U_WMR_CREATEPALETTE: U_WMRCREATEPALETTE_swap(record, torev); break; + case U_WMR_F8: U_WMRF8_swap(record, torev); break; + case U_WMR_CREATEPATTERNBRUSH: U_WMRCREATEPATTERNBRUSH_swap(record, torev); break; + case U_WMR_CREATEPENINDIRECT: U_WMRCREATEPENINDIRECT_swap(record, torev); break; + case U_WMR_CREATEFONTINDIRECT: U_WMRCREATEFONTINDIRECT_swap(record, torev); break; + case U_WMR_CREATEBRUSHINDIRECT: U_WMRCREATEBRUSHINDIRECT_swap(record, torev); break; + case U_WMR_CREATEBITMAPINDIRECT: U_WMRCREATEBITMAPINDIRECT_swap(record, torev); break; + case U_WMR_CREATEBITMAP: U_WMRCREATEBITMAP_swap(record, torev); break; + case U_WMR_CREATEREGION: U_WMRCREATEREGION_swap(record, torev); break; + default: U_WMRNOTIMPLEMENTED_swap(record, torev); break; + } //end of switch + record += 2*Size16; + offset += 2*Size16; + recnum++; + } //end of while + return(1); +} + + +#ifdef __cplusplus +} +#endif diff --git a/src/libuemf/uwmf_endian.h b/src/libuemf/uwmf_endian.h new file mode 100644 index 000000000..dc01c0d07 --- /dev/null +++ b/src/libuemf/uwmf_endian.h @@ -0,0 +1,39 @@ +/** + @file uemf_endian.h Prototype for function for converting EMF records between Big Endian and Little Endian +*/ + +/* +File: uwmf_endian.h +Version: 0.0.1 +Date: 10-JAN-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2013 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifndef _UWMF_ENDIAN_ +#define _UWMF_ENDIAN_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "uemf_endian.h" + +/* There is no way for the preprocessor, in general, to figure out endianness. So the command line must define + WORDS_BIGENDIAN for a big endian machine. Otherwise we assume is is little endian. If it is something + else this code won't work in any case. */ +#ifdef WORDS_BIGENDIAN +#define U_BYTE_SWAP 1 +#else +#define U_BYTE_SWAP 0 +#endif + +// prototypes +int U_wmf_endian(char *contents, size_t length, int torev); + +#ifdef __cplusplus +} +#endif + +#endif /* _UWMF_ENDIAN_ */ diff --git a/src/libuemf/uwmf_print.c b/src/libuemf/uwmf_print.c new file mode 100644 index 000000000..d044cf9b6 --- /dev/null +++ b/src/libuemf/uwmf_print.c @@ -0,0 +1,1624 @@ +/** + @file uwmf_print.c Functions for printing WMF records +*/ + +/* +File: uwmf_print.c +Version: 0.0.2 +Date: 18-FEB-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2012 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <stddef.h> /* for offsetof() macro */ +#include <string.h> +#include "uwmf_print.h" + +/* ********************************************************************************************** + These functions print standard objects used in the WMR records. + The low level ones do not append EOL. +*********************************************************************************************** */ + +/* many of these are implemented in uemf_print.c and not replicated here */ + + + +/** + \brief Print a U_BRUSH object. + \param b U_BRUSH object. + style bColor bHatch + U_BS_SOLID ColorRef Object Not used (bytes present???) + U_BS_NULL ignored ignored (bytes present???). + U_BS_PATTERN ignored Bitmap16 object holding patern + U_BS_DIBPATTERNPT ColorUsage Enum DIB object + U_BS_HATCHED ColorRef Object HatchStyle Enumeration +*/ +void brush_print( + U_BRUSH b + ){ + uint16_t hatch; + U_COLORREF Color; + switch(b.Style){ + case U_BS_SOLID: + memcpy(&Color, &b.Color, sizeof(U_COLORREF)); + printf("Color:"); colorref_print(Color); + break; + case U_BS_NULL: + printf("Null"); + break; + case U_BS_PATTERN: + printf("Pattern:(not shown)"); + break; + case U_BS_DIBPATTERNPT: + printf("DIBPattern:(not shown)"); + break; + case U_BS_HATCHED: + memcpy(&hatch, b.Data, 2); + printf("Hatch:0x%4.4X ", hatch); + break; + } +} + +/** + \brief Print a U_FONT object from a char *pointer. + The U_FONT struct object may not be properly aligned, but all of the fields within it will + OK for alignment. + \param font U_FONT object (as a char * pointer) +*/ +void font_print( + const char *font + ){ + printf("Height:%d ", *(int16_t *)(font + offsetof(U_FONT,Height ))); + printf("Width:%d ", *(int16_t *)(font + offsetof(U_FONT,Width ))); + printf("Escapement:%d ", *(int16_t *)(font + offsetof(U_FONT,Escapement ))); + printf("Orientation:%d ", *(int16_t *)(font + offsetof(U_FONT,Orientation ))); + printf("Weight:%d ", *(int16_t *)(font + offsetof(U_FONT,Weight ))); + printf("Italic:0x%2.2X ", *(uint8_t *)(font + offsetof(U_FONT,Italic ))); + printf("Underline:0x%2.2X ", *(uint8_t *)(font + offsetof(U_FONT,Underline ))); + printf("StrikeOut:0x%2.2X ", *(uint8_t *)(font + offsetof(U_FONT,StrikeOut ))); + printf("CharSet:0x%2.2X ", *(uint8_t *)(font + offsetof(U_FONT,CharSet ))); + printf("OutPrecision:0x%2.2X ", *(uint8_t *)(font + offsetof(U_FONT,OutPrecision ))); + printf("ClipPrecision:0x%2.2X ", *(uint8_t *)(font + offsetof(U_FONT,ClipPrecision ))); + printf("Quality:0x%2.2X ", *(uint8_t *)(font + offsetof(U_FONT,Quality ))); + printf("PitchAndFamily:0x%2.2X ", *(uint8_t *)(font + offsetof(U_FONT,PitchAndFamily))); + printf("FaceName:%s ", (font + offsetof(U_FONT,FaceName ))); +} + + +/** + \brief Print a U_PLTNTRY object. + \param pny U_PLTNTRY object. +*/ +void pltntry_print( + U_PLTNTRY pny + ){ + printf("Value:%u ", pny.Value); + printf("Red:%u ", pny.Red ); + printf("Green:%u ", pny.Green); + printf("Blue:%u ", pny.Blue ); +} + + +/** + \brief Print a pointer to a U_PALETTE object. + \param p Pointer to a U_PALETTE object + \param PalEntries Array of Palette Entries +*/ +void palette_print( + const PU_PALETTE p, + const char *PalEntries + ){ + int i; + U_PLTNTRY pny; + + printf("Start:%X ", p->Start ); + printf("NumEntries:%u ",p->NumEntries ); + if(p->NumEntries && PalEntries){ + for(i=0; i < p->NumEntries; i++, PalEntries += sizeof(U_PLTNTRY)){ + memcpy(&pny, PalEntries, sizeof(U_PLTNTRY)); + printf("%d:",i); pltntry_print(pny); + } + } +} + +/** + \brief Print a U_PEN object. + \param p U_PEN object + uint16_t Style; //!< PenStyle Enumeration + uint16_t Width; //!< Pen Width in object dimensions + uint16_t unused; //!< unused + union { + U_COLORREF Color; //!< Pen color (NOT aligned on 4n byte boundary!) + uint16_t Colorw[2]; //!< reassemble/store the Color value using these, NOT Color. + }; +*/ +void pen_print( + U_PEN p + ){ + U_COLORREF Color; + printf("Style:0x%8.8X " ,p.Style ); + printf("Width:%u " ,p.Widthw[0] ); + memcpy(&Color, &p.Color, sizeof(U_COLORREF)); + printf("Color"); colorref_print(Color); +} + +/** + \brief Print U_RECT16 object + Prints in order left, top, right, bottom + \param rect U_RECT16 object +*/ +void rect16_ltrb_print( + U_RECT16 rect + ){ + printf("LTRB{%d,%d,%d,%d} ",rect.left,rect.top,rect.right,rect.bottom); +} + +/** + \brief Print U_RECT16 object + Some WMF rects use the order bottom, right, top, left. These are passed in using + the same structure as for a normal U_RECT16 so: + position holds + left bottom + top right + right top + bottom left + This is used by WMR_RECTANGLE and many others. + \param rect U_RECT16 object +*/ +void rect16_brtl_print( + U_RECT16 rect + ){ + printf("BRTL{%d,%d,%d,%d} ",rect.bottom,rect.right,rect.top,rect.left); +} + + + +/** + \brief Print U_REGION object from a char * pointer. + \param region U_REGION object +*/ +void region_print( + const char *region + ){ + U_RECT16 rect16; + printf("Type:%d ", *(uint16_t *)(region + offsetof(U_REGION,Type ))); + printf("Size:%d ", *( int16_t *)(region + offsetof(U_REGION,Size ))); + printf("sCount:%d ",*( int16_t *)(region + offsetof(U_REGION,sCount))); + printf("sMax:%d ", *( int16_t *)(region + offsetof(U_REGION,sMax ))); + memcpy(&rect16, (region + offsetof(U_REGION,sRect )), sizeof(U_RECT16)); + printf("sRect: "); rect16_ltrb_print(rect16); +} + + +/** + \brief Print U_BITMAP16 object + \param b U_BITMAP16 object +*/ +void bitmap16_print( + U_BITMAP16 b + ){ + printf("Type:%d ", b.Type ); + printf("Width:%d ", b.Width ); + printf("Height:%d ", b.Height ); + printf("WidthBytes:%d ", b.WidthBytes); + printf("Planes:%d ", b.Planes ); + printf("BitsPixel:%d ", b.BitsPixel ); + printf("BitsBytes:%d ", (((b.Width * b.BitsPixel + 15) >> 4) << 1) * b.Height ); +} + +/** + \brief Print U_BITMAPCOREHEADER object + \param ch U_BITMAPCOREHEADER object +*/ +void bitmapcoreheader_print( + U_BITMAPCOREHEADER ch + ){ + uint32_t Size; + memcpy(&Size, &(ch.Size_4), 4); /* will be aligned, but is in two pieces */ + printf("Size:%d ", Size); + printf("Width:%d ", ch.Width); + printf("Height:%d ", ch.Height); + printf("Planes:%d ", ch.Planes); + printf("BitCount:%d ", ch.BitCount); +} + +/** LogBrushW Object WMF PDF 2.2.2.10 + \brief Print a U_LOGBRUSHW object. + \param lb U_LOGBRUSHW object. + + style Color Hatch + U_BS_SOLID ColorRef Object Not used (bytes present???) + U_BS_NULL ignored ignored (bytes present???). + U_BS_PATTERN ignored not used (Action is not strictly defined) + U_BS_DIBPATTERN ignored not used (Action is not strictly defined) + U_BS_DIBPATTERNPT ignored not used (Action is not strictly defined) + U_BS_HATCHED ColorRef Object HatchStyle Enumeration +*/ +void wlogbrush_print( + const char *lb + ){ + U_COLORREF Color; + uint16_t Style = *(uint16_t *)(lb + offsetof(U_WLOGBRUSH,Style)); + uint16_t Hatch = *(uint16_t *)(lb + offsetof(U_WLOGBRUSH,Hatch)); + memcpy(&Color, lb + offsetof(U_WLOGBRUSH,Color), sizeof(U_COLORREF)); + printf("Style:0x%4.4X ",Style); + switch(Style){ + case U_BS_SOLID: + printf("Color:"); colorref_print(Color); + break; + case U_BS_NULL: + printf("Null"); + break; + case U_BS_PATTERN: + printf("Pattern:(not implemented)"); + break; + case U_BS_DIBPATTERN: + printf("DIBPattern:(not implemented)"); + break; + case U_BS_DIBPATTERNPT: + printf("DIBPatternPt:(not implemented)"); + break; + case U_BS_HATCHED: + printf("Color:"); colorref_print(Color); + printf("Hatch:0x%4.4X ", Hatch); + break; + } +} + + +/** + \brief Print U_POLYPOLY object from pointer + \param nPolys Number of elements in aPolyCounts + \param aPolyCounts Number of points in each poly (sequential) + \param Points pointer to array of U_POINT16 in memory. Probably not aligned. +*/ +void polypolygon_print( + uint16_t nPolys, + const uint16_t *aPolyCounts, + const char *Points + ){ + int i,j; + U_POINT16 pt; + for(i=0; i<nPolys; i++, aPolyCounts++){ + printf(" Polygon[%d]: ",i); + for(j=0; j < *aPolyCounts; j++, Points += sizeof(U_POINT16)){ + memcpy(&pt, Points, sizeof(U_POINT16)); /* may not be aligned */ + point16_print(pt); + } + } +} + +/** + \brief Print U_SCAN object + \param sc U_SCAN object +*/ +void scan_print( + U_SCAN sc + ){ + printf("Count:%d ", sc.count); + printf("Top:%d ", sc.top); + printf("Bottom:%d ", sc.bottom); + printf("data:(not shown)"); +} + +/** + \brief Print a summary of a DIB header + \param dh void pointer to DIB header + A DIB header in an WMF may be either a BitmapCoreHeader or BitmapInfoHeader. +*/ +void dibheader_print(const void *dh){ + uint32_t Size; + memcpy(&Size, dh, 4); /* may not be aligned */ + if(Size == 0xC ){ + printf("(BitmapCoreHeader) "); + U_BITMAPCOREHEADER bmch; + memcpy(&bmch, dh, sizeof(U_BITMAPCOREHEADER)); /* may not be aligned */ + bitmapcoreheader_print(bmch); + } + else { + printf(" (BitmapInfoHeader) "); + bitmapinfo_print(dh); /* may not be aligned, called routine must handle it */ + } +} + +/** + \brief Print WMF header object + \returns size of entire header structure + \param contents pointer to the first byte in the buffer holding the entire WMF file in memory + \param blimit pointer to the byte after the last byte in contents + + If the header is preceded by a placeable struture, print that as well. +*/ +int wmfheader_print( + const char *contents, + const char *blimit + ){ + U_WMRPLACEABLE Placeable; + U_WMRHEADER Header; + int size = wmfheader_get(contents, blimit, &Placeable, &Header); + uint32_t utmp4; + U_RECT16 rect16; + uint32_t Key; + memcpy(&Key, contents + offsetof(U_WMRPLACEABLE,Key), 4); + if(Placeable.Key == 0x9AC6CDD7){ + printf("WMF, Placeable: "); + printf("HWmf:%u ", Placeable.HWmf); + memcpy(&rect16, &(Placeable.Dst), sizeof(U_RECT16)); + printf("Box:"); rect16_ltrb_print(rect16); + printf("Inch:%u ", Placeable.Inch); + printf("Checksum:%d ", Placeable.Checksum); + printf("Calculated_Checksum:%d\n",U_16_checksum((int16_t *)contents,10)); + } + else { + printf("WMF, Not Placeable\n"); + } + printf(" RecType:%d\n", Header.iType); + printf(" 16bit words in record:%d\n", Header.Size16w); + printf(" Version:%d\n", Header.version); + memcpy(&utmp4, &(Header.Sizew),4); + printf(" 16bit words in file:%d\n",utmp4); + printf(" Objects:%d\n", Header.nObjects); + memcpy(&utmp4, &(Header.maxSize),4); + printf(" Largest Record:%d\n", utmp4); + printf(" nMembers:%d\n", Header.nMembers); + + return(size); +} + + + +// hide these from Doxygen +//! @cond +/* ********************************************************************************************** +These functions contain shared code used by various U_WMR*_print functions. These should NEVER be called +by end user code and to further that end prototypes are NOT provided and they are hidden from Doxygen. +*********************************************************************************************** */ + + +void wcore_points_print(uint16_t nPoints, const char *aPoints){ + int i; + U_POINT16 pt; + printf(" Points: "); + for(i=0;i<nPoints; i++){ + memcpy(&pt, aPoints + i*4, sizeof(U_POINT16)); /* aPoints U_POINT16 structure may not be aligned, so copy it out */ + printf("[%d]:",i); point16_print(pt); + } + printf("\n"); +} + + + +//! @endcond + +/* ********************************************************************************************** +These are the core WMR functions, each creates a particular type of record. +All return these records via a char* pointer, which is NULL if the call failed. +They are listed in order by the corresponding U_WMR_* index number. +*********************************************************************************************** */ + +/** + \brief Print a pointer to a U_WMR_whatever record which has not been implemented. + \param contents pointer to a buffer holding a WMR record +*/ +void U_WMRNOTIMPLEMENTED_print(const char *contents){ + (void) contents; + printf(" Not Implemented!\n"); +} + +void U_WMREOF_print(const char *contents){ + (void) contents; +} + +void U_WMRSETBKCOLOR_print(const char *contents){ + U_COLORREF Color; + int size = U_WMRSETBKCOLOR_get(contents, &Color); + if(size>0){ + printf(" %-15s ","Color:"); colorref_print(Color); printf("\n"); + } +} + +void U_WMRSETBKMODE_print(const char *contents){ + uint16_t iMode; + int size = U_WMRSETBKMODE_get(contents, &iMode); + if(size>0){ + printf(" %-15s 0x%4.4X\n","iMode:", iMode); + } +} + +void U_WMRSETMAPMODE_print(const char *contents){ + uint16_t iMode; + int size = U_WMRSETMAPMODE_get(contents, &iMode); + if(size>0){ + printf(" %-15s 0x%4.4X\n","iMode:", iMode); + } +} + +void U_WMRSETROP2_print(const char *contents){ + uint16_t iMode; + int size = U_WMRSETROP2_get(contents, &iMode); + if(size>0){ + printf(" %-15s 0x%4.4X\n","iMode:", iMode); + } +} + +void U_WMRSETRELABS_print(const char *contents){ + (void) contents; + /* This record type has only the common 6 bytes, so nothing (else) to print */ +} + +void U_WMRSETPOLYFILLMODE_print(const char *contents){ + uint16_t iMode; + int size = U_WMRSETPOLYFILLMODE_get(contents, &iMode); + if(size>0){ + printf(" %-15s 0x%4.4X\n","iMode:", iMode); + } +} + +void U_WMRSETSTRETCHBLTMODE_print(const char *contents){ + uint16_t iMode; + int size = U_WMRSETSTRETCHBLTMODE_get(contents, &iMode); + if(size>0){ + printf(" %-15s 0x%4.4X\n","iMode:", iMode); + } +} + +void U_WMRSETTEXTCHAREXTRA_print(const char *contents){ + uint16_t iMode; + int size = U_WMRSETTEXTCHAREXTRA_get(contents, &iMode); + if(size>0){ + printf(" %-15s 0x%4.4X\n","iMode:", iMode); + } +} + +void U_WMRSETTEXTCOLOR_print(const char *contents){ + U_COLORREF Color; + int size = U_WMRSETTEXTCOLOR_get(contents, &Color); + if(size>0){ + printf(" %-15s ","Color:"); colorref_print(Color); printf("\n"); + } +} + +void U_WMRSETTEXTJUSTIFICATION_print(const char *contents){ + uint16_t Count; + uint16_t Extra; + int size = U_WMRSETTEXTJUSTIFICATION_get(contents, &Count, &Extra); + if(size){ + printf(" %-15s %d\n","Count", Count); + printf(" %-15s %d\n","Extra", Extra); + } +} + +void U_WMRSETWINDOWORG_print(const char *contents){ + U_POINT16 coord; + int size = U_WMRSETWINDOWORG_get(contents, &coord); + if(size){ + printf(" %-15s {%d,%d}\n","X,Y",coord.x, coord.y); + } +} + +void U_WMRSETWINDOWEXT_print(const char *contents){ + U_POINT16 coord; + int size = U_WMRSETWINDOWEXT_get(contents, &coord); + if(size){ + printf(" %-15s {%d,%d}\n","W,H",coord.x, coord.y); + } +} + +void U_WMRSETVIEWPORTORG_print(const char *contents){ + U_POINT16 coord; + int size = U_WMRSETVIEWPORTORG_get(contents, &coord); + if(size){ + printf(" %-15s {%d,%d}\n","X,Y",coord.x, coord.y); + } +} + +void U_WMRSETVIEWPORTEXT_print(const char *contents){ + U_POINT16 coord; + int size = U_WMRSETVIEWPORTEXT_get(contents, &coord); + if(size){ + printf(" %-15s {%d,%d}\n","W,H",coord.x, coord.y); + } +} + +void U_WMROFFSETWINDOWORG_print(const char *contents){ + U_POINT16 coord; + int size = U_WMROFFSETWINDOWORG_get(contents, &coord); + if(size){ + printf(" %-15s {%d,%d}\n","X,Y",coord.x, coord.y); + } +} + +void U_WMRSCALEWINDOWEXT_print(const char *contents){ + U_POINT16 Denom, Num; + int size = U_WMRSCALEWINDOWEXT_get(contents, &Denom, &Num); + if(size > 0){ + printf(" yDenom:%d\n", Denom.y); + printf(" yNum:%d\n", Num.y ); + printf(" xDenom:%d\n", Denom.x); + printf(" xNum:%d\n", Num.x ); + } +} + +void U_WMROFFSETVIEWPORTORG_print(const char *contents){ + U_POINT16 coord; + int size = U_WMROFFSETVIEWPORTORG_get(contents, &coord); + if(size){ + printf(" %-15s {%d,%d}\n","X,Y",coord.x, coord.y); + } +} + +void U_WMRSCALEVIEWPORTEXT_print(const char *contents){ + U_POINT16 Denom, Num; + int size = U_WMRSCALEVIEWPORTEXT_get(contents, &Denom, &Num); + if(size > 0){ + printf(" yDenom:%d\n", Denom.y); + printf(" yNum:%d\n", Num.y ); + printf(" xDenom:%d\n", Denom.x); + printf(" xNum:%d\n", Num.x ); + } +} + +void U_WMRLINETO_print(const char *contents){ + U_POINT16 coord; + int size = U_WMRLINETO_get(contents, &coord); + if(size){ + printf(" %-15s {%d,%d}\n","X,Y",coord.x, coord.y); + } +} + +void U_WMRMOVETO_print(const char *contents){ + U_POINT16 coord; + int size = U_WMRMOVETO_get(contents, &coord); + if(size > 0){ + printf(" %-15s {%d,%d}\n","X,Y",coord.x, coord.y); + } +} + +void U_WMREXCLUDECLIPRECT_print(const char *contents){ + U_RECT16 rect16; + int size = U_WMREXCLUDECLIPRECT_get(contents, &rect16); + if(size > 0){ + printf(" Rect:"); + rect16_ltrb_print(rect16); + printf("\n"); + } +} + +void U_WMRINTERSECTCLIPRECT_print(const char *contents){ + U_RECT16 rect16; + int size = U_WMRINTERSECTCLIPRECT_get(contents, &rect16); + if(size > 0){ + printf(" Rect:"); + rect16_ltrb_print(rect16); + printf("\n"); + } +} + +void U_WMRARC_print(const char *contents){ + U_POINT16 StartArc, EndArc; + U_RECT16 rect16; + int size = U_WMRARC_get(contents, &StartArc, &EndArc, &rect16); + if(size > 0){ + printf(" yRadial2:%d\n", EndArc.y); + printf(" xRadial2:%d\n", EndArc.x); + printf(" yRadial1:%d\n", StartArc.y); + printf(" xRadial1:%d\n", StartArc.x); + printf(" Rect:"); rect16_ltrb_print(rect16); printf("\n"); + } +} + +void U_WMRELLIPSE_print(const char *contents){ + U_RECT16 rect16; + int size = U_WMRELLIPSE_get(contents, &rect16); + if(size > 0){ + printf(" Rect:"); + rect16_ltrb_print(rect16); + printf("\n"); + } +} + +void U_WMRFLOODFILL_print(const char *contents){ + uint16_t Mode; + U_COLORREF Color; + U_POINT16 coord; + int size = U_WMRFLOODFILL_get(contents, &Mode, &Color, &coord); + if(size > 0){ + printf(" Mode 0x%4.4X\n", Mode); + printf(" Color:"); colorref_print(Color); printf("\n"); + printf(" X,Y {%d,%d}\n", coord.x, coord.y); + } +} + +void U_WMRPIE_print(const char *contents){ + U_POINT16 StartArc, EndArc; + U_RECT16 rect16; + int size = U_WMRPIE_get(contents, &StartArc, &EndArc, &rect16); + if(size > 0){ + printf(" yRadial2:%d\n", EndArc.y); + printf(" xRadial2:%d\n", EndArc.x); + printf(" yRadial1:%d\n", StartArc.y); + printf(" xRadial1:%d\n", StartArc.x); + printf(" Rect:"); rect16_ltrb_print(rect16); printf("\n"); + } +} + +void U_WMRRECTANGLE_print(const char *contents){ + U_RECT16 rect16; + int size = U_WMRRECTANGLE_get(contents, &rect16); + if(size > 0){ + printf(" Rect:"); + rect16_ltrb_print(rect16); + printf("\n"); + } +} + +void U_WMRROUNDRECT_print(const char *contents){ + int16_t Height, Width; + U_RECT16 rect16; + int size = U_WMRROUNDRECT_get(contents, &Width, &Height, &rect16); + if(size > 0){ + printf(" Width:%d\n", Width); + printf(" Height:%d\n", Height); + printf(" Rect:"); + rect16_ltrb_print(rect16); + printf("\n"); + } +} + +void U_WMRPATBLT_print(const char *contents){ + uint32_t dwRop3; + U_POINT16 Dst; + U_POINT16 cwh; + int size = U_WMRPATBLT_get(contents, &Dst, &cwh, &dwRop3); + if(size > 0){ + printf(" Rop3:%8.8X\n", dwRop3 ); + printf(" W,H:%d,%d\n", cwh.x, cwh.y ); + printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y ); + } +} + +void U_WMRSAVEDC_print(const char *contents){ + (void) contents; + /* This record type has only the common 6 bytes, so nothing (else) to print */ +} + +void U_WMRSETPIXEL_print(const char *contents){ + U_COLORREF Color; + U_POINT16 coord; + int size = U_WMRSETPIXEL_get(contents, &Color, &coord); + if(size > 0){ + printf(" Color:"); colorref_print(Color); printf("\n"); + printf(" X,Y {%d,%d}\n", coord.x, coord.y); + } +} + +void U_WMROFFSETCLIPRGN_print(const char *contents){ + U_POINT16 coord; + int size = U_WMROFFSETCLIPRGN_get(contents, &coord); + if(size > 0){ + printf(" %-15s {%d,%d}\n","X,Y",coord.x, coord.y); + } +} + +void U_WMRTEXTOUT_print(const char *contents){ + int16_t Length; + const char *string; + U_POINT16 Dst; + int size = U_WMRTEXTOUT_get(contents, &Dst, &Length, &string); + if(size > 0){ + printf(" X,Y:{%d,%d}\n", Dst.y,Dst.x); /* y/x order in record is reversed, fix that here */ + printf(" Length:%d\n", Length); + printf(" String:<%.*s>\n", Length, string); /* May not be null terminated */ + } +} + +void U_WMRBITBLT_print(const char *contents){ + uint32_t dwRop3; + U_POINT16 Dst, Src, cwh; + U_BITMAP16 Bm16; + const char *px; + int size = U_WMRBITBLT_get(contents, &Dst, &cwh, &Src, &dwRop3, &Bm16, &px); + if(size > 0){ + printf(" Rop3:%8.8X\n", dwRop3 ); + printf(" Src X,Y:{%d,%d}\n", Src.x, Src.y); + printf(" W,H:%d,%d\n", cwh.x, cwh.y); + printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y); + if(px){ printf(" Bitmap16:"); bitmap16_print(Bm16); printf("\n"); } + else { printf(" Bitmap16: none\n"); } + } +} + +void U_WMRSTRETCHBLT_print(const char *contents){ + uint32_t dwRop3; + U_POINT16 Dst, Src, cDst, cSrc; + U_BITMAP16 Bm16; + const char *px; + int size = U_WMRSTRETCHBLT_get(contents, &Dst, &cDst, &Src, &cSrc, &dwRop3, &Bm16, &px); + if(size > 0){ + printf(" Rop3:%8.8X\n", dwRop3 ); + printf(" Src W,H:%d,%d\n", cSrc.x, cSrc.y); + printf(" Src X,Y:{%d,%d}\n", Src.x, Src.y ); + printf(" Dst W,H:%d,%d\n", cDst.x, cDst.y); + printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y ); + if(px){ printf(" Bitmap16:"); bitmap16_print(Bm16); printf("\n"); } + else { printf(" Bitmap16: none\n"); } + } +} + +void U_WMRPOLYGON_print(const char *contents){ + uint16_t Length; + const char *Data; + int size = U_WMRPOLYGON_get(contents, &Length, &Data); + if(size > 0){ + wcore_points_print(Length, Data); + } +} + +void U_WMRPOLYLINE_print(const char *contents){ + uint16_t Length; + const char *Data; + int size = U_WMRPOLYLINE_get(contents, &Length, &Data); + if(size > 0){ + wcore_points_print(Length, Data); + } +} + +void U_WMRESCAPE_print(const char *contents){ + uint32_t utmp4; + uint16_t Escape; + uint16_t Length; + const char *Data; + int size = U_WMRESCAPE_get(contents, &Escape, &Length, &Data); + if(size > 0){ + printf(" EscType:%s\n",U_wmr_escnames(Escape)); + printf(" nBytes:%d\n",Length); + if((Escape == U_MFE_SETLINECAP) || (Escape == U_MFE_SETLINEJOIN) || (Escape == U_MFE_SETMITERLIMIT)){ + memcpy(&utmp4, Data ,4); + printf(" Data:%d\n", utmp4); + } + else { + printf(" Data: (not shown)\n"); + } + } +} + +void U_WMRRESTOREDC_print(const char *contents){ + (void) contents; + /* This record type has only the common 6 bytes, so nothing (else) to print */ +} + +void U_WMRFILLREGION_print(const char *contents){ + uint16_t Region; + uint16_t Brush; + int size = U_WMRFILLREGION_get(contents, &Region, &Brush); + if(size > 0){ + printf(" %-15s %d\n","Region", Region); + printf(" %-15s %d\n","Brush", Brush); + } +} + +void U_WMRFRAMEREGION_print(const char *contents){ + uint16_t Region; + uint16_t Brush; + int16_t Height; + int16_t Width; + int size = U_WMRFRAMEREGION_get(contents, &Region, &Brush, &Height, &Width); + if(size > 0){ + printf(" Region:%d\n",Region); + printf(" Brush:%d\n", Brush ); + printf(" Height:%d\n",Height); + printf(" Width:%d\n", Width ); + } +} + +void U_WMRINVERTREGION_print(const char *contents){ + uint16_t Region; + int size = U_WMRSETTEXTALIGN_get(contents, &Region); + if(size > 0){ + printf(" %-15s %d\n","Region:", Region); + } +} + +void U_WMRPAINTREGION_print(const char *contents){ + uint16_t Region; + int size = U_WMRPAINTREGION_get(contents, &Region); + if(size>0){ + printf(" %-15s %d\n","Region:", Region); + } +} + +void U_WMRSELECTCLIPREGION_print(const char *contents){ + uint16_t Region; + int size = U_WMRSELECTCLIPREGION_get(contents, &Region); + if(size>0){ + printf(" %-15s %d\n","Region:", Region); + } +} + +void U_WMRSELECTOBJECT_print(const char *contents){ + uint16_t Object; + int size = U_WMRSELECTOBJECT_get(contents, &Object); + if(size>0){ + printf(" %-15s %d\n","Object:", Object); + } +} + +void U_WMRSETTEXTALIGN_print(const char *contents){ + uint16_t iMode; + int size = U_WMRSETTEXTALIGN_get(contents, &iMode); + if(size>0){ + printf(" %-15s 0x%4.4X\n","iMode:", iMode); + } +} + +#define U_WMRDRAWTEXT_print U_WMRNOTIMPLEMENTED_print + +void U_WMRCHORD_print(const char *contents){ + U_POINT16 StartArc, EndArc; + U_RECT16 rect16; + int size = U_WMRCHORD_get(contents, &StartArc, &EndArc, &rect16); + if(size > 0){ + printf(" yRadial2:%d\n", EndArc.y); + printf(" xRadial2:%d\n", EndArc.x); + printf(" yRadial1:%d\n", StartArc.y); + printf(" xRadial1:%d\n", StartArc.x); + printf(" Rect:"); rect16_ltrb_print(rect16); printf("\n"); + } +} + +void U_WMRSETMAPPERFLAGS_print(const char *contents){ + uint32_t Flags4; + int size = U_WMRSETMAPPERFLAGS_get(contents, &Flags4); + if(size > 0){ + printf(" %-15s 0x%8.8X\n","Flags4:", Flags4); + } +} + +void U_WMREXTTEXTOUT_print(const char *contents){ + U_RECT16 rect16; + U_POINT16 Dst; + int16_t Length; + uint16_t Opts; + const int16_t *dx; + const char *string; + int i; + int size = U_WMREXTTEXTOUT_get(contents, &Dst, &Length, &Opts, &string, &dx, &rect16); + if(size > 0){ + printf(" X,Y:{%d,%d}\n", Dst.x, Dst.y); + printf(" Length:%d\n", Length ); + printf(" Opts:%4.4X\n", Opts ); + if(Opts & (U_ETO_OPAQUE | U_ETO_CLIPPED)){ + printf(" Rect:"); rect16_ltrb_print(rect16); printf("\n"); + } + printf(" String:<%.*s>\n",Length, string); + printf(" Dx:"); + for(i=0; i<Length; i++,dx++){ printf("%d:", *dx ); } + printf("\n"); + } +} + +void U_WMRSETDIBTODEV_print(const char *contents){ + uint16_t cUsage; + uint16_t ScanCount; + uint16_t StartScan; + U_POINT16 Dst; + U_POINT16 cwh; + U_POINT16 Src; + const char *dib; + int size = U_WMRSETDIBTODEV_get(contents, &Dst, &cwh, &Src, &cUsage, &ScanCount, &StartScan, &dib); + if(size > 0){ + printf(" cUsage:%d\n", cUsage ); + printf(" ScanCount:%d\n", ScanCount ); + printf(" StartScan:%d\n", StartScan ); + printf(" Src X,Y:{%d,%d}\n", Src.x, Src.y ); + printf(" W,H:%d,%d\n", cwh.x, cwh.y ); + printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y ); + printf(" DIB:"); dibheader_print(dib); printf("\n"); + } +} + +void U_WMRSELECTPALETTE_print(const char *contents){ + uint16_t Palette; + int size = U_WMRSELECTPALETTE_get(contents, &Palette); + if(size > 0){ + printf(" %-15s %d\n","Palette:", Palette); + } +} + +void U_WMRREALIZEPALETTE_print(const char *contents){ + (void) contents; + /* This record type has only the common 6 bytes, so nothing (else) to print */ +} + +void U_WMRANIMATEPALETTE_print(const char *contents){ + U_PALETTE Palette; + const char *PalEntries; + int size = U_WMRANIMATEPALETTE_get(contents, &Palette, &PalEntries); + if(size > 0){ + printf(" Palette:"); palette_print(&Palette, PalEntries); printf("\n"); + + } +} + +void U_WMRSETPALENTRIES_print(const char *contents){ + U_PALETTE Palette; + const char *PalEntries; + int size = U_WMRSETPALENTRIES_get(contents, &Palette, &PalEntries); + if(size > 0){ + printf(" Palette:"); palette_print(&Palette, PalEntries); printf("\n"); + } +} + +void U_WMRPOLYPOLYGON_print(const char *contents){ + uint16_t nPolys; + const uint16_t *aPolyCounts; + const char *Points; + int size = U_WMRPOLYPOLYGON_get(contents, &nPolys, &aPolyCounts, &Points); + if(size > 0){ + printf(" Polygons:"); polypolygon_print(nPolys, aPolyCounts, Points); printf("\n"); + } +} + +void U_WMRRESIZEPALETTE_print(const char *contents){ + uint16_t Palette; + int size = U_WMRSELECTCLIPREGION_get(contents, &Palette); + if(size>0){ + printf(" %-15s %d\n","Palette:", Palette); + } +} + +#define U_WMR3A_print U_WMRNOTIMPLEMENTED_print +#define U_WMR3B_print U_WMRNOTIMPLEMENTED_print +#define U_WMR3C_print U_WMRNOTIMPLEMENTED_print +#define U_WMR3D_print U_WMRNOTIMPLEMENTED_print +#define U_WMR3E_print U_WMRNOTIMPLEMENTED_print +#define U_WMR3F_print U_WMRNOTIMPLEMENTED_print + +void U_WMRDIBBITBLT_print(const char *contents){ + U_POINT16 Dst, cwh, Src; + uint32_t dwRop3; + const char *dib; + int size = U_WMRDIBBITBLT_get(contents, &Dst, &cwh, &Src, &dwRop3, &dib); + if(size > 0){ + printf(" Rop3:%8.8X\n", dwRop3 ); + printf(" Src X,Y:{%d,%d}\n", Src.x, Src.x ); + printf(" W,H:%d,%d\n", cwh.x, cwh.y ); + printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y ); + if(dib){ printf(" DIB:"); dibheader_print(dib); printf("\n"); } + else { printf(" DIB: none\n"); } + } +} + +void U_WMRDIBSTRETCHBLT_print(const char *contents){ + U_POINT16 Dst, cDst, Src, cSrc; + uint32_t dwRop3; + const char *dib; + int size = U_WMRDIBSTRETCHBLT_get(contents, &Dst, &cDst, &Src, &cSrc, &dwRop3, &dib); + if(size > 0){ + printf(" Rop3:%8.8X\n", dwRop3 ); + printf(" Src W,H:%d,%d\n", cSrc.x, cSrc.y ); + printf(" Src X,Y:{%d,%d}\n", Src.x, Src.x ); + printf(" Dst W,H:%d,%d\n", cDst.x, cDst.y ); + printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y ); + if(dib){ printf(" DIB:"); dibheader_print(dib); printf("\n"); } + else { printf(" DIB: none\n"); } + } +} + +void U_WMRDIBCREATEPATTERNBRUSH_print(const char *contents){ + uint16_t Style, cUsage; + const char *TBm16; + const char *dib; + int size = U_WMRDIBCREATEPATTERNBRUSH_get(contents, &Style, &cUsage, &TBm16, &dib); + if(size > 0){ + U_BITMAP16 Bm16; + printf(" Style:%d\n", Style ); + printf(" cUsage:%d\n", cUsage); + if(TBm16){ + memcpy(&Bm16, TBm16, U_SIZE_BITMAP16); + printf(" Src:Bitmap16:"); bitmap16_print(Bm16); printf("\n"); + } + else { /* from DIB */ + printf(" Src:DIB:"); dibheader_print(dib); printf("\n"); + } + } +} + +void U_WMRSTRETCHDIB_print(const char *contents){ + U_POINT16 Dst, cDst, Src, cSrc; + uint32_t dwRop3; + uint16_t cUsage; + const char *dib; + int size = U_WMRSTRETCHDIB_get(contents, &Dst, &cDst, &Src, &cSrc, &cUsage, &dwRop3, &dib); + if(size > 0){ + printf(" Rop3:%8.8X\n", dwRop3 ); + printf(" cUsage:%d\n", cUsage ); + printf(" Src W,H:%d,%d\n", cSrc.x, cSrc.y ); + printf(" Src X,Y:{%d,%d}\n", Src.x, Src.x ); + printf(" Dst W,H:%d,%d\n", cDst.x, cDst.y ); + printf(" Dst X,Y:{%d,%d}\n", Dst.x, Dst.y ); + if(dib){ printf(" DIB:"); dibheader_print(dib); printf("\n"); } + else { printf(" DIB: none\n"); } + } +} + +#define U_WMR44_print U_WMRNOTIMPLEMENTED_print +#define U_WMR45_print U_WMRNOTIMPLEMENTED_print +#define U_WMR46_print U_WMRNOTIMPLEMENTED_print +#define U_WMR47_print U_WMRNOTIMPLEMENTED_print + +void U_WMREXTFLOODFILL_print(const char *contents){ + uint16_t Mode; + U_COLORREF Color; + U_POINT16 coord; + int size = U_WMREXTFLOODFILL_get(contents, &Mode, &Color, &coord); + if(size > 0){ + printf(" Mode 0x%4.4X\n", Mode); + printf(" Color:"); colorref_print(Color); printf("\n"); + printf(" X,Y {%d,%d}\n", coord.x, coord.y); + } +} + +#define U_WMR49_print U_WMRNOTIMPLEMENTED_print +#define U_WMR4A_print U_WMRNOTIMPLEMENTED_print +#define U_WMR4B_print U_WMRNOTIMPLEMENTED_print +#define U_WMR4C_print U_WMRNOTIMPLEMENTED_print +#define U_WMR4D_print U_WMRNOTIMPLEMENTED_print +#define U_WMR4E_print U_WMRNOTIMPLEMENTED_print +#define U_WMR4F_print U_WMRNOTIMPLEMENTED_print +#define U_WMR50_print U_WMRNOTIMPLEMENTED_print +#define U_WMR51_print U_WMRNOTIMPLEMENTED_print +#define U_WMR52_print U_WMRNOTIMPLEMENTED_print +#define U_WMR53_print U_WMRNOTIMPLEMENTED_print +#define U_WMR54_print U_WMRNOTIMPLEMENTED_print +#define U_WMR55_print U_WMRNOTIMPLEMENTED_print +#define U_WMR56_print U_WMRNOTIMPLEMENTED_print +#define U_WMR57_print U_WMRNOTIMPLEMENTED_print +#define U_WMR58_print U_WMRNOTIMPLEMENTED_print +#define U_WMR59_print U_WMRNOTIMPLEMENTED_print +#define U_WMR5A_print U_WMRNOTIMPLEMENTED_print +#define U_WMR5B_print U_WMRNOTIMPLEMENTED_print +#define U_WMR5C_print U_WMRNOTIMPLEMENTED_print +#define U_WMR5D_print U_WMRNOTIMPLEMENTED_print +#define U_WMR5E_print U_WMRNOTIMPLEMENTED_print +#define U_WMR5F_print U_WMRNOTIMPLEMENTED_print +#define U_WMR60_print U_WMRNOTIMPLEMENTED_print +#define U_WMR61_print U_WMRNOTIMPLEMENTED_print +#define U_WMR62_print U_WMRNOTIMPLEMENTED_print +#define U_WMR63_print U_WMRNOTIMPLEMENTED_print +#define U_WMR64_print U_WMRNOTIMPLEMENTED_print +#define U_WMR65_print U_WMRNOTIMPLEMENTED_print +#define U_WMR66_print U_WMRNOTIMPLEMENTED_print +#define U_WMR67_print U_WMRNOTIMPLEMENTED_print +#define U_WMR68_print U_WMRNOTIMPLEMENTED_print +#define U_WMR69_print U_WMRNOTIMPLEMENTED_print +#define U_WMR6A_print U_WMRNOTIMPLEMENTED_print +#define U_WMR6B_print U_WMRNOTIMPLEMENTED_print +#define U_WMR6C_print U_WMRNOTIMPLEMENTED_print +#define U_WMR6D_print U_WMRNOTIMPLEMENTED_print +#define U_WMR6E_print U_WMRNOTIMPLEMENTED_print +#define U_WMR6F_print U_WMRNOTIMPLEMENTED_print +#define U_WMR70_print U_WMRNOTIMPLEMENTED_print +#define U_WMR71_print U_WMRNOTIMPLEMENTED_print +#define U_WMR72_print U_WMRNOTIMPLEMENTED_print +#define U_WMR73_print U_WMRNOTIMPLEMENTED_print +#define U_WMR74_print U_WMRNOTIMPLEMENTED_print +#define U_WMR75_print U_WMRNOTIMPLEMENTED_print +#define U_WMR76_print U_WMRNOTIMPLEMENTED_print +#define U_WMR77_print U_WMRNOTIMPLEMENTED_print +#define U_WMR78_print U_WMRNOTIMPLEMENTED_print +#define U_WMR79_print U_WMRNOTIMPLEMENTED_print +#define U_WMR7A_print U_WMRNOTIMPLEMENTED_print +#define U_WMR7B_print U_WMRNOTIMPLEMENTED_print +#define U_WMR7C_print U_WMRNOTIMPLEMENTED_print +#define U_WMR7D_print U_WMRNOTIMPLEMENTED_print +#define U_WMR7E_print U_WMRNOTIMPLEMENTED_print +#define U_WMR7F_print U_WMRNOTIMPLEMENTED_print +#define U_WMR80_print U_WMRNOTIMPLEMENTED_print +#define U_WMR81_print U_WMRNOTIMPLEMENTED_print +#define U_WMR82_print U_WMRNOTIMPLEMENTED_print +#define U_WMR83_print U_WMRNOTIMPLEMENTED_print +#define U_WMR84_print U_WMRNOTIMPLEMENTED_print +#define U_WMR85_print U_WMRNOTIMPLEMENTED_print +#define U_WMR86_print U_WMRNOTIMPLEMENTED_print +#define U_WMR87_print U_WMRNOTIMPLEMENTED_print +#define U_WMR88_print U_WMRNOTIMPLEMENTED_print +#define U_WMR89_print U_WMRNOTIMPLEMENTED_print +#define U_WMR8A_print U_WMRNOTIMPLEMENTED_print +#define U_WMR8B_print U_WMRNOTIMPLEMENTED_print +#define U_WMR8C_print U_WMRNOTIMPLEMENTED_print +#define U_WMR8D_print U_WMRNOTIMPLEMENTED_print +#define U_WMR8E_print U_WMRNOTIMPLEMENTED_print +#define U_WMR8F_print U_WMRNOTIMPLEMENTED_print +#define U_WMR90_print U_WMRNOTIMPLEMENTED_print +#define U_WMR91_print U_WMRNOTIMPLEMENTED_print +#define U_WMR92_print U_WMRNOTIMPLEMENTED_print +#define U_WMR93_print U_WMRNOTIMPLEMENTED_print +#define U_WMR94_print U_WMRNOTIMPLEMENTED_print +#define U_WMR95_print U_WMRNOTIMPLEMENTED_print +#define U_WMR96_print U_WMRNOTIMPLEMENTED_print +#define U_WMR97_print U_WMRNOTIMPLEMENTED_print +#define U_WMR98_print U_WMRNOTIMPLEMENTED_print +#define U_WMR99_print U_WMRNOTIMPLEMENTED_print +#define U_WMR9A_print U_WMRNOTIMPLEMENTED_print +#define U_WMR9B_print U_WMRNOTIMPLEMENTED_print +#define U_WMR9C_print U_WMRNOTIMPLEMENTED_print +#define U_WMR9D_print U_WMRNOTIMPLEMENTED_print +#define U_WMR9E_print U_WMRNOTIMPLEMENTED_print +#define U_WMR9F_print U_WMRNOTIMPLEMENTED_print +#define U_WMRA0_print U_WMRNOTIMPLEMENTED_print +#define U_WMRA1_print U_WMRNOTIMPLEMENTED_print +#define U_WMRA2_print U_WMRNOTIMPLEMENTED_print +#define U_WMRA3_print U_WMRNOTIMPLEMENTED_print +#define U_WMRA4_print U_WMRNOTIMPLEMENTED_print +#define U_WMRA5_print U_WMRNOTIMPLEMENTED_print +#define U_WMRA6_print U_WMRNOTIMPLEMENTED_print +#define U_WMRA7_print U_WMRNOTIMPLEMENTED_print +#define U_WMRA8_print U_WMRNOTIMPLEMENTED_print +#define U_WMRA9_print U_WMRNOTIMPLEMENTED_print +#define U_WMRAA_print U_WMRNOTIMPLEMENTED_print +#define U_WMRAB_print U_WMRNOTIMPLEMENTED_print +#define U_WMRAC_print U_WMRNOTIMPLEMENTED_print +#define U_WMRAD_print U_WMRNOTIMPLEMENTED_print +#define U_WMRAE_print U_WMRNOTIMPLEMENTED_print +#define U_WMRAF_print U_WMRNOTIMPLEMENTED_print +#define U_WMRB0_print U_WMRNOTIMPLEMENTED_print +#define U_WMRB1_print U_WMRNOTIMPLEMENTED_print +#define U_WMRB2_print U_WMRNOTIMPLEMENTED_print +#define U_WMRB3_print U_WMRNOTIMPLEMENTED_print +#define U_WMRB4_print U_WMRNOTIMPLEMENTED_print +#define U_WMRB5_print U_WMRNOTIMPLEMENTED_print +#define U_WMRB6_print U_WMRNOTIMPLEMENTED_print +#define U_WMRB7_print U_WMRNOTIMPLEMENTED_print +#define U_WMRB8_print U_WMRNOTIMPLEMENTED_print +#define U_WMRB9_print U_WMRNOTIMPLEMENTED_print +#define U_WMRBA_print U_WMRNOTIMPLEMENTED_print +#define U_WMRBB_print U_WMRNOTIMPLEMENTED_print +#define U_WMRBC_print U_WMRNOTIMPLEMENTED_print +#define U_WMRBD_print U_WMRNOTIMPLEMENTED_print +#define U_WMRBE_print U_WMRNOTIMPLEMENTED_print +#define U_WMRBF_print U_WMRNOTIMPLEMENTED_print +#define U_WMRC0_print U_WMRNOTIMPLEMENTED_print +#define U_WMRC1_print U_WMRNOTIMPLEMENTED_print +#define U_WMRC2_print U_WMRNOTIMPLEMENTED_print +#define U_WMRC3_print U_WMRNOTIMPLEMENTED_print +#define U_WMRC4_print U_WMRNOTIMPLEMENTED_print +#define U_WMRC5_print U_WMRNOTIMPLEMENTED_print +#define U_WMRC6_print U_WMRNOTIMPLEMENTED_print +#define U_WMRC7_print U_WMRNOTIMPLEMENTED_print +#define U_WMRC8_print U_WMRNOTIMPLEMENTED_print +#define U_WMRC9_print U_WMRNOTIMPLEMENTED_print +#define U_WMRCA_print U_WMRNOTIMPLEMENTED_print +#define U_WMRCB_print U_WMRNOTIMPLEMENTED_print +#define U_WMRCC_print U_WMRNOTIMPLEMENTED_print +#define U_WMRCD_print U_WMRNOTIMPLEMENTED_print +#define U_WMRCE_print U_WMRNOTIMPLEMENTED_print +#define U_WMRCF_print U_WMRNOTIMPLEMENTED_print +#define U_WMRD0_print U_WMRNOTIMPLEMENTED_print +#define U_WMRD1_print U_WMRNOTIMPLEMENTED_print +#define U_WMRD2_print U_WMRNOTIMPLEMENTED_print +#define U_WMRD3_print U_WMRNOTIMPLEMENTED_print +#define U_WMRD4_print U_WMRNOTIMPLEMENTED_print +#define U_WMRD5_print U_WMRNOTIMPLEMENTED_print +#define U_WMRD6_print U_WMRNOTIMPLEMENTED_print +#define U_WMRD7_print U_WMRNOTIMPLEMENTED_print +#define U_WMRD8_print U_WMRNOTIMPLEMENTED_print +#define U_WMRD9_print U_WMRNOTIMPLEMENTED_print +#define U_WMRDA_print U_WMRNOTIMPLEMENTED_print +#define U_WMRDB_print U_WMRNOTIMPLEMENTED_print +#define U_WMRDC_print U_WMRNOTIMPLEMENTED_print +#define U_WMRDD_print U_WMRNOTIMPLEMENTED_print +#define U_WMRDE_print U_WMRNOTIMPLEMENTED_print +#define U_WMRDF_print U_WMRNOTIMPLEMENTED_print +#define U_WMRE0_print U_WMRNOTIMPLEMENTED_print +#define U_WMRE1_print U_WMRNOTIMPLEMENTED_print +#define U_WMRE2_print U_WMRNOTIMPLEMENTED_print +#define U_WMRE3_print U_WMRNOTIMPLEMENTED_print +#define U_WMRE4_print U_WMRNOTIMPLEMENTED_print +#define U_WMRE5_print U_WMRNOTIMPLEMENTED_print +#define U_WMRE6_print U_WMRNOTIMPLEMENTED_print +#define U_WMRE7_print U_WMRNOTIMPLEMENTED_print +#define U_WMRE8_print U_WMRNOTIMPLEMENTED_print +#define U_WMRE9_print U_WMRNOTIMPLEMENTED_print +#define U_WMREA_print U_WMRNOTIMPLEMENTED_print +#define U_WMREB_print U_WMRNOTIMPLEMENTED_print +#define U_WMREC_print U_WMRNOTIMPLEMENTED_print +#define U_WMRED_print U_WMRNOTIMPLEMENTED_print +#define U_WMREE_print U_WMRNOTIMPLEMENTED_print +#define U_WMREF_print U_WMRNOTIMPLEMENTED_print + +void U_WMRDELETEOBJECT_print(const char *contents){ + uint16_t Object; + int size = U_WMRDELETEOBJECT_get(contents, &Object); + if(size>0){ + printf(" %-15s %d\n","Object:", Object); + } +} + +#define U_WMRF1_print U_WMRNOTIMPLEMENTED_print +#define U_WMRF2_print U_WMRNOTIMPLEMENTED_print +#define U_WMRF3_print U_WMRNOTIMPLEMENTED_print +#define U_WMRF4_print U_WMRNOTIMPLEMENTED_print +#define U_WMRF5_print U_WMRNOTIMPLEMENTED_print +#define U_WMRF6_print U_WMRNOTIMPLEMENTED_print + +void U_WMRCREATEPALETTE_print(const char *contents){ + U_PALETTE Palette; + const char *PalEntries; + int size = U_WMRCREATEPALETTE_get(contents, &Palette, &PalEntries); + if(size > 0){ + printf(" Palette:"); palette_print(&Palette, PalEntries); printf("\n"); + + } +} + +#define U_WMRF8_print U_WMRNOTIMPLEMENTED_print + +void U_WMRCREATEPATTERNBRUSH_print(const char *contents){ + U_BITMAP16 Bm16; + int pasize; + int i; + const char *Pattern; + + int size = U_WMRCREATEPATTERNBRUSH_get(contents, &Bm16, &pasize, &Pattern); + if(size > 0){ + /* BM16 is truncated, but bitmap16_print does not get into the part that was omitted */ + printf(" BitMap16: "); bitmap16_print(Bm16); printf("\n"); + printf(" Pattern: "); + for(i=0;i<pasize;i++){ + printf("%2.2X ",Pattern[i]); + } + printf("\n"); + } +} + +void U_WMRCREATEPENINDIRECT_print(const char *contents){ + U_PEN pen; + int size = U_WMRCREATEPENINDIRECT_get(contents, &pen); + if(size > 0){ + printf(" Pen:"); pen_print(pen); printf("\n"); + } +} + +void U_WMRCREATEFONTINDIRECT_print(const char *contents){ + const char *font; /* Note, because of possible struct alignment issue have to use char * to reference the data */ + int size = U_WMRCREATEFONTINDIRECT_get(contents, &font); + if(size > 0){ + printf(" Font:"); + font_print(font); + printf("\n"); + } +} + +void U_WMRCREATEBRUSHINDIRECT_print(const char *contents){ + const char *brush; /* Note, because of possible struct alignment issue have to use char * to reference the data */ + int size = U_WMRCREATEBRUSHINDIRECT_get(contents, &brush); + if(size > 0){ + printf(" Brush:"); + wlogbrush_print(brush); + printf("\n"); + } +} + +void U_WMRCREATEBITMAPINDIRECT_print(const char *contents){ /* in Wine, not in WMF PDF */ + U_WMRNOTIMPLEMENTED_print(contents); +} + +void U_WMRCREATEBITMAP_print(const char *contents){ /* in Wine, not in WMF PDF */ + U_WMRNOTIMPLEMENTED_print(contents); +} + +void U_WMRCREATEREGION_print(const char *contents){ + const char *region; /* Note, because of possible struct alignment issue have to use char * to reference the data */ + int size = U_WMRCREATEBRUSHINDIRECT_get(contents, ®ion); + if(size > 0){ + printf(" Brush:"); + printf(" Region: "); region_print(region); printf("\n"); + } +} + +/** + \brief Print any record in a wmf + \returns record length for a normal record, 0 for WMREOF, -1 for a bad record + \param contents pointer to a buffer holding all WMR records + \param blimit one byte past the last WMF record in memory. + \param recnum number of this record in contents + \param off offset to this record in contents +*/ +int U_wmf_onerec_print(const char *contents, const char *blimit, int recnum, size_t off){ + + + uint8_t iType; + size_t size; + + contents += off; + + /* Check that the record size is OK, abort if not. + Pointer math might wrap, so check both sides of the range */ + size = U_WMRRECSAFE_get(contents, blimit); + if(!size)return(-1); + + iType = *(uint8_t *)(contents + offsetof(U_METARECORD, iType ) ); + +#if 1 /* show record checksums, this is NOT portable, result changes with endian type, useful for debugging */ + printf("%-30srecord:%5d type:%3u offset:%8d size:%8u\n", + U_wmr_names(iType), recnum, iType, (int) off, (int) size); +#else + printf("%-30srecord:%5d type:%3u offset:%8d size:%8u recchecksum:%u\n", + U_wmr_names(iType), recnum, iType, (int) off, (int) size, U_16_checksum((int16_t *)contents, size)); +#endif + + switch (iType) + { + case U_WMR_EOF: U_WMREOF_print(contents); size=0; break; + case U_WMR_SETBKCOLOR: U_WMRSETBKCOLOR_print(contents); break; + case U_WMR_SETBKMODE: U_WMRSETBKMODE_print(contents); break; + case U_WMR_SETMAPMODE: U_WMRSETMAPMODE_print(contents); break; + case U_WMR_SETROP2: U_WMRSETROP2_print(contents); break; + case U_WMR_SETRELABS: U_WMRSETRELABS_print(contents); break; + case U_WMR_SETPOLYFILLMODE: U_WMRSETPOLYFILLMODE_print(contents); break; + case U_WMR_SETSTRETCHBLTMODE: U_WMRSETSTRETCHBLTMODE_print(contents); break; + case U_WMR_SETTEXTCHAREXTRA: U_WMRSETTEXTCHAREXTRA_print(contents); break; + case U_WMR_SETTEXTCOLOR: U_WMRSETTEXTCOLOR_print(contents); break; + case U_WMR_SETTEXTJUSTIFICATION: U_WMRSETTEXTJUSTIFICATION_print(contents); break; + case U_WMR_SETWINDOWORG: U_WMRSETWINDOWORG_print(contents); break; + case U_WMR_SETWINDOWEXT: U_WMRSETWINDOWEXT_print(contents); break; + case U_WMR_SETVIEWPORTORG: U_WMRSETVIEWPORTORG_print(contents); break; + case U_WMR_SETVIEWPORTEXT: U_WMRSETVIEWPORTEXT_print(contents); break; + case U_WMR_OFFSETWINDOWORG: U_WMROFFSETWINDOWORG_print(contents); break; + case U_WMR_SCALEWINDOWEXT: U_WMRSCALEWINDOWEXT_print(contents); break; + case U_WMR_OFFSETVIEWPORTORG: U_WMROFFSETVIEWPORTORG_print(contents); break; + case U_WMR_SCALEVIEWPORTEXT: U_WMRSCALEVIEWPORTEXT_print(contents); break; + case U_WMR_LINETO: U_WMRLINETO_print(contents); break; + case U_WMR_MOVETO: U_WMRMOVETO_print(contents); break; + case U_WMR_EXCLUDECLIPRECT: U_WMREXCLUDECLIPRECT_print(contents); break; + case U_WMR_INTERSECTCLIPRECT: U_WMRINTERSECTCLIPRECT_print(contents); break; + case U_WMR_ARC: U_WMRARC_print(contents); break; + case U_WMR_ELLIPSE: U_WMRELLIPSE_print(contents); break; + case U_WMR_FLOODFILL: U_WMRFLOODFILL_print(contents); break; + case U_WMR_PIE: U_WMRPIE_print(contents); break; + case U_WMR_RECTANGLE: U_WMRRECTANGLE_print(contents); break; + case U_WMR_ROUNDRECT: U_WMRROUNDRECT_print(contents); break; + case U_WMR_PATBLT: U_WMRPATBLT_print(contents); break; + case U_WMR_SAVEDC: U_WMRSAVEDC_print(contents); break; + case U_WMR_SETPIXEL: U_WMRSETPIXEL_print(contents); break; + case U_WMR_OFFSETCLIPRGN: U_WMROFFSETCLIPRGN_print(contents); break; + case U_WMR_TEXTOUT: U_WMRTEXTOUT_print(contents); break; + case U_WMR_BITBLT: U_WMRBITBLT_print(contents); break; + case U_WMR_STRETCHBLT: U_WMRSTRETCHBLT_print(contents); break; + case U_WMR_POLYGON: U_WMRPOLYGON_print(contents); break; + case U_WMR_POLYLINE: U_WMRPOLYLINE_print(contents); break; + case U_WMR_ESCAPE: U_WMRESCAPE_print(contents); break; + case U_WMR_RESTOREDC: U_WMRRESTOREDC_print(contents); break; + case U_WMR_FILLREGION: U_WMRFILLREGION_print(contents); break; + case U_WMR_FRAMEREGION: U_WMRFRAMEREGION_print(contents); break; + case U_WMR_INVERTREGION: U_WMRINVERTREGION_print(contents); break; + case U_WMR_PAINTREGION: U_WMRPAINTREGION_print(contents); break; + case U_WMR_SELECTCLIPREGION: U_WMRSELECTCLIPREGION_print(contents); break; + case U_WMR_SELECTOBJECT: U_WMRSELECTOBJECT_print(contents); break; + case U_WMR_SETTEXTALIGN: U_WMRSETTEXTALIGN_print(contents); break; + case U_WMR_DRAWTEXT: U_WMRDRAWTEXT_print(contents); break; + case U_WMR_CHORD: U_WMRCHORD_print(contents); break; + case U_WMR_SETMAPPERFLAGS: U_WMRSETMAPPERFLAGS_print(contents); break; + case U_WMR_EXTTEXTOUT: U_WMREXTTEXTOUT_print(contents); break; + case U_WMR_SETDIBTODEV: U_WMRSETDIBTODEV_print(contents); break; + case U_WMR_SELECTPALETTE: U_WMRSELECTPALETTE_print(contents); break; + case U_WMR_REALIZEPALETTE: U_WMRREALIZEPALETTE_print(contents); break; + case U_WMR_ANIMATEPALETTE: U_WMRANIMATEPALETTE_print(contents); break; + case U_WMR_SETPALENTRIES: U_WMRSETPALENTRIES_print(contents); break; + case U_WMR_POLYPOLYGON: U_WMRPOLYPOLYGON_print(contents); break; + case U_WMR_RESIZEPALETTE: U_WMRRESIZEPALETTE_print(contents); break; + case U_WMR_3A: U_WMR3A_print(contents); break; + case U_WMR_3B: U_WMR3B_print(contents); break; + case U_WMR_3C: U_WMR3C_print(contents); break; + case U_WMR_3D: U_WMR3D_print(contents); break; + case U_WMR_3E: U_WMR3E_print(contents); break; + case U_WMR_3F: U_WMR3F_print(contents); break; + case U_WMR_DIBBITBLT: U_WMRDIBBITBLT_print(contents); break; + case U_WMR_DIBSTRETCHBLT: U_WMRDIBSTRETCHBLT_print(contents); break; + case U_WMR_DIBCREATEPATTERNBRUSH: U_WMRDIBCREATEPATTERNBRUSH_print(contents); break; + case U_WMR_STRETCHDIB: U_WMRSTRETCHDIB_print(contents); break; + case U_WMR_44: U_WMR44_print(contents); break; + case U_WMR_45: U_WMR45_print(contents); break; + case U_WMR_46: U_WMR46_print(contents); break; + case U_WMR_47: U_WMR47_print(contents); break; + case U_WMR_EXTFLOODFILL: U_WMREXTFLOODFILL_print(contents); break; + case U_WMR_49: U_WMR49_print(contents); break; + case U_WMR_4A: U_WMR4A_print(contents); break; + case U_WMR_4B: U_WMR4B_print(contents); break; + case U_WMR_4C: U_WMR4C_print(contents); break; + case U_WMR_4D: U_WMR4D_print(contents); break; + case U_WMR_4E: U_WMR4E_print(contents); break; + case U_WMR_4F: U_WMR4F_print(contents); break; + case U_WMR_50: U_WMR50_print(contents); break; + case U_WMR_51: U_WMR51_print(contents); break; + case U_WMR_52: U_WMR52_print(contents); break; + case U_WMR_53: U_WMR53_print(contents); break; + case U_WMR_54: U_WMR54_print(contents); break; + case U_WMR_55: U_WMR55_print(contents); break; + case U_WMR_56: U_WMR56_print(contents); break; + case U_WMR_57: U_WMR57_print(contents); break; + case U_WMR_58: U_WMR58_print(contents); break; + case U_WMR_59: U_WMR59_print(contents); break; + case U_WMR_5A: U_WMR5A_print(contents); break; + case U_WMR_5B: U_WMR5B_print(contents); break; + case U_WMR_5C: U_WMR5C_print(contents); break; + case U_WMR_5D: U_WMR5D_print(contents); break; + case U_WMR_5E: U_WMR5E_print(contents); break; + case U_WMR_5F: U_WMR5F_print(contents); break; + case U_WMR_60: U_WMR60_print(contents); break; + case U_WMR_61: U_WMR61_print(contents); break; + case U_WMR_62: U_WMR62_print(contents); break; + case U_WMR_63: U_WMR63_print(contents); break; + case U_WMR_64: U_WMR64_print(contents); break; + case U_WMR_65: U_WMR65_print(contents); break; + case U_WMR_66: U_WMR66_print(contents); break; + case U_WMR_67: U_WMR67_print(contents); break; + case U_WMR_68: U_WMR68_print(contents); break; + case U_WMR_69: U_WMR69_print(contents); break; + case U_WMR_6A: U_WMR6A_print(contents); break; + case U_WMR_6B: U_WMR6B_print(contents); break; + case U_WMR_6C: U_WMR6C_print(contents); break; + case U_WMR_6D: U_WMR6D_print(contents); break; + case U_WMR_6E: U_WMR6E_print(contents); break; + case U_WMR_6F: U_WMR6F_print(contents); break; + case U_WMR_70: U_WMR70_print(contents); break; + case U_WMR_71: U_WMR71_print(contents); break; + case U_WMR_72: U_WMR72_print(contents); break; + case U_WMR_73: U_WMR73_print(contents); break; + case U_WMR_74: U_WMR74_print(contents); break; + case U_WMR_75: U_WMR75_print(contents); break; + case U_WMR_76: U_WMR76_print(contents); break; + case U_WMR_77: U_WMR77_print(contents); break; + case U_WMR_78: U_WMR78_print(contents); break; + case U_WMR_79: U_WMR79_print(contents); break; + case U_WMR_7A: U_WMR7A_print(contents); break; + case U_WMR_7B: U_WMR7B_print(contents); break; + case U_WMR_7C: U_WMR7C_print(contents); break; + case U_WMR_7D: U_WMR7D_print(contents); break; + case U_WMR_7E: U_WMR7E_print(contents); break; + case U_WMR_7F: U_WMR7F_print(contents); break; + case U_WMR_80: U_WMR80_print(contents); break; + case U_WMR_81: U_WMR81_print(contents); break; + case U_WMR_82: U_WMR82_print(contents); break; + case U_WMR_83: U_WMR83_print(contents); break; + case U_WMR_84: U_WMR84_print(contents); break; + case U_WMR_85: U_WMR85_print(contents); break; + case U_WMR_86: U_WMR86_print(contents); break; + case U_WMR_87: U_WMR87_print(contents); break; + case U_WMR_88: U_WMR88_print(contents); break; + case U_WMR_89: U_WMR89_print(contents); break; + case U_WMR_8A: U_WMR8A_print(contents); break; + case U_WMR_8B: U_WMR8B_print(contents); break; + case U_WMR_8C: U_WMR8C_print(contents); break; + case U_WMR_8D: U_WMR8D_print(contents); break; + case U_WMR_8E: U_WMR8E_print(contents); break; + case U_WMR_8F: U_WMR8F_print(contents); break; + case U_WMR_90: U_WMR90_print(contents); break; + case U_WMR_91: U_WMR91_print(contents); break; + case U_WMR_92: U_WMR92_print(contents); break; + case U_WMR_93: U_WMR93_print(contents); break; + case U_WMR_94: U_WMR94_print(contents); break; + case U_WMR_95: U_WMR95_print(contents); break; + case U_WMR_96: U_WMR96_print(contents); break; + case U_WMR_97: U_WMR97_print(contents); break; + case U_WMR_98: U_WMR98_print(contents); break; + case U_WMR_99: U_WMR99_print(contents); break; + case U_WMR_9A: U_WMR9A_print(contents); break; + case U_WMR_9B: U_WMR9B_print(contents); break; + case U_WMR_9C: U_WMR9C_print(contents); break; + case U_WMR_9D: U_WMR9D_print(contents); break; + case U_WMR_9E: U_WMR9E_print(contents); break; + case U_WMR_9F: U_WMR9F_print(contents); break; + case U_WMR_A0: U_WMRA0_print(contents); break; + case U_WMR_A1: U_WMRA1_print(contents); break; + case U_WMR_A2: U_WMRA2_print(contents); break; + case U_WMR_A3: U_WMRA3_print(contents); break; + case U_WMR_A4: U_WMRA4_print(contents); break; + case U_WMR_A5: U_WMRA5_print(contents); break; + case U_WMR_A6: U_WMRA6_print(contents); break; + case U_WMR_A7: U_WMRA7_print(contents); break; + case U_WMR_A8: U_WMRA8_print(contents); break; + case U_WMR_A9: U_WMRA9_print(contents); break; + case U_WMR_AA: U_WMRAA_print(contents); break; + case U_WMR_AB: U_WMRAB_print(contents); break; + case U_WMR_AC: U_WMRAC_print(contents); break; + case U_WMR_AD: U_WMRAD_print(contents); break; + case U_WMR_AE: U_WMRAE_print(contents); break; + case U_WMR_AF: U_WMRAF_print(contents); break; + case U_WMR_B0: U_WMRB0_print(contents); break; + case U_WMR_B1: U_WMRB1_print(contents); break; + case U_WMR_B2: U_WMRB2_print(contents); break; + case U_WMR_B3: U_WMRB3_print(contents); break; + case U_WMR_B4: U_WMRB4_print(contents); break; + case U_WMR_B5: U_WMRB5_print(contents); break; + case U_WMR_B6: U_WMRB6_print(contents); break; + case U_WMR_B7: U_WMRB7_print(contents); break; + case U_WMR_B8: U_WMRB8_print(contents); break; + case U_WMR_B9: U_WMRB9_print(contents); break; + case U_WMR_BA: U_WMRBA_print(contents); break; + case U_WMR_BB: U_WMRBB_print(contents); break; + case U_WMR_BC: U_WMRBC_print(contents); break; + case U_WMR_BD: U_WMRBD_print(contents); break; + case U_WMR_BE: U_WMRBE_print(contents); break; + case U_WMR_BF: U_WMRBF_print(contents); break; + case U_WMR_C0: U_WMRC0_print(contents); break; + case U_WMR_C1: U_WMRC1_print(contents); break; + case U_WMR_C2: U_WMRC2_print(contents); break; + case U_WMR_C3: U_WMRC3_print(contents); break; + case U_WMR_C4: U_WMRC4_print(contents); break; + case U_WMR_C5: U_WMRC5_print(contents); break; + case U_WMR_C6: U_WMRC6_print(contents); break; + case U_WMR_C7: U_WMRC7_print(contents); break; + case U_WMR_C8: U_WMRC8_print(contents); break; + case U_WMR_C9: U_WMRC9_print(contents); break; + case U_WMR_CA: U_WMRCA_print(contents); break; + case U_WMR_CB: U_WMRCB_print(contents); break; + case U_WMR_CC: U_WMRCC_print(contents); break; + case U_WMR_CD: U_WMRCD_print(contents); break; + case U_WMR_CE: U_WMRCE_print(contents); break; + case U_WMR_CF: U_WMRCF_print(contents); break; + case U_WMR_D0: U_WMRD0_print(contents); break; + case U_WMR_D1: U_WMRD1_print(contents); break; + case U_WMR_D2: U_WMRD2_print(contents); break; + case U_WMR_D3: U_WMRD3_print(contents); break; + case U_WMR_D4: U_WMRD4_print(contents); break; + case U_WMR_D5: U_WMRD5_print(contents); break; + case U_WMR_D6: U_WMRD6_print(contents); break; + case U_WMR_D7: U_WMRD7_print(contents); break; + case U_WMR_D8: U_WMRD8_print(contents); break; + case U_WMR_D9: U_WMRD9_print(contents); break; + case U_WMR_DA: U_WMRDA_print(contents); break; + case U_WMR_DB: U_WMRDB_print(contents); break; + case U_WMR_DC: U_WMRDC_print(contents); break; + case U_WMR_DD: U_WMRDD_print(contents); break; + case U_WMR_DE: U_WMRDE_print(contents); break; + case U_WMR_DF: U_WMRDF_print(contents); break; + case U_WMR_E0: U_WMRE0_print(contents); break; + case U_WMR_E1: U_WMRE1_print(contents); break; + case U_WMR_E2: U_WMRE2_print(contents); break; + case U_WMR_E3: U_WMRE3_print(contents); break; + case U_WMR_E4: U_WMRE4_print(contents); break; + case U_WMR_E5: U_WMRE5_print(contents); break; + case U_WMR_E6: U_WMRE6_print(contents); break; + case U_WMR_E7: U_WMRE7_print(contents); break; + case U_WMR_E8: U_WMRE8_print(contents); break; + case U_WMR_E9: U_WMRE9_print(contents); break; + case U_WMR_EA: U_WMREA_print(contents); break; + case U_WMR_EB: U_WMREB_print(contents); break; + case U_WMR_EC: U_WMREC_print(contents); break; + case U_WMR_ED: U_WMRED_print(contents); break; + case U_WMR_EE: U_WMREE_print(contents); break; + case U_WMR_EF: U_WMREF_print(contents); break; + case U_WMR_DELETEOBJECT: U_WMRDELETEOBJECT_print(contents); break; + case U_WMR_F1: U_WMRF1_print(contents); break; + case U_WMR_F2: U_WMRF2_print(contents); break; + case U_WMR_F3: U_WMRF3_print(contents); break; + case U_WMR_F4: U_WMRF4_print(contents); break; + case U_WMR_F5: U_WMRF5_print(contents); break; + case U_WMR_F6: U_WMRF6_print(contents); break; + case U_WMR_CREATEPALETTE: U_WMRCREATEPALETTE_print(contents); break; + case U_WMR_F8: U_WMRF8_print(contents); break; + case U_WMR_CREATEPATTERNBRUSH: U_WMRCREATEPATTERNBRUSH_print(contents); break; + case U_WMR_CREATEPENINDIRECT: U_WMRCREATEPENINDIRECT_print(contents); break; + case U_WMR_CREATEFONTINDIRECT: U_WMRCREATEFONTINDIRECT_print(contents); break; + case U_WMR_CREATEBRUSHINDIRECT: U_WMRCREATEBRUSHINDIRECT_print(contents); break; + case U_WMR_CREATEBITMAPINDIRECT: U_WMRCREATEBITMAPINDIRECT_print(contents); break; + case U_WMR_CREATEBITMAP: U_WMRCREATEBITMAP_print(contents); break; + case U_WMR_CREATEREGION: U_WMRCREATEREGION_print(contents); break; + default: U_WMRNOTIMPLEMENTED_print(contents); break; + } //end of switch + return(size); +} + + +#ifdef __cplusplus +} +#endif diff --git a/src/libuemf/uwmf_print.h b/src/libuemf/uwmf_print.h new file mode 100644 index 000000000..31a8df5dc --- /dev/null +++ b/src/libuemf/uwmf_print.h @@ -0,0 +1,48 @@ +/** + @file uwmf_print.h Functions for printing records from WMF files. +*/ + +/* +File: uwmf_print.h +Version: 0.0.2 +Date: 14-FEB-2013 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2012 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifndef _UWMF_PRINT_ +#define _UWMF_PRINT_ + +#ifdef __cplusplus +extern "C" { +#endif +#include "uwmf.h" +#include "uemf_print.h" + +/* prototypes for objects used in WMR records (other than those defined in uemf_print.h) */ +void brush_print(U_BRUSH b); +void font_print(const char *font); +void pltntry_print(U_PLTNTRY pny); +void palette_print(const PU_PALETTE p, const char *PalEntries); +void pen_print(U_PEN p); +void rect16_ltrb_print(U_RECT16 rect); +void rect16_brtl_print(U_RECT16 rect); +void region_print(const char *region); +void bitmap16_print(U_BITMAP16 b); +void bitmapcoreheader_print(U_BITMAPCOREHEADER ch); +void logbrushw_print(U_WLOGBRUSH lb); +void polypolygon_print(uint16_t nPolys, const uint16_t *aPolyCounts, const char *Points); +void scan_print(U_SCAN sc); +void dibheader_print(const void *dh); + +/* prototypes for WMF records */ +int wmfheader_print(const char *contents, const char *blimit); +void U_WMRNOTIMPLEMENTED_print(const char *contents); +int U_wmf_onerec_print(const char *contents, const char *blimit, int recnum, size_t off); + +#ifdef __cplusplus +} +#endif + +#endif /* _UWMF_PRINT_ */ diff --git a/src/menus-skeleton.h b/src/menus-skeleton.h index d01e3e38c..5b141902b 100644 --- a/src/menus-skeleton.h +++ b/src/menus-skeleton.h @@ -16,7 +16,7 @@ static char const menus_skeleton[] = " <submenu name=\"" N_("_File") "\">\n" " <submenu name=\"" N_("_New") "\">\n" " <verb verb-id=\"FileNew\" />\n" -" <template-list/>\n" +" <verb verb-id=\"FileTemplates\" />\n" " </submenu>\n" " <verb verb-id=\"FileOpen\" />\n" " <recent-file-list/>\n" diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h index c5d972966..17b912d33 100644 --- a/src/preferences-skeleton.h +++ b/src/preferences-skeleton.h @@ -326,7 +326,7 @@ static char const preferences_skeleton[] = " minimumexponent=\"-8\" " " inlineattrs=\"0\" " " indent=\"2\" " -" allowrelativecoordinates=\"1\" " +" pathstring_format=\"2\" " " forcerepeatcommands=\"0\" " " incorrect_attributes_warn=\"1\" " " incorrect_attributes_remove=\"0\" " diff --git a/src/preferences-test.h b/src/preferences-test.h index 8e8ddb65b..92cb14247 100644 --- a/src/preferences-test.h +++ b/src/preferences-test.h @@ -18,7 +18,7 @@ public: TestObserver(Glib::ustring const &path) : Inkscape::Preferences::Observer(path), value(0) {} - + virtual void notify(Inkscape::Preferences::Entry const &val) { value = val.getInt(); @@ -35,29 +35,29 @@ public: prefs = NULL; Inkscape::Preferences::unload(); } - + void testStartingState() { - TS_ASSERT(prefs != NULL); - TS_ASSERT_EQUALS(prefs->isWritable(), false); + TS_ASSERT_DIFFERS(prefs, static_cast<void*>(0)); + TS_ASSERT_EQUALS(prefs->isWritable(), true); } - + void testOverwrite() { prefs->setInt("/test/intvalue", 123); prefs->setInt("/test/intvalue", 321); TS_ASSERT_EQUALS(prefs->getInt("/test/intvalue"), 321); } - + void testDefaultReturn() { TS_ASSERT_EQUALS(prefs->getInt("/this/path/does/not/exist", 123), 123); } - + void testLimitedReturn() { prefs->setInt("/test/intvalue", 1000); - + // simple case TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 0, 500), 123); // the below may seem quirky but this behaviour is intended @@ -66,7 +66,7 @@ public: TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 0, 1000), 1000); TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 1000, 5000), 1000); } - + void testKeyObserverNotification() { Glib::ustring const path = "/some/random/path"; @@ -74,18 +74,18 @@ public: obs.value = 1; prefs->setInt(path, 5); TS_ASSERT_EQUALS(obs.value, 1); // no notifications sent before adding - + prefs->addObserver(obs); prefs->setInt(path, 10); TS_ASSERT_EQUALS(obs.value, 10); prefs->setInt("/some/other/random/path", 10); TS_ASSERT_EQUALS(obs.value, 10); // value should not change - + prefs->removeObserver(obs); prefs->setInt(path, 15); TS_ASSERT_EQUALS(obs.value, 10); // no notifications sent after removal } - + void testEntryObserverNotification() { Glib::ustring const path = "/some/random/path"; @@ -93,11 +93,11 @@ public: obs.value = 1; prefs->setInt(path, 5); TS_ASSERT_EQUALS(obs.value, 1); // no notifications sent before adding - + prefs->addObserver(obs); prefs->setInt(path, 10); TS_ASSERT_EQUALS(obs.value, 10); - + // test that filtering works properly prefs->setInt("/some/random/value", 1234); TS_ASSERT_EQUALS(obs.value, 10); @@ -105,12 +105,12 @@ public: TS_ASSERT_EQUALS(obs.value, 10); prefs->setInt("/some/random/path2", 1234); TS_ASSERT_EQUALS(obs.value, 10); - + prefs->removeObserver(obs); prefs->setInt(path, 15); TS_ASSERT_EQUALS(obs.value, 10); // no notifications sent after removal } - + void testPreferencesEntryMethods() { prefs->setInt("/test/prefentry", 100); diff --git a/src/sp-image.cpp b/src/sp-image.cpp index d60fbc181..10d294d5c 100644 --- a/src/sp-image.cpp +++ b/src/sp-image.cpp @@ -1056,6 +1056,7 @@ static void sp_image_print( SPItem *item, SPPrintContext *ctx ) t = ti * t; sp_print_image_R8G8B8A8_N(ctx, px + trimx*pixskip + trimy*rs, trimwidth, trimheight, rs, t, item->style); } + free(px); // else big memory leak on each image print! } } diff --git a/src/splivarot.cpp b/src/splivarot.cpp index d6fef3a17..8a57fa98a 100644 --- a/src/splivarot.cpp +++ b/src/splivarot.cpp @@ -54,6 +54,7 @@ #include "splivarot.h" #include "verbs.h" +#include "2geom/svg-path-parser.h" // to get from SVG on boolean to Geom::Path using Inkscape::DocumentUndo; @@ -121,7 +122,205 @@ boolop_display_error_message(SPDesktop *desktop, Glib::ustring const &msg) } } -// boolean operations +// boolean operations PathVectors A,B -> PathVector result. +// This is derived from sp_selected_path_boolop +// take the source paths from the file, do the operation, delete the originals and add the results +// fra,fra are fill_rules for PathVectors a,b +Geom::PathVector +sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, bool_op bop, fill_typ fra, fill_typ frb) +{ + + // extract the livarot Paths from the source objects + // also get the winding rule specified in the style + int nbOriginaux = 2; + std::vector<Path *> originaux(nbOriginaux); + std::vector<FillRule> origWind(nbOriginaux); + origWind[0]=fra; + origWind[1]=frb; + Geom::PathVector patht; + // Livarot's outline of arcs is broken. So convert the path to linear and cubics only, for which the outline is created correctly. + originaux[0] = Path_for_pathvector(pathv_to_linear_and_cubic_beziers( pathva)); + originaux[1] = Path_for_pathvector(pathv_to_linear_and_cubic_beziers( pathvb)); + + // some temporary instances, first + Shape *theShapeA = new Shape; + Shape *theShapeB = new Shape; + Shape *theShape = new Shape; + Path *res = new Path; + res->SetBackData(false); + Path::cut_position *toCut=NULL; + int nbToCut=0; + + if ( bop == bool_op_inters || bop == bool_op_union || bop == bool_op_diff || bop == bool_op_symdiff ) { + // true boolean op + // get the polygons of each path, with the winding rule specified, and apply the operation iteratively + originaux[0]->ConvertWithBackData(0.1); + + originaux[0]->Fill(theShape, 0); + + theShapeA->ConvertToShape(theShape, origWind[0]); + + originaux[1]->ConvertWithBackData(0.1); + + originaux[1]->Fill(theShape, 1); + + theShapeB->ConvertToShape(theShape, origWind[1]); + + theShape->Booleen(theShapeB, theShapeA, bop); + + } else if ( bop == bool_op_cut ) { + // cuts= sort of a bastard boolean operation, thus not the axact same modus operandi + // technically, the cut path is not necessarily a polygon (thus has no winding rule) + // it is just uncrossed, and cleaned from duplicate edges and points + // then it's fed to Booleen() which will uncross it against the other path + // then comes the trick: each edge of the cut path is duplicated (one in each direction), + // thus making a polygon. the weight of the edges of the cut are all 0, but + // the Booleen need to invert the ones inside the source polygon (for the subsequent + // ConvertToForme) + + // the cut path needs to have the highest pathID in the back data + // that's how the Booleen() function knows it's an edge of the cut + + // FIXME: this gives poor results, the final paths are full of extraneous nodes. Decreasing + // ConvertWithBackData parameter below simply increases the number of nodes, so for now I + // left it at 1.0. Investigate replacing this by a combination of difference and + // intersection of the same two paths. -- bb + { + Path* swap=originaux[0];originaux[0]=originaux[1];originaux[1]=swap; + int swai=origWind[0];origWind[0]=origWind[1];origWind[1]=(fill_typ)swai; + } + originaux[0]->ConvertWithBackData(1.0); + + originaux[0]->Fill(theShape, 0); + + theShapeA->ConvertToShape(theShape, origWind[0]); + + originaux[1]->ConvertWithBackData(1.0); + + originaux[1]->Fill(theShape, 1,false,false,false); //do not closeIfNeeded + + theShapeB->ConvertToShape(theShape, fill_justDont); // fill_justDont doesn't computes winding numbers + + // les elements arrivent en ordre inverse dans la liste + theShape->Booleen(theShapeB, theShapeA, bool_op_cut, 1); + + } else if ( bop == bool_op_slice ) { + // slice is not really a boolean operation + // you just put the 2 shapes in a single polygon, uncross it + // the points where the degree is > 2 are intersections + // just check it's an intersection on the path you want to cut, and keep it + // the intersections you have found are then fed to ConvertPositionsToMoveTo() which will + // make new subpath at each one of these positions + // inversion pour l'opration + { + Path* swap=originaux[0];originaux[0]=originaux[1];originaux[1]=swap; + int swai=origWind[0];origWind[0]=origWind[1];origWind[1]=(fill_typ)swai; + } + originaux[0]->ConvertWithBackData(1.0); + + originaux[0]->Fill(theShapeA, 0,false,false,false); // don't closeIfNeeded + + originaux[1]->ConvertWithBackData(1.0); + + originaux[1]->Fill(theShapeA, 1,true,false,false);// don't closeIfNeeded and just dump in the shape, don't reset it + + theShape->ConvertToShape(theShapeA, fill_justDont); + + if ( theShape->hasBackData() ) { + // should always be the case, but ya never know + { + for (int i = 0; i < theShape->numberOfPoints(); i++) { + if ( theShape->getPoint(i).totalDegree() > 2 ) { + // possibly an intersection + // we need to check that at least one edge from the source path is incident to it + // before we declare it's an intersection + int cb = theShape->getPoint(i).incidentEdge[FIRST]; + int nbOrig=0; + int nbOther=0; + int piece=-1; + float t=0.0; + while ( cb >= 0 && cb < theShape->numberOfEdges() ) { + if ( theShape->ebData[cb].pathID == 0 ) { + // the source has an edge incident to the point, get its position on the path + piece=theShape->ebData[cb].pieceID; + if ( theShape->getEdge(cb).st == i ) { + t=theShape->ebData[cb].tSt; + } else { + t=theShape->ebData[cb].tEn; + } + nbOrig++; + } + if ( theShape->ebData[cb].pathID == 1 ) nbOther++; // the cut is incident to this point + cb=theShape->NextAt(i, cb); + } + if ( nbOrig > 0 && nbOther > 0 ) { + // point incident to both path and cut: an intersection + // note that you only keep one position on the source; you could have degenerate + // cases where the source crosses itself at this point, and you wouyld miss an intersection + toCut=(Path::cut_position*)realloc(toCut, (nbToCut+1)*sizeof(Path::cut_position)); + toCut[nbToCut].piece=piece; + toCut[nbToCut].t=t; + nbToCut++; + } + } + } + } + { + // i think it's useless now + int i = theShape->numberOfEdges() - 1; + for (;i>=0;i--) { + if ( theShape->ebData[i].pathID == 1 ) { + theShape->SubEdge(i); + } + } + } + + } + } + + int* nesting=NULL; + int* conts=NULL; + int nbNest=0; + // pour compenser le swap juste avant + if ( bop == bool_op_slice ) { +// theShape->ConvertToForme(res, nbOriginaux, originaux, true); +// res->ConvertForcedToMoveTo(); + res->Copy(originaux[0]); + res->ConvertPositionsToMoveTo(nbToCut, toCut); // cut where you found intersections + free(toCut); + } else if ( bop == bool_op_cut ) { + // il faut appeler pour desallouer PointData (pas vital, mais bon) + // the Booleen() function did not deallocated the point_data array in theShape, because this + // function needs it. + // this function uses the point_data to get the winding number of each path (ie: is a hole or not) + // for later reconstruction in objects, you also need to extract which path is parent of holes (nesting info) + theShape->ConvertToFormeNested(res, nbOriginaux, &originaux[0], 1, nbNest, nesting, conts); + } else { + theShape->ConvertToForme(res, nbOriginaux, &originaux[0]); + } + + delete theShape; + delete theShapeA; + delete theShapeB; + delete originaux[0]; + delete originaux[1]; + + std::vector<Geom::Path> outres = Geom::parse_svg_path(res->svg_dump_path()); + + + delete res; + return outres; +} + + +/* Convert from a livarot path to a 2geom PathVector */ +Geom::PathVector pathliv_to_pathvector(Path *pathliv){ + std::vector<Geom::Path> outres = Geom::parse_svg_path(pathliv->svg_dump_path()); + return outres; +} + + +// boolean operations on the desktop // take the source paths from the file, do the operation, delete the originals and add the results void sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool_op bop, const unsigned int verb, const Glib::ustring description) @@ -793,7 +992,7 @@ Geom::PathVector* item_outline(SPItem const *item, bool bbox_only) } } - // Livarots outline of arcs is broken. So convert the path to linear and cubics only, for which the outline is created correctly. + // Livarot's outline of arcs is broken. So convert the path to linear and cubics only, for which the outline is created correctly. Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() ); Path *orig = new Path; @@ -1059,7 +1258,7 @@ sp_selected_path_outline(SPDesktop *desktop) curve->unref(); continue; } - // Livarots outline of arcs is broken. So convert the path to linear and cubics only, for which the outline is created correctly. + // Livarot's outline of arcs is broken. So convert the path to linear and cubics only, for which the outline is created correctly. Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curvetemp->get_pathvector() ); curvetemp->unref(); @@ -2082,6 +2281,21 @@ Ancetre(Inkscape::XML::Node *a, Inkscape::XML::Node *who) return Ancetre(a->parent(), who); } +// derived from Path_for_item +// there must be some other way to load dest directly from epathv, without going through pathv... +Path * +Path_for_pathvector(Geom::PathVector const &epathv) +{ + Geom::PathVector *pathv = new Geom::PathVector; + std::copy(epathv.begin(), epathv.end(), std::back_inserter(*pathv)); + + Path *dest = new Path; + dest->LoadPathVector(*pathv); + delete pathv; + + return dest; +} + Path * Path_for_item(SPItem *item, bool doTransformation, bool transformFull) { diff --git a/src/splivarot.h b/src/splivarot.h index 0cefed3c2..329fc65d2 100644 --- a/src/splivarot.h +++ b/src/splivarot.h @@ -57,6 +57,7 @@ Geom::PathVector* item_outline(SPItem const *item, bool bbox_only = false); // simplifies a path (removes small segments and the like) void sp_selected_path_simplify (SPDesktop *desktop); +Path *Path_for_pathvector(Geom::PathVector const &pathv); Path *Path_for_item(SPItem *item, bool doTransformation, bool transformFull = true); Path *Path_for_item_before_LPE(SPItem *item, bool doTransformation, bool transformFull = true); Geom::PathVector* pathvector_for_curve(SPItem *item, SPCurve *curve, bool doTransformation, bool transformFull, Geom::Affine extraPreAffine, Geom::Affine extraPostAffine); @@ -64,6 +65,7 @@ SPCurve *curve_for_item(SPItem *item); SPCurve *curve_for_item_before_LPE(SPItem *item); boost::optional<Path::cut_position> get_nearest_position_on_Path(Path *path, Geom::Point p, unsigned seg = 0); Geom::Point get_point_on_Path(Path *path, int piece, double t); +Geom::PathVector sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pathvb, bool_op bop, FillRule fra, FillRule frb); #endif diff --git a/src/style.cpp b/src/style.cpp index 8c0000f3a..11cc5b9d3 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -89,7 +89,12 @@ static void sp_style_read_ienum(SPIEnum *val, gchar const *str, SPStyleEnum cons static void sp_style_read_istring(SPIString *val, gchar const *str); static void sp_style_read_ilength(SPILength *val, gchar const *str); static void sp_style_read_ilengthornormal(SPILengthOrNormal *val, gchar const *str); -static void sp_style_read_itextdecoration(SPITextDecoration *val, gchar const *str); + +static void sp_style_read_itextdecoration(SPITextDecorationLine *line, SPITextDecorationStyle *style, SPIPaint *color, gchar const *str); +static void sp_style_read_itextdecorationLine(SPITextDecorationLine *line, gchar const *str); +static void sp_style_read_itextdecorationStyle(SPITextDecorationStyle *style, gchar const *str); +static void sp_style_read_itextdecorationColor(SPIPaint *color, gchar const *str); + static void sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document); static void sp_style_read_ifontsize(SPIFontSize *val, gchar const *str); static void sp_style_read_ibaselineshift(SPIBaselineShift *val, gchar const *str); @@ -110,7 +115,14 @@ static gint sp_style_write_ipaint(gchar *b, gint len, gchar const *key, SPIPaint static gint sp_style_write_ifontsize(gchar *p, gint len, gchar const *key, SPIFontSize const *val, SPIFontSize const *base, guint flags); static gint sp_style_write_ibaselineshift(gchar *p, gint len, gchar const *key, SPIBaselineShift const *val, SPIBaselineShift const *base, guint flags); static gint sp_style_write_ilengthornormal(gchar *p, gint const len, gchar const *const key, SPILengthOrNormal const *const val, SPILengthOrNormal const *const base, guint const flags); -static gint sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key, SPITextDecoration const *const val, SPITextDecoration const *const base, guint const flags); +static gint sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key, + SPITextDecorationLine const *const line, + SPITextDecorationStyle const *const style, + SPIPaint const *const color, + SPITextDecorationLine const *const baseLine, + SPITextDecorationStyle const *const baseStyle, + SPIPaint const *const baseColor, + guint const flags); static gint sp_style_write_ifilter(gchar *b, gint len, gchar const *key, SPIFilter const *filter, SPIFilter const *base, guint flags); static void sp_style_filter_clear(SPStyle *style); @@ -605,7 +617,7 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) /* 1. Style attribute */ gchar const *val = repr->attribute("style"); - if (val != NULL) { + if (val != NULL && *val) { sp_style_merge_from_style_string(style, val); } @@ -632,10 +644,10 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) /* Text (css2 chapter 16) */ SPS_READ_PLENGTH_IF_UNSET(&style->text_indent, repr, "text-indent"); SPS_READ_PENUM_IF_UNSET(&style->text_align, repr, "text-align", enum_text_align, true); - if (!style->text_decoration.set) { - val = repr->attribute("text-decoration"); - if (val) { - sp_style_read_itextdecoration(&style->text_decoration, val); + if (!style->text_decoration_line.set) { + // assume it uses either text-decoration or text-decoration-line, but not both + if ((val = repr->attribute("text-decoration")) || (val = repr->attribute("text-decoration-line"))) { + sp_style_read_itextdecoration(&style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color, val); } } if (!style->line_height.set) { @@ -1090,8 +1102,23 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) SPS_READ_IENUM_IF_UNSET(&style->text_align, val, enum_text_align, true); break; case SP_PROP_TEXT_DECORATION: - if (!style->text_decoration.set) { - sp_style_read_itextdecoration(&style->text_decoration, val); + if (!style->text_decoration_line.set) { + sp_style_read_itextdecoration(&style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color, val); + } + break; + case SP_PROP_TEXT_DECORATION_LINE: + if (!style->text_decoration_line.set) { + sp_style_read_itextdecorationLine(&style->text_decoration_line, val); + } + break; + case SP_PROP_TEXT_DECORATION_STYLE: + if (!style->text_decoration_style.set) { + sp_style_read_itextdecorationStyle(&style->text_decoration_style, val); + } + break; + case SP_PROP_TEXT_DECORATION_COLOR: + if (!style->text_decoration_color.set) { + sp_style_read_itextdecorationColor(&style->text_decoration_color, val); } break; case SP_PROP_LINE_HEIGHT: @@ -1679,11 +1706,23 @@ sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent) style->text_align.computed = parent->text_align.computed; } - if (!style->text_decoration.set || style->text_decoration.inherit) { - style->text_decoration.underline = parent->text_decoration.underline; - style->text_decoration.overline = parent->text_decoration.overline; - style->text_decoration.line_through = parent->text_decoration.line_through; - style->text_decoration.blink = parent->text_decoration.blink; + if (!style->text_decoration_line.set || style->text_decoration_line.inherit) { + style->text_decoration_line.underline = parent->text_decoration_line.underline; + style->text_decoration_line.overline = parent->text_decoration_line.overline; + style->text_decoration_line.line_through = parent->text_decoration_line.line_through; + style->text_decoration_line.blink = parent->text_decoration_line.blink; + } + + if (!style->text_decoration_style.set || style->text_decoration_style.inherit) { + style->text_decoration_style.solid = parent->text_decoration_style.solid; + style->text_decoration_style.isdouble = parent->text_decoration_style.isdouble; + style->text_decoration_style.dotted = parent->text_decoration_style.dotted; + style->text_decoration_style.dashed = parent->text_decoration_style.dashed; + style->text_decoration_style.wavy = parent->text_decoration_style.wavy; + } + + if (!style->text_decoration_color.set || style->text_decoration_color.inherit) { + sp_style_merge_ipaint(style, &style->text_decoration_color, &parent->text_decoration_color); } if (!style->line_height.set || style->line_height.inherit) { @@ -2169,8 +2208,9 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare * property (fixme). This code may need changing once we do the * special fill/stroke inheritance mentioned by the spec. */ - sp_style_merge_prop_from_dying_parent<SPITextDecoration>(style->text_decoration, - parent->text_decoration); + sp_style_merge_prop_from_dying_parent<SPITextDecorationLine>( style->text_decoration_line, parent->text_decoration_line); + sp_style_merge_prop_from_dying_parent<SPITextDecorationStyle>(style->text_decoration_style, parent->text_decoration_style); + sp_style_merge_paint_prop_from_dying_parent(style,style->text_decoration_color, parent->text_decoration_color); //nyi: font-size-adjust, // <number> | none | inherit //nyi: glyph-orientation-horizontal, @@ -2623,7 +2663,9 @@ sp_style_write_string(SPStyle const *const style, guint const flags) /* Text */ p += sp_style_write_ilength(p, c + BMAX - p, "text-indent", &style->text_indent, NULL, flags); p += sp_style_write_ienum(p, c + BMAX - p, "text-align", enum_text_align, &style->text_align, NULL, flags); - p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration", &style->text_decoration, NULL, flags); + p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration", + &style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color, + NULL, NULL, NULL, flags); p += sp_style_write_ilengthornormal(p, c + BMAX - p, "line-height", &style->line_height, NULL, flags); p += sp_style_write_ilengthornormal(p, c + BMAX - p, "letter-spacing", &style->letter_spacing, NULL, flags); p += sp_style_write_ilengthornormal(p, c + BMAX - p, "word-spacing", &style->word_spacing, NULL, flags); @@ -2801,7 +2843,10 @@ sp_style_write_difference(SPStyle const *const from, SPStyle const *const to) /* Text */ p += sp_style_write_ilength(p, c + BMAX - p, "text-indent", &from->text_indent, &to->text_indent, SP_STYLE_FLAG_IFDIFF); p += sp_style_write_ienum(p, c + BMAX - p, "text-align", enum_text_align, &from->text_align, &to->text_align, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration", &from->text_decoration, &to->text_decoration, SP_STYLE_FLAG_IFDIFF); + p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration", + &from->text_decoration_line, &from->text_decoration_style, &from->text_decoration_color, + &to->text_decoration_line, &to->text_decoration_style, &to->text_decoration_color, + SP_STYLE_FLAG_IFDIFF); p += sp_style_write_ilengthornormal(p, c + BMAX - p, "line-height", &from->line_height, &to->line_height, SP_STYLE_FLAG_IFDIFF); p += sp_style_write_ilengthornormal(p, c + BMAX - p, "letter-spacing", &from->letter_spacing, &to->letter_spacing, SP_STYLE_FLAG_IFDIFF); p += sp_style_write_ilengthornormal(p, c + BMAX - p, "word-spacing", &from->word_spacing, &to->word_spacing, SP_STYLE_FLAG_IFDIFF); @@ -3031,12 +3076,22 @@ sp_style_clear(SPStyle *style) style->text_align.inherit = FALSE; style->text_align.value = style->text_align.computed = SP_CSS_TEXT_ALIGN_START; - style->text_decoration.set = FALSE; - style->text_decoration.inherit = FALSE; - style->text_decoration.underline = FALSE; - style->text_decoration.overline = FALSE; - style->text_decoration.line_through = FALSE; - style->text_decoration.blink = FALSE; + style->text_decoration_line.set = FALSE; + style->text_decoration_line.inherit = FALSE; + style->text_decoration_line.underline = FALSE; + style->text_decoration_line.overline = FALSE; + style->text_decoration_line.line_through = FALSE; + style->text_decoration_line.blink = FALSE; + + style->text_decoration_style.set = FALSE; + style->text_decoration_style.inherit = FALSE; + style->text_decoration_style.solid = FALSE; + style->text_decoration_style.isdouble = FALSE; + style->text_decoration_style.dotted = FALSE; + style->text_decoration_style.dashed = FALSE; + style->text_decoration_style.wavy = FALSE; + + style->text_decoration_color.clear(); style->line_height.set = FALSE; style->line_height.inherit = FALSE; @@ -3535,54 +3590,157 @@ sp_style_read_ilengthornormal(SPILengthOrNormal *val, gchar const *str) * Set SPITextDecoration object from string. */ static void -sp_style_read_itextdecoration(SPITextDecoration *val, gchar const *str) -{ +sp_style_read_itextdecoration(SPITextDecorationLine *line, SPITextDecorationStyle *style, SPIPaint *color, gchar const *str){ + sp_style_read_itextdecorationLine(line, str); // scans all tokens for line types + sp_style_read_itextdecorationStyle(style, str); // scans all tokens for style types + // the color routine must be fed one token at a time - if multiple colors are found the LAST one is used + const gchar *hstr = str; + while (1) { + if (*str == ' ' || *str == ',' || *str == '\0'){ + int slen = str - hstr; + gchar *frag = g_strndup(hstr,slen+1); // only send one piece at a time, since keywords may be intermixed + sp_style_read_itextdecorationColor(color, frag); + free(frag); + if(color->set)break; + if(*str == '\0')break; + hstr = str + 1; + } + str++; + } +} + +/** + * Set SPITextDecorationLine object from string. + * returns true if there was a match, false otherwise + */ +static void +sp_style_read_itextdecorationLine(SPITextDecorationLine *line, gchar const *str){ if (!strcmp(str, "inherit")) { - val->set = TRUE; - val->inherit = TRUE; + line->set = true; + line->inherit = true; } else if (!strcmp(str, "none")) { - val->set = TRUE; - val->inherit = FALSE; - val->underline = FALSE; - val->overline = FALSE; - val->line_through = FALSE; - val->blink = FALSE; + line->set = true; + line->inherit = false; + line->underline = false; + line->overline = false; + line->line_through = false; + line->blink = false; } else { - bool found_underline = false; - bool found_overline = false; + bool found_one = false; + bool hit_one = false; + + // CSS 2 keywords + bool found_underline = false; + bool found_overline = false; bool found_line_through = false; - bool found_blink = false; - for ( ; *str ; str++ ) { - if (*str == ' ') continue; - if (strneq(str, "underline", 9) && (str[9] == ' ' || str[9] == '\0')) { - found_underline = true; - str += 9; - } else if (strneq(str, "overline", 8) && (str[8] == ' ' || str[8] == '\0')) { - found_overline = true; - str += 8; - } else if (strneq(str, "line-through", 12) && (str[12] == ' ' || str[12] == '\0')) { - found_line_through = true; - str += 12; - } else if (strneq(str, "blink", 5) && (str[5] == ' ' || str[5] == '\0')) { - found_blink = true; - str += 5; - } else { - return; // invalid value + bool found_blink = false; + + // this method ignores inlineid keys and extra delimiters, so " ,,, blink hello" will set blink and ignore hello + const gchar *hstr = str; + while (1) { + if (*str == ' ' || *str == ',' || *str == '\0'){ + int slen = str - hstr; + // CSS 2 keywords + while(1){ // not really a loop, used to avoid a goto + hit_one = true; // most likely we will + if ((slen == 9) && strneq(hstr, "underline", slen)){ found_underline = true; break; } + if ((slen == 8) && strneq(hstr, "overline", slen)){ found_overline = true; break; } + if ((slen == 12) && strneq(hstr, "line-through", slen)){ found_line_through = true; break; } + if ((slen == 5) && strneq(hstr, "blink", slen)){ found_blink = true; break; } + if ((slen == 4) && strneq(hstr, "none", slen)){ break; } + + hit_one = false; // whatever this thing is, we do not recognize it + break; + } + found_one |= hit_one; + if(*str == '\0')break; + hstr = str + 1; } + str++; } - if (!(found_underline || found_overline || found_line_through || found_blink)) { - return; // invalid value: empty + if (found_one) { + line->set = true; + line->inherit = false; + line->underline = found_underline; + line->overline = found_overline; + line->line_through = found_line_through; + line->blink = found_blink; + } + else { + line->set = false; + line->inherit = false; } - val->set = TRUE; - val->inherit = FALSE; - val->underline = found_underline; - val->overline = found_overline; - val->line_through = found_line_through; - val->blink = found_blink; } } /** + * Set SPITextDecorationStyle object from string. + * returns true if there was a match, false otherwise +*/ +static void +sp_style_read_itextdecorationStyle(SPITextDecorationStyle *style, gchar const *str){ + if (!strcmp(str, "inherit")) { + style->set = true; + style->inherit = true; + } else if (!strcmp(str, "none")) { + style->set = true; + style->inherit = false; + style->solid = false; + style->isdouble = false; + style->dotted = false; + style->dashed = false; + style->wavy = false; + } else { + // note, these are CSS 3 keywords + bool found_solid = false; + bool found_double = false; + bool found_dotted = false; + bool found_dashed = false; + bool found_wavy = false; + bool found_one = false; + + // this method ignores inlineid keys and extra delimiters, so " ,,, style hello" will set style and ignore hello + // if more than one style is present, the first is used + const gchar *hstr = str; + while (1) { + if (*str == ' ' || *str == ',' || *str == '\0'){ + int slen = str - hstr; + if ( (slen == 5) && strneq(hstr, "solid", slen)){ found_solid = true; found_one = true; break; } + else if ((slen == 6) && strneq(hstr, "double", slen)){ found_double = true; found_one = true; break; } + else if ((slen == 6) && strneq(hstr, "dotted", slen)){ found_dotted = true; found_one = true; break; } + else if ((slen == 6) && strneq(hstr, "dashed", slen)){ found_dashed = true; found_one = true; break; } + else if ((slen == 4) && strneq(hstr, "wavy", slen)){ found_wavy = true; found_one = true; break; } + if(*str == '\0')break; // nothing more to test + hstr = str + 1; + } + str++; + } + if(found_one){ + style->set = true; + style->inherit = false; + style->solid = found_solid; + style->isdouble = found_double; + style->dotted = found_dotted; + style->dashed = found_dashed; + style->wavy = found_wavy; + } + else { + style->set = false; + style->inherit = false; + } + } +} + +/** + * Set SPIPaint object from string. + */ +static void +sp_style_read_itextdecorationColor(SPIPaint *color, gchar const *str){ + sp_style_read_icolor(color, str, NULL,NULL); +} + + +/** * Set SPIPaint object from string containing an integer value. * \param document Ignored */ @@ -3596,11 +3754,11 @@ sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume paint->set = TRUE; paint->inherit = TRUE; } else { + paint->inherit = FALSE; guint32 const rgb0 = sp_svg_read_color(str, 0xff); if (rgb0 != 0xff) { paint->setColor(rgb0); paint->set = TRUE; - paint->inherit = FALSE; } } } @@ -4138,12 +4296,39 @@ sp_style_write_ilengthornormal(gchar *p, gint const len, gchar const *const key, * */ static bool -sp_textdecoration_differ(SPITextDecoration const *const a, SPITextDecoration const *const b) +sp_textdecorationLine_differ(SPITextDecorationLine const *const a, SPITextDecorationLine const *const b) { - return a->underline != b->underline - || a->overline != b->overline - || a->line_through != b->line_through - || a->blink != b->blink; + return( (a->underline != b->underline ) + || (a->overline != b->overline ) + || (a->line_through != b->line_through) + || (a->blink != b->blink ) + ); +} + +/** + * + */ +static bool +sp_textdecorationStyle_differ(SPITextDecorationStyle const *const a, SPITextDecorationStyle const *const b) +{ + return( (a->solid != b->solid ) + || (a->isdouble != b->isdouble ) + || (a->dotted != b->dotted ) + || (a->dashed != b->dashed ) + || (a->wavy != b->wavy ) + ); +} + +/** + * + */ +static bool +sp_textdecorationColor_differ(SPIPaint const *const a, SPIPaint const *const b) +{ + bool status = (a->isPaintserver() == b->isPaintserver()) && + (a->colorSet == b->colorSet) && + (a->currentcolor == b->currentcolor); + return(status); } /** @@ -4151,31 +4336,55 @@ sp_textdecoration_differ(SPITextDecoration const *const a, SPITextDecoration con */ static gint sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key, - SPITextDecoration const *const val, - SPITextDecoration const *const base, + SPITextDecorationLine const *const line, + SPITextDecorationStyle const *const style, + SPIPaint const *const color, + SPITextDecorationLine const *const baseLine, + SPITextDecorationStyle const *const baseStyle, + SPIPaint const *const baseColor, guint const flags) { Inkscape::CSSOStringStream os; - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set - && (!base->set || sp_textdecoration_differ(val, base)))) - { - if (val->inherit) { - return g_snprintf(p, len, "%s:inherit;", key); - } else { - os << key << ":"; - if (val->underline || val->overline || val->line_through || val->blink) { - if (val->underline) os << " underline"; - if (val->overline) os << " overline"; - if (val->line_through) os << " line-through"; - if (val->blink) os << " blink"; - } else - os << "none"; - os << ";"; - return g_strlcpy(p, os.str().c_str(), len); + if ( (flags & SP_STYLE_FLAG_ALWAYS) + || ((flags & SP_STYLE_FLAG_IFSET) && line->set) + || ((flags & SP_STYLE_FLAG_IFDIFF) && line->set + && ( !baseLine->set || sp_textdecorationLine_differ(line, baseLine))) + || ((flags & SP_STYLE_FLAG_IFSET) && style->set) + || ((flags & SP_STYLE_FLAG_IFDIFF) && style->set + && ( !baseStyle->set || sp_textdecorationStyle_differ(style, baseStyle))) + || ((flags & SP_STYLE_FLAG_IFSET) && color->set) + || ((flags & SP_STYLE_FLAG_IFDIFF) && color->set + && ( !baseColor->set || sp_textdecorationColor_differ(color, baseColor))) + ){ + os << key << ":"; + if (line->inherit || style->inherit || color->inherit) { + os << " inherit"; + } + else if (line->underline || line->overline || line->line_through || line->blink) { + if (line->underline) os << " underline"; + if (line->overline) os << " overline"; + if (line->line_through) os << " line-through"; + if (line->blink) os << " blink"; + + if ( style->solid) os << " solid"; + else if (style->isdouble) os << " double"; + else if (style->dotted) os << " dotted"; + else if (style->dashed) os << " dashed"; + else if (style->wavy) os << " wavy"; + // color, if it is set, otherwise omit it + if(color->set){ + char color_buf[8]; + sp_svg_write_color(color_buf, sizeof(color_buf), color->value.color.toRGBA32( 0 )); + os << " "; + os << color_buf; + } + } + else { + os << "none"; } + os << ";"; + return g_strlcpy(p, os.str().c_str(), len); } return 0; } diff --git a/src/style.h b/src/style.h index 2b98c9e5d..bc4df96e2 100644 --- a/src/style.h +++ b/src/style.h @@ -164,13 +164,6 @@ struct SPIPaint { void setColor( SPColor const& color ) {value.color = color; colorSet = true;} void read( gchar const *str, SPStyle &tyle, SPDocument *document = 0); - - // Win32 is a temp work-around until the emf extension is fixed: -#ifndef WIN32 -private: - SPIPaint(SPIPaint const&); - SPIPaint &operator=(SPIPaint const &); -#endif // WIN32 }; /// Filter type internal to SPStyle @@ -224,8 +217,9 @@ struct SPIBaselineShift { float computed; }; +// CSS 2. Changes in CSS 3, where description is for TextDecorationLine, NOT TextDecoration /// Text decoration type internal to SPStyle. -struct SPITextDecoration { +struct SPITextDecorationLine { unsigned set : 1; unsigned inherit : 1; unsigned underline : 1; @@ -234,6 +228,18 @@ struct SPITextDecoration { unsigned blink : 1; // "Conforming user agents are not required to support this value." yay! }; +// CSS3 2.2 +/// Text decoration style type internal to SPStyle. +struct SPITextDecorationStyle { + unsigned set : 1; + unsigned inherit : 1; + unsigned solid : 1; + unsigned isdouble : 1; // cannot use "double" as it is a reserved keyword + unsigned dotted : 1; + unsigned dashed : 1; + unsigned wavy : 1; +}; + /// Extended length type internal to SPStyle. struct SPILengthOrNormal { unsigned set : 1; @@ -244,6 +250,21 @@ struct SPILengthOrNormal { float computed; }; +// These are used to implement text_decoration. The values are not saved to or read from SVG file +struct SPITextDecorationData { + float phase_length; // length along text line,used for phase for dot/dash/wavy + bool tspan_line_start; // is first span on a line + bool tspan_line_end; // is last span on a line + float tspan_width; // from libnrtype, when it calculates spans + float ascender; // the rest from tspan's font + float descender; + float line_gap; + float underline_thickness; + float underline_position; + float line_through_thickness; + float line_through_position; +}; + struct SPTextStyle; /// Stroke dash details. @@ -284,8 +305,18 @@ struct SPStyle { SPILength text_indent; /** text alignment (css2 16.2) (not to be confused with text-anchor) */ SPIEnum text_align; - /** text decoration (css2 16.3.1) */ - SPITextDecoration text_decoration; + /** text decoration (css2 16.3.1) is now handled as a subset of css3 2.4 */ + // SPITextDecoration text_decoration; + + /** CSS 3 2.1, 2.2, 2.3 */ + /** Not done yet, test_decoration3 = css3 2.4*/ + SPITextDecorationLine text_decoration_line; + SPIPaint text_decoration_color; + SPITextDecorationStyle text_decoration_style; + + // used to implement text_decoration, not saved to or read from SVG file + SPITextDecorationData text_decoration_data; + // 16.3.2 is text-shadow. That's complicated. /** Line spacing (css2 10.8.1) */ SPILengthOrNormal line_height; diff --git a/src/svg/path-string.cpp b/src/svg/path-string.cpp index 61e9c90a2..6dddeadff 100644 --- a/src/svg/path-string.cpp +++ b/src/svg/path-string.cpp @@ -2,6 +2,7 @@ * Inkscape::SVG::PathString - builder for SVG path strings * * Copyright 2008 Jasper van de Gronde <th.v.d.gronde@hccnet.nl> + * Copyright 2013 Tavmjong Bah <tavmjong@free.fr> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,44 +26,65 @@ static int const maxprec = 16; int Inkscape::SVG::PathString::numericprecision; int Inkscape::SVG::PathString::minimumexponent; +Inkscape::SVG::PATHSTRING_FORMAT Inkscape::SVG::PathString::format; Inkscape::SVG::PathString::PathString() : - allow_relative_coordinates(Inkscape::Preferences::get()->getBool("/options/svgoutput/allowrelativecoordinates", true)), force_repeat_commands(Inkscape::Preferences::get()->getBool("/options/svgoutput/forcerepeatcommands")) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + format = (PATHSTRING_FORMAT)prefs->getIntLimited("/options/svgoutput/pathstring_format", 1, 0, PATHSTRING_FORMAT_SIZE - 1 ); numericprecision = std::max<int>(minprec,std::min<int>(maxprec, prefs->getInt("/options/svgoutput/numericprecision", 8))); minimumexponent = prefs->getInt("/options/svgoutput/minimumexponent", -8); } +// For absolute and relative paths... the entire path is kept in the "tail". +// For optimized path, at a switch between absolute and relative, add tail to commonbase. void Inkscape::SVG::PathString::_appendOp(char abs_op, char rel_op) { bool abs_op_repeated = _abs_state.prevop == abs_op && !force_repeat_commands; bool rel_op_repeated = _rel_state.prevop == rel_op && !force_repeat_commands; - unsigned int const abs_added_size = abs_op_repeated ? 0 : 2; - unsigned int const rel_added_size = rel_op_repeated ? 0 : 2; - if ( _rel_state.str.size()+2 < _abs_state.str.size()+abs_added_size && allow_relative_coordinates ) { - // Store common prefix - commonbase += _rel_state.str; - _rel_state.str.clear(); - // Copy rel to abs - _abs_state = _rel_state; - _abs_state.switches++; - abs_op_repeated = false; - // We do not have to copy abs to rel: - // _rel_state.str.size()+2 < _abs_state.str.size()+abs_added_size - // _rel_state.str.size()+rel_added_size < _abs_state.str.size()+2 - // _abs_state.str.size()+2 > _rel_state.str.size()+rel_added_size - } else if ( _abs_state.str.size()+2 < _rel_state.str.size()+rel_added_size ) { - // Store common prefix - commonbase += _abs_state.str; - _abs_state.str.clear(); - // Copy abs to rel - _rel_state = _abs_state; - _abs_state.switches++; - rel_op_repeated = false; + + // For absolute and relative paths... do nothing. + switch (format) { + case PATHSTRING_ABSOLUTE: + if ( !abs_op_repeated ) _abs_state.appendOp(abs_op); + break; + case PATHSTRING_RELATIVE: + if ( !rel_op_repeated ) _rel_state.appendOp(rel_op); + break; + case PATHSTRING_OPTIMIZE: + { + unsigned int const abs_added_size = abs_op_repeated ? 0 : 2; + unsigned int const rel_added_size = rel_op_repeated ? 0 : 2; + if ( _rel_state.str.size()+2 < _abs_state.str.size()+abs_added_size ) { + + // Store common prefix + commonbase += _rel_state.str; + _rel_state.str.clear(); + // Copy rel to abs + _abs_state = _rel_state; + _abs_state.switches++; + abs_op_repeated = false; + // We do not have to copy abs to rel: + // _rel_state.str.size()+2 < _abs_state.str.size()+abs_added_size + // _rel_state.str.size()+rel_added_size < _abs_state.str.size()+2 + // _abs_state.str.size()+2 > _rel_state.str.size()+rel_added_size + } else if ( _abs_state.str.size()+2 < _rel_state.str.size()+rel_added_size ) { + + // Store common prefix + commonbase += _abs_state.str; + _abs_state.str.clear(); + // Copy abs to rel + _rel_state = _abs_state; + _abs_state.switches++; + rel_op_repeated = false; + } + if ( !abs_op_repeated ) _abs_state.appendOp(abs_op); + if ( !rel_op_repeated ) _rel_state.appendOp(rel_op); + } + break; + default: + std::cout << "Better not be here!" << std::endl; } - if ( !abs_op_repeated ) _abs_state.appendOp(abs_op); - if ( !rel_op_repeated ) _rel_state.appendOp(rel_op); } void Inkscape::SVG::PathString::State::append(Geom::Coord v) { diff --git a/src/svg/path-string.h b/src/svg/path-string.h index 11018e65c..3a891873d 100644 --- a/src/svg/path-string.h +++ b/src/svg/path-string.h @@ -1,6 +1,7 @@ /* * Copyright 2007 MenTaLguY <mental@rydia.net> * Copyright 2008 Jasper van de Gronde <th.v.d.gronde@hccnet.nl> + * Copyright 2013 Tavmjong Bah <tavmjong@free.fr> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -23,6 +24,14 @@ namespace Inkscape { namespace SVG { +// Relative vs. absolute coordinates +enum PATHSTRING_FORMAT { + PATHSTRING_ABSOLUTE, // Use only absolute coordinates + PATHSTRING_RELATIVE, // Use only relative coordinates + PATHSTRING_OPTIMIZE, // Optimize for path string length + PATHSTRING_FORMAT_SIZE +}; + /** * Builder for SVG path strings. */ @@ -38,6 +47,7 @@ public: final.reserve(commonbase.size()+t.size()); final = commonbase; final += tail(); + // std::cout << " final: " << final << std::endl; return final; } @@ -130,12 +140,10 @@ public: } PathString &closePath() { - commonbase += _abs_state.str; - _abs_state.str.clear(); - _rel_state = _abs_state; + _abs_state.appendOp('Z'); _rel_state.appendOp('z'); - _rel_state.switches++; + _current_point = _initial_point; return *this; } @@ -229,9 +237,13 @@ private: // to cause a quadratic time complexity (in the number of characters/operators) std::string commonbase; std::string final; - std::string const &tail() const { return ((_abs_state <= _rel_state || !allow_relative_coordinates) ? _abs_state.str : _rel_state.str); } + std::string const &tail() const { + return ( (format == PATHSTRING_ABSOLUTE) || + (format == PATHSTRING_OPTIMIZE && _abs_state <= _rel_state ) ? + _abs_state.str : _rel_state.str ); + } - bool const allow_relative_coordinates; + static PATHSTRING_FORMAT format; bool const force_repeat_commands; static int numericprecision; static int minimumexponent; diff --git a/src/svg/svg-length.cpp b/src/svg/svg-length.cpp index 359884f05..bb6cc5428 100644 --- a/src/svg/svg-length.cpp +++ b/src/svg/svg-length.cpp @@ -285,6 +285,10 @@ std::vector<SVGLength> sp_svg_length_list_read(gchar const *str) static unsigned sp_svg_length_read_lff(gchar const *str, SVGLength::Unit *unit, float *val, float *computed, char **next) { +/* note: this function is sometimes fed a string with several consecutive numbers, e.g. by sp_svg_length_list_read. +So after the number, the string does not necessarily have a \0 or a unit, it might also contain a space or comma and then the next number! +*/ + if (!str) { return 0; } @@ -330,8 +334,8 @@ static unsigned sp_svg_length_read_lff(gchar const *str, SVGLength::Unit *unit, *next = (char *) e + 1; } return 1; - } else if (g_ascii_isspace(e[0])) { - return 0; // spaces are not allowed + } else if (g_ascii_isspace(e[0]) && e[1] && g_ascii_isalpha(e[1])) { + return 0; // spaces between value and unit are not allowed } else { /* Unitless */ if (unit) { diff --git a/src/text-editing.cpp b/src/text-editing.cpp index cc59b0145..cf1c4811f 100644 --- a/src/text-editing.cpp +++ b/src/text-editing.cpp @@ -43,7 +43,7 @@ static const gchar *tref_edit_message = _("You cannot edit <b>cloned character data</b>."); -static bool tidy_xml_tree_recursively(SPObject *root); +static bool tidy_xml_tree_recursively(SPObject *root, bool has_text_decoration); Inkscape::Text::Layout const * te_get_layout (SPItem const *item) { @@ -774,6 +774,10 @@ sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, SPObject *common_ancestor = get_common_ancestor(item, start_item, end_item); + bool has_text_decoration = false; + gchar const *root_style = (item)->getRepr()->attribute("style"); + if(strstr(root_style,"text-decoration"))has_text_decoration = true; + if (start_item == end_item) { // the quick case where we're deleting stuff all from the same string if (SP_IS_STRING(start_item)) { // always true (if it_start != it_end anyway) @@ -835,7 +839,7 @@ sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, } } - while (tidy_xml_tree_recursively(common_ancestor)){}; + while (tidy_xml_tree_recursively(common_ancestor, has_text_decoration)){}; te_update_layout_now(item); item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); layout->validateIterator(&iter_pair.first); @@ -1554,7 +1558,7 @@ static SPObject* ascend_while_first(SPObject *item, Glib::ustring::iterator text /** empty spans: abc<span></span>def -> abcdef */ -static bool tidy_operator_empty_spans(SPObject **item) +static bool tidy_operator_empty_spans(SPObject **item, bool has_text_decoration) { bool result = false; if ( !(*item)->hasChildren() @@ -1572,7 +1576,7 @@ static bool tidy_operator_empty_spans(SPObject **item) /** inexplicable spans: abc<span style="">def</span>ghi -> "abc""def""ghi" the repeated strings will be merged by another operator. */ -static bool tidy_operator_inexplicable_spans(SPObject **item) +static bool tidy_operator_inexplicable_spans(SPObject **item, bool has_text_decoration) { //XML Tree being directly used here while it shouldn't be. if (*item && sp_repr_is_meta_element((*item)->getRepr())) { @@ -1607,7 +1611,7 @@ static bool tidy_operator_inexplicable_spans(SPObject **item) /** repeated spans: <font a>abc</font><font a>def</font> -> <font a>abcdef</font> */ -static bool tidy_operator_repeated_spans(SPObject **item) +static bool tidy_operator_repeated_spans(SPObject **item, bool has_text_decoration) { SPObject *first = *item; SPObject *second = first->getNext(); @@ -1653,7 +1657,7 @@ static bool tidy_operator_repeated_spans(SPObject **item) -> <font b>abc</font> excessive nesting: <font a><size 1>abc</size></font> -> <font a,size 1>abc</font> */ -static bool tidy_operator_excessive_nesting(SPObject **item) +static bool tidy_operator_excessive_nesting(SPObject **item, bool has_text_decoration) { if (!(*item)->hasChildren()) { return false; @@ -1731,7 +1735,7 @@ example. You may note that this only does its work when the doubly-nested child is the first or last. The other cases are called 'style inversion' below, and I'm not yet convinced that the result of that operation will be tidier in all cases. */ -static bool tidy_operator_redundant_double_nesting(SPObject **item) +static bool tidy_operator_redundant_double_nesting(SPObject **item, bool has_text_decoration) { if (!(*item)->hasChildren()) return false; if ((*item)->firstChild() == (*item)->lastChild()) return false; // this is excessive nesting, done above @@ -1792,7 +1796,7 @@ static bool redundant_semi_nesting_processor(SPObject **item, SPObject *child, b -> <font b>abc</font><font>def</font> test this by applying a colour to a region, then a different colour to a partially-overlapping region. */ -static bool tidy_operator_redundant_semi_nesting(SPObject **item) +static bool tidy_operator_redundant_semi_nesting(SPObject **item, bool has_text_decoration) { if (!(*item)->hasChildren()) return false; if ((*item)->firstChild() == (*item)->lastChild()) return false; // this is redundant nesting, done above @@ -1819,13 +1823,21 @@ static SPString* find_last_string_child_not_equal_to(SPObject *root, SPObject *n return NULL; } -/** whitespace-only spans: abc<font> </font>def - -> abc<font></font> def - abc<b><i>def</i> </b>ghi - -> abc<b><i>def</i></b> ghi */ -static bool tidy_operator_styled_whitespace(SPObject **item) +/** whitespace-only spans: + abc<font> </font>def + -> abc<font></font> def + abc<b><i>def</i> </b>ghi + -> abc<b><i>def</i></b> ghi + + visible text-decoration changes on white spaces should not be subject + to this sort of processing. So + abc<text-decoration-color> </text-decoration-color>def + is unchanged. +*/ +static bool tidy_operator_styled_whitespace(SPObject **item, bool has_text_decoration) { - if (!SP_IS_STRING(*item)) { + // any type of visible text decoration is OK as pure spaces, so do nothing here in that case. + if (!SP_IS_STRING(*item) || has_text_decoration ) { return false; } Glib::ustring const &str = SP_STRING(*item)->string; @@ -1835,6 +1847,7 @@ static bool tidy_operator_styled_whitespace(SPObject **item) } } + SPObject *test_item = *item; SPString *next_string; for ( ; ; ) { // find the next string @@ -1862,7 +1875,7 @@ static bool tidy_operator_styled_whitespace(SPObject **item) if (next_string == NULL) { return false; // an empty paragraph } - next_string->string += str; + next_string->string = str + next_string->string; break; } } @@ -1896,9 +1909,11 @@ It may be that some of the later tidy operators that I wrote are actually general cases of the earlier operators, and hence the special-case-only versions can be removed. I haven't analysed my work in detail to figure out if this is so. */ -static bool tidy_xml_tree_recursively(SPObject *root) +static bool tidy_xml_tree_recursively(SPObject *root, bool has_text_decoration) { - static bool (* const tidy_operators[])(SPObject**) = { + gchar const *root_style = (root)->getRepr()->attribute("style"); + if(root_style && strstr(root_style,"text-decoration"))has_text_decoration = true; + static bool (* const tidy_operators[])(SPObject**, bool) = { tidy_operator_empty_spans, tidy_operator_inexplicable_spans, tidy_operator_repeated_spans, @@ -1915,12 +1930,12 @@ static bool tidy_xml_tree_recursively(SPObject *root) continue; } if (child->hasChildren()) { - changes |= tidy_xml_tree_recursively(child); + changes |= tidy_xml_tree_recursively(child, has_text_decoration); } unsigned i; for (i = 0 ; i < sizeof(tidy_operators) / sizeof(tidy_operators[0]) ; i++) { - if (tidy_operators[i](&child)) { + if (tidy_operators[i](&child, has_text_decoration)) { changes = true; break; } @@ -2018,7 +2033,10 @@ void sp_te_apply_style(SPItem *text, Inkscape::Text::Layout::iterator const &sta and neither option can be made to work, a fallback could be to reduce everything to a single level of nesting and drop all pretence of roundtrippability. */ - while (tidy_xml_tree_recursively(common_ancestor)){}; + bool has_text_decoration = false; + gchar const *root_style = (text)->getRepr()->attribute("style"); + if(strstr(root_style,"text-decoration"))has_text_decoration = true; + while (tidy_xml_tree_recursively(common_ancestor, has_text_decoration)){}; // if we only modified subobjects this won't have been automatically sent text->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index b592d2527..c95dd35cc 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -54,6 +54,7 @@ set(ui_SRC dialog/livepatheffect-editor.cpp dialog/memory.cpp dialog/messages.cpp + dialog/new-from-template.cpp dialog/object-attributes.cpp dialog/object-properties.cpp dialog/ocaldialogs.cpp @@ -65,6 +66,8 @@ set(ui_SRC dialog/spellcheck.cpp dialog/svg-fonts-dialog.cpp dialog/swatches.cpp + dialog/template-load-tab.cpp + dialog/template-widget.cpp dialog/text-edit.cpp dialog/tile.cpp dialog/tracedialog.cpp @@ -166,16 +169,20 @@ set(ui_SRC dialog/livepatheffect-editor.h dialog/memory.h dialog/messages.h + dialog/new-from-template.h dialog/object-attributes.h dialog/object-properties.h dialog/ocaldialogs.h dialog/panel-dialog.h dialog/print-colors-preview-dialog.h dialog/print.h + dialog/spellcheck.h dialog/svg-fonts-dialog.h dialog/swatches.h dialog/symbols.h + dialog/template-load-tab.h + dialog/template-widget.h dialog/text-edit.h dialog/tile.h dialog/tracedialog.h diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index 084b2ebd8..48d32d64b 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -94,12 +94,6 @@ #ifdef WIN32 #include <windows.h> -// Clipboard Formats: http://msdn.microsoft.com/en-us/library/ms649013(VS.85).aspx -// On Windows, most graphical applications can handle CF_DIB/CF_BITMAP and/or CF_ENHMETAFILE -// GTK automatically presents an "image/bmp" target as CF_DIB/CF_BITMAP -// Presenting "image/x-emf" as CF_ENHMETAFILE must be done by Inkscape ? -#define CLIPBOARD_WIN32_EMF_TARGET "CF_ENHMETAFILE" -#define CLIPBOARD_WIN32_EMF_MIME "image/x-emf" #endif namespace Inkscape { @@ -179,13 +173,18 @@ ClipboardManagerImpl::ClipboardManagerImpl() _text_style(NULL), _clipboard( Gtk::Clipboard::get() ) { + // Clipboard Formats: http://msdn.microsoft.com/en-us/library/ms649013(VS.85).aspx + // On Windows, most graphical applications can handle CF_DIB/CF_BITMAP and/or CF_ENHMETAFILE + // GTK automatically presents an "image/bmp" target as CF_DIB/CF_BITMAP + // Presenting "image/x-emf" as CF_ENHMETAFILE must be done by Inkscape ? + // push supported clipboard targets, in order of preference _preferred_targets.push_back("image/x-inkscape-svg"); _preferred_targets.push_back("image/svg+xml"); _preferred_targets.push_back("image/svg+xml-compressed"); -#ifdef WIN32 - _preferred_targets.push_back(CLIPBOARD_WIN32_EMF_MIME); -#endif + _preferred_targets.push_back("image/x-emf"); + _preferred_targets.push_back("CF_ENHMETAFILE"); + _preferred_targets.push_back("WCF_ENHMETAFILE"); // seen on Wine _preferred_targets.push_back("application/pdf"); _preferred_targets.push_back("image/x-adobe-illustrator"); } @@ -981,7 +980,7 @@ SPDocument *ClipboardManagerImpl::_retrieveClipboard(Glib::ustring required_targ Glib::ustring target = best_target; #ifdef WIN32 - if (best_target == CLIPBOARD_WIN32_EMF_TARGET) + if (best_target == "CF_ENHMETAFILE" || best_target == "WCF_ENHMETAFILE") { // Try to save clipboard data as en emf file (using win32 api) if (OpenClipboard(NULL)) { HGLOBAL hglb = GetClipboardData(CF_ENHMETAFILE); @@ -989,7 +988,7 @@ SPDocument *ClipboardManagerImpl::_retrieveClipboard(Glib::ustring required_targ HENHMETAFILE hemf = CopyEnhMetaFile((HENHMETAFILE) hglb, filename); if (hemf) { file_saved = true; - target = CLIPBOARD_WIN32_EMF_MIME; + target = "image/x-emf"; DeleteEnhMetaFile(hemf); } } @@ -1020,6 +1019,10 @@ SPDocument *ClipboardManagerImpl::_retrieveClipboard(Glib::ustring required_targ if (target == "image/x-inkscape-svg") { target = "image/svg+xml"; } + // Use the EMF extension to import metafiles + if (target == "CF_ENHMETAFILE" || target == "WCF_ENHMETAFILE") { + target = "image/x-emf"; + } Inkscape::Extension::DB::InputList inlist; Inkscape::Extension::db.get_input_list(inlist); @@ -1213,10 +1216,10 @@ Glib::ustring ClipboardManagerImpl::_getBestTarget() // clipboard target debugging snippet /* - g_debug("Begin clipboard targets"); + g_message("Begin clipboard targets"); for ( std::list<Glib::ustring>::iterator x = targets.begin() ; x != targets.end(); ++x ) - g_debug("Clipboard target: %s", (*x).data()); - g_debug("End clipboard targets\n"); + g_message("Clipboard target: %s", (*x).data()); + g_message("End clipboard targets\n"); //*/ for (std::list<Glib::ustring>::iterator i = _preferred_targets.begin() ; @@ -1239,7 +1242,7 @@ Glib::ustring ClipboardManagerImpl::_getBestTarget() CloseClipboard(); if (format == CF_ENHMETAFILE) { - return CLIPBOARD_WIN32_EMF_TARGET; + return "CF_ENHMETAFILE"; } if (format == CF_DIB || format == CF_BITMAP) { return CLIPBOARD_GDK_PIXBUF_TARGET; @@ -1247,7 +1250,7 @@ Glib::ustring ClipboardManagerImpl::_getBestTarget() } if (IsClipboardFormatAvailable(CF_ENHMETAFILE)) { - return CLIPBOARD_WIN32_EMF_TARGET; + return "CF_ENHMETAFILE"; } #endif if (_clipboard->wait_is_image_available()) { @@ -1311,7 +1314,7 @@ void ClipboardManagerImpl::_setClipboardTargets() if (OpenClipboard(NULL)) { if ( _clipboardSPDoc != NULL ) { - const Glib::ustring target = CLIPBOARD_WIN32_EMF_MIME; + const Glib::ustring target = "image/x-emf"; Inkscape::Extension::DB::OutputList outlist; Inkscape::Extension::db.get_output_list(outlist); diff --git a/src/ui/dialog/Makefile_insert b/src/ui/dialog/Makefile_insert index bbede9df1..09a7ef573 100644 --- a/src/ui/dialog/Makefile_insert +++ b/src/ui/dialog/Makefile_insert @@ -70,6 +70,8 @@ ink_common_sources += \ ui/dialog/memory.h \ ui/dialog/messages.cpp \ ui/dialog/messages.h \ + ui/dialog/new-from-template.cpp \ + ui/dialog/new-from-template.h \ ui/dialog/ocaldialogs.cpp \ ui/dialog/ocaldialogs.h \ ui/dialog/object-attributes.cpp \ @@ -89,6 +91,10 @@ ink_common_sources += \ ui/dialog/swatches.h \ ui/dialog/symbols.cpp \ ui/dialog/symbols.h \ + ui/dialog/template-load-tab.cpp \ + ui/dialog/template-load-tab.h \ + ui/dialog/template-widget.cpp \ + ui/dialog/template-widget.h \ ui/dialog/text-edit.cpp \ ui/dialog/text-edit.h \ ui/dialog/tile.cpp \ diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp index c98e23000..577793496 100644 --- a/src/ui/dialog/export.cpp +++ b/src/ui/dialog/export.cpp @@ -201,11 +201,11 @@ Export::Export (void) : earlier than that */ unit_selector = new Inkscape::UI::Widget::UnitMenu(); unit_selector->setUnitType(Inkscape::Util::UNIT_TYPE_LINEAR); - unitChangedConn = unit_selector->signal_changed().connect(sigc::mem_fun(*this, &Export::onUnitChanged)); SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop) unit_selector->setUnit(sp_desktop_namedview(desktop)->doc_units->abbr); + unitChangedConn = unit_selector->signal_changed().connect(sigc::mem_fun(*this, &Export::onUnitChanged)); unitbox.pack_end(*unit_selector, false, false, 0); unitbox.pack_end(units_label, false, false, 3); diff --git a/src/ui/dialog/filter-effects-dialog.cpp b/src/ui/dialog/filter-effects-dialog.cpp index 4401d5658..4ce2eafd1 100644 --- a/src/ui/dialog/filter-effects-dialog.cpp +++ b/src/ui/dialog/filter-effects-dialog.cpp @@ -1310,7 +1310,8 @@ void FilterEffectsDialog::FilterModifier::on_name_edited(const Glib::ustring& pa void FilterEffectsDialog::FilterModifier::on_filter_reorder(const Gtk::TreeModel::Path& path) { for(Gtk::TreeModel::iterator i = _model->children().begin(); i != _model->children().end(); ++i) { SPObject* object = (*i)[_columns.filter]; - object->getRepr()->setPosition(0); + if(object && object->getRepr()) + object->getRepr()->setPosition(0); } } diff --git a/src/ui/dialog/inkscape-preferences.cpp b/src/ui/dialog/inkscape-preferences.cpp index 7890b0b4c..b06c1fd1f 100644 --- a/src/ui/dialog/inkscape-preferences.cpp +++ b/src/ui/dialog/inkscape-preferences.cpp @@ -879,8 +879,12 @@ void InkscapePreferences::initPageIO() _page_svgoutput.add_group_header( _("Path data")); - _svgoutput_allowrelativecoordinates.init( _("Allow relative coordinates"), "/options/svgoutput/allowrelativecoordinates", true); - _page_svgoutput.add_line( true, "", _svgoutput_allowrelativecoordinates, "", _("If set, relative coordinates may be used in path data"), false); + int const numPathstringFormat = 3; + Glib::ustring pathstringFormatLabels[numPathstringFormat] = {_("Absolute"), _("Relative"), _("Optimized")}; + int pathstringFormatValues[numPathstringFormat] = {0, 1, 2}; + + _svgoutput_pathformat.init("/options/svgoutput/pathstring_format", pathstringFormatLabels, pathstringFormatValues, numPathstringFormat, 2); + _page_svgoutput.add_line( true, _("Path string format"), _svgoutput_pathformat, "", _("Path data should be written: only with absolute coordinates, only with relative coordinates, or optimized for string length (mixed absolute and relative coordinates)"), false); _svgoutput_forcerepeatcommands.init( _("Force repeat commands"), "/options/svgoutput/forcerepeatcommands", false); _page_svgoutput.add_line( true, "", _svgoutput_forcerepeatcommands, "", _("Force repeating of the same path command (for example, 'L 1,2 L 3,4' instead of 'L 1,2 3,4')"), false); diff --git a/src/ui/dialog/inkscape-preferences.h b/src/ui/dialog/inkscape-preferences.h index 37c05df05..56222fb22 100644 --- a/src/ui/dialog/inkscape-preferences.h +++ b/src/ui/dialog/inkscape-preferences.h @@ -426,7 +426,7 @@ protected: UI::Widget::PrefSpinButton _svgoutput_minimumexponent; UI::Widget::PrefCheckButton _svgoutput_inlineattrs; UI::Widget::PrefSpinButton _svgoutput_indent; - UI::Widget::PrefCheckButton _svgoutput_allowrelativecoordinates; + UI::Widget::PrefCombo _svgoutput_pathformat; UI::Widget::PrefCheckButton _svgoutput_forcerepeatcommands; // Attribute Checking controls for SVG Output page: diff --git a/src/ui/dialog/layers.cpp b/src/ui/dialog/layers.cpp index c41046123..c01903f94 100644 --- a/src/ui/dialog/layers.cpp +++ b/src/ui/dialog/layers.cpp @@ -838,7 +838,8 @@ LayersPanel::LayersPanel() : _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel); _tree.set_expander_column( *_tree.get_column(nameColNum) ); - + _tree.set_search_column(nameColNum + 1); + _compositeSettings.setSubject(&_subject); _selectedConnection = _tree.get_selection()->signal_changed().connect( sigc::mem_fun(*this, &LayersPanel::_pushTreeSelectionToCurrent) ); diff --git a/src/ui/dialog/new-from-template.cpp b/src/ui/dialog/new-from-template.cpp new file mode 100644 index 000000000..2595e2cf5 --- /dev/null +++ b/src/ui/dialog/new-from-template.cpp @@ -0,0 +1,59 @@ +/** @file + * @brief New From Template main dialog - implementation + */ +/* Authors: + * Jan Darowski <jan.darowski@gmail.com>, supervised by Krzysztof Kosiński + * + * Copyright (C) 2013 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#include "new-from-template.h" +#include "file.h" + +#include <gtkmm/alignment.h> +#include <glibmm/i18n.h> + + +namespace Inkscape { +namespace UI { + + +NewFromTemplate::NewFromTemplate() + : _create_template_button(_("Create from template")) +{ + set_title(_("New From Template")); + resize(400, 400); + + get_vbox()->pack_start(_main_widget); + + Gtk::Alignment *align; + align = manage(new Gtk::Alignment(Gtk::ALIGN_END, Gtk::ALIGN_CENTER, 0.0, 0.0)); + get_vbox()->pack_end(*align, Gtk::PACK_SHRINK); + align->set_padding(0, 0, 0, 15); + align->add(_create_template_button); + + _create_template_button.signal_pressed().connect( + sigc::mem_fun(*this, &NewFromTemplate::_createFromTemplate)); + + show_all(); +} + + +void NewFromTemplate::_createFromTemplate() +{ + _main_widget.createTemplate(); + + response(0); +} + + +void NewFromTemplate::load_new_from_template() +{ + NewFromTemplate dl; + dl.run(); +} + +} +} diff --git a/src/ui/dialog/new-from-template.h b/src/ui/dialog/new-from-template.h new file mode 100644 index 000000000..8ebcb2863 --- /dev/null +++ b/src/ui/dialog/new-from-template.h @@ -0,0 +1,39 @@ +/** @file + * @brief New From Template main dialog + */ +/* Authors: + * Jan Darowski <jan.darowski@gmail.com>, supervised by Krzysztof Kosiński + * + * Copyright (C) 2013 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_SEEN_UI_DIALOG_NEW_FROM_TEMPLATE_H +#define INKSCAPE_SEEN_UI_DIALOG_NEW_FROM_TEMPLATE_H + +#include <gtkmm/dialog.h> +#include <gtkmm/button.h> + +#include "template-load-tab.h" + + +namespace Inkscape { +namespace UI { + + +class NewFromTemplate : public Gtk::Dialog +{ +public: + static void load_new_from_template(); + +private: + NewFromTemplate(); + Gtk::Button _create_template_button; + TemplateLoadTab _main_widget; + + void _createFromTemplate(); +}; + +} +} +#endif diff --git a/src/ui/dialog/template-load-tab.cpp b/src/ui/dialog/template-load-tab.cpp new file mode 100644 index 000000000..265ee8026 --- /dev/null +++ b/src/ui/dialog/template-load-tab.cpp @@ -0,0 +1,281 @@ +/** @file + * @brief New From Template abstract tab implementation + */ +/* Authors: + * Jan Darowski <jan.darowski@gmail.com>, supervised by Krzysztof Kosiński + * + * Copyright (C) 2013 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "template-widget.h" + +#include "template-load-tab.h" + +#include <gtkmm/messagedialog.h> +#include <gtkmm/scrolledwindow.h> +#include <glibmm/i18n.h> +#include <glibmm/fileutils.h> +#include <glibmm/miscutils.h> +#include <glibmm/stringutils.h> + +#include "interface.h" +#include "file.h" +#include "path-prefix.h" +#include "preferences.h" +#include "inkscape.h" +#include "xml/repr.h" +#include "xml/document.h" +#include "xml/node.h" + + +namespace Inkscape { +namespace UI { + + +TemplateLoadTab::TemplateLoadTab() + : _current_keyword("") + , _keywords_combo(true) + , _current_search_type(ALL) +{ + set_border_width(10); + + _info_widget = manage(new TemplateWidget()); + + Gtk::Label *title; + title = manage(new Gtk::Label(_("Search:"))); + _search_box.pack_start(*title, Gtk::PACK_SHRINK); + _search_box.pack_start(_keywords_combo, Gtk::PACK_SHRINK, 5); + + _tlist_box.pack_start(_search_box, Gtk::PACK_SHRINK, 10); + + pack_start(_tlist_box, Gtk::PACK_SHRINK); + pack_start(*_info_widget, Gtk::PACK_EXPAND_WIDGET, 5); + + Gtk::ScrolledWindow *scrolled; + scrolled = manage(new Gtk::ScrolledWindow()); + scrolled->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); + scrolled->add(_tlist_view); + _tlist_box.pack_start(*scrolled, Gtk::PACK_EXPAND_WIDGET, 5); + + _keywords_combo.signal_changed().connect( + sigc::mem_fun(*this, &TemplateLoadTab::_keywordSelected)); + this->show_all(); + + _loading_path = ""; + _loadTemplates(); + _initLists(); +} + + +TemplateLoadTab::~TemplateLoadTab() +{ +} + + +void TemplateLoadTab::createTemplate() +{ + _info_widget->create(); +} + + +void TemplateLoadTab::_displayTemplateInfo() +{ + Glib::RefPtr<Gtk::TreeSelection> templateSelectionRef = _tlist_view.get_selection(); + if (templateSelectionRef->get_selected()) { + _current_template = (*templateSelectionRef->get_selected())[_columns.textValue]; + + _info_widget->display(_tdata[_current_template]); + } + +} + + +void TemplateLoadTab::_initKeywordsList() +{ + _keywords_combo.append(_("All")); + + for (std::set<Glib::ustring>::iterator it = _keywords.begin() ; it != _keywords.end() ; ++it){ + _keywords_combo.append(*it); + } +} + + +void TemplateLoadTab::_initLists() +{ + _tlist_store = Gtk::ListStore::create(_columns); + _tlist_view.set_model(_tlist_store); + _tlist_view.append_column("", _columns.textValue); + _tlist_view.set_headers_visible(false); + + _initKeywordsList(); + _refreshTemplatesList(); + + Glib::RefPtr<Gtk::TreeSelection> templateSelectionRef = + _tlist_view.get_selection(); + templateSelectionRef->signal_changed().connect( + sigc::mem_fun(*this, &TemplateLoadTab::_displayTemplateInfo)); +} + + +void TemplateLoadTab::_keywordSelected() +{ + _current_keyword = _keywords_combo.get_active_text(); + if (_current_keyword == ""){ + _current_keyword = _keywords_combo.get_entry_text(); + _current_search_type = USER_SPECIFIED; + } + else + _current_search_type = LIST_KEYWORD; + + if (_current_keyword == "" || _current_keyword == _("All")) + _current_search_type = ALL; + + _refreshTemplatesList(); +} + + +void TemplateLoadTab::_refreshTemplatesList() +{ + _tlist_store->clear(); + + switch (_current_search_type){ + case ALL :{ + + for (std::map<Glib::ustring, TemplateData>::iterator it = _tdata.begin() ; it != _tdata.end() ; ++it) { + Gtk::TreeModel::iterator iter = _tlist_store->append(); + Gtk::TreeModel::Row row = *iter; + row[_columns.textValue] = it->first; + } + break; + } + + case LIST_KEYWORD: { + for (std::map<Glib::ustring, TemplateData>::iterator it = _tdata.begin() ; it != _tdata.end() ; ++it) { + if (it->second.keywords.count(_current_keyword) != 0){ + Gtk::TreeModel::iterator iter = _tlist_store->append(); + Gtk::TreeModel::Row row = *iter; + row[_columns.textValue] = it->first; + } + } + break; + } + + case USER_SPECIFIED : { + for (std::map<Glib::ustring, TemplateData>::iterator it = _tdata.begin() ; it != _tdata.end() ; ++it) { + if (it->second.keywords.count(_current_keyword) != 0 || + it->second.display_name.find(_current_keyword) != Glib::ustring::npos || + it->second.author.find(_current_keyword) != Glib::ustring::npos || + it->second.short_description.find(_current_keyword) != Glib::ustring::npos || + it->second.long_description.find(_current_keyword) != Glib::ustring::npos ) + { + Gtk::TreeModel::iterator iter = _tlist_store->append(); + Gtk::TreeModel::Row row = *iter; + row[_columns.textValue] = it->first; + } + } + break; + } + } +} + + +void TemplateLoadTab::_loadTemplates() +{ + // user's local dir + _getTemplatesFromDir(profile_path("templates") + _loading_path); + + // system templates dir + _getTemplatesFromDir(INKSCAPE_TEMPLATESDIR + _loading_path); +} + + +TemplateLoadTab::TemplateData TemplateLoadTab::_processTemplateFile(const Glib::ustring &path) +{ + TemplateData result; + result.path = path; + result.is_procedural = false; + result.preview_name = ""; + + // convert path into valid template name + result.display_name = Glib::path_get_basename(path); + gsize n = 0; + while ((n = result.display_name.find_first_of("_", 0)) < Glib::ustring::npos){ + result.display_name.replace(n, 1, 1, ' '); + } + n = result.display_name.rfind(".svg"); + result.display_name.replace(n, 4, 1, ' '); + + Inkscape::XML::Document *rdoc; + rdoc = sp_repr_read_file(path.data(), SP_SVG_NS_URI); + Inkscape::XML::Node *myRoot; + Inkscape::XML::Node *dataNode; + + if (rdoc){ + myRoot = rdoc->root(); + if (strcmp(myRoot->name(), "svg:svg") != 0){ // Wrong file format + return result; + } + + myRoot = sp_repr_lookup_name(myRoot, "inkscape:_templateinfo"); + + if (myRoot == NULL) // No template info + return result; + + if ((dataNode = sp_repr_lookup_name(myRoot, "inkscape:_name")) != NULL) + result.display_name = dgettext("Document template name", dataNode->firstChild()->content()); + if ((dataNode = sp_repr_lookup_name(myRoot, "inkscape:author")) != NULL) + result.author = dataNode->firstChild()->content(); + if ((dataNode = sp_repr_lookup_name(myRoot, "inkscape:_short")) != NULL) + result.short_description = dgettext("Document template short description", dataNode->firstChild()->content()); + if ((dataNode = sp_repr_lookup_name(myRoot, "inkscape:_long") )!= NULL) + result.long_description = dgettext("Document template long description", dataNode->firstChild()->content()); + if ((dataNode = sp_repr_lookup_name(myRoot, "inkscape:preview")) != NULL) + result.preview_name = dataNode->firstChild()->content(); + if ((dataNode = sp_repr_lookup_name(myRoot, "inkscape:date")) != NULL){ + result.creation_date = dataNode->firstChild()->content(); + } + + if ((dataNode = sp_repr_lookup_name(myRoot, "inkscape:_keywords")) != NULL){ + Glib::ustring data = dataNode->firstChild()->content(); + while (!data.empty()){ + std::size_t pos = data.find_first_of(" "); + if (pos == Glib::ustring::npos) + pos = data.size(); + + Glib::ustring keyword = dgettext("Document template keyword", data.substr(0, pos).data()); + result.keywords.insert(keyword); + _keywords.insert(keyword); + + if (pos == data.size()) + break; + data.erase(0, pos+1); + } + } + } + + return result; +} + + +void TemplateLoadTab::_getTemplatesFromDir(const Glib::ustring &path) +{ + if ( !Glib::file_test(path, Glib::FILE_TEST_EXISTS) || + !Glib::file_test(path, Glib::FILE_TEST_IS_DIR)) + return; + + Glib::Dir dir(path); + + Glib::ustring file = Glib::build_filename(path, dir.read_name()); + while (file != path){ + if (Glib::str_has_suffix(file, ".svg") && !Glib::str_has_prefix(Glib::path_get_basename(file), "default")){ + TemplateData tmp = _processTemplateFile(file); + if (tmp.display_name != "") + _tdata[tmp.display_name] = tmp; + } + file = Glib::build_filename(path, dir.read_name()); + } +} + +} +} diff --git a/src/ui/dialog/template-load-tab.h b/src/ui/dialog/template-load-tab.h new file mode 100644 index 000000000..50f3e0be2 --- /dev/null +++ b/src/ui/dialog/template-load-tab.h @@ -0,0 +1,102 @@ +/** @file + * @brief New From Template abstract tab class + */ +/* Authors: + * Jan Darowski <jan.darowski@gmail.com>, supervised by Krzysztof Kosiński + * + * Copyright (C) 2013 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_SEEN_UI_DIALOG_TEMPLATE_LOAD_TAB_H +#define INKSCAPE_SEEN_UI_DIALOG_TEMPLATE_LOAD_TAB_H + +#include <gtkmm/box.h> +#include <gtkmm/comboboxtext.h> +#include <gtkmm/frame.h> +#include <gtkmm/liststore.h> +#include <gtkmm/treeview.h> +#include <map> +#include <set> + + +namespace Inkscape { +namespace UI { + +class TemplateWidget; + +class TemplateLoadTab : public Gtk::HBox +{ + +public: + struct TemplateData + { + bool is_procedural; + Glib::ustring path; + Glib::ustring display_name; + Glib::ustring author; + Glib::ustring short_description; + Glib::ustring long_description; + Glib::ustring preview_name; + Glib::ustring creation_date; + std::set<Glib::ustring> keywords; + }; + + TemplateLoadTab(); + virtual ~TemplateLoadTab(); + virtual void createTemplate(); + +protected: + class StringModelColumns : public Gtk::TreeModelColumnRecord + { + public: + StringModelColumns() + { + add(textValue); + } + + Gtk::TreeModelColumn<Glib::ustring> textValue; + }; + + Glib::ustring _current_keyword; + Glib::ustring _current_template; + Glib::ustring _loading_path; + std::map<Glib::ustring, TemplateData> _tdata; + std::set<Glib::ustring> _keywords; + + + virtual void _displayTemplateInfo(); + virtual void _initKeywordsList(); + virtual void _refreshTemplatesList(); + void _loadTemplates(); + void _initLists(); + + Gtk::VBox _tlist_box; + Gtk::HBox _search_box; + TemplateWidget *_info_widget; + + Gtk::ComboBoxText _keywords_combo; + + Gtk::TreeView _tlist_view; + Glib::RefPtr<Gtk::ListStore> _tlist_store; + StringModelColumns _columns; + +private: + enum SearchType + { + LIST_KEYWORD, + USER_SPECIFIED, + ALL + }; + + SearchType _current_search_type; + + void _getTemplatesFromDir(const Glib::ustring &); + void _keywordSelected(); + TemplateData _processTemplateFile(const Glib::ustring &); +}; + +} +} + +#endif diff --git a/src/ui/dialog/template-widget.cpp b/src/ui/dialog/template-widget.cpp new file mode 100644 index 000000000..be7e2b515 --- /dev/null +++ b/src/ui/dialog/template-widget.cpp @@ -0,0 +1,117 @@ +/** @file + * @brief New From Template - templates widget - implementation + */ +/* Authors: + * Jan Darowski <jan.darowski@gmail.com>, supervised by Krzysztof Kosiński + * + * Copyright (C) 2013 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "template-widget.h" + +#include <gtkmm/alignment.h> +#include <gtkmm/button.h> +#include <gtkmm/label.h> +#include <gtkmm/messagedialog.h> + +#include <glibmm/i18n.h> +#include <glibmm/miscutils.h> + +#include "template-load-tab.h" +#include "file.h" + +namespace Inkscape { +namespace UI { + + +TemplateWidget::TemplateWidget() + : _more_info_button(_("More info")) + , _short_description_label(_(" ")) + , _template_author_label(_(" ")) + , _template_name_label(_("no template selected")) +{ + pack_start(_template_name_label, Gtk::PACK_SHRINK, 10); + pack_start(_template_author_label, Gtk::PACK_SHRINK, 0); + pack_start(_preview_box, Gtk::PACK_SHRINK, 0); + + _preview_box.pack_start(_preview_image, Gtk::PACK_EXPAND_PADDING, 15); + _preview_box.pack_start(_preview_render, Gtk::PACK_EXPAND_PADDING, 10); + + _short_description_label.set_line_wrap(true); + //_short_description_label.set_size_request(200); + + Gtk::Alignment *align; + align = manage(new Gtk::Alignment(Gtk::ALIGN_END, Gtk::ALIGN_CENTER, 0.0, 0.0)); + pack_end(*align, Gtk::PACK_SHRINK); + align->add(_more_info_button); + + pack_end(_short_description_label, Gtk::PACK_SHRINK, 5); + + _more_info_button.signal_pressed().connect( + sigc::mem_fun(*this, &TemplateWidget::_displayTemplateDetails)); +} + + +void TemplateWidget::create() +{ + if (_current_template.path == "") + return; + + if (_current_template.is_procedural) {} + else { + sp_file_new(_current_template.path); + } +} + + +void TemplateWidget::display(TemplateLoadTab::TemplateData data) +{ + _current_template = data; + if (data.is_procedural){} + else{ + _template_name_label.set_text(_current_template.display_name); + _template_author_label.set_text(_current_template.author); + _short_description_label.set_text(_current_template.short_description); + + Glib::ustring imagePath = Glib::build_filename(Glib::path_get_dirname(_current_template.path), _current_template.preview_name); + if (data.preview_name != ""){ + _preview_image.set(imagePath); + _preview_image.show(); + _preview_render.hide(); + } + else{ + _preview_render.showImage(data.path); + _preview_render.show(); + _preview_image.hide(); + } + } +} + + +void TemplateWidget::_displayTemplateDetails() +{ + if (_current_template.path == "") + return; + + Glib::ustring message = _current_template.display_name + "\n\n" + + _("Path: ") + _current_template.path + "\n\n"; + + if (_current_template.long_description != "") + message += _("Description: ") + _current_template.long_description + "\n\n"; + if (_current_template.keywords.size() > 0){ + message += _("Keywords: "); + for (std::set<Glib::ustring>::iterator it = _current_template.keywords.begin(); it != _current_template.keywords.end(); ++it) + message += *it + " "; + message += "\n\n"; + } + + if (_current_template.author != "") + message += _("By: ") + _current_template.author + " " + _current_template.creation_date + "\n\n"; + + Gtk::MessageDialog dl(message, false, Gtk::MESSAGE_OTHER); + dl.run(); +} + +} +} diff --git a/src/ui/dialog/template-widget.h b/src/ui/dialog/template-widget.h new file mode 100644 index 000000000..f7e1267ce --- /dev/null +++ b/src/ui/dialog/template-widget.h @@ -0,0 +1,48 @@ +/** @file + * @brief New From Template - template widget + */ +/* Authors: + * Jan Darowski <jan.darowski@gmail.com>, supervised by Krzysztof Kosiński + * + * Copyright (C) 2013 Authors + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef INKSCAPE_SEEN_UI_DIALOG_TEMPLATE_WIDGET_H +#define INKSCAPE_SEEN_UI_DIALOG_TEMPLATE_WIDGET_H + +#include "filedialogimpl-gtkmm.h" + +#include <gtkmm/box.h> + +#include "template-load-tab.h" + +namespace Inkscape { +namespace UI { + + +class TemplateWidget : public Gtk::VBox +{ +public: + TemplateWidget (); + void create(); + void display(TemplateLoadTab::TemplateData); + +private: + TemplateLoadTab::TemplateData _current_template; + + Gtk::Button _more_info_button; + Gtk::HBox _preview_box; + Gtk::Image _preview_image; + Dialog::SVGPreview _preview_render; + Gtk::Label _short_description_label; + Gtk::Label _template_author_label; + Gtk::Label _template_name_label; + + void _displayTemplateDetails(); +}; + +} +} + +#endif diff --git a/src/util/units.cpp b/src/util/units.cpp index 2b09337b6..414885040 100644 --- a/src/util/units.cpp +++ b/src/util/units.cpp @@ -257,9 +257,10 @@ Quantity UnitTable::getQuantity(Glib::ustring const& q) const // Extract value double value = 0; - Glib::RefPtr<Glib::Regex> value_regex = Glib::Regex::create("[-+]*[\\d+]*\\.*[\\d+]*[eE]*[-+]*\\d+"); + Glib::RefPtr<Glib::Regex> value_regex = Glib::Regex::create("[-+]*[\\d+]*[\\.,]*[\\d+]*[eE]*[-+]*\\d+"); if (value_regex->match(q, match_info)) { - value = atof(match_info.fetch(0).c_str()); + std::istringstream tmp_v(match_info.fetch(0)); + tmp_v >> value; } // Extract unit abbreviation diff --git a/src/verbs.cpp b/src/verbs.cpp index baac07d60..737d9e150 100644 --- a/src/verbs.cpp +++ b/src/verbs.cpp @@ -80,6 +80,7 @@ #include "ui/dialog/inkscape-preferences.h" #include "ui/dialog/layer-properties.h" #include "ui/dialog/layers.h" +#include "ui/dialog/new-from-template.h" #include "ui/dialog/object-properties.h" #include "ui/dialog/swatches.h" #include "ui/dialog/symbols.h" @@ -880,6 +881,9 @@ void FileVerb::perform(SPAction *action, void *data) case SP_VERB_FILE_CLOSE_VIEW: sp_ui_close_view(NULL); break; + case SP_VERB_FILE_TEMPLATES: + Inkscape::UI::NewFromTemplate::load_new_from_template(); + break; default: break; } @@ -2372,6 +2376,8 @@ Verb *Verb::_base_verbs[] = { new FileVerb(SP_VERB_FILE_CLOSE_VIEW, "FileClose", N_("_Close"), N_("Close this document window"), GTK_STOCK_CLOSE), new FileVerb(SP_VERB_FILE_QUIT, "FileQuit", N_("_Quit"), N_("Quit Inkscape"), GTK_STOCK_QUIT), + new FileVerb(SP_VERB_FILE_TEMPLATES, "FileTemplates", N_("_Templates..."), + N_("Create new project from template"), INKSCAPE_ICON("dialog-templates")), // Edit new EditVerb(SP_VERB_EDIT_UNDO, "EditUndo", N_("_Undo"), N_("Undo last action"), diff --git a/src/verbs.h b/src/verbs.h index 5cc2ad12e..c4b01dfbf 100644 --- a/src/verbs.h +++ b/src/verbs.h @@ -59,6 +59,7 @@ enum { SP_VERB_FILE_PREV_DESKTOP, SP_VERB_FILE_CLOSE_VIEW, SP_VERB_FILE_QUIT, + SP_VERB_FILE_TEMPLATES, /* Edit */ SP_VERB_EDIT_UNDO, SP_VERB_EDIT_REDO, diff --git a/src/widgets/dash-selector.cpp b/src/widgets/dash-selector.cpp index 901d3b68c..51483a9c4 100644 --- a/src/widgets/dash-selector.cpp +++ b/src/widgets/dash-selector.cpp @@ -39,6 +39,7 @@ static double dash_4_1[] = {4.0, 1.0, -1.0}; static double dash_1_2[] = {1.0, 2.0, -1.0}; static double dash_1_4[] = {1.0, 4.0, -1.0}; +#define bd_len 7 // must correspond to the number of entries in the next line static double *builtin_dashes[] = {dash_0, dash_1_1, dash_2_1, dash_4_1, dash_1_2, dash_1_4, NULL}; static double **dashes = NULL; @@ -79,12 +80,18 @@ SPDashSelector::SPDashSelector() this->pack_start(*sb, false, false, 0); - for (int i = 0; dashes[i]; i++) { + int np=0; + while (dashes[np]){ np++;} + for (int i = 0; i<np-1; i++) { // all but the custom one go this way // Add the dashes to the combobox Gtk::TreeModel::Row row = *(dash_store->append()); row[dash_columns.dash] = dashes[i]; row[dash_columns.pixbuf] = Glib::wrap(sp_dash_to_pixbuf(dashes[i])); } + // add the custom one + Gtk::TreeModel::Row row = *(dash_store->append()); + row[dash_columns.dash] = dashes[np-1]; + row[dash_columns.pixbuf] = Glib::wrap(sp_text_to_pixbuf((char *)"Custom")); this->set_data("pattern", dashes[0]); } @@ -109,10 +116,10 @@ void SPDashSelector::init_dashes() { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); std::vector<Glib::ustring> dash_prefs = prefs->getAllDirs(_prefs_path); + int pos = 0; if (!dash_prefs.empty()) { - int pos = 0; SPStyle *style = sp_style_new (NULL); - dashes = g_new (double *, dash_prefs.size() + 1); + dashes = g_new (double *, dash_prefs.size() + 2); // +1 for custom slot, +1 for terminator slot for (std::vector<Glib::ustring>::iterator i = dash_prefs.begin(); i != dash_prefs.end(); ++i) { sp_style_read_from_prefs(style, *i); @@ -130,23 +137,36 @@ void SPDashSelector::init_dashes() { } pos += 1; } - dashes[pos] = NULL; - } else { - dashes = builtin_dashes; + } else { // This code may never execute - a new preferences.xml is created for a new user. Maybe if the user deletes dashes from preferences.xml? + dashes = g_new (double *, bd_len + 2); // +1 for custom slot, +1 for terminator slot + int i; + for(i=0;i<bd_len;i++) { + dashes[i] = builtin_dashes[i]; + } + pos = bd_len; } + // make a place to hold the custom dashes, up to 15 positions long (+ terminator) + dashes[pos] = g_new (double, 16); + double *d = dashes[pos]; + int i=0; + for(i=0;i<15;i++){ d[i]=i; } // have to put something in there, this is a pattern hopefully nobody would choose + d[15]=-1.0; + // final terminator + dashes[++pos] = NULL; } } void SPDashSelector::set_dash (int ndash, double *dash, double o) { - int pos = 0; + int pos = -1; // Allows custom patterns to remain unscathed by this. + int count = 0; // will hold the NULL terminator at the end of the dashes list if (ndash > 0) { double delta = 0.0; for (int i = 0; i < ndash; i++) delta += dash[i]; delta /= 1000.0; - for (int i = 0; dashes[i]; i++) { + for (int i = 0; dashes[i]; i++,count++) { double *pattern = dashes[i]; int np = 0; while (pattern[np] >= 0.0) @@ -154,6 +174,7 @@ void SPDashSelector::set_dash (int ndash, double *dash, double o) if (np == ndash) { int j; for (j = 0; j < ndash; j++) { + if (!Geom::are_near(dash[j], pattern[j], delta)) break; } @@ -164,10 +185,27 @@ void SPDashSelector::set_dash (int ndash, double *dash, double o) } } } + else if(ndash==0) { + pos = 0; + } - this->set_data("pattern", dashes[pos]); - this->dash_combo.set_active(pos); - this->offset->set_value(o); + if(pos>=0){ + this->set_data("pattern", dashes[pos]); + this->dash_combo.set_active(pos); + this->offset->set_value(o); + } + else { // Hit a custom pattern in the SVG, write it into the combobox. + count--; // the one slot for custom patterns + double *d = dashes[count]; + int i=0; + for(i=0;i< (ndash > 15 ? 15 : ndash) ;i++) { + d[i]=dash[i]; + } // store the custom pattern + d[ndash]=-1.0; //terminate it + this->set_data("pattern", dashes[count]); + this->dash_combo.set_active(count); + this->offset->set_value(o); // what does this do???? + } } void SPDashSelector::get_dash(int *ndash, double **dash, double *off) @@ -227,6 +265,32 @@ GdkPixbuf* SPDashSelector::sp_dash_to_pixbuf(double *pattern) { return pixbuf; } +/** + * Fill a pixbuf with a text label using standard cairo drawing + */ +GdkPixbuf* SPDashSelector::sp_text_to_pixbuf(char *text) { + + cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, preview_width, preview_height); + cairo_t *ct = cairo_create(s); + + cairo_select_font_face (ct, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size (ct, 12.0); + cairo_set_source_rgb (ct, 0.0, 0.0, 0.0); + cairo_move_to (ct, 16.0, 13.0); + cairo_show_text (ct, text); + + cairo_stroke (ct); + + cairo_destroy(ct); + cairo_surface_flush(s); + + GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data( cairo_image_surface_get_data(s), + GDK_COLORSPACE_RGB, TRUE, 8, + preview_width, preview_height, cairo_image_surface_get_stride(s), + ink_cairo_pixbuf_cleanup, s); + convert_pixbuf_argb32_to_normal(pixbuf); + return pixbuf; +} void SPDashSelector::on_selection () { diff --git a/src/widgets/dash-selector.h b/src/widgets/dash-selector.h index faa667ac7..5b9ebfa2b 100644 --- a/src/widgets/dash-selector.h +++ b/src/widgets/dash-selector.h @@ -54,6 +54,11 @@ private: GdkPixbuf* sp_dash_to_pixbuf(double *pattern); /** + * Fill a pixbuf with text standard cairo drawing + */ + GdkPixbuf* sp_text_to_pixbuf(char *text); + + /** * Callback for combobox image renderer */ void prepareImageRenderer( Gtk::TreeModel::const_iterator const &row ); diff --git a/src/xml/repr-io.cpp b/src/xml/repr-io.cpp index af47779fe..579c7598c 100644 --- a/src/xml/repr-io.cpp +++ b/src/xml/repr-io.cpp @@ -100,6 +100,9 @@ public: int setFile( char const * filename ); + xmlDocPtr readXml(); + bool SystemCheck; // Checks for SYSTEM Entities + static int readCb( void * context, char * buffer, int len ); static int closeCb( void * context ); @@ -121,6 +124,7 @@ int XmlSource::setFile(char const *filename) { int retVal = -1; + this->SystemCheck = false; this->filename = filename; fp = Inkscape::IO::fopen_utf8name(filename, "r"); @@ -178,13 +182,53 @@ int XmlSource::setFile(char const *filename) return retVal; } +xmlDocPtr XmlSource::readXml() +{ + int parse_options = XML_PARSE_HUGE | XML_PARSE_RECOVER; + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool allowNetAccess = prefs->getBool("/options/externalresources/xml/allow_net_access", false); + if (!allowNetAccess) parse_options |= XML_PARSE_NONET; + + // Allow NOENT only if we're filtering out SYSTEM and PUBLIC entities + if (SystemCheck) parse_options |= XML_PARSE_NOENT; + + return xmlReadIO( readCb, closeCb, this, + filename, getEncoding(), parse_options); +} int XmlSource::readCb( void * context, char * buffer, int len ) { int retVal = -1; + if ( context ) { XmlSource* self = static_cast<XmlSource*>(context); retVal = self->read( buffer, len ); + + if(self->SystemCheck) { + GMatchInfo *info; + gint start, end; + + GRegex *regex = g_regex_new( + "<!ENTITY\\s+[^>\\s]+\\s+(SYSTEM|PUBLIC\\s+\"[^>\"]+\")\\s+\"[^>\"]+\"\\s*>", + G_REGEX_CASELESS, G_REGEX_MATCH_NEWLINE_ANY, NULL); + + // Check for SYSTEM or PUBLIC entities and kill them with spaces + // Note: g_regex_replace does not modify buffer in place, this + // logic is used instead because we can just blank out the offending + // charicters in the right place without hurting the length. + g_regex_match (regex, buffer, G_REGEX_MATCH_NEWLINE_ANY, &info); + + while (g_match_info_matches (info)) { + if (g_match_info_fetch_pos (info, 1, &start, &end)) { + for (int x=start; x<end; x++) + buffer[x] = 0x20; + } + g_match_info_next (info, NULL); + } + g_match_info_unref(info); + g_regex_unref(regex); + } } return retVal; } @@ -299,22 +343,21 @@ Document *sp_repr_read_file (const gchar * filename, const gchar *default_ns) XmlSource src; if ( (src.setFile(filename) == 0) ) { - int parse_options = XML_PARSE_HUGE; // do not use XML_PARSE_NOENT ! see bug lp:1025185 - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - bool allowNetAccess = prefs->getBool("/options/externalresources/xml/allow_net_access", false); - if (!allowNetAccess) { - parse_options |= XML_PARSE_NONET; + doc = src.readXml(); + rdoc = sp_repr_do_read( doc, default_ns ); + // For some reason, failed ns loading results in this + // We try a system check version of load with NOENT for adobe + if(rdoc && strcmp(rdoc->root()->name(), "ns:svg") == 0) { + xmlFreeDoc( doc ); + src.setFile(filename); + src.SystemCheck = true; + doc = src.readXml(); + rdoc = sp_repr_do_read( doc, default_ns ); } - doc = xmlReadIO( XmlSource::readCb, - XmlSource::closeCb, - &src, - localFilename, - src.getEncoding(), - parse_options); } } - rdoc = sp_repr_do_read( doc, default_ns ); + if ( doc ) { xmlFreeDoc( doc ); } @@ -947,15 +990,15 @@ void sp_repr_write_stream_element( Node * repr, Writer & out, // THIS DOESN'T APPEAR TO DO ANYTHING. Can it be commented out or deleted? { GQuark const href_key = g_quark_from_static_string("xlink:href"); - GQuark const absref_key = g_quark_from_static_string("sodipodi:absref"); + //GQuark const absref_key = g_quark_from_static_string("sodipodi:absref"); gchar const *xxHref = 0; - gchar const *xxAbsref = 0; + //gchar const *xxAbsref = 0; for ( List<AttributeRecord const> ai(attributes); ai; ++ai ) { if ( ai->key == href_key ) { xxHref = ai->value; - } else if ( ai->key == absref_key ) { - xxAbsref = ai->value; + //} else if ( ai->key == absref_key ) { + //xxAbsref = ai->value; } } |
