Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/starmath/source/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 46 kB image not shown  

Quelle  document.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 <sal/config.h>

#include <com/sun/star/accessibility/AccessibleEventId.hpp>
#include <com/sun/star/uno/Any.h>

#include <comphelper/fileformat.h>
#include <comphelper/accessibletexthelper.hxx>
#include <comphelper/string.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <unotools/eventcfg.hxx>
#include <sfx2/event.hxx>
#include <sfx2/app.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/msg.hxx>
#include <sfx2/objface.hxx>
#include <sfx2/printer.hxx>
#include <sfx2/request.hxx>
#include <sfx2/viewfrm.hxx>
#include <comphelper/classids.hxx>
#include <sot/formats.hxx>
#include <sot/storage.hxx>
#include <svl/eitem.hxx>
#include <svl/intitem.hxx>
#include <svl/itempool.hxx>
#include <svl/slstitm.hxx>
#include <svl/hint.hxx>
#include <svl/stritem.hxx>
#include <svl/undo.hxx>
#include <svl/whiter.hxx>
#include <vcl/mapmod.hxx>
#include <vcl/virdev.hxx>
#include <tools/mapunit.hxx>
#include <vcl/settings.hxx>

#include <document.hxx>
#include <action.hxx>
#include <dialog.hxx>
#include <format.hxx>
#include <parse.hxx>
#include <starmath.hrc>
#include <strings.hrc>
#include <smmod.hxx>
#include <symbol.hxx>
#include <unomodel.hxx>
#include <utility.hxx>
#include <view.hxx>
#include "mathtype.hxx"
#include "ooxmlexport.hxx"
#include "ooxmlimport.hxx"
#include "rtfexport.hxx"
#include <mathmlimport.hxx>
#include <mathmlexport.hxx>
#include <svx/svxids.hrc>
#include <cursor.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <visitors.hxx>
#include "accessibility.hxx"
#include <cfgitem.hxx>
#include <utility>
#include <oox/mathml/imexport.hxx>
#include <ElementsDockingWindow.hxx>
#include <smediteng.hxx>
#include <editeng/editund2.hxx>

#define ShellClass_SmDocShell
#include <smslots.hxx>

using namespace ::com::sun::star;
using namespace ::com::sun::star::accessibility;
using namespace ::com::sun::star::uno;


SFX_IMPL_SUPERCLASS_INTERFACE(SmDocShell, SfxObjectShell)

void SmDocShell::InitInterface_Impl()
{
    GetStaticInterface()->RegisterPopupMenu(u"view"_ustr);
}

void SmDocShell::SetSmSyntaxVersion(sal_Int16 nSmSyntaxVersion)
{
    mnSmSyntaxVersion = nSmSyntaxVersion;
    maParser.reset(starmathdatabase::GetVersionSmParser(mnSmSyntaxVersion));
}

SFX_IMPL_OBJECTFACTORY(SmDocShell, SvGlobalName(SO3_SM_CLASSID), u"smath"_ustr )

void SmDocShell::Notify(SfxBroadcaster&, const SfxHint& rHint)
{
    if (rHint.GetId() == SfxHintId::MathFormatChanged)
    {
        SetFormulaArranged(false);

        mnModifyCount++;     //! see comment for SID_GRAPHIC_SM in SmDocShell::GetState

        Repaint();
    }
}

void SmDocShell::LoadSymbols()
{
    SmModule::get()->GetSymbolManager().Load();
}


OUString SmDocShell::GetComment() const
{
    uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
        GetModel(), uno::UNO_QUERY_THROW);
    uno::Reference<document::XDocumentProperties> xDocProps(
        xDPS->getDocumentProperties());
    return xDocProps->getDescription();
}


void SmDocShell::SetText(const OUString& rBuffer)
{
    if (rBuffer == maText)
        return;

    bool bIsEnabled = IsEnableSetModified();
    if( bIsEnabled )
        EnableSetModified( false );

    maText = rBuffer;
    SetFormulaArranged( false );

    Parse();

    SmViewShell *pViewSh = SmGetActiveView();
    if (pViewSh)
    {
        pViewSh->GetViewFrame().GetBindings().Invalidate(SID_TEXT);
        if ( SfxObjectCreateMode::EMBEDDED == GetCreateMode() )
        {
            // have SwOleClient::FormatChanged() to align the modified formula properly
            // even if the visible area does not change (e.g. when formula text changes from
            // "{a over b + c} over d" to "d over {a over b + c}"
            SfxGetpApp()->NotifyEvent(SfxEventHint( SfxEventHintId::VisAreaChanged, GlobalEventConfig::GetEventName(GlobalEventId::VISAREACHANGED), this));

            Repaint();
        }
        else
            pViewSh->GetGraphicWidget().Invalidate();
    }

    if ( bIsEnabled )
        EnableSetModified( bIsEnabled );
    SetModified();

    // launch accessible event if necessary
    SmGraphicAccessible *pAcc = pViewSh ? pViewSh->GetGraphicWidget().GetAccessible_Impl() : nullptr;
    if (pAcc)
    {
        Any aOldValue, aNewValue;
        if ( comphelper::OCommonAccessibleText::implInitTextChangedEvent( maText, rBuffer, aOldValue, aNewValue ) )
        {
            pAcc->LaunchEvent( AccessibleEventId::TEXT_CHANGED,
                    aOldValue, aNewValue );
        }
    }

    if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
        OnDocumentPrinterChanged(nullptr);
}

void SmDocShell::SetFormat(SmFormat const & rFormat)
{
    maFormat = rFormat;
    SetFormulaArranged( false );
    SetModified();

    mnModifyCount++;     //! see comment for SID_GRAPHIC_SM in SmDocShell::GetState

    // don't use SmGetActiveView since the view shell might not be active (0 pointer)
    // if for example the Basic Macro dialog currently has the focus. Thus:
    SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this );
    while (pFrm)
    {
        pFrm->GetBindings().Invalidate(SID_GRAPHIC_SM);
        pFrm = SfxViewFrame::GetNext( *pFrm, this );
    }
}

OUString const & SmDocShell::GetAccessibleText()
{
    ArrangeFormula();
    if (maAccText.isEmpty())
    {
        OSL_ENSURE( mpTree, "Tree missing" );
        if (mpTree)
        {
            OUStringBuffer aBuf;
            mpTree->GetAccessibleText(aBuf);
            maAccText = aBuf.makeStringAndClear();
        }
    }
    return maAccText;
}

