GPU-Calc: remove Alloc_Host_Ptr for clmem of NAN vector
[LibreOffice.git] / oox / source / ppt / presentationfragmenthandler.cxx
blob826130cf65a9377f83728964a89765390a410e54
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "comphelper/anytostring.hxx"
21 #include "cppuhelper/exc_hlp.hxx"
22 #include <tools/multisel.hxx>
24 #include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
25 #include <com/sun/star/drawing/XDrawPages.hpp>
26 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
27 #include <com/sun/star/drawing/XMasterPageTarget.hpp>
28 #include <com/sun/star/xml/dom/XDocument.hpp>
29 #include <com/sun/star/xml/sax/XFastSAXSerializable.hpp>
30 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
31 #include <com/sun/star/style/XStyle.hpp>
32 #include <com/sun/star/presentation/XPresentationPage.hpp>
33 #include <com/sun/star/task/XStatusIndicator.hpp>
35 #include "oox/drawingml/theme.hxx"
36 #include "oox/drawingml/drawingmltypes.hxx"
37 #include "oox/drawingml/themefragmenthandler.hxx"
38 #include "oox/drawingml/textliststylecontext.hxx"
39 #include "oox/ppt/pptshape.hxx"
40 #include "oox/ppt/presentationfragmenthandler.hxx"
41 #include "oox/ppt/slidefragmenthandler.hxx"
42 #include "oox/ppt/layoutfragmenthandler.hxx"
43 #include "oox/ppt/pptimport.hxx"
45 #include <com/sun/star/office/XAnnotation.hpp>
46 #include <com/sun/star/office/XAnnotationAccess.hpp>
48 using namespace ::com::sun::star;
49 using namespace ::oox::core;
50 using namespace ::oox::drawingml;
51 using namespace ::com::sun::star::uno;
52 using namespace ::com::sun::star::beans;
53 using namespace ::com::sun::star::drawing;
54 using namespace ::com::sun::star::presentation;
55 using namespace ::com::sun::star::xml::sax;
57 namespace oox { namespace ppt {
59 PresentationFragmentHandler::PresentationFragmentHandler( XmlFilterBase& rFilter, const OUString& rFragmentPath ) throw()
60 : FragmentHandler2( rFilter, rFragmentPath )
61 , mpTextListStyle( new TextListStyle )
62 , mbCommentAuthorsRead(false)
64 TextParagraphPropertiesVector& rParagraphDefaulsVector( mpTextListStyle->getListStyle() );
65 TextParagraphPropertiesVector::iterator aParagraphDefaultIter( rParagraphDefaulsVector.begin() );
66 while( aParagraphDefaultIter != rParagraphDefaulsVector.end() )
68 // ppt is having zero bottom margin per default, whereas OOo is 0,5cm,
69 // so this attribute needs to be set always
70 (*aParagraphDefaultIter++)->getParaBottomMargin() = TextSpacing( 0 );
74 PresentationFragmentHandler::~PresentationFragmentHandler() throw()
78 void ResolveTextFields( XmlFilterBase& rFilter )
80 const oox::core::TextFieldStack& rTextFields = rFilter.getTextFieldStack();
81 if ( rTextFields.size() )
83 Reference< frame::XModel > xModel( rFilter.getModel() );
84 oox::core::TextFieldStack::const_iterator aIter( rTextFields.begin() );
85 while( aIter != rTextFields.end() )
87 const OUString sURL = "URL";
88 Reference< drawing::XDrawPagesSupplier > xDPS( xModel, uno::UNO_QUERY_THROW );
89 Reference< drawing::XDrawPages > xDrawPages( xDPS->getDrawPages(), uno::UNO_QUERY_THROW );
91 const oox::core::TextField& rTextField( *aIter++ );
92 Reference< XPropertySet > xPropSet( rTextField.xTextField, UNO_QUERY );
93 Reference< XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() );
94 if ( xPropSetInfo->hasPropertyByName( sURL ) )
96 OUString aURL;
97 if ( xPropSet->getPropertyValue( sURL ) >>= aURL )
99 const OUString sSlide = "#Slide ";
100 const OUString sNotes = "#Notes ";
101 sal_Bool bNotes = sal_False;
102 sal_Int32 nPageNumber = 0;
103 if ( aURL.match( sSlide ) )
104 nPageNumber = aURL.copy( sSlide.getLength() ).toInt32();
105 else if ( aURL.match( sNotes ) )
107 nPageNumber = aURL.copy( sNotes.getLength() ).toInt32();
108 bNotes = sal_True;
110 if ( nPageNumber )
114 Reference< XDrawPage > xDrawPage;
115 xDrawPages->getByIndex( nPageNumber - 1 ) >>= xDrawPage;
116 if ( bNotes )
118 Reference< ::com::sun::star::presentation::XPresentationPage > xPresentationPage( xDrawPage, UNO_QUERY_THROW );
119 xDrawPage = xPresentationPage->getNotesPage();
121 Reference< container::XNamed > xNamed( xDrawPage, UNO_QUERY_THROW );
122 aURL = "#" + xNamed->getName();
123 xPropSet->setPropertyValue( sURL, Any( aURL ) );
124 Reference< text::XTextContent > xContent( rTextField.xTextField, UNO_QUERY);
125 Reference< text::XTextRange > xTextRange( rTextField.xTextCursor, UNO_QUERY );
126 rTextField.xText->insertTextContent( xTextRange, xContent, sal_True );
128 catch( uno::Exception& )
138 void PresentationFragmentHandler::importSlide(sal_uInt32 nSlide, sal_Bool bFirstPage, sal_Bool bImportNotesPage)
140 PowerPointImport& rFilter = dynamic_cast< PowerPointImport& >( getFilter() );
142 Reference< frame::XModel > xModel( rFilter.getModel() );
143 Reference< drawing::XDrawPage > xSlide;
145 // importing slide pages and its corresponding notes page
146 Reference< drawing::XDrawPagesSupplier > xDPS( xModel, uno::UNO_QUERY_THROW );
147 Reference< drawing::XDrawPages > xDrawPages( xDPS->getDrawPages(), uno::UNO_QUERY_THROW );
149 try {
151 if( bFirstPage )
152 xDrawPages->getByIndex( 0 ) >>= xSlide;
153 else
154 xSlide = xDrawPages->insertNewByIndex( xDrawPages->getCount() );
156 OUString aSlideFragmentPath = getFragmentPathFromRelId( maSlidesVector[ nSlide ] );
157 if( !aSlideFragmentPath.isEmpty() )
159 SlidePersistPtr pMasterPersistPtr;
160 SlidePersistPtr pSlidePersistPtr( new SlidePersist( rFilter, sal_False, sal_False, xSlide,
161 ShapePtr( new PPTShape( Slide, "com.sun.star.drawing.GroupShape" ) ), mpTextListStyle ) );
163 FragmentHandlerRef xSlideFragmentHandler( new SlideFragmentHandler( rFilter, aSlideFragmentPath, pSlidePersistPtr, Slide ) );
165 // importing the corresponding masterpage/layout
166 OUString aLayoutFragmentPath = xSlideFragmentHandler->getFragmentPathFromFirstType( CREATE_OFFICEDOC_RELATION_TYPE( "slideLayout" ) );
167 OUString aCommentFragmentPath = xSlideFragmentHandler->getFragmentPathFromFirstType( CREATE_OFFICEDOC_RELATION_TYPE( "comments" ) );
168 if ( !aLayoutFragmentPath.isEmpty() )
170 // importing layout
171 RelationsRef xLayoutRelations = rFilter.importRelations( aLayoutFragmentPath );
172 OUString aMasterFragmentPath = xLayoutRelations->getFragmentPathFromFirstType( CREATE_OFFICEDOC_RELATION_TYPE( "slideMaster" ) );
173 if( !aMasterFragmentPath.isEmpty() )
175 // check if the corresponding masterpage+layout has already been imported
176 std::vector< SlidePersistPtr >& rMasterPages( rFilter.getMasterPages() );
177 std::vector< SlidePersistPtr >::iterator aIter( rMasterPages.begin() );
178 while( aIter != rMasterPages.end() )
180 if ( ( (*aIter)->getPath() == aMasterFragmentPath ) && ( (*aIter)->getLayoutPath() == aLayoutFragmentPath ) )
182 pMasterPersistPtr = *aIter;
183 break;
185 ++aIter;
188 if ( !pMasterPersistPtr.get() )
189 { // masterpersist not found, we have to load it
190 Reference< drawing::XDrawPage > xMasterPage;
191 Reference< drawing::XMasterPagesSupplier > xMPS( xModel, uno::UNO_QUERY_THROW );
192 Reference< drawing::XDrawPages > xMasterPages( xMPS->getMasterPages(), uno::UNO_QUERY_THROW );
194 if( !(rFilter.getMasterPages().size() ))
195 xMasterPages->getByIndex( 0 ) >>= xMasterPage;
196 else
197 xMasterPage = xMasterPages->insertNewByIndex( xMasterPages->getCount() );
199 pMasterPersistPtr = SlidePersistPtr( new SlidePersist( rFilter, sal_True, sal_False, xMasterPage,
200 ShapePtr( new PPTShape( Master, "com.sun.star.drawing.GroupShape" ) ), mpTextListStyle ) );
201 pMasterPersistPtr->setLayoutPath( aLayoutFragmentPath );
202 rFilter.getMasterPages().push_back( pMasterPersistPtr );
203 rFilter.setActualSlidePersist( pMasterPersistPtr );
204 FragmentHandlerRef xMasterFragmentHandler( new SlideFragmentHandler( rFilter, aMasterFragmentPath, pMasterPersistPtr, Master ) );
206 // set the correct theme
207 OUString aThemeFragmentPath = xMasterFragmentHandler->getFragmentPathFromFirstType( CREATE_OFFICEDOC_RELATION_TYPE( "theme" ) );
208 if( !aThemeFragmentPath.isEmpty() )
210 std::map< OUString, oox::drawingml::ThemePtr >& rThemes( rFilter.getThemes() );
211 std::map< OUString, oox::drawingml::ThemePtr >::iterator aIter2( rThemes.find( aThemeFragmentPath ) );
212 if( aIter2 == rThemes.end() )
214 oox::drawingml::ThemePtr pThemePtr( new oox::drawingml::Theme() );
215 pMasterPersistPtr->setTheme( pThemePtr );
216 Reference<xml::dom::XDocument> xDoc=
217 rFilter.importFragment(aThemeFragmentPath);
219 rFilter.importFragment(
220 new ThemeFragmentHandler(
221 rFilter, aThemeFragmentPath, *pThemePtr ),
222 Reference<xml::sax::XFastSAXSerializable>(
223 xDoc,
224 UNO_QUERY_THROW));
225 rThemes[ aThemeFragmentPath ] = pThemePtr;
226 pThemePtr->setFragment(xDoc);
228 else
230 pMasterPersistPtr->setTheme( (*aIter2).second );
233 importSlide( xMasterFragmentHandler, pMasterPersistPtr );
234 rFilter.importFragment( new LayoutFragmentHandler( rFilter, aLayoutFragmentPath, pMasterPersistPtr ) );
235 pMasterPersistPtr->createBackground( rFilter );
236 pMasterPersistPtr->createXShapes( rFilter );
241 // importing slide page
242 if (pMasterPersistPtr.get()) {
243 pSlidePersistPtr->setMasterPersist( pMasterPersistPtr );
244 pSlidePersistPtr->setTheme( pMasterPersistPtr->getTheme() );
245 Reference< drawing::XMasterPageTarget > xMasterPageTarget( pSlidePersistPtr->getPage(), UNO_QUERY );
246 if( xMasterPageTarget.is() )
247 xMasterPageTarget->setMasterPage( pMasterPersistPtr->getPage() );
249 rFilter.getDrawPages().push_back( pSlidePersistPtr );
250 rFilter.setActualSlidePersist( pSlidePersistPtr );
251 importSlide( xSlideFragmentHandler, pSlidePersistPtr );
252 pSlidePersistPtr->createBackground( rFilter );
253 pSlidePersistPtr->createXShapes( rFilter );
255 if(bImportNotesPage) {
257 // now importing the notes page
258 OUString aNotesFragmentPath = xSlideFragmentHandler->getFragmentPathFromFirstType( CREATE_OFFICEDOC_RELATION_TYPE( "notesSlide" ) );
259 if( !aNotesFragmentPath.isEmpty() )
261 Reference< XPresentationPage > xPresentationPage( xSlide, UNO_QUERY );
262 if ( xPresentationPage.is() )
264 Reference< XDrawPage > xNotesPage( xPresentationPage->getNotesPage() );
265 if ( xNotesPage.is() )
267 SlidePersistPtr pNotesPersistPtr( new SlidePersist( rFilter, sal_False, sal_True, xNotesPage,
268 ShapePtr( new PPTShape( Slide, "com.sun.star.drawing.GroupShape" ) ), mpTextListStyle ) );
269 FragmentHandlerRef xNotesFragmentHandler( new SlideFragmentHandler( getFilter(), aNotesFragmentPath, pNotesPersistPtr, Slide ) );
270 rFilter.getNotesPages().push_back( pNotesPersistPtr );
271 rFilter.setActualSlidePersist( pNotesPersistPtr );
272 importSlide( xNotesFragmentHandler, pNotesPersistPtr );
273 pNotesPersistPtr->createBackground( rFilter );
274 pNotesPersistPtr->createXShapes( rFilter );
280 if( !mbCommentAuthorsRead && !aCommentFragmentPath.isEmpty() )
282 // Comments are present and commentAuthors.xml has still not been read
283 mbCommentAuthorsRead = true;
284 OUString aCommentAuthorsFragmentPath = "ppt/commentAuthors.xml";
285 Reference< XPresentationPage > xPresentationPage( xSlide, UNO_QUERY );
286 Reference< XDrawPage > xCommentAuthorsPage( xPresentationPage->getNotesPage() );
287 SlidePersistPtr pCommentAuthorsPersistPtr(
288 new SlidePersist( rFilter, sal_False, sal_True, xCommentAuthorsPage,
289 ShapePtr(
290 new PPTShape(
291 Slide, "com.sun.star.drawing.GroupShape" ) ),
292 mpTextListStyle ) );
293 FragmentHandlerRef xCommentAuthorsFragmentHandler(
294 new SlideFragmentHandler( getFilter(),
295 aCommentAuthorsFragmentPath,
296 pCommentAuthorsPersistPtr,
297 Slide ) );
299 importSlide( xCommentAuthorsFragmentHandler, pCommentAuthorsPersistPtr );
300 maAuthorList.setValues( pCommentAuthorsPersistPtr->getCommentAuthors() );
302 if( !aCommentFragmentPath.isEmpty() )
304 Reference< XPresentationPage > xPresentationPage( xSlide, UNO_QUERY );
305 Reference< XDrawPage > xCommentsPage( xPresentationPage->getNotesPage() );
306 SlidePersistPtr pCommentsPersistPtr(
307 new SlidePersist(
308 rFilter, sal_False, sal_True, xCommentsPage,
309 ShapePtr(
310 new PPTShape(
311 Slide, "com.sun.star.drawing.GroupShape" ) ),
312 mpTextListStyle ) );
314 FragmentHandlerRef xCommentsFragmentHandler(
315 new SlideFragmentHandler(
316 getFilter(),
317 aCommentFragmentPath,
318 pCommentsPersistPtr,
319 Slide ) );
320 pCommentsPersistPtr->getCommentsList().cmLst.clear();
321 importSlide( xCommentsFragmentHandler, pCommentsPersistPtr );
323 //set comment chars for last comment on slide
324 SlideFragmentHandler* comment_handler =
325 dynamic_cast<SlideFragmentHandler*>(xCommentsFragmentHandler.get());
326 // some comments have no text -> set empty string as text to avoid
327 // crash (back() on empty vector is undefined) and losing other
328 // comment data that might be there (author, position, timestamp etc.)
329 pCommentsPersistPtr->getCommentsList().cmLst.back().setText(
330 comment_handler->getCharVector().empty() ? "" :
331 comment_handler->getCharVector().back() );
332 pCommentsPersistPtr->getCommentAuthors().setValues(maAuthorList);
334 //insert all comments from commentsList
335 for(int i=0; i<pCommentsPersistPtr->getCommentsList().getSize(); i++)
337 try {
338 Comment aComment = pCommentsPersistPtr->getCommentsList().getCommentAtIndex(i);
339 uno::Reference< office::XAnnotationAccess > xAnnotationAccess( xSlide, UNO_QUERY_THROW );
340 uno::Reference< office::XAnnotation > xAnnotation( xAnnotationAccess->createAndInsertAnnotation() );
341 int nPosX = aComment.getIntX();
342 int nPosY = aComment.getIntY();
343 xAnnotation->setPosition(
344 geometry::RealPoint2D(
345 ::oox::drawingml::convertEmuToHmm( nPosX ) * 15.87,
346 ::oox::drawingml::convertEmuToHmm( nPosY ) * 15.87 ) );
347 xAnnotation->setAuthor( aComment.getAuthor(maAuthorList) );
348 xAnnotation->setDateTime( aComment.getDateTime() );
349 uno::Reference< text::XText > xText( xAnnotation->getTextRange() );
350 xText->setString( aComment.get_text());
351 } catch( css::lang::IllegalArgumentException& ) {}
356 catch( uno::Exception& )
358 OSL_FAIL( OString("oox::ppt::PresentationFragmentHandler::EndDocument(), "
359 "exception caught: " +
360 OUStringToOString(
361 comphelper::anyToString( cppu::getCaughtException() ),
362 RTL_TEXTENCODING_UTF8 )).getStr() );
367 void PresentationFragmentHandler::finalizeImport()
369 PowerPointImport& rFilter = dynamic_cast< PowerPointImport& >( getFilter() );
371 sal_Int32 nPageCount = maSlidesVector.size();
373 // we will take the FilterData property "PageRange" if available, otherwise full range is used
374 comphelper::SequenceAsHashMap& rFilterData = rFilter.getFilterData();
376 // writing back the original PageCount of this document, it can be accessed from the XModel
377 // via getArgs after the import.
378 rFilterData["OriginalPageCount"] = makeAny(nPageCount);
379 sal_Bool bImportNotesPages = rFilterData.getUnpackedValueOrDefault("ImportNotesPages", sal_True);
380 OUString aPageRange = rFilterData.getUnpackedValueOrDefault("PageRange", OUString());
382 if( !aPageRange.getLength() )
384 aPageRange = OUStringBuffer()
385 .append( static_cast< sal_Int32 >( 1 ) )
386 .append( static_cast< sal_Unicode >( '-' ) )
387 .append( nPageCount ).makeStringAndClear();
390 StringRangeEnumerator aRangeEnumerator( aPageRange, 0, nPageCount - 1 );
391 StringRangeEnumerator::Iterator aIter = aRangeEnumerator.begin();
392 StringRangeEnumerator::Iterator aEnd = aRangeEnumerator.end();
393 if(aIter!=aEnd) {
395 // todo: localized progress bar text
396 const Reference< task::XStatusIndicator >& rxStatusIndicator( getFilter().getStatusIndicator() );
397 if ( rxStatusIndicator.is() )
398 rxStatusIndicator->start( OUString(), 10000 );
402 int nPagesImported = 0;
403 while (aIter!=aEnd)
405 if ( rxStatusIndicator.is() )
406 rxStatusIndicator->setValue((nPagesImported * 10000) / aRangeEnumerator.size());
408 importSlide(*aIter, !nPagesImported, bImportNotesPages);
409 nPagesImported++;
410 ++aIter;
412 ResolveTextFields( rFilter );
414 catch( uno::Exception& )
416 OSL_FAIL( OString("oox::ppt::PresentationFragmentHandler::finalizeImport(), "
417 "exception caught: " +
418 OUStringToOString(
419 comphelper::anyToString( cppu::getCaughtException() ),
420 RTL_TEXTENCODING_UTF8 )).getStr() );
422 // todo error handling;
423 if ( rxStatusIndicator.is() )
424 rxStatusIndicator->end();
428 // CT_Presentation
429 ::oox::core::ContextHandlerRef PresentationFragmentHandler::onCreateContext( sal_Int32 aElementToken, const AttributeList& rAttribs )
431 switch( aElementToken )
433 case PPT_TOKEN( presentation ):
434 case PPT_TOKEN( sldMasterIdLst ):
435 case PPT_TOKEN( notesMasterIdLst ):
436 case PPT_TOKEN( sldIdLst ):
437 return this;
438 case PPT_TOKEN( sldMasterId ):
439 maSlideMasterVector.push_back( rAttribs.getString( R_TOKEN( id ), OUString() ) );
440 return this;
441 case PPT_TOKEN( sldId ):
442 maSlidesVector.push_back( rAttribs.getString( R_TOKEN( id ), OUString() ) );
443 return this;
444 case PPT_TOKEN( notesMasterId ):
445 maNotesMasterVector.push_back( rAttribs.getString( R_TOKEN( id ), OUString() ) );
446 return this;
447 case PPT_TOKEN( sldSz ):
448 maSlideSize = GetSize2D( rAttribs.getFastAttributeList() );
449 return this;
450 case PPT_TOKEN( notesSz ):
451 maNotesSize = GetSize2D( rAttribs.getFastAttributeList() );
452 return this;
453 case PPT_TOKEN( custShowLst ):
454 return new CustomShowListContext( *this, maCustomShowList );
455 case PPT_TOKEN( defaultTextStyle ):
456 return new TextListStyleContext( *this, *mpTextListStyle );
458 return this;
461 bool PresentationFragmentHandler::importSlide( const FragmentHandlerRef& rxSlideFragmentHandler,
462 const SlidePersistPtr pSlidePersistPtr )
464 Reference< drawing::XDrawPage > xSlide( pSlidePersistPtr->getPage() );
465 SlidePersistPtr pMasterPersistPtr( pSlidePersistPtr->getMasterPersist() );
466 if ( pMasterPersistPtr.get() )
468 const OUString sLayout = "Layout";
469 uno::Reference< beans::XPropertySet > xSet( xSlide, uno::UNO_QUERY_THROW );
470 xSet->setPropertyValue( sLayout, Any( pMasterPersistPtr->getLayoutFromValueToken() ) );
472 while( xSlide->getCount() )
474 Reference< drawing::XShape > xShape;
475 xSlide->getByIndex(0) >>= xShape;
476 xSlide->remove( xShape );
479 Reference< XPropertySet > xPropertySet( xSlide, UNO_QUERY );
480 if ( xPropertySet.is() )
482 awt::Size& rPageSize( pSlidePersistPtr->isNotesPage() ? maNotesSize : maSlideSize );
483 xPropertySet->setPropertyValue( "Width", Any( rPageSize.Width ) );
484 xPropertySet->setPropertyValue( "Height", Any( rPageSize.Height ) );
486 oox::ppt::HeaderFooter aHeaderFooter( pSlidePersistPtr->getHeaderFooter() );
487 if ( !pSlidePersistPtr->isMasterPage() )
488 aHeaderFooter.mbSlideNumber = aHeaderFooter.mbHeader = aHeaderFooter.mbFooter = aHeaderFooter.mbDateTime = sal_False;
491 if ( pSlidePersistPtr->isNotesPage() )
492 xPropertySet->setPropertyValue( "IsHeaderVisible", Any( aHeaderFooter.mbHeader ) );
493 xPropertySet->setPropertyValue( "IsFooterVisible", Any( aHeaderFooter.mbFooter ) );
494 xPropertySet->setPropertyValue( "IsDateTimeVisible", Any( aHeaderFooter.mbDateTime ) );
495 xPropertySet->setPropertyValue( "IsPageNumberVisible", Any( aHeaderFooter.mbSlideNumber ) );
497 catch( uno::Exception& )
501 pSlidePersistPtr->setPath( rxSlideFragmentHandler->getFragmentPath() );
502 return getFilter().importFragment( rxSlideFragmentHandler );
507 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */