Logo Search packages:      
Sourcecode: abiword version File versions

gr_Win32Graphics.cpp

/* AbiWord
 * Copyright (C) 1998 AbiSource, Inc.
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

#define WIN32_LEAN_AND_MEAN
#define NOWINABLE
#define NOMETAFILE
#define NOSERVICE
#define NOIME
#define NOMCX
#include <windows.h>
#include <winspool.h>

#include "gr_Win32Graphics.h"
#include "gr_Win32Image.h"
#include <xap_Win32App.h>

#include <xap_Win32Res_Cursors.rc2>

#include "xap_Prefs.h"
#include "xap_Frame.h"
#include "xap_Dialog_Id.h"
#include "xap_Win32Dlg_Print.h"

#include "ut_debugmsg.h"
#include "ut_assert.h"
#include "ut_string.h"
#include "ut_Win32OS.h"

#ifdef __MINGW32__
#include <w32api.h>
#endif


#define LOG_WIN32_EXCPT(msg)                                                  \
{                                                                             \
    UT_String __s;                                                            \
    UT_String_sprintf(__s, "%s (%d): %s",__FILE__, __LINE__, msg);            \
      UT_DEBUGMSG(("%s",__s.c_str()));                                          \
    if(XAP_App::getApp()->getPrefs())                                         \
    {                                                                         \
          XAP_App::getApp()->getPrefs()->log("gr_Win32Graphics", __s.c_str());  \
    }                                                                         \
}

//#define GR_GRAPHICS_DEBUG   1

/*****************************************************************/

const char* GR_Graphics::findNearestFont(const char* pszFontFamily,
                                                             const char* pszFontStyle,
                                                             const char* pszFontVariant,
                                                             const char* pszFontWeight,
                                                             const char* pszFontStretch,
                                                             const char* pszFontSize)
{
      return pszFontFamily;
}

// A small helper class
class private_FontReverter
{
public:
      private_FontReverter(GR_Win32Graphics& gr, GR_Font* pOldFont)
      :     m_gr(gr),
            m_pOldFont(pOldFont)
      {
      }
      ~private_FontReverter()
      {
            if (m_pOldFont)
                  m_gr.setFont(m_pOldFont);
            // else, Oh joy, no old font to revert to... :-<
      }

private:
      private_FontReverter(const private_FontReverter&);    // no impl
      void operator=(const private_FontReverter&);          // no impl

      GR_Win32Graphics& m_gr;
      GR_Font*                m_pOldFont;
};

/*****************************************************************/

void GR_Win32Graphics::_constructorCommonCode(HDC hdc)
{
      UT_ASSERT(hdc);

      m_hdc = hdc;
      m_printHDC = NULL;
      m_nPrintLogPixelsY = 0;
      m_hwnd = 0;
      m_iLineWidth = 0; // default to a hairline
      m_bPrint = false;
      m_bStartPrint = false;
      m_bStartPage = false;
      m_pFont = NULL;
      m_pFontGUI = NULL;

      m_cs = GR_Graphics::GR_COLORSPACE_COLOR;
      m_cursor = GR_CURSOR_INVALID;

      m_clrXorPen = 0;
      m_hXorPen = 0;

      setCursor(GR_CURSOR_DEFAULT);

      m_remapBuffer = NULL;
      m_remapBufferSize = 0;
      m_remapIndices = NULL;

      m_eJoinStyle = JOIN_MITER;
      m_eCapStyle  = CAP_PROJECTING;
      m_eLineStyle = LINE_SOLID;

      setBrush((HBRUSH) GetStockObject(WHITE_BRUSH)); // Default brush

      m_nLogPixelsY = GetDeviceCaps(m_hdc, LOGPIXELSY);
      int nLogPixelsX = GetDeviceCaps(m_hdc, LOGPIXELSX);
      if(m_nLogPixelsY)
      {
            m_fXYRatio = (double)nLogPixelsX / (double) m_nLogPixelsY;
      }
      else
      {
            UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
            m_fXYRatio = 1;
      }
      
      m_hDevMode = NULL;
      
      
      typedef struct
      {
            HPEN hPen;
            int    nStyle;
            int  nWidth;
            int    nColour;
      
      } CACHE_PEN;
      
      m_pArPens = new GR_Win32Graphics::CACHE_PEN [_MAX_CACHE_PENS];
      memset (m_pArPens, 0, _MAX_CACHE_PENS*sizeof(CACHE_PEN));
      m_nArPenPos = 0;
}

GR_Win32Graphics::GR_Win32Graphics(HDC hdc, HWND hwnd, XAP_App * app)
{
      _constructorCommonCode(hdc);
      m_pApp = app;
      m_hwnd = hwnd;

      // init the print HDC with one for the default printer
      m_printHDC = UT_GetDefaultPrinterDC();

      if(m_printHDC)
      {
            m_nPrintLogPixelsY = GetDeviceCaps(getPrintDC(), LOGPIXELSY);
            int nLogPixelsX = GetDeviceCaps(getPrintDC(), LOGPIXELSX);
            if(m_nPrintLogPixelsY)
            {
                  m_fXYRatioPrint = (double)nLogPixelsX / (double) m_nPrintLogPixelsY;
            }
            else
            {
                  UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
                  m_fXYRatioPrint = 1;
            }
      }
}

void GR_Win32Graphics::setPrintDC(HDC dc)
{
      if(!m_bPrint)
      {
            // only do this for screen graphics
            m_printHDC = dc;

            // now make our views to rebuild themselves ...
            // (the win32 graphics currently cannot take advantate of the print dc, so we do nothing
      }
}


GR_Win32Graphics::GR_Win32Graphics(HDC hdc, const DOCINFO * pDocInfo, XAP_App * app, HGLOBAL hDevMode)
{
      _constructorCommonCode(hdc);
      m_pApp = app;
      m_bPrint = true;
      m_pDocInfo = pDocInfo;
      m_hDevMode = hDevMode;
}

GR_Win32Graphics::~GR_Win32Graphics()
{
      _destroyFonts ();

      UT_VECTOR_PURGEALL(UT_Rect*, m_vSaveRect);
            
      /* Release saved bitmaps */
      HBITMAP hBit;
      for (UT_uint32 i = 0; i < m_vSaveRectBuf.size (); i++)
      {
            hBit = (HBITMAP)m_vSaveRectBuf.getNthItem (i);
            DeleteObject(hBit);                 
      }

      if (m_hXorPen)
            DeleteObject(m_hXorPen);

      delete [] m_remapBuffer;
      delete [] m_remapIndices;
      
      /*Release created pens*/
      CACHE_PEN * pArPens =  m_pArPens;
      for (int n = 0; n<m_nArPenPos; n++, pArPens++)
            DeleteObject(pArPens->hPen);  

      delete m_pArPens;

      DELETEP(m_pFontGUI);

      if(m_printHDC)
            DeleteDC(m_printHDC);
}

bool GR_Win32Graphics::queryProperties(GR_Graphics::Properties gp) const
{
      switch (gp)
      {
      case DGP_SCREEN:
            return !m_bPrint;
      case DGP_PAPER:
            return m_bPrint;
      case DGP_OPAQUEOVERLAY:
            return true;
      default:
            UT_ASSERT(0);
            return false;
      }
}

GR_Win32Font * GR_Win32Graphics::_newFont(LOGFONT & lf, double fPoints, HDC hdc, HDC printHDC)
{
      return GR_Win32Font::newFont(lf, fPoints, hdc, printHDC);
}

GR_Font* GR_Win32Graphics::getGUIFont(void)
{
      if (!m_pFontGUI)
      {
            // lazily grab this (once)
            HFONT f = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
            LOGFONT lf;
            int iRes = GetObject(f, sizeof(LOGFONT), &lf);
            m_pFontGUI = _newFont(lf, 0, m_hdc, m_hdc);
            UT_ASSERT(m_pFontGUI);
            DeleteObject(f);
      }
      if(m_pFontGUI)
            m_pFontGUI->markGUIFont();
      
      return m_pFontGUI;
}

extern "C"
int CALLBACK
win32Internal_fontEnumProcedure(ENUMLOGFONT* pLogFont,
                                                NEWTEXTMETRICEX* pTextMetric,
                                                int Font_type,
                                                LPARAM lParam)
{
      LOGFONT *lf = (LOGFONT*)lParam;
      lf->lfCharSet = pLogFont->elfLogFont.lfCharSet;
      return 0;
}

GR_Font* GR_Win32Graphics::_findFont(const char* pszFontFamily,
                                                       const char* pszFontStyle,
                                                       const char* pszFontVariant,
                                                       const char* pszFontWeight,
                                                       const char* pszFontStretch,
                                                       const char* pszFontSize)
{     
      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::findFont %s %s %s\n", pszFontFamily, pszFontStyle, pszFontSize));     
      #endif
      
      LOGFONT lf = { 0 };
      HDC hPrintDC = m_printHDC ? m_printHDC : m_hdc;

      /*
            TODO we need to fill out the LOGFONT object such that
            we'll get the closest possible matching font.  For
            now, we're hard-coding a hack.
      */

      // we need to get the size in logpixels for the current DC, which
      // simply means to divide points by 72 and multiply by device Y resolution

      // See: http://support.microsoft.com/support/kb/articles/Q74/2/99.asp
      double fPointSize = UT_convertToPoints(pszFontSize);
      lf.lfHeight = (int)(-fPointSize * (double)GetDeviceCaps(m_hdc, LOGPIXELSY) / 72.0);       

      // TODO note that we don't support all those other ways of expressing weight.
      if (0 == UT_stricmp(pszFontWeight, "bold"))
            lf.lfWeight = 700;

      // TODO -- remove this block entirely, since oblique is no longer a valid style
      // We squash oblique into italic
      if (0 == UT_stricmp(pszFontStyle, "italic") || 0 == UT_stricmp(pszFontStyle, "oblique"))
            lf.lfItalic = TRUE;

      // TODO note that we currently think pszFontFamily is a single name, not a list!
      // TODO why don't these generic family names work?!?
      if (0 == UT_stricmp(pszFontFamily, "serif"))
            lf.lfPitchAndFamily = DEFAULT_PITCH | FF_ROMAN;
      else if (0 == UT_stricmp(pszFontFamily, "sans-serif"))
            lf.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS;
      else if (0 == UT_stricmp(pszFontFamily, "cursive"))
            lf.lfPitchAndFamily = DEFAULT_PITCH | FF_SCRIPT;
      else if (0 == UT_stricmp(pszFontFamily, "fantasy"))
            lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DECORATIVE;
      else if (0 == UT_stricmp(pszFontFamily, "monospace"))
            lf.lfPitchAndFamily = DEFAULT_PITCH | FF_MODERN;
      else
            strcpy(lf.lfFaceName, pszFontFamily);

      // Get character set value from the font itself
      LOGFONT enumlf = { 0 };
      enumlf.lfCharSet = DEFAULT_CHARSET;
      strcpy(enumlf.lfFaceName, lf.lfFaceName);
      EnumFontFamiliesEx(m_hdc, &enumlf,
            (FONTENUMPROC)win32Internal_fontEnumProcedure, (LPARAM)&lf, 0);

      lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;         // Choose only True Type fonts.
      lf.lfQuality = PROOF_QUALITY;

      return _newFont(lf, fPointSize, m_hdc, hPrintDC);
}

