Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <vcl/outdev.hxx>
21 : #include <vcl/svapp.hxx>
22 : #include <vcl/graph.hxx>
23 : #include <vcl/metaact.hxx>
24 : #include <impgraph.hxx>
25 : #include <comphelper/processfactory.hxx>
26 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
27 : #include <com/sun/star/graphic/GraphicProvider.hpp>
28 : #include <com/sun/star/graphic/XGraphicProvider.hpp>
29 : #include <com/sun/star/lang/XUnoTunnel.hpp>
30 : #include <com/sun/star/lang/XTypeProvider.hpp>
31 : #include <com/sun/star/graphic/XGraphic.hpp>
32 :
33 : using namespace ::com::sun::star;
34 :
35 36 : static void ImplDrawDefault( OutputDevice* pOutDev, const OUString* pText,
36 : Font* pFont, const Bitmap* pBitmap, const BitmapEx* pBitmapEx,
37 : const Point& rDestPt, const Size& rDestSize )
38 : {
39 36 : sal_uInt16 nPixel = (sal_uInt16) pOutDev->PixelToLogic( Size( 1, 1 ) ).Width();
40 36 : sal_uInt16 nPixelWidth = nPixel;
41 36 : Point aPoint( rDestPt.X() + nPixelWidth, rDestPt.Y() + nPixelWidth );
42 36 : Size aSize( rDestSize.Width() - ( nPixelWidth << 1 ), rDestSize.Height() - ( nPixelWidth << 1 ) );
43 36 : bool bFilled = ( pBitmap != NULL || pBitmapEx != NULL || pFont != NULL );
44 36 : Rectangle aBorderRect( aPoint, aSize );
45 :
46 36 : pOutDev->Push();
47 :
48 36 : pOutDev->SetFillColor();
49 :
50 : // On the printer a black rectangle and on the screen one with 3D effect
51 36 : if ( pOutDev->GetOutDevType() == OUTDEV_PRINTER )
52 0 : pOutDev->SetLineColor( COL_BLACK );
53 : else
54 : {
55 36 : aBorderRect.Left() += nPixel;
56 36 : aBorderRect.Top() += nPixel;
57 :
58 36 : pOutDev->SetLineColor( COL_LIGHTGRAY );
59 36 : pOutDev->DrawRect( aBorderRect );
60 :
61 36 : aBorderRect.Left() -= nPixel;
62 36 : aBorderRect.Top() -= nPixel;
63 36 : aBorderRect.Right() -= nPixel;
64 36 : aBorderRect.Bottom() -= nPixel;
65 36 : pOutDev->SetLineColor( COL_GRAY );
66 : }
67 :
68 36 : pOutDev->DrawRect( aBorderRect );
69 :
70 36 : aPoint.X() += nPixelWidth + 2*nPixel;
71 36 : aPoint.Y() += nPixelWidth + 2*nPixel;
72 36 : aSize.Width() -= 2*nPixelWidth + 4*nPixel;
73 36 : aSize.Height() -= 2*nPixelWidth + 4*nPixel;
74 :
75 106 : if( aSize.Width() > 0 && aSize.Height() > 0
76 70 : && ( ( pBitmap && !!*pBitmap ) || ( pBitmapEx && !!*pBitmapEx ) ) )
77 : {
78 34 : Size aBitmapSize( pOutDev->PixelToLogic( pBitmap ? pBitmap->GetSizePixel() : pBitmapEx->GetSizePixel() ) );
79 :
80 34 : if( aSize.Height() > aBitmapSize.Height() && aSize.Width() > aBitmapSize.Width() )
81 : {
82 23 : if ( pBitmap )
83 0 : pOutDev->DrawBitmap( aPoint, *pBitmap );
84 : else
85 23 : pOutDev->DrawBitmapEx( aPoint, *pBitmapEx );
86 23 : aPoint.X() += aBitmapSize.Width() + 2*nPixel;
87 23 : aSize.Width() -= aBitmapSize.Width() + 2*nPixel;
88 : }
89 : }
90 :
91 106 : if ( aSize.Width() > 0 && aSize.Height() > 0 && pFont && pText && pText->getLength()
92 70 : && !(!pOutDev->IsOutputEnabled() /*&& pOutDev->GetConnectMetaFile() */) )
93 : {
94 34 : MapMode aMapMode( MAP_POINT );
95 34 : Size aSz = pOutDev->LogicToLogic( Size( 0, 12 ), &aMapMode, NULL );
96 34 : long nThreshold = aSz.Height() / 2;
97 34 : long nStep = nThreshold / 3;
98 :
99 34 : if ( !nStep )
100 0 : nStep = aSz.Height() - nThreshold;
101 :
102 33 : for(;; aSz.Height() -= nStep )
103 : {
104 67 : pFont->SetSize( aSz );
105 67 : pOutDev->SetFont( *pFont );
106 :
107 67 : long nTextHeight = pOutDev->GetTextHeight();
108 67 : long nTextWidth = pOutDev->GetTextWidth( *pText );
109 67 : if ( nTextHeight )
110 : {
111 : // The approximation does not respect imprecisions caused
112 : // by word wraps
113 67 : long nLines = aSize.Height() / nTextHeight;
114 67 : long nWidth = aSize.Width() * nLines; // Approximation!!!
115 :
116 67 : if ( nTextWidth <= nWidth || aSz.Height() <= nThreshold )
117 : {
118 34 : sal_uInt16 nStart = 0;
119 34 : sal_uInt16 nLen = 0;
120 :
121 68 : while( nStart < pText->getLength() && (*pText)[nStart] == ' ' )
122 0 : nStart++;
123 2079 : while( nStart+nLen < pText->getLength() && (*pText)[nStart+nLen] != ' ' )
124 2011 : nLen++;
125 102 : while( nStart < pText->getLength() && nLines-- )
126 : {
127 34 : sal_uInt16 nNext = nLen;
128 0 : do
129 : {
130 68 : while ( nStart+nNext < pText->getLength() && (*pText)[nStart+nNext] == ' ' )
131 0 : nNext++;
132 68 : while ( nStart+nNext < pText->getLength() && (*pText)[nStart+nNext] != ' ' )
133 0 : nNext++;
134 34 : nTextWidth = pOutDev->GetTextWidth( *pText, nStart, nNext );
135 34 : if ( nTextWidth > aSize.Width() )
136 34 : break;
137 0 : nLen = nNext;
138 : }
139 0 : while ( nStart+nNext < pText->getLength() );
140 :
141 34 : sal_uInt16 n = nLen;
142 34 : nTextWidth = pOutDev->GetTextWidth( *pText, nStart, n );
143 1641 : while( nTextWidth > aSize.Width() )
144 1573 : nTextWidth = pOutDev->GetTextWidth( *pText, nStart, --n );
145 34 : pOutDev->DrawText( aPoint, *pText, nStart, n );
146 :
147 34 : aPoint.Y() += nTextHeight;
148 34 : nStart = sal::static_int_cast<sal_uInt16>(nStart + nLen);
149 34 : nLen = nNext-nLen;
150 68 : while( nStart < pText->getLength() && (*pText)[nStart] == ' ' )
151 : {
152 0 : nStart++;
153 0 : nLen--;
154 : }
155 : }
156 34 : break;
157 : }
158 : }
159 : else
160 0 : break;
161 33 : }
162 : }
163 :
164 : // If the default graphic does not have content, we draw a red rectangle
165 36 : if( !bFilled )
166 : {
167 0 : aBorderRect.Left()++;
168 0 : aBorderRect.Top()++;
169 0 : aBorderRect.Right()--;
170 0 : aBorderRect.Bottom()--;
171 :
172 0 : pOutDev->SetLineColor( COL_LIGHTRED );
173 0 : pOutDev->DrawLine( aBorderRect.TopLeft(), aBorderRect.BottomRight() );
174 0 : pOutDev->DrawLine( aBorderRect.TopRight(), aBorderRect.BottomLeft() );
175 : }
176 :
177 36 : pOutDev->Pop();
178 36 : }
179 :
180 0 : TYPEINIT1_AUTOFACTORY( Graphic, SvDataCopyStream );
181 :
182 7486 : Graphic::Graphic()
183 : {
184 7486 : mpImpGraphic = new ImpGraphic;
185 7486 : }
186 :
187 48913 : Graphic::Graphic( const Graphic& rGraphic ) :
188 48913 : SvDataCopyStream()
189 : {
190 48913 : if( rGraphic.IsAnimated() )
191 0 : mpImpGraphic = new ImpGraphic( *rGraphic.mpImpGraphic );
192 : else
193 : {
194 48913 : mpImpGraphic = rGraphic.mpImpGraphic;
195 48913 : mpImpGraphic->mnRefCount++;
196 : }
197 48913 : }
198 :
199 144 : Graphic::Graphic( const Bitmap& rBmp )
200 : {
201 144 : mpImpGraphic = new ImpGraphic( rBmp );
202 144 : }
203 :
204 117032 : Graphic::Graphic( const BitmapEx& rBmpEx )
205 : {
206 117032 : mpImpGraphic = new ImpGraphic( rBmpEx );
207 117032 : }
208 :
209 292 : Graphic::Graphic(const SvgDataPtr& rSvgDataPtr)
210 : {
211 292 : mpImpGraphic = new ImpGraphic(rSvgDataPtr);
212 292 : }
213 :
214 7 : Graphic::Graphic( const Animation& rAnimation )
215 : {
216 7 : mpImpGraphic = new ImpGraphic( rAnimation );
217 7 : }
218 :
219 152 : Graphic::Graphic( const GDIMetaFile& rMtf )
220 : {
221 152 : mpImpGraphic = new ImpGraphic( rMtf );
222 152 : }
223 :
224 114298 : Graphic::Graphic( const ::com::sun::star::uno::Reference< ::com::sun::star::graphic::XGraphic >& rxGraphic )
225 : {
226 114298 : uno::Reference< lang::XUnoTunnel > xTunnel( rxGraphic, uno::UNO_QUERY );
227 228596 : uno::Reference< lang::XTypeProvider > xProv( rxGraphic, uno::UNO_QUERY );
228 157405 : const ::Graphic* pGraphic = ( ( xTunnel.is() && xProv.is() ) ?
229 243619 : reinterpret_cast< ::Graphic* >( xTunnel->getSomething( xProv->getImplementationId() ) ) :
230 386001 : NULL );
231 :
232 114298 : if( pGraphic )
233 : {
234 43107 : if( pGraphic->IsAnimated() )
235 0 : mpImpGraphic = new ImpGraphic( *pGraphic->mpImpGraphic );
236 : else
237 : {
238 43107 : mpImpGraphic = pGraphic->mpImpGraphic;
239 43107 : mpImpGraphic->mnRefCount++;
240 : }
241 : }
242 : else
243 185489 : mpImpGraphic = new ImpGraphic;
244 114298 : }
245 :
246 622700 : Graphic::~Graphic()
247 : {
248 288239 : if( mpImpGraphic->mnRefCount == 1UL )
249 194789 : delete mpImpGraphic;
250 : else
251 93450 : mpImpGraphic->mnRefCount--;
252 334461 : }
253 :
254 6829 : void Graphic::ImplTestRefCount()
255 : {
256 6829 : if( mpImpGraphic->mnRefCount > 1UL )
257 : {
258 245 : mpImpGraphic->mnRefCount--;
259 245 : mpImpGraphic = new ImpGraphic( *mpImpGraphic );
260 : }
261 6829 : }
262 :
263 2896 : Graphic& Graphic::operator=( const Graphic& rGraphic )
264 : {
265 2896 : if( &rGraphic != this )
266 : {
267 2896 : if( rGraphic.IsAnimated() )
268 : {
269 7 : if( mpImpGraphic->mnRefCount == 1UL )
270 7 : delete mpImpGraphic;
271 : else
272 0 : mpImpGraphic->mnRefCount--;
273 :
274 7 : mpImpGraphic = new ImpGraphic( *rGraphic.mpImpGraphic );
275 : }
276 : else
277 : {
278 2889 : rGraphic.mpImpGraphic->mnRefCount++;
279 :
280 2889 : if( mpImpGraphic->mnRefCount == 1UL )
281 1678 : delete mpImpGraphic;
282 : else
283 1211 : mpImpGraphic->mnRefCount--;
284 :
285 2889 : mpImpGraphic = rGraphic.mpImpGraphic;
286 : }
287 : }
288 :
289 2896 : return *this;
290 : }
291 :
292 1457 : sal_Bool Graphic::operator==( const Graphic& rGraphic ) const
293 : {
294 1457 : return( *mpImpGraphic == *rGraphic.mpImpGraphic );
295 : }
296 :
297 0 : sal_Bool Graphic::operator!=( const Graphic& rGraphic ) const
298 : {
299 0 : return( *mpImpGraphic != *rGraphic.mpImpGraphic );
300 : }
301 :
302 0 : sal_Bool Graphic::operator!() const
303 : {
304 0 : return( GRAPHIC_NONE == mpImpGraphic->ImplGetType() );
305 : }
306 :
307 0 : void Graphic::Load( SvStream& rIStm )
308 : {
309 0 : rIStm >> *this;
310 0 : }
311 :
312 0 : void Graphic::Save( SvStream& rOStm )
313 : {
314 0 : rOStm << *this;
315 0 : }
316 :
317 0 : void Graphic::Assign( const SvDataCopyStream& rCopyStream )
318 : {
319 0 : *this = (const Graphic& ) rCopyStream;
320 0 : }
321 :
322 33 : void Graphic::Clear()
323 : {
324 33 : ImplTestRefCount();
325 33 : mpImpGraphic->ImplClear();
326 33 : }
327 :
328 136820 : GraphicType Graphic::GetType() const
329 : {
330 136820 : return mpImpGraphic->ImplGetType();
331 : }
332 :
333 15 : void Graphic::SetDefaultType()
334 : {
335 15 : ImplTestRefCount();
336 15 : mpImpGraphic->ImplSetDefaultType();
337 15 : }
338 :
339 44 : sal_Bool Graphic::IsSupportedGraphic() const
340 : {
341 44 : return mpImpGraphic->ImplIsSupportedGraphic();
342 : }
343 :
344 5344 : sal_Bool Graphic::IsTransparent() const
345 : {
346 5344 : return mpImpGraphic->ImplIsTransparent();
347 : }
348 :
349 5343 : sal_Bool Graphic::IsAlpha() const
350 : {
351 5343 : return mpImpGraphic->ImplIsAlpha();
352 : }
353 :
354 101487 : sal_Bool Graphic::IsAnimated() const
355 : {
356 101487 : return mpImpGraphic->ImplIsAnimated();
357 : }
358 :
359 5220 : sal_Bool Graphic::IsEPS() const
360 : {
361 5220 : return mpImpGraphic->ImplIsEPS();
362 : }
363 :
364 2 : Bitmap Graphic::GetBitmap(const GraphicConversionParameters& rParameters) const
365 : {
366 2 : return mpImpGraphic->ImplGetBitmap(rParameters);
367 : }
368 :
369 116076 : BitmapEx Graphic::GetBitmapEx(const GraphicConversionParameters& rParameters) const
370 : {
371 116076 : return mpImpGraphic->ImplGetBitmapEx(rParameters);
372 : }
373 :
374 0 : Animation Graphic::GetAnimation() const
375 : {
376 0 : return mpImpGraphic->ImplGetAnimation();
377 : }
378 :
379 336 : const GDIMetaFile& Graphic::GetGDIMetaFile() const
380 : {
381 336 : return mpImpGraphic->ImplGetGDIMetaFile();
382 : }
383 :
384 115214 : uno::Reference< graphic::XGraphic > Graphic::GetXGraphic() const
385 : {
386 115214 : uno::Reference< graphic::XGraphic > xRet;
387 :
388 115214 : if( GetType() != GRAPHIC_NONE )
389 : {
390 43259 : uno::Reference < uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
391 86518 : uno::Reference< graphic::XGraphicProvider > xProv( graphic::GraphicProvider::create( xContext ) );
392 :
393 86518 : uno::Sequence< beans::PropertyValue > aLoadProps( 1 );
394 86518 : OUString aURL( "private:memorygraphic/" );
395 :
396 43259 : aLoadProps[ 0 ].Name = OUString( "URL" );
397 43259 : aLoadProps[ 0 ].Value <<= ( aURL += OUString::valueOf( reinterpret_cast< sal_Int64 >( this ) ) );
398 :
399 86518 : xRet = xProv->queryGraphic( aLoadProps );
400 : }
401 :
402 115214 : return xRet;
403 : }
404 :
405 7674 : Size Graphic::GetPrefSize() const
406 : {
407 7674 : return mpImpGraphic->ImplGetPrefSize();
408 : }
409 :
410 1343 : void Graphic::SetPrefSize( const Size& rPrefSize )
411 : {
412 1343 : ImplTestRefCount();
413 1343 : mpImpGraphic->ImplSetPrefSize( rPrefSize );
414 1343 : }
415 :
416 7332 : MapMode Graphic::GetPrefMapMode() const
417 : {
418 7332 : return mpImpGraphic->ImplGetPrefMapMode();
419 : }
420 :
421 1100 : void Graphic::SetPrefMapMode( const MapMode& rPrefMapMode )
422 : {
423 1100 : ImplTestRefCount();
424 1100 : mpImpGraphic->ImplSetPrefMapMode( rPrefMapMode );
425 1100 : }
426 :
427 134 : Size Graphic::GetSizePixel( const OutputDevice* pRefDevice ) const
428 : {
429 134 : Size aRet;
430 :
431 134 : if( GRAPHIC_BITMAP == mpImpGraphic->ImplGetType() )
432 134 : aRet = mpImpGraphic->ImplGetBitmapEx(GraphicConversionParameters()).GetSizePixel();
433 : else
434 0 : aRet = ( pRefDevice ? pRefDevice : Application::GetDefaultDevice() )->LogicToPixel( GetPrefSize(), GetPrefMapMode() );
435 :
436 134 : return aRet;
437 : }
438 :
439 5226 : sal_uLong Graphic::GetSizeBytes() const
440 : {
441 5226 : return mpImpGraphic->ImplGetSizeBytes();
442 : }
443 :
444 0 : void Graphic::Draw( OutputDevice* pOutDev, const Point& rDestPt ) const
445 : {
446 0 : mpImpGraphic->ImplDraw( pOutDev, rDestPt );
447 0 : }
448 :
449 38 : void Graphic::Draw( OutputDevice* pOutDev,
450 : const Point& rDestPt, const Size& rDestSz ) const
451 : {
452 38 : if( GRAPHIC_DEFAULT == mpImpGraphic->ImplGetType() )
453 0 : ImplDrawDefault( pOutDev, NULL, NULL, NULL, NULL, rDestPt, rDestSz );
454 : else
455 38 : mpImpGraphic->ImplDraw( pOutDev, rDestPt, rDestSz );
456 38 : }
457 :
458 36 : void Graphic::DrawEx( OutputDevice* pOutDev, const OUString& rText,
459 : Font& rFont, const BitmapEx& rBitmap,
460 : const Point& rDestPt, const Size& rDestSz )
461 : {
462 36 : ImplDrawDefault( pOutDev, &rText, &rFont, NULL, &rBitmap, rDestPt, rDestSz );
463 36 : }
464 :
465 0 : void Graphic::StartAnimation( OutputDevice* pOutDev, const Point& rDestPt,
466 : const Size& rDestSz, long nExtraData,
467 : OutputDevice* pFirstFrameOutDev )
468 : {
469 0 : ImplTestRefCount();
470 0 : mpImpGraphic->ImplStartAnimation( pOutDev, rDestPt, rDestSz, nExtraData, pFirstFrameOutDev );
471 0 : }
472 :
473 0 : void Graphic::StopAnimation( OutputDevice* pOutDev, long nExtraData )
474 : {
475 0 : ImplTestRefCount();
476 0 : mpImpGraphic->ImplStopAnimation( pOutDev, nExtraData );
477 0 : }
478 :
479 1066 : void Graphic::SetAnimationNotifyHdl( const Link& rLink )
480 : {
481 1066 : mpImpGraphic->ImplSetAnimationNotifyHdl( rLink );
482 1066 : }
483 :
484 1329 : Link Graphic::GetAnimationNotifyHdl() const
485 : {
486 1329 : return mpImpGraphic->ImplGetAnimationNotifyHdl();
487 : }
488 :
489 0 : sal_uLong Graphic::GetAnimationLoopCount() const
490 : {
491 0 : return mpImpGraphic->ImplGetAnimationLoopCount();
492 : }
493 :
494 5695 : GraphicReader* Graphic::GetContext()
495 : {
496 5695 : return mpImpGraphic->ImplGetContext();
497 : }
498 :
499 58 : void Graphic::SetContext( GraphicReader* pReader )
500 : {
501 58 : mpImpGraphic->ImplSetContext( pReader );
502 58 : }
503 :
504 1066 : void Graphic::SetDocFileName( const String& rName, sal_uLong nFilePos )
505 : {
506 1066 : mpImpGraphic->ImplSetDocFileName( rName, nFilePos );
507 1066 : }
508 :
509 1329 : const String& Graphic::GetDocFileName() const
510 : {
511 1329 : return mpImpGraphic->ImplGetDocFileName();
512 : }
513 :
514 1329 : sal_uLong Graphic::GetDocFilePos() const
515 : {
516 1329 : return mpImpGraphic->ImplGetDocFilePos();
517 : }
518 :
519 11 : sal_Bool Graphic::SwapOut()
520 : {
521 11 : ImplTestRefCount();
522 11 : return mpImpGraphic->ImplSwapOut();
523 : }
524 :
525 0 : sal_Bool Graphic::SwapOut( SvStream* pOStream )
526 : {
527 0 : ImplTestRefCount();
528 0 : return mpImpGraphic->ImplSwapOut( pOStream );
529 : }
530 :
531 8 : sal_Bool Graphic::SwapIn()
532 : {
533 8 : ImplTestRefCount();
534 8 : return mpImpGraphic->ImplSwapIn();
535 : }
536 :
537 0 : sal_Bool Graphic::SwapIn( SvStream* pStrm )
538 : {
539 0 : ImplTestRefCount();
540 0 : return mpImpGraphic->ImplSwapIn( pStrm );
541 : }
542 :
543 9004 : sal_Bool Graphic::IsSwapOut() const
544 : {
545 9004 : return mpImpGraphic->ImplIsSwapOut();
546 : }
547 :
548 1628 : void Graphic::SetLink( const GfxLink& rGfxLink )
549 : {
550 1628 : ImplTestRefCount();
551 1628 : mpImpGraphic->ImplSetLink( rGfxLink );
552 1628 : }
553 :
554 239 : GfxLink Graphic::GetLink() const
555 : {
556 239 : return mpImpGraphic->ImplGetLink();
557 : }
558 :
559 8728 : sal_Bool Graphic::IsLink() const
560 : {
561 8728 : return mpImpGraphic->ImplIsLink();
562 : }
563 :
564 722 : sal_uLong Graphic::GetChecksum() const
565 : {
566 722 : return mpImpGraphic->ImplGetChecksum();
567 : }
568 :
569 156 : sal_Bool Graphic::ExportNative( SvStream& rOStream ) const
570 : {
571 156 : return mpImpGraphic->ImplExportNative( rOStream );
572 : }
573 :
574 2691 : SvStream& operator>>( SvStream& rIStream, Graphic& rGraphic )
575 : {
576 2691 : rGraphic.ImplTestRefCount();
577 2691 : return rIStream >> *rGraphic.mpImpGraphic;
578 : }
579 :
580 6 : SvStream& operator<<( SvStream& rOStream, const Graphic& rGraphic )
581 : {
582 6 : return rOStream << *rGraphic.mpImpGraphic;
583 : }
584 :
585 1422 : const SvgDataPtr& Graphic::getSvgData() const
586 : {
587 1422 : return mpImpGraphic->getSvgData();
588 465 : }
589 :
590 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|