Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  textaction.cxx   Sprache: 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 <comphelper/diagnose_ex.hxx>

#include <com/sun/star/rendering/PathCapType.hpp>
#include <com/sun/star/rendering/PathJoinType.hpp>
#include <com/sun/star/rendering/XCanvas.hpp>
#include <com/sun/star/rendering/XCanvasFont.hpp>

#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/range/b2drectangle.hxx>
#include <basegfx/vector/b2dsize.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>

#include <tools/gen.hxx>
#include <utility>
#include <vcl/canvastools.hxx>
#include <vcl/virdev.hxx>

#include <basegfx/utils/canvastools.hxx>
#include <canvas/canvastools.hxx>
#include <memory>
#include <sal/log.hxx>

#include "textaction.hxx"
#include "textlineshelper.hxx"
#include <outdevstate.hxx>
#include "mtftools.hxx"


using namespace ::com::sun::star;

namespace cppcanvas::internal
{
        namespace
        {
            void init( rendering::RenderState&                  o_rRenderState,
                       const ::basegfx::B2DPoint&               rStartPoint,
                       const OutDevState&                       rState,
                       const CanvasSharedPtr&                   rCanvas      )
            {
                tools::initRenderState(o_rRenderState,rState);

                // #i36950# Offset clip back to origin (as it's also moved
                // by rStartPoint)
                // #i53964# Also take VCL font rotation into account,
                // since this, opposed to the FontMatrix rotation
                // elsewhere, _does_ get incorporated into the render
                // state transform.
                tools::modifyClip( o_rRenderState,
                                   rState,
                                   rCanvas,
                                   rStartPoint,
                                   nullptr,
                                   &rState.fontRotation );

                basegfx::B2DHomMatrix aLocalTransformation(basegfx::utils::createRotateB2DHomMatrix(rState.fontRotation));
                aLocalTransformation.translate( rStartPoint.getX(),
                                                rStartPoint.getY() );
                ::canvas::tools::appendToRenderState( o_rRenderState,
                                                      aLocalTransformation );

                o_rRenderState.DeviceColor = rState.textColor;
            }

            void init( rendering::RenderState&                  o_rRenderState,
                       const ::basegfx::B2DPoint&               rStartPoint,
                       const OutDevState&                       rState,
                       const CanvasSharedPtr&                   rCanvas,
                       const ::basegfx::B2DHomMatrix&           rTextTransform  )
            {
                init( o_rRenderState, rStartPoint, rState, rCanvas );

                // TODO(F2): Also inversely-transform clip with
                // rTextTransform (which is actually rather hard, as the
                // text transform is _prepended_ to the render state)!

                // prepend extra font transform to render state
                // (prepend it, because it's interpreted in the unit
                // rect coordinate space)
                ::canvas::tools::prependToRenderState( o_rRenderState,
                                                       rTextTransform );
            }

            void init( rendering::RenderState&                      o_rRenderState,
                       uno::Reference< rendering::XCanvasFont >&    o_rFont,
                       const ::basegfx::B2DPoint&                   rStartPoint,
                       const OutDevState&                           rState,
                       const CanvasSharedPtr&                       rCanvas      )
            {
                // ensure that o_rFont is valid. It is possible that
                // text actions are generated without previously
                // setting a font. Then, just take a default font
                if( !o_rFont.is() )
                {
                    // Use completely default FontRequest
                    const rendering::FontRequest aFontRequest;

                    geometry::Matrix2D aFontMatrix;
                    ::canvas::tools::setIdentityMatrix2D( aFontMatrix );

                    o_rFont = rCanvas->getUNOCanvas()->createFont(
                        aFontRequest,
                        uno::Sequence< beans::PropertyValue >(),
                        aFontMatrix );
                }

                init( o_rRenderState,
                      rStartPoint,
                      rState,
                      rCanvas );
            }

            void init( rendering::RenderState&                      o_rRenderState,
                       uno::Reference< rendering::XCanvasFont >&    o_rFont,
                       const ::basegfx::B2DPoint&                   rStartPoint,
                       const OutDevState&                           rState,
                       const CanvasSharedPtr&                       rCanvas,
                       const ::basegfx::B2DHomMatrix&               rTextTransform  )
            {
                init( o_rRenderState, o_rFont, rStartPoint, rState, rCanvas );

                // TODO(F2): Also inversely-transform clip with
                // rTextTransform (which is actually rather hard, as the
                // text transform is _prepended_ to the render state)!

                // prepend extra font transform to render state
                // (prepend it, because it's interpreted in the unit
                // rect coordinate space)
                ::canvas::tools::prependToRenderState( o_rRenderState,
                                                       rTextTransform );
            }

            void initLayoutWidth(double& rLayoutWidth, const uno::Sequence<double>& rOffsets)
            {
                ENSURE_OR_THROW(rOffsets.hasElements(),
                                  "::cppcanvas::internal::initLayoutWidth(): zero-length array" );
                rLayoutWidth = *(std::max_element(rOffsets.begin(), rOffsets.end()));
            }

            uno::Sequence< double > setupDXArray( KernArraySpan    rCharWidths,
                                                  sal_Int32          nLen,
                                                  const OutDevState& rState )
            {
                // convert character widths from logical units
                uno::Sequence< double > aCharWidthSeq( nLen );
                double*                 pOutputWidths( aCharWidthSeq.getArray() );

                // #143885# maintain (nearly) full precision of DX
                // array, by circumventing integer-based
                // OutDev-mapping
                const double nScale( rState.mapModeTransform.get(0,0) );
                forint i = 0; i < nLen; ++i )
                {
                    // TODO(F2): use correct scale direction
                    *pOutputWidths++ = rCharWidths[i] * nScale;
                }

                return aCharWidthSeq;
            }

