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_canvas.hxx"
26 
27 #include <tools/poly.hxx>
28 
29 #include <vcl/metric.hxx>
30 #include <vcl/virdev.hxx>
31 #include <vcl/metric.hxx>
32 #include <vcl/canvastools.hxx>
33 #include <tools/diagnose_ex.h>
34 
35 #include <boost/scoped_array.hpp>
36 #include <boost/bind.hpp>
37 #include <com/sun/star/rendering/FontRequest.hpp>
38 #include <com/sun/star/rendering/PanoseProportion.hpp>
39 #include <com/sun/star/rendering/XCanvasFont.hpp>
40 #include <comphelper/sequence.hxx>
41 #include <comphelper/scopeguard.hxx>
42 #include <tools/color.hxx>
43 #include <basegfx/polygon/b2dpolypolygon.hxx>
44 #include <basegfx/tools/canvastools.hxx>
45 #include <canvas/canvastools.hxx>
46 #include <canvas/debug.hxx>
47 #include "dx_impltools.hxx"
48 #include <vcl/sysdata.hxx>
49 #include <i18npool/mslangid.hxx>
50 #include "dx_textlayout_drawhelper.hxx"
51 #include "dx_bitmap.hxx"
52 #include "dx_canvasfont.hxx"
53 
54 class ::com::sun::star::rendering::XCanvasFont;
55 
56 using namespace ::com::sun::star;
57 
58 
59 //////////////////////////////////////////////////////////////////////////////
60 
61 namespace dxcanvas
62 {
63 	class DXBitmap;
64 	TextLayoutDrawHelper::TextLayoutDrawHelper(
65 		const uno::Reference< rendering::XGraphicDevice >& xGraphicDevice ) :
66 			mxGraphicDevice(xGraphicDevice)
67 	{
68 	}
69 
70 	TextLayoutDrawHelper::~TextLayoutDrawHelper()
71 	{
72 	}
73 
74 	void TextLayoutDrawHelper::drawText(
75         const GraphicsSharedPtr&                            rGraphics,
76         const ::com::sun::star::rendering::ViewState& 		rViewState,
77         const ::com::sun::star::rendering::RenderState& 	rRenderState,
78         const ::basegfx::B2ISize& 							rOutputOffset,
79         const ::com::sun::star::rendering::StringContext& 	rText,
80         const ::com::sun::star::uno::Sequence< double >& 	rLogicalAdvancements,
81         const ::com::sun::star::uno::Reference<
82             ::com::sun::star::rendering::XCanvasFont >& 	rCanvasFont,
83         const ::com::sun::star::geometry::Matrix2D& 		rFontMatrix,
84         bool                                                bAlphaSurface )
85 	{
86         HDC hdc = rGraphics->GetHDC();
87 
88         // issue an ReleaseHDC() when leaving the scope
89         const ::comphelper::ScopeGuard aGuard(
90             boost::bind( &Gdiplus::Graphics::ReleaseHDC,
91                          rGraphics.get(),
92                          hdc ));
93 
94         SystemGraphicsData aSystemGraphicsData;
95         aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
96         aSystemGraphicsData.hDC = reinterpret_cast< ::HDC >(hdc);
97         VirtualDevice aVirtualDevice(&aSystemGraphicsData, 0);
98 
99         // disable font antialiasing - GDI does not handle alpha
100         // surfaces properly.
101         if( bAlphaSurface )
102 			aVirtualDevice.SetAntialiasing(ANTIALIASING_DISABLE_TEXT);
103 
104         if(rText.Length)
105         {
106 			sal_Bool test = mxGraphicDevice.is();
107             ENSURE_OR_THROW( test,
108                               "TextLayoutDrawHelper::drawText(): Invalid GraphicDevice" );
109 
110             // set text color. Make sure to remove transparence part first.
111             Color aColor( COL_WHITE );
112 
113             if( rRenderState.DeviceColor.getLength() > 2 )
114                 aColor = ::vcl::unotools::doubleSequenceToColor(
115                     rRenderState.DeviceColor,
116                     mxGraphicDevice->getDeviceColorSpace());
117             aColor.SetTransparency(0);
118             aVirtualDevice.SetTextColor(aColor);
119 
120             // create the font
121             const ::com::sun::star::rendering::FontRequest& rFontRequest = rCanvasFont->getFontRequest();
122             Font aFont(
123                 rFontRequest.FontDescription.FamilyName,
124                 rFontRequest.FontDescription.StyleName,
125                 Size( 0, ::basegfx::fround(rFontRequest.CellSize)));
126 
127             aFont.SetAlign( ALIGN_BASELINE );
128             aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==com::sun::star::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
129             aFont.SetVertical( (rFontRequest.FontDescription.IsVertical==com::sun::star::util::TriState_YES) ? sal_True : sal_False );
130             aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
131             aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
132             aFont.SetPitch(
133                     rFontRequest.FontDescription.FontDescription.Proportion == rendering::PanoseProportion::MONO_SPACED
134                     ? PITCH_FIXED : PITCH_VARIABLE);
135 
136             aFont.SetLanguage(MsLangId::convertLocaleToLanguage(rFontRequest.Locale));
137 
138             // setup font color
139             aFont.SetColor( aColor );
140             aFont.SetFillColor( aColor );
141 
142             // adjust to stretched font
143             if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11))
144             {
145                 const Size aSize = aVirtualDevice.GetFontMetric( aFont ).GetSize();
146                 const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
147                 double fStretch = (rFontMatrix.m00 + rFontMatrix.m01);
148 
149                 if( !::basegfx::fTools::equalZero( fDividend) )
150                     fStretch /= fDividend;
151 
152                 const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );
153 
154                 aFont.SetWidth( nNewWidth );
155             }
156 
157             // set font
158             aVirtualDevice.SetFont(aFont);
159 
160             // create world transformation matrix
161             ::basegfx::B2DHomMatrix aWorldTransform;
162             ::canvas::tools::mergeViewAndRenderTransform(aWorldTransform, rViewState, rRenderState);
163 
164             if(!rOutputOffset.equalZero())
165             {
166                 aWorldTransform.translate(rOutputOffset.getX(), rOutputOffset.getY());
167             }
168 
169             // set ViewState clipping
170             if(rViewState.Clip.is())
171             {
172                 ::basegfx::B2DPolyPolygon aClipPoly(dxcanvas::tools::polyPolygonFromXPolyPolygon2D(rViewState.Clip));
173                 ::basegfx::B2DHomMatrix aMatrix;
174                 ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix, rViewState.AffineTransform );
175 
176                 if(!rOutputOffset.equalZero())
177                 {
178                     aMatrix.translate(rOutputOffset.getX(), rOutputOffset.getY());
179                 }
180 
181                 aClipPoly.transform(aMatrix);
182                 const Region& rClipRegion = Region(PolyPolygon(aClipPoly));
183                 aVirtualDevice.IntersectClipRegion(rClipRegion);
184             }
185 
186             if(rRenderState.Clip.is())
187             {
188                 ::basegfx::B2DPolyPolygon aClipPoly(dxcanvas::tools::polyPolygonFromXPolyPolygon2D(rRenderState.Clip));
189                 aClipPoly.transform(aWorldTransform);
190                 const Region& rClipRegion = Region(PolyPolygon(aClipPoly));
191                 aVirtualDevice.IntersectClipRegion(rClipRegion);
192             }
193 
194             // set world transform
195             XFORM aXForm;
196             aXForm.eM11 = (FLOAT)aWorldTransform.get(0, 0);
197             aXForm.eM12 = (FLOAT)aWorldTransform.get(1, 0);
198             aXForm.eM21 = (FLOAT)aWorldTransform.get(0, 1);
199             aXForm.eM22 = (FLOAT)aWorldTransform.get(1, 1);
200             aXForm.eDx = (FLOAT)aWorldTransform.get(0, 2);
201             aXForm.eDy = (FLOAT)aWorldTransform.get(1, 2);
202 
203             // TODO(F3): This is NOT supported on 95/98/ME!
204             SetGraphicsMode(hdc, GM_ADVANCED);
205             SetTextAlign(hdc, TA_BASELINE);
206             SetWorldTransform(hdc, &aXForm);
207 
208             // use a empty StartPosition for text rendering
209             const Point aEmptyPoint(0, 0);
210 
211             // create the String
212             const String aText(rText.Text.getStr());
213 
214             if( rLogicalAdvancements.getLength() )
215             {
216                 // create the DXArray
217                 const sal_Int32 nLen( rLogicalAdvancements.getLength() );
218                 ::boost::scoped_array<sal_Int32> pDXArray( new sal_Int32[nLen] );
219                 for( sal_Int32 i=0; i<nLen; ++i )
220                     pDXArray[i] = basegfx::fround( rLogicalAdvancements[i] );
221 
222                 // draw the String
223                 aVirtualDevice.DrawTextArray( aEmptyPoint,
224                                               aText,
225                                               pDXArray.get(),
226                                               (xub_StrLen)rText.StartPosition,
227                                               (xub_StrLen)rText.Length );
228             }
229             else
230             {
231                 // draw the String
232                 aVirtualDevice.DrawText( aEmptyPoint,
233                                          aText,
234                                          (xub_StrLen)rText.StartPosition,
235                                          (xub_StrLen)rText.Length );
236             }
237         }
238 	}
239 
240 	geometry::RealRectangle2D TextLayoutDrawHelper::queryTextBounds( const rendering::StringContext& 					rText,
241                                                                      const uno::Sequence< double >& 					rLogicalAdvancements,
242                                                                      const uno::Reference< rendering::XCanvasFont >&	rCanvasFont,
243                                                                      const geometry::Matrix2D& 							rFontMatrix )
244 	{
245 		if(!(rText.Length))
246             return geometry::RealRectangle2D();
247 
248         // TODO(F1): Fetching default screen DC here, will yield wrong
249         // metrics when e.g. formatting for a printer!
250 		SystemGraphicsData aSystemGraphicsData;
251 		aSystemGraphicsData.nSize = sizeof(SystemGraphicsData);
252 		aSystemGraphicsData.hDC = reinterpret_cast< ::HDC >(GetDC( NULL ));
253 		VirtualDevice aVirtualDevice(&aSystemGraphicsData, 0);
254 
255         // create the font
256         const ::com::sun::star::rendering::FontRequest& rFontRequest = rCanvasFont->getFontRequest();
257         Font aFont(
258             rFontRequest.FontDescription.FamilyName,
259             rFontRequest.FontDescription.StyleName,
260             Size( 0, ::basegfx::fround(rFontRequest.CellSize)));
261 
262         aFont.SetAlign( ALIGN_BASELINE );
263         aFont.SetCharSet( (rFontRequest.FontDescription.IsSymbolFont==com::sun::star::util::TriState_YES) ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
264         aFont.SetVertical( (rFontRequest.FontDescription.IsVertical==com::sun::star::util::TriState_YES) ? sal_True : sal_False );
265         aFont.SetWeight( static_cast<FontWeight>(rFontRequest.FontDescription.FontDescription.Weight) );
266         aFont.SetItalic( (rFontRequest.FontDescription.FontDescription.Letterform<=8) ? ITALIC_NONE : ITALIC_NORMAL );
267         aFont.SetPitch(
268                 rFontRequest.FontDescription.FontDescription.Proportion == rendering::PanoseProportion::MONO_SPACED
269                 ? PITCH_FIXED : PITCH_VARIABLE);
270 
271         // adjust to stretched font
272         if(!::rtl::math::approxEqual(rFontMatrix.m00, rFontMatrix.m11))
273         {
274             const Size aSize = aVirtualDevice.GetFontMetric( aFont ).GetSize();
275             const double fDividend( rFontMatrix.m10 + rFontMatrix.m11 );
276             double fStretch = (rFontMatrix.m00 + rFontMatrix.m01);
277 
278             if( !::basegfx::fTools::equalZero( fDividend) )
279                 fStretch /= fDividend;
280 
281             const sal_Int32 nNewWidth = ::basegfx::fround( aSize.Width() * fStretch );
282 
283             aFont.SetWidth( nNewWidth );
284         }
285 
286         // set font
287         aVirtualDevice.SetFont(aFont);
288 
289         // need metrics for Y offset, the XCanvas always renders
290         // relative to baseline
291         const ::FontMetric& aMetric( aVirtualDevice.GetFontMetric() );
292 
293         const sal_Int32 nAboveBaseline( -aMetric.GetIntLeading() - aMetric.GetAscent() );
294         const sal_Int32 nBelowBaseline( aMetric.GetDescent() );
295 
296         if( rLogicalAdvancements.getLength() )
297         {
298             return geometry::RealRectangle2D( 0, nAboveBaseline,
299                                               rLogicalAdvancements[ rLogicalAdvancements.getLength()-1 ],
300                                               nBelowBaseline );
301         }
302         else
303         {
304             return geometry::RealRectangle2D( 0, nAboveBaseline,
305                                               aVirtualDevice.GetTextWidth(
306                                                   rText.Text,
307                                                   ::canvas::tools::numeric_cast<sal_uInt16>(rText.StartPosition),
308                                                   ::canvas::tools::numeric_cast<sal_uInt16>(rText.Length) ),
309                                               nBelowBaseline );
310         }
311 	}
312 }
313 
314 
315 // eof
316