void GR_Win32Graphics::drawGlyph(UT_uint32 Char, UT_sint32 xoff, UT_sint32 yoff)
{
      UT_ASSERT(UT_TODO);
}

void GR_Win32Graphics::drawChar(UT_UCSChar Char, UT_sint32 xoff, UT_sint32 yoff)
{
      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::drawChar %c %u %u\n", Char, xoff, yoff));     
      #endif      
      
      xoff = (UT_sint32)((double)_tduX(xoff) * m_fXYRatio);
      yoff = _tduY(yoff);

#if 0 // this bypassesthe font handling mechanism
      HFONT hFont = m_pFont->getDisplayFont(this);
      SelectObject(m_hdc, hFont);
#else
      m_pFont->selectFontIntoDC(this, m_hdc);
#endif
      
      SetTextAlign(m_hdc, TA_LEFT | TA_TOP);
      SetBkMode(m_hdc, TRANSPARENT);            // TODO: remember and reset?

      UT_UCSChar currentChar = Char;
      //TODO this is a temporary hack -- need a way to handle true 32-bit chars
      UT_UCS2Char aChar = (UT_UCS2Char) currentChar;

      if(currentChar == 0x200B || currentChar == 0xFEFF
         /*|| currentChar == UCS_LIGATURE_PLACEHOLDER*/)
            return;
      // Windows NT and Windows 95 support the Unicode Font file.
      // All of the Unicode glyphs can be rendered if the glyph is found in
      // the font file. However, Windows 95 does  not support the Unicode
      // characters other than the characters for which the particular codepage
      // of the font file is defined.
      // Reference Microsoft knowledge base:
      // Q145754 - PRB ExtTextOutW or TextOutW Unicode Text Output Is Blank
      LOGFONT lf;
      int iRes = GetObject(m_pFont->getFontHandle(), sizeof(LOGFONT), &lf);
      UT_ASSERT(iRes);
      if (UT_IsWinNT() == false && lf.lfCharSet == SYMBOL_CHARSET)
      {
            // Symbol character handling for Win9x
            char str[sizeof(UT_UCS2Char)];

            int iConverted = WideCharToMultiByte(CP_ACP, NULL,
                  (LPCWSTR) &aChar, 1,
                  str, sizeof(str), NULL, NULL);
            ExtTextOutA(m_hdc, xoff, yoff, 0, NULL, str, iConverted, NULL);
      }
      else
      {
            // Unicode font and default character set handling for WinNT and Win9x
            ExtTextOutW(m_hdc, xoff, yoff, 0/*ETO_GLYPH_INDEX*/, NULL, (LPCWSTR) &aChar, 1, NULL);
      }
}

UT_uint16*  GR_Win32Graphics::_remapGlyphs(const UT_UCSChar* pChars, int iCharOffset, int &iLength)
{
      // TODO -- make this handle 32-bit chars properly
      if (iLength > (int)m_remapBufferSize)
      {
            delete [] m_remapBuffer;

            if(getApp()->theOSHasBidiSupport() != XAP_App::BIDI_SUPPORT_NONE)
            {
                  delete [] m_remapIndices;
                  m_remapIndices = new UT_UCS2Char[iLength];
            }

            m_remapBuffer = new UT_UCS2Char[iLength];
            m_remapBufferSize = iLength;
      }

    // Need to handle zero-width spaces correctly
      int i, j;
      for (i = 0, j = 0; i < iLength; ++i, ++j)
      {
            m_remapBuffer[j] = (UT_UCS2Char)pChars[iCharOffset + i];
            
            if(m_remapBuffer[j] == 0x200B || m_remapBuffer[j] == 0xFEFF
               /*|| m_remapBuffer[j] == UCS_LIGATURE_PLACEHOLDER*/)
                  j--;
      }

      iLength -= (i - j);

      return m_remapBuffer;
}

void GR_Win32Graphics::drawChars(const UT_UCSChar* pChars,
                                                 int iCharOffset, int iLengthOrig,
                                                 UT_sint32 xoff, UT_sint32 yoff,
                                                 int * pCharWidths)
{
      UT_ASSERT(pChars);

      if(!iLengthOrig)
      {
            // I do not want to assert here, as this makes debugging
            // virtually impossible
            UT_DEBUGMSG(("GR_Win32Graphics::drawChars: asked to draw zero chars !!!\n"));
            return;
      }
      
      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::drawChars %c %u %u\n", pChars, xoff, yoff));  
      #endif
      
      xoff = (UT_sint32)((double)_tduX(xoff) * m_fXYRatio);
      yoff = _tduY(yoff);
      int *pCharAdvances = NULL;
      
      // iLength can be modified by _remapGlyphs
      int iLength = iLengthOrig;
      HFONT hFont = m_pFont->getDisplayFont(this);
      SelectObject(m_hdc, hFont);
      SetTextAlign(m_hdc, TA_LEFT | TA_TOP);
      SetBkMode(m_hdc, TRANSPARENT);            // TODO: remember and reset?

      UT_uint16* currentChars = _remapGlyphs(pChars, iCharOffset, iLength);

      // Windows NT and Windows 95 support the Unicode Font file.
      // All of the Unicode glyphs can be rendered if the glyph is found in
      // the font file. However, Windows 95 does  not support the Unicode
      // characters other than the characters for which the particular codepage
      // of the font file is defined.
      // Reference Microsoft knowledge base:
      // Q145754 - PRB ExtTextOutW or TextOutW Unicode Text Output Is Blank
      LOGFONT lf;
      int iRes = GetObject(hFont, sizeof(LOGFONT), &lf);
      UT_ASSERT(iRes);

      if (UT_IsWinNT() == false && lf.lfCharSet == SYMBOL_CHARSET)
      {
            // Symbol character handling for Win9x
            char* str = new char[iLength * sizeof(UT_UCS2Char)];
            int iConverted = WideCharToMultiByte(CP_ACP, NULL,
                  (LPCWSTR) currentChars, iLength,
                  str, iLength * sizeof(UT_UCSChar), NULL, NULL);

            ExtTextOutA(m_hdc, xoff, yoff, 0, NULL, str, iConverted, NULL);
            delete [] str;
      }
      else
      {
            int duCharWidths [256];

            if (pCharWidths)
            {
                  if (iLengthOrig > (sizeof(duCharWidths)/sizeof(int)) )
                        pCharAdvances = new int [iLengthOrig];
                  else
                        pCharAdvances = duCharWidths;
                  UT_ASSERT(pCharAdvances != NULL);

                  // convert width into display units; since we have removed
                  // all 0x200B and 0xFEFF characters, we also have to
                  // remove their entires from the advances
                  UT_sint32 i,j;
                  UT_sint32 iwidth = 0;
                  UT_sint32 iadvance = 0;
                  UT_sint32 inextAdvance = 0;
                  for (i = 0, j = 0; i < iLengthOrig; i++)
                  {
                        if(! (pChars[iCharOffset+i] == 0x200B || pChars[iCharOffset+i] == 0xFEFF
                           /*|| pChars[iCharOffset+i] == UCS_LIGATURE_PLACEHOLDER*/ ) )
                        {
#if 1
                              // I rather stupidly changed this to the version
                              // below the #else, but this one produces much
                              // smaller rounding errors. Tomas, Dec 6, 2003
                              iwidth += pCharWidths[iCharOffset + i];
                              inextAdvance = (UT_sint32)((double)_tduX(iwidth) * m_fXYRatio);
                              pCharAdvances[j] = inextAdvance - iadvance;
                              iadvance = inextAdvance;
#else
                              pCharAdvances[j] = tdu(pCharWidths[iCharOffset + i]);
#endif
                              j++;
                        }
                  }
            }
            else
              {
                  pCharAdvances=NULL;
              }

            // Unicode font and default character set handling for WinNT and Win9x

            // The bidi aspect of the drawing is handled as follows:
            //     1. The OS has no bidi support: use simple ExTextOut
            //        call, since all the bidi processing has been already
            //        done in the layout engine
            //
            //     2. If the OS has bidi support: we will not use it for
            //        drawing in the main window, only let the system to
            //        handle processing for the GUI. This requires that we
            //        call GetCharacterPlacement function without
            //        requesting reordering and then feed the indices to
            //        ExTextOut (direct call to ExTextOut automatically reorders)
            if(getApp()->theOSHasBidiSupport() != XAP_App::BIDI_SUPPORT_NONE)
            {
                  UT_ASSERT(m_remapIndices);
                  GCP_RESULTSW gcpResult;
                  gcpResult.lStructSize = sizeof(GCP_RESULTS);
                  gcpResult.lpOutString = NULL;             // Output string
                  gcpResult.lpOrder = NULL;                       // Ordering indices
                  // we must set here lpDx to NULL so that
                  // GetCharacterPlacement does not change our values ...
                  gcpResult.lpDx = NULL;                  // Distances between character cells
                  gcpResult.lpCaretPos = NULL;              // Caret positions      
                  gcpResult.lpClass = NULL;                       // Character classifications
// w32api changed lpGlyphs from UINT * to LPWSTR to match MS PSDK in w32api v2.4
#if defined(__MINGW32__) && (__W32API_MAJOR_VERSION == 2 && __W32API_MINOR_VERSION < 4)
                  gcpResult.lpGlyphs = (UINT *) m_remapIndices;   // Character glyphs
#else             
                  gcpResult.lpGlyphs = (LPWSTR) m_remapIndices;   // Character glyphs
#endif                  
                  gcpResult.nGlyphs = m_remapBufferSize;  // Array size

                  DWORD placementResult;

                  if(getApp()->theOSHasBidiSupport() == XAP_App::BIDI_SUPPORT_GUI)
                        placementResult = GetCharacterPlacementW(m_hdc, (LPCWSTR) currentChars, iLength, 0, &gcpResult, 0);
                  else
                        placementResult = GetCharacterPlacementW(m_hdc, (LPCWSTR) currentChars, iLength, 0, &gcpResult, GCP_REORDER);
                  // now we set the character advances ...
                  gcpResult.lpDx = pCharAdvances;         // Distances between character cells
                  
                  if(placementResult)
                  {
                        ExtTextOutW(m_hdc, xoff, yoff, ETO_GLYPH_INDEX, NULL, (LPCWSTR) m_remapIndices, gcpResult.nGlyphs, pCharAdvances);
                  }
                  else
                  {
                        UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
                        goto simple_exttextout;
                  }
            }
            else
            {
simple_exttextout:
                  ExtTextOutW(m_hdc, xoff, yoff, 0, NULL, (LPCWSTR) currentChars, iLength, pCharAdvances);
            }

            if (pCharAdvances && (iLengthOrig > (sizeof(duCharWidths)/sizeof(int))) )
                  delete[] pCharAdvances;
      }
}

