/** @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 <string.h>
//#include <signal.h>
//#include <errno.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 "display/canvas-bpath.h"
#include "sp-item.h"

//#include "glib.h"
//#include "gtk/gtk.h"

//#include "glibmm/i18n.h"
//#include "enums.h"
//#include "document.h"
#include "style.h"
//#include "sp-paint-server.h"
#include "inkscape-version.h"
#include "sp-root.h"

//#include "libnrtype/FontFactory.h"
//#include "libnrtype/font-instance.h"
//#include "libnrtype/font-style-to-pos.h"

#include "emf-win32-print.h"

#include "unit-constants.h"

//#include "extension/extension.h"
#include "extension/system.h"
#include "extension/print.h"

//#include "io/sys.h"

//#include "macros.h"

namespace Inkscape {
namespace Extension {
namespace Internal {

static float dwDPI = 2540;


PrintEmfWin32::PrintEmfWin32 (void):
    hdc(NULL),
    hbrush(NULL),
    hbrushOld(NULL),
    hpen(NULL),
    stroke_and_fill(false),
    fill_only(false),
    simple_shape(false)
{
}


PrintEmfWin32::~PrintEmfWin32 (void)
{
    if (hdc) {
        HENHMETAFILE metafile = CloseEnhMetaFile( hdc );
        if ( metafile ) {
            DeleteEnhMetaFile( metafile );
        }
        DeleteDC( hdc );
    }

    /* restore default signal handling for SIGPIPE */
#if !defined(_WIN32) && !defined(__WIN32__)
    (void) signal(SIGPIPE, SIG_DFL);
#endif
    return;
}


unsigned int
PrintEmfWin32::setup (Inkscape::Extension::Print * /*mod*/)
{
    return TRUE;
}


unsigned int
PrintEmfWin32::begin (Inkscape::Extension::Print *mod, SPDocument *doc)
{
    gchar const *utf8_fn = mod->get_param_string("destination");

    gsize bytesRead = 0;
    gsize bytesWritten = 0;
    GError* error = NULL;
    gchar *local_fn =
        g_filename_from_utf8( utf8_fn, -1,  &bytesRead,  &bytesWritten, &error );

    if (local_fn == NULL) {
        return 1;
    }

    CHAR *ansi_uri = (CHAR *) local_fn;
    gunichar2 *unicode_fn = g_utf8_to_utf16( local_fn, -1, NULL, NULL, NULL );
    WCHAR *unicode_uri = (WCHAR *) unicode_fn;

    // width and height in px
    _width = doc->getWidth();
    _height = doc->getHeight();

    bool pageBoundingBox;
    pageBoundingBox = mod->get_param_bool("pageBoundingBox");

    Geom::Rect d;
    if (pageBoundingBox) {
        d = Geom::Rect::from_xywh(0, 0, _width, _height);
    } else {
        SPItem* doc_item = doc->getRoot();
        Geom::OptRect bbox = doc_item->desktopVisualBounds();
        if (bbox) d = *bbox;
    }

    d *= Geom::Scale(IN_PER_PX);

    float dwInchesX = d.width();
    float dwInchesY = d.height();

    // dwInchesX x dwInchesY in .01mm units
    SetRect( &rc, 0, 0, (int) ceil(dwInchesX*2540), (int) ceil(dwInchesY*2540) );

    // Get a Reference DC
    HDC hScreenDC = GetDC( NULL );

    // Get the physical characteristics of the reference DC
    int PixelsX = GetDeviceCaps( hScreenDC, HORZRES );
    int PixelsY = GetDeviceCaps( hScreenDC, VERTRES );
    int MMX = GetDeviceCaps( hScreenDC, HORZSIZE );
    int MMY = GetDeviceCaps( hScreenDC, VERTSIZE );

    CHAR buff[1024];
    ZeroMemory(buff, sizeof(buff));
    snprintf(buff, sizeof(buff)-1, "Inkscape %s (%s)", Inkscape::version_string, __DATE__);
    INT len = strlen(buff);
    CHAR *p1 = strrchr(ansi_uri, '\\');
    CHAR *p2 = strrchr(ansi_uri, '/');
    CHAR *p = MAX(p1, p2);
    if (p)
        p++;
    else
        p = ansi_uri;
    snprintf(buff+len+1, sizeof(buff)-len-2, "%s", p);
    
    // Create the Metafile
    {
        WCHAR wbuff[1024];
        ZeroMemory(wbuff, sizeof(wbuff));
        MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, buff, sizeof(buff)/sizeof(buff[0]), wbuff, sizeof(wbuff)/sizeof(wbuff[0]));
        hdc = CreateEnhMetaFileW( hScreenDC, unicode_uri, &rc, wbuff );
    }

    // Release the reference DC
    ReleaseDC( NULL, hScreenDC );

    // Did we get a good metafile?
    if (hdc == NULL)
    {
        g_free(local_fn);
        g_free(unicode_fn);
        return 1;
    }

    // Anisotropic mapping mode
    SetMapMode( hdc, MM_ANISOTROPIC );

    // Set the Windows extent
    int windowextX = (int) ceil(dwInchesX*dwDPI);
    int windowextY = (int) ceil(dwInchesY*dwDPI);
    SetWindowExtEx( hdc, windowextX, windowextY, NULL );

    // Set the viewport extent to reflect
    // dwInchesX" x dwInchesY" in device units
    int viewportextX = (int)((float)dwInchesX*25.4f*(float)PixelsX/(float)MMX);
    int viewportextY = (int)((float)dwInchesY*25.4f*(float)PixelsY/(float)MMY);
    SetViewportExtEx( hdc, viewportextX, viewportextY, NULL );

    if (1) {
        snprintf(buff, sizeof(buff)-1, "Screen=%dx%dpx, %dx%dmm", PixelsX, PixelsY, MMX, MMY);
        GdiComment(hdc, strlen(buff), (BYTE*) buff);

        snprintf(buff, sizeof(buff)-1, "Drawing=%.1lfx%.1lfpx, %.1lfx%.1lfmm", _width, _height, dwInchesX * MM_PER_IN, dwInchesY * MM_PER_IN);
        GdiComment(hdc, strlen(buff), (BYTE*) buff);
    }

    SetRect( &rc, 0, 0, (int) ceil(dwInchesX*dwDPI), (int) ceil(dwInchesY*dwDPI) );

    g_free(local_fn);
    g_free(unicode_fn);

    m_tr_stack.push( Geom::Scale(1, -1) * Geom::Translate(0, doc->getHeight())); /// @fixme hardcoded doc2dt transform

    return 0;
}


