diff options
Diffstat (limited to 'starmath/source/document.cxx')
-rw-r--r-- | starmath/source/document.cxx | 1583 |
1 files changed, 1583 insertions, 0 deletions
diff --git a/starmath/source/document.cxx b/starmath/source/document.cxx new file mode 100644 index 0000000000..ff28f448cb --- /dev/null +++ b/starmath/source/document.cxx @@ -0,0 +1,1583 @@ +/* -*- 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("view"); +} + +void SmDocShell::SetSmSyntaxVersion(sal_Int16 nSmSyntaxVersion) +{ + mnSmSyntaxVersion = nSmSyntaxVersion; + maParser.reset(starmathdatabase::GetVersionSmParser(mnSmSyntaxVersion)); +} + +SFX_IMPL_OBJECTFACTORY(SmDocShell, SvGlobalName(SO3_SM_CLASSID), "smath" ) + +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 *pp = SM_MOD(); + pp->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 = &SM_MOD()->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 | + vcl::PushFlags::RTLENABLED); + + // We want the device to always be LTR, we handle RTL formulas ourselves. + 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->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 | + vcl::PushFlags::RTLENABLED); + + // We want the device to always be LTR, we handle RTL formulas ourselves. + 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.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 *pp = SM_MOD(); + pp->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) + , mnSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion()) +{ + SvtLinguConfig().GetOptions(maLinguOptions); + + SetPool(&SfxGetpApp()->GetPool()); + + SmModule *pp = SM_MOD(); + maFormat = pp->GetConfig()->GetStandardFormat(); + + StartListening(maFormat); + StartListening(*pp->GetConfig()); + + SetBaseModel(new SmModel(this)); + SetSmSyntaxVersion(mnSmSyntaxVersion); + + SetMapUnit(SmMapUnit()); +} + +SmDocShell::~SmDocShell() +{ + SmModule *pp = SM_MOD(); + + EndListening(maFormat); + EndListening(*pp->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 ) ) + { + tools::SvRef<SotStorage> aStorage = new SotStorage( pStream, false ); + if ( aStorage->IsStream("Equation Native") ) + { + // 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("content.xml") && xStorage->isStreamElement("content.xml")) + { + // 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(); + + Reference<css::frame::XModel> xModel(GetModel()); + SmXMLExportWrapper aEquation(xModel); + 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(); + + Reference<css::frame::XModel> xModel(GetModel()); + SmXMLExportWrapper aEquation(xModel); + 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) + { + Reference<css::frame::XModel> xModel(GetModel()); + SmXMLExportWrapper aEquation(xModel); + aEquation.SetFlat(false); + bRet = aEquation.Export(rMedium); + } + else if(rFltName == MATHML_XML) + { + Reference<css::frame::XModel> xModel(GetModel()); + SmXMLExportWrapper aEquation(xModel); + 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 : + { + SmModule *pp = SM_MOD(); + bool bRedraw = pp->GetConfig()->IsAutoRedraw(); + pp->GetConfig()->SetAutoRedraw(!bRedraw); + } + 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 = &SM_MOD()->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); + + SmModule *pp = SM_MOD(); + SmFormat aFmt( pp->GetConfig()->GetStandardFormat() ); + aAlignDialog.WriteTo( aFmt ); + pp->GetConfig()->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)(); + } + catch( const 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 : + { + SmModule *pp = SM_MOD(); + bool bRedraw = pp->GetConfig()->IsAutoRedraw(); + + rSet.Put(SfxBoolItem(SID_AUTO_REDRAW, bRedraw)); + } + 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 *pp = SM_MOD(); + pp->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: */ |