Files
libreoffice/vcl/generic/print/text_gfx.cxx
Stephan Bergmann f6ec07a396 loplugin:cstylecast: deal with remaining pointer casts
Change-Id: I07bf1403e6b992807541a499b786d47f835b2f81
2015-06-08 16:29:41 +02:00

729 lines
24 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* 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 .
*/
#include <math.h>
#include "psputil.hxx"
#include "glyphset.hxx"
#include "generic/printergfx.hxx"
#include "fontmanager.hxx"
#include "vcl/helper.hxx"
#include "osl/thread.h"
#include "sal/alloca.h"
using namespace psp ;
namespace psp {
/*
container for a font and its helper fonts:
1st is the font itself
2nd is a fallback font, usually a font with unicode glyph repertoir (e.g. andale)
symbol fonts (adobe-fontspecific) may need special glyphmapping
(symbol page vc. latin page)
*/
class Font2
{
private:
#define Font2Size 2
fontID mpFont [Font2Size];
bool mbSymbol;
public:
fontID GetFont (int nIdx) const
{ return nIdx < Font2Size ? mpFont[nIdx] : -1 ; }
bool IsSymbolFont () const
{ return mbSymbol; }
explicit Font2 (const PrinterGfx &rGfx);
~Font2 () {}
};
Font2::Font2(const PrinterGfx &rGfx)
{
mpFont[0] = rGfx.GetFontID();
mpFont[1] = rGfx.getFallbackID();
PrintFontManager &rMgr = PrintFontManager::get();
mbSymbol = mpFont[0] != -1 &&
rMgr.getFontEncoding(mpFont[0]) == RTL_TEXTENCODING_SYMBOL;
}
} // namespace psp
static int getVerticalDeltaAngle( sal_Unicode nChar )
{
int nRotation = GetVerticalFlags(nChar);
if (nRotation == GF_ROTR)
return -900;
if (nRotation == GF_ROTL)
return 900;
return 0;
}
void
PrinterGfx::PSUploadPS1Font (sal_Int32 nFontID)
{
std::list< sal_Int32 >::iterator aFont;
// already in the document header ?
for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont )
if( nFontID == *aFont )
return;
// no occurrenc yet, mark for download
// add the fontid to the list
maPS1Font.push_back (nFontID);
}
/*
* implement text handling printer routines,
*/
sal_uInt16
PrinterGfx::SetFont(
sal_Int32 nFontID,
sal_Int32 nHeight,
sal_Int32 nWidth,
sal_Int32 nAngle,
bool bVertical,
bool bArtItalic,
bool bArtBold
)
{
// font and encoding will be set by drawText again immediately
// before PSShowText
mnFontID = nFontID;
maVirtualStatus.maFont.clear();
maVirtualStatus.maEncoding = RTL_TEXTENCODING_DONTKNOW;
maVirtualStatus.mnTextHeight = nHeight;
maVirtualStatus.mnTextWidth = nWidth;
maVirtualStatus.mbArtItalic = bArtItalic;
maVirtualStatus.mbArtBold = bArtBold;
mnTextAngle = nAngle;
mbTextVertical = bVertical;
return 0;
}
void PrinterGfx::drawGlyphs(
const Point& rPoint,
sal_GlyphId* pGlyphIds,
sal_Unicode* pUnicodes,
sal_Int16 nLen,
sal_Int32* pDeltaArray
)
{
// draw the string
// search for a glyph set matching the set font
std::list< GlyphSet >::iterator aIter;
for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter)
if ( ((*aIter).GetFontID() == mnFontID)
&& ((*aIter).IsVertical() == mbTextVertical))
{
(*aIter).DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray);
break;
}
// not found ? create a new one
if (aIter == maPS3Font.end())
{
maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical));
maPS3Font.back().DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray);
}
}
void PrinterGfx::DrawGlyphs(
const Point& rPoint,
sal_GlyphId* pGlyphIds,
sal_Unicode* pUnicodes,
sal_Int16 nLen,
sal_Int32* pDeltaArray
)
{
if( nLen <= 0 )
return;
if ( !mrFontMgr.isFontDownloadingAllowedForPrinting( mnFontID ) )
{
LicenseWarning(rPoint, pUnicodes, nLen, pDeltaArray);
return;
}
if( mrFontMgr.getFontType( mnFontID ) != fonttype::TrueType )
{
DrawText( rPoint, pUnicodes, nLen, pDeltaArray );
return;
}
// move and rotate the user coordinate system
// avoid the gsave/grestore for the simple cases since it allows
// reuse of the current font if it hasn't changed
sal_Int32 nCurrentTextAngle = mnTextAngle;
Point aPoint( rPoint );
if (nCurrentTextAngle != 0)
{
PSGSave ();
PSTranslate (rPoint);
PSRotate (nCurrentTextAngle);
mnTextAngle = 0;
aPoint = Point( 0, 0 );
}
if( mbTextVertical )
{
// vertical glyphs can have an additional rotation ... sigh.
// so break up text in chunks of normal glyphs and print out
// specially rotated glyphs extra
sal_GlyphId* pTempGlyphIds = static_cast<sal_GlyphId*>(alloca(sizeof(sal_Int32)*nLen));
sal_Int32* pTempDelta = static_cast<sal_Int32*>(alloca(sizeof(sal_Int32)*nLen));
sal_Unicode* pTempUnicodes = static_cast<sal_Unicode*>(alloca(sizeof(sal_Unicode)*nLen));
sal_Int16 nTempLen = 0;
sal_Int32 nTempFirstDelta = 0;
Point aRotPoint;
sal_Int32 nTextHeight = maVirtualStatus.mnTextHeight;
sal_Int32 nTextWidth = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight;
sal_Int32 nAscend = mrFontMgr.getFontAscend( mnFontID );
sal_Int32 nDescend = mrFontMgr.getFontDescend( mnFontID );
nDescend = nDescend * nTextHeight / 1000;
nAscend = nAscend * nTextHeight / 1000;
for( sal_Int16 i = 0; i < nLen; i++ )
{
const sal_GlyphId nRot = pGlyphIds[i] & GF_ROTMASK;
if( nRot == GF_NONE )
{
pTempUnicodes[nTempLen] = pUnicodes[i];
pTempGlyphIds[nTempLen] = pGlyphIds[i];
if( nTempLen > 0 )
pTempDelta[nTempLen-1] = pDeltaArray[i-1]-nTempFirstDelta;
else
{
// the first element in pDeltaArray shows
// the offset of the second character
// so if the first glyph is normal
// then we do not need to move the delta indices
// else we have to move them down by one and
// recalculate aPoint and all deltas
if( i != 0 )
nTempFirstDelta = pDeltaArray[ i-1 ];
}
nTempLen++;
}
else
{
sal_Int32 nOffset = i > 0 ? pDeltaArray[i-1] : 0;
sal_Int32 nRotAngle = 0;
switch( nRot )
{
case GF_ROTR:
nRotAngle = 2700;
aRotPoint = Point( -nAscend*nTextWidth/nTextHeight, -nDescend*nTextWidth/nTextHeight - nOffset );
break;
case GF_VERT:
nRotAngle = 1800;
aRotPoint = Point( -nOffset, (nAscend+nDescend) );
break;
case GF_ROTL:
nRotAngle = 900;
aRotPoint = Point( -nDescend*nTextWidth/nTextHeight, nOffset + nAscend*nTextWidth/nTextHeight );
break;
}
sal_GlyphId nRotGlyphId = pGlyphIds[i];
sal_Unicode nRotUnicode = pUnicodes[i];
sal_Int32 nRotDelta = 0;
// transform matrix to new individual direction
PSGSave ();
GraphicsStatus aSaveStatus = maVirtualStatus;
if( nRot != 2 ) // switch font aspect
{
maVirtualStatus.mnTextWidth = nTextHeight;
maVirtualStatus.mnTextHeight = nTextWidth;
}
if( aPoint.X() || aPoint.Y() )
PSTranslate( aPoint );
PSRotate (nRotAngle);
// draw the rotated glyph
drawGlyphs( aRotPoint, &nRotGlyphId, &nRotUnicode, 1, &nRotDelta );
// restore previous state
maVirtualStatus = aSaveStatus;
PSGRestore();
}
}
pGlyphIds = pTempGlyphIds;
pUnicodes = pTempUnicodes;
pDeltaArray = pTempDelta;
nLen = nTempLen;
aPoint.X() += nTempFirstDelta;
}
if( nLen > 0 )
drawGlyphs( aPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray );
// restore the user coordinate system
if (nCurrentTextAngle != 0)
{
PSGRestore ();
mnTextAngle = nCurrentTextAngle;
}
}
void
PrinterGfx::DrawText (
const Point& rPoint,
const sal_Unicode* pStr,
sal_Int16 nLen,
const sal_Int32* pDeltaArray
)
{
if (!nLen)
return;
fontID nRestoreFont = mnFontID;
// setup font[substitutes] and map the string into the symbol area in case of
// symbol font
Font2 aFont(*this);
sal_Unicode *pEffectiveStr;
if ( aFont.IsSymbolFont() )
{
pEffectiveStr = static_cast<sal_Unicode*>(alloca(nLen * sizeof(pStr[0])));
for (int i = 0; i < nLen; i++)
pEffectiveStr[i] = pStr[i] < 256 ? pStr[i] + 0xF000 : pStr[i];
}
else
{
pEffectiveStr = const_cast<sal_Unicode*>(pStr);
}
fontID *pFontMap = static_cast<fontID*>(alloca(nLen * sizeof(fontID)));
sal_Int32 *pCharWidth = static_cast<sal_Int32*>(alloca(nLen * sizeof(sal_Int32)));
for( int n = 0; n < nLen; n++ )
{
CharacterMetric aBBox;
// coverity[callee_ptr_arith]
pFontMap[n] = getCharMetric(aFont, pEffectiveStr[n], &aBBox);
pCharWidth[n] = getCharWidth(mbTextVertical, pEffectiveStr[n], &aBBox);
}
// setup a new delta array, use virtual resolution of 1000
sal_Int32* pNewDeltaArray = static_cast<sal_Int32*>(alloca( sizeof( sal_Int32 )*nLen ));
if ( pDeltaArray != 0)
{
for (int i = 0; i < nLen - 1; i++)
pNewDeltaArray[i] = 1000 * pDeltaArray[i];
pNewDeltaArray[nLen - 1] = 0;
}
else
{
pNewDeltaArray[0] = pCharWidth[0];
for (int i = 1; i < nLen; i++)
pNewDeltaArray[i] = pNewDeltaArray[i-1] + pCharWidth[i];
}
// move and rotate the user coordinate system
// avoid the gsave/grestore for the simple cases since it allows
// reuse of the current font if it hasn't changed
sal_Int32 nCurrentTextAngle = mnTextAngle;
sal_Int32 nCurrentPointX;
sal_Int32 nCurrentPointY;
if (nCurrentTextAngle != 0)
{
PSGSave ();
PSTranslate (rPoint);
PSRotate (nCurrentTextAngle);
mnTextAngle = 0;
nCurrentPointX = 0;
nCurrentPointY = 0;
}
else
{
nCurrentPointX = rPoint.X();
nCurrentPointY = rPoint.Y();
}
// draw the string
sal_Int32 nDelta = 0;
for (int nTo = 0; nTo < nLen; )
{
int nFrom = nTo;
fontID nFont = pFontMap[ nFrom ];
while ((nTo < nLen) && (nFont == pFontMap[nTo]))
{
pNewDeltaArray[ nTo ] = (sal_Int32)(((0.5 + pNewDeltaArray[ nTo ]) / 1000.0) - nDelta);
nTo++ ;
}
SetFont( nFont,
maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth,
mnTextAngle,
mbTextVertical,
maVirtualStatus.mbArtItalic,
maVirtualStatus.mbArtBold
);
if (mbTextVertical)
{
drawVerticalizedText(
Point(nCurrentPointX + nDelta, nCurrentPointY),
pEffectiveStr + nFrom, nTo - nFrom,
pNewDeltaArray + nFrom );
}
else
{
drawText(
Point(nCurrentPointX + nDelta, nCurrentPointY),
pEffectiveStr + nFrom, nTo - nFrom,
pDeltaArray == NULL ? NULL : pNewDeltaArray + nFrom );
}
nDelta += pNewDeltaArray[ nTo - 1 ];
}
// restore the user coordinate system
if (nCurrentTextAngle != 0)
{
PSGRestore ();
mnTextAngle = nCurrentTextAngle;
}
// restore the original font settings
SetFont( nRestoreFont,
maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth,
mnTextAngle, mbTextVertical,
maVirtualStatus.mbArtItalic,
maVirtualStatus.mbArtBold
);
}
bool PrinterGfx::drawVerticalizedText(
const Point& rPoint,
const sal_Unicode* pStr,
sal_Int16 nLen,
const sal_Int32* pDeltaArray
)
{
PrintFontManager &rMgr = PrintFontManager::get();
PrintFontInfo aInfo;
if (!rMgr.getFontInfo(mnFontID, aInfo))
return false;
sal_Int32* pDelta = static_cast<sal_Int32*>(alloca( nLen * sizeof(sal_Int32) ));
int nTextScale = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight;
int nNormalAngle = mnTextAngle;
int nLastPos = 0;
double fSin = sin( -2.0*M_PI*nNormalAngle/3600 );
double fCos = cos( -2.0*M_PI*nNormalAngle/3600 );
bool* pGsubFlags = static_cast<bool*>(alloca( nLen * sizeof(bool) ));
rMgr.hasVerticalSubstitutions( mnFontID, pStr, nLen, pGsubFlags );
Point aPoint( rPoint );
for( int i = 0; i < nLen; )
{
int nDeltaAngle;
while( ( nDeltaAngle = getVerticalDeltaAngle( pStr[i] ) ) == 0 && i < nLen )
i++;
if( i <= nLen && i > nLastPos )
{
for( int n = nLastPos; n < i; n++ )
pDelta[n] = pDeltaArray[n] - (aPoint.X() - rPoint.X() );
SetFont( mnFontID,
maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth,
nNormalAngle, mbTextVertical,
maVirtualStatus.mbArtItalic,
maVirtualStatus.mbArtBold );
drawText( aPoint, pStr + nLastPos, i - nLastPos, pDelta + nLastPos );
aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i-1] * fCos));
aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i-1] * fSin));
}
if( i < nLen )
{
int nOldWidth = maVirtualStatus.mnTextWidth;
int nOldHeight = maVirtualStatus.mnTextHeight;
SetFont( mnFontID,
nTextScale,
maVirtualStatus.mnTextHeight,
nNormalAngle + nDeltaAngle,
mbTextVertical,
maVirtualStatus.mbArtItalic,
maVirtualStatus.mbArtBold );
double nA = nTextScale * aInfo.m_nAscend / 1000.0;
double nD = nTextScale * aInfo.m_nDescend / 1000.0;
double fStretch = (double)maVirtualStatus.mnTextWidth / maVirtualStatus.mnTextHeight;
if( !pGsubFlags[i] )
nD *= fStretch;
Point aPos( aPoint );
switch( nDeltaAngle )
{
case +900:
aPos.X() += (sal_Int32)(+nA * fCos + nD * fSin);
aPos.Y() += (sal_Int32)(-nA * fSin + nD * fCos);
break;
case -900:
aPos.X() += (sal_Int32)(+nA * fSin + nD * fCos);
aPos.Y() += (sal_Int32)(-(nTextScale*fStretch - nD) * fCos);
break;
}
drawText( aPos, pStr+i, 1, NULL );
if( i < nLen-1 && pDeltaArray )
{
aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i] * fCos));
aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i] * fSin));
}
// swap text width/height again
SetFont( mnFontID,
nOldHeight,
nOldWidth,
nNormalAngle,
mbTextVertical,
maVirtualStatus.mbArtItalic,
maVirtualStatus.mbArtBold );
}
i++;
nLastPos = i;
}
mnTextAngle = nNormalAngle;
return true;
}
void
PrinterGfx::LicenseWarning(const Point& rPoint, const sal_Unicode* pStr,
sal_Int16 nLen, const sal_Int32* pDeltaArray)
{
// treat it like a builtin font in case a user has that font also in the
// printer. This is not so unlikely as it may seem; no print embedding
// licensed fonts are often used (or so they say) in companies:
// they are installed on displays and printers, but get not embedded in
// print files or documents because they are not licensed for use outside
// the company.
OString aMessage( "The font " );
aMessage += OUStringToOString( mrFontMgr.getPSName(mnFontID),
RTL_TEXTENCODING_ASCII_US );
aMessage += " could not be downloaded\nbecause its license does not allow for that";
PSComment( aMessage.getStr() );
OString aFontName = OUStringToOString(
mrFontMgr.getPSName(mnFontID),
RTL_TEXTENCODING_ASCII_US);
PSSetFont (aFontName, RTL_TEXTENCODING_ISO_8859_1);
sal_Size nSize = 4 * nLen;
unsigned char* pBuffer = static_cast<unsigned char*>(alloca (nSize* sizeof(unsigned char)));
ConverterFactory &rCvt = GetConverterFactory ();
nSize = rCvt.Convert (pStr, nLen, pBuffer, nSize, RTL_TEXTENCODING_ISO_8859_1);
PSMoveTo (rPoint);
PSShowText (pBuffer, nLen, nSize, pDeltaArray);
}
void
PrinterGfx::drawText(
const Point& rPoint,
const sal_Unicode* pStr,
sal_Int16 nLen,
const sal_Int32* pDeltaArray
)
{
if (!(nLen > 0))
return;
fonttype::type eType = mrFontMgr.getFontType (mnFontID);
if (eType == fonttype::Type1)
PSUploadPS1Font (mnFontID);
if ( eType == fonttype::TrueType
&& !mrFontMgr.isFontDownloadingAllowedForPrinting(mnFontID))
{
LicenseWarning(rPoint, pStr, nLen, pDeltaArray);
return;
}
if( mrFontMgr.getUseOnlyFontEncoding( mnFontID ) )
{
GlyphSet aGSet( mnFontID, mbTextVertical );
aGSet.DrawText( *this, rPoint, pStr, nLen, pDeltaArray );
return;
}
// search for a glyph set matching the set font
std::list< GlyphSet >::iterator aIter;
for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter)
if ( ((*aIter).GetFontID() == mnFontID)
&& ((*aIter).IsVertical() == mbTextVertical))
{
(*aIter).DrawText (*this, rPoint, pStr, nLen, pDeltaArray);
break;
}
// not found ? create a new one
if (aIter == maPS3Font.end())
{
maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical));
maPS3Font.back().DrawText (*this, rPoint, pStr, nLen, pDeltaArray);
}
}
int
PrinterGfx::getCharWidth (bool b_vert, sal_Unicode n_char, CharacterMetric *p_bbox)
{
b_vert = b_vert && (getVerticalDeltaAngle(n_char) != 0);
int w = b_vert ? p_bbox->height : p_bbox->width;
w *= maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight;
return w;
}
fontID
PrinterGfx::getCharMetric (const Font2 &rFont, sal_Unicode n_char, CharacterMetric *p_bbox)
{
p_bbox->width = -1;
p_bbox->height = -1;
for (fontID n = 0; n < Font2Size; n++)
{
fontID n_font = rFont.GetFont(n);
if (n_font != -1)
mrFontMgr.getMetrics( n_font, n_char, n_char, p_bbox );
if (p_bbox->width >= 0 && p_bbox->height >= 0)
return n_font;
}
if (n_char != '?')
return getCharMetric (rFont, '?', p_bbox);
return rFont.GetFont(0) != -1 ? rFont.GetFont(0) : rFont.GetFont(1);
}
sal_Int32
PrinterGfx::GetCharWidth (sal_Unicode nFrom, sal_Unicode nTo, long *pWidthArray)
{
Font2 aFont(*this);
if (aFont.IsSymbolFont() && (nFrom < 256) && (nTo < 256))
{
nFrom += 0xF000;
nTo += 0xF000;
}
for( int n = 0; n < (nTo - nFrom + 1); n++ )
{
CharacterMetric aBBox;
// coverity[callee_ptr_arith]
getCharMetric(aFont, n + nFrom, &aBBox);
pWidthArray[n] = getCharWidth (mbTextVertical, n + nFrom, &aBBox);
}
// returned metrics have postscript precision
return 1000;
}
/*
* spool the converted truetype fonts to the page header after the page body is
* complete
* for Type1 fonts spool additional reencoding vectors that are necessary to access the
* whole font
*/
void
PrinterGfx::OnEndJob ()
{
maPS3Font.clear();
maPS1Font.clear();
}
void
PrinterGfx::writeResources( osl::File* pFile, std::list< OString >& rSuppliedFonts )
{
// write all type 1 fonts
std::list< sal_Int32 >::iterator aFont;
// already in the document header ?
for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont)
{
const OString& rSysPath (mrFontMgr.getFontFileSysPath(*aFont) );
OUString aUNCPath;
osl::File::getFileURLFromSystemPath (OStringToOUString (rSysPath, osl_getThreadTextEncoding()), aUNCPath);
osl::File aFontFile (aUNCPath);
// provide the pfb or pfa font as a (pfa-)font resource
OString aPostScriptName =
OUStringToOString ( mrFontMgr.getPSName(*aFont),
RTL_TEXTENCODING_ASCII_US );
WritePS (pFile, "%%BeginResource: font ");
WritePS (pFile, aPostScriptName.getStr());
WritePS (pFile, "\n");
osl::File::RC nError = aFontFile.open(osl_File_OpenFlag_Read);
if (nError == osl::File::E_None)
{
convertPfbToPfa (aFontFile, *pFile);
aFontFile.close ();
char lastchar = '\n';
if (pFile->setPos(osl_Pos_Current, -1) == osl::FileBase::E_None)
{
sal_uInt64 uBytes(1);
pFile->read(static_cast<void *>(&lastchar), uBytes, uBytes);
}
if (lastchar != '\n')
WritePS (pFile, "\n");
}
WritePS (pFile, "%%EndResource\n");
rSuppliedFonts.push_back( aPostScriptName );
}
// write glyphsets and reencodings
std::list< GlyphSet >::iterator aIter;
for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter)
{
if (aIter->GetFontType() == fonttype::TrueType)
{
aIter->PSUploadFont (*pFile, *this, mbUploadPS42Fonts, rSuppliedFonts );
}
else
{
aIter->PSUploadEncoding (pFile, *this);
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */