#include "wxinc.h"

#include "XFont.h"
#include "xvtpdf.h"

#define RAD2DEG 57.29577951308
#define BEZIERK 0.55228475
#define PDF_DPI 720

IMPLEMENT_DYNAMIC_CLASS(TwxPDFDC, wxDC)

//-- Contructor and Destructor -------------------------------------------

TwxPDFDC::TwxPDFDC()
{
  wxASSERT(false); // Dummy constructor!
}

TwxPDFDC::TwxPDFDC (const wxPrintData& printData, const char* strFilename) 
        : m_PDFlib(NULL), m_p(NULL), m_fileName(strFilename)
{
  m_pageNumber = 0;
  m_topDown = true;
  m_clipping = false;
  m_pageopen = false;
  m_printData = printData;

  for(int i=0; i < PDF_IMG_CACHE_SIZE; i++)
  {
    m_filenames[i] = "";
    m_handles[i] = 0;
  }
  m_cacheidx = 0;
  
  m_PDFlib = PDF_new_dl(&m_p);
  m_ok = m_PDFlib != NULL;
  if (!m_ok)
    ::wxMessageBox(_("Cannot create PDFlib object (DLL not found?)"), "PDFlib", wxOK|wxICON_ERROR);
}

TwxPDFDC::~TwxPDFDC ()
{
  if (m_PDFlib != NULL)
  {
    PDF_delete_dl(m_PDFlib, m_p);
    m_PDFlib = NULL;
    m_p = NULL;
  }
}

//-- Status --------------------------------------------------------------

bool TwxPDFDC::Ok() const
{ return m_ok; }

//-- Document and Pages --------------------------------------------------

bool TwxPDFDC::StartDoc( const wxString& message )
{
  wxCHECK_MSG( m_ok, FALSE, wxT("invalid PDF dc") );

  if (m_PDFlib->PDF_begin_document(m_p, m_fileName, 0, "") == -1) 
  {
    wxLogError( _("Cannot open file for PDF printing!"));
    return m_ok = false;
  }

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    wxString strFontPath;
    wxChar* lpszFontPath = strFontPath.GetWriteBuf(_MAX_PATH);
    ::GetFontsFolder(lpszFontPath, _MAX_PATH);
    strFontPath.UngetWriteBuf();
    m_PDFlib->PDF_set_parameter(m_p, "SearchPath", strFontPath);
    
    const wxString strUser = wxGetUserId();
    m_PDFlib->PDF_set_info(m_p, "Author", strUser);
    m_PDFlib->PDF_set_info(m_p, "Creator", "Campo");
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
    m_ok = false;
  }

  m_pageNumber = 0;
  return m_ok = true;
}

void TwxPDFDC::EndDoc ()
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  m_clipping = false;

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_end_document(m_p, "");
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
    m_ok = false;
  }
}

void TwxPDFDC::StartPage()
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );
  if (m_pageopen)
    return;

  m_pageNumber++;
  SetAxisOrientation(true, false);

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    int size_x, size_y; DoGetSize(&size_x, &size_y);
    const double scale = 72.0 / PDF_DPI;
    m_PDFlib->PDF_begin_page_ext(m_p, size_x*scale, size_y*scale, "");
    m_PDFlib->PDF_scale(m_p, scale, scale);
    m_pageopen = true;
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
    m_ok = false;
  }

  SetBrush(*wxBLACK_BRUSH);
  SetPen(*wxBLACK_PEN);
  SetBackground(*wxWHITE_BRUSH);
  SetTextForeground(*wxBLACK);
}

void TwxPDFDC::EndPage()
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (!m_pageopen)
    return;

  if (m_clipping) 
    DestroyClippingRegion();

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_end_page_ext(m_p, "");
    m_pageopen = false;
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
    m_ok = false;
  }
}

//-- Graphics ------------------------------------------------------------

void TwxPDFDC::DoDrawPoint(wxCoord x, wxCoord y)
{
  DoDrawLine(x, y, x+1, y);
}