void SmDocShell::Parse()
{
    mpTree.reset();
    ReplaceBadChars();
    mpTree = maParser->Parse(maText);
    mnModifyCount++;     //! see comment for SID_GRAPHIC_SM in SmDocShell::GetState
    SetFormulaArranged( false );
    InvalidateCursor();
    maUsedSymbols = maParser->GetUsedSymbols();
}


void SmDocShell::ArrangeFormula()
{
    if (mbFormulaArranged)
        return;

    // Only for the duration of the existence of this object the correct settings
    // at the printer are guaranteed!
    SmPrinterAccess  aPrtAcc(*this);
    OutputDevice* pOutDev = aPrtAcc.GetRefDev();

    SAL_WARN_IF( !pOutDev, "starmath""!! SmDocShell::ArrangeFormula: reference device missing !!");

    // if necessary get another OutputDevice for which we format
    if (!pOutDev)
    {
        if (SmViewShell *pView = SmGetActiveView())
            pOutDev = &pView->GetGraphicWidget().GetDrawingArea()->get_ref_device();
        else
        {
            pOutDev = &SmModule::get()->GetDefaultVirtualDev();
            pOutDev->SetMapMode( MapMode(SmMapUnit()) );
        }
    }
    OSL_ENSURE(pOutDev->GetMapMode().GetMapUnit() == SmMapUnit(),
               "Sm : wrong MapMode");

    const SmFormat &rFormat = GetFormat();
    mpTree->Prepare(rFormat, *this, 0);

    pOutDev->Push(vcl::PushFlags::TEXTLAYOUTMODE | vcl::PushFlags::TEXTLANGUAGE);

    // We want the device to always be LTR, we handle RTL formulas ourselves.
    bool bOldRTL = pOutDev->IsRTLEnabled();
    pOutDev->EnableRTL(false);

    // For RTL formulas, we want the brackets to be mirrored.
    bool bRTL = GetFormat().IsRightToLeft();
    pOutDev->SetLayoutMode(bRTL ? vcl::text::ComplexTextLayoutFlags::BiDiRtl
                                : vcl::text::ComplexTextLayoutFlags::Default);

    // Numbers should not be converted, for now.
    pOutDev->SetDigitLanguage( LANGUAGE_ENGLISH );

    mpTree->Arrange(*pOutDev, rFormat);

    pOutDev->EnableRTL(bOldRTL);
    pOutDev->Pop();

    SetFormulaArranged(true);

    // invalidate accessible text
    maAccText.clear();
}

void SmDocShell::UpdateEditEngineDefaultFonts()
{
    SmEditEngine::setSmItemPool(mpEditEngineItemPool.get(), maLinguOptions);
}

EditEngine& SmDocShell::GetEditEngine()
{
    if (!mpEditEngine)
    {
        //!
        //! see also SmEditWindow::DataChanged !
        //!
        mpEditEngineItemPool = EditEngine::CreatePool();
        SmEditEngine::setSmItemPool(mpEditEngineItemPool.get(), maLinguOptions);
        mpEditEngine.reset( new SmEditEngine( mpEditEngineItemPool.get() ) );
        mpEditEngine->EraseVirtualDevice();

        // set initial text if the document already has some...
        // (may be the case when reloading a doc)
        OUString aTxt( GetText() );
        if (!aTxt.isEmpty())
            mpEditEngine->SetText( aTxt );
        mpEditEngine->ClearModifyFlag();
    }
    return *mpEditEngine;
}


void SmDocShell::DrawFormula(OutputDevice &rDev, Point &rPosition, bool bDrawSelection)
{
    if (!mpTree)
        Parse();
    OSL_ENSURE(mpTree, "Sm : NULL pointer");

    ArrangeFormula();

    bool bRTL = GetFormat().IsRightToLeft();

    // Problem: What happens to WYSIWYG? While we're active inplace, we don't have a reference
    // device and aren't aligned to that either. So now there can be a difference between the
    // VisArea (i.e. the size within the client) and the current size.
    // Idea: The difference could be adapted with SmNod::SetSize (no long-term solution)

    rPosition.AdjustX(maFormat.GetDistance( DIS_LEFTSPACE ) );
    rPosition.AdjustY(maFormat.GetDistance( DIS_TOPSPACE  ) );

    Point aPosition(rPosition);
    if (bRTL && rDev.GetOutDevType() != OUTDEV_WINDOW)
        aPosition.AdjustX(GetSize().Width()
                          - maFormat.GetDistance(DIS_LEFTSPACE)
                          - maFormat.GetDistance(DIS_RIGHTSPACE));

    //! in case of high contrast-mode (accessibility option!)
    //! the draw mode needs to be set to default, because when embedding
    //! Math for example in Calc in "a over b" the fraction bar may not
    //! be visible else. More generally: the FillColor may have been changed.
    DrawModeFlags nOldDrawMode = DrawModeFlags::Default;
    bool bRestoreDrawMode = false;
    if (OUTDEV_WINDOW == rDev.GetOutDevType() &&
        rDev.GetOwnerWindow()->GetSettings().GetStyleSettings().GetHighContrastMode())
    {
        nOldDrawMode = rDev.GetDrawMode();
        rDev.SetDrawMode( DrawModeFlags::Default );
        bRestoreDrawMode = true;
    }

    rDev.Push(vcl::PushFlags::TEXTLAYOUTMODE | vcl::PushFlags::TEXTLANGUAGE);

    // We want the device to always be LTR, we handle RTL formulas ourselves.
    bool bOldRTL = rDev.IsRTLEnabled();
    if (rDev.GetOutDevType() == OUTDEV_WINDOW)
        rDev.EnableRTL(bRTL);
    else
        rDev.EnableRTL(false);

    auto nLayoutFlags = vcl::text::ComplexTextLayoutFlags::Default;
    if (bRTL)
    {
        // For RTL formulas, we want the brackets to be mirrored.
        nLayoutFlags |= vcl::text::ComplexTextLayoutFlags::BiDiRtl;
        if (rDev.GetOutDevType() == OUTDEV_WINDOW)
            nLayoutFlags |= vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
    }

    rDev.SetLayoutMode(nLayoutFlags);

    // Numbers should not be converted, for now.
    rDev.SetDigitLanguage( LANGUAGE_ENGLISH );

    //Set selection if any
    if(mpCursor && bDrawSelection){
        mpCursor->AnnotateSelection();
        SmSelectionDrawingVisitor(rDev, mpTree.get(), aPosition);
    }

    //Drawing using visitor
    SmDrawingVisitor(rDev, aPosition, mpTree.get(), GetFormat());

    rDev.EnableRTL(bOldRTL);
    rDev.Pop();

    if (bRestoreDrawMode)
        rDev.SetDrawMode( nOldDrawMode );
}