void GR_Win32Graphics::setFont(GR_Font* pFont)
{
      UT_ASSERT(pFont); // TODO should we allow pFont == NULL?

      GR_Win32Font* pWin32Font = static_cast<GR_Win32Font*>(pFont);

// TMN: This currently won't work since there is no way to tell
// GR_Win32Graphics to "drop" a font. If a user of this class:
// 1. Creates a new font on the heap.
// 2. Calls this setFont()
// 3. Deletes the font.
// 4. Creates a new font on the heap.
// 5. Calls this setFont()
// it is possible (and it has happened) that the pointers are the same,
// but it's two different fonts.
//

      // this should work though, the allocation number is unique, even
      // if the pointers are identical
      if (m_pFont == NULL || pFont->getAllocNumber() != m_iFontAllocNo
            || pWin32Font->getPrimaryHDC() != m_hdc)
      {
            m_pFont = pWin32Font;
            m_iFontAllocNo = pFont->getAllocNumber();
            m_pFont->selectFontIntoDC(this, m_hdc);
      }
}

UT_uint32 GR_Win32Graphics::getFontHeight(GR_Font * fnt)
{
      private_FontReverter janitor_(*this, m_pFont);

      setFont(fnt);

      return getFontHeight();
}

UT_uint32 GR_Win32Graphics::getFontHeight()
{
      UT_return_val_if_fail( m_pFont, 0 );

      return (UT_uint32)((m_pFont->getHeight(m_hdc, m_printHDC)) * getResolution() / getDeviceResolution());
}

UT_uint32 GR_Win32Graphics::getFontAscent(GR_Font* fnt)
{
      private_FontReverter janitor_(*this, m_pFont);

      setFont(fnt);

      return getFontAscent();
}

UT_uint32 GR_Win32Graphics::getFontAscent()
{
      UT_return_val_if_fail( m_pFont, 0 );
      return (UT_uint32)((m_pFont->getAscent(m_hdc, m_printHDC)) * getResolution() / getDeviceResolution());
}

UT_uint32 GR_Win32Graphics::getFontDescent(GR_Font* fnt)
{
      private_FontReverter janitor_(*this, m_pFont);

      setFont(fnt);

      return getFontDescent();
}

UT_uint32 GR_Win32Graphics::getFontDescent()
{
      UT_return_val_if_fail( m_pFont, 0 );
      return (UT_uint32)((m_pFont->getDescent(m_hdc, m_printHDC)) * getResolution() / getDeviceResolution());
}

void GR_Win32Graphics::getCoverage(UT_NumberVector& coverage)
{
      coverage.clear();
      coverage.push_back(' ');
      coverage.push_back((255 - ' '));
      
      //UT_ASSERT(UT_TODO);
}

UT_sint32 GR_Win32Graphics::measureUnRemappedChar(const UT_UCSChar c)
{
      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::measureUnRemappedChar\n")); 
      #endif

      UT_ASSERT(m_pFont);
      UT_sint32 iWidth = m_pFont->measureUnRemappedChar(c);

      if (iWidth==GR_CW_UNKNOWN || iWidth==GR_CW_ABSENT)
            return iWidth;
      
      iWidth *= (UT_sint32)getResolution();
      iWidth /= (UT_sint32)getDeviceResolution();
      iWidth = (UT_sint32)((double)iWidth * m_fXYRatio);
      return iWidth;    
}

UT_uint32 GR_Win32Graphics::getDeviceResolution(void) const
{     
      return m_nLogPixelsY; // NOTE: assumes square pixels
}

void GR_Win32Graphics::setColor(const UT_RGBColor& clr)
{
      m_curColor = clr;
      _setColor(RGB(clr.m_red, clr.m_grn, clr.m_blu));
}

void GR_Win32Graphics::getColor(UT_RGBColor& clr)
{
      clr = m_curColor;
}

void GR_Win32Graphics::_setColor(DWORD dwColor)
{
      m_clrCurrent = dwColor;
      SetTextColor(m_hdc, m_clrCurrent);
}

#ifdef GR_GRAPHICS_DEBUG
static nCacheHit = 0;
static nCacheFailed = 0;
#endif

void GR_Win32Graphics::drawLine(UT_sint32 x1, UT_sint32 y1, UT_sint32 x2, UT_sint32 y2)
{     
      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::drawLine %u %u %u %u\n", x1,  y1, x2,  y2));  
      #endif                        
      
      x1 = (UT_sint32)((double)_tduX(x1) * m_fXYRatio);
      x2 = (UT_sint32)((double)_tduX(x2) * m_fXYRatio);
      y1 = _tduY(y1);
      y2 = _tduY(y2);   

      int penStyle;
      HPEN hPen = NULL;
      bool bCached = false;
      UT_sint32 iLineWidth = (UT_sint32)((double)_tduR(m_iLineWidth) * m_fXYRatio);
      
      switch(m_eLineStyle)
      {
            case LINE_DOUBLE_DASH:
            case LINE_ON_OFF_DASH:      penStyle = PS_DASH;  break;
            case LINE_SOLID:            penStyle = PS_SOLID; break;
            case LINE_DOTTED:           penStyle = PS_DOT;   break;

            default:
                  UT_ASSERT(UT_NOT_IMPLEMENTED);
                  penStyle = PS_SOLID;
      }
      
      /*Look for a cached pen*/
      CACHE_PEN * pArPens =  m_pArPens;
      for (int n = 0; n<m_nArPenPos; n++, pArPens++)
      {
            if (pArPens->nStyle==penStyle &&
                  pArPens->nWidth==iLineWidth &&
                  pArPens->dwColour==m_clrCurrent)
                  {
                        hPen = pArPens->hPen;
                        bCached = true;
#ifdef GR_GRAPHICS_DEBUG
                        nCacheHit++;
#endif
                        break;
                  }           
      }
      
      /*If not cached, let's create it*/
      if (!hPen)
      {
            hPen = CreatePen(penStyle, iLineWidth, m_clrCurrent);

            if (m_nArPenPos<_MAX_CACHE_PENS)
            {                 
                  pArPens =  m_pArPens + m_nArPenPos;
                  pArPens->nStyle=penStyle;
                  pArPens->nWidth=iLineWidth;
                  pArPens->dwColour=m_clrCurrent;
                  pArPens->hPen = hPen;
                  bCached = true;
                  m_nArPenPos++;
#ifdef GR_GRAPHICS_DEBUG
                  nCacheFailed++;
#endif
            }
      }     

      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::drawline: cached elements %u cached hits %u  failed %u\n", m_nArPenPos, 
             nCacheHit, nCacheFailed));   
      #endif
      
      HPEN hOldPen = (HPEN) SelectObject(m_hdc, hPen);      

      MoveToEx(m_hdc, x1, y1, NULL);
      LineTo(m_hdc, x2, y2);

      (void) SelectObject(m_hdc, hOldPen);
      
      if (!bCached)
            DeleteObject(hPen);
}

void GR_Win32Graphics::setLineProperties(double iLineWidth,
                                                             JoinStyle inJoinStyle,
                                                             CapStyle inCapStyle,
                                                             LineStyle inLineStyle)
{
      m_eJoinStyle = inJoinStyle;
      m_eCapStyle  = inCapStyle;
      m_eLineStyle = inLineStyle;
      m_iLineWidth = static_cast<UT_sint32>(iLineWidth);
}

void GR_Win32Graphics::setLineWidth(UT_sint32 iLineWidth)
{
      m_iLineWidth = iLineWidth;
}

void GR_Win32Graphics::xorLine(UT_sint32 x1, UT_sint32 y1, UT_sint32 x2, UT_sint32 y2)
{
      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::xorLine %u %u %u %u\n", x1,  y1, x2,  y2));   
      #endif
      
      /*
        Note that we always use a pixel width of 1 for xorLine, since
        this should always be done to the screen.
      */

      x1 = (UT_sint32)((double)_tduX(x1) * m_fXYRatio);
      x2 = (UT_sint32)((double)_tduX(x2) * m_fXYRatio);
      y1 = _tduY(y1);
      y2 = _tduY(y2);

      if (m_clrCurrent != m_clrXorPen || !m_hXorPen)
      {
            if (m_hXorPen)
                  DeleteObject(m_hXorPen);
            m_hXorPen = CreatePen(PS_SOLID, (UT_sint32)((double)_tduR(m_iLineWidth) * m_fXYRatio), m_clrCurrent);
            m_clrXorPen = m_clrCurrent;
      }

      int iROP = SetROP2(m_hdc, R2_XORPEN);
      HPEN hOldPen = (HPEN)SelectObject(m_hdc, m_hXorPen);

      MoveToEx(m_hdc, x1, y1, NULL);
      LineTo(m_hdc, x2, y2);

      SelectObject(m_hdc, hOldPen);
      SetROP2(m_hdc, iROP);
}