void TwxPDFDC::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (m_pen.GetStyle() == wxTRANSPARENT) 
    return;

  SetPen(m_pen);

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_moveto(m_p, x1, y1);
    m_PDFlib->PDF_lineto(m_p, x2, y2);
    m_PDFlib->PDF_stroke(m_p);
    CalcBoundingBox(x1, y1);
    CalcBoundingBox(x2, y2);
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::DoDrawLines (int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset)
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (m_pen.GetStyle() == wxTRANSPARENT) 
    return;

  if (n <= 0) 
    return;

  SetPen(m_pen);

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    int i;
    CalcBoundingBox(points[0].x+xoffset, points[0].y+yoffset);
    m_PDFlib->PDF_moveto(m_p, points[0].x+xoffset, points[0].y+yoffset);
    for (i=1;i<n;i++)
    {
      m_PDFlib->PDF_lineto(m_p, points[i].x+xoffset, points[i].y+yoffset);
      CalcBoundingBox(points[i].x+xoffset, points[i].y+yoffset);
    }
    m_PDFlib->PDF_stroke(m_p);
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::DoDrawPolygon (int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, int WXUNUSED(fillStyle))
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (n <= 0) 
    return;

  if (m_pen.GetStyle() == wxTRANSPARENT && m_brush.GetStyle() == wxTRANSPARENT) 
    return;

  if (m_pen.GetStyle () != wxTRANSPARENT)
    SetPen(m_pen);

  if (m_brush.GetStyle() != wxTRANSPARENT)
    SetBrush(m_brush);

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    CalcBoundingBox(points[0].x + xoffset, points[0].y + yoffset);
    m_PDFlib->PDF_moveto(m_p, points[0].x+xoffset, points[0].y+yoffset);
    for (int i=1;i<n;i++)
    {
      m_PDFlib->PDF_lineto(m_p, points[i].x+xoffset, points[i].y+yoffset);
      CalcBoundingBox(points[i].x+xoffset, points[i].y+yoffset);
    }
    m_PDFlib->PDF_closepath(m_p);

    if (m_pen.GetStyle () != wxTRANSPARENT && m_brush.GetStyle() != wxTRANSPARENT)
      m_PDFlib->PDF_fill_stroke(m_p);
    else
    {
      if (m_pen.GetStyle () != wxTRANSPARENT)
        m_PDFlib->PDF_stroke(m_p);
      else
        m_PDFlib->PDF_fill(m_p);
    }
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::DoDrawRectangle (wxCoord x, wxCoord y, wxCoord width, wxCoord height)
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (m_pen.GetStyle() == wxTRANSPARENT && m_brush.GetStyle() == wxTRANSPARENT) 
    return;

  if (m_pen.GetStyle () != wxTRANSPARENT)
    SetPen(m_pen);

  if (m_brush.GetStyle() != wxTRANSPARENT)
    SetBrush(m_brush);

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_rect(m_p, x, y + height, width, height);
    CalcBoundingBox( x, y );
    CalcBoundingBox( x + width, y + height );

    if (m_pen.GetStyle () != wxTRANSPARENT && m_brush.GetStyle() != wxTRANSPARENT)
      m_PDFlib->PDF_fill_stroke(m_p);
    else
    {
      if (m_pen.GetStyle () != wxTRANSPARENT)
        m_PDFlib->PDF_stroke(m_p);
      else
        m_PDFlib->PDF_fill(m_p);
    }
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }

}