Size SmDocShell::GetSize()
{
    Size aRet;

    if (!mpTree)
        Parse();

    if (mpTree)
    {
        ArrangeFormula();
        aRet = mpTree->GetSize();

        if ( !aRet.Width() || aRet.Width() == 1 )
            aRet.setWidth( 2000 );
        else
            aRet.AdjustWidth(maFormat.GetDistance( DIS_LEFTSPACE ) +
                             maFormat.GetDistance( DIS_RIGHTSPACE ) );
        if ( !aRet.Height() )
            aRet.setHeight( 1000 );
        else
            aRet.AdjustHeight(maFormat.GetDistance( DIS_TOPSPACE ) +
                             maFormat.GetDistance( DIS_BOTTOMSPACE ) );
    }

    return aRet;
}

void SmDocShell::InvalidateCursor(){
    mpCursor.reset();
}

SmCursor& SmDocShell::GetCursor(){
    if(!mpCursor)
        mpCursor.reset(new SmCursor(mpTree.get(), this));
    return *mpCursor;
}

bool SmDocShell::HasCursor() const { return mpCursor != nullptr; }

SmPrinterAccess::SmPrinterAccess( SmDocShell &rDocShell )
{
    pPrinter = rDocShell.GetPrt();
    if ( pPrinter )
    {
        pPrinter->Push( vcl::PushFlags::MAPMODE );
        if ( SfxObjectCreateMode::EMBEDDED == rDocShell.GetCreateMode() )
        {
            // if it is an embedded object (without its own printer)
            // we change the MapMode temporarily.
            //!If it is a document with its own printer the MapMode should
            //!be set correct (once) elsewhere(!), in order to avoid numerous
            //!superfluous pushing and popping of the MapMode when using
            //!this class.

            const MapUnit eOld = pPrinter->GetMapMode().GetMapUnit();
            if ( SmMapUnit() != eOld )
            {
                MapMode aMap( pPrinter->GetMapMode() );
                aMap.SetMapUnit( SmMapUnit() );
                Point aTmp( aMap.GetOrigin() );
                aTmp.setX( OutputDevice::LogicToLogic( aTmp.X(), eOld, SmMapUnit() ) );
                aTmp.setY( OutputDevice::LogicToLogic( aTmp.Y(), eOld, SmMapUnit() ) );
                aMap.SetOrigin( aTmp );
                pPrinter->SetMapMode( aMap );
            }
        }
    }
    pRefDev = rDocShell.GetRefDev();
    if ( !pRefDev || pPrinter.get() == pRefDev.get() )
        return;

    pRefDev->Push( vcl::PushFlags::MAPMODE );
    if ( SfxObjectCreateMode::EMBEDDED != rDocShell.GetCreateMode() )
        return;

    // if it is an embedded object (without its own printer)
    // we change the MapMode temporarily.
    //!If it is a document with its own printer the MapMode should
    //!be set correct (once) elsewhere(!), in order to avoid numerous
    //!superfluous pushing and popping of the MapMode when using
    //!this class.

    const MapUnit eOld = pRefDev->GetMapMode().GetMapUnit();
    if ( SmMapUnit() != eOld )
    {
        MapMode aMap( pRefDev->GetMapMode() );
        aMap.SetMapUnit( SmMapUnit() );
        Point aTmp( aMap.GetOrigin() );
        aTmp.setX( OutputDevice::LogicToLogic( aTmp.X(), eOld, SmMapUnit() ) );
        aTmp.setY( OutputDevice::LogicToLogic( aTmp.Y(), eOld, SmMapUnit() ) );
        aMap.SetOrigin( aTmp );
        pRefDev->SetMapMode( aMap );
    }
}

SmPrinterAccess::~SmPrinterAccess()
{
    if ( pPrinter )
        pPrinter->Pop();
    if ( pRefDev && pRefDev != pPrinter )
        pRefDev->Pop();
}

Printer* SmDocShell::GetPrt()
{
    if (SfxObjectCreateMode::EMBEDDED == GetCreateMode())
    {
        // Normally the server provides the printer. But if it doesn't provide one (e.g. because
        // there is no connection) it still can be the case that we know the printer because it
        // has been passed on by the server in OnDocumentPrinterChanged and being kept temporarily.
        Printer* pPrt = GetDocumentPrinter();
        if (!pPrt && mpTmpPrinter)
            pPrt = mpTmpPrinter;
        return pPrt;
    }
    else if (!mpPrinter)
    {
        auto pOptions = std::make_unique<SfxItemSetFixed<
                SID_PRINTTITLE, SID_PRINTZOOM,
                SID_NO_RIGHT_SPACES, SID_SAVE_ONLY_USED_SYMBOLS,
                SID_AUTO_CLOSE_BRACKETS, SID_SMEDITWINDOWZOOM,
                SID_INLINE_EDIT_ENABLE, SID_INLINE_EDIT_ENABLE>>(GetPool());
        SmModule::get()->GetConfig()->ConfigToItemSet(*pOptions);
        mpPrinter = VclPtr<SfxPrinter>::Create(std::move(pOptions));
        mpPrinter->SetMapMode(MapMode(SmMapUnit()));
    }
    return mpPrinter;
}

OutputDevice* SmDocShell::GetRefDev()
{
    if (SfxObjectCreateMode::EMBEDDED == GetCreateMode())
    {
        OutputDevice* pOutDev = GetDocumentRefDev();
        if (pOutDev)
            return pOutDev;
    }

    return GetPrt();
}

void SmDocShell::SetPrinter( SfxPrinter *pNew )
{
    mpPrinter.disposeAndClear();
    mpPrinter = pNew;    //Transfer ownership
    mpPrinter->SetMapMode( MapMode(SmMapUnit()) );
    SetFormulaArranged(false);
    Repaint();
}

void SmDocShell::OnDocumentPrinterChanged( Printer *pPrt )
{
    mpTmpPrinter = pPrt;
    SetFormulaArranged(false);
    Size aOldSize = GetVisArea().GetSize();
    Repaint();
    if( aOldSize != GetVisArea().GetSize() && !maText.isEmpty() )
        SetModified();
    mpTmpPrinter = nullptr;
}

void SmDocShell::Repaint()
{
    bool bIsEnabled = IsEnableSetModified();
    if (bIsEnabled)
        EnableSetModified( false );

    SetFormulaArranged(false);

    Size aVisSize = GetSize();
    SetVisAreaSize(aVisSize);
    if (SmViewShell* pViewSh = SmGetActiveView())
        pViewSh->GetGraphicWidget().Invalidate();

    if (bIsEnabled)
        EnableSetModified(bIsEnabled);
}