void GR_Win32Graphics::polyLine(UT_Point * pts, UT_uint32 nPoints)
{
      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::polyLine %u\n", nPoints));  
      #endif
      
      HPEN hPen = CreatePen(PS_SOLID, (UT_sint32)((double)_tduR(m_iLineWidth) * m_fXYRatio), m_clrCurrent);
      HPEN hOldPen = (HPEN) SelectObject(m_hdc, hPen);

      POINT * points = (POINT *)UT_calloc(nPoints, sizeof(POINT));
      UT_ASSERT(points);

      for (UT_uint32 i = 0; i < nPoints; i++)
      {
            points[i].x = (UT_sint32)((double)_tduX(pts[i].x) * m_fXYRatio);
            points[i].y = _tduY(pts[i].y);
      }

      Polyline(m_hdc, points, nPoints);

      (void) SelectObject(m_hdc, hOldPen);
      DeleteObject(hPen);
      FREEP(points);
}

void GR_Win32Graphics::fillRect(const UT_RGBColor& c, UT_sint32 x, UT_sint32 y, UT_sint32 w, UT_sint32 h)
{
      
      RECT r;
      r.left = (UT_sint32)((double)_tduX(x) * m_fXYRatio);
      r.top = _tduY(y);
      r.right = _tduX(x + w);
      r.bottom =(UT_sint32)((double) _tduY(y + h) * m_fXYRatio);
      w=(UT_sint32)((double)_tduR(w) * m_fXYRatio);
      h=_tduR(h);

      COLORREF clr = RGB(c.m_red, c.m_grn, c.m_blu);

      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::fillRect %x %u %u %u %u\n",  clr, r.left, r.top, w, h));  
      #endif
            
      // This might look wierd (and I think it is), but it's MUCH faster.
      // CreateSolidBrush is dog slow.
      HDC hdc = m_hdc;
      const COLORREF cr = ::SetBkColor(hdc,  clr);
      ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &r, NULL, 0, NULL);
      ::SetBkColor(hdc, cr);
      setExposePending(false);
}

bool GR_Win32Graphics::startPrint(void)
{
      UT_ASSERT(m_bPrint);
      UT_ASSERT(!m_bStartPrint);
      m_bStartPrint = ( StartDoc(m_hdc,m_pDocInfo) > 0 );

      return m_bStartPrint;
}

bool GR_Win32Graphics::startPage(const char * szPageLabel, UT_uint32 pageNumber,
                                                      bool bPortrait, UT_uint32 iWidth, UT_uint32 iHeight)
{
      iWidth = (UT_sint32)((double)_tduR(iWidth) * m_fXYRatio);
      iHeight = _tduR(iHeight);
      if (m_bStartPage)
      {
            EndPage(m_hdc);
      }

      // Correct for Portrait vs Lanscape mode
      if (m_hDevMode)
      {
            DEVMODE *pDevMode = (DEVMODE*) GlobalLock(m_hDevMode);
            pDevMode->dmFields = DM_ORIENTATION;
            pDevMode->dmOrientation = (bPortrait) ? DMORIENT_PORTRAIT : DMORIENT_LANDSCAPE;
            GlobalUnlock(m_hDevMode);
            
            pDevMode = (DEVMODE*) GlobalLock(m_hDevMode);
            ResetDC(m_hdc, pDevMode);
            GlobalUnlock(m_hDevMode);
      }

      const int iRet = StartPage(m_hdc);

      m_bStartPage = iRet > 0;

      if (iRet > 0)
      {
            // PHYSICALOFFSETX returns offset of printable area from the left edge
            // of the physical printer paper. The value returned is in device units.
            // Since the current mapping mode is MM_TEXT, this code _should_ work.
            const POINT ptNew = {
                  -GetDeviceCaps(m_hdc, PHYSICALOFFSETX),
                  -GetDeviceCaps(m_hdc, PHYSICALOFFSETY)
            };
            SetViewportOrgEx(m_hdc, ptNew.x, ptNew.y, 0);
      }

      return m_bStartPage;
}

bool GR_Win32Graphics::endPrint(void)
{
      if (m_bStartPage)
            EndPage(m_hdc);

      return (EndDoc(m_hdc) > 0);
}

/**
 **  The View calls this when it receives a SetX/YScrollOffset
 **  call.  Move the contents of the window appropriately.  View
 **  will draw after you exit from this call.  View will only
 **  draw into the "exposed" area.
 **
 ** dx & dy are the change in x/y from the current scrolled position
 ** negative values indcate left/up movement, positive right/down movement
 **/
void GR_Win32Graphics::scroll(UT_sint32 dx, UT_sint32 dy)
{
      UT_sint32 oldDY = tdu(getPrevYOffset());
      UT_sint32 oldDX = (UT_sint32)((double)tdu(getPrevXOffset()) * m_fXYRatio);
      UT_sint32 newY = getPrevYOffset() + dy;
      UT_sint32 newX = getPrevXOffset() + dx;
      UT_sint32 ddx = -(UT_sint32)((double)(tdu(newX) - oldDX) * m_fXYRatio);
      UT_sint32 ddy = -(tdu(newY) - oldDY);
      setPrevYOffset(newY);
      setPrevXOffset(newX);
      if(ddx == 0 && ddy == 0)
      {
            return;
      }
      GR_CaretDisabler caretDisabler(getCaret());

      setExposePending(true);
      ScrollWindowEx(m_hwnd, ddx, ddy, NULL, NULL, NULL, 0, SW_INVALIDATE);
}

void GR_Win32Graphics::scroll(UT_sint32 x_dest, UT_sint32 y_dest,
                                            UT_sint32 x_src, UT_sint32 y_src,
                                            UT_sint32 width, UT_sint32 height)
{
      x_dest = (UT_sint32)((double)tdu(x_dest) * m_fXYRatio);
      y_dest = tdu(y_dest);
      x_src = (UT_sint32)((double)tdu(x_src) * m_fXYRatio);
      y_src = tdu(y_src);
      width = (UT_sint32)((double)tdu(width) * m_fXYRatio);
      height = tdu(height);
      RECT r;
      r.left = x_src;
      r.top = y_src;
      r.right = r.left + width;
      r.bottom = r.top + height;
      
      GR_CaretDisabler caretDisabler(getCaret());
      
      setExposePending(true);
      ScrollWindowEx(m_hwnd, (x_dest - x_src), (y_dest - y_src),
                           &r, NULL, NULL, NULL, SW_ERASE);
}

void GR_Win32Graphics::clearArea(UT_sint32 x, UT_sint32 y, UT_sint32 width, UT_sint32 height)
{
      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::clearArea %u %u %u %u\n",  x, y, width, height));   
      #endif
      
      x = (UT_sint32)((double)_tduX(x) * m_fXYRatio);
      y = _tduY(y);
      width = (UT_sint32)((double)_tduR(width) * m_fXYRatio);
      height = _tduR(height); 
      
      RECT r;
      r.left = x;
      r.top = y;
      r.right = r.left + width;
      r.bottom = r.top + height;

      FillRect(m_hdc, &r, m_hClearBrush);
}

void GR_Win32Graphics::invertRect(const UT_Rect* pRect)
{
      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::invertRect\n"));      
      #endif
      
      RECT r;

      r.left = (UT_sint32)((double)_tduX(pRect->left) * m_fXYRatio);
      r.top = _tduY(pRect->top);
      r.right = (UT_sint32)((double)_tduX(pRect->left + pRect->width) * m_fXYRatio);
      r.bottom = _tduY(pRect->top + pRect->height);

      InvertRect(m_hdc, &r);
}

void GR_Win32Graphics::setClipRect(const UT_Rect* pRect)
{
      // This causes a lot of drawing and screen refresh problems.
      // It can be removed from here without problems, but may
      // not work for other things.  For now leave code in place.
      // return;

      int res;
      m_pRect = pRect;
      if (pRect)
      {
            // set the clip rectangle
            HRGN hrgn = CreateRectRgn((UT_sint32)((double)_tduX(pRect->left) * m_fXYRatio),
                                                  _tduY(pRect->top),
                                                  (UT_sint32)((double)_tduX(pRect->left + pRect->width) * m_fXYRatio),
                                                  _tduY(pRect->top + pRect->height));
            UT_ASSERT(hrgn);

            res = SelectClipRgn(m_hdc, hrgn);

            DeleteObject(hrgn);
      }
      else        // stop clipping
            res = SelectClipRgn(m_hdc, NULL);

      UT_ASSERT(res != ERROR);
}

GR_Image* GR_Win32Graphics::createNewImage(const char* pszName, const UT_ByteBuf* pBB,
                                 UT_sint32 iDisplayWidth, UT_sint32 iDisplayHeight,
                                 GR_Image::GRType iType)
{
      iDisplayWidth = (UT_sint32)((double)_tduR(iDisplayWidth) * m_fXYRatio);
      iDisplayHeight = _tduR(iDisplayHeight);
      
      GR_Image* pImg = NULL;
      if (iType == GR_Image::GRT_Raster)
            pImg = new GR_Win32Image(pszName);
      else
            pImg = new GR_VectorImage(pszName);

      pImg->convertFromBuffer(pBB, iDisplayWidth,iDisplayHeight);

      return pImg;
}

void GR_Win32Graphics::drawImage(GR_Image* pImg, UT_sint32 xDest, UT_sint32 yDest)
{
      UT_ASSERT(pImg);
      
      xDest = (UT_sint32)((double)_tduX(xDest) * m_fXYRatio);
      yDest = _tduY(yDest);
      
      if (pImg->getType() != GR_Image::GRT_Raster)
      {
            pImg->render(this, xDest, yDest);
            return;
      }

      // When Printing yDest must be within Page Height for each
      // Page.
      if (queryProperties(GR_Graphics::DGP_PAPER))
      {
            UT_sint32 nPageHeight = GetDeviceCaps(m_hdc, PHYSICALHEIGHT);
            yDest = ( yDest % nPageHeight );
      }

      GR_Win32Image* pWin32Img = static_cast<GR_Win32Image*>(pImg);

      BITMAPINFO* pDIB = pWin32Img->getDIB();

      if (pDIB == NULL)
            return;
            
      UT_uint32 iSizeOfColorData = pDIB->bmiHeader.biClrUsed * sizeof(RGBQUAD);
      UT_Byte* pBits = ((unsigned char*) pDIB) + pDIB->bmiHeader.biSize + iSizeOfColorData;

      int iRes = StretchDIBits(m_hdc,
                                           xDest, yDest,
                                           pImg->getDisplayWidth(), pImg->getDisplayHeight(),
                                           0, 0,
                                           pDIB->bmiHeader.biWidth, pDIB->bmiHeader.biHeight,
                                           pBits, pDIB, DIB_RGB_COLORS, SRCCOPY);

      if (iRes == GDI_ERROR)
      {
            DWORD err = GetLastError();
            UT_DEBUGMSG(("StretchDIBits failed with err %d\n", err));
      }
}