void TwxPDFDC::DoDrawArc (wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord xc, wxCoord yc)
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (m_pen.GetStyle() == wxTRANSPARENT && m_brush.GetStyle() == wxTRANSPARENT) 
    return;

  if (m_pen.GetStyle () != wxTRANSPARENT)
    SetPen(m_pen);

  if (m_brush.GetStyle() != wxTRANSPARENT)
    SetBrush(m_brush);

  const wxCoord radius = (wxCoord)_hypot(x1 - xc, y1 - yc) / 2;
  double alpha1, alpha2;

  if (x1 == x2 && y1 == y2)
  {
      alpha1 = 0.0;
      alpha2 = 360.0;
  }
  else if (radius == 0.0)
  {
      alpha1 = alpha2 = 0.0;
  }
  else
  {
      alpha1 = (x1 - xc == 0) ?
          (y1 - yc < 0) ? 90.0 : -90.0 :
              -atan2(double(y1-yc), double(x1-xc)) * RAD2DEG;
      alpha2 = (x2 - xc == 0) ?
          (y2 - yc < 0) ? 90.0 : -90.0 :
              -atan2(double(y2-yc), double(x2-xc)) * RAD2DEG;
  }
  while (alpha1 <  0)   alpha1 += 360;
  while (alpha2 <= 0)   alpha2 += 360; // adjust angles to be between
  while (alpha1 > 360)  alpha1 -= 360; // 0 and 360 degree
  while (alpha2 > 360)  alpha2 -= 360;

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_moveto(m_p, xc, yc);
    m_PDFlib->PDF_arc(m_p, xc, yc, radius, alpha1, alpha2);
    m_PDFlib->PDF_closepath(m_p);

    CalcBoundingBox( xc-radius, yc-radius );
    CalcBoundingBox( xc+radius, yc+radius );

    if (m_pen.GetStyle () != wxTRANSPARENT && m_brush.GetStyle() != wxTRANSPARENT)
      m_PDFlib->PDF_fill_stroke(m_p);
    else
    {
      if (m_pen.GetStyle () != wxTRANSPARENT)
        m_PDFlib->PDF_stroke(m_p);
      else
        m_PDFlib->PDF_fill(m_p);
    }
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::DoDrawEllipse (wxCoord x, wxCoord y, wxCoord width, wxCoord height)
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (m_pen.GetStyle() == wxTRANSPARENT && m_brush.GetStyle() == wxTRANSPARENT) 
    return;

  if (m_pen.GetStyle () != wxTRANSPARENT)
    SetPen(m_pen);

  if (m_brush.GetStyle() != wxTRANSPARENT)
    SetBrush(m_brush);

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    width /= 2;
    height /= 2;
    x += width;
    y += height;
    m_PDFlib->PDF_moveto(m_p, x+width, y);
    m_PDFlib->PDF_curveto(m_p, x+width, y+BEZIERK*height, x+BEZIERK*width, y+height, x, y+height);
    m_PDFlib->PDF_curveto(m_p, x-BEZIERK*width, y+height, x-width, y+BEZIERK*height, x-width, y);
    m_PDFlib->PDF_curveto(m_p, x-width, y-BEZIERK*height, x-BEZIERK*width, y-height, x, y-height);
    m_PDFlib->PDF_curveto(m_p, x+BEZIERK*width, y-height, x+width, y-BEZIERK*height, x+width, y);
    m_PDFlib->PDF_closepath(m_p);
    CalcBoundingBox(x-width, y-height);
    CalcBoundingBox(x+width, y+height);

    if (m_pen.GetStyle () != wxTRANSPARENT && m_brush.GetStyle() != wxTRANSPARENT)
      m_PDFlib->PDF_fill_stroke(m_p);
    else
    {
      if (m_pen.GetStyle () != wxTRANSPARENT)
        m_PDFlib->PDF_stroke(m_p);
      else
        m_PDFlib->PDF_fill(m_p);
    }
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::DoDrawEllipticArc(wxCoord x,wxCoord y,wxCoord w,wxCoord h,double sa,double ea)
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (m_pen.GetStyle() == wxTRANSPARENT && m_brush.GetStyle() == wxTRANSPARENT) 
    return;

  if (m_pen.GetStyle () != wxTRANSPARENT)
    SetPen(m_pen);

  if (m_brush.GetStyle() != wxTRANSPARENT)
    SetBrush(m_brush);

  if (sa>=360 || sa<=-360) sa=sa-int(sa/360)*360;
  if (ea>=360 || ea<=-360) ea=ea-int(ea/360)*360;
  if (sa<0) sa+=360;
  if (ea<0) ea+=360;

  if (sa==ea)
  {
      DrawEllipse(x,y,w,h);
      return;
  }

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_save(m_p);
    m_PDFlib->PDF_moveto(m_p, x, y);
    m_PDFlib->PDF_scale(m_p, 1, h/w);
    m_PDFlib->PDF_arc(m_p, x, y, w, sa, ea);
    m_PDFlib->PDF_closepath(m_p);

    CalcBoundingBox( x ,y );
    CalcBoundingBox( x+w, y+h );

    if (m_pen.GetStyle () != wxTRANSPARENT && m_brush.GetStyle() != wxTRANSPARENT)
      m_PDFlib->PDF_fill_stroke(m_p);
    else
    {
      if (m_pen.GetStyle () != wxTRANSPARENT)
        m_PDFlib->PDF_stroke(m_p);
      else
        m_PDFlib->PDF_fill(m_p);
    }

    m_PDFlib->PDF_restore(m_p);
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::DoDrawRoundedRectangle (wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (m_pen.GetStyle() == wxTRANSPARENT && m_brush.GetStyle() == wxTRANSPARENT) 
    return;

  if (m_pen.GetStyle () != wxTRANSPARENT)
    SetPen(m_pen);

  if (m_brush.GetStyle() != wxTRANSPARENT)
    SetBrush(m_brush);

  if (radius < 0.0)
  {
    double smallest = 0.0;
    if (width < height)
    smallest = width;
    else
    smallest = height;
    radius =  (-radius * smallest);
  }

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_moveto(m_p, x, y+radius);
    m_PDFlib->PDF_lineto(m_p, x, y+height-radius);
    m_PDFlib->PDF_arc(m_p, x+radius, y+height-radius, radius, 180, 270);
    m_PDFlib->PDF_lineto(m_p, x+width-radius, y+height);
    m_PDFlib->PDF_arc(m_p, x+width-radius, y+height-radius, radius, 270, 360);
    m_PDFlib->PDF_lineto(m_p, x+width, y+radius);
    m_PDFlib->PDF_arc(m_p, x+width-radius, y+radius, radius, 0, 90);
    m_PDFlib->PDF_lineto(m_p, x+radius, y);
    m_PDFlib->PDF_arc(m_p, x+radius, y+radius, radius, 90, 180);
    m_PDFlib->PDF_closepath(m_p);

    CalcBoundingBox(x ,y);
    CalcBoundingBox(x+width, y+height);

    if (m_pen.GetStyle () != wxTRANSPARENT && m_brush.GetStyle() != wxTRANSPARENT)
      m_PDFlib->PDF_fill_stroke(m_p);
    else
    {
      if (m_pen.GetStyle () != wxTRANSPARENT)
        m_PDFlib->PDF_stroke(m_p);
      else
        m_PDFlib->PDF_fill(m_p);
    }
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::DoDrawSpline( wxList *points )
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  SetPen(m_pen);

  double a, b, c, d, x1, y1, x2, y2, x3, y3;
  wxPoint *p, *q;

  wxNode *node = points->GetFirst();
  p = (wxPoint *)node->GetData();
  x1 = p->x;
  y1 = p->y;

  node = node->GetNext();
  p = (wxPoint *)node->GetData();
  c = p->x;
  d = p->y;
  x3 = a = (double)(x1 + c) / 2;
  y3 = b = (double)(y1 + d) / 2;

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_moveto(m_p, x1, y1);
    m_PDFlib->PDF_lineto(m_p, x3, y3);
    CalcBoundingBox( (wxCoord)x1, (wxCoord)y1 );
    CalcBoundingBox( (wxCoord)x3, (wxCoord)y3 );

    while ((node = node->GetNext()) != NULL)
    {
      q = (wxPoint *)node->GetData();

      x1 = x3;
      y1 = y3;
      x2 = c;
      y2 = d;
      c = q->x;
      d = q->y;
      x3 = (double)(x2 + c) / 2;
      y3 = (double)(y2 + d) / 2;

      m_PDFlib->PDF_curveto(m_p, x1, y1, x2, y2, x3, y3);
      CalcBoundingBox( (wxCoord)x1, (wxCoord)y1 );
      CalcBoundingBox( (wxCoord)x3, (wxCoord)y3 );
    }

    m_PDFlib->PDF_lineto(m_p, c, d); 
    m_PDFlib->PDF_stroke(m_p);
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

//-- Images and Bitmaps --------------------------------------------------

void TwxPDFDC::DoDrawIcon( const wxIcon& icon, wxCoord x, wxCoord y )
{
  DrawBitmap( icon, x, y, TRUE );
}

void TwxPDFDC::DoDrawBitmap( const wxBitmap& bitmap, wxCoord x, wxCoord y, bool WXUNUSED(useMask) )
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (!bitmap.Ok()) 
    return;

  int intImgHandle = GetBitmapHandle(bitmap);
  const wxCoord h = bitmap.GetHeight();

  if (intImgHandle<0)
    return;

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_fit_image(m_p, intImgHandle, x, y+h, "");
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::DoDrawImage( const wxString& name, const wxRect& dst )
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  const int intImgHandle = GetImageHandle(name);
  if (intImgHandle<0)
    return;

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    wxString strOptions;
    strOptions.Printf("boxsize={%d %d} fitmethod=entire", dst.GetWidth(), dst.GetHeight());
    m_PDFlib->PDF_fit_image(m_p, intImgHandle, dst.GetX(), dst.GetY()+dst.GetHeight(), strOptions);
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

int TwxPDFDC::GetBitmapHandle(const wxBitmap& bitmap) const
{
  wxString strFilename = wxGetTempFileName(wxT("tmpbmp"));
  ((wxBitmap&)bitmap).SaveFile(strFilename, wxBITMAP_TYPE_PNG);
  
  PDF_TRY_DL(m_PDFlib, m_p)
  {
    return m_PDFlib->PDF_load_image(m_p, "png", strFilename, 0, "");
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }

  return -1;
}

int TwxPDFDC::GetImageHandle(const wxString& name)
{
  int intFound = -1;
  int intNewHandle = -1;
  for (int i=PDF_IMG_CACHE_SIZE;i>0;i--)
  {
    int idx = (i + m_cacheidx) % PDF_IMG_CACHE_SIZE; 
    if (m_filenames[idx]!="" && m_filenames[idx]==name)
      intFound = idx;
    if (intFound>=0)
      break;
  }

  if (intFound<0 && (intNewHandle=m_PDFlib->PDF_load_image(m_p, "auto", name, 0, ""))>=0)
  {
    PDF_TRY_DL(m_PDFlib, m_p)
    {
      m_filenames[m_cacheidx] = name;
      m_handles[m_cacheidx] = intNewHandle;
      intFound = m_cacheidx;
      m_cacheidx++;
    }
    PDF_CATCH_DL(m_PDFlib, m_p) 
    {
      wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
    }
  }

  return m_handles[intFound];
}

bool TwxPDFDC::DoBlit( wxCoord xdest, wxCoord ydest,
                           wxCoord fwidth, wxCoord fheight,
                           wxDC *source,
                           wxCoord xsrc, wxCoord ysrc,
                           int rop, bool WXUNUSED(useMask), wxCoord WXUNUSED(xsrcMask), wxCoord WXUNUSED(ysrcMask) )
{
  wxCHECK_MSG( m_ok, FALSE, wxT("invalid PDF dc") );
  wxCHECK_MSG( source, FALSE, wxT("invalid source dc") );

  wxBitmap bitmap( (int)fwidth, (int)fheight );
  wxMemoryDC memDC;
  memDC.SelectObject(bitmap);
  memDC.Blit(0, 0, fwidth, fheight, source, xsrc, ysrc, rop);
  memDC.SelectObject(wxNullBitmap);

  DrawBitmap( bitmap, xdest, ydest );

  return TRUE;
}

//-- Text ----------------------------------------------------------------

void TwxPDFDC::DoDrawText( const wxString& text, wxCoord x, wxCoord y )
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  SetFont(m_font);
  SetFontColor(m_textForegroundColour);

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_set_text_pos(m_p, x, y + m_fontsize);
    m_PDFlib->PDF_show(m_p, text);
    wxCoord text_w = m_PDFlib->PDF_stringwidth(m_p, text, m_fontnr, m_fontsize);
    CalcBoundingBox( x, y );
    CalcBoundingBox( x + text_w , y + m_fontsize );
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }

  SetBrush(m_brush);
}

