From 14fa008ab8fdde920687fe80b67b6ca86072effb Mon Sep 17 00:00:00 2001 From: "pete.zha%sun.com" Date: Mon, 20 Sep 2004 06:46:16 +0000 Subject: [PATCH] bug 234470 There is something wrong with plugin printing on linux or unix r=leon.sha@sun.com sr=roc patch=roland --- gfx/public/nsIRenderingContext.h | 47 ++--- gfx/src/nsRenderingContextImpl.h | 2 +- gfx/src/ps/Makefile.in | 2 + gfx/src/ps/nsEPSObjectPS.cpp | 185 +++++++++++++++++ gfx/src/ps/nsEPSObjectPS.h | 97 +++++++++ gfx/src/ps/nsPostScriptObj.cpp | 68 ++++++- gfx/src/ps/nsPostScriptObj.h | 14 ++ gfx/src/ps/nsRenderingContextPS.cpp | 58 ++++-- gfx/src/ps/nsRenderingContextPS.h | 15 +- gfx/src/shared/nsRenderingContextImpl.cpp | 2 +- gfx/src/xprint/nsRenderingContextXp.cpp | 48 ++++- gfx/src/xprint/nsRenderingContextXp.h | 2 +- gfx/src/xprint/nsXPrintContext.cpp | 237 +++++++++++++++++++++- gfx/src/xprint/nsXPrintContext.h | 2 +- layout/generic/nsObjectFrame.cpp | 47 +---- layout/html/base/src/nsObjectFrame.cpp | 47 +---- 16 files changed, 723 insertions(+), 150 deletions(-) create mode 100644 gfx/src/ps/nsEPSObjectPS.cpp create mode 100644 gfx/src/ps/nsEPSObjectPS.h diff --git a/gfx/public/nsIRenderingContext.h b/gfx/public/nsIRenderingContext.h index 518fb97ba163..81db0662f5dd 100644 --- a/gfx/public/nsIRenderingContext.h +++ b/gfx/public/nsIRenderingContext.h @@ -46,6 +46,7 @@ #include "nsColor.h" #include "nsCoord.h" #include "nsIDrawingSurface.h" +#include class nsIWidget; class nsIFontMetrics; @@ -63,7 +64,6 @@ struct nsTextDimensions; struct nsBoundingMetrics; #endif - /* gfx2 */ class imgIContainer; @@ -777,44 +777,21 @@ public: const nsRect * aTargetRect) = 0; /** - * Render the provided postscript fragment to the current rendering + * Render an encapsulated postscript object onto the current rendering * surface. * - * If the device does not support rendering postscript fragments, then - * NS_ERROR_NOT_IMPLEMENTED is returned. Otherwise, the device will - * attempt to incorporate the provided PostScript into the document. + * The EPS object must conform to the EPSF standard. See Adobe + * specification #5002, "Encapsulated PostScript File Format Specification" + * at . + * In particular, the EPS object must contain a BoundingBox comment. * - * The provided postscript runs within the following environment: - * - * 1) The coordinate system is scaled to points (1/72th of an inch). - * 2) The origin (coordinate [0,0]) is at the top left corner of the - * page's printable region. - * 3) The Y axis increases downward. - * - * This must be called after nsIDeviceContext::BeginPage() and - * before nsIDeviceContext::EndPage(). Before calling this function, the - * caller must call PushState(), which effectively performs a "gsave". - * After calling this function, the caller must call PopState(), which - * effectively performs a "grestore". There may be at most one call to - * RenderPostScriptDataFragment() between any PushState()/PopState() pair. - * - * The caller may draw to any part of the page, though well-behaved - * callers will limit themselves to a region designated to them in - * cooperation with the layout module. The PostScript code fragment - * MUST NOT contain any code which begins a new page. - * - * Mozilla currently targets level 2 PostScript. The PostScript code - * fragment should not make use of any level 3 (or later) PostScript - * features. - * - * The PostScript fragment may be included more than once in the output, - * e.g. if the region it's expected to render is split over two pages. - * - * @param aData - PostScript fragment data - * @param aLength - Length of PostScript fragment data - * @return error status + * @param aRect Rectangle in which to render the EPSF. + * @param aDataFile - plugin data stored in a file + * @return NS_OK for success, or a suitable error value. + * NS_ERROR_NOT_IMPLEMENTED is returned if the rendering context + * doesn't support rendering EPSF, */ - NS_IMETHOD RenderPostScriptDataFragment(const unsigned char *aData, unsigned long aDatalen) = 0; + NS_IMETHOD RenderEPS(const nsRect& aRect, FILE *aDataFile) = 0; }; //modifiers for text rendering diff --git a/gfx/src/nsRenderingContextImpl.h b/gfx/src/nsRenderingContextImpl.h index 2ec780246633..165b2c8678ea 100644 --- a/gfx/src/nsRenderingContextImpl.h +++ b/gfx/src/nsRenderingContextImpl.h @@ -104,7 +104,7 @@ public: NS_IMETHOD DrawImage(imgIContainer *aImage, const nsRect & aSrcRect, const nsRect & aDestRect); NS_IMETHOD DrawTile(imgIContainer *aImage, nscoord aXOffset, nscoord aYOffset, const nsRect * aTargetRect); - NS_IMETHOD RenderPostScriptDataFragment(const unsigned char *aData, unsigned long aDatalen); + NS_IMETHOD RenderEPS(const nsRect& aRect, FILE *aDataFile); protected: virtual ~nsRenderingContextImpl(); diff --git a/gfx/src/ps/Makefile.in b/gfx/src/ps/Makefile.in index 53eb78b9b2f8..b3cdba30e502 100644 --- a/gfx/src/ps/Makefile.in +++ b/gfx/src/ps/Makefile.in @@ -66,6 +66,7 @@ EXTRA_DSO_LIBS += gkgfx CPPSRCS = \ nsDeviceContextPS.cpp \ nsFontMetricsPS.cpp \ + nsEPSObjectPS.cpp \ nsRenderingContextPS.cpp \ nsPostScriptObj.cpp \ nsAFMObject.cpp \ @@ -80,6 +81,7 @@ EXPORTS = \ nsGfxPSCID.h \ nsIDeviceContextSpecPS.h \ nsTempfilePS.h \ + nsEPSObjectPS.h \ $(NULL) EXPORT_RESOURCE = \ diff --git a/gfx/src/ps/nsEPSObjectPS.cpp b/gfx/src/ps/nsEPSObjectPS.cpp new file mode 100644 index 000000000000..974ab186fc7f --- /dev/null +++ b/gfx/src/ps/nsEPSObjectPS.cpp @@ -0,0 +1,185 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ex: set tabstop=8 softtabstop=4 shiftwidth=4 expandtab: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is developed for mozilla. + * + * The Initial Developer of the Original Code is + * Kenneth Herron . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roland Mainz + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsEPSObjectPS.h" +#include "prprf.h" + + +/* For details on the EPSF spec, see Adobe specification #5002, + * "Encapsulated PostScript File Format Specification". The document + * structuring conventions are described in Specificion #5001, + * "PostScript Language Document Structuring Conventions Specification". + */ + + +/** ------------------------------------------------------------------ + * Constructor + */ + +nsEPSObjectPS::nsEPSObjectPS(const char *aData, unsigned long aDataLength) : + mStatus(NS_ERROR_INVALID_ARG), + mData(nsnull), + mDataLength(0UL), + mCurrPos(nsnull), + mBBllx(0.0), + mBBlly(0.0), + mBBurx(0.0), + mBBury(0.0) +{ + mData = aData; + mDataLength = aDataLength; + + NS_PRECONDITION(aData != nsnull, "aData == nsnull"); + NS_PRECONDITION(aDataLength > 0UL, "No data"); + + Reset(); + Parse(); +} + +/** ------------------------------------------------------------------ + * Read one line from the file handle into the buffer, following rules + * for EPS data. The line terminator is not copied into the buffer. + * + * EPS file lines must be less than 256 characters, not including + * line terminators. Lines may be terminated by CR, LF, CRLF, or LFCR. + * See EPSF spec, section 2.9, "Miscellaneous Constraints". + * + * @param aBuffer Buffer in which to place the EPSF text. + * @param aBufSiz Size of aBuffer + * @param aSrc FILE opened for reading + * @return PR_TRUE if a line could be read into aBuffer successfully. + * PR_FALSE if EOF or an I/O error was encountered without reading + * any data, or if the line being read is too large for + * the buffer. + */ + +PRBool +nsEPSObjectPS::EPSFFgets(nsACString& aBuffer) +{ + aBuffer.Truncate(); + while (1) { + int ch = *mCurrPos++; + if ('\n' == ch) { + /* Eat any following carriage return */ + ch = *mCurrPos++; + if ((mCurrPos < (mData + mDataLength)) && ('\r' != ch)) + mCurrPos--; + return PR_TRUE; + } + else if ('\r' == ch) { + /* Eat any following line feed */ + ch = *mCurrPos++; + if ((mCurrPos < (mData + mDataLength)) && ('\n' != ch)) + mCurrPos--; + return PR_TRUE; + } + else if (mCurrPos >= (mData + mDataLength)) { + /* If we read any text before the EOF, return true. */ + return !aBuffer.IsEmpty(); + } + + /* Normal case */ + aBuffer.Append((char)ch); + } +} + +/** ------------------------------------------------------------------ + * Reset current position in data + */ +void +nsEPSObjectPS::Reset() +{ + mCurrPos = mData; +} + +/** ------------------------------------------------------------------ + * Parse the EPSF and initialize the object accordingly. + */ + +void +nsEPSObjectPS::Parse() +{ + nsCAutoString line; + + Reset(); + while (EPSFFgets(line)) { + if (PR_sscanf(line.get(), "%%%%BoundingBox: %lf %lf %lf %lf", + &mBBllx, &mBBlly, &mBBurx, &mBBury) == 4) { + mStatus = NS_OK; + return; + } + } + mStatus = NS_ERROR_INVALID_ARG; +} + + +/** ------------------------------------------------------------------ + * Write the EPSF to the specified file handle. + * @return NS_OK if the entire EPSF was written without error, or + * else a suitable error code. + */ +nsresult +nsEPSObjectPS::WriteTo(FILE *aDest) +{ + NS_PRECONDITION(NS_SUCCEEDED(mStatus), "Bad status"); + + nsCAutoString line; + PRBool inPreview = PR_FALSE; + + Reset(); + while (EPSFFgets(line)) { + if (inPreview) { + /* filter out the print-preview section */ + if (StringBeginsWith(line, NS_LITERAL_CSTRING("%%EndPreview"))) + inPreview = PR_FALSE; + continue; + } + else if (StringBeginsWith(line, NS_LITERAL_CSTRING("%%BeginPreview:"))){ + inPreview = PR_TRUE; + continue; + } + + /* Output the EPSF with this platform's line terminator */ + fwrite(line.get(), line.Length(), 1, aDest); + putc('\n', aDest); + } + return NS_OK; +} + + diff --git a/gfx/src/ps/nsEPSObjectPS.h b/gfx/src/ps/nsEPSObjectPS.h new file mode 100644 index 000000000000..e83b51b564b9 --- /dev/null +++ b/gfx/src/ps/nsEPSObjectPS.h @@ -0,0 +1,97 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ex: set tabstop=8 softtabstop=4 shiftwidth=4 expandtab: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is developed for mozilla. + * + * The Initial Developer of the Original Code is + * Kenneth Herron . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roland Mainz + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef NSEPSOBJECTPS_H +#define NSEPSOBJECTPS_H + +#include +#include + +#include "nscore.h" +#include "prtypes.h" +#include "nsString.h" +#include "nsReadableUtils.h" + +class nsEPSObjectPS { + public: + /** --------------------------------------------------- + * Constructor + */ + nsEPSObjectPS(const char *aData, unsigned long aDataLength); + + /** --------------------------------------------------- + * @return the result code from parsing the EPS data. + * If the return value is not NS_OK, the EPS object is + * invalid and should not be used further. + */ + nsresult GetStatus() { return mStatus; }; + + /** --------------------------------------------------- + * Return Bounding box coordinates: lower left x, + * lower left y, upper right x, upper right y. + */ + PRFloat64 GetBoundingBoxLLX() { return mBBllx; }; + PRFloat64 GetBoundingBoxLLY() { return mBBlly; }; + PRFloat64 GetBoundingBoxURX() { return mBBurx; }; + PRFloat64 GetBoundingBoxURY() { return mBBury; }; + + /** --------------------------------------------------- + * Write the EPS object to the provided file stream. + * @return NS_OK if the EPS was written successfully, or + * a suitable error code. + */ + nsresult WriteTo(FILE *aDest); + + private: + nsresult mStatus; + const char *mData; + unsigned long mDataLength; + const char *mCurrPos; + PRFloat64 mBBllx, + mBBlly, + mBBurx, + mBBury; + + void Parse(); + void Reset(); + PRBool EPSFFgets(nsACString& aBuffer); +}; + +#endif /* !NSEPSOBJECTPS_H */ + diff --git a/gfx/src/ps/nsPostScriptObj.cpp b/gfx/src/ps/nsPostScriptObj.cpp index a7310592bfde..d85c83d556f5 100644 --- a/gfx/src/ps/nsPostScriptObj.cpp +++ b/gfx/src/ps/nsPostScriptObj.cpp @@ -2227,7 +2227,7 @@ void nsPostScriptObj::scale(float aX, float aY) { fprintf(mScriptFP, "%s %s scale\n", - fpCString(aX).get(), fpCString(aX).get()); + fpCString(aX).get(), fpCString(aY).get()); } /** --------------------------------------------------- @@ -2752,3 +2752,69 @@ nsPostScriptObj::initlanggroup(FILE *aHandle) PrefEnumCallback, (void *) &closure); } + + /** --------------------------------------------------- + * See documentation in nsPostScriptObj.h + * @update 3/6/2004 kherron + * @update 3/25/2004 dantifer + */ +nsresult +nsPostScriptObj::render_eps(const nsRect& aRect, nsEPSObjectPS &anEPS) +{ + FILE *bfile = mScriptFP; + nsresult rv; + + NS_PRECONDITION(nsnull != bfile, "No document body file handle"); + + /* Set up EPSF state. See Adobe spec #5002 section 3.2 */ + fputs( + "/b4_Inc_state save def\n" + "/dict_count countdictstack def\n" + "/op_count count 1 sub def\n" + "userdict begin\n" + "/showpage { } def\n" + "0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin\n" + "10 setmiterlimit [ ] 0 setdash newpath\n" + "/languagelevel where\n" + "{pop languagelevel\n" + " 1 ne\n" + " {false setstrokeadjust false setoverprint\n" + " } if\n" + "} if\n", + bfile); + + /* Set up a clipping region around the EPS rectangle */ + box(aRect.x, aRect.y, aRect.width, aRect.height); + clip(); + + /* translate to the lower left corner of the rectangle */ + translate(aRect.x, aRect.y + aRect.height); + + /* Rescale */ + scale( + aRect.width / (anEPS.GetBoundingBoxURX() - anEPS.GetBoundingBoxLLX()), + -(aRect.height / (anEPS.GetBoundingBoxURY() - anEPS.GetBoundingBoxLLY())) + ); + + /* Translate to the EPSF origin. Can't use translate() here because + * it takes integers. + */ + fprintf(bfile, "%s %s translate\n", + fpCString(-anEPS.GetBoundingBoxLLX()).get(), + fpCString(-anEPS.GetBoundingBoxLLY()).get() + ); + + /* embeding EPS file content */ + comment("%BeginDocument: Mozilla-Internal"); + rv = anEPS.WriteTo(bfile); + comment("%EndDocument"); + + /* Restore previous state */ + fputs( + "count op_count sub { pop } repeat\n" + "countdictstack dict_count sub { end } repeat\n" + "b4_Inc_state restore\n", + bfile); + + return rv; +} diff --git a/gfx/src/ps/nsPostScriptObj.h b/gfx/src/ps/nsPostScriptObj.h index c67b087912aa..b1d31cc38653 100644 --- a/gfx/src/ps/nsPostScriptObj.h +++ b/gfx/src/ps/nsPostScriptObj.h @@ -56,6 +56,7 @@ #include "nsIDeviceContextSpecPS.h" #include "nsIPersistentProperties2.h" #include "nsTempfilePS.h" +#include "nsEPSObjectPS.h" class nsIImage; class nsIAtom; @@ -156,6 +157,10 @@ struct PSContext_{ }; typedef struct PSContext_ PSContext; +struct PSBoundingBox { /* For BeginEPSF() */ + float llx, lly, urx, ury; +}; + #ifdef __cplusplus class nsPostScriptObj { @@ -393,6 +398,15 @@ public: * @update 6/1/2000 katakai */ void preshow(const PRUnichar* aText, int aLen); + + /** --------------------------------------------------- + * Render an encapsulated postscript object into the document + * @update 3/6/2004 kherron + * @param aRect Rectangle in which to render the EPS + * @param anEPS Object to render + * @return NS_OK if the object was copied successfully. + */ + nsresult render_eps(const nsRect& aRect, nsEPSObjectPS &anEPS); void settitle(PRUnichar * aTitle); diff --git a/gfx/src/ps/nsRenderingContextPS.cpp b/gfx/src/ps/nsRenderingContextPS.cpp index 6f8db13abb05..68fe8a1622f0 100644 --- a/gfx/src/ps/nsRenderingContextPS.cpp +++ b/gfx/src/ps/nsRenderingContextPS.cpp @@ -48,7 +48,12 @@ #include "gfxIImageFrame.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" +#include "nsEPSObjectPS.h" +#include "nsLocalFile.h" +#include +#include +#include #include #define NS_PIXELS_TO_POINTS(x) ((x) * 10) @@ -1365,24 +1370,49 @@ NS_IMETHODIMP nsRenderingContextPS::RetrieveCurrentNativeGraphicData(PRUint32 * } /** --------------------------------------------------- - * Output postscript supplied by the caller to the print job. The - * caller should have already called PushState() (and preferably - * SetClipRect()). - * @update 9/31/2003 kherron - * @param aData Buffer containing postscript to be output - * aDataLen Number of characters in aData - * @return NS_OK + * See documentation in nsRenderingContextPS.h and + * gfx/public/nsIRenderingContext.h. + * @update 3/6/2004 + * @param @param aRect Rectangle in which to render the EPSF. + * @param aDataFile - data stored in a file + * @return NS_OK or a suitable error code. */ -NS_IMETHODIMP nsRenderingContextPS::RenderPostScriptDataFragment(const unsigned char *aData, unsigned long aDatalen) +NS_IMETHODIMP nsRenderingContextPS::RenderEPS(const nsRect& aRect, FILE *aDataFile) { - NS_ASSERTION(mPSObj != NULL, "No nsPostScriptObj"); + nsresult rv; + int fd; + const char *data; + size_t datalen; - // Reset the coordinate system to point-sized. The origin and Y axis - // orientation are already correct. - mPSObj->scale(TWIPS_PER_POINT_FLOAT, TWIPS_PER_POINT_FLOAT); - fwrite(aData, aDatalen, 1, mPSObj->GetScriptHandle()); + /* EPSFs aren't supposed to have side effects, so if width or height is + * zero, just return. */ + if ((aRect.width == 0) || (aRect.height == 0)) + return NS_OK; - return NS_OK; + /* Get file size */ + fseek(aDataFile, 0, SEEK_END); + datalen = ftell(aDataFile); + + fflush(aDataFile); + fd = fileno(aDataFile); + data = (const char *)mmap(0, datalen, PROT_READ, MAP_SHARED, fd, 0); + if (!data) + return nsresultForErrno(errno); + + nsEPSObjectPS eps(data, datalen); + if (NS_FAILED(eps.GetStatus())) { + munmap((void *)data, datalen); + return NS_ERROR_INVALID_ARG; + } + + nsRect trect = aRect; + mTranMatrix->TransformCoord(&trect.x, &trect.y, &trect.width, &trect.height); + + rv = mPSObj->render_eps(trect, eps); + + munmap((void *)data, datalen); + + return rv; } #ifdef NOTNOW diff --git a/gfx/src/ps/nsRenderingContextPS.h b/gfx/src/ps/nsRenderingContextPS.h index c84024eb57da..7700e572db3f 100644 --- a/gfx/src/ps/nsRenderingContextPS.h +++ b/gfx/src/ps/nsRenderingContextPS.h @@ -275,15 +275,14 @@ public: #endif /* MOZ_MATHML */ /** --------------------------------------------------- - * Output postscript supplied by the caller to the print job. The - * caller should have already called PushState() (and preferably - * SetClipRect()). - * @update 9/31/2003 kherron - * @param aData Buffer containing postscript to be output - * aDataLen Number of characters in aData - * @return NS_OK + * Output an encapsulated postscript file to the print job. See + * documentation in gfx/public/nsIRenderingContext.h. + * @update 3/6/2004 kherron + * @param aRect Rectangle in which to render the EPS + * @param aDataFile - data stored in a file + * @return NS_OK or a suitable error code. */ - NS_IMETHOD RenderPostScriptDataFragment(const unsigned char *aData, unsigned long aDatalen); + NS_IMETHOD RenderEPS(const nsRect& aRect, FILE *aDataFile); private: nsresult CommonInit(void); diff --git a/gfx/src/shared/nsRenderingContextImpl.cpp b/gfx/src/shared/nsRenderingContextImpl.cpp index 94b8934e2c2b..1dc53d0386e6 100644 --- a/gfx/src/shared/nsRenderingContextImpl.cpp +++ b/gfx/src/shared/nsRenderingContextImpl.cpp @@ -398,7 +398,7 @@ nsRenderingContextImpl::FlushRect(nscoord aX, nscoord aY, nscoord aWidth, nscoor } NS_IMETHODIMP -nsRenderingContextImpl::RenderPostScriptDataFragment(const unsigned char *aData, unsigned long aDatalen) +nsRenderingContextImpl::RenderEPS(const nsRect& aRect, FILE *aDataFile) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/gfx/src/xprint/nsRenderingContextXp.cpp b/gfx/src/xprint/nsRenderingContextXp.cpp index 43dc605d72f9..a1672c473751 100644 --- a/gfx/src/xprint/nsRenderingContextXp.cpp +++ b/gfx/src/xprint/nsRenderingContextXp.cpp @@ -49,6 +49,9 @@ #include "gfxIImageFrame.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" +#include "nsLocalFile.h" +#include +#include #ifdef PR_LOGGING static PRLogModuleInfo *RenderingContextXpLM = PR_NewLogModule("RenderingContextXp"); @@ -197,11 +200,52 @@ nsRenderingContextXp::CopyOffScreenBits(nsIDrawingSurface* aSrcSurf, PRInt32 aSr } NS_IMETHODIMP -nsRenderingContextXp::RenderPostScriptDataFragment(const unsigned char *aData, unsigned long aDatalen) +nsRenderingContextXp::RenderEPS(const nsRect& aRect, FILE *aDataFile) { + nsresult rv; + int fd; + const unsigned char *data; + size_t datalen; + PR_LOG(RenderingContextXpLM, PR_LOG_DEBUG, ("nsRenderingContextXp::RenderPostScriptDataFragment()\n")); - return mPrintContext->RenderPostScriptDataFragment(aData, aDatalen); + /* Get file size */ + fseek(aDataFile, 0, SEEK_END); + datalen = ftell(aDataFile); + + PR_LOG(RenderingContextXpLM, PR_LOG_DEBUG, ("file size=%ld\n", (long)datalen)); + + /* Safeguard against bogus values + * (make sure we clamp the size to a reasonable value (say... 128 MB)) */ + if (datalen <= 0 || datalen > (128 * 1024 * 1024)) { + PR_LOG(RenderingContextXpLM, PR_LOG_DEBUG, ("error: file size %ld too large\n", (long)datalen)); + return NS_ERROR_FAILURE; + } + + fflush(aDataFile); + fd = fileno(aDataFile); + PR_LOG(RenderingContextXpLM, PR_LOG_DEBUG, ("fileno=%d\n", fd)); + data = (const unsigned char *)mmap(0, datalen, PROT_READ, MAP_SHARED, fd, 0); + if (!data) { + int saved_errno = errno; + PR_LOG(RenderingContextXpLM, PR_LOG_DEBUG, ("mmap() failure, errno=%s/%d\n", + strerror(saved_errno), saved_errno)); + return nsresultForErrno(saved_errno); + } + + PushState(); + + nsRect trect = aRect; + mTranMatrix->TransformCoord(&trect.x, &trect.y, &trect.width, &trect.height); + UpdateGC(); + rv = mPrintContext->RenderEPS(trect, data, datalen); + + PopState(); + + munmap((void *)data, datalen); + + return rv; } + diff --git a/gfx/src/xprint/nsRenderingContextXp.h b/gfx/src/xprint/nsRenderingContextXp.h index 9e7f3b1a03bb..e3f76586bfe1 100644 --- a/gfx/src/xprint/nsRenderingContextXp.h +++ b/gfx/src/xprint/nsRenderingContextXp.h @@ -73,7 +73,7 @@ class nsRenderingContextXp : public nsRenderingContextXlib NS_IMETHOD CopyOffScreenBits(nsIDrawingSurface* aSrcSurf, PRInt32 aSrcX, PRInt32 aSrcY, const nsRect &aDestBounds, PRUint32 aCopyFlags); - NS_IMETHOD RenderPostScriptDataFragment(const unsigned char *aData, unsigned long aDatalen); + NS_IMETHOD RenderEPS(const nsRect& aRect, FILE *aDataFile); protected: nsXPrintContext *mPrintContext; /* identical to |mRenderingSurface| diff --git a/gfx/src/xprint/nsXPrintContext.cpp b/gfx/src/xprint/nsXPrintContext.cpp index 67725aeb87a4..71592a6f9ad4 100644 --- a/gfx/src/xprint/nsXPrintContext.cpp +++ b/gfx/src/xprint/nsXPrintContext.cpp @@ -20,7 +20,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * Roland Mainz + * Roland Mainz * Leon Sha * * Alternatively, the contents of this file may be used under the terms of @@ -57,6 +57,9 @@ #include "nsDeviceContextXP.h" #include "xprintutil.h" #include "prenv.h" /* for PR_GetEnv */ +#include "prprf.h" +#include "plstr.h" +#include "nsPrintfCString.h" /* NS_XPRINT_RGB_DITHER: Macro to check whether we should dither or not. * In theory we only have to look at the visual and depth ("TrueColor" with @@ -1378,13 +1381,166 @@ NS_IMETHODIMP nsXPrintContext::GetPrintResolution(int &aPrintResolution) return NS_ERROR_FAILURE; } -NS_IMETHODIMP nsXPrintContext::RenderPostScriptDataFragment(const unsigned char *aData, unsigned long aDatalen) +/* nsEPSObjectXp - a EPSF helper class + * For details on the EPSF spec, see Adobe specification #5002, + * "Encapsulated PostScript File Format Specification". The document + * structuring conventions are described in Specificion #5001, + * "PostScript Language Document Structuring Conventions Specification". + */ +class nsEPSObjectXp { + public: + /** --------------------------------------------------- + * Constructor + */ + nsEPSObjectXp(const unsigned char *aData, unsigned long aDataLength) : + mStatus(NS_ERROR_INVALID_ARG), + mData(nsnull), + mDataLength(0UL), + mCurrPos(nsnull), + mBBllx(0.0), + mBBlly(0.0), + mBBurx(0.0), + mBBury(0.0) + { + mData = aData; + mDataLength = aDataLength; + + NS_PRECONDITION(aData != nsnull, "aData == nsnull"); + NS_PRECONDITION(aDataLength > 0UL, "No data"); + + Reset(); + Parse(); + } + + static inline + PRBool IsEPSF(const unsigned char *aData, unsigned long aDataLength) + { + /* First line (assuming a single line of PostScript line is not longer + * than 256 chars) should usually look like "%!PS-Adobe-3.0 EPSF-3.0" + * (version numbers may be different) */ + return (PL_strnstr(aData, " EPSF-", PR_MIN(aDataLength, 256)) != nsnull); + } + + + /** --------------------------------------------------- + * @return the result code from parsing the EPS data. + * If the return value is not NS_OK, the EPS object is + * invalid and should not be used further. + */ + nsresult GetStatus() { return mStatus; }; + + /** --------------------------------------------------- + * Return Bounding box coordinates: lower left x, + * lower left y, upper right x, upper right y. + */ + inline void GetBoundingBox(PRFloat64 &aBBllx, + PRFloat64 &aBBlly, + PRFloat64 &aBBurx, + PRFloat64 &aBBury) + { + aBBllx = mBBllx; + aBBlly = mBBlly; + aBBurx = mBBurx; + aBBury = mBBury; + }; + + /** --------------------------------------------------- + * Append the EPS object to the provided string object. + */ + void AppendTo(nsACString& aDestBuffer) + { + nsCAutoString line; + PRBool inPreview = PR_FALSE; + + Reset(); + while (EPSFFgets(line)) { + if (inPreview) { + /* filter out the print-preview section */ + if (StringBeginsWith(line, NS_LITERAL_CSTRING("%%EndPreview"))) + inPreview = PR_FALSE; + continue; + } + else if (StringBeginsWith(line, NS_LITERAL_CSTRING("%%BeginPreview:"))){ + inPreview = PR_TRUE; + continue; + } + + /* Output the EPSF with this platform's line terminator */ + aDestBuffer.Append(line.get(), line.Length()); + aDestBuffer.Append(NS_LITERAL_CSTRING("\n")); + } + } + private: + nsresult mStatus; + const unsigned char *mData; + unsigned long mDataLength; + const char *mCurrPos; + PRFloat64 mBBllx, + mBBlly, + mBBurx, + mBBury; + + void Parse() + { + nsCAutoString line; + + Reset(); + while (EPSFFgets(line)) { + if (PR_sscanf(line.get(), "%%%%BoundingBox: %lf %lf %lf %lf", + &mBBllx, &mBBlly, &mBBurx, &mBBury) == 4) { + mStatus = NS_OK; + return; + } + } + mStatus = NS_ERROR_INVALID_ARG; + } + + inline void Reset() + { + mCurrPos = mData; + } + + PRBool EPSFFgets(nsACString& aBuffer) + { + aBuffer.Truncate(); + + if (!mCurrPos) + return PR_FALSE; + + while (1) { + int ch = *mCurrPos++; + if ('\n' == ch) { + /* Eat any following carriage return */ + ch = *mCurrPos++; + if ((mCurrPos < (mData + mDataLength)) && ('\r' != ch)) + mCurrPos--; + return PR_TRUE; + } + else if ('\r' == ch) { + /* Eat any following line feed */ + ch = *mCurrPos++; + if ((mCurrPos < (mData + mDataLength)) && ('\n' != ch)) + mCurrPos--; + return PR_TRUE; + } + else if (mCurrPos >= (mData + mDataLength)) { + /* If we read any text before the EOF, return true. */ + return !aBuffer.IsEmpty(); + } + + /* Normal case */ + aBuffer.Append((char)ch); + } + } +}; + +NS_IMETHODIMP nsXPrintContext::RenderEPS(const nsRect& aRect, const unsigned char *aData, unsigned long aDatalen) { PR_LOG(nsXPrintContextLM, PR_LOG_DEBUG, - ("nsXPrintContext::RenderPostScriptDataFragment(aData, aDatalen=%d)\n", aDatalen)); + ("nsXPrintContext::EPS(aData, aDatalen=%d)\n", aDatalen)); char xp_formats_supported[] = "xp-embedded-formats-supported"; - const char *embedded_formats_supported = XpGetOneAttribute(mPDisplay, mPContext,XPPrinterAttr, xp_formats_supported); + const char *embedded_formats_supported = XpGetOneAttribute(mPDisplay, mPContext, XPPrinterAttr, xp_formats_supported); /* Check whether "PostScript Level 2" is supported as embedding format * (The content of the "xp-embedded-formats-supported" attribute needs @@ -1394,7 +1550,7 @@ NS_IMETHODIMP nsXPrintContext::RenderPostScriptDataFragment(const unsigned char * To avoid problems we simply use |PL_strcasestr()| (case-insensitive * strstr()) instead of |strstr()| here...) */ - if( embedded_formats_supported == NULL ) + if (embedded_formats_supported == NULL) { PR_LOG(nsXPrintContextLM, PR_LOG_DEBUG, ("nsXPrintContext::RenderPostScriptDataFragment(): Embedding data not supported for this DDX/Printer\n")); return NS_ERROR_FAILURE; @@ -1408,6 +1564,75 @@ NS_IMETHODIMP nsXPrintContext::RenderPostScriptDataFragment(const unsigned char XFree((void *)embedded_formats_supported); return NS_ERROR_FAILURE; } + + /* Temp buffer for EPSF data - this has to live outside the if()/else() + * block below to gurantee that the pointers we get from it are still + * valid when we feed the data to |XpPutDocumentData| */ + nsXPIDLCString aBuffer; + const unsigned char *embedData; + unsigned long embedDataLength; + + /* If the input is EPSF then we need to do some EPSF-specific handling */ + if (nsEPSObjectXp::IsEPSF(aData, aDatalen)) + { + PRFloat64 boxLLX, + boxLLY, + boxURX, + boxURY; + + nsEPSObjectXp epsfData(aData, aDatalen); + /* Non-EPSF data are not supported yet */ + if (NS_FAILED(epsfData.GetStatus())) + return NS_ERROR_INVALID_ARG; + + epsfData.GetBoundingBox(boxLLX, boxLLY, boxURX, boxURY); + + /* Size buffer that all the data in |aData| and context fits into the string */ + aBuffer.SetCapacity(aDatalen + 1024); + aBuffer.Assign("%%BeginDocument: Mozilla EPSF plugin data\n" + "/b4_Inc_state save def\n" + "/dict_count countdictstack def\n" + "/op_count count 1 sub def\n" + "userdict begin\n" + "/showpage { } def\n" + "0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin\n" + "10 setmiterlimit [ ] 0 setdash newpath\n" + "/languagelevel where\n" + "{pop languagelevel\n" + " 1 ne\n" + " {false setstrokeadjust false setoverprint\n" + " } if\n" + "} if\n"); + + /* translate to the lower left corner of the rectangle */ + aBuffer.Append(nsPrintfCString(64, "%f %f translate\n", + double(aRect.x), + double(aRect.y + aRect.height))); + + /* Rescale */ + aBuffer.Append(nsPrintfCString(64, "%f %f scale\n", + double(aRect.width / (boxURX - boxLLX)), + double(-(aRect.height / (boxURY - boxLLY))))); + + /* Translate to the EPSF origin. Can't use translate() here because + * it takes integers. + */ + aBuffer.Append(nsPrintfCString(64, "%f %f translate\n", double(-boxLLX), double(-boxLLY))); + + epsfData.AppendTo(aBuffer); + aBuffer.Append("count op_count sub { pop } repeat\n" + "countdictstack dict_count sub { end } repeat\n" + "b4_Inc_state restore\n" + "%%EndDocument\n"); + embedData = aBuffer.get(); + embedDataLength = aBuffer.Length(); + } + else + { + /* Non-EPSF codepath - pass the data as-is... */ + embedData = aData; + embedDataLength = aDatalen; + } /* Note that the embedded PostScript code uses the same resolution and * coordinate space as currently be used by the DDX (if you do not @@ -1421,7 +1646,7 @@ NS_IMETHODIMP nsXPrintContext::RenderPostScriptDataFragment(const unsigned char * supported options/option values) */ /* XpPutDocumentData() takes |const| input for all string arguments, only the X11 prototypes do not allow |const| yet */ - XpPutDocumentData(mPDisplay, mDrawable, (unsigned char *)aData, aDatalen, (char *)type, (char *)option); + XpPutDocumentData(mPDisplay, mDrawable, (unsigned char *)embedData, embedDataLength, (char *)type, (char *)option); XFree((void *)embedded_formats_supported); diff --git a/gfx/src/xprint/nsXPrintContext.h b/gfx/src/xprint/nsXPrintContext.h index 15f71c9c649d..a32941c718c3 100644 --- a/gfx/src/xprint/nsXPrintContext.h +++ b/gfx/src/xprint/nsXPrintContext.h @@ -73,7 +73,7 @@ public: NS_IMETHOD Init(nsDeviceContextXp *dc, nsIDeviceContextSpecXp *aSpec); NS_IMETHOD BeginPage(); NS_IMETHOD EndPage(); - NS_IMETHOD RenderPostScriptDataFragment(const unsigned char *aData, unsigned long aDatalen); + NS_IMETHOD RenderEPS(const nsRect& aRect, const unsigned char *aData, unsigned long aDatalen); NS_IMETHOD BeginDocument(PRUnichar * aTitle, PRUnichar* aPrintToFileName, PRInt32 aStartPage, PRInt32 aEndPage); NS_IMETHOD EndDocument(); NS_IMETHOD AbortDocument(); diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index d3455795fe79..a707d54f1947 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -1633,6 +1633,11 @@ nsObjectFrame::Paint(nsPresContext* aPresContext, npPrintInfo.type = NP_PRINT; npPrintInfo.fp = plugintmpfile; npprint.print.embedPrint.platformPrint = (void *)&npPrintInfo; + /* aDirtyRect contains the right information for ps print */ + window.x = aDirtyRect.x; + window.y = aDirtyRect.y; + window.width = aDirtyRect.width; + window.height = aDirtyRect.height; npprint.print.embedPrint.window = window; rv = pi->Print(&npprint); if (NS_FAILED(rv)) { @@ -1641,48 +1646,10 @@ nsObjectFrame::Paint(nsPresContext* aPresContext, return rv; } - unsigned char *pluginbuffer; - long fileLength; - - /* Get file size */ - fseek(plugintmpfile, 0, SEEK_END); - fileLength = ftell(plugintmpfile); - - /* Safeguard against bogus values - * (make sure we clamp the size to a reasonable value (say... 128 MB)) */ - if( fileLength <= 0 || fileLength > (128 * 1024 * 1024) ) { - PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, ("error: file size %ld too large\n", fileLength)); - fclose(plugintmpfile); - return NS_ERROR_FAILURE; - } - - pluginbuffer = new unsigned char[fileLength+1]; - if (!pluginbuffer) { - PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, ("error: no buffer memory for %ld bytes\n", fileLength+1)); - fclose(plugintmpfile); - return NS_ERROR_OUT_OF_MEMORY; - } - - /* Read data into temp. buffer */ - long numBytesRead = 0; - fseek(plugintmpfile, 0, SEEK_SET); - numBytesRead = fread(pluginbuffer, 1, fileLength, plugintmpfile); - - if( numBytesRead == fileLength ) { - PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, ("sending %ld bytes of PostScript data to printer\n", numBytesRead)); - - /* Send data to printer */ - aRenderingContext.RenderPostScriptDataFragment(pluginbuffer, numBytesRead); - } - else - { - PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, - ("error: bytes read in buffer (%ld) does not match file length (%ld)\n", - numBytesRead, fileLength)); - } + /* Send data to printer */ + rv = aRenderingContext.RenderEPS(aDirtyRect, plugintmpfile); fclose(plugintmpfile); - delete [] pluginbuffer; PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, ("plugin printing done, return code is %lx\n", (long)rv)); diff --git a/layout/html/base/src/nsObjectFrame.cpp b/layout/html/base/src/nsObjectFrame.cpp index d3455795fe79..a707d54f1947 100644 --- a/layout/html/base/src/nsObjectFrame.cpp +++ b/layout/html/base/src/nsObjectFrame.cpp @@ -1633,6 +1633,11 @@ nsObjectFrame::Paint(nsPresContext* aPresContext, npPrintInfo.type = NP_PRINT; npPrintInfo.fp = plugintmpfile; npprint.print.embedPrint.platformPrint = (void *)&npPrintInfo; + /* aDirtyRect contains the right information for ps print */ + window.x = aDirtyRect.x; + window.y = aDirtyRect.y; + window.width = aDirtyRect.width; + window.height = aDirtyRect.height; npprint.print.embedPrint.window = window; rv = pi->Print(&npprint); if (NS_FAILED(rv)) { @@ -1641,48 +1646,10 @@ nsObjectFrame::Paint(nsPresContext* aPresContext, return rv; } - unsigned char *pluginbuffer; - long fileLength; - - /* Get file size */ - fseek(plugintmpfile, 0, SEEK_END); - fileLength = ftell(plugintmpfile); - - /* Safeguard against bogus values - * (make sure we clamp the size to a reasonable value (say... 128 MB)) */ - if( fileLength <= 0 || fileLength > (128 * 1024 * 1024) ) { - PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, ("error: file size %ld too large\n", fileLength)); - fclose(plugintmpfile); - return NS_ERROR_FAILURE; - } - - pluginbuffer = new unsigned char[fileLength+1]; - if (!pluginbuffer) { - PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, ("error: no buffer memory for %ld bytes\n", fileLength+1)); - fclose(plugintmpfile); - return NS_ERROR_OUT_OF_MEMORY; - } - - /* Read data into temp. buffer */ - long numBytesRead = 0; - fseek(plugintmpfile, 0, SEEK_SET); - numBytesRead = fread(pluginbuffer, 1, fileLength, plugintmpfile); - - if( numBytesRead == fileLength ) { - PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, ("sending %ld bytes of PostScript data to printer\n", numBytesRead)); - - /* Send data to printer */ - aRenderingContext.RenderPostScriptDataFragment(pluginbuffer, numBytesRead); - } - else - { - PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, - ("error: bytes read in buffer (%ld) does not match file length (%ld)\n", - numBytesRead, fileLength)); - } + /* Send data to printer */ + rv = aRenderingContext.RenderEPS(aDirtyRect, plugintmpfile); fclose(plugintmpfile); - delete [] pluginbuffer; PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, ("plugin printing done, return code is %lx\n", (long)rv));