SmDocShell::SmDocShell( SfxModelFlags i_nSfxCreationFlags )
    : SfxObjectShell(i_nSfxCreationFlags)
    , m_pMlElementTree(nullptr)
    , mpPrinter(nullptr)
    , mpTmpPrinter(nullptr)
    , mnModifyCount(0)
    , mbFormulaArranged(false)
{
    SvtLinguConfig().GetOptions(maLinguOptions);

    SetPool(&SfxGetpApp()->GetPool());

    auto* config = SmModule::get()->GetConfig();
    mnSmSyntaxVersion = config->GetDefaultSmSyntaxVersion();
    maFormat = config->GetStandardFormat();

    StartListening(maFormat);
    StartListening(*config);

    SetBaseModel(new SmModel(this));
    SetSmSyntaxVersion(mnSmSyntaxVersion);

    SetMapUnit(SmMapUnit());
}

SmDocShell::~SmDocShell()
{
    EndListening(maFormat);
    EndListening(*SmModule::get()->GetConfig());

    mpCursor.reset();
    mpEditEngine.reset();
    mpEditEngineItemPool.clear();
    mpPrinter.disposeAndClear();

    mathml::SmMlIteratorFree(m_pMlElementTree);
}

bool SmDocShell::ConvertFrom(SfxMedium &rMedium)
{
    bool     bSuccess = false;
    const OUString& rFltName = rMedium.GetFilter()->GetFilterName();

    OSL_ENSURE( rFltName != STAROFFICE_XML, "Wrong filter!");

    if ( rFltName == MATHML_XML )
    {
        if (mpTree)
        {
            mpTree.reset();
            InvalidateCursor();
        }
        rtl::Reference<SmModel> xModel(dynamic_cast<SmModel*>(GetModel().get()));
        SmXMLImportWrapper aEquation(xModel);
        aEquation.useHTMLMLEntities(true);
        bSuccess = ( ERRCODE_NONE == aEquation.Import(rMedium) );
    }
    else
    {
        SvStream *pStream = rMedium.GetInStream();
        if ( pStream )
        {
            if ( SotStorage::IsStorageFile( pStream ) )
            {
                rtl::Reference<SotStorage> aStorage = new SotStorage(pStream, false);
                if ( aStorage->IsStream(u"Equation Native"_ustr) )
                {
                    // is this a MathType Storage?
                    OUStringBuffer aBuffer;
                    MathType aEquation(aBuffer);
                    bSuccess = aEquation.Parse( aStorage.get() );
                    if ( bSuccess )
                    {
                        maText = aBuffer.makeStringAndClear();
                        Parse();
                    }
                }
            }
        }
    }

    if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
    {
        SetFormulaArranged( false );
        Repaint();
    }

    FinishedLoading();
    return bSuccess;
}


bool SmDocShell::InitNew( const uno::Reference < embed::XStorage >& xStorage )
{
    bool bRet = false;
    if ( SfxObjectShell::InitNew( xStorage ) )
    {
        bRet = true;
        SetVisArea(tools::Rectangle(Point(0, 0), Size(2000, 1000)));
    }
    return bRet;
}


bool SmDocShell::Load( SfxMedium& rMedium )
{
    bool bRet = false;
    if( SfxObjectShell::Load( rMedium ))
    {
        uno::Reference < embed::XStorage > xStorage = GetMedium()->GetStorage();
        if (xStorage->hasByName(u"content.xml"_ustr) && xStorage->isStreamElement(u"content.xml"_ustr))
        {
            // is this a fabulous math package ?
            rtl::Reference<SmModel> xModel(dynamic_cast<SmModel*>(GetModel().get()));
            SmXMLImportWrapper aEquation(xModel);
            auto nError = aEquation.Import(rMedium);
            bRet = ERRCODE_NONE == nError;
            SetError(nError);
        }
    }

    if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
    {
        SetFormulaArranged( false );
        Repaint();
    }

    FinishedLoading();
    return bRet;
}


bool SmDocShell::Save()
{
    //! apply latest changes if necessary
    UpdateText();

    if ( SfxObjectShell::Save() )
    {
        if (!mpTree)
            Parse();
        if( mpTree )
            ArrangeFormula();

        SmXMLExportWrapper aEquation(GetModel());
        aEquation.SetFlat(false);
        return aEquation.Export(*GetMedium());
    }

    return false;
}

/*
 * replace bad characters that can not be saved. (#i74144)
 * */

void SmDocShell::ReplaceBadChars()
{
    bool bReplace = false;

    if (!mpEditEngine)
        return;

    OUStringBuffer aBuf( mpEditEngine->GetText() );

    for (sal_Int32 i = 0;  i < aBuf.getLength();  ++i)
    {
        if (aBuf[i] < ' ' && aBuf[i] != '\r' && aBuf[i] != '\n' && aBuf[i] != '\t')
        {
            aBuf[i] = ' ';
            bReplace = true;
        }
    }

    if (bReplace)
        maText = aBuf.makeStringAndClear();
}


void SmDocShell::UpdateText()
{
    if (mpEditEngine && mpEditEngine->IsModified())
    {
        OUString aEngTxt( mpEditEngine->GetText() );
        if (GetText() != aEngTxt)
            SetText( aEngTxt );
    }
}


bool SmDocShell::SaveAs( SfxMedium& rMedium )
{
    bool bRet = false;

    //! apply latest changes if necessary
    UpdateText();

    if ( SfxObjectShell::SaveAs( rMedium ) )
    {
        if (!mpTree)
            Parse();
        if( mpTree )
            ArrangeFormula();

        SmXMLExportWrapper aEquation(GetModel());
        aEquation.SetFlat(false);
        bRet = aEquation.Export(rMedium);
    }
    return bRet;
}

bool SmDocShell::ConvertTo( SfxMedium &rMedium )
{
    bool bRet = false;
    std::shared_ptr<const SfxFilter> pFlt = rMedium.GetFilter();
    if( pFlt )
    {
        if( !mpTree )
            Parse();
        if( mpTree )
            ArrangeFormula();

        const OUString& rFltName = pFlt->GetFilterName();
        if(rFltName == STAROFFICE_XML)
        {
            SmXMLExportWrapper aEquation(GetModel());
            aEquation.SetFlat(false);
            bRet = aEquation.Export(rMedium);
        }
        else if(rFltName == MATHML_XML)
        {
            SmXMLExportWrapper aEquation(GetModel());
            aEquation.SetFlat(true);
            aEquation.SetUseHTMLMLEntities(true);
            bRet = aEquation.Export(rMedium);
        }
        else if (pFlt->GetFilterName() == "MathType 3.x")
            bRet = WriteAsMathType3( rMedium );
    }
    return bRet;
}