void TwxPDFDC::DoDrawRotatedText( const wxString& text, wxCoord x, wxCoord y, double angle )
{
  if (angle == 0.0)
  {
      DoDrawText(text, x, y);
      return;
  }
  
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  SetFont( m_font );
  
  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_save(m_p);
    m_PDFlib->PDF_set_text_pos(m_p, x, y);
    m_PDFlib->PDF_translate(m_p, x, y); 
    m_PDFlib->PDF_rotate(m_p, angle);
    m_PDFlib->PDF_show(m_p, text);
    m_PDFlib->PDF_restore(m_p);

    CalcBoundingBox( x, y );
    CalcBoundingBox( m_PDFlib->PDF_get_value(m_p, "textx", x), m_PDFlib->PDF_get_value(m_p, "texty", y) );
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

wxCoord TwxPDFDC::GetCharHeight() const
{
  if (m_font.Ok())
    return m_fontsize;
  else
    return (12 * PDF_DPI) / 72;
}

wxCoord TwxPDFDC::GetCharWidth() const
{
  return (wxCoord) (GetCharHeight() * 72.0 / 120.0);
}

void TwxPDFDC::DoGetTextExtent(const wxString& text,
                                     wxCoord *x, wxCoord *y,
                                     wxCoord *d, wxCoord *e,
                                     wxFont *theFont ) const
{
  PDF_TRY_DL(m_PDFlib, m_p)
  {
    if ((theFont==NULL&&m_font.Ok()) || (m_font.Ok()&&(*theFont==m_font)))
    {
      if (x)
        *x = m_PDFlib->PDF_stringwidth(m_p, text, m_fontnr, m_fontsize);
      if (y)
        *y = m_fontsize;
    }
    else
    {
      if (theFont!=NULL)
      {
        wxString strStyle = (theFont->GetWeight()==wxBOLD?"bold":"");
        strStyle += (theFont->GetStyle()==wxITALIC?"italic":"");
        wxString strFamily = theFont->GetFaceName();
        double fontsize = theFont->GetPointSize();
        int fontnr = m_PDFlib->PDF_load_font(m_p, strFamily, 0, "auto", "fontstyle {" + strStyle + "}");
        if (x)
          *x = m_PDFlib->PDF_stringwidth(m_p, text, fontnr, fontsize);
        if (y)
          *y = fontsize;
      }
    }

    if (d)
      *d = 0;
    if (e)
      *e = 0;
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

//-- Settings ------------------------------------------------------------

bool TwxPDFDC::IsValidFontFile(const char* szFontFile) const
{
  wxString strFile = szFontFile;
  strFile.MakeLower();
  return strFile.EndsWith(".ttf");
}

bool TwxPDFDC::GetFontFamily(const wxFont& font, wxString& file, wxString& family) const
{
  static wxFont m_LastFont;
  static wxString m_LastFile, m_strFamily;

  if (!m_LastFont.IsOk() ||
    font.GetPointSize()!= m_LastFont.GetPointSize() || // FAMILY NON DOVREBBE DIPENDERE DA POINTSIZE!
    font.GetWeight()   != m_LastFont.GetWeight() ||
    font.GetStyle()    != m_LastFont.GetStyle() ||
    font.GetFaceName() != m_LastFont.GetFaceName())
  {
    wxString strStyle = font.GetWeight() >= wxBOLD ? " Bold" : "";
    strStyle += font.GetStyle() == wxITALIC ? " Italic" : "";
    m_strFamily = font.GetFaceName();

    TCHAR szDisplayName[MAX_PATH], szFontFile[MAX_PATH];
    bool ok = GetFontFile(m_strFamily + strStyle, szDisplayName, MAX_PATH, szFontFile, MAX_PATH) != 0;
    if (ok)
      m_strFamily += strStyle;
    else
      ok = GetFontFile(m_strFamily, szDisplayName, MAX_PATH, szFontFile, MAX_PATH) != 0;

    m_LastFont = font;
    if (ok)
      m_LastFile = szFontFile;
    else
      m_LastFile = m_strFamily = wxEmptyString;

    if (!IsValidFontFile(szFontFile))
    {
      const wxFont swiss(font.GetPointSize(), font.GetFamily(), font.GetStyle(), 
                         font.GetWeight(), font.GetUnderlined());
      return GetFontFamily(swiss, file, family);
    }
  }
  
  file = m_LastFile;
  family = m_strFamily;
  return !file.IsEmpty();
}

void TwxPDFDC::SetFont( const wxFont& font )
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (!font.Ok())
    return;

  wxString strFontFile, strFamily;
  bool ok = GetFontFamily(font, strFontFile, strFamily);
  if (ok)
  {
    PDF_TRY_DL(m_PDFlib, m_p)
    {
      const wxString strScope = m_PDFlib->PDF_get_parameter(m_p, "scope", 0);
      if (strScope != "document") // Solitamente strScope = "page"
      {
        const wxString strParameter = strFamily + "=" + strFontFile;
        m_PDFlib->PDF_set_parameter(m_p, "FontOutline", strParameter);
        m_fontsize = font.GetPointSize();

        // maialata gigante da eliminare data dal fatto che a volte la GetPointSize() scazza
        // completamente a ritornare la dimensione (problema di wxWindoz) e ritorna un valore
        // molto molto minore di quello effettivo
        if (m_fontsize < 36)
          m_fontsize *= 9;

        m_font = font;
        m_fontnr = m_PDFlib->PDF_load_font(m_p, strFamily, 0, "host", "");

        m_PDFlib->PDF_setfont(m_p, m_fontnr, m_fontsize);
        m_PDFlib->PDF_set_parameter(m_p, "underline", m_font.GetUnderlined()?"true":"false");
      }
    }
    PDF_CATCH_DL(m_PDFlib, m_p) 
    {
      wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
    }
  }
  else
    wxLogError("SetFont(%s): Can't find font file", (const char*)font.GetFaceName());
}

void TwxPDFDC::SetPen( const wxPen& pen )
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (!pen.Ok()) 
    return;

  m_pen = pen;

  static const char *dotted = "dasharray {2 5} dashphase {2}";
  static const char *short_dashed = "dasharray {4 4} dashphase {2}";
  static const char *long_dashed = "dasharray {4 8} dashphase {2}";
  static const char *dotted_dashed = "dasharray {6 6 2 6} dashphase {4}";
  const char *pdfdash = (char *) NULL;

  const double redPDF   = m_pen.GetColour().Red()   / 255.0;
  const double greenPDF = m_pen.GetColour().Green() / 255.0;
  const double bluePDF  = m_pen.GetColour().Blue()  / 255.0;
  bool solid = false;
  switch (m_pen.GetStyle())
  {
  case wxDOT        : pdfdash = dotted; break;
  case wxSHORT_DASH : pdfdash = short_dashed; break;
  case wxLONG_DASH  : pdfdash = long_dashed; break;
  case wxDOT_DASH   : pdfdash = dotted_dashed; break;
  case wxSOLID      :
  case wxTRANSPARENT:  
  default           : solid = true; break;
  }

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    wxString strScope = m_PDFlib->PDF_get_parameter(m_p, "scope", 0);
    if (strScope != "document")
    {
      double dWidth = m_pen.GetWidth();
      if (dWidth <= 0) dWidth = 1;
      m_PDFlib->PDF_setlinewidth(m_p, dWidth);
      if (solid)
        m_PDFlib->PDF_setdash(m_p, 0, 0);
      else
        m_PDFlib->PDF_setdashpattern(m_p, pdfdash);
      m_PDFlib->PDF_setcolor(m_p, "stroke", "rgb", redPDF, greenPDF, bluePDF, 0);
    }
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::SetBrush( const wxBrush& brush )
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (!brush.Ok()) 
    return;

  m_brush = brush;

  unsigned char red = m_brush.GetColour().Red();
  unsigned char blue = m_brush.GetColour().Blue();
  unsigned char green = m_brush.GetColour().Green();

  double redPDF = (double)(red) / 255.0;
  double bluePDF = (double)(blue) / 255.0;
  double greenPDF = (double)(green) / 255.0;

  int intPatHandle;
  int intStyle = m_brush.GetStyle();
  bool solid = (intStyle==wxSOLID || intStyle==wxTRANSPARENT);
  wxSize step = GetPPI();
  step.x /= 180;
  step.y /= 180;

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    wxString strScope = m_PDFlib->PDF_get_parameter(m_p, "scope", 0);
    if (strScope != "document")
    {
      intPatHandle = m_PDFlib->PDF_begin_pattern(m_p, step.x, step.y , step.x, step.y, 2);
      m_PDFlib->PDF_setlinewidth(m_p, 0.2f);
      switch (intStyle)
      {
      case wxBDIAGONAL_HATCH:
        m_PDFlib->PDF_moveto(m_p, 0, 0);
        m_PDFlib->PDF_lineto(m_p, step.x, step.y);
        m_PDFlib->PDF_stroke(m_p);
        break;
      case wxCROSSDIAG_HATCH:
        m_PDFlib->PDF_moveto(m_p, 0, step.y);
        m_PDFlib->PDF_lineto(m_p, step.x, 0);
        m_PDFlib->PDF_stroke(m_p);
        m_PDFlib->PDF_moveto(m_p, 0, 0);
        m_PDFlib->PDF_lineto(m_p, step.x, step.y);
        m_PDFlib->PDF_stroke(m_p);
        break;
      case wxFDIAGONAL_HATCH:
        m_PDFlib->PDF_moveto(m_p, 0, step.y);
        m_PDFlib->PDF_lineto(m_p, step.x, 0);
        m_PDFlib->PDF_stroke(m_p);
        break;
      case wxCROSS_HATCH:
        m_PDFlib->PDF_moveto(m_p, 0, step.y/2);
        m_PDFlib->PDF_lineto(m_p, step.x, step.y/2);
        m_PDFlib->PDF_stroke(m_p);
        m_PDFlib->PDF_moveto(m_p, step.x/2, 0);
        m_PDFlib->PDF_lineto(m_p, step.x/2, step.y);
        m_PDFlib->PDF_stroke(m_p);
        break;
      case wxHORIZONTAL_HATCH:
        m_PDFlib->PDF_moveto(m_p, 0, step.y/2);
        m_PDFlib->PDF_lineto(m_p, step.x, step.y/2);
        m_PDFlib->PDF_stroke(m_p);
        break;
      case wxVERTICAL_HATCH:
        m_PDFlib->PDF_moveto(m_p, step.x/2, 0);
        m_PDFlib->PDF_lineto(m_p, step.x/2, step.y);
        m_PDFlib->PDF_stroke(m_p);
        break;
      default:
        break;
      }
      m_PDFlib->PDF_end_pattern(m_p);
    }
    m_PDFlib->PDF_setcolor(m_p, "fill", "rgb", redPDF, greenPDF, bluePDF, 0);
    if (!solid)
      m_PDFlib->PDF_setcolor(m_p, "fill", "pattern", intPatHandle, 0, 0, 0);
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::SetFontColor( const wxColor& color ) const
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (!color.Ok()) 
    return;

  unsigned char red = color.Red();
  unsigned char blue = color.Blue();
  unsigned char green = color.Green();

  double redPDF = (double)(red) / 255.0;
  double bluePDF = (double)(blue) / 255.0;
  double greenPDF = (double)(green) / 255.0;

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_setcolor(m_p, "fill", "rgb", redPDF, greenPDF, bluePDF, 0);
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::SetAxisOrientation( bool xLeftRight, bool yBottomUp )
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  m_topDown = !yBottomUp;
  //m_signX = (xLeftRight ? 1 : -1);
  //m_signY = (yBottomUp  ? 1 : -1);

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_set_parameter(m_p, "topdown", m_topDown?"true":"false");
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

void TwxPDFDC::SetDeviceOrigin(wxCoord x, wxCoord y)
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_translate(m_p, x, y);
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
}

//-- Page Size -----------------------------------------------------------

void TwxPDFDC::DoGetSizeMM(int* size_x, int* size_y) const
{
  const wxSize s = m_printData.GetPaperSize();
  if (m_printData.GetOrientation() == wxLANDSCAPE)
  {
    *size_x = s.y;
    *size_y = s.x;
  }
  else
  {
    *size_x = s.x;
    *size_y = s.y;
  }
}

void TwxPDFDC::DoGetSize(int *width, int *height) const
{
  DoGetSizeMM(width, height);
  *width = *width * (10 * PDF_DPI) / 254 ;
  *height = *height * (10 * PDF_DPI) / 254 ;
}

wxSize TwxPDFDC::GetPPI(void) const
{
  return wxSize(PDF_DPI, PDF_DPI);
}

//-- Unimplemented -------------------------------------------------------

void TwxPDFDC::SetBackground (const wxBrush& brush)
{
  // unimplemented
}

void TwxPDFDC::SetLogicalFunction (int WXUNUSED(function))
{
  // unimplemented
}

void TwxPDFDC::Clear()
{
  // unimplemented
}

bool TwxPDFDC::DoFloodFill (wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), const wxColour &WXUNUSED(col), int WXUNUSED(style))
{
  // unimplemented
  return FALSE;
}