unsigned int
PrintEmfWin32::finish (Inkscape::Extension::Print * /*mod*/)
{
    if (!hdc) return 0;

    flush_fill(); // flush any pending fills

    HENHMETAFILE metafile = CloseEnhMetaFile( hdc );
    if ( metafile ) {
        DeleteEnhMetaFile( metafile );
    }
    DeleteDC( hdc );

    hdc = NULL;

    return 0;
}


unsigned int
PrintEmfWin32::comment (Inkscape::Extension::Print * /*module*/,
                        const char * /*comment*/)
{
    if (!hdc) return 0;

    flush_fill(); // flush any pending fills

    return 0;
}


int
PrintEmfWin32::create_brush(SPStyle const *style)
{
    float rgb[3];

    if (style) {
        float opacity = SP_SCALE24_TO_FLOAT(style->fill_opacity.value);
        if (opacity <= 0.0)
            return 1;

        sp_color_get_rgb_floatv( &style->fill.value.color, rgb );
        hbrush = CreateSolidBrush( RGB(255*rgb[0], 255*rgb[1], 255*rgb[2]) );
        hbrushOld = (HBRUSH) SelectObject( hdc, hbrush );

        SetPolyFillMode( hdc,
                         style->fill_rule.computed == 0 ? WINDING :
                         style->fill_rule.computed == 2 ? ALTERNATE : ALTERNATE );
    } else { // if (!style)
        hbrush = CreateSolidBrush( RGB(255, 255, 255) );
        hbrushOld = (HBRUSH) SelectObject( hdc, hbrush );
        SetPolyFillMode( hdc, ALTERNATE );
    }

    return 0;
}


void
PrintEmfWin32::destroy_brush()
{
    SelectObject( hdc, hbrushOld );
    if (hbrush)
        DeleteObject( hbrush );
    hbrush = NULL;
    hbrushOld = NULL;
}


