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