HWND GR_Win32Graphics::getHwnd(void) const
{
      return m_hwnd;
}

void GR_Win32Graphics::setColorSpace(GR_Graphics::ColorSpace c)
{
      // TODO:  maybe?
      UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
}

GR_Graphics::ColorSpace GR_Win32Graphics::getColorSpace(void) const
{
      return m_cs;
}

void GR_Win32Graphics::setCursor(GR_Graphics::Cursor c)
{
      // set the cursor type, but wait for a WM_SETCURSOR
      // to do anything about it.
      m_cursor = c;
}

GR_Graphics::Cursor GR_Win32Graphics::getCursor(void) const
{
      return m_cursor;
}

void GR_Win32Graphics::handleSetCursorMessage(void)
{
      // deal with WM_SETCURSOR message.

      XAP_Win32App * pWin32App = static_cast<XAP_Win32App *>(m_pApp);
      HINSTANCE hinst = pWin32App->getInstance();
      LPCTSTR cursor_name;

      switch (m_cursor)
      {
      case GR_CURSOR_CROSSHAIR:     
            cursor_name = IDC_CROSS;
            hinst = NULL;
            break;

      default:
      {
            // this assert makes debugging virtuall impossible !!!
            static bool bDoneThisAlready = false;
            if(!bDoneThisAlready)
            {
                  bDoneThisAlready = true;
                  UT_ASSERT(UT_NOT_IMPLEMENTED);
            }
            
      }
            /*FALLTHRU*/
      case GR_CURSOR_DEFAULT:
            cursor_name = IDC_ARROW;            // top-left arrow
            hinst = NULL;
            break;

      case GR_CURSOR_LINK:
      case GR_CURSOR_GRAB:
#ifndef IDC_HAND
            cursor_name = MAKEINTRESOURCE(IDC_ABIHAND);
#else
            if (UT_IsWin95())
                  cursor_name = MAKEINTRESOURCE(IDC_ABIHAND);
            else
            {
                  cursor_name = IDC_HAND;
                  hinst = NULL;
            }
#endif
            break;

      case GR_CURSOR_EXCHANGE:
            cursor_name = MAKEINTRESOURCE(IDC_EXCHANGE);
            break;

      case GR_CURSOR_IBEAM:
            cursor_name = IDC_IBEAM;
            hinst = NULL;
            break;      

      case GR_CURSOR_RIGHTARROW:
            cursor_name = MAKEINTRESOURCE(IDC_ABIRIGHTARROW);
            break;

      case GR_CURSOR_LEFTARROW:
            cursor_name = IDC_ARROW;            // TODO change this
            hinst = NULL;
            break;

      case GR_CURSOR_DOWNARROW:
            cursor_name = MAKEINTRESOURCE(IDC_ABIDOWNARROW);
            break;

      case GR_CURSOR_IMAGE:
            cursor_name = IDC_SIZEALL;
            hinst = NULL;
            break;

      case GR_CURSOR_IMAGESIZE_NW:
      case GR_CURSOR_IMAGESIZE_SE:
            cursor_name = IDC_SIZENWSE;
            hinst = NULL;
            break;

      case GR_CURSOR_HLINE_DRAG:
      case GR_CURSOR_UPDOWN:
      case GR_CURSOR_IMAGESIZE_N:
      case GR_CURSOR_IMAGESIZE_S:
            cursor_name = IDC_SIZENS;
            hinst = NULL;
            break;

      case GR_CURSOR_IMAGESIZE_NE:
      case GR_CURSOR_IMAGESIZE_SW:
            cursor_name = IDC_SIZENESW;
            hinst = NULL;
            break;

      case GR_CURSOR_VLINE_DRAG:
      case GR_CURSOR_LEFTRIGHT:
      case GR_CURSOR_IMAGESIZE_E:
      case GR_CURSOR_IMAGESIZE_W:
            cursor_name = IDC_SIZEWE;
            hinst = NULL;
            break;

      case GR_CURSOR_WAIT:
            cursor_name = IDC_WAIT;
            hinst = NULL;
            break;
      }

      HCURSOR hCursor = LoadCursor(hinst,cursor_name); //TODO: Leaking resource
      if (hCursor != NULL)
            SetCursor(hCursor);
}

void GR_Win32Graphics::setColor3D(GR_Color3D c)
{
      UT_ASSERT(c < COUNT_3D_COLORS);
      _setColor(m_3dColors[c]);
}

void GR_Win32Graphics::init3dColors(void)
{
      m_3dColors[CLR3D_Foreground] = GetSysColor(COLOR_BTNTEXT);
      m_3dColors[CLR3D_Background] = GetSysColor(COLOR_3DFACE);
      m_3dColors[CLR3D_BevelUp]    = GetSysColor(COLOR_3DHIGHLIGHT);
      m_3dColors[CLR3D_BevelDown]  = GetSysColor(COLOR_3DSHADOW);
      m_3dColors[CLR3D_Highlight]  = GetSysColor(COLOR_WINDOW);
}

void GR_Win32Graphics::fillRect(GR_Color3D c, UT_sint32 x, UT_sint32 y, UT_sint32 w, UT_sint32 h)
{
      UT_ASSERT(c < COUNT_3D_COLORS);
      HBRUSH hBrush = CreateSolidBrush(m_3dColors[c]); 

      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::fillRect GR_Color3D  %x %u %u %u %u\n",  c, x, y, w, h)); 
      #endif
      
      RECT r;
      r.left = (UT_sint32)((double)_tduX(x) * m_fXYRatio);
      r.top = _tduY(y);
      r.right = (UT_sint32)((double)_tduX(x + w) * m_fXYRatio);
      r.bottom = _tduY(y + h);

      FillRect(m_hdc, &r, hBrush);
      DeleteObject(hBrush);
      setExposePending(false);
}

void GR_Win32Graphics::fillRect(GR_Color3D c, UT_Rect &r)
{
      UT_ASSERT(c < COUNT_3D_COLORS);
      fillRect(c,r.left,r.top,r.width,r.height);
      setExposePending(false);
}

//////////////////////////////////////////////////////////////////
// This is a static method in the GR_Font base class implemented
// in platform code.
//////////////////////////////////////////////////////////////////

void GR_Font::s_getGenericFontProperties(const char * szFontName,
                                                             FontFamilyEnum * pff,
                                                             FontPitchEnum * pfp,
                                                             bool * pbTrueType)
{
      // describe in generic terms the named font.

      // we borrow some code from GR_Win32Graphics::findFont()

      LOGFONT lf = { 0 };
      TEXTMETRIC tm = { 0 };

      // TODO i'm not sure why we special case these, but the other
      // TODO code did, so i'm going to here.

      if (UT_stricmp(szFontName, "serif") == 0)
            lf.lfPitchAndFamily = DEFAULT_PITCH | FF_ROMAN;
      else if (UT_stricmp(szFontName, "sans-serif") == 0)
            lf.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS;
      else if (UT_stricmp(szFontName, "cursive") == 0)
            lf.lfPitchAndFamily = DEFAULT_PITCH | FF_SCRIPT;
      else if (UT_stricmp(szFontName, "fantasy") == 0)
            lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DECORATIVE;
      else if (UT_stricmp(szFontName, "monospace") == 0)
            lf.lfPitchAndFamily = DEFAULT_PITCH | FF_MODERN;
      else
            strcpy(lf.lfFaceName, szFontName);

      // let the system create the font with the given name or
      // properties and then query it and see what was actually
      // created.  hopefully, this will let us more accurately
      // reflect what is being seen on screen.

      HFONT hFont = CreateFontIndirect(&lf);
      HDC hdc = CreateDC("DISPLAY",NULL,NULL,NULL);
      HFONT hFontOld = (HFONT) SelectObject(hdc,hFont);
      GetTextMetrics(hdc,&tm);
      SelectObject(hdc,hFontOld);
      DeleteObject(hFont);
      DeleteDC(hdc);

      BYTE xx = tm.tmPitchAndFamily;

      switch (xx & 0xf0)
      {
      default:
      case FF_DONTCARE: *pff = FF_Unknown;            break;
      case FF_ROMAN:          *pff = FF_Roman;        break;
      case FF_SWISS:          *pff = FF_Swiss;        break;
      case FF_MODERN:         *pff = FF_Modern;       break;
      case FF_SCRIPT:         *pff = FF_Script;       break;
      case FF_DECORATIVE:     *pff = FF_Decorative;   break;
      }

      if ((xx & TMPF_FIXED_PITCH) == TMPF_FIXED_PITCH)
            *pfp = FP_Variable;                       // yes these look backwards
      else                                            // but that is how windows
            *pfp = FP_Fixed;                    // defines the bits...

      *pbTrueType = ((xx & TMPF_TRUETYPE) == TMPF_TRUETYPE);

      return;
}