void SmDocShell::writeFormulaOoxml(
        ::sax_fastparser::FSHelperPtr const& pSerializer,
        oox::core::OoxmlVersion const version,
        oox::drawingml::DocumentType const documentType,
        const sal_Int8 nAlign)
{
    if( !mpTree )
        Parse();
    if( mpTree )
        ArrangeFormula();
    SmOoxmlExport aEquation(mpTree.get(), version, documentType);
    if(documentType == oox::drawingml::DOCUMENT_DOCX)
        aEquation.ConvertFromStarMath( pSerializer, nAlign);
    else
        aEquation.ConvertFromStarMath(pSerializer, oox::FormulaImExportBase::eFormulaAlign::INLINE);
}

void SmDocShell::writeFormulaRtf(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding)
{
    if (!mpTree)
        Parse();
    if (mpTree)
        ArrangeFormula();
    SmRtfExport aEquation(mpTree.get());
    aEquation.ConvertFromStarMath(rBuffer, nEncoding);
}

void SmDocShell::readFormulaOoxml( oox::formulaimport::XmlStream& stream )
{
    SmOoxmlImport aEquation( stream );
    SetText( aEquation.ConvertToStarMath());
}

void SmDocShell::Execute(SfxRequest& rReq)
{
    switch (rReq.GetSlot())
    {
        case SID_TEXTMODE:
        {
            SmFormat aOldFormat  = GetFormat();
            SmFormat aNewFormat( aOldFormat );
            aNewFormat.SetTextmode(!aOldFormat.IsTextmode());

            SfxUndoManager *pTmpUndoMgr = GetUndoManager();
            if (pTmpUndoMgr)
                pTmpUndoMgr->AddUndoAction(
                    std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));

            SetFormat( aNewFormat );
            Repaint();
        }
        break;

        case SID_AUTO_REDRAW :
        {
            auto* config = SmModule::get()->GetConfig();
            config->SetAutoRedraw(!config->IsAutoRedraw());
        }
        break;

        case SID_LOADSYMBOLS:
            LoadSymbols();
        break;

        case SID_SAVESYMBOLS:
            SaveSymbols();
        break;

        case SID_FONT:
        {
            // get device used to retrieve the FontList
            OutputDevice *pDev = GetPrinter();
            if (!pDev || pDev->GetFontFaceCollectionCount() == 0)
                pDev = &SmModule::get()->GetDefaultVirtualDev();
            OSL_ENSURE (pDev, "device for font list missing" );

            SmFontTypeDialog aFontTypeDialog(rReq.GetFrameWeld(), pDev);

            SmFormat aOldFormat  = GetFormat();
            aFontTypeDialog.ReadFrom( aOldFormat );
            if (aFontTypeDialog.run() == RET_OK)
            {
                SmFormat aNewFormat( aOldFormat );

                aFontTypeDialog.WriteTo(aNewFormat);
                SfxUndoManager *pTmpUndoMgr = GetUndoManager();
                if (pTmpUndoMgr)
                    pTmpUndoMgr->AddUndoAction(
                        std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));

                SetFormat( aNewFormat );
                Repaint();
            }
        }
        break;

        case SID_FONTSIZE:
        {
            SmFontSizeDialog aFontSizeDialog(rReq.GetFrameWeld());

            SmFormat aOldFormat  = GetFormat();
            aFontSizeDialog.ReadFrom( aOldFormat );
            if (aFontSizeDialog.run() == RET_OK)
            {
                SmFormat aNewFormat( aOldFormat );

                aFontSizeDialog.WriteTo(aNewFormat);

                SfxUndoManager *pTmpUndoMgr = GetUndoManager();
                if (pTmpUndoMgr)
                    pTmpUndoMgr->AddUndoAction(
                        std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));

                SetFormat( aNewFormat );
                Repaint();
            }
        }
        break;

        case SID_DISTANCE:
        {
            SmDistanceDialog aDistanceDialog(rReq.GetFrameWeld());

            SmFormat aOldFormat  = GetFormat();
            aDistanceDialog.ReadFrom( aOldFormat );
            if (aDistanceDialog.run() == RET_OK)
            {
                SmFormat aNewFormat( aOldFormat );

                aDistanceDialog.WriteTo(aNewFormat);

                SfxUndoManager *pTmpUndoMgr = GetUndoManager();
                if (pTmpUndoMgr)
                    pTmpUndoMgr->AddUndoAction(
                        std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));

                SetFormat( aNewFormat );
                Repaint();
            }
        }
        break;

        case SID_ALIGN:
        {
            SmAlignDialog aAlignDialog(rReq.GetFrameWeld());

            SmFormat aOldFormat  = GetFormat();
            aAlignDialog.ReadFrom( aOldFormat );
            if (aAlignDialog.run() == RET_OK)
            {
                SmFormat aNewFormat( aOldFormat );

                aAlignDialog.WriteTo(aNewFormat);

                auto* config = SmModule::get()->GetConfig();
                SmFormat aFmt(config->GetStandardFormat());
                aAlignDialog.WriteTo( aFmt );
                config->SetStandardFormat(aFmt);

                SfxUndoManager *pTmpUndoMgr = GetUndoManager();
                if (pTmpUndoMgr)
                    pTmpUndoMgr->AddUndoAction(
                        std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));

                SetFormat( aNewFormat );
                Repaint();
            }
        }
        break;

        case SID_TEXT:
        {
            const SfxStringItem& rItem = rReq.GetArgs()->Get(SID_TEXT);
            if (GetText() != rItem.GetValue())
                SetText(rItem.GetValue());
        }
        break;

        case SID_UNDO:
        case SID_REDO:
        {
            SfxUndoManager* pTmpUndoMgr = GetUndoManager();
            if( pTmpUndoMgr )
            {
                sal_uInt16 nId = rReq.GetSlot(), nCnt = 1;
                const SfxItemSet* pArgs = rReq.GetArgs();
                const SfxPoolItem* pItem;
                if( pArgs && SfxItemState::SET == pArgs->GetItemState( nId, false, &pItem ))
                    nCnt = static_cast<const SfxUInt16Item*>(pItem)->GetValue();

                bool (SfxUndoManager::*fnDo)();

                size_t nCount;
                if( SID_UNDO == rReq.GetSlot() )
                {
                    nCount = pTmpUndoMgr->GetUndoActionCount();
                    fnDo = &SfxUndoManager::Undo;
                }
                else
                {
                    nCount = pTmpUndoMgr->GetRedoActionCount();
                    fnDo = &SfxUndoManager::Redo;
                }

                try
                {
                    for( ; nCnt && nCount; --nCnt, --nCount )
                        (pTmpUndoMgr->*fnDo)();
                }
                catchconst Exception& )
                {
                    DBG_UNHANDLED_EXCEPTION("starmath");
                }
            }
            Repaint();
            UpdateText();
            SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this );
            while( pFrm )
            {
                SfxBindings& rBind = pFrm->GetBindings();
                rBind.Invalidate(SID_UNDO);
                rBind.Invalidate(SID_REDO);
                rBind.Invalidate(SID_REPEAT);
                rBind.Invalidate(SID_CLEARHISTORY);
                pFrm = SfxViewFrame::GetNext( *pFrm, this );
            }
        }
        break;
    }

    rReq.Done();
}


