| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_drawinglayer.hxx" |
| |
| #include <drawinglayer/primitive2d/textlayoutdevice.hxx> |
| #include <vcl/timer.hxx> |
| #include <vcl/virdev.hxx> |
| #include <vcl/font.hxx> |
| #include <vcl/metric.hxx> |
| #include <i18npool/mslangid.hxx> |
| #include <drawinglayer/primitive2d/textprimitive2d.hxx> |
| #include <vcl/svapp.hxx> |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // VDev RevDevice provider |
| |
| namespace |
| { |
| class ImpTimedRefDev : public Timer |
| { |
| ImpTimedRefDev** mppStaticPointerOnMe; |
| VirtualDevice* mpVirDev; |
| sal_uInt32 mnUseCount; |
| |
| public: |
| ImpTimedRefDev(ImpTimedRefDev** ppStaticPointerOnMe); |
| ~ImpTimedRefDev(); |
| virtual void Timeout(); |
| |
| VirtualDevice& acquireVirtualDevice(); |
| void releaseVirtualDevice(); |
| }; |
| |
| ImpTimedRefDev::ImpTimedRefDev(ImpTimedRefDev** ppStaticPointerOnMe) |
| : mppStaticPointerOnMe(ppStaticPointerOnMe), |
| mpVirDev(0L), |
| mnUseCount(0L) |
| { |
| SetTimeout(3L * 60L * 1000L); // three minutes |
| Start(); |
| } |
| |
| ImpTimedRefDev::~ImpTimedRefDev() |
| { |
| OSL_ENSURE(0L == mnUseCount, "destruction of a still used ImpTimedRefDev (!)"); |
| |
| if(mppStaticPointerOnMe && *mppStaticPointerOnMe) |
| { |
| *mppStaticPointerOnMe = 0L; |
| } |
| |
| if(mpVirDev) |
| { |
| delete mpVirDev; |
| } |
| } |
| |
| void ImpTimedRefDev::Timeout() |
| { |
| // for obvious reasons, do not call anything after this |
| delete (this); |
| } |
| |
| VirtualDevice& ImpTimedRefDev::acquireVirtualDevice() |
| { |
| if(!mpVirDev) |
| { |
| mpVirDev = new VirtualDevice(); |
| mpVirDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_MSO1 ); |
| } |
| |
| if(!mnUseCount) |
| { |
| Stop(); |
| } |
| |
| mnUseCount++; |
| |
| return *mpVirDev; |
| } |
| |
| void ImpTimedRefDev::releaseVirtualDevice() |
| { |
| OSL_ENSURE(mnUseCount, "mismatch call number to releaseVirtualDevice() (!)"); |
| mnUseCount--; |
| |
| if(!mnUseCount) |
| { |
| Start(); |
| } |
| } |
| } // end of anonymous namespace |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // access to one global ImpTimedRefDev incarnation in namespace drawinglayer::primitive |
| |
| namespace drawinglayer |
| { |
| namespace primitive2d |
| { |
| // static pointer here |
| static ImpTimedRefDev* pImpGlobalRefDev = 0L; |
| |
| // static methods here |
| VirtualDevice& acquireGlobalVirtualDevice() |
| { |
| if(!pImpGlobalRefDev) |
| { |
| pImpGlobalRefDev = new ImpTimedRefDev(&pImpGlobalRefDev); |
| } |
| |
| return pImpGlobalRefDev->acquireVirtualDevice(); |
| } |
| |
| void releaseGlobalVirtualDevice() |
| { |
| OSL_ENSURE(pImpGlobalRefDev, "releaseGlobalVirtualDevice() without prior acquireGlobalVirtualDevice() call(!)"); |
| pImpGlobalRefDev->releaseVirtualDevice(); |
| } |
| |
| TextLayouterDevice::TextLayouterDevice() |
| : mrDevice(acquireGlobalVirtualDevice()) |
| { |
| } |
| |
| TextLayouterDevice::~TextLayouterDevice() |
| { |
| releaseGlobalVirtualDevice(); |
| } |
| |
| void TextLayouterDevice::setFont(const Font& rFont) |
| { |
| mrDevice.SetFont( rFont ); |
| } |
| |
| void TextLayouterDevice::setFontAttribute( |
| const attribute::FontAttribute& rFontAttribute, |
| double fFontScaleX, |
| double fFontScaleY, |
| const ::com::sun::star::lang::Locale& rLocale) |
| { |
| setFont(getVclFontFromFontAttribute( |
| rFontAttribute, |
| fFontScaleX, |
| fFontScaleY, |
| 0.0, |
| rLocale)); |
| } |
| |
| double TextLayouterDevice::getOverlineOffset() const |
| { |
| const ::FontMetric& rMetric = mrDevice.GetFontMetric(); |
| double fRet = (rMetric.GetIntLeading() / 2.0) - rMetric.GetAscent(); |
| return fRet; |
| } |
| |
| double TextLayouterDevice::getUnderlineOffset() const |
| { |
| const ::FontMetric& rMetric = mrDevice.GetFontMetric(); |
| double fRet = rMetric.GetDescent() / 2.0; |
| return fRet; |
| } |
| |
| double TextLayouterDevice::getStrikeoutOffset() const |
| { |
| const ::FontMetric& rMetric = mrDevice.GetFontMetric(); |
| double fRet = (rMetric.GetAscent() - rMetric.GetIntLeading()) / 3.0; |
| return fRet; |
| } |
| |
| double TextLayouterDevice::getOverlineHeight() const |
| { |
| const ::FontMetric& rMetric = mrDevice.GetFontMetric(); |
| double fRet = rMetric.GetIntLeading() / 2.5; |
| return fRet; |
| } |
| |
| double TextLayouterDevice::getUnderlineHeight() const |
| { |
| const ::FontMetric& rMetric = mrDevice.GetFontMetric(); |
| double fRet = rMetric.GetDescent() / 4.0; |
| return fRet; |
| } |
| |
| double TextLayouterDevice::getTextHeight() const |
| { |
| return mrDevice.GetTextHeight(); |
| } |
| |
| double TextLayouterDevice::getTextWidth( |
| const String& rText, |
| sal_uInt32 nIndex, |
| sal_uInt32 nLength) const |
| { |
| return mrDevice.GetTextWidth(rText, nIndex, nLength); |
| } |
| |
| bool TextLayouterDevice::getTextOutlines( |
| basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector, |
| const String& rText, |
| sal_uInt32 nIndex, |
| sal_uInt32 nLength, |
| const ::std::vector< double >& rDXArray) const |
| { |
| const sal_uInt32 nDXArrayCount(rDXArray.size()); |
| sal_uInt32 nTextLength(nLength); |
| const sal_uInt32 nStringLength(rText.Len()); |
| |
| if(nTextLength + nIndex > nStringLength) |
| { |
| nTextLength = nStringLength - nIndex; |
| } |
| |
| if(nDXArrayCount) |
| { |
| OSL_ENSURE(nDXArrayCount == nTextLength, "DXArray size does not correspond to text portion size (!)"); |
| std::vector< sal_Int32 > aIntegerDXArray(nDXArrayCount); |
| |
| for(sal_uInt32 a(0); a < nDXArrayCount; a++) |
| { |
| aIntegerDXArray[a] = basegfx::fround(rDXArray[a]); |
| } |
| |
| return mrDevice.GetTextOutlines( |
| rB2DPolyPolyVector, |
| rText, |
| nIndex, |
| nIndex, |
| nLength, |
| true, |
| 0, |
| &(aIntegerDXArray[0])); |
| } |
| else |
| { |
| return mrDevice.GetTextOutlines( |
| rB2DPolyPolyVector, |
| rText, |
| nIndex, |
| nIndex, |
| nLength, |
| true, |
| 0, |
| 0); |
| } |
| } |
| |
| basegfx::B2DRange TextLayouterDevice::getTextBoundRect( |
| const String& rText, |
| sal_uInt32 nIndex, |
| sal_uInt32 nLength) const |
| { |
| sal_uInt32 nTextLength(nLength); |
| const sal_uInt32 nStringLength(rText.Len()); |
| |
| if(nTextLength + nIndex > nStringLength) |
| { |
| nTextLength = nStringLength - nIndex; |
| } |
| |
| if(nTextLength) |
| { |
| Rectangle aRect; |
| |
| mrDevice.GetTextBoundRect( |
| aRect, |
| rText, |
| nIndex, |
| nIndex, |
| nLength); |
| |
| // #i104432#, #i102556# take empty results into account |
| if(!aRect.IsEmpty()) |
| { |
| return basegfx::B2DRange( |
| aRect.Left(), aRect.Top(), |
| aRect.Right(), aRect.Bottom()); |
| } |
| } |
| |
| return basegfx::B2DRange(); |
| } |
| |
| double TextLayouterDevice::getFontAscent() const |
| { |
| const ::FontMetric& rMetric = mrDevice.GetFontMetric(); |
| return rMetric.GetAscent(); |
| } |
| |
| double TextLayouterDevice::getFontDescent() const |
| { |
| const ::FontMetric& rMetric = mrDevice.GetFontMetric(); |
| return rMetric.GetDescent(); |
| } |
| |
| void TextLayouterDevice::addTextRectActions( |
| const Rectangle& rRectangle, |
| const String& rText, |
| sal_uInt16 nStyle, |
| GDIMetaFile& rGDIMetaFile) const |
| { |
| mrDevice.AddTextRectActions( |
| rRectangle, rText, nStyle, rGDIMetaFile); |
| } |
| |
| ::std::vector< double > TextLayouterDevice::getTextArray( |
| const String& rText, |
| sal_uInt32 nIndex, |
| sal_uInt32 nLength) const |
| { |
| ::std::vector< double > aRetval; |
| sal_uInt32 nTextLength(nLength); |
| const sal_uInt32 nStringLength(rText.Len()); |
| |
| if(nTextLength + nIndex > nStringLength) |
| { |
| nTextLength = nStringLength - nIndex; |
| } |
| |
| if(nTextLength) |
| { |
| aRetval.reserve(nTextLength); |
| ::std::vector<sal_Int32> aDXArray( nTextLength); |
| mrDevice.GetTextArray(rText, &aDXArray[0], nIndex, nLength); |
| |
| for(sal_uInt32 a(0); a < nTextLength; a++) |
| { |
| aRetval.push_back(aDXArray[a]); |
| } |
| } |
| |
| return aRetval; |
| } |
| |
| } // end of namespace primitive2d |
| } // end of namespace drawinglayer |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // helper methods for vcl font handling |
| |
| namespace drawinglayer |
| { |
| namespace primitive2d |
| { |
| Font getVclFontFromFontAttribute( |
| const attribute::FontAttribute& rFontAttribute, |
| double fFontScaleX, |
| double fFontScaleY, |
| double fFontRotation, |
| const ::com::sun::star::lang::Locale& rLocale) |
| { |
| // detect FontScaling |
| const sal_uInt32 nHeight(basegfx::fround(fabs(fFontScaleY))); |
| const sal_uInt32 nWidth(basegfx::fround(fabs(fFontScaleX))); |
| const bool bFontIsScaled(nHeight != nWidth); |
| |
| #ifdef WIN32 |
| // for WIN32 systems, start with creating an unscaled font. If FontScaling |
| // is wanted, that width needs to be adapted using FontMetric again to get a |
| // width of the unscaled font |
| Font aRetval( |
| rFontAttribute.getFamilyName(), |
| rFontAttribute.getStyleName(), |
| Size(0, nHeight)); |
| #else |
| // for non-WIN32 systems things are easier since these accept a Font creation |
| // with initially nWidth != nHeight for FontScaling. Despite that, use zero for |
| // FontWidth when no scaling is used to explicitely have that zero when e.g. the |
| // Font would be recorded in a MetaFile (The MetaFile FontAction WILL record a |
| // set FontWidth; import that in a WIN32 system, and trouble is there) |
| Font aRetval( |
| rFontAttribute.getFamilyName(), |
| rFontAttribute.getStyleName(), |
| Size(bFontIsScaled ? nWidth : 0, nHeight)); |
| #endif |
| // define various other FontAttribute |
| aRetval.SetAlign(ALIGN_BASELINE); |
| aRetval.SetCharSet(rFontAttribute.getSymbol() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE); |
| aRetval.SetVertical(rFontAttribute.getVertical() ? sal_True : sal_False); |
| aRetval.SetWeight(static_cast<FontWeight>(rFontAttribute.getWeight())); |
| aRetval.SetItalic(rFontAttribute.getItalic() ? ITALIC_NORMAL : ITALIC_NONE); |
| aRetval.SetOutline(rFontAttribute.getOutline()); |
| aRetval.SetPitch(rFontAttribute.getMonospaced() ? PITCH_FIXED : PITCH_VARIABLE); |
| aRetval.SetLanguage(MsLangId::convertLocaleToLanguage(rLocale)); |
| |
| #ifdef WIN32 |
| // for WIN32 systems, correct the FontWidth if FontScaling is used |
| if(bFontIsScaled && nHeight > 0) |
| { |
| const FontMetric aUnscaledFontMetric(Application::GetDefaultDevice()->GetFontMetric(aRetval)); |
| |
| if(aUnscaledFontMetric.GetWidth() > 0) |
| { |
| const double fScaleFactor((double)nWidth / (double)nHeight); |
| const sal_uInt32 nScaledWidth(basegfx::fround((double)aUnscaledFontMetric.GetWidth() * fScaleFactor)); |
| aRetval.SetWidth(nScaledWidth); |
| } |
| } |
| #endif |
| // handle FontRotation (if defined) |
| if(!basegfx::fTools::equalZero(fFontRotation)) |
| { |
| sal_Int16 aRotate10th((sal_Int16)(fFontRotation * (-1800.0/F_PI))); |
| aRetval.SetOrientation(aRotate10th % 3600); |
| } |
| |
| return aRetval; |
| } |
| |
| attribute::FontAttribute getFontAttributeFromVclFont( |
| basegfx::B2DVector& o_rSize, |
| const Font& rFont, |
| bool bRTL, |
| bool bBiDiStrong) |
| { |
| const attribute::FontAttribute aRetval( |
| rFont.GetName(), |
| rFont.GetStyleName(), |
| static_cast<sal_uInt16>(rFont.GetWeight()), |
| RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet(), |
| rFont.IsVertical(), |
| ITALIC_NONE != rFont.GetItalic(), |
| PITCH_FIXED == rFont.GetPitch(), |
| rFont.IsOutline(), |
| bRTL, |
| bBiDiStrong); |
| // TODO: eKerning |
| |
| // set FontHeight and init to no FontScaling |
| o_rSize.setY(rFont.GetSize().getHeight() > 0 ? rFont.GetSize().getHeight() : 0); |
| o_rSize.setX(o_rSize.getY()); |
| |
| #ifdef WIN32 |
| // for WIN32 systems, the FontScaling at the Font is detected by |
| // checking that FontWidth != 0. When FontScaling is used, WIN32 |
| // needs to do extra stuff to detect the correct width (since it's |
| // zero and not equal the font height) and it's relationship to |
| // the height |
| if(rFont.GetSize().getWidth() > 0) |
| { |
| Font aUnscaledFont(rFont); |
| aUnscaledFont.SetWidth(0); |
| const FontMetric aUnscaledFontMetric(Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont)); |
| |
| if(aUnscaledFontMetric.GetWidth() > 0) |
| { |
| const double fScaleFactor((double)rFont.GetSize().getWidth() / (double)aUnscaledFontMetric.GetWidth()); |
| o_rSize.setX(fScaleFactor * o_rSize.getY()); |
| } |
| } |
| #else |
| // For non-WIN32 systems the detection is the same, but the value |
| // is easier achieved since width == height is interpreted as no |
| // scaling. Ergo, Width == 0 means width == height, and width != 0 |
| // means the scaling is in the direct relation of width to height |
| if(rFont.GetSize().getWidth() > 0) |
| { |
| o_rSize.setX((double)rFont.GetSize().getWidth()); |
| } |
| #endif |
| return aRetval; |
| } |
| } // end of namespace primitive2d |
| } // end of namespace drawinglayer |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // eof |