void
PrintEmfWin32::create_pen(SPStyle const *style, const Geom::Affine &transform)
{
    if (style) {
        float rgb[3];

        sp_color_get_rgb_floatv(&style->stroke.value.color, rgb);

        LOGBRUSH lb;
        ZeroMemory(&lb, sizeof(lb));
        lb.lbStyle = BS_SOLID;
        lb.lbColor = RGB( 255*rgb[0], 255*rgb[1], 255*rgb[2] );

        int linestyle = PS_SOLID;
        int linecap = 0;
        int linejoin = 0;
        DWORD n_dash = 0;
        DWORD *dash = NULL;

        using Geom::X;
        using Geom::Y;

        Geom::Point zero(0, 0);
        Geom::Point one(1, 1);
        Geom::Point p0(zero * transform);
        Geom::Point p1(one * transform);
        Geom::Point p(p1 - p0);

        double scale = sqrt( (p[X]*p[X]) + (p[Y]*p[Y]) ) / sqrt(2);

        DWORD linewidth = MAX( 1, (DWORD) (scale * style->stroke_width.computed * IN_PER_PX * dwDPI) );

        if (style->stroke_linecap.computed == 0) {
            linecap = PS_ENDCAP_FLAT;
        }
        else if (style->stroke_linecap.computed == 1) {
            linecap = PS_ENDCAP_ROUND;
        }
        else if (style->stroke_linecap.computed == 2) {
            linecap = PS_ENDCAP_SQUARE;
        }

        if (style->stroke_linejoin.computed == 0) {
            linejoin = PS_JOIN_MITER;
        }
        else if (style->stroke_linejoin.computed == 1) {
            linejoin = PS_JOIN_ROUND;
        }
        else if (style->stroke_linejoin.computed == 2) {
            linejoin = PS_JOIN_BEVEL;
        }

        if (style->stroke_dash.n_dash   &&
            style->stroke_dash.dash       )
        {
            int i = 0;
            while (linestyle != PS_USERSTYLE &&
                   (i < style->stroke_dash.n_dash)) {
                if (style->stroke_dash.dash[i] > 0.00000001)
                    linestyle = PS_USERSTYLE;
                i++;
            }

            if (linestyle == PS_USERSTYLE) {
                n_dash = style->stroke_dash.n_dash;
                dash = new DWORD[n_dash];
                for (i = 0; i < style->stroke_dash.n_dash; i++) {
                    dash[i] = (DWORD) (style->stroke_dash.dash[i] * IN_PER_PX * dwDPI);
                }
            }
        }

        hpen = ExtCreatePen(
            PS_GEOMETRIC | linestyle | linecap | linejoin,
            linewidth,
            &lb,
            n_dash,
            dash );

        if ( !hpen && linestyle == PS_USERSTYLE ) {
            hpen = ExtCreatePen(
                PS_GEOMETRIC | PS_SOLID | linecap | linejoin,
                linewidth,
                &lb,
                0,
                NULL );
        }

        if ( !hpen ) {
            hpen = CreatePen(
                PS_SOLID,
                linewidth,
                lb.lbColor );
        }

        hpenOld = (HPEN) SelectObject( hdc, hpen );

        if (linejoin == PS_JOIN_MITER) {
            float oldmiterlimit;
            float miterlimit = style->stroke_miterlimit.value;

            miterlimit = miterlimit * 10.0 / 4.0;
            if (miterlimit < 1)
                miterlimit = 10.0;

            miterlimit = miterlimit * IN_PER_PX * dwDPI;

            SetMiterLimit(
                hdc,
                miterlimit,
                &oldmiterlimit );
        }

        if (n_dash) {
            delete[] dash;
        }
    }
    else { // if (!style)
        hpen = CreatePen( PS_SOLID, 1, RGB(0, 0, 0) );
        hpenOld = (HPEN) SelectObject( hdc, hpen );
    }
}


void
PrintEmfWin32::destroy_pen()
{
    SelectObject( hdc, hpenOld );
    if (hpen)
        DeleteObject( hpen );
    hpen = NULL;
}


void
PrintEmfWin32::flush_fill()
{
    if (!fill_pathv.empty()) {
        stroke_and_fill = false;
        fill_only = true;
        print_pathv(fill_pathv, fill_transform);
        fill_only = false;
        if (!simple_shape)
            FillPath( hdc );
        destroy_brush();
        fill_pathv.clear();
    }
}