void SmDocShell::GetState(SfxItemSet &rSet)
{
    SfxWhichIter aIter(rSet);

    for (sal_uInt16 nWh = aIter.FirstWhich();  0 != nWh;  nWh = aIter.NextWhich())
    {
        switch (nWh)
        {
        case SID_TEXTMODE:
            rSet.Put(SfxBoolItem(SID_TEXTMODE, GetFormat().IsTextmode()));
            break;

        case SID_DOCTEMPLATE :
            rSet.DisableItem(SID_DOCTEMPLATE);
            break;

        case SID_AUTO_REDRAW :
            rSet.Put(SfxBoolItem(SID_AUTO_REDRAW, SmModule::get()->GetConfig()->IsAutoRedraw()));
            break;

        case SID_MODIFYSTATUS:
            {
                sal_Unicode cMod = ' ';
                if (IsModified())
                    cMod = '*';
                rSet.Put(SfxStringItem(SID_MODIFYSTATUS, OUString(cMod)));
            }
            break;

        case SID_TEXT:
            rSet.Put(SfxStringItem(SID_TEXT, GetText()));
            break;

        case SID_GRAPHIC_SM:
            //! very old (pre UNO) and ugly hack to invalidate the SmGraphicWidget.
            //! If mnModifyCount gets changed then the call below will implicitly notify
            //! SmGraphicController::StateChanged and there the window gets invalidated.
            //! Thus all the 'mnModifyCount++' before invalidating this slot.
            rSet.Put(SfxInt16Item(SID_GRAPHIC_SM, mnModifyCount));
            break;

        case SID_UNDO:
        case SID_REDO:
            {
                SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this );
                if( pFrm )
                    pFrm->GetSlotState( nWh, nullptr, &rSet );
                else
                    rSet.DisableItem( nWh );
            }
            break;

        case SID_GETUNDOSTRINGS:
        case SID_GETREDOSTRINGS:
            {
                SfxUndoManager* pTmpUndoMgr = GetUndoManager();
                if( pTmpUndoMgr )
                {
                    OUString(SfxUndoManager::*fnGetComment)( size_t, bool const ) const;

                    size_t nCount;
                    if( SID_GETUNDOSTRINGS == nWh )
                    {
                        nCount = pTmpUndoMgr->GetUndoActionCount();
                        fnGetComment = &SfxUndoManager::GetUndoActionComment;
                    }
                    else
                    {
                        nCount = pTmpUndoMgr->GetRedoActionCount();
                        fnGetComment = &SfxUndoManager::GetRedoActionComment;
                    }
                    if (nCount)
                    {
                        OUStringBuffer aBuf;
                        for (size_t n = 0; n < nCount; ++n)
                        {
                            aBuf.append((pTmpUndoMgr->*fnGetComment)( n, SfxUndoManager::TopLevel ));
                            aBuf.append('\n');
                        }

                        SfxStringListItem aItem( nWh );
                        aItem.SetString( aBuf.makeStringAndClear() );
                        rSet.Put( aItem );
                    }
                }
                else
                    rSet.DisableItem( nWh );
            }
            break;
        }
    }
}


SfxUndoManager *SmDocShell::GetUndoManager()
{
    if (!mpEditEngine)
        GetEditEngine();
    return &mpEditEngine->GetUndoManager();
}


void SmDocShell::SaveSymbols()
{
    SmModule::get()->GetSymbolManager().Save();
}


void SmDocShell::Draw(OutputDevice *pDevice,
                      const JobSetup &,
                      sal_uInt16 /*nAspect*/,
                      bool /*bOutputForScreen*/)
{
    pDevice->IntersectClipRegion(GetVisArea());
    Point atmppoint;
    DrawFormula(*pDevice, atmppoint);
}

SfxItemPool& SmDocShell::GetPool()
{
    return SfxGetpApp()->GetPool();
}

void SmDocShell::SetVisArea(const tools::Rectangle & rVisArea)
{
    tools::Rectangle aNewRect(rVisArea);

    aNewRect.SetPos(Point());

    if (aNewRect.IsWidthEmpty())
        aNewRect.SetRight( 2000 );
    if (aNewRect.IsHeightEmpty())
        aNewRect.SetBottom( 1000 );

    bool bIsEnabled = IsEnableSetModified();
    if ( bIsEnabled )
        EnableSetModified( false );

    //TODO/LATER: it's unclear how this interacts with the SFX code
    // If outplace editing, then don't resize the OutplaceWindow. But the
    // ObjectShell has to resize.
    bool bUnLockFrame;
    if( GetCreateMode() == SfxObjectCreateMode::EMBEDDED && !IsInPlaceActive() && GetFrame() )
    {
        GetFrame()->LockAdjustPosSizePixel();
        bUnLockFrame = true;
    }
    else
        bUnLockFrame = false;

    SfxObjectShell::SetVisArea( aNewRect );

    if( bUnLockFrame )
        GetFrame()->UnlockAdjustPosSizePixel();

    if ( bIsEnabled )
        EnableSetModified( bIsEnabled );
}


