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