unsigned int
PrintEmfWin32::bind(Inkscape::Extension::Print * /*mod*/, Geom::Affine const &transform, float /*opacity*/)
{   
    if (m_tr_stack.size()) {
        Geom::Affine tr_top = m_tr_stack.top();
        m_tr_stack.push(transform * tr_top);
    } else {
        m_tr_stack.push(transform);
    }

    return 1;
}

unsigned int
PrintEmfWin32::release(Inkscape::Extension::Print * /*mod*/)
{
    m_tr_stack.pop();
    return 1;
}

unsigned int
PrintEmfWin32::fill(Inkscape::Extension::Print * /*mod*/,
                    Geom::PathVector const &pathv, Geom::Affine const & /*transform*/, SPStyle const *style,
                    Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/)
{
    if (!hdc) return 0;

    Geom::Affine tf = m_tr_stack.top();

    flush_fill(); // flush any pending fills

    if (style->fill.isColor()) {
        if (create_brush(style))
            return 0;
    } else {
        // create_brush(NULL);
        return 0;
    }

    fill_pathv.clear();
    std::copy(pathv.begin(), pathv.end(), std::back_inserter(fill_pathv));
    fill_transform = tf;

    // postpone fill in case of stroke-and-fill

    return 0;
}


unsigned int
PrintEmfWin32::stroke (Inkscape::Extension::Print * /*mod*/,
                       Geom::PathVector const &pathv, const Geom::Affine &/*transform*/, const SPStyle *style,
                       Geom::OptRect const &/*pbox*/, Geom::OptRect const &/*dbox*/, Geom::OptRect const &/*bbox*/)
{
    if (!hdc) return 0;

    Geom::Affine tf = m_tr_stack.top();

    stroke_and_fill = ( pathv == fill_pathv );

    if (!stroke_and_fill) {
        flush_fill(); // flush any pending fills
    }

    if (style->stroke.isColor()) {
        create_pen(style, tf);
    } else {
        // create_pen(NULL, tf);
        return 0;
    }

    print_pathv(pathv, tf);

    if (stroke_and_fill) {
        if (!simple_shape)
            StrokeAndFillPath( hdc );
        destroy_brush();
        fill_pathv.clear();
    } else {
        if (!simple_shape)
            StrokePath( hdc );
    }

    destroy_pen();

    return 0;
}