void SmDocShell::FillClass(SvGlobalName* pClassName,
                           SotClipboardFormatId*  pFormat,
                           OUString* pFullTypeName,
                           sal_Int32 nFileFormat,
                           bool bTemplate /* = false */) const
{
    if (nFileFormat == SOFFICE_FILEFORMAT_60 )
    {
        *pClassName     = SvGlobalName(SO3_SM_CLASSID_60);
        *pFormat        = SotClipboardFormatId::STARMATH_60;
        *pFullTypeName  = SmResId(STR_MATH_DOCUMENT_FULLTYPE_CURRENT);
    }
    else if (nFileFormat == SOFFICE_FILEFORMAT_8 )
    {
        *pClassName     = SvGlobalName(SO3_SM_CLASSID_60);
        *pFormat        = bTemplate ? SotClipboardFormatId::STARMATH_8_TEMPLATE : SotClipboardFormatId::STARMATH_8;
        *pFullTypeName  = SmResId(STR_MATH_DOCUMENT_FULLTYPE_CURRENT);
    }
}

void SmDocShell::SetModified(bool bModified)
{
    if( IsEnableSetModified() )
    {
        SfxObjectShell::SetModified( bModified );
        Broadcast(SfxHint(SfxHintId::DocChanged));
    }
}

bool SmDocShell::WriteAsMathType3( SfxMedium& rMedium )
{
    OUStringBuffer aTextAsBuffer(maText);
    MathType aEquation(aTextAsBuffer, mpTree.get());
    return aEquation.ConvertFromStarMath( rMedium );
}

void SmDocShell::SetRightToLeft(bool bRTL)
{
    SmFormat aOldFormat = GetFormat();
    if (aOldFormat.IsRightToLeft() == bRTL)
        return;

    SmFormat aNewFormat(aOldFormat);
    aNewFormat.SetRightToLeft(bRTL);

    SfxUndoManager* pTmpUndoMgr = GetUndoManager();
    if (pTmpUndoMgr)
        pTmpUndoMgr->AddUndoAction(
            std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));

    SetFormat(aNewFormat);
    Repaint();
}

static Size GetTextLineSize(OutputDevice const& rDevice, const OUString& rLine)
{
    Size aSize(rDevice.GetTextWidth(rLine), rDevice.GetTextHeight());
    const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;

    if (nTabPos)
    {
        aSize.setWidth(0);
        sal_Int32 nPos = 0;
        do
        {
            if (nPos > 0)
                aSize.setWidth(((aSize.Width() / nTabPos) + 1) * nTabPos);

            const OUString aText = rLine.getToken(0, '\t', nPos);
            aSize.AdjustWidth(rDevice.GetTextWidth(aText));
        } while (nPos >= 0);
    }

    return aSize;
}

static Size GetTextSize(OutputDevice const& rDevice, std::u16string_view rText,
                        tools::Long MaxWidth)
{
    Size aSize;
    Size aTextSize;
    if (rText.empty())
        return aTextSize;

    sal_Int32 nPos = 0;
    do
    {
        OUString aLine(o3tl::getToken(rText, 0, '\n', nPos));
        aLine = aLine.replaceAll("\r""");

        aSize = GetTextLineSize(rDevice, aLine);

        if (aSize.Width() > MaxWidth)
        {
            do
            {
                OUString aText;
                sal_Int32 m = aLine.getLength();
                sal_Int32 nLen = m;

                for (sal_Int32 n = 0; n < nLen; n++)
                {
                    sal_Unicode cLineChar = aLine[n];
                    if ((cLineChar == ' ') || (cLineChar == '\t'))
                    {
                        aText = aLine.copy(0, n);
                        if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
                            m = n;
                        else
                            break;
                    }
                }

                aText = aLine.copy(0, m);
                aLine = aLine.replaceAt(0, m, u"");
                aSize = GetTextLineSize(rDevice, aText);
                aTextSize.AdjustHeight(aSize.Height());
                aTextSize.setWidth(std::clamp(aSize.Width(), aTextSize.Width(), MaxWidth));

                aLine = comphelper::string::stripStart(aLine, ' ');
                aLine = comphelper::string::stripStart(aLine, '\t');
                aLine = comphelper::string::stripStart(aLine, ' ');
            } while (!aLine.isEmpty());
        }
        else
        {
            aTextSize.AdjustHeight(aSize.Height());
            aTextSize.setWidth(std::max(aTextSize.Width(), aSize.Width()));
        }
    } while (nPos >= 0);

    return aTextSize;
}

static void DrawTextLine(OutputDevice& rDevice, const Point& rPosition, const OUString& rLine)
{
    Point aPoint(rPosition);
    const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;

    if (nTabPos)
    {
        sal_Int32 nPos = 0;
        do
        {
            if (nPos > 0)
                aPoint.setX(((aPoint.X() / nTabPos) + 1) * nTabPos);

            OUString aText = rLine.getToken(0, '\t', nPos);
            rDevice.DrawText(aPoint, aText);
            aPoint.AdjustX(rDevice.GetTextWidth(aText));
        } while (nPos >= 0);
    }
    else
        rDevice.DrawText(aPoint, rLine);
}

static void DrawText(OutputDevice& rDevice, const Point& rPosition, std::u16string_view rText,
                     sal_uInt16 MaxWidth)
{
    if (rText.empty())
        return;

    Point aPoint(rPosition);
    Size aSize;

    sal_Int32 nPos = 0;
    do
    {
        OUString aLine(o3tl::getToken(rText, 0, '\n', nPos));
        aLine = aLine.replaceAll("\r""");
        aSize = GetTextLineSize(rDevice, aLine);
        if (aSize.Width() > MaxWidth)
        {
            do
            {
                OUString aText;
                sal_Int32 m = aLine.getLength();
                sal_Int32 nLen = m;

                for (sal_Int32 n = 0; n < nLen; n++)
                {
                    sal_Unicode cLineChar = aLine[n];
                    if ((cLineChar == ' ') || (cLineChar == '\t'))
                    {
                        aText = aLine.copy(0, n);
                        if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
                            m = n;
                        else
                            break;
                    }
                }
                aText = aLine.copy(0, m);
                aLine = aLine.replaceAt(0, m, u"");

                DrawTextLine(rDevice, aPoint, aText);
                aPoint.AdjustY(aSize.Height());

                aLine = comphelper::string::stripStart(aLine, ' ');
                aLine = comphelper::string::stripStart(aLine, '\t');
                aLine = comphelper::string::stripStart(aLine, ' ');
            } while (GetTextLineSize(rDevice, aLine).Width() > MaxWidth);

            // print the remaining text
            if (!aLine.isEmpty())
            {
                DrawTextLine(rDevice, aPoint, aLine);
                aPoint.AdjustY(aSize.Height());
            }
        }
        else
        {
            DrawTextLine(rDevice, aPoint, aLine);
            aPoint.AdjustY(aSize.Height());
        }
    } while (nPos >= 0);
}