/*!
    hdc - the primary hdc on which we are expected to draw
    printHDC - the hdc for which to retrive metrics
*/
GR_Win32Font::GR_Win32Font(LOGFONT & lf, double fPoints, HDC hdc, HDC printHDC)
:     m_hdc(hdc),
      m_xhdc(0), // once all the processing is done, this is changed to printHDC
      m_yhdc(0), // once all the processing is done, this is changed to printHDC
      m_layoutFont (0),
      m_defaultCharWidth(0),
      m_tm(TEXTMETRIC()),
      m_bGUIFont(false),
      m_fPointSize(fPoints)
{
      m_iHeight = abs(lf.lfHeight);

      m_layoutFont = CreateFontIndirect(&lf); // this is what we see to start with

      if(!m_layoutFont)
      {
            LOG_WIN32_EXCPT("CreateFontIndirectFailed");
            return;
      }
      
      insertFontInCache (m_iHeight, m_layoutFont);

      HFONT printFont = m_layoutFont;
      
      if(hdc != printHDC)
      {
            int nLogPixelsY      = GetDeviceCaps(hdc, LOGPIXELSY);
            int nPrintLogPixelsY = GetDeviceCaps(printHDC, LOGPIXELSY);

            lf.lfHeight = MulDiv(lf.lfHeight, nPrintLogPixelsY, nLogPixelsY);
            printFont = CreateFontIndirect(&lf);
            
            if(!printFont)
            {
                  LOG_WIN32_EXCPT("CreateFontIndirectFailed");
                  return;
            }

            insertFontInCache(abs(lf.lfHeight), printFont);

      }
      
      //
      // TMN: We need to initialize 'this' to _something_, why we use the
      // screen DC. Note that this is a *bad hack* forced by bad design. :-(
      //
      if (!hdc || !printHDC)
      {
            // NOW what? We can't throw an exeption, and this object isn't
            // legal yet...
            LOG_WIN32_EXCPT("No DC")
            m_layoutFont = NULL;
            return;
      }
      else
      {
            // now we need to retrieve the y-axis metrics
            HFONT hOldFont = (HFONT)SelectObject(printHDC, printFont);
            if (hOldFont == (HFONT)GDI_ERROR)
            {
                  LOG_WIN32_EXCPT("Could not select font into DC.")
                  m_layoutFont = NULL;
                  return;
            }
            else
            {
                  // Setting the m_hashKey 
                  char lpFaceName[1000];
                  
                  GetTextFace(printHDC, 1000, lpFaceName );

                  _updateFontYMetrics(hdc, printHDC);
                  
                  UT_String_sprintf(m_hashKey, "%s-%d-%d-%d-%d-%d",
                                            lpFaceName,
                                            m_tm.tmHeight, m_tm.tmWeight, m_tm.tmItalic, m_tm.tmUnderlined,
                                            m_tm.tmStruckOut);

                  // now we measure the default character
                  // 
                  // when we are called, the char widths might not exist yet; we
                  // will force initialisation by a bogus call to
                  // getCharWidthFromCache(); by measuring the space, we will
                  // preload the entire Latin1 page
                  getCharWidthFromCache(' ');
                  
                  UINT d = m_tm.tmDefaultChar;

                  UT_return_if_fail(_getCharWidths());
                  _getCharWidths()->setCharWidthsOfRange(printHDC, d, d);
                  m_defaultCharWidth = getCharWidthFromCache(d);

                  // this is not good SelectObject() is very expensive, and chances are this
                  // call is unnecessary, but because of the design of the graphics class we
                  // have no way of knowing
                  SelectObject(printHDC, hOldFont);
            }

            m_xhdc = printHDC;
            m_yhdc = printHDC;
      }
}

GR_Win32Font * GR_Win32Font::newFont(LOGFONT &lf, double fPoints, HDC hdc, HDC printHDC)
{
      GR_Win32Font * f = new GR_Win32Font(lf, fPoints, hdc, printHDC);

      if(!f || !f->getFontHandle())
      {
            delete f;
            f = NULL;
      }

      return f;
}


GR_Win32Font::~GR_Win32Font()
{
      // We currently can't delete the font, since it might
      // be selected into a DC. Also, we don't keep track of previously
      // selected font [into the DC] why it's impossible to revert to that
      // one! :-<
      // But, let's try it like this...
      //
      // the GetObjectType call with 0 throws a first chance exception
      // (this whole thing is rather messy)

      DWORD dwObjType;
      bool bIsDC;

      if(m_hdc) 
      {
            dwObjType = GetObjectType((HGDIOBJ)m_hdc);
            bIsDC = dwObjType == OBJ_DC || dwObjType == OBJ_MEMDC;
      }

      for (UT_uint32 i = 0; i < m_allocFonts.getItemCount(); ++i)
        {
              allocFont *p = (allocFont *)m_allocFonts.getNthItem(i);

              if(m_hdc) 
              {
                    if (!bIsDC || p->hFont != (HFONT)::GetCurrentObject(m_hdc, OBJ_FONT))
                    {
                          DeleteObject(p->hFont);
                    }
              }

              delete p;
        }
}

/*!
    hdc -- handle to the primary DC on which we draw
    printHDC -- handle to the DC on which we want to measure the font (typically the printer)
                can be NULL (in which case hdc will be used

    NB: the caller must ensure that the corresponding HFONT is already selected onto the
    DC represented by printHDC (or hdc if NULL).
*/
void GR_Win32Font::_updateFontYMetrics(HDC hdc, HDC printHDC)
{
      // have to have at least the prinary DC
      UT_return_if_fail( hdc );
      
      // only do this if we have reason to believe the cached values are stale
      if(!printHDC)
      {
            printHDC = hdc;
      }

      // we have to remeasure if (a) the printer changed, or (b) the primary device changed
      if(printHDC != m_yhdc || hdc != m_hdc)
      {
            GetTextMetrics(printHDC,&m_tm);

            // now we have to scale the metrics down from the printHDC to primary dc
            // NB: we do not want to obtain the metrics directly for the primary dc
            // Windows is designed to get slightly bigger font for the screen than the
            // user asks for (MS say to improve readibility), and this causes us
            // non-WYSIWYG behaviour (basically our characters are wider on screen than on
            // the printer and our lines are further apart)
            int nLogPixelsY      = GetDeviceCaps(hdc, LOGPIXELSY);
            int nPrintLogPixelsY = GetDeviceCaps(printHDC, LOGPIXELSY);

            if(nLogPixelsY != nPrintLogPixelsY)
            {
                  m_tm.tmAscent  = MulDiv(m_tm.tmAscent, nLogPixelsY, nPrintLogPixelsY);
                  m_tm.tmDescent = MulDiv(m_tm.tmDescent, nLogPixelsY, nPrintLogPixelsY);
                  m_tm.tmHeight  = MulDiv(m_tm.tmHeight, nLogPixelsY, nPrintLogPixelsY);
            }

            // now remember what HDC these values are for
            m_yhdc = printHDC;
            m_hdc = hdc;
      }
}


/*! hdc - handle to device context for which the measurment is required */
UT_uint32 GR_Win32Font::getAscent(HDC hdc, HDC printHDC)
{
      if(!m_bGUIFont)
            _updateFontYMetrics(hdc, printHDC);
      return m_tm.tmAscent;
}

/*! hdc - handle to device context for which the measurment is required */
UT_uint32 GR_Win32Font::getDescent(HDC hdc, HDC printHDC)
{
      if(!m_bGUIFont)
            _updateFontYMetrics(hdc, printHDC);
      return m_tm.tmDescent;
}

/*! hdc - handle to device context for which the measurment is required */
UT_uint32 GR_Win32Font::getHeight(HDC hdc, HDC printHDC)
{
      if(!m_bGUIFont)
            _updateFontYMetrics(hdc, printHDC);
      return m_tm.tmHeight;
}


UT_sint32 GR_Win32Font::measureUnremappedCharForCache(UT_UCSChar cChar) const
{
#ifndef ABI_GRAPHICS_PLUGIN_NO_WIDTHS
      // first of all, handle 0-width spaces ...
      if(cChar == 0xFEFF || cChar == 0x200b || cChar == UCS_LIGATURE_PLACEHOLDER)
            return 0;

      // this function is only called when there is no value in the
      // cache; we will set not only the value for the character
      // required, but also for everything on the same char-width page
    UT_return_val_if_fail(_getCharWidths(),0);

      // calculate the limits of the 256-char page
      UT_UCS4Char base = (cChar & 0xffffff00);
      UT_UCS4Char limit = (cChar | 0x000000ff);
      HDC hdc = CreateDC("DISPLAY",NULL,NULL,NULL);
      SelectObject(hdc,m_layoutFont);
      _getCharWidths()->setCharWidthsOfRange(hdc, base, limit);
      DeleteDC(hdc);
      return _getCharWidths()->getWidth(cChar);
#else
      UT_return_val_if_fail(UT_NOT_IMPLEMENTED,0);
#endif
}

/*!
      Implements a GR_CharWidths.
      Override if you which to instanciate a subclass.
 */
GR_CharWidths* GR_Win32Font::newFontWidths(void) const
{
#ifndef ABI_GRAPHICS_PLUGIN_NO_WIDTHS
      return new GR_Win32CharWidths();
#else
      UT_return_val_if_fail(UT_NOT_IMPLEMENTED,NULL);
#endif
}

HFONT GR_Win32Font::getFontFromCache(UT_uint32 pixelsize, bool /*bIsLayout*/, UT_uint32 /*zoomPercentage*/) const
{
      UT_uint32 l = 0;
      UT_uint32 count = m_allocFonts.getItemCount();
      allocFont *entry;

      while (l < count)
        {
              entry = (allocFont *)m_allocFonts.getNthItem(l);
              if (entry && entry->pixelSize == pixelsize)
                    return entry->hFont;
              l++;
        }

      return NULL;
}

void GR_Win32Font::insertFontInCache(UT_uint32 pixelsize, HFONT pFont) const
{
      allocFont *entry = new allocFont;
      entry->pixelSize = pixelsize;
      entry->hFont = pFont;

      m_allocFonts.push_back(static_cast<void *>(entry));
}

void GR_Win32Font::fetchFont(UT_uint32 pixelsize) const
{
      LOGFONT lf;

      GetObject(m_layoutFont, sizeof(LOGFONT), &lf);
      lf.lfHeight = pixelsize;

      if (lf.lfHeight>0) lf.lfHeight = - lf.lfHeight;

      HFONT pFont = CreateFontIndirect(&lf);

      insertFontInCache(pixelsize, pFont);
}

HFONT GR_Win32Font::getDisplayFont(GR_Graphics * pG)
{
      UT_uint32 zoom = m_bGUIFont ? 100 : pG->getZoomPercentage();
      UT_uint32 pixels = m_bGUIFont ? m_iHeight : m_iHeight*zoom/100;
            
      HFONT pFont = getFontFromCache(pixels, false, zoom);
      if (pFont)
            return pFont;

      fetchFont(pixels);
      return getFontFromCache(pixels, false, zoom);
}

UT_sint32 GR_Win32Font::measureUnRemappedChar(UT_UCSChar c)
{
#ifndef ABI_GRAPHICS_PLUGIN_NO_WIDTHS
      // first of all, handle 0-width spaces ...
      if(c == 0xFEFF || c == 0x200b || c == UCS_LIGATURE_PLACEHOLDER)
            return 0;

      UT_sint32 iWidth = getCharWidthFromCache(c);
      return iWidth;
#else
      UT_return_val_if_fail(UT_NOT_IMPLEMENTED,0);
#endif
}

