diff options
Diffstat (limited to 'canvas/source/cairo/cairo_canvashelper_text.cxx')
-rw-r--r-- | canvas/source/cairo/cairo_canvashelper_text.cxx | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/canvas/source/cairo/cairo_canvashelper_text.cxx b/canvas/source/cairo/cairo_canvashelper_text.cxx new file mode 100644 index 000000000000..ac530f5d4632 --- /dev/null +++ b/canvas/source/cairo/cairo_canvashelper_text.cxx @@ -0,0 +1,401 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: cairo_canvashelper_text.cxx,v $ + * $Revision: 1.9 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_canvas.hxx" + +#include <canvas/debug.hxx> +#include <canvas/canvastools.hxx> +#include <tools/diagnose_ex.h> + +#include <vcl/virdev.hxx> +#include <vcl/metric.hxx> +#include <vcl/canvastools.hxx> + +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/tools/canvastools.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* , + const rendering::FontRequest& fontRequest, + const uno::Sequence< beans::PropertyValue >& extraFontProperties, + const geometry::Matrix2D& fontMatrix ) + { + return uno::Reference< rendering::XCanvasFont >( new CanvasFont( fontRequest, extraFontProperties, fontMatrix, mpSurfaceProvider )); + } + + uno::Sequence< rendering::FontInfo > CanvasHelper::queryAvailableFonts( const rendering::XCanvas* , + const rendering::FontInfo& /*aFilter*/, + const uno::Sequence< beans::PropertyValue >& /*aFontProperties*/ ) + { + // TODO + return uno::Sequence< rendering::FontInfo >(); + } + + 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, + const rendering::XCanvas* pOwner, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + ColorType eColorType ) + { + ::canvas::tools::verifyInput( renderState, + BOOST_CURRENT_FUNCTION, + const_cast<rendering::XCanvas*>(pOwner), // only for refcount + 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( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( + 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( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( + 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::stdColorSpaceSequenceToColor( 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_OR_THROW( false, + "CanvasHelper::setupOutDevState(): Unexpected color type"); + break; + } + } + + return nTransparency; + } + + bool setupTextOutput( OutputDevice& rOutDev, + const rendering::XCanvas* pOwner, + ::Point& o_rOutPos, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Reference< rendering::XCanvasFont >& xFont ) + { + setupOutDevState( rOutDev, pOwner, viewState, renderState, TEXT_COLOR ); + + ::Font aVCLFont; + + CanvasFont* pFont = dynamic_cast< CanvasFont* >( xFont.get() ); + + ENSURE_ARG_OR_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::stdColorSpaceSequenceToColor(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* pOwner, + 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 + + ENSURE_ARG_OR_THROW( xFont.is(), + "CanvasHelper::drawText(): font is NULL"); + + if( !mpVirtualDevice ) + mpVirtualDevice = mpSurface->createVirtualDevice(); + + if( mpVirtualDevice ) + { +#if defined CAIRO_HAS_WIN32_SURFACE + // FIXME: Some kind of work-araound... + cairo_rectangle (mpSurface->getCairo().get(), 0, 0, 0, 0); + cairo_fill(mpSurface->getCairo().get()); +#endif + ::Point aOutpos; + if( !setupTextOutput( *mpVirtualDevice, pOwner, 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 ); + + OSL_TRACE(":cairocanvas::CanvasHelper::drawText(O,t,f,v,r,d): %s", ::rtl::OUStringToOString( text.Text.copy( text.StartPosition, text.Length ), + RTL_TEXTENCODING_UTF8 ).getStr()); + + TextLayout* pTextLayout = new TextLayout(text, textDirection, 0, CanvasFont::Reference(dynamic_cast< CanvasFont* >( xFont.get() )), mpSurfaceProvider); + pTextLayout->draw( mpSurface, *mpVirtualDevice, aOutpos, viewState, renderState ); + } + + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + + uno::Reference< rendering::XCachedPrimitive > CanvasHelper::drawTextLayout( const rendering::XCanvas* pOwner, + const uno::Reference< rendering::XTextLayout >& xLayoutedText, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + ENSURE_ARG_OR_THROW( xLayoutedText.is(), + "CanvasHelper::drawTextLayout(): layout is NULL"); + + TextLayout* pTextLayout = dynamic_cast< TextLayout* >( xLayoutedText.get() ); + + if( pTextLayout ) + { + if( !mpVirtualDevice ) + mpVirtualDevice = mpSurface->createVirtualDevice(); + + if( mpVirtualDevice ) + { +#if defined CAIRO_HAS_WIN32_SURFACE + // FIXME: Some kind of work-araound... + cairo_rectangle( mpSurface->getCairo().get(), 0, 0, 0, 0); + cairo_fill(mpSurface->getCairo().get()); +#endif + // 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, pOwner, aOutpos, viewState, renderState, xLayoutedText->getFont() ) ) + return uno::Reference< rendering::XCachedPrimitive >(NULL); // no output necessary + + // TODO(F2): What about the offset scalings? + pTextLayout->draw( mpSurface, *mpVirtualDevice, aOutpos, viewState, renderState ); + } + } + else + { + ENSURE_ARG_OR_THROW( false, + "CanvasHelper::drawTextLayout(): TextLayout not compatible with this canvas" ); + } + + return uno::Reference< rendering::XCachedPrimitive >(NULL); + } + +} |