bool
PrintEmfWin32::print_simple_shape(Geom::PathVector const &pathv, const Geom::Affine &transform)
{
    Geom::PathVector pv = pathv_to_linear_and_cubic_beziers( pathv * transform );
    
    int nodes = 0;
    int moves = 0;
    int lines = 0;
    int curves = 0;

    for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit)
    {
        moves++;
        nodes++;
        
        for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit)
        {
            nodes++;
            
            if ( is_straight_curve(*cit) ) {
                lines++;
            }
            else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
                cubic = cubic;
                curves++;
            }
        }
    }

    if (!nodes)
        return false;
    
    POINT *lpPoints = new POINT[moves + lines + curves*3];
    int i = 0;

    /**
     * For all Subpaths in the <path>
     */	     
    for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit)
    {
        using Geom::X;
        using Geom::Y;

        Geom::Point p0 = pit->initialPoint();

        p0[X] = (p0[X] * IN_PER_PX * dwDPI);
        p0[Y] = (p0[Y] * IN_PER_PX * dwDPI);
                
        LONG const x0 = (LONG) round(p0[X]);
        LONG const y0 = (LONG) round(rc.bottom-p0[Y]);

        lpPoints[i].x = x0;
        lpPoints[i].y = y0;
        i = i + 1;

        /**
         * For all segments in the subpath
         */
        for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit)
        {
            if ( is_straight_curve(*cit) )
            {
                //Geom::Point p0 = cit->initialPoint();
                Geom::Point p1 = cit->finalPoint();

                //p0[X] = (p0[X] * IN_PER_PX * dwDPI);
                p1[X] = (p1[X] * IN_PER_PX * dwDPI);
                //p0[Y] = (p0[Y] * IN_PER_PX * dwDPI);
                p1[Y] = (p1[Y] * IN_PER_PX * dwDPI);
                
                //LONG const x0 = (LONG) round(p0[X]);
                //LONG const y0 = (LONG) round(rc.bottom-p0[Y]);
                LONG const x1 = (LONG) round(p1[X]);
                LONG const y1 = (LONG) round(rc.bottom-p1[Y]);

                lpPoints[i].x = x1;
                lpPoints[i].y = y1;
                i = i + 1;
            }
            else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))
            {
                std::vector<Geom::Point> points = cubic->points();
                //Geom::Point p0 = points[0];
                Geom::Point p1 = points[1];
                Geom::Point p2 = points[2];
                Geom::Point p3 = points[3];

                //p0[X] = (p0[X] * IN_PER_PX * dwDPI);
                p1[X] = (p1[X] * IN_PER_PX * dwDPI);
                p2[X] = (p2[X] * IN_PER_PX * dwDPI);
                p3[X] = (p3[X] * IN_PER_PX * dwDPI);
                //p0[Y] = (p0[Y] * IN_PER_PX * dwDPI);
                p1[Y] = (p1[Y] * IN_PER_PX * dwDPI);
                p2[Y] = (p2[Y] * IN_PER_PX * dwDPI);
                p3[Y] = (p3[Y] * IN_PER_PX * dwDPI);
                
                //LONG const x0 = (LONG) round(p0[X]);
                //LONG const y0 = (LONG) round(rc.bottom-p0[Y]);
                LONG const x1 = (LONG) round(p1[X]);
                LONG const y1 = (LONG) round(rc.bottom-p1[Y]);
                LONG const x2 = (LONG) round(p2[X]);
                LONG const y2 = (LONG) round(rc.bottom-p2[Y]);
                LONG const x3 = (LONG) round(p3[X]);
                LONG const y3 = (LONG) round(rc.bottom-p3[Y]);

                POINT pt[3];
                pt[0].x = x1;
                pt[0].y = y1;
                pt[1].x = x2;
                pt[1].y = y2;
                pt[2].x = x3;
                pt[2].y = y3;

                lpPoints[i].x = x1;
                lpPoints[i].y = y1;
                lpPoints[i+1].x = x2;
                lpPoints[i+1].y = y2;
                lpPoints[i+2].x = x3;
                lpPoints[i+2].y = y3;
                i = i + 3;
            }
        }
    }

    bool done = false;
    bool closed = (lpPoints[0].x == lpPoints[i-1].x) && (lpPoints[0].y == lpPoints[i-1].y);
    bool polygon = false;
    bool rectangle = false;
    bool ellipse = false;
    
    if (moves == 1 && moves+lines == nodes && closed) {
        polygon = true;
//        if (nodes==5) {                             // disable due to LP Bug 407394
//            if (lpPoints[0].x == lpPoints[3].x && lpPoints[1].x == lpPoints[2].x &&
//                lpPoints[0].y == lpPoints[1].y && lpPoints[2].y == lpPoints[3].y)
//            {
//                rectangle = true;
//            }
//        }
    }
    else if (moves == 1 && nodes == 5 && moves+curves == nodes && closed) {
//        if (lpPoints[0].x == lpPoints[1].x && lpPoints[1].x == lpPoints[11].x &&
//            lpPoints[5].x == lpPoints[6].x && lpPoints[6].x == lpPoints[7].x &&
//            lpPoints[2].x == lpPoints[10].x && lpPoints[3].x == lpPoints[9].x && lpPoints[4].x == lpPoints[8].x &&
//            lpPoints[2].y == lpPoints[3].y && lpPoints[3].y == lpPoints[4].y &&
//            lpPoints[8].y == lpPoints[9].y && lpPoints[9].y == lpPoints[10].y &&
//            lpPoints[5].y == lpPoints[1].y && lpPoints[6].y == lpPoints[0].y && lpPoints[7].y == lpPoints[11].y)
//        {                                           // disable due to LP Bug 407394
//            ellipse = true;
//        }
    }

    if (polygon || ellipse) {
        HPEN hpenTmp = NULL;
        HPEN hpenOld = NULL;
        HBRUSH hbrushTmp = NULL;
        HBRUSH hbrushOld = NULL;

        if (!stroke_and_fill) {
            if (fill_only) {
                hpenTmp = (HPEN) GetStockObject(NULL_PEN);
                hpenOld = (HPEN) SelectObject( hdc, hpenTmp );
            }
            else { // if (stroke_only)
                hbrushTmp = (HBRUSH) GetStockObject(NULL_BRUSH);
                hbrushOld = (HBRUSH) SelectObject( hdc, hbrushTmp );
            }
        }

        if (polygon) {
            if (rectangle)
                Rectangle( hdc, lpPoints[0].x, lpPoints[0].y, lpPoints[2].x, lpPoints[2].y );
            else
                Polygon( hdc, lpPoints, nodes );
        }
        else if (ellipse) {
            Ellipse( hdc, lpPoints[6].x, lpPoints[3].y, lpPoints[0].x, lpPoints[9].y);
        }
        
        done = true;

        if (hpenOld)
            SelectObject( hdc, hpenOld );
        if (hpenTmp)
            DeleteObject( hpenTmp );
        if (hbrushOld)
            SelectObject( hdc, hbrushOld );
        if (hbrushTmp)
            DeleteObject( hbrushTmp );
    }

    delete[] lpPoints;
    
    return done;
}

