psdc.cpp

Go to the documentation of this file.
00001 // $Id: psdc.cpp 1389 2006-06-29 11:29:54Z alex $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 
00099 // Device context used for printing to PostScript devices.
00100 
00101 /*
00102 */
00103 
00104 #include "camtypes.h"
00105 
00106 DECLARE_SOURCE("$Revision: 1389 $");
00107 
00108 #define new CAM_DEBUG_NEW
00109 
00110 #include "psdc.h"
00111 
00112 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 #include "camelot.h"
00114 #include "prntview.h"
00115 #include "psrndrgn.h"
00116 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 #include "fontman.h"
00118 
00119 CC_IMPLEMENT_DYNAMIC(PSPrintDC, KernelDC);
00120 
00121 /********************************************************************************************
00122 
00123 >   PSDCFontInfo::PSDCFontInfo()
00124 
00125     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00126     Created:    02/06/95
00127     Purpose:    Initialisation of the font cache information.
00128     SeeAlso:    PSDCFontInfo
00129 
00130 ********************************************************************************************/
00131 
00132 PSDCFontInfo::PSDCFontInfo()
00133 {
00134     Rotation = FIXED16(0);
00135 }
00136 
00137 
00138 /********************************************************************************************
00139 
00140 >   PSPrintDC::PSPrintDC(CNativeDC *pDC)
00141 
00142     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00143     Created:    23/04/95
00144     Inputs:     The device context of the PostScript printer.
00145     Purpose:    Initialise a DC for printing PostScript.
00146 
00147 ********************************************************************************************/
00148 
00149 PSPrintDC::PSPrintDC(CNativeDC *pDC) : KernelDC(pDC, RENDERTYPE_PRINTER_PS)
00150 {
00151     // Initialise the buffer to being empty.
00152     Buffer.nCount = 0;
00153     Buffer.Data[0] = 0;
00154 
00155     // No view yet
00156     pView = NULL;
00157 
00158     // We haven't set up the Camelot context yet, and no-one has asked to do any GDI
00159     // output yet.
00160     GDIContextIsCurrent = TRUE;
00161     SafeToUseGDI = 0;
00162 
00163     // Once per page
00164     StartOfPage = TRUE;
00165 
00166     // No fonts yet
00167     FontInfo.pRenderFont = NULL;
00168     FontInfo.pOldFont = NULL;
00169 
00170     // No render region yet.
00171     pPSRegion = NULL;
00172 
00173     // Suppress the output of a postscript clipping region
00174     UsePSLevelClipping = FALSE;
00175 
00176 }
00177 
00178 
00179 /********************************************************************************************
00180 
00181 >   PSPrintDC::~PSPrintDC()
00182 
00183     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00184     Created:    05/30/95
00185     Purpose:    Clean up the fonts in the DC.
00186 
00187 ********************************************************************************************/
00188 
00189 PSPrintDC::~PSPrintDC()
00190 {
00191     // Deselect the normal rendering font from the DC
00192     if (FontInfo.pRenderFont != NULL)
00193     {
00194         GetDC()->SetFont(*FontInfo.pOldFont);
00195         FontInfo.pOldFont = NULL;
00196         delete FontInfo.pRenderFont;
00197         FontInfo.pRenderFont = NULL;
00198     }
00199 }
00200 
00201 
00202 
00203 /********************************************************************************************
00204 
00205 >   BOOL PSPrintDC::OutputNewLine()
00206 
00207     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00208     Created:    23/04/95
00209     Returns:    TRUE if the data was written ok;
00210                 FALSE if not => ERROR1
00211     Purpose:    Causes a new line to be started in the PostScript stream.
00212     SeeAlso:    PSPrintDC; PSPrintDC::OutputToken
00213     Errors:     Disk/file/print error => ERROR1
00214 
00215 ********************************************************************************************/
00216 
00217 BOOL PSPrintDC::OutputNewLine()
00218 {
00219     static TCHAR NewLine[] = _T("\n");
00220 
00221     // Make sure we have enough room in the buffer
00222     if (!MakeRoomInBuffer(camStrlen(NewLine)))
00223         // Error occured in buffer handling.
00224         return FALSE;
00225 
00226     // Add newline to buffer
00227     camStrcat(Buffer.Data, NewLine);
00228     Buffer.nCount += camStrlen(NewLine);
00229 
00230     // Update line width record.
00231     LineWidth = 0;
00232 
00233     // Success
00234     return TRUE;
00235 }
00236 
00237 /********************************************************************************************
00238 
00239 >   BOOL PSPrintDC::OutputToken(TCHAR *Str)
00240 
00241     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00242     Created:    23/04/95
00243     Inputs:     Str - the character string to write to the file.
00244     Returns:    TRUE if the data was written ok;
00245                 FALSE if not => ERROR1
00246     Purpose:    Outputs a string token to the EPS file.
00247     SeeAlso:    PSPrintDC; KernelDC::OutputToken
00248     Errors:     Disk/file error => ERROR1
00249 
00250 ********************************************************************************************/
00251 
00252 BOOL PSPrintDC::OutputToken(TCHAR *Str)
00253 {
00254 #ifndef STANDALONE
00255     // Should we re-instate the Camelot context?
00256     if (!SafeToUseGDI && GDIContextIsCurrent)
00257     {
00258         // Yes - just save the context (so we can restore GDI's context) and put our
00259         // dictionary on the dict stack.
00260         // Must do this first or else we get an infinite loop!
00261         GDIContextIsCurrent = FALSE;
00262 
00263         // Code by Mike
00264         // We need to work out where we are in order to render the correct bits of postscript header.
00265         BOOL AtStartOfPaper = FALSE;
00266         BOOL AtStartOfPatch = StartOfPage;
00267         StartOfPage=FALSE;
00268 
00269         // Work out whether this is the first call to output token for this page
00270         if (AtStartOfPatch)
00271         {
00272             PrintView* pView = (PrintView*)(pPSRegion->GetRenderView());
00273             ERROR2IF(pView==NULL,FALSE,"No printview in this PS render region");
00274             ERROR3IF(!pView->IsKindOf(CC_RUNTIME_CLASS(PrintView)), "Not a PrintView in PSPrintDC output token");
00275             AtStartOfPaper = ((pView->PatchInfo.PatchNumber)==1);
00276         }
00277         
00278         OutputNewLine();
00279 
00280         // ok now check our flag. This may be the first time we've
00281         // been called from this PSPrintDC construction. If so we
00282         // need to output page header stuff.
00283         if (AtStartOfPaper)
00284         {
00285             // We need to get the name of the output plate here
00286             // this might be a bit tricky! Na easy...
00287             if (!pPSRegion->WritePlateName(this))
00288                 return FALSE;
00289         }
00290 
00291         // save the graphics state
00292         OutputToken(_T("save"));
00293         OutputNewLine();
00294 
00295         // wxWidgets and Windows both use (0,0) to mean the top left hand origin of the
00296         // paper in logical coordinates. However, they do something rather different in
00297         // terms of physical coordinates. What windows printer drivers do (and thus what
00298         // Camelot expects) is use a postcript page transformation matrix to invert the
00299         // page, and for the postscript to be written in native DPI coordinates with the
00300         // origin at the TOP left of the page. What wxWidgets does is write its postscript
00301         // in the more natural way with the origin at the BOTTOM of the page, and handle
00302         // the coordinate inversion wihin wxWidgets.
00303         //
00304         // Note that this means while our internally generated PS expects a transformation
00305         // matrix that flips the Y coordinate (as the windows drivers produce). We
00306         // can't change the transformation matrix in the prologue because this would
00307         // affect wxWidgets output (and also it's technically difficult to do). We can't
00308         // change our own rendering matrix (so we produce the right coordinates) because
00309         // lots of things seem to explode with a matrix which flips the Y access (negative
00310         // scale factors etc.). We can't just change the coordinates we write because
00311         // other things (e.g. bitmaps) use the native render matrix. So what we do is
00312         // temporarily invert the PS transformation matrix during the period of our output.
00313         // This seems arcane but reasonably extensive research suggests it is the best
00314         // way forward. It's restored before we do any OS output.
00315 
00316         TCHAR flipbuf[256];
00317         INT32 translate = GetDC()->GetSize().GetHeight();
00318         camSprintf(flipbuf, _T("0 %d translate 1 -1 scale"), translate);
00319         OutputToken(flipbuf);
00320         OutputNewLine();
00321 
00322         // Now start plate writing etc.
00323 
00324         if (!pPSRegion->WritePhotoNegative(this))
00325             return FALSE;
00326 
00327         if (AtStartOfPaper)
00328         {
00329             // We need to fill the page with black if the plate says we
00330             // need to negate everything.
00331             if (!pPSRegion->WriteRenderPaper(this))
00332                 return FALSE;
00333         }
00334 
00335         // Write out our procset.
00336         if (!pPSRegion->WriteProlog(this))
00337             // Error 
00338             return FALSE;
00339 
00340         OutputNewLine();
00341         OutputToken(_T("XaraStudio1Dict begin"));
00342         OutputNewLine();
00343 
00344         // Now output the setscreen function for this plate
00345         if (!pPSRegion->WritePlateScreen(this))
00346             return FALSE;
00347 
00348         // try to write out the sep tables if we need them
00349         if (!pPSRegion->WriteSepTables(this))
00350             return FALSE;
00351 
00352         // export any clip region we might need.
00353         if (!OutputPSClipping())
00354             return FALSE;
00355     }   
00356 
00357     // Special tokens
00358 //  static TCHAR Space = _T(' ');
00359 //  static TCHAR NewLine[] = _T("\n");
00360 
00361     if (LineWidth > 70)
00362     {
00363         // Line is getting INT32 - wrap around
00364         OutputNewLine();
00365     }
00366 
00367     // Pad with a space (unless at the beginning of the line)
00368     if (LineWidth > 0)
00369     {
00370         // Make sure we have enough room in the buffer
00371         if (!MakeRoomInBuffer(1))
00372             // Error occured in buffer handling.
00373             return FALSE;
00374 
00375         // Add space to buffer
00376         camStrcat(Buffer.Data, _T(" "));
00377         Buffer.nCount++;
00378 
00379         // Update line width record.
00380         LineWidth++;
00381     }
00382 
00383     // Write the token out to the file
00384     INT32 Len = camStrlen(Str);
00385 
00386     // Make sure we have enough room in the buffer
00387     if (!MakeRoomInBuffer(Len))
00388         // Error occured in buffer handling.
00389         return FALSE;
00390 
00391     // Add space to buffer
00392     camStrcat(Buffer.Data, Str);
00393     Buffer.nCount += Len;
00394 
00395     // Update line width record.
00396     LineWidth += Len;
00397 
00398     // Success
00399     return TRUE;
00400 #else
00401     return FALSE;
00402 #endif
00403 }
00404 
00405 
00406 /********************************************************************************************
00407 
00408 >   BOOL PSPrintDC::OutputDirect(BYTE *Buf, INT32 nBytes)
00409 
00410     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00411     Created:    23/04/95
00412     Inputs:     Buf - the bytes to send to the stream.
00413                 nBytes - the number of bytes to send to the stream.
00414     Returns:    TRUE if all the bytes were sent ok;
00415                 FALSE if not.
00416     Purpose:    Send bytes directly to the PostScript stream with no alteration or padding.
00417                 Used for sending binary/hex data to stream.
00418                 NB. The data output must NOT affect the rendering context set up by GDI
00419                     in any way, as this function does not preserve that context (it cannot
00420                     because it does not change the data passed in in any way).
00421 
00422                     Do NOT use this to send commands that will render something, as the
00423                     CTM will NOT be set up correctly for Camelot's co-ordinate system.
00424 
00425     SeeAlso:    KernelDC::OutputNewLine; KernelDC::OutputToken
00426 
00427 ********************************************************************************************/
00428 
00429 BOOL PSPrintDC::OutputDirect(BYTE *Buf, INT32 nBytes)
00430 {
00431     // This could be anything, so we flush the buffer first, and then write directly
00432     // to stream.
00433     if (!FlushPSBuffer())
00434         // Error occured in buffer handling
00435         return FALSE;
00436 
00437     // Copy data to our buffer - do it in stages if necessary
00438     while (nBytes > 0)
00439     {
00440         // Work out how much we can send this time around.
00441         INT32 nBytesToSend = min(nBytes, MAX_PSBUF);
00442 
00443         WritePSchar((char *)Buf, nBytesToSend);
00444 
00445         // Update variables to reflect sending this number of bytes
00446         nBytes -= nBytesToSend;
00447         Buf += nBytesToSend;
00448     }
00449 
00450     // All ok
00451     return TRUE;
00452 }
00453 
00454 
00455 /********************************************************************************************
00456 
00457 >   BOOL PSPrintDC::FlushPSBuffer()
00458 
00459     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00460     Created:    04/24/95
00461     Returns:    TRUE if data flushed ok;
00462                 FALSE if not (e.g. printer/driver error)
00463     Purpose:    Flushes the buffer of pending PostScript data to the PostScript device.
00464     SeeAlso:    PSPrintDC::MakeRoomInBuffer
00465 
00466 ********************************************************************************************/
00467 
00468 BOOL PSPrintDC::FlushPSBuffer()
00469 {
00470     if (Buffer.nCount <= 0)
00471         // Nothing to flush
00472         return TRUE;
00473 
00474     // Get system to flush any of its data first.
00475     // (don't have to do anything here)
00476 
00477     if (Buffer.nCount>MAX_PSBUF) // MAX_PSBUF itself is legal
00478     {
00479         Buffer.nCount=MAX_PSBUF;
00480         ERROR3("Postscript buffer overrun");
00481     }
00482 
00483     // Ensure the buffer is zero terminated
00484     WritePSTCHAR(Buffer.Data, Buffer.nCount);
00485     
00486     // Clear out buffer
00487     Buffer.nCount = 0;
00488     Buffer.Data[0] = 0;
00489 
00490     return TRUE;
00491 }
00492 
00493 
00494 /********************************************************************************************
00495 
00496 >   void PSPrintDC::WritePSTCHAR(TCHAR * pBuf, INT32 nBytes)
00497 
00498     Author:     Alex Bligh
00499     Created:    23/06/2006
00500     Returns:    -
00501     Purpose:    Writes nBytes of TCHAR to the output stream. Uses Buffer.chardata as a
00502                 scratch area. Max of MAX_PSBUF can be written at a time
00503     SeeAlso:    -
00504 
00505 ********************************************************************************************/
00506 
00507 void PSPrintDC::WritePSTCHAR(TCHAR * pBuf, INT32 nBytes)
00508 {
00509     if ((nBytes>MAX_PSBUF) || (nBytes<=0))
00510     {
00511         ERROR3("Bad PSPrintDC write");
00512         return;
00513     }
00514 
00515     if (sizeof(TCHAR) == sizeof(char))
00516     {
00517         WritePSchar((char *)pBuf, nBytes);
00518         return;
00519     }
00520 
00521     INT32 i;
00522     for (i=0; i<nBytes; i++)
00523     {
00524         ERROR3IF(!pBuf[i], "Zero byte in PS buffer");
00525         Buffer.CharData[i]=pBuf[i]; // 1:1 copy
00526     }
00527 
00528     Buffer.CharData[nBytes]=0; // safe as chardata one larger than MAX_PSBUF
00529     ((wxPostScriptDC *)GetDC())->PsPrint(Buffer.CharData);
00530 }
00531 
00532 /********************************************************************************************
00533 
00534 >   void PSPrintDC::WritePSchar(char * pBuf, INT32 nBytes)
00535 
00536     Author:     Alex Bligh
00537     Created:    23/06/2006
00538     Returns:    -
00539     Purpose:    Writes nBytes of TCHAR to the output stream. Uses Buffer.chardata as a
00540                 scratch area. Max of MAX_PSBUF can be written at a time
00541     SeeAlso:    -
00542 
00543 ********************************************************************************************/
00544 
00545 void PSPrintDC::WritePSchar(char * pBuf, INT32 nBytes)
00546 {
00547     if ((nBytes>MAX_PSBUF) || (nBytes<=0))
00548     {
00549         ERROR3("Bad PSPrintDC write");
00550         return;
00551     }
00552 
00553 #ifdef _DEBUG
00554     INT32 i;
00555     for (i=0; i<nBytes; i++)
00556     {
00557         if (!pBuf[i])
00558         {
00559             ERROR3("Zero byte in PS Buffer!");
00560             break;
00561         }
00562     }
00563 #endif
00564 
00565     // Need to copy it so we can safely zero terminate it
00566     memcpy(Buffer.CharData, pBuf, nBytes);
00567     Buffer.CharData[nBytes]=0; // safe as chardata one larger than MAX_PSBUF
00568     ((wxPostScriptDC *)GetDC())->PsPrint(Buffer.CharData);
00569 }
00570 
00571 /********************************************************************************************
00572 
00573 >   BOOL PSPrintDC::MakeRoomInBuffer(INT32 nBytesRequired)
00574 
00575     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00576     Created:    04/24/95
00577     Inputs:     nBytesRequired - how many bytes of free space are needed in the buffer.
00578     Returns:    TRUE if enough space was found;
00579                 FALSE if not.
00580     Purpose:    Make sure there is enough room in the PostScript buffer to add the
00581                 specified number of bytes to the stream.  If not, the buffer is flushed
00582                 to the device.
00583     Errors:     Printer/driver error => ERROR1
00584                 Buffer cannot accomodate requested size even when empty => ERROR2
00585     SeeAlso:    PSPrintDC::FlushPSBuffer
00586 
00587 ********************************************************************************************/
00588 
00589 BOOL PSPrintDC::MakeRoomInBuffer(INT32 nBytesRequired)
00590 {
00591     if (nBytesRequired >= MAX_PSBUF)
00592     {
00593         // Can't ever do that many bytes!
00594         ERROR2(FALSE, "Too many bytes asked for in PSPrintDC::MakeRoomInBuffer()");
00595     }
00596 
00597     // Do we need to flush?
00598     if (nBytesRequired > (MAX_PSBUF - Buffer.nCount))
00599         // Yes
00600         return FlushPSBuffer();
00601 
00602     // All ok if we get here
00603     return TRUE;
00604 }
00605 
00606 
00607 /********************************************************************************************
00608 
00609 >   BOOL PSPrintDC::FlushDC()
00610 
00611     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00612     Created:    04/25/95
00613     Returns:    TRUE if ok;
00614                 FALSE if not (e.g. output problem)
00615     Purpose:    Tidy up the PostScript DC and reinstate the GDI context if necessary.
00616                 Should be called when printing is finished.  (Won't cause problems if
00617                 called more often - just more output).
00618     SeeAlso:    PSPrintDC::FlushPSBuffer()
00619 
00620 ********************************************************************************************/
00621 
00622 BOOL PSPrintDC::FlushDC()
00623 {
00624     // Restore OS context - just pretend we want to do some OS output
00625     //if (!StartOSOutput())
00626         // Error
00627     //  return FALSE;
00628 
00629     // Clear out the buffer.
00630     if (!FlushPSBuffer())
00631         // Error
00632         return FALSE;
00633 
00634     // Go back to using Camelot context
00635     // (NB this is deferred - it will only be reinstated if we do Camelot output)
00636     //return EndOSOutput();
00637     return TRUE;
00638 }
00639 
00640 
00641 
00642 /********************************************************************************************
00643 
00644 >   BOOL PSPrintDC::OutputUserSpaceValue(MILLIPOINT n, 
00645                                          EPSAccuracy Accuracy = ACCURACY_NORMAL)
00646 
00647     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00648     Created:    28/04/95
00649     Inputs:     n - the value (in millipoints) to output.
00650                 Accuracy - optional parameter - ignored for GDI PostScript output as GDI
00651                            sets up a coordinate system where 1 user space unit = 1 device unit.
00652     Returns:    TRUE if the data was written ok;
00653                 FALSE if not => ERROR1
00654 
00655     Purpose:    Output a 'user space' value to the device context.
00656                 User space is the coordinate system used for PostScript files.
00657                 GDI sets up the CTM so that 1 user space unit = 1 device unit, so all we
00658                 do is use the rendering matrix to convert to Windows co-ordinates and output
00659                 this directly at 0dp accuracy (because a fractional user space value would
00660                 equate to a fractional device pixel, which has no effect on output).
00661 
00662     SeeAlso:    KernelDC::OutputCoord; KernelDC::OutputToken;
00663                 KernelDC::SetFullAccuracy
00664     Errors:     Disk/file error => ERROR1
00665 
00666 ********************************************************************************************/
00667 
00668 BOOL PSPrintDC::OutputUserSpaceValue(MILLIPOINT n, EPSAccuracy Accuracy)
00669 {
00670     // We need a view to scale the value.
00671     ERROR2IF(pView == NULL, FALSE, "No view attached to PostScript DC!");
00672 
00673     // Use pixel size to scale to device units.
00674     n /= PixelSize;
00675 
00676     // Output as device units
00677     return OutputValue(n);
00678 }
00679 
00680 
00681 /********************************************************************************************
00682 
00683 >   BOOL PSPrintDC::OutputCoord(DocCoord& Coord, EPSAccuracy Accuracy = ACCURACY_NORMAL)
00684 
00685     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00686     Created:    28/03/94
00687     Inputs:     Coord - the coordinate to write out to the file.
00688                 Accuracy - optional parameter - ignored for GDI PostScript output as GDI
00689                            sets up a coordinate system where 1 user space unit = 1 device unit.
00690     Returns:    TRUE if the data was written ok;
00691                 FALSE if not => ERROR1
00692     Purpose:    Write out a coordinate x,y pair to the export file, automatically 
00693                 converting from spread coordinates to the user space coordinate system.
00694                 User space is the coordinate system used for PostScript files.
00695                 GDI sets up the CTM so that 1 user space unit = 1 device unit, so all we
00696                 do is use the rendering matrix to convert to Windows co-ordinates and output
00697                 this directly at 0dp accuracy (because a fractional user space value would
00698                 equate to a fractional device pixel, which has no effect on output).
00699 
00700     SeeAlso:    PSPrintDC::OutputUserSpaceValue
00701     Errors:     File/disk error => ERROR1
00702 
00703 ********************************************************************************************/
00704 
00705 BOOL PSPrintDC::OutputCoord(DocCoord& Coord, EPSAccuracy Accuracy)
00706 {
00707     // We need a view to scale the coords.
00708     ERROR2IF(pView == NULL, FALSE, "No view attached to PostScript DC!");
00709 
00710     WinCoord PSCoord = TransformCoord(Coord);
00711 
00712     // Output to PostScript stream.
00713     BOOL Ok = (OutputValue(PSCoord.x) && OutputValue(PSCoord.y));
00714 
00715     return Ok;
00716 }
00717 
00718 
00719 /********************************************************************************************
00720 
00721 >   void PSPrintDC::SetDCTransforms(Matrix NewRenderMatrix, View *pNewView)
00722 
00723     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00724     Created:    29/04/95
00725     Inputs:     The matrix and view to transform user space values with.
00726     Purpose:    Inform the DC how to transform user space values when performing output
00727                 via OutputCoord. and OutputUserSpaceValue().
00728     SeeAlso:    PSPrintDC::OutputCoord; PSPrintDC::OutputUserSpaceValue
00729 
00730 ********************************************************************************************/
00731 
00732 void PSPrintDC::SetDCTransforms(Matrix NewRenderMatrix, View *pNewView)
00733 {
00734     // Remember these values
00735     RenderMatrix = NewRenderMatrix;
00736     pView = pNewView;
00737 
00738     // Get the pixel size from the view
00739     FIXED16 fxPixelWidth, fxPixelHeight;
00740     pView->GetScaledPixelSize(&fxPixelWidth, &fxPixelHeight);
00741     PixelSize = (MILLIPOINT) fxPixelWidth.MakeLong();
00742 }
00743 
00744 
00745 /********************************************************************************************
00746 
00747 >   WinCoord PSPrintDC::TransformCoord(const DocCoord &Coord)
00748 
00749     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00750     Created:    30/6/95
00751     Inputs:     Coord - the co-ordinate to transform.
00752     Returns:    The transformed co-ordinate.
00753     Purpose:    Transform a DocCoord to a WinCoord, using this DC's transformations, as set
00754                 up by SetDCTransforms().
00755     SeeAlso:    PSPrintDC::SetDCTransforms
00756 
00757 ********************************************************************************************/
00758 
00759 WinCoord PSPrintDC::TransformCoord(const DocCoord &Coord)
00760 {
00761     // Use the rendering matrix of our parent render region to transform
00762     // the co-ordinate to device units before proceeding.
00763     OilCoord NewCoord(Coord.x, Coord.y);
00764     RenderMatrix.transform(&NewCoord);
00765 
00766     // Convert to Windows device units.
00767     WinCoord PSCoord = NewCoord.ToWin(pView);
00768 
00769     // Return to caller
00770     return PSCoord;
00771 }
00772 
00773 
00774 /********************************************************************************************
00775 
00776 >   BOOL PSPrintDC::StartOSOutput()
00777 
00778     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00779     Created:    04/30/95
00780     Returns:    TRUE if ok;
00781                 FALSE if not (e.g. file/printer/driver error).
00782     Purpose:    Restore the PostScript DC to the context expected by the host OS (GDI), so 
00783                 we can use OS (GDI) functions.
00784                 You must call EndOSOutput() before calling any Camelot rendering functions.
00785                 There may be nested calls to this function.
00786     Errors:     File/printer error
00787     SeeAlso:    PSPrintDC::EndOSOutput
00788 
00789 ********************************************************************************************/
00790 
00791 BOOL PSPrintDC::StartOSOutput()
00792 {
00793     if ((SafeToUseGDI == 0) && !GDIContextIsCurrent)
00794     {
00795         // We need to restore the GDI context - this involves removing our dictionary
00796         // from the dictionary stack, and calling restore.
00797         if (!OutputToken(_T("end restore")) || !OutputNewLine())
00798             // Error
00799             return FALSE;
00800         
00801         // Flush our buffer so Camelot PS syncs correctly with GDI PS.
00802         if (!FlushPSBuffer())
00803             // Error
00804             return FALSE;
00805 
00806         GDIContextIsCurrent = TRUE;
00807     }
00808 
00809     // Increment GDI count
00810     SafeToUseGDI++;
00811 
00812     // Ok to use GDI now
00813     return TRUE;
00814 }
00815 
00816 /********************************************************************************************
00817 
00818 >   BOOL PSPrintDC::EndOSOutput()
00819 
00820     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00821     Created:    04/30/95
00822     Returns:    TRUE if ok;
00823                 FALSE if not (e.g. call has no corresponding call to StartOSOutput()).
00824     Purpose:    Inform the DC that we have finished doing GDI output, and so we should
00825                 prepare the DC for Camelot PS commands (this is deferred until we actually
00826                 do some output on the stream).
00827     SeeAlso:    PSPrintDC::StartOSOutput
00828 
00829 ********************************************************************************************/
00830 
00831 BOOL PSPrintDC::EndOSOutput()
00832 {
00833     // Just decrement counter - we do the other stuff in OutputToken().
00834     SafeToUseGDI--;
00835     ERROR2IF(SafeToUseGDI < 0, FALSE, "Unbalanced call to Start/EndOSOutput() functions!");
00836 
00837     // All ok
00838     return TRUE;
00839 }
00840 
00841 
00842 /********************************************************************************************
00843 
00844 >   BOOL PSPrintDC::SelectNewFont(WORD Typeface, BOOL Bold, BOOL Italic, 
00845                                   MILLIPOINT Width, MILLIPOINT Height)
00846 
00847     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00848     Created:    05/28/95
00849     Inputs:     Details of the font to select.
00850                 NB. Width is not the actual width; it expresses the aspect ratio required.
00851                     So, e.g. if Width == Height/2 then the aspect ratio is 50%, and so on.
00852     Returns:    TRUE if the font was selected ok;
00853                 FALSE if not.
00854     Purpose:    Selects the specified font into the DC.  Performs clever stuff to cache
00855                 the font so that subsequent requests for the same font don't cause anything
00856                 to happen.
00857 
00858 ********************************************************************************************/
00859 
00860 BOOL PSPrintDC::SelectNewFont(WORD Typeface, BOOL Bold, BOOL Italic, 
00861                               MILLIPOINT Width, MILLIPOINT Height, ANGLE Rotation)
00862 {
00863 PORTNOTE("printing", "Removed postscript printing of text")
00864 #ifndef EXCLUDE_FROM_XARALX
00865 
00866     // Check to see if it is cached
00867     if ((FontInfo.pRenderFont != NULL) &&
00868         (FontInfo.Typeface == Typeface) &&
00869         (FontInfo.Bold == Bold) &&
00870         (FontInfo.Italic == Italic) &&
00871         (FontInfo.Width == Width) &&
00872         (FontInfo.Height == Height) &&
00873         (FontInfo.Rotation == Rotation))
00874     {
00875         // It is the same as the currently selected/cached font
00876         TRACEUSER( "Tim", _T("Using the cached font\n"));
00877         return TRUE;
00878     }
00879 
00880     TRACEUSER( "Tim", _T("Font cache invalid - generating font to use\n"));
00881 
00882     // Get ready for GDI operations
00883     if (!StartOSOutput())
00884         return FALSE;
00885 
00886     // Not the cached font - ok, deselect and delete the cached font.
00887     if (FontInfo.pOldFont != NULL)
00888     {
00889         GetDC->SetFont(*FontInfo.pOldFont);
00890         FontInfo.pOldFont = NULL;
00891     }
00892 
00893     if (FontInfo.pRenderFont != NULL)
00894     {
00895         delete FontInfo.pRenderFont;
00896         FontInfo.pRenderFont = NULL;
00897     }
00898 
00899     // Create the font and select it into the DC
00900     FontInfo.pRenderFont = new wxFont;
00901     if (!FontInfo.pRenderFont)
00902         return FALSE;
00903 
00904     // Work out the font weight - bold or normal?
00905     INT32 FontWeight = Bold ? FW_BOLD : FW_NORMAL;
00906 
00907     // Get the typeface name of the font
00908 
00909     ENUMLOGFONT *pEnumLogFont = FONTMANAGER->GetEnumLogFont(Typeface);
00910     if (pEnumLogFont == NULL)
00911         // Error
00912         return FALSE;
00913 
00914     // Work out how big the font is, in logical pixels
00915     FIXED16 fxPixWidth, fxPixHeight;
00916     pView->GetScaledPixelSize(&fxPixWidth, &fxPixHeight);
00917     INT32 FontWidth = (INT32) (Width / fxPixWidth.MakeDouble());
00918     INT32 FontHeight = (INT32) (Height / fxPixHeight.MakeDouble());
00919 
00920     if (FontHeight == FontWidth)
00921     {
00922         // Aspect ratio is 100% - use width of 0, and the GDI font engine gives us 100%
00923         // ratio automatically
00924         FontWidth = 0;
00925     }
00926     else
00927     {
00928         // Aspect ratio is not 100% - we need to do some clever stuff to work out
00929         // how wide we want the font to be.  Because fonts are generally much taller
00930         // than they are wide, we can't just pass in FontWidth directly - we must
00931         // transform it to the correct width.
00932 
00933         // BODGETEXT: ideally, we should use a NEWTEXTMETRIC structure here, to work out
00934         //            the correct width, but this involves calling EnumLogFontFamily() or
00935         //            whatever, which is too slow.  As the text code caches the LOGFONT
00936         //            for each font, it could also cache the NEWTEXTMETRIC structure,
00937         //            which this routine could then use.
00938         //            Having said all that, this code seems to work!
00939 
00940         // Create the font - the first time is to find out how wide the characters are,
00941         // so we can scale the width correctly. (So we give a width of 0 to get the normal
00942         // width for the given height)
00943         if (FontInfo.pRenderFont->CreateFont(-100, 0, 0, 0, FontWeight, Italic, 
00944                                              FALSE, FALSE, DEFAULT_CHARSET, 
00945                                              OUT_TT_PRECIS, CLIP_TT_ALWAYS,
00946                                              PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
00947                                              pEnumLogFont->elfLogFont.lfFaceName) == 0)
00948         {
00949             // Can't create font - do it as curves
00950             delete FontInfo.pRenderFont;
00951             FontInfo.pRenderFont = NULL;
00952             EndOSOutput();
00953             return FALSE;
00954         }
00955 
00956         if (FALSE)//TRUE)
00957         {
00958             FontInfo.pOldFont = SelectObject(FontInfo.pRenderFont);
00959 
00960             // Get the outlines to see how big the font should be...
00961             INT32 Widths[30];
00962 
00963             // First for lower case
00964             GetOutputCharWidth('a', 'z', Widths);
00965             INT32 AveCharWidth = 0;
00966             for (INT32 i = 0; i < 26; i++)
00967                 AveCharWidth += Widths[i];
00968 
00969             // Now for upper case
00970             GetOutputCharWidth('A', 'Z', Widths);
00971             for (i = 0; i < 26; i++)
00972                 AveCharWidth += Widths[i];
00973 
00974             // Ok, get average width
00975             AveCharWidth /= 52;
00976 
00977             // Scale the width by the font's aspect ratio: 
00978 
00979             // ActualWidth = (RealWidth / Height) * Width
00980             FontWidth = MulDiv(AveCharWidth, FontWidth, 100);
00981 
00982             // Select old font back into DC
00983             SelectObject(FontInfo.pOldFont);
00984         }
00985         else
00986         {
00987             // Get the metrics
00988             TEXTMETRIC Metrics;
00989 
00990             if (TRUE) //RFlags.Metafile)
00991             {
00992                 // Metafile - we need to use the screen DC to do this
00993                 CNativeDC *pDesktopDC = CWnd::GetDesktopWindow()->GetDC();
00994 
00995                 // Get the metrics
00996                 FontInfo.pOldFont = pDesktopDC->SelectObject(FontInfo.pRenderFont);
00997                 pDesktopDC->GetTextMetrics(&Metrics);
00998 
00999                 // Select old font back into screen DC
01000                 pDesktopDC->SelectObject(FontInfo.pOldFont);
01001                 CWnd::GetDesktopWindow()->ReleaseDC(pDesktopDC);
01002             }
01003             else
01004             {
01005                 FontInfo.pOldFont = SelectObject(FontInfo.pRenderFont);
01006 
01007                 GetTextMetrics(&Metrics);
01008 
01009                 // Select old font back into DC
01010                 SelectObject(FontInfo.pOldFont);
01011             }
01012 
01013             // Scale the width by the font's aspect ratio: 
01014 
01015             // ActualWidth = (RealWidth / Height) * Width
01016             FontWidth = MulDiv(Metrics.tmAveCharWidth, FontWidth, 100);
01017         }
01018 
01019         // Delete the font
01020         delete FontInfo.pRenderFont;
01021 
01022         // Create the 'proper' font and select it into the DC
01023         TRY
01024         {
01025             FontInfo.pRenderFont = new CFont;
01026         }
01027         CATCH (CMemoryException, e)
01028         {
01029             return FALSE;
01030         }
01031         END_CATCH
01032     }
01033 
01034     // Work out the rotation of the font, in tenths of degrees.
01035     INT32 lfRotation;
01036     if (Rotation == FIXED16(0))
01037         lfRotation = 0;
01038     else
01039         lfRotation = (INT32) ( (Rotation.MakeDouble() * 360.0 * 10.0) / (2 * PI) );
01040 
01041     // Create the font with the correct width and rotation
01042     if (FontInfo.pRenderFont->CreateFont(-FontHeight, FontWidth, lfRotation, 0, 
01043                                          FontWeight, Italic, 
01044                                          FALSE, FALSE, DEFAULT_CHARSET, 
01045                                          OUT_TT_PRECIS, CLIP_TT_ALWAYS,
01046                                          PROOF_QUALITY, DEFAULT_PITCH | FF_DONTCARE, 
01047                                          pEnumLogFont->elfLogFont.lfFaceName) == 0)
01048     {
01049         // Can't create font (device may not support rotation).
01050         delete FontInfo.pRenderFont;
01051         FontInfo.pRenderFont = NULL;
01052         EndOSOutput();
01053         return FALSE;
01054     }
01055 
01056     // Select the real font into the DC.
01057     FontInfo.pOldFont = SelectObject(FontInfo.pRenderFont);
01058 
01059     // Validate the cache
01060     FontInfo.Typeface = Typeface;
01061     FontInfo.Bold     = Bold;
01062     FontInfo.Italic   = Italic;
01063     FontInfo.Width    = Width;
01064     FontInfo.Height   = Height;
01065     FontInfo.Rotation = Rotation;
01066 
01067     // End of GDI operations
01068     if (!EndOSOutput())
01069         return FALSE;
01070 
01071     // Font created and selected ok
01072     return TRUE;
01073 #else
01074     return FALSE; // hopefully it will print as curves
01075 #endif
01076 }
01077 
01078 
01079 /********************************************************************************************
01080 
01081 >   BOOL PSPrintDC::SetClipping(BOOL state)
01082 
01083     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01084     Created:    09/09/96
01085     Returns:    The old state of the clipping flag
01086     Purpose:    Set the use of a clip region.
01087 
01088 ********************************************************************************************/
01089 
01090 BOOL PSPrintDC::SetClipping(BOOL newstate)
01091 {
01092     BOOL oldstate = UsePSLevelClipping;
01093     UsePSLevelClipping = newstate;
01094     return oldstate;
01095 }
01096 
01097 
01098 /********************************************************************************************
01099 
01100 >   BOOL PSPrintDC::OutputPSClipping()
01101 
01102     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01103     Created:    09/09/96
01104     Returns:    TRUE if a clip region has been exported
01105     Purpose:    Set the use of a clip region.
01106 
01107 ********************************************************************************************/
01108 
01109 BOOL PSPrintDC::OutputPSClipping()
01110 {
01111     if (UsePSLevelClipping)
01112     {
01113         // DocRect theRect =  pPSRegion->CurrentClipRect;
01114 
01115         // BODGE - Currently need to deflate this clip region
01116         // by the bleed size. This only works because we know we are not
01117         // stripping but rendering complete pages. Hence this clip rectangle represents
01118         // the page with bleed applied in the document area. We need to output a clip
01119         // rectangle which will clip all objects beyond the bleed. This is quite a complex process.
01120 
01121         PrintView* pView = (PrintView*)(pPSRegion->GetRenderView());
01122         ERROR2IF(pView==NULL,FALSE,"No printview in this PS render region");
01123         ERROR3IF(!pView->IsKindOf(CC_RUNTIME_CLASS(PrintView)), "Not a PrintView in PSPrintDC output token");
01124 
01125         // Get the current patch clip rectangle
01126         DocRect theRect = pView->PatchInfo.GetClipRect(FALSE,FALSE);
01127 
01128         // read the regions render matrix.
01129         Matrix RMatrix = pPSRegion->GetMatrix();
01130         Matrix IMatrix = RMatrix.Inverse();
01131         RMatrix.transform(&theRect.lo);
01132         RMatrix.transform(&theRect.hi);
01133         PrintView::CorrectRotatedRectangle((Rect*)&theRect);
01134         pView->PatchInfo.InflateRectBy(&theRect,TRUE,FALSE);
01135         IMatrix.transform(&theRect.lo);
01136         IMatrix.transform(&theRect.hi);
01137         PrintView::CorrectRotatedRectangle((Rect*)&theRect);
01138 
01139         // And finally write the clip region out
01140         if (!pPSRegion->WriteClipRegion(this, theRect))
01141             return FALSE;
01142     }
01143     return TRUE;
01144 }

Generated on Sat Nov 10 03:48:49 2007 for Camelot by  doxygen 1.4.4