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 }