blob: 53274e4ce0ba6e10db4cc25ae1e20d4511b91002 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#include "psputil.hxx"
#include "glyphset.hxx"
#include "printergfx.hxx"
#include "printerjob.hxx"
#include "vcl/fontmanager.hxx"
#include "vcl/strhelper.hxx"
#include "vcl/printerinfomanager.hxx"
#include "tools/debug.hxx"
#include "tools/color.hxx"
#include "tools/poly.hxx"
using namespace psp ;
static const sal_Int32 nMaxTextColumn = 80;
GraphicsStatus::GraphicsStatus() :
mbArtItalic( false ),
mbArtBold( false ),
mnTextHeight( 0 ),
mnTextWidth( 0 ),
mfLineWidth( -1 )
{
}
/*
* non graphics graphics routines
*/
sal_Bool
PrinterGfx::Init (PrinterJob &rPrinterJob)
{
mpPageHeader = rPrinterJob.GetCurrentPageHeader ();
mpPageBody = rPrinterJob.GetCurrentPageBody ();
mnDepth = rPrinterJob.GetDepth ();
mnPSLevel = rPrinterJob.GetPostscriptLevel ();
mbColor = rPrinterJob.IsColorPrinter ();
mnDpi = rPrinterJob.GetResolution();
rPrinterJob.GetScale (mfScaleX, mfScaleY);
const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rPrinterJob.GetPrinterName() ) );
if( mpFontSubstitutes )
delete const_cast< ::std::hash_map<fontID,fontID>* >(mpFontSubstitutes);
if( rInfo.m_bPerformFontSubstitution )
mpFontSubstitutes = new ::std::hash_map< fontID, fontID >( rInfo.m_aFontSubstitutions );
else
mpFontSubstitutes = NULL;
mbUploadPS42Fonts = rInfo.m_pParser ? ( rInfo.m_pParser->isType42Capable() ? sal_True : sal_False ) : sal_False;
return sal_True;
}
sal_Bool
PrinterGfx::Init (const JobData& rData)
{
mpPageHeader = NULL;
mpPageBody = NULL;
mnDepth = rData.m_nColorDepth;
mnPSLevel = rData.m_nPSLevel ? rData.m_nPSLevel : (rData.m_pParser ? rData.m_pParser->getLanguageLevel() : 2 );
mbColor = rData.m_nColorDevice ? ( rData.m_nColorDevice == -1 ? sal_False : sal_True ) : (( rData.m_pParser ? (rData.m_pParser->isColorDevice() ? sal_True : sal_False ) : sal_True ) );
int nRes = rData.m_aContext.getRenderResolution();
mnDpi = nRes;
mfScaleX = (double)72.0 / (double)mnDpi;
mfScaleY = (double)72.0 / (double)mnDpi;
const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rData.m_aPrinterName ) );
if( mpFontSubstitutes )
delete const_cast< ::std::hash_map<fontID,fontID>* >(mpFontSubstitutes);
if( rInfo.m_bPerformFontSubstitution )
mpFontSubstitutes = new ::std::hash_map< fontID, fontID >( rInfo.m_aFontSubstitutions );
else
mpFontSubstitutes = NULL;
mbUploadPS42Fonts = rInfo.m_pParser ? ( rInfo.m_pParser->isType42Capable() ? sal_True : sal_False ) : sal_False;
return sal_True;
}
void
PrinterGfx::GetResolution (sal_Int32 &rDpiX, sal_Int32 &rDpiY) const
{
rDpiX = mnDpi;
rDpiY = mnDpi;
}
sal_uInt16
PrinterGfx::GetBitCount ()
{
return mnDepth;
}
PrinterGfx::PrinterGfx() :
mpPageHeader (NULL),
mpPageBody (NULL),
mnFontID (0),
mnFallbackID (0),
mnTextAngle (0),
mbTextVertical (false),
mrFontMgr (PrintFontManager::get()),
mbCompressBmp (sal_True),
maFillColor (0xff,0,0),
maTextColor (0,0,0),
maLineColor (0, 0xff, 0),
mpFontSubstitutes( NULL ),
mbStrictSO52Compatibility( false )
{
maVirtualStatus.mfLineWidth = 1.0;
maVirtualStatus.mnTextHeight = 12;
maVirtualStatus.mnTextWidth = 0;
maGraphicsStack.push_back( GraphicsStatus() );
}
PrinterGfx::~PrinterGfx()
{
/*
* #95810# the original reasoning why mpFontSubstitutes is a pointer was
* that applications should release all PrinterGfx when printers change
* because they are really invalid; the corresponding printers may have
* changed their settings or even not exist anymore.
*
* Alas, this is not always done real time. So we keep a local copy of
* the font substitutes now in case of bad timing.
*/
delete const_cast< ::std::hash_map<fontID,fontID>* >(mpFontSubstitutes);
}
void
PrinterGfx::Clear()
{
mpPageHeader = NULL;
mpPageBody = NULL;
mnFontID = 0;
maVirtualStatus = GraphicsStatus();
maVirtualStatus.mnTextHeight = 12;
maVirtualStatus.mnTextWidth = 0;
maVirtualStatus.mfLineWidth = 1.0;
mbTextVertical = false;
maLineColor = PrinterColor();
maFillColor = PrinterColor();
maTextColor = PrinterColor();
mbCompressBmp = sal_True;
mnDpi = 300;
mnDepth = 24;
mnPSLevel = 2;
mbColor = sal_True;
mnTextAngle = 0;
maClipRegion.clear();
maGraphicsStack.clear();
maGraphicsStack.push_back( GraphicsStatus() );
}
/*
* clip region handling
*/
void
PrinterGfx::ResetClipRegion()
{
maClipRegion.clear();
PSGRestore ();
PSGSave (); // get "clean" clippath
}
void
PrinterGfx::BeginSetClipRegion( sal_uInt32 )
{
maClipRegion.clear();
}
sal_Bool
PrinterGfx::UnionClipRegion (sal_Int32 nX,sal_Int32 nY,sal_Int32 nDX,sal_Int32 nDY)
{
if( nDX && nDY )
maClipRegion.push_back (Rectangle(Point(nX,nY ), Size(nDX,nDY)));
return sal_True;
}
sal_Bool
PrinterGfx::JoinVerticalClipRectangles( std::list< Rectangle >::iterator& it,
Point& rOldPoint, sal_Int32& rColumn )
{
sal_Bool bSuccess = sal_False;
std::list< Rectangle >::iterator tempit, nextit;
nextit = it;
++nextit;
std::list< Point > leftside, rightside;
Rectangle aLastRect( *it );
leftside.push_back( Point( it->Left(), it->Top() ) );
rightside.push_back( Point( it->Right()+1, it->Top() ) );
while( nextit != maClipRegion.end() )
{
tempit = nextit;
++tempit;
if( nextit->Top() == aLastRect.Bottom()+1 )
{
if(
( nextit->Left() >= aLastRect.Left() && nextit->Left() <= aLastRect.Right() ) // left endpoint touches last rectangle
||
( nextit->Right() >= aLastRect.Left() && nextit->Right() <= aLastRect.Right() ) // right endpoint touches last rectangle
||
( nextit->Left() <= aLastRect.Left() && nextit->Right() >= aLastRect.Right() ) // whole line touches last rectangle
)
{
if( aLastRect.GetHeight() > 1 ||
abs( aLastRect.Left() - nextit->Left() ) > 2 ||
abs( aLastRect.Right() - nextit->Right() ) > 2
)
{
leftside.push_back( Point( aLastRect.Left(), aLastRect.Bottom()+1 ) );
rightside.push_back( Point( aLastRect.Right()+1, aLastRect.Bottom()+1 ) );
}
aLastRect = *nextit;
leftside.push_back( aLastRect.TopLeft() );
rightside.push_back( aLastRect.TopRight() );
maClipRegion.erase( nextit );
}
}
nextit = tempit;
}
if( leftside.size() > 1 )
{
// push the last coordinates
leftside.push_back( Point( aLastRect.Left(), aLastRect.Bottom()+1 ) );
rightside.push_back( Point( aLastRect.Right()+1, aLastRect.Bottom()+1 ) );
// cool, we can concatenate rectangles
int nDX = -65536, nDY = 65536;
int nNewDX = 0, nNewDY = 0;
Point aLastPoint = leftside.front();
PSBinMoveTo (aLastPoint, rOldPoint, rColumn);
leftside.pop_front();
while( leftside.begin() != leftside.end() )
{
Point aPoint (leftside.front());
leftside.pop_front();
// may have been the last one
if( leftside.begin() != leftside.end() )
{
nNewDX = aPoint.X() - aLastPoint.X();
nNewDY = aPoint.Y() - aLastPoint.Y();
if( nNewDX == 0 && nDX == 0 )
continue;
if( nDX != 0 && nNewDX != 0 &&
(double)nNewDY/(double)nNewDX == (double)nDY/(double)nDX )
continue;
}
PSBinLineTo (aPoint, rOldPoint, rColumn);
aLastPoint = aPoint;
}
aLastPoint = rightside.back();
nDX = -65536;
nDY = 65536;
PSBinLineTo (aLastPoint, rOldPoint, rColumn);
rightside.pop_back();
while( rightside.begin() != rightside.end() )
{
Point aPoint (rightside.back());
rightside.pop_back();
if( rightside.begin() != rightside.end() )
{
nNewDX = aPoint.X() - aLastPoint.X();
nNewDY = aPoint.Y() - aLastPoint.Y();
if( nNewDX == 0 && nDX == 0 )
continue;
if( nDX != 0 && nNewDX != 0 &&
(double)nNewDY/(double)nNewDX == (double)nDY/(double)nDX )
continue;
}
PSBinLineTo (aPoint, rOldPoint, rColumn);
}
tempit = it;
++tempit;
maClipRegion.erase( it );
it = tempit;
bSuccess = sal_True;
}
return bSuccess;
}
void
PrinterGfx::EndSetClipRegion()
{
PSGRestore ();
PSGSave (); // get "clean" clippath
PSBinStartPath ();
Point aOldPoint (0, 0);
sal_Int32 nColumn = 0;
std::list< Rectangle >::iterator it = maClipRegion.begin();
while( it != maClipRegion.end() )
{
// try to concatenate adjacent rectangles
// first try in y direction, then in x direction
if( ! JoinVerticalClipRectangles( it, aOldPoint, nColumn ) )
{
// failed, so it is a single rectangle
PSBinMoveTo (it->TopLeft(), aOldPoint, nColumn );
PSBinLineTo (Point( it->Left(), it->Bottom()+1 ), aOldPoint, nColumn );
PSBinLineTo (Point( it->Right()+1, it->Bottom()+1 ), aOldPoint, nColumn );
PSBinLineTo (Point( it->Right()+1, it->Top() ), aOldPoint, nColumn );
++it;
}
}
PSBinEndPath ();
WritePS (mpPageBody, "closepath clip newpath\n");
maClipRegion.clear();
}
/*
* draw graphic primitives
*/
void
PrinterGfx::DrawRect (const Rectangle& rRectangle )
{
char pRect [128];
sal_Int32 nChar = 0;
nChar = psp::getValueOf (rRectangle.TopLeft().X(), pRect);
nChar += psp::appendStr (" ", pRect + nChar);
nChar += psp::getValueOf (rRectangle.TopLeft().Y(), pRect + nChar);
nChar += psp::appendStr (" ", pRect + nChar);
nChar += psp::getValueOf (rRectangle.GetWidth(), pRect + nChar);
nChar += psp::appendStr (" ", pRect + nChar);
nChar += psp::getValueOf (rRectangle.GetHeight(), pRect + nChar);
nChar += psp::appendStr (" ", pRect + nChar);
if( maFillColor.Is() )
{
PSSetColor (maFillColor);
PSSetColor ();
WritePS (mpPageBody, pRect, nChar);
WritePS (mpPageBody, "rectfill\n");
}
if( maLineColor.Is() )
{
PSSetColor (maLineColor);
PSSetColor ();
PSSetLineWidth ();
WritePS (mpPageBody, pRect, nChar);
WritePS (mpPageBody, "rectstroke\n");
}
}
void
PrinterGfx::DrawLine (const Point& rFrom, const Point& rTo)
{
if( maLineColor.Is() )
{
PSSetColor (maLineColor);
PSSetColor ();
PSSetLineWidth ();
PSMoveTo (rFrom);
PSLineTo (rTo);
WritePS (mpPageBody, "stroke\n" );
}
}
void
PrinterGfx::DrawPixel (const Point& rPoint, const PrinterColor& rPixelColor)
{
if( rPixelColor.Is() )
{
PSSetColor (rPixelColor);
PSSetColor ();
PSMoveTo (rPoint);
PSLineTo (Point (rPoint.X ()+1, rPoint.Y ()));
PSLineTo (Point (rPoint.X ()+1, rPoint.Y ()+1));
PSLineTo (Point (rPoint.X (), rPoint.Y ()+1));
WritePS (mpPageBody, "fill\n" );
}
}
void
PrinterGfx::DrawPolyLine (sal_uInt32 nPoints, const Point* pPath)
{
if( maLineColor.Is() && nPoints && pPath )
{
PSSetColor (maLineColor);
PSSetColor ();
PSSetLineWidth ();
PSBinCurrentPath (nPoints, pPath);
WritePS (mpPageBody, "stroke\n" );
}
}
void
PrinterGfx::DrawPolygon (sal_uInt32 nPoints, const Point* pPath)
{
// premature end of operation
if (!(nPoints > 1) || (pPath == NULL) || !(maFillColor.Is() || maLineColor.Is()))
return;
// setup closed path
Point aPoint( 0, 0 );
sal_Int32 nColumn( 0 );
PSBinStartPath();
PSBinMoveTo( pPath[0], aPoint, nColumn );
for( unsigned int n = 1; n < nPoints; n++ )
PSBinLineTo( pPath[n], aPoint, nColumn );
if( pPath[0] != pPath[nPoints-1] )
PSBinLineTo( pPath[0], aPoint, nColumn );
PSBinEndPath();
// fill the polygon first, then draw the border, note that fill and
// stroke reset the currentpath
// if fill and stroke, save the current path
if( maFillColor.Is() && maLineColor.Is())
PSGSave();
if (maFillColor.Is ())
{
PSSetColor (maFillColor);
PSSetColor ();
WritePS (mpPageBody, "eofill\n");
}
// restore the current path
if( maFillColor.Is() && maLineColor.Is())
PSGRestore();
if (maLineColor.Is ())
{
PSSetColor (maLineColor);
PSSetColor ();
PSSetLineWidth ();
WritePS (mpPageBody, "stroke\n");
}
}
void
PrinterGfx::DrawPolyPolygon (sal_uInt32 nPoly, const sal_uInt32* pSizes, const Point** pPaths )
{
// sanity check
if ( !nPoly || !pPaths || !(maFillColor.Is() || maLineColor.Is()))
return;
// setup closed path
for( unsigned int i = 0; i < nPoly; i++ )
{
Point aPoint( 0, 0 );
sal_Int32 nColumn( 0 );
PSBinStartPath();
PSBinMoveTo( pPaths[i][0], aPoint, nColumn );
for( unsigned int n = 1; n < pSizes[i]; n++ )
PSBinLineTo( pPaths[i][n], aPoint, nColumn );
if( pPaths[i][0] != pPaths[i][pSizes[i]-1] )
PSBinLineTo( pPaths[i][0], aPoint, nColumn );
PSBinEndPath();
}
// if eofill and stroke, save the current path
if( maFillColor.Is() && maLineColor.Is())
PSGSave();
// first draw area
if( maFillColor.Is() )
{
PSSetColor (maFillColor);
PSSetColor ();
WritePS (mpPageBody, "eofill\n");
}
// restore the current path
if( maFillColor.Is() && maLineColor.Is())
PSGRestore();
// now draw outlines
if( maLineColor.Is() )
{
PSSetColor (maLineColor);
PSSetColor ();
PSSetLineWidth ();
WritePS (mpPageBody, "stroke\n");
}
}
/*
* Bezier Polygon Drawing methods.
*/
void
PrinterGfx::DrawPolyLineBezier (sal_uInt32 nPoints, const Point* pPath, const sal_uInt8* pFlgAry)
{
const sal_uInt32 nBezString= 1024;
sal_Char pString[nBezString];
if ( nPoints > 1 && maLineColor.Is() && pPath )
{
PSSetColor (maLineColor);
PSSetColor ();
PSSetLineWidth ();
snprintf(pString, nBezString, "%li %li moveto\n", pPath[0].X(), pPath[0].Y());
WritePS(mpPageBody, pString);
// Handle the drawing of mixed lines mixed with curves
// - a normal point followed by a normal point is a line
// - a normal point followed by 2 control points and a normal point is a curve
for (unsigned int i=1; i<nPoints;)
{
if (pFlgAry[i] != POLY_CONTROL) //If the next point is a POLY_NORMAL, we're drawing a line
{
snprintf(pString, nBezString, "%li %li lineto\n", pPath[i].X(), pPath[i].Y());
i++;
}
else //Otherwise we're drawing a spline
{
if (i+2 >= nPoints)
return; //Error: wrong sequence of contol/normal points somehow
if ((pFlgAry[i] == POLY_CONTROL) && (pFlgAry[i+1] == POLY_CONTROL) &&
(pFlgAry[i+2] != POLY_CONTROL))
{
snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n",
pPath[i].X(), pPath[i].Y(),
pPath[i+1].X(), pPath[i+1].Y(),
pPath[i+2].X(), pPath[i+2].Y());
}
else
{
DBG_ERROR( "PrinterGfx::DrawPolyLineBezier: Strange output" );
}
i+=3;
}
WritePS(mpPageBody, pString);
}
// now draw outlines
WritePS (mpPageBody, "stroke\n");
}
}
void
PrinterGfx::DrawPolygonBezier (sal_uInt32 nPoints, const Point* pPath, const sal_uInt8* pFlgAry)
{
const sal_uInt32 nBezString = 1024;
sal_Char pString[nBezString];
// premature end of operation
if (!(nPoints > 1) || (pPath == NULL) || !(maFillColor.Is() || maLineColor.Is()))
return;
snprintf(pString, nBezString, "%li %li moveto\n", pPath[0].X(), pPath[0].Y());
WritePS(mpPageBody, pString); //Move to the starting point for the PolyPoygon
for (unsigned int i=1; i < nPoints;)
{
if (pFlgAry[i] != POLY_CONTROL)
{
snprintf(pString, nBezString, "%li %li lineto\n", pPath[i].X(), pPath[i].Y());
WritePS(mpPageBody, pString);
i++;
}
else
{
if (i+2 >= nPoints)
return; //Error: wrong sequence of contol/normal points somehow
if ((pFlgAry[i] == POLY_CONTROL) && (pFlgAry[i+1] == POLY_CONTROL) &&
(pFlgAry[i+2] != POLY_CONTROL))
{
snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n",
pPath[i].X(), pPath[i].Y(),
pPath[i+1].X(), pPath[i+1].Y(),
pPath[i+2].X(), pPath[i+2].Y());
WritePS(mpPageBody, pString);
}
else
{
DBG_ERROR( "PrinterGfx::DrawPolygonBezier: Strange output" );
}
i+=3;
}
}
// if fill and stroke, save the current path
if( maFillColor.Is() && maLineColor.Is())
PSGSave();
if (maFillColor.Is ())
{
PSSetColor (maFillColor);
PSSetColor ();
WritePS (mpPageBody, "eofill\n");
}
// restore the current path
if( maFillColor.Is() && maLineColor.Is())
PSGRestore();
}
void
PrinterGfx::DrawPolyPolygonBezier (sal_uInt32 nPoly, const sal_uInt32 * pPoints, const Point* const * pPtAry, const sal_uInt8* const* pFlgAry)
{
const sal_uInt32 nBezString = 1024;
sal_Char pString[nBezString];
if ( !nPoly || !pPtAry || !pPoints || !(maFillColor.Is() || maLineColor.Is()))
return;
for (unsigned int i=0; i<nPoly;i++)
{
sal_uInt32 nPoints = pPoints[i];
// #112689# sanity check
if( nPoints == 0 || pPtAry[i] == NULL )
continue;
snprintf(pString, nBezString, "%li %li moveto\n", pPtAry[i][0].X(), pPtAry[i][0].Y()); //Move to the starting point
WritePS(mpPageBody, pString);
for (unsigned int j=1; j < nPoints;)
{
// if no flag array exists for this polygon, then it must be a regular
// polygon without beziers
if ( ! pFlgAry[i] || pFlgAry[i][j] != POLY_CONTROL)
{
snprintf(pString, nBezString, "%li %li lineto\n", pPtAry[i][j].X(), pPtAry[i][j].Y());
WritePS(mpPageBody, pString);
j++;
}
else
{
if (j+2 >= nPoints)
break; //Error: wrong sequence of contol/normal points somehow
if ((pFlgAry[i][j] == POLY_CONTROL) && (pFlgAry[i][j+1] == POLY_CONTROL) && (pFlgAry[i][j+2] != POLY_CONTROL))
{
snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n",
pPtAry[i][j].X(), pPtAry[i][j].Y(),
pPtAry[i][j+1].X(), pPtAry[i][j+1].Y(),
pPtAry[i][j+2].X(), pPtAry[i][j+2].Y());
WritePS(mpPageBody, pString);
}
else
{
DBG_ERROR( "PrinterGfx::DrawPolyPolygonBezier: Strange output" );
}
j+=3;
}
}
}
// if fill and stroke, save the current path
if( maFillColor.Is() && maLineColor.Is())
PSGSave();
if (maFillColor.Is ())
{
PSSetColor (maFillColor);
PSSetColor ();
WritePS (mpPageBody, "eofill\n");
}
// restore the current path
if( maFillColor.Is() && maLineColor.Is())
PSGRestore();
}
/*
* postscript generating routines
*/
void
PrinterGfx::PSGSave ()
{
WritePS (mpPageBody, "gsave\n" );
GraphicsStatus aNewState;
if( maGraphicsStack.begin() != maGraphicsStack.end() )
aNewState = maGraphicsStack.front();
maGraphicsStack.push_front( aNewState );
}
void
PrinterGfx::PSGRestore ()
{
WritePS (mpPageBody, "grestore\n" );
if( maGraphicsStack.begin() == maGraphicsStack.end() )
WritePS (mpPageBody, "Error: too many grestores\n" );
else
maGraphicsStack.pop_front();
}
void
PrinterGfx::PSSetLineWidth ()
{
if( currentState().mfLineWidth != maVirtualStatus.mfLineWidth )
{
char pBuffer[128];
sal_Int32 nChar = 0;
currentState().mfLineWidth = maVirtualStatus.mfLineWidth;
nChar = psp::getValueOfDouble (pBuffer, maVirtualStatus.mfLineWidth, 5);
nChar += psp::appendStr (" setlinewidth\n", pBuffer + nChar);
WritePS (mpPageBody, pBuffer, nChar);
}
}
void
PrinterGfx::PSSetColor ()
{
PrinterColor& rColor( maVirtualStatus.maColor );
if( currentState().maColor != rColor )
{
currentState().maColor = rColor;
char pBuffer[128];
sal_Int32 nChar = 0;
if( mbColor )
{
nChar = psp::getValueOfDouble (pBuffer,
(double)rColor.GetRed() / 255.0, 5);
nChar += psp::appendStr (" ", pBuffer + nChar);
nChar += psp::getValueOfDouble (pBuffer + nChar,
(double)rColor.GetGreen() / 255.0, 5);
nChar += psp::appendStr (" ", pBuffer + nChar);
nChar += psp::getValueOfDouble (pBuffer + nChar,
(double)rColor.GetBlue() / 255.0, 5);
nChar += psp::appendStr (" setrgbcolor\n", pBuffer + nChar );
}
else
{
Color aColor( rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
sal_uInt8 nCol = aColor.GetLuminance();
nChar = psp::getValueOfDouble( pBuffer, (double)nCol / 255.0, 5 );
nChar += psp::appendStr( " setgray\n", pBuffer + nChar );
}
WritePS (mpPageBody, pBuffer, nChar);
}
}
void
PrinterGfx::PSSetFont ()
{
GraphicsStatus& rCurrent( currentState() );
if( maVirtualStatus.maFont != rCurrent.maFont ||
maVirtualStatus.mnTextHeight != rCurrent.mnTextHeight ||
maVirtualStatus.maEncoding != rCurrent.maEncoding ||
maVirtualStatus.mnTextWidth != rCurrent.mnTextWidth ||
maVirtualStatus.mbArtBold != rCurrent.mbArtBold ||
maVirtualStatus.mbArtItalic != rCurrent.mbArtItalic
)
{
rCurrent.maFont = maVirtualStatus.maFont;
rCurrent.maEncoding = maVirtualStatus.maEncoding;
rCurrent.mnTextWidth = maVirtualStatus.mnTextWidth;
rCurrent.mnTextHeight = maVirtualStatus.mnTextHeight;
rCurrent.mbArtItalic = maVirtualStatus.mbArtItalic;
rCurrent.mbArtBold = maVirtualStatus.mbArtBold;
sal_Int32 nTextHeight = rCurrent.mnTextHeight;
sal_Int32 nTextWidth = rCurrent.mnTextWidth ? rCurrent.mnTextWidth
: rCurrent.mnTextHeight;
sal_Char pSetFont [256];
sal_Int32 nChar = 0;
// postscript based fonts need reencoding
if ( ( rCurrent.maEncoding == RTL_TEXTENCODING_MS_1252)
|| ( rCurrent.maEncoding == RTL_TEXTENCODING_ISO_8859_1)
|| ( rCurrent.maEncoding >= RTL_TEXTENCODING_USER_START
&& rCurrent.maEncoding <= RTL_TEXTENCODING_USER_END)
)
{
rtl::OString aReencodedFont =
psp::GlyphSet::GetReencodedFontName (rCurrent.maEncoding,
rCurrent.maFont);
nChar += psp::appendStr ("(", pSetFont + nChar);
nChar += psp::appendStr (aReencodedFont.getStr(),
pSetFont + nChar);
nChar += psp::appendStr (") cvn findfont ",
pSetFont + nChar);
}
else
// tt based fonts mustn't reencode, the encoding is implied by the fontname
// same for symbol type1 fonts, dont try to touch them
{
nChar += psp::appendStr ("(", pSetFont + nChar);
nChar += psp::appendStr (rCurrent.maFont.getStr(),
pSetFont + nChar);
nChar += psp::appendStr (") cvn findfont ",
pSetFont + nChar);
}
if( ! rCurrent.mbArtItalic )
{
nChar += psp::getValueOf (nTextWidth, pSetFont + nChar);
nChar += psp::appendStr (" ", pSetFont + nChar);
nChar += psp::getValueOf (-nTextHeight, pSetFont + nChar);
nChar += psp::appendStr (" matrix scale makefont setfont\n", pSetFont + nChar);
}
else // skew 15 degrees to right
{
nChar += psp::appendStr ( " [", pSetFont + nChar);
nChar += psp::getValueOf (nTextWidth, pSetFont + nChar);
nChar += psp::appendStr (" 0 ", pSetFont + nChar);
nChar += psp::getValueOfDouble (pSetFont + nChar, 0.27*(double)nTextWidth, 3 );
nChar += psp::appendStr ( " ", pSetFont + nChar);
nChar += psp::getValueOf (-nTextHeight, pSetFont + nChar);
nChar += psp::appendStr (" 0 0] makefont setfont\n", pSetFont + nChar);
}
WritePS (mpPageBody, pSetFont);
}
}
void
PrinterGfx::PSRotate (sal_Int32 nAngle)
{
sal_Int32 nPostScriptAngle = -nAngle;
while( nPostScriptAngle < 0 )
nPostScriptAngle += 3600;
if (nPostScriptAngle == 0)
return;
sal_Int32 nFullAngle = nPostScriptAngle / 10;
sal_Int32 nTenthAngle = nPostScriptAngle % 10;
sal_Char pRotate [48];
sal_Int32 nChar = 0;
nChar = psp::getValueOf (nFullAngle, pRotate);
nChar += psp::appendStr (".", pRotate + nChar);
nChar += psp::getValueOf (nTenthAngle, pRotate + nChar);
nChar += psp::appendStr (" rotate\n", pRotate + nChar);
WritePS (mpPageBody, pRotate);
}
void
PrinterGfx::PSPointOp (const Point& rPoint, const sal_Char* pOperator)
{
sal_Char pPSCommand [48];
sal_Int32 nChar = 0;
nChar = psp::getValueOf (rPoint.X(), pPSCommand);
nChar += psp::appendStr (" ", pPSCommand + nChar);
nChar += psp::getValueOf (rPoint.Y(), pPSCommand + nChar);
nChar += psp::appendStr (" ", pPSCommand + nChar);
nChar += psp::appendStr (pOperator, pPSCommand + nChar);
nChar += psp::appendStr ("\n", pPSCommand + nChar);
DBG_ASSERT (nChar < 48, "Buffer overflow in PSPointOp");
WritePS (mpPageBody, pPSCommand);
}
void
PrinterGfx::PSTranslate (const Point& rPoint)
{
PSPointOp (rPoint, "translate");
}
void
PrinterGfx::PSMoveTo (const Point& rPoint)
{
PSPointOp (rPoint, "moveto");
}
void
PrinterGfx::PSLineTo (const Point& rPoint)
{
PSPointOp (rPoint, "lineto");
}
void
PrinterGfx::PSRMoveTo (sal_Int32 nDx, sal_Int32 nDy)
{
Point aPoint(nDx, nDy);
PSPointOp (aPoint, "rmoveto");
}
/* get a compressed representation of the path information */
#define DEBUG_BINPATH 0
void
PrinterGfx::PSBinLineTo (const Point& rCurrent, Point& rOld, sal_Int32& nColumn)
{
#if (DEBUG_BINPATH == 1)
PSLineTo (rCurrent);
#else
PSBinPath (rCurrent, rOld, lineto, nColumn);
#endif
}
void
PrinterGfx::PSBinMoveTo (const Point& rCurrent, Point& rOld, sal_Int32& nColumn)
{
#if (DEBUG_BINPATH == 1)
PSMoveTo (rCurrent);
#else
PSBinPath (rCurrent, rOld, moveto, nColumn);
#endif
}
void
PrinterGfx::PSBinStartPath ()
{
#if (DEBUG_BINPATH == 1)
WritePS (mpPageBody, "% PSBinStartPath\n");
#else
WritePS (mpPageBody, "readpath\n" );
#endif
}
void
PrinterGfx::PSBinEndPath ()
{
#if (DEBUG_BINPATH == 1)
WritePS (mpPageBody, "% PSBinEndPath\n");
#else
WritePS (mpPageBody, "~\n");
#endif
}
void
PrinterGfx::PSBinCurrentPath (sal_uInt32 nPoints, const Point* pPath)
{
// create the path
Point aPoint (0, 0);
sal_Int32 nColumn = 0;
PSBinStartPath ();
PSBinMoveTo (*pPath, aPoint, nColumn);
for (unsigned int i = 1; i < nPoints; i++)
PSBinLineTo (pPath[i], aPoint, nColumn);
PSBinEndPath ();
}
void
PrinterGfx::PSBinPath (const Point& rCurrent, Point& rOld,
pspath_t eType, sal_Int32& nColumn)
{
sal_Char pPath[48];
sal_Int32 nChar;
// create the hex representation of the dx and dy path shift, store the field
// width as it is needed for the building the command
sal_Int32 nXPrec = getAlignedHexValueOf (rCurrent.X() - rOld.X(), pPath + 1);
sal_Int32 nYPrec = getAlignedHexValueOf (rCurrent.Y() - rOld.Y(), pPath + 1 + nXPrec);
pPath [ 1 + nXPrec + nYPrec ] = 0;
// build the command, it is a char with bit represention 000cxxyy
// c represents the char, xx and yy repr. the field width of the dx and dy shift,
// dx and dy represent the number of bytes to read after the opcode
sal_Char cCmd = (eType == lineto ? (sal_Char)0x00 : (sal_Char)0x10);
switch (nYPrec)
{
case 2: break;
case 4: cCmd |= 0x01; break;
case 6: cCmd |= 0x02; break;
case 8: cCmd |= 0x03; break;
default: DBG_ERROR ("invalid x precision in binary path");
}
switch (nXPrec)
{
case 2: break;
case 4: cCmd |= 0x04; break;
case 6: cCmd |= 0x08; break;
case 8: cCmd |= 0x0c; break;
default: DBG_ERROR ("invalid y precision in binary path");
}
cCmd += 'A';
pPath[0] = cCmd;
// write the command to file,
// line breaking at column nMaxTextColumn (80)
nChar = 1 + nXPrec + nYPrec;
if ((nColumn + nChar) > nMaxTextColumn)
{
sal_Int32 nSegment = nMaxTextColumn - nColumn;
WritePS (mpPageBody, pPath, nSegment);
WritePS (mpPageBody, "\n", 1);
WritePS (mpPageBody, pPath + nSegment, nChar - nSegment);
nColumn = nChar - nSegment;
}
else
{
WritePS (mpPageBody, pPath, nChar);
nColumn += nChar;
}
rOld = rCurrent;
}
void
PrinterGfx::PSScale (double fScaleX, double fScaleY)
{
sal_Char pScale [48];
sal_Int32 nChar = 0;
nChar = psp::getValueOfDouble (pScale, fScaleX, 5);
nChar += psp::appendStr (" ", pScale + nChar);
nChar += psp::getValueOfDouble (pScale + nChar, fScaleY, 5);
nChar += psp::appendStr (" scale\n", pScale + nChar);
WritePS (mpPageBody, pScale);
}
/* psshowtext helper routines: draw an hex string for show/xshow */
void
PrinterGfx::PSHexString (const sal_uChar* pString, sal_Int16 nLen)
{
sal_Char pHexString [128];
sal_Int32 nChar = 0;
nChar = psp::appendStr ("<", pHexString);
for (int i = 0; i < nLen; i++)
{
if (nChar >= (nMaxTextColumn - 1))
{
nChar += psp::appendStr ("\n", pHexString + nChar);
WritePS (mpPageBody, pHexString, nChar);
nChar = 0;
}
nChar += psp::getHexValueOf ((sal_Int32)pString[i], pHexString + nChar);
}
nChar += psp::appendStr (">\n", pHexString + nChar);
WritePS (mpPageBody, pHexString, nChar);
}
/* psshowtext helper routines: draw an array for xshow ps operator */
void
PrinterGfx::PSDeltaArray (const sal_Int32 *pArray, sal_Int16 nEntries)
{
sal_Char pPSArray [128];
sal_Int32 nChar = 0;
nChar = psp::appendStr ("[", pPSArray + nChar);
nChar += psp::getValueOf (pArray[0], pPSArray + nChar);
for (int i = 1; i < nEntries; i++)
{
if (nChar >= (nMaxTextColumn - 1))
{
nChar += psp::appendStr ("\n", pPSArray + nChar);
WritePS (mpPageBody, pPSArray, nChar);
nChar = 0;
}
nChar += psp::appendStr (" ", pPSArray + nChar);
nChar += psp::getValueOf (pArray[i] - pArray[i-1], pPSArray + nChar);
}
nChar += psp::appendStr (" 0]\n", pPSArray + nChar);
WritePS (mpPageBody, pPSArray);
}
/* the DrawText equivalent, pDeltaArray may be NULL. For Type1 fonts or single byte
* fonts in general nBytes and nGlyphs is the same. For printer resident Composite
* fonts it may be different (these fonts may be SJIS encoded for example) */
void
PrinterGfx::PSShowText (const sal_uChar* pStr, sal_Int16 nGlyphs, sal_Int16 nBytes,
const sal_Int32* pDeltaArray)
{
PSSetColor (maTextColor);
PSSetColor ();
PSSetFont ();
// rotate the user coordinate system
if (mnTextAngle != 0)
{
PSGSave ();
PSRotate (mnTextAngle);
}
sal_Char pBuffer[256];
if( maVirtualStatus.mbArtBold )
{
sal_Int32 nLW = maVirtualStatus.mnTextWidth;
if( nLW == 0 )
nLW = maVirtualStatus.mnTextHeight;
else
nLW = nLW < maVirtualStatus.mnTextHeight ? nLW : maVirtualStatus.mnTextHeight;
psp::getValueOfDouble( pBuffer, (double)nLW / 30.0 );
}
// dispatch to the drawing method
if (pDeltaArray == NULL)
{
PSHexString (pStr, nBytes);
if( maVirtualStatus.mbArtBold )
{
WritePS( mpPageBody, pBuffer );
WritePS( mpPageBody, " bshow\n" );
}
else
WritePS (mpPageBody, "show\n");
}
else
{
PSHexString (pStr, nBytes);
PSDeltaArray (pDeltaArray, nGlyphs - 1);
if( maVirtualStatus.mbArtBold )
{
WritePS( mpPageBody, pBuffer );
WritePS( mpPageBody, " bxshow\n" );
}
else
WritePS (mpPageBody, "xshow\n");
}
// restore the user coordinate system
if (mnTextAngle != 0)
PSGRestore ();
}
void
PrinterGfx::PSComment( const sal_Char* pComment )
{
const sal_Char* pLast = pComment;
while( pComment && *pComment )
{
while( *pComment && *pComment != '\n' && *pComment != '\r' )
pComment++;
if( pComment - pLast > 1 )
{
WritePS( mpPageBody, "% ", 2 );
WritePS( mpPageBody, pLast, pComment - pLast );
WritePS( mpPageBody, "\n", 1 );
}
if( *pComment )
pLast = ++pComment;
}
}
sal_Bool
PrinterGfx::DrawEPS( const Rectangle& rBoundingBox, void* pPtr, sal_uInt32 nSize )
{
if( nSize == 0 )
return sal_True;
if( ! mpPageBody )
return sal_False;
sal_Bool bSuccess = sal_False;
// first search the BoundingBox of the EPS data
SvMemoryStream aStream( pPtr, nSize, STREAM_READ );
aStream.Seek( STREAM_SEEK_TO_BEGIN );
ByteString aLine;
ByteString aDocTitle;
double fLeft = 0, fRight = 0, fTop = 0, fBottom = 0;
bool bEndComments = false;
while( ! aStream.IsEof()
&& ( ( fLeft == 0 && fRight == 0 && fTop == 0 && fBottom == 0 ) ||
( aDocTitle.Len() == 0 && bEndComments == false ) )
)
{
aStream.ReadLine( aLine );
if( aLine.Len() > 1 && aLine.GetChar( 0 ) == '%' )
{
char cChar = aLine.GetChar(1);
if( cChar == '%' )
{
if( aLine.CompareIgnoreCaseToAscii( "%%BoundingBox:", 14 ) == COMPARE_EQUAL )
{
aLine = WhitespaceToSpace( aLine.GetToken( 1, ':' ) );
if( aLine.Len() && aLine.Search( "atend" ) == STRING_NOTFOUND )
{
fLeft = StringToDouble( GetCommandLineToken( 0, aLine ) );
fBottom = StringToDouble( GetCommandLineToken( 1, aLine ) );
fRight = StringToDouble( GetCommandLineToken( 2, aLine ) );
fTop = StringToDouble( GetCommandLineToken( 3, aLine ) );
}
}
else if( aLine.CompareIgnoreCaseToAscii( "%%Title:", 8 ) == COMPARE_EQUAL )
aDocTitle = WhitespaceToSpace( aLine.Copy( 8 ) );
else if( aLine.CompareIgnoreCaseToAscii( "%%EndComments", 13 ) == COMPARE_EQUAL )
bEndComments = true;
}
else if( cChar == ' ' || cChar == '\t' || cChar == '\r' || cChar == '\n' )
bEndComments = true;
}
else
bEndComments = true;
}
static sal_uInt16 nEps = 0;
if( ! aDocTitle.Len() )
aDocTitle = ByteString::CreateFromInt32( (sal_Int32)(nEps++) );
if( fLeft != fRight && fTop != fBottom )
{
double fScaleX = (double)rBoundingBox.GetWidth()/(fRight-fLeft);
double fScaleY = -(double)rBoundingBox.GetHeight()/(fTop-fBottom);
Point aTranslatePoint( (int)(rBoundingBox.Left()-fLeft*fScaleX),
(int)(rBoundingBox.Bottom()+1-fBottom*fScaleY) );
// prepare EPS
WritePS( mpPageBody,
"/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" );
// set up clip path and scale
BeginSetClipRegion( 1 );
UnionClipRegion( rBoundingBox.Left(), rBoundingBox.Top(), rBoundingBox.GetWidth(), rBoundingBox.GetHeight() );
EndSetClipRegion();
PSTranslate( aTranslatePoint );
PSScale( fScaleX, fScaleY );
// DSC requires BeginDocument
WritePS( mpPageBody, "%%BeginDocument: " );
WritePS( mpPageBody, aDocTitle );
WritePS( mpPageBody, "\n" );
// write the EPS data
sal_uInt64 nOutLength;
mpPageBody->write( pPtr, nSize, nOutLength );
bSuccess = nOutLength == nSize;
// corresponding EndDocument
if( ((char*)pPtr)[ nSize-1 ] != '\n' )
WritePS( mpPageBody, "\n" );
WritePS( mpPageBody, "%%EndDocument\n" );
// clean up EPS
WritePS( mpPageBody,
"count op_count sub {pop} repeat\n"
"countdictstack dict_count sub {end} repeat\n"
"b4_Inc_state restore\n" );
}
return bSuccess;
}