Files
libreoffice/canvas/source/cairo/cairo_canvashelper_text.cxx

406 lines
16 KiB
C++
Raw Normal View History

/*************************************************************************
*
* OpenOffice.org - a multi-platform office productivity suite
*
* $RCSfile: cairo_canvashelper_text.cxx,v $
*
* $Revision: 1.3 $
*
* last change: $Author: obo $ $Date: 2006-03-22 10:59:59 $
*
* The Contents of this file are made available subject to
* the terms of GNU Lesser General Public License Version 2.1.
*
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2005 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
************************************************************************/
#include <canvas/debug.hxx>
#include <canvas/canvastools.hxx>
#include <vcl/virdev.hxx>
#include <vcl/sysdata.hxx>
#include <vcl/metric.hxx>
#include <vcl/canvastools.hxx>
#include <basegfx/tools/canvastools.hxx>
#include "cairo_spritecanvas.hxx"
#include "cairo_canvasfont.hxx"
#include "cairo_textlayout.hxx"
#include "cairo_canvashelper.hxx"
using namespace ::cairo;
using namespace ::com::sun::star;
namespace cairocanvas
{
enum ColorType
{
LINE_COLOR, FILL_COLOR, TEXT_COLOR, IGNORE_COLOR
};
uno::Reference< rendering::XCanvasFont > CanvasHelper::createFont( const rendering::XCanvas* pCanvas,
const rendering::FontRequest& fontRequest,
const uno::Sequence< beans::PropertyValue >& extraFontProperties,
const geometry::Matrix2D& fontMatrix )
{
return uno::Reference< rendering::XCanvasFont >( new CanvasFont( fontRequest, extraFontProperties, fontMatrix, mpDevice ) );
}
uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* pCanvas,
const rendering::FontInfo& aFilter,
const uno::Sequence< beans::PropertyValue >& aFontProperties )
{
// TODO
return uno::Sequence< rendering::FontInfo >();
}
static VirtualDevice*
createVirtualDevice( Surface* pSurface )
{
SystemGraphicsData aSystemGraphicsData;
aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
aSystemGraphicsData.hDrawable = pSurface->getPixmap();
aSystemGraphicsData.pRenderFormat = pSurface->getRenderFormat();
return new VirtualDevice( &aSystemGraphicsData, pSurface->getDepth() );
}
static bool
setupFontTransform( ::OutputDevice& rOutDev,
::Point& o_rPoint,
::Font& io_rVCLFont,
const rendering::ViewState& rViewState,
const rendering::RenderState& rRenderState )
{
::basegfx::B2DHomMatrix aMatrix;
::canvas::tools::mergeViewAndRenderTransform(aMatrix,
rViewState,
rRenderState);
::basegfx::B2DTuple aScale;
::basegfx::B2DTuple aTranslate;
double nRotate, nShearX;
aMatrix.decompose( aScale, aTranslate, nRotate, nShearX );
// query font metric _before_ tampering with width and height
if( !::rtl::math::approxEqual(aScale.getX(), aScale.getY()) )
{
// retrieve true font width
const sal_Int32 nFontWidth( rOutDev.GetFontMetric( io_rVCLFont ).GetWidth() );
const sal_Int32 nScaledFontWidth( ::basegfx::fround(nFontWidth * aScale.getX()) );
if( !nScaledFontWidth )
{
// scale is smaller than one pixel - disable text
// output altogether
return false;
}
io_rVCLFont.SetWidth( nScaledFontWidth );
}
if( !::rtl::math::approxEqual(aScale.getY(), 1.0) )
{
const sal_Int32 nFontHeight( io_rVCLFont.GetHeight() );
io_rVCLFont.SetHeight( ::basegfx::fround(nFontHeight * aScale.getY()) );
}
io_rVCLFont.SetOrientation( static_cast< short >( ::basegfx::fround(-fmod(nRotate, 2*M_PI)*(1800.0/M_PI)) ) );
// TODO(F2): Missing functionality in VCL: shearing
o_rPoint.X() = ::basegfx::fround(aTranslate.getX());
o_rPoint.Y() = ::basegfx::fround(aTranslate.getY());
return true;
}
static int
setupOutDevState( OutputDevice& rOutDev,
SpriteCanvas* pDevice,
const rendering::ViewState& viewState,
const rendering::RenderState& renderState,
ColorType eColorType )
{
::canvas::tools::verifyInput( renderState,
BOOST_CURRENT_FUNCTION,
static_cast< ::cppu::OWeakObject* >(pDevice),
2,
eColorType == IGNORE_COLOR ? 0 : 3 );
int nTransparency(0);
// TODO(P2): Don't change clipping all the time, maintain current clip
// state and change only when update is necessary
// accumulate non-empty clips into one region
// ==========================================
Region aClipRegion;
if( viewState.Clip.is() )
{
::basegfx::B2DPolyPolygon aClipPoly(
::canvas::tools::polyPolygonFromXPolyPolygon2D(
viewState.Clip) );
if( aClipPoly.count() )
{
// setup non-empty clipping
::basegfx::B2DHomMatrix aMatrix;
aClipPoly.transform(
::basegfx::unotools::homMatrixFromAffineMatrix( aMatrix,
viewState.AffineTransform ) );
aClipRegion = Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly ) );
}
}
if( renderState.Clip.is() )
{
::basegfx::B2DPolyPolygon aClipPoly(
::canvas::tools::polyPolygonFromXPolyPolygon2D(
renderState.Clip) );
::basegfx::B2DHomMatrix aMatrix;
aClipPoly.transform(
::canvas::tools::mergeViewAndRenderTransform( aMatrix,
viewState,
renderState ) );
if( aClipPoly.count() )
{
// setup non-empty clipping
Region aRegion = Region::GetRegionFromPolyPolygon( ::PolyPolygon( aClipPoly ) );
if( aClipRegion.IsEmpty() )
aClipRegion = aRegion;
else
aClipRegion.Intersect( aRegion );
}
else
{
// clip polygon is empty
aClipRegion.SetEmpty();
}
}
// setup accumulated clip region. Note that setting an
// empty clip region denotes "clip everything" on the
// OutputDevice (which is why we translate that into
// SetClipRegion() here). When both view and render clip
// are empty, aClipRegion remains default-constructed,
// i.e. empty, too.
if( aClipRegion.IsEmpty() )
{
rOutDev.SetClipRegion();
}
else
{
rOutDev.SetClipRegion( aClipRegion );
}
if( eColorType != IGNORE_COLOR )
{
Color aColor( COL_WHITE );
if( renderState.DeviceColor.getLength() > 2 )
{
aColor = ::vcl::unotools::sequenceToColor( pDevice,
renderState.DeviceColor );
}
// extract alpha, and make color opaque
// afterwards. Otherwise, OutputDevice won't draw anything
nTransparency = aColor.GetTransparency();
aColor.SetTransparency(0);
switch( eColorType )
{
case LINE_COLOR:
rOutDev.SetLineColor( aColor );
rOutDev.SetFillColor();
break;
case FILL_COLOR:
rOutDev.SetFillColor( aColor );
rOutDev.SetLineColor();
break;
case TEXT_COLOR:
rOutDev.SetTextColor( aColor );
break;
default:
ENSURE_AND_THROW( false,
"CanvasHelper::setupOutDevState(): Unexpected color type");
break;
}
}
return nTransparency;
}
bool setupTextOutput( OutputDevice& rOutDev,
SpriteCanvas* pDevice,
::Point& o_rOutPos,
const rendering::ViewState& viewState,
const rendering::RenderState& renderState,
const uno::Reference< rendering::XCanvasFont >& xFont )
{
setupOutDevState( rOutDev, pDevice, viewState, renderState, TEXT_COLOR );
::Font aVCLFont;
CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() );
CHECK_AND_THROW( pFont,
"CanvasHelper::setupTextOutput(): Font not compatible with this canvas" );
aVCLFont = pFont->getVCLFont();
Color aColor( COL_BLACK );
if( renderState.DeviceColor.getLength() > 2 )
{
aColor = ::vcl::unotools::sequenceToColor( pDevice,
renderState.DeviceColor );
}
// setup font color
aVCLFont.SetColor( aColor );
aVCLFont.SetFillColor( aColor );
// no need to replicate this for mp2ndOutDev, we're modifying only aVCLFont here.
if( !setupFontTransform( rOutDev, o_rOutPos, aVCLFont, viewState, renderState ) )
return false;
rOutDev.SetFont( aVCLFont );
return true;
}
uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawText( const rendering::XCanvas* pCanvas,
const rendering::StringContext& text,
const uno::Reference< rendering::XCanvasFont >& xFont,
const rendering::ViewState& viewState,
const rendering::RenderState& renderState,
sal_Int8 textDirection )
{
#ifdef CAIRO_CANVAS_PERF_TRACE
struct timespec aTimer;
mxDevice->startPerfTrace( &aTimer );
#endif
CHECK_AND_THROW( xFont.is(),
"CanvasHelper::drawText(): font is NULL");
if( !mpVirtualDevice )
mpVirtualDevice = createVirtualDevice( mpSurface );
if( mpVirtualDevice )
{
::Point aOutpos;
if( !setupTextOutput( *mpVirtualDevice, mpDevice, aOutpos, viewState, renderState, xFont ) )
return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary
// change text direction and layout mode
ULONG nLayoutMode(0);
switch( textDirection )
{
case rendering::TextDirection::WEAK_LEFT_TO_RIGHT:
nLayoutMode |= TEXT_LAYOUT_BIDI_LTR;
// FALLTHROUGH intended
case rendering::TextDirection::STRONG_LEFT_TO_RIGHT:
nLayoutMode |= TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG;
nLayoutMode |= TEXT_LAYOUT_TEXTORIGIN_LEFT;
break;
case rendering::TextDirection::WEAK_RIGHT_TO_LEFT:
nLayoutMode |= TEXT_LAYOUT_BIDI_RTL;
// FALLTHROUGH intended
case rendering::TextDirection::STRONG_RIGHT_TO_LEFT:
nLayoutMode |= TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG;
nLayoutMode |= TEXT_LAYOUT_TEXTORIGIN_RIGHT;
break;
}
// TODO(F2): alpha
mpVirtualDevice->SetLayoutMode( nLayoutMode );
mpVirtualDevice->DrawText( aOutpos,
text.Text,
::canvas::tools::numeric_cast<USHORT>(text.StartPosition),
::canvas::tools::numeric_cast<USHORT>(text.Length) );
}
return uno::Reference< rendering::XCachedPrimitive >(NULL);
}
uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* pCanvas,
const uno::Reference< rendering::XTextLayout >& xLayoutedText,
const rendering::ViewState& viewState,
const rendering::RenderState& renderState )
{
CHECK_AND_THROW( xLayoutedText.is(),
"CanvasHelper::drawTextLayout(): layout is NULL");
TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() );
if( pTextLayout )
{
if( !mpVirtualDevice )
mpVirtualDevice = createVirtualDevice( mpSurface );
if( mpVirtualDevice )
{
// TODO(T3): Race condition. We're taking the font
// from xLayoutedText, and then calling draw() at it,
// without exclusive access. Move setupTextOutput(),
// e.g. to impltools?
::Point aOutpos;
if( !setupTextOutput( *mpVirtualDevice, mpDevice, aOutpos, viewState, renderState, xLayoutedText->getFont() ) )
return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary
// TODO(F2): What about the offset scalings?
pTextLayout->draw( *mpVirtualDevice, aOutpos, viewState, renderState );
}
}
else
{
CHECK_AND_THROW( false,
"CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" );
}
return uno::Reference< rendering::XCachedPrimitive >(NULL);
}
}