void SmDocShell::Impl_Print(OutputDevice& rOutDev, const SmPrintUIOptions& rPrintUIOptions,
                tools::Rectangle aOutRect)
{
    const bool bIsPrintTitle = rPrintUIOptions.getBoolValue(PRTUIOPT_TITLE_ROW, true);
    const bool bIsPrintFrame = rPrintUIOptions.getBoolValue(PRTUIOPT_BORDER, true);
    const bool bIsPrintFormulaText = rPrintUIOptions.getBoolValue(PRTUIOPT_FORMULA_TEXT, true);
    SmPrintSize ePrintSize(static_cast<SmPrintSize>(
        rPrintUIOptions.getIntValue(PRTUIOPT_PRINT_FORMAT, PRINT_SIZE_NORMAL)));
    const sal_uInt16 nZoomFactor
        = static_cast<sal_uInt16>(rPrintUIOptions.getIntValue(PRTUIOPT_PRINT_SCALE, 100));

    rOutDev.Push();
    rOutDev.SetLineColor(COL_BLACK);

    // output text on top
    if (bIsPrintTitle)
    {
        Size aSize600(0, 600);
        Size aSize650(0, 650);
        vcl::Font aFont(FAMILY_DONTKNOW, aSize600);

        aFont.SetAlignment(ALIGN_TOP);
        aFont.SetWeight(WEIGHT_BOLD);
        aFont.SetFontSize(aSize650);
        aFont.SetColor(COL_BLACK);
        rOutDev.SetFont(aFont);

        Size aTitleSize(GetTextSize(rOutDev, GetTitle(), aOutRect.GetWidth() - 200));

        aFont.SetWeight(WEIGHT_NORMAL);
        aFont.SetFontSize(aSize600);
        rOutDev.SetFont(aFont);

        Size aDescSize(GetTextSize(rOutDev, GetComment(), aOutRect.GetWidth() - 200));

        if (bIsPrintFrame)
            rOutDev.DrawRect(tools::Rectangle(
                aOutRect.TopLeft(), Size(aOutRect.GetWidth(), 100 + aTitleSize.Height() + 200
                                                                  + aDescSize.Height() + 100)));
        aOutRect.AdjustTop(200);

        // output title
        aFont.SetWeight(WEIGHT_BOLD);
        aFont.SetFontSize(aSize650);
        rOutDev.SetFont(aFont);
        Point aPoint(aOutRect.Left() + (aOutRect.GetWidth() - aTitleSize.Width()) / 2,
                     aOutRect.Top());
        DrawText(rOutDev, aPoint, GetTitle(),
                 sal::static_int_cast<sal_uInt16>(aOutRect.GetWidth() - 200));
        aOutRect.AdjustTop(aTitleSize.Height() + 200);

        // output description
        aFont.SetWeight(WEIGHT_NORMAL);
        aFont.SetFontSize(aSize600);
        rOutDev.SetFont(aFont);
        aPoint.setX(aOutRect.Left() + (aOutRect.GetWidth() - aDescSize.Width()) / 2);
        aPoint.setY(aOutRect.Top());
        DrawText(rOutDev, aPoint, GetComment(),
                 sal::static_int_cast<sal_uInt16>(aOutRect.GetWidth() - 200));
        aOutRect.AdjustTop(aDescSize.Height() + 300);
    }

    // output text on bottom
    if (bIsPrintFormulaText)
    {
        vcl::Font aFont(FAMILY_DONTKNOW, Size(0, 600));
        aFont.SetAlignment(ALIGN_TOP);
        aFont.SetColor(COL_BLACK);

        // get size
        rOutDev.SetFont(aFont);

        Size aSize(GetTextSize(rOutDev, GetText(), aOutRect.GetWidth() - 200));

        aOutRect.AdjustBottom(-(aSize.Height() + 600));

        if (bIsPrintFrame)
            rOutDev.DrawRect(tools::Rectangle(
                aOutRect.BottomLeft(), Size(aOutRect.GetWidth(), 200 + aSize.Height() + 200)));

        Point aPoint(aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
                     aOutRect.Bottom() + 300);
        DrawText(rOutDev, aPoint, GetText(),
                 sal::static_int_cast<sal_uInt16>(aOutRect.GetWidth() - 200));
        aOutRect.AdjustBottom(-200);
    }

    if (bIsPrintFrame)
        rOutDev.DrawRect(aOutRect);

    aOutRect.AdjustTop(100);
    aOutRect.AdjustLeft(100);
    aOutRect.AdjustBottom(-100);
    aOutRect.AdjustRight(-100);

    Size aSize(GetSize());

    MapMode OutputMapMode;
    switch (ePrintSize)
    {
        case PRINT_SIZE_NORMAL:
            OutputMapMode = MapMode(SmMapUnit());
            break;

        case PRINT_SIZE_SCALED:
            if (!aSize.IsEmpty())
            {
                sal_uInt16 nZ
                    = std::min(o3tl::convert(aOutRect.GetWidth(), 100, aSize.Width()),
                               o3tl::convert(aOutRect.GetHeight(), 100, aSize.Height()));
                if (bIsPrintFrame && nZ > MINZOOM)
                    nZ -= 10;
                Fraction aFraction(std::clamp(nZ, MINZOOM, MAXZOOM), 100);

                OutputMapMode = MapMode(SmMapUnit(), Point(), aFraction, aFraction);
            }
            else
                OutputMapMode = MapMode(SmMapUnit());
            break;

        case PRINT_SIZE_ZOOMED:
        {
            Fraction aFraction(nZoomFactor, 100);

            OutputMapMode = MapMode(SmMapUnit(), Point(), aFraction, aFraction);
            break;
        }
    }

    aSize = OutputDevice::LogicToLogic(aSize, OutputMapMode, MapMode(SmMapUnit()));

    Point aPos(aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
               aOutRect.Top() + (aOutRect.GetHeight() - aSize.Height()) / 2);

    aPos = OutputDevice::LogicToLogic(aPos, MapMode(SmMapUnit()), OutputMapMode);
    aOutRect = OutputDevice::LogicToLogic(aOutRect, MapMode(SmMapUnit()), OutputMapMode);

    rOutDev.SetMapMode(OutputMapMode);
    rOutDev.SetClipRegion(vcl::Region(aOutRect));
    DrawFormula(rOutDev, aPos);
    rOutDev.SetClipRegion();

    rOutDev.Pop();
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=92 H=95 G=93

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© 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 und die Messung sind noch experimentell.