void GR_Win32Font::selectFontIntoDC(GR_Graphics * pGr, HDC hdc)
{
      HFONT hFont = getDisplayFont(pGr);
      HGDIOBJ hRet = SelectObject(hdc, hFont);

      // hate having to do the cast, here
      UT_ASSERT_HARMLESS( hRet != (void*)GDI_ERROR);
      
      if (hdc != m_hdc)
      {
            // invalidate cached info when we change hdc's.
            // this is probably unnecessary except when
            // sharing a font with screen and printer.
            // TODO consider changing our invalidate test
            // TODO to not invalidate if old and new are
            // TODO both on screen.

            _clearAnyCachedInfo();
            m_hdc = hdc;
      }
}

void GR_Win32Graphics::polygon(UT_RGBColor& c,UT_Point *pts,UT_uint32 nPoints)
{
      HPEN hPen = CreatePen(PS_SOLID, (UT_sint32)((double)_tduR(m_iLineWidth) * m_fXYRatio), RGB(c.m_red, c.m_grn, c.m_blu));
      HPEN hOldPen = (HPEN)SelectObject(m_hdc,hPen);

      HBRUSH hBrush = CreateSolidBrush(RGB(c.m_red, c.m_grn, c.m_blu));
      HBRUSH hOldBrush = (HBRUSH)SelectObject(m_hdc, hBrush);

      POINT * points = new POINT[nPoints];
      UT_ASSERT(points);

      for (UT_uint32 i = 0; i < nPoints; ++i)
      {
            points[i].x = (UT_sint32)((double)_tduX(pts[i].x) * m_fXYRatio);
            points[i].y = _tduY(pts[i].y);
      }

      Polygon(m_hdc, points, nPoints);

      (void) SelectObject(m_hdc, hOldBrush);
      DeleteObject(hBrush);

      (void) SelectObject(m_hdc, hOldPen);
      DeleteObject(hPen);

      delete[] points;
}

bool GR_Win32Graphics::_setTransform(const GR_Transform & tr)
{
#if 0
      if(UT_IsWinNT())
      {
            XFORM xForm;
            float fScale = static_cast<float>(static_cast<float>(tlu(1)) * static_cast<float>(getZoomPercentage()) / 100.0);
      
            xForm.eM11 = (float)(tr.getM11() * fScale);
            xForm.eM12 = (float)tr.getM12();
            xForm.eM21 = (float)tr.getM21();
            xForm.eM22 = (float)(tr.getM22() * fScale);
            xForm.eDx  = (float)(tr.getDx());
            xForm.eDy  = (float)(tr.getDy());

            bool ret = (SetWorldTransform(m_hdc, & xForm) != 0);
            UT_ASSERT( ret );
            return ret;
      }
      else
      {
            // world tranforms are not supported, fiddle with the view
            // port, etc
            UT_sint32 iScale = static_cast<UT_sint32>((static_cast<float>(tlu(1)) * static_cast<float>(getZoomPercentage())/100.0);
            
            bool res = (SetWindowExtEx(m_hdc, iScale, iScale, NULL) != 0);
            UT_ASSERT( res );

            if(res)
            {
                  res = (SetViewportExtEx(m_hdc,
                                                      GetDeviceCaps(m_hdc, LOGPIXELSX), 
                                                      GetDeviceCaps(m_hdc, LOGPIXELSY),
                                                      NULL) != 0);
                  UT_ASSERT( res );
            }
            
            // change of origins is not implemented yet
            UT_ASSERT(!tr.getDx() && !tr.getDy());

            return res;
      }
#else
      return true;
#endif
}


void GR_Win32Graphics::saveRectangle(UT_Rect & r, UT_uint32 iIndx) 
{           
      UT_Rect * oldR = NULL;
      m_vSaveRect.setNthItem(iIndx, (void*)new UT_Rect(r),(const void **)&oldR);
      DELETEP(oldR);

      UT_uint32 iWidth = (UT_sint32)((double)_tduR(r.width) * m_fXYRatio);
      UT_uint32 iHeight = _tduR(r.height);
      UT_sint32 x = (UT_sint32)((double)_tduX(r.left) * m_fXYRatio);
      UT_sint32 y = _tduY(r.top);    

      #ifdef GR_GRAPHICS_DEBUG      
      UT_DEBUGMSG(("GR_Win32Graphics::saveRectangle %u, %u %u %u %u\n", iIndx,
            x,y, iWidth, iHeight)); 
      #endif

      BITMAPINFO bmi;

      BYTE *imagedata;
      HDC   hMemDC = CreateCompatibleDC(m_hdc);
            
      bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);      
      bmi.bmiHeader.biWidth = iWidth;
      bmi.bmiHeader.biHeight = iHeight;
      bmi.bmiHeader.biPlanes = 1; 
      bmi.bmiHeader.biBitCount = 24; // as we want true-color
      bmi.bmiHeader.biCompression = BI_RGB; // no compression
      bmi.bmiHeader.biSizeImage = (((iWidth * bmi.bmiHeader.biBitCount + 31) & ~31) >> 3) * iHeight; 
      bmi.bmiHeader.biXPelsPerMeter = 0;
      bmi.bmiHeader.biYPelsPerMeter = 0; 
      bmi.bmiHeader.biClrImportant = 0;
      bmi.bmiHeader.biClrUsed = 0; // we are not using palette
            
      HBITMAP hBit = CreateDIBSection(hMemDC,&bmi,DIB_RGB_COLORS,(void**)&imagedata,0,0);
      GdiFlush();

      HBITMAP hOld = (HBITMAP) SelectObject(hMemDC, hBit);
      BitBlt(hMemDC, 0, 0, iWidth, iHeight, m_hdc, x, y, SRCCOPY);
      hBit =  (HBITMAP)SelectObject(hMemDC, hOld);
      DeleteDC(hMemDC);

      m_vSaveRectBuf.setNthItem(iIndx, (void*) hBit, (const void**)&hOld);
      DeleteObject(hOld);
}

void GR_Win32Graphics::restoreRectangle(UT_uint32 iIndx) 
{
      UT_Rect * r = (UT_Rect*)m_vSaveRect.getNthItem(iIndx);
      HBITMAP hBit = (HBITMAP)m_vSaveRectBuf.getNthItem(iIndx);
      
      UT_ASSERT(r);
      UT_ASSERT(hBit);
      
      UT_uint32 iWidth = (UT_sint32)((double)_tduR(r->width) * m_fXYRatio);
      UT_uint32 iHeight = _tduR(r->height);
      UT_sint32 x = (UT_sint32)((double)_tduX(r->left) * m_fXYRatio);
      UT_sint32 y = _tduY(r->top);              

      #ifdef GR_GRAPHICS_DEBUG
      UT_DEBUGMSG(("GR_Win32Graphics::restoreRectangle %u, %u %u %u %u\n", iIndx,
            x,y, iWidth, iHeight)); 
      #endif      

      HDC         hMemDC = CreateCompatibleDC(m_hdc);
      HBITMAP hOld = (HBITMAP) SelectObject(hMemDC, hBit);
      
      BitBlt(m_hdc, x, y, iWidth, iHeight, hMemDC, 0, 0, SRCCOPY);
      SelectObject(hMemDC, hOld);
      DeleteDC(hMemDC);       
}

void GR_Win32Graphics::flush(void)
{     
      GdiFlush(); 
}

//--------------------------------------------------------------------------/
//--------------------------------------------------------------------------/

BITMAPINFO * GR_Win32Graphics::ConvertDDBToDIB(HBITMAP bitmap, HPALETTE hPal, DWORD dwCompression) {

      BITMAP                        bm;   
      BITMAPINFOHEADER  bi;   
      LPBITMAPINFOHEADER      lpbi; 
      DWORD                   dwLen;
      HANDLE                        hDIB; 
      HANDLE                        handle;     
      HDC                     hDC;  
      
      if (dwCompression == BI_BITFIELDS)
            return NULL;

      if (hPal == NULL)
            hPal = (HPALETTE)GetStockObject(DEFAULT_PALETTE);     
            
      // Get bitmap information
      ::GetObject(bitmap, sizeof(bm),(LPSTR)&bm);     
      
      // Initialize the bitmapinfoheader
      bi.biSize               = sizeof(BITMAPINFOHEADER);   
      bi.biWidth              = bm.bmWidth;
      bi.biHeight             = bm.bmHeight;    
      bi.biPlanes             = 1;
      bi.biBitCount           = bm.bmPlanes * bm.bmBitsPixel;
      bi.biCompression  = dwCompression;  
      bi.biSizeImage          = 0;  
      bi.biXPelsPerMeter      = 0;
      bi.biYPelsPerMeter      = 0;  
      bi.biClrUsed            = 0;  
      bi.biClrImportant = 0;
      
      // Compute the size of the  infoheader and the color table
      int nColors = (1 << bi.biBitCount); 
      if( nColors > 256 )           
            nColors = 0;
      dwLen  = bi.biSize + nColors * sizeof(RGBQUAD);
      
      // We need a device context to get the DIB from 
      hDC = CreateCompatibleDC(m_hdc);
      hPal = SelectPalette(hDC, hPal, TRUE);    
      RealizePalette(hDC);
      
      // Allocate enough memory to hold bitmapinfoheader and color table
      hDIB = malloc(dwLen);   
      if (!hDIB){
            SelectPalette(hDC, hPal, TRUE);           
            DeleteDC(hDC);
            return NULL;      
            }
      lpbi = (LPBITMAPINFOHEADER)hDIB;    
      *lpbi = bi;
      
      // Call GetDIBits with a NULL lpBits param, so the device driver 
      // will calculate the biSizeImage field 
      GetDIBits(
            hDC, 
            bitmap, 
            0L, 
            (DWORD)bi.biHeight,
            (LPBYTE)NULL, 
            (LPBITMAPINFO)lpbi, 
            (DWORD)DIB_RGB_COLORS
            );    
      bi = *lpbi;
      
      // If the driver did not fill in the biSizeImage field, then compute it
      // Each scan line of the image is aligned on a DWORD (32bit) boundary
      if (bi.biSizeImage == 0) {
            bi.biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;
      
            // If a compression scheme is used the result may infact be larger
            // Increase the size to account for this.       
            if (dwCompression != BI_RGB)
                  bi.biSizeImage = (bi.biSizeImage * 3) / 2;
            }
      
      // Realloc the buffer so that it can hold all the bits      
      dwLen += bi.biSizeImage;
      if (handle = realloc(hDIB, dwLen))        
            hDIB = handle;    
      else{
            free(hDIB);       
            // Reselect the original palette
            SelectPalette(hDC,hPal,TRUE);       
            DeleteDC(hDC);
            return NULL;      
            }
      
      // Get the bitmap bits  
      lpbi = (LPBITMAPINFOHEADER)hDIB;    
      
      // FINALLY get the DIB
      BOOL bGotBits = GetDIBits( 
            hDC, 
            bitmap, 
            0L,                                       // Start scan line                        
            (DWORD)bi.biHeight,                 // # of scan lines
            (LPBYTE)lpbi                        // address for bitmap bits
            + (bi.biSize + nColors * sizeof(RGBQUAD)),
            (LPBITMAPINFO)lpbi,                 // address of bitmapinfo
            (DWORD)DIB_RGB_COLORS         // Use RGB for color table
            );
      
      if (!bGotBits)    {
            free(hDIB);                   
            SelectPalette(hDC,hPal,TRUE);       
            DeleteDC(hDC);
            return NULL;      
            }     
            
      SelectPalette(hDC,hPal,TRUE); 
      DeleteDC(hDC);

      return (BITMAPINFO*)hDIB;
      }

