1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_drawinglayer.hxx"
26 
27 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
28 #include <vcl/timer.hxx>
29 #include <vcl/virdev.hxx>
30 #include <vcl/font.hxx>
31 #include <vcl/metric.hxx>
32 #include <i18npool/mslangid.hxx>
33 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
34 #include <vcl/svapp.hxx>
35 
36 //////////////////////////////////////////////////////////////////////////////
37 // VDev RevDevice provider
38 
39 namespace
40 {
41 	class ImpTimedRefDev : public Timer
42 	{
43 		ImpTimedRefDev**					mppStaticPointerOnMe;
44 		VirtualDevice*						mpVirDev;
45 		sal_uInt32							mnUseCount;
46 
47 	public:
48 		ImpTimedRefDev(ImpTimedRefDev** ppStaticPointerOnMe);
49 		~ImpTimedRefDev();
50 	    virtual void Timeout();
51 
52 		VirtualDevice& acquireVirtualDevice();
53 		void releaseVirtualDevice();
54 	};
55 
56 	ImpTimedRefDev::ImpTimedRefDev(ImpTimedRefDev** ppStaticPointerOnMe)
57 	:	mppStaticPointerOnMe(ppStaticPointerOnMe),
58 		mpVirDev(0L),
59 		mnUseCount(0L)
60 	{
61 		SetTimeout(3L * 60L * 1000L); // three minutes
62 		Start();
63 	}
64 
65 	ImpTimedRefDev::~ImpTimedRefDev()
66 	{
67 		OSL_ENSURE(0L == mnUseCount, "destruction of a still used ImpTimedRefDev (!)");
68 
69 		if(mppStaticPointerOnMe && *mppStaticPointerOnMe)
70 		{
71 			*mppStaticPointerOnMe = 0L;
72 		}
73 
74 		if(mpVirDev)
75 		{
76 			delete mpVirDev;
77 		}
78 	}
79 
80 	void ImpTimedRefDev::Timeout()
81 	{
82 		// for obvious reasons, do not call anything after this
83 		delete (this);
84 	}
85 
86 	VirtualDevice& ImpTimedRefDev::acquireVirtualDevice()
87 	{
88 		if(!mpVirDev)
89 		{
90 			mpVirDev = new VirtualDevice();
91 			mpVirDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_MSO1 );
92 		}
93 
94 		if(!mnUseCount)
95 		{
96 			Stop();
97 		}
98 
99 		mnUseCount++;
100 
101 		return *mpVirDev;
102 	}
103 
104 	void ImpTimedRefDev::releaseVirtualDevice()
105 	{
106 		OSL_ENSURE(mnUseCount, "mismatch call number to releaseVirtualDevice() (!)");
107 		mnUseCount--;
108 
109 		if(!mnUseCount)
110 		{
111 			Start();
112 		}
113 	}
114 } // end of anonymous namespace
115 
116 //////////////////////////////////////////////////////////////////////////////
117 // access to one global ImpTimedRefDev incarnation in namespace drawinglayer::primitive
118 
119 namespace drawinglayer
120 {
121 	namespace primitive2d
122 	{
123 		// static pointer here
124 		static ImpTimedRefDev* pImpGlobalRefDev = 0L;
125 
126 		// static methods here
127 		VirtualDevice& acquireGlobalVirtualDevice()
128 		{
129 			if(!pImpGlobalRefDev)
130 			{
131 				pImpGlobalRefDev = new ImpTimedRefDev(&pImpGlobalRefDev);
132 			}
133 
134 			return pImpGlobalRefDev->acquireVirtualDevice();
135 		}
136 
137 		void releaseGlobalVirtualDevice()
138 		{
139 			OSL_ENSURE(pImpGlobalRefDev, "releaseGlobalVirtualDevice() without prior acquireGlobalVirtualDevice() call(!)");
140 			pImpGlobalRefDev->releaseVirtualDevice();
141 		}
142 
143 		TextLayouterDevice::TextLayouterDevice()
144 		:	mrDevice(acquireGlobalVirtualDevice())
145 		{
146 		}
147 
148 		TextLayouterDevice::~TextLayouterDevice()
149 		{
150 			releaseGlobalVirtualDevice();
151 		}
152 
153 		void TextLayouterDevice::setFont(const Font& rFont)
154 		{
155 			mrDevice.SetFont( rFont );
156 		}
157 
158 		void TextLayouterDevice::setFontAttribute(
159             const attribute::FontAttribute& rFontAttribute,
160             double fFontScaleX,
161             double fFontScaleY,
162             const ::com::sun::star::lang::Locale& rLocale)
163 		{
164 			setFont(getVclFontFromFontAttribute(
165                 rFontAttribute,
166                 fFontScaleX,
167                 fFontScaleY,
168                 0.0,
169                 rLocale));
170         }
171 
172         double TextLayouterDevice::getOverlineOffset() const
173         {
174             const ::FontMetric& rMetric = mrDevice.GetFontMetric();
175             double fRet = (rMetric.GetIntLeading() / 2.0) - rMetric.GetAscent();
176             return fRet;
177         }
178 
179 		double TextLayouterDevice::getUnderlineOffset() const
180 		{
181 			const ::FontMetric& rMetric = mrDevice.GetFontMetric();
182 			double fRet = rMetric.GetDescent() / 2.0;
183 			return fRet;
184 		}
185 
186 		double TextLayouterDevice::getStrikeoutOffset() const
187 		{
188 			const ::FontMetric& rMetric = mrDevice.GetFontMetric();
189 			double fRet = (rMetric.GetAscent() - rMetric.GetIntLeading()) / 3.0;
190 			return fRet;
191 		}
192 
193         double TextLayouterDevice::getOverlineHeight() const
194         {
195             const ::FontMetric& rMetric = mrDevice.GetFontMetric();
196             double fRet = rMetric.GetIntLeading() / 2.5;
197             return fRet;
198         }
199 
200 		double TextLayouterDevice::getUnderlineHeight() const
201 		{
202 			const ::FontMetric& rMetric = mrDevice.GetFontMetric();
203 			double fRet = rMetric.GetDescent() / 4.0;
204 			return fRet;
205 		}
206 
207 		double TextLayouterDevice::getTextHeight() const
208 		{
209 			return mrDevice.GetTextHeight();
210 		}
211 
212 		double TextLayouterDevice::getTextWidth(
213 			const String& rText,
214 			sal_uInt32 nIndex,
215 			sal_uInt32 nLength) const
216 		{
217 			return mrDevice.GetTextWidth(rText, nIndex, nLength);
218 		}
219 
220 		bool TextLayouterDevice::getTextOutlines(
221 			basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector,
222 			const String& rText,
223 			sal_uInt32 nIndex,
224 			sal_uInt32 nLength,
225             const ::std::vector< double >& rDXArray) const
226 		{
227             const sal_uInt32 nDXArrayCount(rDXArray.size());
228 			sal_uInt32 nTextLength(nLength);
229 			const sal_uInt32 nStringLength(rText.Len());
230 
231 			if(nTextLength + nIndex > nStringLength)
232 			{
233 				nTextLength = nStringLength - nIndex;
234 			}
235 
236             if(nDXArrayCount)
237             {
238                 OSL_ENSURE(nDXArrayCount == nTextLength, "DXArray size does not correspond to text portion size (!)");
239     		    std::vector< sal_Int32 > aIntegerDXArray(nDXArrayCount);
240 
241                 for(sal_uInt32 a(0); a < nDXArrayCount; a++)
242                 {
243                     aIntegerDXArray[a] = basegfx::fround(rDXArray[a]);
244                 }
245 
246                 return mrDevice.GetTextOutlines(
247                     rB2DPolyPolyVector,
248                     rText,
249                     nIndex,
250                     nIndex,
251                     nLength,
252                     true,
253 			        0,
254                     &(aIntegerDXArray[0]));
255             }
256             else
257             {
258                 return mrDevice.GetTextOutlines(
259                     rB2DPolyPolyVector,
260                     rText,
261                     nIndex,
262                     nIndex,
263                     nLength,
264                     true,
265 			        0,
266                     0);
267             }
268 		}
269 
270 		basegfx::B2DRange TextLayouterDevice::getTextBoundRect(
271 			const String& rText,
272 			sal_uInt32 nIndex,
273 			sal_uInt32 nLength) const
274 		{
275 			sal_uInt32 nTextLength(nLength);
276 			const sal_uInt32 nStringLength(rText.Len());
277 
278 			if(nTextLength + nIndex > nStringLength)
279 			{
280 				nTextLength = nStringLength - nIndex;
281 			}
282 
283 			if(nTextLength)
284 			{
285 				Rectangle aRect;
286 
287 				mrDevice.GetTextBoundRect(
288                     aRect,
289                     rText,
290                     nIndex,
291                     nIndex,
292                     nLength);
293 
294                 // #i104432#, #i102556# take empty results into account
295                 if(!aRect.IsEmpty())
296                 {
297     				return basegfx::B2DRange(
298 						aRect.Left(), aRect.Top(),
299 						aRect.Right(), aRect.Bottom());
300                 }
301 			}
302 
303             return basegfx::B2DRange();
304 		}
305 
306         double TextLayouterDevice::getFontAscent() const
307         {
308             const ::FontMetric& rMetric = mrDevice.GetFontMetric();
309             return rMetric.GetAscent();
310         }
311 
312         double TextLayouterDevice::getFontDescent() const
313         {
314             const ::FontMetric& rMetric = mrDevice.GetFontMetric();
315             return rMetric.GetDescent();
316         }
317 
318 		void TextLayouterDevice::addTextRectActions(
319 			const Rectangle& rRectangle,
320 			const String& rText,
321 			sal_uInt16 nStyle,
322 			GDIMetaFile& rGDIMetaFile) const
323 		{
324 			mrDevice.AddTextRectActions(
325 				rRectangle, rText, nStyle, rGDIMetaFile);
326 		}
327 
328 		::std::vector< double > TextLayouterDevice::getTextArray(
329 			const String& rText,
330 			sal_uInt32 nIndex,
331 			sal_uInt32 nLength) const
332 		{
333 			::std::vector< double > aRetval;
334 			sal_uInt32 nTextLength(nLength);
335 			const sal_uInt32 nStringLength(rText.Len());
336 
337 			if(nTextLength + nIndex > nStringLength)
338 			{
339 				nTextLength = nStringLength - nIndex;
340 			}
341 
342 			if(nTextLength)
343 			{
344 				aRetval.reserve(nTextLength);
345 				sal_Int32* pArray = new sal_Int32[nTextLength];
346 				mrDevice.GetTextArray(rText, pArray, nIndex, nLength);
347 
348 				for(sal_uInt32 a(0); a < nTextLength; a++)
349 				{
350 					aRetval.push_back(pArray[a]);
351 				}
352 			}
353 
354 			return aRetval;
355 		}
356 
357 	} // end of namespace primitive2d
358 } // end of namespace drawinglayer
359 
360 //////////////////////////////////////////////////////////////////////////////
361 // helper methods for vcl font handling
362 
363 namespace drawinglayer
364 {
365 	namespace primitive2d
366 	{
367 		Font getVclFontFromFontAttribute(
368             const attribute::FontAttribute& rFontAttribute,
369             double fFontScaleX,
370             double fFontScaleY,
371             double fFontRotation,
372             const ::com::sun::star::lang::Locale& rLocale)
373 		{
374             // detect FontScaling
375 			const sal_uInt32 nHeight(basegfx::fround(fabs(fFontScaleY)));
376             const sal_uInt32 nWidth(basegfx::fround(fabs(fFontScaleX)));
377             const bool bFontIsScaled(nHeight != nWidth);
378 
379 #ifdef WIN32
380             // for WIN32 systems, start with creating an unscaled font. If FontScaling
381             // is wanted, that width needs to be adapted using FontMetric again to get a
382             // width of the unscaled font
383 			Font aRetval(
384 				rFontAttribute.getFamilyName(),
385 				rFontAttribute.getStyleName(),
386 				Size(0, nHeight));
387 #else
388             // for non-WIN32 systems things are easier since these accept a Font creation
389             // with initially nWidth != nHeight for FontScaling. Despite that, use zero for
390             // FontWidth when no scaling is used to explicitely have that zero when e.g. the
391             // Font would be recorded in a MetaFile (The MetaFile FontAction WILL record a
392             // set FontWidth; import that in a WIN32 system, and trouble is there)
393 			Font aRetval(
394 				rFontAttribute.getFamilyName(),
395 				rFontAttribute.getStyleName(),
396                 Size(bFontIsScaled ? nWidth : 0, nHeight));
397 #endif
398             // define various other FontAttribute
399 			aRetval.SetAlign(ALIGN_BASELINE);
400 			aRetval.SetCharSet(rFontAttribute.getSymbol() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE);
401 			aRetval.SetVertical(rFontAttribute.getVertical() ? sal_True : sal_False);
402 			aRetval.SetWeight(static_cast<FontWeight>(rFontAttribute.getWeight()));
403 			aRetval.SetItalic(rFontAttribute.getItalic() ? ITALIC_NORMAL : ITALIC_NONE);
404 			aRetval.SetOutline(rFontAttribute.getOutline());
405             aRetval.SetPitch(rFontAttribute.getMonospaced() ? PITCH_FIXED : PITCH_VARIABLE);
406             aRetval.SetLanguage(MsLangId::convertLocaleToLanguage(rLocale));
407 
408 #ifdef WIN32
409             // for WIN32 systems, correct the FontWidth if FontScaling is used
410             if(bFontIsScaled && nHeight > 0)
411             {
412                 const FontMetric aUnscaledFontMetric(Application::GetDefaultDevice()->GetFontMetric(aRetval));
413 
414                 if(aUnscaledFontMetric.GetWidth() > 0)
415                 {
416                     const double fScaleFactor((double)nWidth / (double)nHeight);
417                     const sal_uInt32 nScaledWidth(basegfx::fround((double)aUnscaledFontMetric.GetWidth() * fScaleFactor));
418                     aRetval.SetWidth(nScaledWidth);
419                 }
420             }
421 #endif
422             // handle FontRotation (if defined)
423 			if(!basegfx::fTools::equalZero(fFontRotation))
424 			{
425 				sal_Int16 aRotate10th((sal_Int16)(fFontRotation * (-1800.0/F_PI)));
426 				aRetval.SetOrientation(aRotate10th % 3600);
427 			}
428 
429 			return aRetval;
430 		}
431 
432         attribute::FontAttribute getFontAttributeFromVclFont(
433             basegfx::B2DVector& o_rSize,
434             const Font& rFont,
435             bool bRTL,
436             bool bBiDiStrong)
437 		{
438             const attribute::FontAttribute aRetval(
439 			    rFont.GetName(),
440 			    rFont.GetStyleName(),
441 			    static_cast<sal_uInt16>(rFont.GetWeight()),
442                 RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet(),
443 			    rFont.IsVertical(),
444 			    ITALIC_NONE != rFont.GetItalic(),
445                 PITCH_FIXED == rFont.GetPitch(),
446 			    rFont.IsOutline(),
447                 bRTL,
448                 bBiDiStrong);
449 			// TODO: eKerning
450 
451             // set FontHeight and init to no FontScaling
452             o_rSize.setY(rFont.GetSize().getHeight() > 0 ? rFont.GetSize().getHeight() : 0);
453             o_rSize.setX(o_rSize.getY());
454 
455 #ifdef WIN32
456             // for WIN32 systems, the FontScaling at the Font is detected by
457             // checking that FontWidth != 0. When FontScaling is used, WIN32
458             // needs to do extra stuff to detect the correct width (since it's
459             // zero and not equal the font height) and it's relationship to
460             // the height
461             if(rFont.GetSize().getWidth() > 0)
462             {
463                 Font aUnscaledFont(rFont);
464                 aUnscaledFont.SetWidth(0);
465                 const FontMetric aUnscaledFontMetric(Application::GetDefaultDevice()->GetFontMetric(aUnscaledFont));
466 
467                 if(aUnscaledFontMetric.GetWidth() > 0)
468                 {
469                     const double fScaleFactor((double)rFont.GetSize().getWidth() / (double)aUnscaledFontMetric.GetWidth());
470                     o_rSize.setX(fScaleFactor * o_rSize.getY());
471                 }
472             }
473 #else
474             // For non-WIN32 systems the detection is the same, but the value
475             // is easier achieved since width == height is interpreted as no
476             // scaling. Ergo, Width == 0 means width == height, and width != 0
477             // means the scaling is in the direct relation of width to height
478             if(rFont.GetSize().getWidth() > 0)
479             {
480                 o_rSize.setX((double)rFont.GetSize().getWidth());
481             }
482 #endif
483 			return aRetval;
484 		}
485 	} // end of namespace primitive2d
486 } // end of namespace drawinglayer
487 
488 //////////////////////////////////////////////////////////////////////////////
489 // eof
490