unsigned int
PrintEmfWin32::print_pathv(Geom::PathVector const &pathv, const Geom::Affine &transform)
{
    simple_shape = print_simple_shape(pathv, transform);

    if (simple_shape)
        return TRUE;

    Geom::PathVector pv = pathv_to_linear_and_cubic_beziers( pathv * transform );
    
    BeginPath( hdc );

    /**
     * For all Subpaths in the <path>
     */	     
    for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit)
    {
        using Geom::X;
        using Geom::Y;

        Geom::Point p0 = pit->initialPoint();

        p0[X] = (p0[X] * IN_PER_PX * dwDPI);
        p0[Y] = (p0[Y] * IN_PER_PX * dwDPI);
                
        LONG const x0 = (LONG) round(p0[X]);
        LONG const y0 = (LONG) round(rc.bottom-p0[Y]);

        MoveToEx( hdc, x0, y0, NULL );

        /**
         * For all segments in the subpath
         */
        for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit)
        {
            if ( is_straight_curve(*cit) )
            {
                //Geom::Point p0 = cit->initialPoint();
                Geom::Point p1 = cit->finalPoint();

                //p0[X] = (p0[X] * IN_PER_PX * dwDPI);
                p1[X] = (p1[X] * IN_PER_PX * dwDPI);
                //p0[Y] = (p0[Y] * IN_PER_PX * dwDPI);
                p1[Y] = (p1[Y] * IN_PER_PX * dwDPI);
                
                //LONG const x0 = (LONG) round(p0[X]);
                //LONG const y0 = (LONG) round(rc.bottom-p0[Y]);
                LONG const x1 = (LONG) round(p1[X]);
                LONG const y1 = (LONG) round(rc.bottom-p1[Y]);

                LineTo( hdc, x1, y1 );
            }
            else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))
            {
                std::vector<Geom::Point> points = cubic->points();
                //Geom::Point p0 = points[0];
                Geom::Point p1 = points[1];
                Geom::Point p2 = points[2];
                Geom::Point p3 = points[3];

                //p0[X] = (p0[X] * IN_PER_PX * dwDPI);
                p1[X] = (p1[X] * IN_PER_PX * dwDPI);
                p2[X] = (p2[X] * IN_PER_PX * dwDPI);
                p3[X] = (p3[X] * IN_PER_PX * dwDPI);
                //p0[Y] = (p0[Y] * IN_PER_PX * dwDPI);
                p1[Y] = (p1[Y] * IN_PER_PX * dwDPI);
                p2[Y] = (p2[Y] * IN_PER_PX * dwDPI);
                p3[Y] = (p3[Y] * IN_PER_PX * dwDPI);
                
                //LONG const x0 = (LONG) round(p0[X]);
                //LONG const y0 = (LONG) round(rc.bottom-p0[Y]);
                LONG const x1 = (LONG) round(p1[X]);
                LONG const y1 = (LONG) round(rc.bottom-p1[Y]);
                LONG const x2 = (LONG) round(p2[X]);
                LONG const y2 = (LONG) round(rc.bottom-p2[Y]);
                LONG const x3 = (LONG) round(p3[X]);
                LONG const y3 = (LONG) round(rc.bottom-p3[Y]);

                POINT pt[3];
                pt[0].x = x1;
                pt[0].y = y1;
                pt[1].x = x2;
                pt[1].y = y2;
                pt[2].x = x3;
                pt[2].y = y3;

                PolyBezierTo( hdc, pt, 3 );
            }
            else
            {
                g_warning("logical error, because pathv_to_linear_and_cubic_beziers was used");
            }
        }

        if (pit->end_default() == pit->end_closed()) {
            CloseFigure( hdc );
        }
    }

    EndPath( hdc );

    return TRUE;
}