//--------------------------------------------------------------------------/
//--------------------------------------------------------------------------/

GR_Image * GR_Win32Graphics::genImageFromRectangle(const UT_Rect & r) {

      UT_uint32 iWidth = (UT_sint32)((double)tdu(r.width) * m_fXYRatio);
      UT_uint32 iHeight = tdu(r.height);
      UT_sint32 x = (UT_sint32)((double)tdu(r.left) * m_fXYRatio);
      UT_sint32 y = tdu(r.top);

      if (!iWidth) { // can happen when drag'n'dropping text
            iWidth = 1;
      }
      if (!iHeight) { // though not observed for height, safety never hurts
            iHeight = 1;
      }

      BITMAPINFO bmi; 
      BYTE *imagedata;
      HDC hMemDC = CreateCompatibleDC(m_hdc);
            
      bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);      
      bmi.bmiHeader.biWidth = iWidth;
      bmi.bmiHeader.biHeight = iHeight;
      bmi.bmiHeader.biPlanes = 1; 
      bmi.bmiHeader.biBitCount = 24; // as we want true-color
      bmi.bmiHeader.biCompression = BI_RGB; // no compression
      bmi.bmiHeader.biSizeImage = (((iWidth * bmi.bmiHeader.biBitCount + 31) & ~31) >> 3) * iHeight; 
      bmi.bmiHeader.biXPelsPerMeter = 0;
      bmi.bmiHeader.biYPelsPerMeter = 0; 
      bmi.bmiHeader.biClrImportant = 0;
      bmi.bmiHeader.biClrUsed = 0; // we are not using palette
            
      HBITMAP hBit = CreateDIBSection(hMemDC,&bmi,DIB_RGB_COLORS,(void**)&imagedata,0,0);
      
      GdiFlush();

      HBITMAP hOld = (HBITMAP) SelectObject(hMemDC, hBit);
            BitBlt(hMemDC, 0, 0, iWidth, iHeight, m_hdc, x, y, SRCCOPY);
            hBit =  (HBITMAP)SelectObject(hMemDC, hOld);
      DeleteDC(hMemDC);


      GR_Win32Image *img = new GR_Win32Image("Screenshot");
      img->setDIB((BITMAPINFO *)ConvertDDBToDIB(hBit, NULL, BI_RGB));
      return img;
      }


GR_Graphics * GR_Win32Graphics::graphicsAllocator(GR_AllocInfo &info)
{
#ifndef ABI_GRAPHICS_PLUGIN
      UT_return_val_if_fail(info.getType() == GRID_WIN32, NULL);
      
      GR_Win32AllocInfo &AI = (GR_Win32AllocInfo&)info;

      if(AI.m_pDocInfo)
      {
            // printer graphics required
            return new GR_Win32Graphics(AI.m_hdc, AI.m_pDocInfo, AI.m_pApp, AI.m_hDevMode);
      }
      else
      {
            // screen graphics required
            return new GR_Win32Graphics(AI.m_hdc, AI.m_hwnd, AI.m_pApp);
      }
#else
      UT_return_val_if_fail(UT_NOT_IMPLEMENTED,NULL);
#endif
}

#define _test_and_cleanup(x)                    \
if(!(x))                                        \
{                                               \
    UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN ); \
      goto cleanup;                               \
}

GR_Graphics * GR_Win32Graphics::getPrinterGraphics(const char * pPrinterName,
                                                                           const char * pDocName)
{
      UT_return_val_if_fail( pDocName, NULL );
      GR_Win32Graphics *pGr = NULL;
      HGLOBAL hDM = NULL;
      HANDLE hPrinter = NULL;
      LONG iBuffSize = 0;
      HDC hPDC = NULL;
      DOCINFO * pDI = NULL;
      PDEVMODE pDM = NULL;
      
      bool bFreePN = false;

      // we will use '-' as a shortcut for default printer (the --print command has to have
      // a parameter)
      char * pPN = UT_strcmp("-", pPrinterName) == 0 ? NULL : (char *) pPrinterName;
      
      if(!pPN)
      {
            pPN = UT_GetDefaultPrinterName();
            bFreePN = true;
      }

      _test_and_cleanup(pPN);

      hPDC = CreateDC(NULL, pPN, NULL, NULL);

      _test_and_cleanup(hPDC);
      
      _test_and_cleanup( OpenPrinter(pPN, &hPrinter, NULL));
            
      // first, get the size of the buffer needed for the document mode
      iBuffSize = DocumentProperties(NULL, hPrinter,  pPN, NULL, NULL, 0);
      _test_and_cleanup( iBuffSize );

      // must be global movable memory
      hDM = GlobalAlloc(GHND, iBuffSize);
      pDM = (PDEVMODE)GlobalLock(hDM);
      _test_and_cleanup(hDM && 0<DocumentProperties(NULL, hPrinter, pPN, pDM, NULL, DM_OUT_BUFFER));
      GlobalUnlock(hDM);

      // we have all we need to fill in the doc info structure ...
      pDI = (DOCINFO*) UT_calloc(1, sizeof(DOCINFO));
      _test_and_cleanup(pDI);

      memset(pDI, 0, sizeof(DOCINFO));
      
      pDI->cbSize = sizeof(DOCINFO);
      pDI->lpszDocName = pDocName;
      pDI->lpszOutput  = NULL; // for now we do not support printing into file from cmd line
      pDI->lpszDatatype = NULL;
      pDI->fwType = 0;
      
      {
            GR_Win32AllocInfo ai(hPDC, pDI, XAP_App::getApp(), hDM);
      
            pGr = (GR_Win32Graphics *)XAP_App::getApp()->newGraphics(ai);
            UT_ASSERT_HARMLESS(pGr);
      }
      
 cleanup:
      if(bFreePN)
            free(pPN);

      if(hDM && !pGr)
            GlobalFree(hDM);

      if(pDI && !pGr)
            free(pDI);
      
      if(hPrinter)
            ClosePrinter(hPrinter);
      
      return pGr;
}


/*!
    This function absolutely must be called anytime the DEVMODE structure is modified in
    any way; it also must be called on the DEVMODE handle returned by PringDlg. Those who
    will modify DEVMODE structure and not call this function on it will be shot, without
    exception.

    The issue with the DEVMODE structure is this: it consists of two parts, a public part
    declared by the win32 headers, and a private part, the size and contents of which 
    depend on the printer. Before a printer can be passed the DEVMODE structure, two
    things have to be satisfied:

    1. The memory allocated for the structure has to be large enough to hold both the
       public and private parts of the structure; we obtain the handle from the
       PrintDlg, and it _should_ allocate the necessary size correctly. In any case, we
       cannot meddle with the size, since reallocating means loosing the private part.

    2. The settings represented by the public part have to be transferred into the private
       part; this is achieved by calling the DocumentProperties() function on the
       structure. So any time we modify the contents of DEVMODE in any way we have to call
       DocumentProperties() before we can pass the pointer / handle to any other
       function. In addition, I have a strong suspicion that the PrintDlg function only
       sets the public section, but does not call DocumentProperties(), so we have to do
       that ourselves once we get the handle back.
*/
bool GR_Win32Graphics::fixDevMode(HGLOBAL hDevMode)
{
      DEVMODE * pDM = (DEVMODE*)GlobalLock(hDevMode);
      UT_return_val_if_fail( pDM, false );
      
      // find out how big structure this particular printer really needs
      HANDLE      hPrinter;
      DWORD       dwNeeded, dwRet;

      //Start by opening the printer
      if(!OpenPrinter((char*)& pDM->dmDeviceName, &hPrinter, NULL))
      {
            UT_String msg;
            UT_String_sprintf(msg,"Unable to open printer [%s]", (char*)& pDM->dmDeviceName);
            LOG_WIN32_EXCPT(msg.c_str());
            GlobalUnlock(hDevMode);
            UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
            return false;
      }
      
      dwNeeded = DocumentProperties(NULL,hPrinter, (char*)& pDM->dmDeviceName, NULL, NULL, 0);

      if( (int) dwNeeded > pDM->dmSize + pDM->dmDriverExtra )
      {
            // we are knees deep in crap here ... log it into the users profile, so we have
            // some debug info to go on
            UT_String msg;
            UT_String_sprintf(msg,"DEVMODE too small [%s, needs %d, got %d + %d]",
                                      (char*)& pDM->dmDeviceName, dwNeeded, pDM->dmSize, pDM->dmDriverExtra);
            LOG_WIN32_EXCPT(msg.c_str());
            ClosePrinter(hPrinter);
            GlobalUnlock(hDevMode);
            UT_ASSERT_HARMLESS( UT_SHOULD_NOT_HAPPEN );
            return false;
      }
      
      // now get the printer driver to merge the data in the public section into its private part
      dwRet = DocumentProperties(NULL,hPrinter, (char*)& pDM->dmDeviceName, pDM, pDM, DM_IN_BUFFER | DM_OUT_BUFFER);

      // free what needs be ...
      ClosePrinter(hPrinter);
      GlobalUnlock(hDevMode);

      UT_return_val_if_fail( dwRet == IDOK, false );
      return true;
}


Generated by  Doxygen 1.6.0   Back to index