bool TwxPDFDC::DoGetPixel (wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), wxColour * WXUNUSED(col)) const
{
  // unimplemented
  return FALSE;
}

void TwxPDFDC::DoCrossHair (wxCoord WXUNUSED(x), wxCoord WXUNUSED(y))
{
  // unimplemented
}

//-- Clipping ------------------------------------------------------------

void TwxPDFDC::DoSetClippingRegion (wxCoord x, wxCoord y, wxCoord w, wxCoord h)
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (m_clipping) 
    DestroyClippingRegion();

  wxDC::DoSetClippingRegion(x, y, w, h);

  m_clipping = true;

  PDF_TRY_DL(m_PDFlib, m_p)
  {
    m_PDFlib->PDF_save(m_p);
    m_PDFlib->PDF_moveto(m_p, x, y);
    m_PDFlib->PDF_lineto(m_p, x+w, y);
    m_PDFlib->PDF_lineto(m_p, x+w, y+h);
    m_PDFlib->PDF_lineto(m_p, x, y+h);
    m_PDFlib->PDF_closepath(m_p);
    m_PDFlib->PDF_clip(m_p);
  }
  PDF_CATCH_DL(m_PDFlib, m_p) 
  {
    wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
  }
  
}

void TwxPDFDC::DestroyClippingRegion()
{
  wxCHECK_RET( m_ok, wxT("invalid PDF dc") );

  if (m_clipping)
  {
    m_clipping = false;
    PDF_TRY_DL(m_PDFlib, m_p)
    {
      m_PDFlib->PDF_restore(m_p);
    }
    PDF_CATCH_DL(m_PDFlib, m_p) 
    {
      wxLogError("%s: %s", m_PDFlib->PDF_get_apiname(m_p), m_PDFlib->PDF_get_errmsg(m_p));
    }
  }
  wxDC::DestroyClippingRegion();
}