bool
PrintEmfWin32::textToPath(Inkscape::Extension::Print * ext)
{
    return ext->get_param_bool("textToPath");
}

unsigned int
PrintEmfWin32::text(Inkscape::Extension::Print * /*mod*/, char const *text, Geom::Point const &p,
                    SPStyle const *const style)
{
    if (!hdc) return 0;

    HFONT hfont = NULL;
    Geom::Affine tf = m_tr_stack.top();
    double rot = 1800.0*std::atan2(tf[1], tf[0])/M_PI;	// 0.1 degree rotation
    
#ifdef USE_PANGO_WIN32
/*
    font_instance *tf = (font_factory::Default())->Face(style->text->font_family.value, font_style_to_pos(*style));
    if (tf) {
        LOGFONT *lf = pango_win32_font_logfont(tf->pFont);
        tf->Unref();
        hfont = CreateFontIndirect(lf);
        g_free(lf);
    }
*/
#endif

    if (!hfont) {
        LOGFONTW *lf = (LOGFONTW*)g_malloc(sizeof(LOGFONTW));
        g_assert(lf != NULL);
        
        lf->lfHeight = style->font_size.computed * IN_PER_PX * dwDPI;
        lf->lfWidth = 0;
        lf->lfEscapement = rot;
        lf->lfOrientation = rot;
        lf->lfWeight =
            style->font_weight.computed == SP_CSS_FONT_WEIGHT_100 ? FW_THIN :
            style->font_weight.computed == SP_CSS_FONT_WEIGHT_200 ? FW_EXTRALIGHT :
            style->font_weight.computed == SP_CSS_FONT_WEIGHT_300 ? FW_LIGHT :
            style->font_weight.computed == SP_CSS_FONT_WEIGHT_400 ? FW_NORMAL :
            style->font_weight.computed == SP_CSS_FONT_WEIGHT_500 ? FW_MEDIUM :
            style->font_weight.computed == SP_CSS_FONT_WEIGHT_600 ? FW_SEMIBOLD :
            style->font_weight.computed == SP_CSS_FONT_WEIGHT_700 ? FW_BOLD :
            style->font_weight.computed == SP_CSS_FONT_WEIGHT_800 ? FW_EXTRABOLD :
            style->font_weight.computed == SP_CSS_FONT_WEIGHT_900 ? FW_HEAVY :
            FW_NORMAL;
        lf->lfItalic = (style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC);
        lf->lfUnderline = style->text_decoration.underline;
        lf->lfStrikeOut = style->text_decoration.line_through;
        lf->lfCharSet = DEFAULT_CHARSET;
        lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
        lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
        lf->lfQuality = DEFAULT_QUALITY;
        lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
        
        gunichar2 *unicode_name = g_utf8_to_utf16( style->text->font_family.value, -1, NULL, NULL, NULL );
        wcsncpy(lf->lfFaceName, (wchar_t*) unicode_name, LF_FACESIZE-1);
        g_free(unicode_name);
        
        hfont = CreateFontIndirectW(lf);
        
        g_free(lf);
    }
    
    HFONT hfontOld = (HFONT) SelectObject(hdc, hfont);

    float rgb[3];
    sp_color_get_rgb_floatv( &style->fill.value.color, rgb );
    SetTextColor(hdc, RGB(255*rgb[0], 255*rgb[1], 255*rgb[2]));

    // Text alignment:
    //   - (x,y) coordinates received by this filter are those of the point where the text
    //     actually starts, and already takes into account the text object's alignment;
    //   - for this reason, the EMF text alignment must always be TA_BASELINE|TA_LEFT.
    SetTextAlign(hdc, TA_BASELINE | TA_LEFT);

    // Transparent text background
    SetBkMode(hdc, TRANSPARENT);

    p = p * tf;
    p[Geom::X] = (p[Geom::X] * IN_PER_PX * dwDPI);
    p[Geom::Y] = (p[Geom::Y] * IN_PER_PX * dwDPI);

    LONG const xpos = (LONG) round(p[Geom::X]);
    LONG const ypos = (LONG) round(rc.bottom-p[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)
{
    Inkscape::Extension::Extension * ext;

    /* EMF print */
    ext = 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;
}


}  /* 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 :