            uno::Sequence< double > setupDXArray( const OUString&    rText,
                                                  sal_Int32          nStartPos,
                                                  sal_Int32          nLen,
                                                  VirtualDevice const & rVDev,
                                                  const OutDevState& rState )
            {
                // no external DX array given, create one from given
                // string
                KernArray aCharWidths;

                rVDev.GetTextArray( rText, &aCharWidths, nStartPos, nLen );

                return setupDXArray( aCharWidths, nLen, rState );
            }

            ::basegfx::B2DPoint adaptStartPoint( const ::basegfx::B2DPoint&     rStartPoint,
                                                 const OutDevState&             rState,
                                                 const uno::Sequence< double >& rOffsets )
            {
                ::basegfx::B2DPoint aLocalPoint( rStartPoint );

                if( rState.textAlignment )
                {
                    // text origin is right, not left. Modify start point
                    // accordingly, because XCanvas::drawTextLayout()
                    // always aligns left!

                    const double nOffset( rOffsets[ rOffsets.getLength()-1 ] );

                    // correct start point for rotated text: rotate around
                    // former start point
                    aLocalPoint.setX( aLocalPoint.getX() + cos( rState.fontRotation )*nOffset );
                    aLocalPoint.setY( aLocalPoint.getY() + sin( rState.fontRotation )*nOffset );
                }

                return aLocalPoint;
            }

            /** Perform common setup for array text actions

                This method creates the XTextLayout object and
                initializes it, e.g. with the logical advancements.
             */

            void initArrayAction( rendering::RenderState&                   o_rRenderState,
                                  uno::Reference< rendering::XTextLayout >& o_rTextLayout,
                                  const ::basegfx::B2DPoint&                rStartPoint,
                                  const OUString&                    rText,
                                  sal_Int32                                 nStartPos,
                                  sal_Int32                                 nLen,
                                  const uno::Sequence< double >&            rOffsets,
                                  const uno::Sequence< sal_Bool >&          rKashidas,
                                  const CanvasSharedPtr&                    rCanvas,
                                  const OutDevState&                        rState,
                                  const ::basegfx::B2DHomMatrix*            pTextTransform )
            {
                ENSURE_OR_THROW( rOffsets.hasElements(),
                                  "::cppcanvas::internal::initArrayAction(): zero-length DX array" );

                const ::basegfx::B2DPoint aLocalStartPoint(
                    adaptStartPoint( rStartPoint, rState, rOffsets ) );

                uno::Reference< rendering::XCanvasFont > xFont( rState.xFont );

                if( pTextTransform )
                    init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas, *pTextTransform );
                else
                    init( o_rRenderState, xFont, aLocalStartPoint, rState, rCanvas );

                o_rTextLayout = xFont->createTextLayout(
                    rendering::StringContext( rText, nStartPos, nLen ),
                    rState.textDirection,
                    0 );

                ENSURE_OR_THROW( o_rTextLayout.is(),
                                  "::cppcanvas::internal::initArrayAction(): Invalid font" );

                o_rTextLayout->applyLogicalAdvancements( rOffsets );
                o_rTextLayout->applyKashidaPositions( rKashidas );

            }

            double getLineWidth( ::VirtualDevice const &         rVDev,
                                 const OutDevState&              rState,
                                 const rendering::StringContext& rStringContext )
            {
                // TODO(F2): use correct scale direction
                const ::basegfx::B2DSize aSize( rVDev.GetTextWidth( rStringContext.Text,
                                                                    static_cast<sal_uInt16>(rStringContext.StartPosition),
                                                                    static_cast<sal_uInt16>(rStringContext.Length) ),
                                    0 );

                return (rState.mapModeTransform * aSize).getWidth();
            }

            uno::Sequence< double >
                calcSubsetOffsets( rendering::RenderState&                          io_rRenderState,
                                   double&                                          o_rMinPos,
                                   double&                                          o_rMaxPos,
                                   const uno::Reference< rendering::XTextLayout >&  rOrigTextLayout,
                                   double                                           nLayoutWidth,
                                   const ::cppcanvas::internal::Action::Subset&     rSubset )
            {
                ENSURE_OR_THROW( rSubset.mnSubsetEnd > rSubset.mnSubsetBegin,
                                  "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );

                uno::Sequence< double > aOrigOffsets( rOrigTextLayout->queryLogicalAdvancements() );
                const double*           pOffsets( aOrigOffsets.getConstArray() );

                ENSURE_OR_THROW( aOrigOffsets.getLength() >= rSubset.mnSubsetEnd,
                                  "::cppcanvas::internal::calcSubsetOffsets(): invalid subset range range" );


                // determine leftmost position in given subset range -
                // as the DX array contains the output positions
                // starting with the second character (the first is
                // assumed to have output position 0), correct begin
                // iterator.
                const double nMinPos( rSubset.mnSubsetBegin <= 0 ? 0 :
                                      *(std::min_element( pOffsets+rSubset.mnSubsetBegin-1,
                                                            pOffsets+rSubset.mnSubsetEnd )) );

                // determine rightmost position in given subset range
                // - as the DX array contains the output positions
                // starting with the second character (the first is
                // assumed to have output position 0), correct begin
                // iterator.
                const double nMaxPos(
                    *(std::max_element( pOffsets + (rSubset.mnSubsetBegin <= 0 ?
                                                      0 : rSubset.mnSubsetBegin-1),
                                          pOffsets + rSubset.mnSubsetEnd )) );

                // Logical advancements always increase in logical text order.
                // For RTL text, nMaxPos is the distance from the right edge to
                // the leftmost position in the subset, so we have to convert
                // it to the offset from the origin (i.e. left edge ).
                // LTR: |---- min --->|---- max --->|            |
                // RTL: |             |<--- max ----|<--- min ---|
                //      |<- nOffset ->|                          |
                const double nOffset = rOrigTextLayout->getMainTextDirection()
                    ? nLayoutWidth - nMaxPos : nMinPos;


                // adapt render state, to move text output to given offset


                // TODO(F1): Strictly speaking, we also have to adapt
                // the clip here, which normally should _not_ move
                // with the output offset. Neglected for now, as it
                // does not matter for drawing layer output

                if (nOffset > 0.0)
                {
                    ::basegfx::B2DHomMatrix aTranslation;
                    if( rOrigTextLayout->getFont()->getFontRequest().FontDescription.IsVertical == css::util::TriState_YES )
                    {
                        // vertical text -> offset in y direction
                        aTranslation.translate(0.0, nOffset);
                    }
                    else
                    {
                        // horizontal text -> offset in x direction
                        aTranslation.translate(nOffset, 0.0);
                    }

                    ::canvas::tools::appendToRenderState( io_rRenderState,
                                                          aTranslation );
                }


                // reduce DX array to given substring


                const sal_Int32         nNewElements( rSubset.mnSubsetEnd - rSubset.mnSubsetBegin );
                uno::Sequence< double > aAdaptedOffsets( nNewElements );
                double*                 pAdaptedOffsets( aAdaptedOffsets.getArray() );

                // move to new output position (subtract nMinPos,
                // which is the new '0' position), copy only the range
                // as given by rSubset.
                std::transform( pOffsets + rSubset.mnSubsetBegin,
                                  pOffsets + rSubset.mnSubsetEnd,
                                  pAdaptedOffsets,
                                  [nMinPos](double aPos) { return aPos - nMinPos; } );

                o_rMinPos = nMinPos;
                o_rMaxPos = nMaxPos;

                return aAdaptedOffsets;
            }

            uno::Reference< rendering::XTextLayout >
                createSubsetLayout( const rendering::StringContext&                 rOrigContext,
                                    const ::cppcanvas::internal::Action::Subset&    rSubset,
                                    const uno::Reference< rendering::XTextLayout >& rOrigTextLayout )
            {
                // create temporary new text layout with subset string


                const sal_Int32 nNewStartPos( rOrigContext.StartPosition + std::min(
                                                  rSubset.mnSubsetBegin, rOrigContext.Length-1 ) );
                const sal_Int32 nNewLength( std::max(
                                                std::min(
                                                    rSubset.mnSubsetEnd - rSubset.mnSubsetBegin,
                                                    rOrigContext.Length ),
                                                sal_Int32( 0 ) ) );

                const rendering::StringContext aContext( rOrigContext.Text,
                                                         nNewStartPos,
                                                         nNewLength );

                uno::Reference< rendering::XTextLayout > xTextLayout(
                    rOrigTextLayout->getFont()->createTextLayout( aContext,
                                                                  rOrigTextLayout->getMainTextDirection(),
                                                                  0 ),
                    uno::UNO_SET_THROW );

                return xTextLayout;
            }

            /** Setup subset text layout

                @param io_rTextLayout
                Must contain original (full set) text layout on input,
                will contain subsetted text layout (or empty
                reference, for empty subsets) on output.

                @param io_rRenderState
                Must contain original render state on input, will
                contain shifted render state concatenated with
                rTransformation on output.

                @param rTransformation
                Additional transformation, to be prepended to render
                state

                @param rSubset
                Subset to prepare
             */

            void createSubsetLayout( uno::Reference< rendering::XTextLayout >&  io_rTextLayout,
                                     double                                     nLayoutWidth,
                                     rendering::RenderState&                    io_rRenderState,
                                     double&                                    o_rMinPos,
                                     double&                                    o_rMaxPos,
                                     const ::basegfx::B2DHomMatrix&             rTransformation,
                                     const Action::Subset&                      rSubset )
            {
                ::canvas::tools::prependToRenderState(io_rRenderState, rTransformation);

                if( rSubset.mnSubsetBegin == rSubset.mnSubsetEnd )
                {
                     // empty range, empty layout
                    io_rTextLayout.clear();

                    return;
                }

                ENSURE_OR_THROW( io_rTextLayout.is(),
                                  "createSubsetLayout(): Invalid input layout" );

                const rendering::StringContext aOrigContext( io_rTextLayout->getText() );

                if( rSubset.mnSubsetBegin == 0 &&
                    rSubset.mnSubsetEnd == aOrigContext.Length )
                {
                    // full range, no need for subsetting
                    return;
                }

                uno::Reference< rendering::XTextLayout > xTextLayout(
                    createSubsetLayout( aOrigContext, rSubset, io_rTextLayout ) );

                if( xTextLayout.is() )
                {
                    xTextLayout->applyLogicalAdvancements(
                        calcSubsetOffsets( io_rRenderState,
                                           o_rMinPos,
                                           o_rMaxPos,
                                           io_rTextLayout,
                                           nLayoutWidth,
                                           rSubset ) );
                    uno::Sequence< sal_Bool > aOrigKashidaPositions(io_rTextLayout->queryKashidaPositions());
                    uno::Sequence< sal_Bool > aKashidaPositions(aOrigKashidaPositions.getArray() + rSubset.mnSubsetBegin,
                                                                rSubset.mnSubsetEnd - rSubset.mnSubsetBegin);
                    xTextLayout->applyKashidaPositions(aKashidaPositions);
                }

                io_rTextLayout = std::move(xTextLayout);
            }


            /** Interface for renderEffectText functor below.

                This is interface is used from the renderEffectText()
                method below, to call the client implementation.
             */

            class TextRenderer
            {
            public:
                virtual ~TextRenderer() {}

                /// Render text with given RenderState
                virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color&&nbsp;rTextFillColor, bool bNormalText ) const = 0;
            };

            /** Render effect text.

                @param rRenderer
                Functor object, will be called to render the actual
                part of the text effect (the text itself and the means
                to render it are unknown to this method)
             */

            bool renderEffectText( const TextRenderer&                          rRenderer,
                                   const rendering::RenderState&                rRenderState,
                                   const uno::Reference< rendering::XCanvas >&  xCanvas,
                                   const ::Color&                               rShadowColor,
                                   const ::basegfx::B2DSize&                    rShadowOffset,
                                   const ::Color&                               rReliefColor,
                                   const ::basegfx::B2DSize&                    rReliefOffset,
                                   const ::Color&                               rTextFillColor )
            {
                ::Color aEmptyColor( COL_AUTO );
                uno::Reference<rendering::XColorSpace> xColorSpace(
                    xCanvas->getDevice()->getDeviceColorSpace() );

                // draw shadow text, if enabled
                if( rShadowColor != aEmptyColor )
                {
                    rendering::RenderState aShadowState( rRenderState );
                    ::basegfx::B2DHomMatrix aTranslate;

                    aTranslate.translate(rShadowOffset.getWidth(),
                                         rShadowOffset.getHeight());

                    ::canvas::tools::appendToRenderState(aShadowState, aTranslate);

                    aShadowState.DeviceColor =
                        vcl::unotools::colorToDoubleSequence( rShadowColor,
                                                                xColorSpace );

                    rRenderer( aShadowState, rTextFillColor, false );
                }

                // draw relief text, if enabled
                if( rReliefColor != aEmptyColor )
                {
                    rendering::RenderState aReliefState( rRenderState );
                    ::basegfx::B2DHomMatrix aTranslate;

                    aTranslate.translate(rReliefOffset.getWidth(),
                                         rReliefOffset.getHeight());

                    ::canvas::tools::appendToRenderState(aReliefState, aTranslate);

                    aReliefState.DeviceColor =
                        vcl::unotools::colorToDoubleSequence( rReliefColor,
                                                                xColorSpace );

                    rRenderer( aReliefState, rTextFillColor, false );
                }

                // draw normal text
                rRenderer( rRenderState, rTextFillColor, true );

                return true;
            }


            ::basegfx::B2DRange calcEffectTextBounds( const ::basegfx::B2DRange&    rTextBounds,
                                                      const ::basegfx::B2DRange&    rLineBounds,
                                                      const ::basegfx::B2DSize&     rReliefOffset,
                                                      const ::basegfx::B2DSize&     rShadowOffset,
                                                      const rendering::RenderState& rRenderState,
                                                      const rendering::ViewState&   rViewState )
            {
                ::basegfx::B2DRange aBounds( rTextBounds );

                // add extends of text lines
                aBounds.expand( rLineBounds );

                // TODO(Q3): Provide this functionality at the B2DRange
                ::basegfx::B2DRange aTotalBounds( aBounds );
                aTotalBounds.expand(
                    ::basegfx::B2DRange( aBounds.getMinX() + rReliefOffset.getWidth(),
                                         aBounds.getMinY() + rReliefOffset.getHeight(),
                                         aBounds.getMaxX() + rReliefOffset.getWidth(),
                                         aBounds.getMaxY() + rReliefOffset.getHeight() ) );
                aTotalBounds.expand(
                    ::basegfx::B2DRange( aBounds.getMinX() + rShadowOffset.getWidth(),
                                         aBounds.getMinY() + rShadowOffset.getHeight(),
                                         aBounds.getMaxX() + rShadowOffset.getWidth(),
                                         aBounds.getMaxY() + rShadowOffset.getHeight() ) );

                return tools::calcDevicePixelBounds( aTotalBounds,
                                                     rViewState,
                                                     rRenderState );
            }

            void initEffectLinePolyPolygon( ::basegfx::B2DSize&                             o_rOverallSize,
                                            uno::Reference< rendering::XPolyPolygon2D >&    o_rTextLines,
                                            const CanvasSharedPtr&                          rCanvas,
                                            double                                          nLineWidth,
                                            const tools::TextLineInfo&                      rLineInfo   )
            {
                const ::basegfx::B2DPolyPolygon aPoly(
                    tools::createTextLinesPolyPolygon( 0.0, nLineWidth,
                                                       rLineInfo ) );
                auto aRange = basegfx::utils::getRange( aPoly ).getRange();
                o_rOverallSize = basegfx::B2DSize(aRange.getX(), aRange.getY());

                o_rTextLines = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
                    rCanvas->getUNOCanvas()->getDevice(),
                    aPoly );
            }


            class TextAction : public Action
            {
            public:
                TextAction( const ::basegfx::B2DPoint&  rStartPoint,
                            const OUString&      rString,
                            sal_Int32                   nStartPos,
                            sal_Int32                   nLen,
                            const CanvasSharedPtr&      rCanvas,
                            const OutDevState&          rState );

                TextAction( const ::basegfx::B2DPoint&      rStartPoint,
                            const OUString&          rString,
                            sal_Int32                       nStartPos,
                            sal_Int32                       nLen,
                            const CanvasSharedPtr&          rCanvas,
                            const OutDevState&              rState,
                            const ::basegfx::B2DHomMatrix&  rTextTransform );

                TextAction(const TextAction&) = delete;
                const TextAction& operator=(const TextAction&) = delete;

                virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
                virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
                                           const Subset&                  rSubset ) const override;

                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&   rTransformation,
                                                       const Subset&                    rSubset ) const override;

                virtual sal_Int32 getActionCount() const override;

            private:
                // TODO(P2): This is potentially a real mass object
                // (every character might be a separate TextAction),
                // thus, make it as lightweight as possible. For
                // example, share common RenderState among several
                // TextActions, maybe using maOffsets for the
                // translation.

                uno::Reference< rendering::XCanvasFont >    mxFont;
                const rendering::StringContext              maStringContext;
                const CanvasSharedPtr                       mpCanvas;
                rendering::RenderState                      maState;
                const sal_Int8                              maTextDirection;
            };

            TextAction::TextAction( const ::basegfx::B2DPoint&  rStartPoint,
                                    const OUString&      rString,
                                    sal_Int32                   nStartPos,
                                    sal_Int32                   nLen,
                                    const CanvasSharedPtr&      rCanvas,
                                    const OutDevState&          rState  ) :
                mxFont( rState.xFont ),
                maStringContext( rString, nStartPos, nLen ),
                mpCanvas( rCanvas ),
                maTextDirection( rState.textDirection )
            {
                init( maState, mxFont,
                      rStartPoint,
                      rState, rCanvas );

                ENSURE_OR_THROW( mxFont.is(),
                                  "::cppcanvas::internal::TextAction(): Invalid font" );
            }

            TextAction::TextAction( const ::basegfx::B2DPoint&      rStartPoint,
                                    const OUString&          rString,
                                    sal_Int32                       nStartPos,
                                    sal_Int32                       nLen,
                                    const CanvasSharedPtr&          rCanvas,
                                    const OutDevState&              rState,
                                    const ::basegfx::B2DHomMatrix&  rTextTransform ) :
                mxFont( rState.xFont ),
                maStringContext( rString, nStartPos, nLen ),
                mpCanvas( rCanvas ),
                maTextDirection( rState.textDirection )
            {
                init( maState, mxFont,
                      rStartPoint,
                      rState, rCanvas, rTextTransform );

                ENSURE_OR_THROW( mxFont.is(),
                                  "::cppcanvas::internal::TextAction(): Invalid font" );
            }

            bool TextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
            {
                SAL_INFO( "cppcanvas.emf""::cppcanvas::internal::TextAction::render()" );
                SAL_INFO( "cppcanvas.emf""::cppcanvas::internal::TextAction: 0x" << std::hex << this );

                rendering::RenderState aLocalState( maState );
                ::canvas::tools::prependToRenderState(aLocalState, rTransformation);

                mpCanvas->getUNOCanvas()->drawText( maStringContext, mxFont,
                                                    mpCanvas->getViewState(), aLocalState, maTextDirection );

                return true;
            }

            bool TextAction::renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
                                           const Subset&                  /*rSubset*/ ) const
            {
                SAL_WARN( "cppcanvas.emf""TextAction::renderSubset(): Subset not supported by this object" );

                // TODO(P1): Retrieve necessary font metric info for
                // TextAction from XCanvas. Currently, the
                // TextActionFactory does not generate this object for
                // _subsettable_ text
                return render( rTransformation );
            }

            ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
            {
                // create XTextLayout, to have the
                // XTextLayout::queryTextBounds() method available
                uno::Reference< rendering::XTextLayout > xTextLayout(
                    mxFont->createTextLayout(
                        maStringContext,
                        maTextDirection,
                        0 ) );

                rendering::RenderState aLocalState( maState );
                ::canvas::tools::prependToRenderState(aLocalState, rTransformation);

                return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
                                                         xTextLayout->queryTextBounds() ),
                                                     mpCanvas->getViewState(),
                                                     aLocalState );
            }

            ::basegfx::B2DRange TextAction::getBounds( const ::basegfx::B2DHomMatrix&   rTransformation,
                                                       const Subset&                    /*rSubset*/ ) const
            {
                SAL_WARN( "cppcanvas.emf""TextAction::getBounds(): Subset not supported by this object" );

                // TODO(P1): Retrieve necessary font metric info for
                // TextAction from XCanvas. Currently, the
                // TextActionFactory does not generate this object for
                // _subsettable_ text
                return getBounds( rTransformation );
            }

            sal_Int32 TextAction::getActionCount() const
            {
                // TODO(P1): Retrieve necessary font metric info for
                // TextAction from XCanvas. Currently, the
                // TextActionFactory does not generate this object for
                // _subsettable_ text
                return 1;
            }


            class EffectTextAction :
                public Action,
                public TextRenderer
            {
            public:
                EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
                                  const ::basegfx::B2DSize&  rReliefOffset,
                                  const ::Color&             rReliefColor,
                                  const ::basegfx::B2DSize&  rShadowOffset,
                                  const ::Color&             rShadowColor,
                                  const ::Color&             rTextFillColor,
                                  const OUString&     rText,
                                  sal_Int32                  nStartPos,
                                  sal_Int32                  nLen,
                                  VirtualDevice const &      rVDev,
                                  const CanvasSharedPtr&     rCanvas,
                                  const OutDevState&         rState );

                EffectTextAction( const ::basegfx::B2DPoint&        rStartPoint,
                                  const ::basegfx::B2DSize&         rReliefOffset,
                                  const ::Color&                    rReliefColor,
                                  const ::basegfx::B2DSize&         rShadowOffset,
                                  const ::Color&                    rShadowColor,
                                  const ::Color&                    rTextFillColor,
                                  const OUString&            rText,
                                  sal_Int32                         nStartPos,
                                  sal_Int32                         nLen,
                                  VirtualDevice const &             rVDev,
                                  const CanvasSharedPtr&            rCanvas,
                                  const OutDevState&                rState,
                                  const ::basegfx::B2DHomMatrix&    rTextTransform );

                EffectTextAction(const EffectTextAction&) = delete;
                const EffectTextAction& operator=(const EffectTextAction&) = delete;

                virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
                virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
                                           const Subset&                  rSubset ) const override;

                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&   rTransformation,
                                                       const Subset&                    rSubset ) const override;

                virtual sal_Int32 getActionCount() const override;

            private:
                /// Interface TextRenderer
                virtual bool operator()( const rendering::RenderState& rRenderState, const ::Color&&nbsp;rTextFillColor, bool bNormalText ) const override;

                geometry::RealRectangle2D queryTextBounds() const;
                css::uno::Reference<css::rendering::XPolyPolygon2D> queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const;

                // TODO(P2): This is potentially a real mass object
                // (every character might be a separate TextAction),
                // thus, make it as lightweight as possible. For
                // example, share common RenderState among several
                // TextActions, maybe using maOffsets for the
                // translation.

                uno::Reference< rendering::XCanvasFont >    mxFont;
                const rendering::StringContext              maStringContext;
                const CanvasSharedPtr                       mpCanvas;
                rendering::RenderState                      maState;
                const tools::TextLineInfo                   maTextLineInfo;
                ::basegfx::B2DSize                          maLinesOverallSize;
                uno::Reference< rendering::XPolyPolygon2D > mxTextLines;
                const ::basegfx::B2DSize                    maReliefOffset;
                const ::Color                               maReliefColor;
                const ::basegfx::B2DSize                    maShadowOffset;
                const ::Color                               maShadowColor;
                const ::Color                               maTextFillColor;
                const sal_Int8                              maTextDirection;
            };

            EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint& rStartPoint,
                                                const ::basegfx::B2DSize&  rReliefOffset,
                                                const ::Color&             rReliefColor,
                                                const ::basegfx::B2DSize&  rShadowOffset,
                                                const ::Color&             rShadowColor,
                                                const ::Color&             rTextFillColor,
                                                const OUString&     rText,
                                                sal_Int32                  nStartPos,
                                                sal_Int32                  nLen,
                                                VirtualDevice const &      rVDev,
                                                const CanvasSharedPtr&     rCanvas,
                                                const OutDevState&         rState ) :
                mxFont( rState.xFont ),
                maStringContext( rText, nStartPos, nLen ),
                mpCanvas( rCanvas ),
                maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
                maReliefOffset( rReliefOffset ),
                maReliefColor( rReliefColor ),
                maShadowOffset( rShadowOffset ),
                maShadowColor( rShadowColor ),
                maTextFillColor( rTextFillColor ),
                maTextDirection( rState.textDirection )
            {
                const double nLineWidth(getLineWidth( rVDev, rState, maStringContext ));
                initEffectLinePolyPolygon( maLinesOverallSize,
                                           mxTextLines,
                                           rCanvas,
                                           nLineWidth,
                                           maTextLineInfo );

                init( maState, mxFont,
                      rStartPoint,
                      rState, rCanvas );

                ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
                                  "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
            }

            EffectTextAction::EffectTextAction( const ::basegfx::B2DPoint&      rStartPoint,
                                                const ::basegfx::B2DSize&       rReliefOffset,
                                                const ::Color&                  rReliefColor,
                                                const ::basegfx::B2DSize&       rShadowOffset,
                                                const ::Color&                  rShadowColor,
                                                const ::Color&                  rTextFillColor,
                                                const OUString&          rText,
                                                sal_Int32                       nStartPos,
                                                sal_Int32                       nLen,
                                                VirtualDevice const &           rVDev,
                                                const CanvasSharedPtr&          rCanvas,
                                                const OutDevState&              rState,
                                                const ::basegfx::B2DHomMatrix&  rTextTransform ) :
                mxFont( rState.xFont ),
                maStringContext( rText, nStartPos, nLen ),
                mpCanvas( rCanvas ),
                maTextLineInfo( tools::createTextLineInfo( rVDev, rState ) ),
                maReliefOffset( rReliefOffset ),
                maReliefColor( rReliefColor ),
                maShadowOffset( rShadowOffset ),
                maShadowColor( rShadowColor ),
                maTextFillColor( rTextFillColor ),
                maTextDirection( rState.textDirection )
            {
                const double nLineWidth( getLineWidth( rVDev, rState, maStringContext ) );
                initEffectLinePolyPolygon( maLinesOverallSize,
                                           mxTextLines,
                                           rCanvas,
                                           nLineWidth,
                                           maTextLineInfo );

                init( maState, mxFont,
                      rStartPoint,
                      rState, rCanvas, rTextTransform );

                ENSURE_OR_THROW( mxFont.is() && mxTextLines.is(),
                                  "::cppcanvas::internal::EffectTextAction(): Invalid font or lines" );
            }

            bool EffectTextAction::operator()( const rendering::RenderState& rRenderState, const ::Color& rTextFillColor, bool /*bNormalText*/ ) const
            {
                const rendering::ViewState aViewState( mpCanvas->getViewState() );
                const uno::Reference< rendering::XCanvas > aCanvas( mpCanvas->getUNOCanvas() );

                //rhbz#1589029 non-transparent text fill background support
                if (rTextFillColor != COL_AUTO)
                {
                    rendering::RenderState aLocalState( rRenderState );
                    aLocalState.DeviceColor = vcl::unotools::colorToDoubleSequence(
                        rTextFillColor, aCanvas->getDevice()->getDeviceColorSpace());
                    auto xTextBounds = queryTextBounds(aCanvas);
                    // background of text
                    aCanvas->fillPolyPolygon(xTextBounds, aViewState, aLocalState);
                }

                // under/over lines
                aCanvas->fillPolyPolygon( mxTextLines,
                                          aViewState,
                                          rRenderState );

                aCanvas->drawText( maStringContext, mxFont,
                                   aViewState,
                                   rRenderState,
                                   maTextDirection );

                return true;
            }

            bool EffectTextAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
            {
                SAL_INFO( "cppcanvas.emf""::cppcanvas::internal::EffectTextAction::render()" );
                SAL_INFO( "cppcanvas.emf""::cppcanvas::internal::EffectTextAction: 0x" << std::hex << this );

                rendering::RenderState aLocalState( maState );
                ::canvas::tools::prependToRenderState(aLocalState, rTransformation);

                return renderEffectText( *this,
                                         aLocalState,
                                         mpCanvas->getUNOCanvas(),
                                         maShadowColor,
                                         maShadowOffset,
                                         maReliefColor,
                                         maReliefOffset,
                                         maTextFillColor);
            }

            bool EffectTextAction::renderSubset( const ::basegfx::B2DHomMatrix&   rTransformation,
                                                 const Subset&                    /*rSubset*/ ) const
            {
                SAL_WARN( "cppcanvas.emf""EffectTextAction::renderSubset(): Subset not supported by this object" );

                // TODO(P1): Retrieve necessary font metric info for
                // TextAction from XCanvas. Currently, the
                // TextActionFactory does not generate this object for
                // subsettable text
                return render( rTransformation );
            }

            geometry::RealRectangle2D EffectTextAction::queryTextBounds() const
            {
                // create XTextLayout, to have the
                // XTextLayout::queryTextBounds() method available
                uno::Reference< rendering::XTextLayout > xTextLayout(
                    mxFont->createTextLayout(
                        maStringContext,
                        maTextDirection,
                        0 ) );

                return xTextLayout->queryTextBounds();
            }

            css::uno::Reference<css::rendering::XPolyPolygon2D> EffectTextAction::queryTextBounds(const uno::Reference<rendering::XCanvas>& rCanvas) const
            {
                auto aTextBounds = queryTextBounds();
                auto aB2DBounds = ::basegfx::unotools::b2DRectangleFromRealRectangle2D(aTextBounds);
                auto aTextBoundsPoly = ::basegfx::utils::createPolygonFromRect(aB2DBounds);
                return ::basegfx::unotools::xPolyPolygonFromB2DPolygon(rCanvas->getDevice(), aTextBoundsPoly);
            }

            ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
            {
                rendering::RenderState aLocalState( maState );
                ::canvas::tools::prependToRenderState(aLocalState, rTransformation);

                return calcEffectTextBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
                                                 queryTextBounds() ),
                                             ::basegfx::B2DRange( 0,0,
                                                                  maLinesOverallSize.getWidth(),
                                                                  maLinesOverallSize.getHeight() ),
                                             maReliefOffset,
                                             maShadowOffset,
                                             aLocalState,
                                             mpCanvas->getViewState() );
            }

            ::basegfx::B2DRange EffectTextAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation,
                                                             const Subset&                  /*rSubset*/ ) const
            {
                SAL_WARN( "cppcanvas.emf""EffectTextAction::getBounds(): Subset not supported by this object" );

                // TODO(P1): Retrieve necessary font metric info for
                // TextAction from XCanvas. Currently, the
                // TextActionFactory does not generate this object for
                // _subsettable_ text
                return getBounds( rTransformation );
            }

            sal_Int32 EffectTextAction::getActionCount() const
            {
                // TODO(P1): Retrieve necessary font metric info for
                // TextAction from XCanvas. Currently, the
                // TextActionFactory does not generate this object for
                // subsettable text
                return 1;
            }


            class TextArrayAction : public Action
            {
            public:
                TextArrayAction( const ::basegfx::B2DPoint&     rStartPoint,
                                 const OUString&         rString,
                                 sal_Int32                      nStartPos,
                                 sal_Int32                      nLen,
                                 const uno::Sequence< double >& rOffsets,
                                 const uno::Sequence< sal_Bool >& rKashidas,
                                 const CanvasSharedPtr&         rCanvas,
                                 const OutDevState&             rState );

                TextArrayAction( const ::basegfx::B2DPoint&     rStartPoint,
                                 const OUString&         rString,
                                 sal_Int32                      nStartPos,
                                 sal_Int32                      nLen,
                                 const uno::Sequence< double >& rOffsets,
                                 const uno::Sequence< sal_Bool >& rKashidas,
                                 const CanvasSharedPtr&         rCanvas,
                                 const OutDevState&             rState,
                                 const ::basegfx::B2DHomMatrix& rTextTransform );

                TextArrayAction(const TextArrayAction&) = delete;
                const TextArrayAction& operator=(const TextArrayAction&) = delete;

                virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
                virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
                                           const Subset&                  rSubset ) const override;

                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix&   rTransformation,
                                                       const Subset&                    rSubset ) const override;

                virtual sal_Int32 getActionCount() const override;

            private:
                // TODO(P2): This is potentially a real mass object
                // (every character might be a separate TextAction),
                // thus, make it as lightweight as possible. For
                // example, share common RenderState among several
                // TextActions, maybe using maOffsets for the
                // translation.

                uno::Reference< rendering::XTextLayout >    mxTextLayout;
                const CanvasSharedPtr                       mpCanvas;
                rendering::RenderState                      maState;
                double                                      mnLayoutWidth;
            };

            TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint&        rStartPoint,
                                              const OUString&            rString,
                                              sal_Int32                         nStartPos,
                                              sal_Int32                         nLen,
                                              const uno::Sequence< double >&    rOffsets,
                                              const uno::Sequence< sal_Bool >&  rKashidas,
                                              const CanvasSharedPtr&            rCanvas,
                                              const OutDevState&                rState ) :
                mpCanvas( rCanvas )
            {
                initLayoutWidth(mnLayoutWidth, rOffsets);

                initArrayAction( maState,
                                 mxTextLayout,
                                 rStartPoint,
                                 rString,
                                 nStartPos,
                                 nLen,
                                 rOffsets,
                                 rKashidas,
                                 rCanvas,
                                 rState, nullptr );
            }

            TextArrayAction::TextArrayAction( const ::basegfx::B2DPoint&        rStartPoint,
                                              const OUString&            rString,
                                              sal_Int32                         nStartPos,
                                              sal_Int32                         nLen,
                                              const uno::Sequence< double >&    rOffsets,
                                              const uno::Sequence< sal_Bool >&  rKashidas,
                                              const CanvasSharedPtr&            rCanvas,
                                              const OutDevState&                rState,
                                              const ::basegfx::B2DHomMatrix&    rTextTransform ) :
                mpCanvas( rCanvas )
            {
                initLayoutWidth(mnLayoutWidth, rOffsets);

                initArrayAction( maState,
                                 mxTextLayout,
                                 rStartPoint,
                                 rString,
                                 nStartPos,
                                 nLen,
                                 rOffsets,
                                 rKashidas,
                                 rCanvas,
                                 rState,
                                 &rTextTransform );
            }

            bool TextArrayAction::render( const ::basegfx::B2DHomMatrix& rTransformation ) const
            {
                SAL_INFO( "cppcanvas.emf""::cppcanvas::internal::TextArrayAction::render()" );
                SAL_INFO( "cppcanvas.emf""::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );

                rendering::RenderState aLocalState( maState );
                ::canvas::tools::prependToRenderState(aLocalState, rTransformation);

                mpCanvas->getUNOCanvas()->drawTextLayout( mxTextLayout,
                                                          mpCanvas->getViewState(),
                                                          aLocalState );

                return true;
            }

            bool TextArrayAction::renderSubset( const ::basegfx::B2DHomMatrix&    rTransformation,
                                                const Subset&                     rSubset ) const
            {
                SAL_INFO( "cppcanvas.emf""::cppcanvas::internal::TextArrayAction::renderSubset()" );
                SAL_INFO( "cppcanvas.emf""::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );

                rendering::RenderState                      aLocalState( maState );
                uno::Reference< rendering::XTextLayout >    xTextLayout( mxTextLayout );

                double nDummy0, nDummy1;
                createSubsetLayout( xTextLayout,
                                    mnLayoutWidth,
                                    aLocalState,
                                    nDummy0,
                                    nDummy1,
                                    rTransformation,
                                    rSubset );

                if( !xTextLayout.is() )
                    return true// empty layout, render nothing

                mpCanvas->getUNOCanvas()->drawTextLayout( xTextLayout,
                                                          mpCanvas->getViewState(),
                                                          aLocalState );

                return true;
            }

            ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const
            {
                rendering::RenderState aLocalState( maState );
                ::canvas::tools::prependToRenderState(aLocalState, rTransformation);

                return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
                                                         mxTextLayout->queryTextBounds() ),
                                                     mpCanvas->getViewState(),
                                                     aLocalState );
            }

            ::basegfx::B2DRange TextArrayAction::getBounds( const ::basegfx::B2DHomMatrix&  rTransformation,
                                                            const Subset&                   rSubset ) const
            {
                SAL_INFO( "cppcanvas.emf""::cppcanvas::internal::TextArrayAction::getBounds( subset )" );
                SAL_INFO( "cppcanvas.emf""::cppcanvas::internal::TextArrayAction: 0x" << std::hex << this );

                rendering::RenderState                      aLocalState( maState );
                uno::Reference< rendering::XTextLayout >    xTextLayout( mxTextLayout );

                double nDummy0, nDummy1;
                createSubsetLayout( xTextLayout,
                                    mnLayoutWidth,
                                    aLocalState,
                                    nDummy0,
                                    nDummy1,
                                    rTransformation,
                                    rSubset );

                if( !xTextLayout.is() )
                    return ::basegfx::B2DRange(); // empty layout, empty bounds

                return tools::calcDevicePixelBounds( ::basegfx::unotools::b2DRectangleFromRealRectangle2D(
                                                         xTextLayout->queryTextBounds() ),
                                                     mpCanvas->getViewState(),
                                                     aLocalState );
            }

            sal_Int32 TextArrayAction::getActionCount() const
            {
                const rendering::StringContext aOrigContext( mxTextLayout->getText() );

                return aOrigContext.Length;
            }


            class EffectTextArrayAction :
                public Action,
                public TextRenderer
            {
            public:
                EffectTextArrayAction( const ::basegfx::B2DPoint&       rStartPoint,
                                       const ::basegfx::B2DSize&        rReliefOffset,
                                       const ::Color&                   rReliefColor,
                                       const ::basegfx::B2DSize&        rShadowOffset,
                                       const ::Color&                   rShadowColor,
                                       const ::Color&                   rTextFillColor,
                                       const OUString&           rText,
                                       sal_Int32                        nStartPos,
                                       sal_Int32                        nLen,
                                       const uno::Sequence< double >&   rOffsets,
                                       const uno::Sequence< sal_Bool >& rKashidas,
                                       VirtualDevice const &            rVDev,
                                       const CanvasSharedPtr&           rCanvas,
                                       const OutDevState&               rState  );
                EffectTextArrayAction( const ::basegfx::B2DPoint&       rStartPoint,
                                       const ::basegfx::B2DSize&        rReliefOffset,
                                       const ::Color&                   rReliefColor,
                                       const ::basegfx::B2DSize&        rShadowOffset,
                                       const ::Color&                   rShadowColor,
                                       const ::Color&                   rTextFillColor,
                                       const OUString&           rText,
                                       sal_Int32                        nStartPos,
                                       sal_Int32                        nLen,
                                       const uno::Sequence< double >&   rOffsets,
                                       const uno::Sequence< sal_Bool >& rKashidas,
                                       VirtualDevice const &            rVDev,
                                       const CanvasSharedPtr&           rCanvas,
                                       const OutDevState&               rState,
                                       const ::basegfx::B2DHomMatrix&   rTextTransform );

                EffectTextArrayAction(const EffectTextArrayAction&) = delete;
                const EffectTextArrayAction& operator=(const EffectTextArrayAction&) = delete;

                virtual bool render( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
                virtual bool renderSubset( const ::basegfx::B2DHomMatrix& rTransformation,
                                           const Subset&                  rSubset ) const override;

                virtual ::basegfx::B2DRange getBounds( const ::basegfx::B2DHomMatrix& rTransformation ) const override;
--> --------------------

--> maximum size reached

--> --------------------

98%


¤ Dauer der Verarbeitung: 0.38 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge