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 #ifndef INCLUDED_WRITERFILTER_SOURCE_OOXML_OOXMLFASTCONTEXTHANDLER_HXX 21 #define INCLUDED_WRITERFILTER_SOURCE_OOXML_OOXMLFASTCONTEXTHANDLER_HXX 22 23 #include <set> 24 #include <cppuhelper/implbase.hxx> 25 #include <com/sun/star/uno/XComponentContext.hpp> 26 #include <com/sun/star/xml/sax/XFastContextHandler.hpp> 27 #include <com/sun/star/xml/sax/XFastShapeContextHandler.hpp> 28 #include <oox/mathml/import.hxx> 29 #include <oox/mathml/importutils.hxx> 30 #include "OOXMLParserState.hxx" 31 #include "OOXMLPropertySet.hxx" 32 33 namespace writerfilter { 34 namespace ooxml 35 { 36 class OOXMLDocumentImpl; 37 38 class OOXMLFastContextHandler: public ::cppu::WeakImplHelper<css::xml::sax::XFastContextHandler> 39 { 40 public: 41 typedef tools::SvRef<OOXMLFastContextHandler> Pointer_t; 42 43 enum ResourceEnum_t { UNKNOWN, STREAM, PROPERTIES, TABLE, SHAPE }; 44 45 explicit OOXMLFastContextHandler(css::uno::Reference< css::uno::XComponentContext > const & context); 46 47 explicit OOXMLFastContextHandler(OOXMLFastContextHandler * pContext); 48 49 OOXMLFastContextHandler(OOXMLFastContextHandler const &) = default; 50 51 virtual ~OOXMLFastContextHandler() override; 52 53 // css::xml::sax::XFastContextHandler: 54 virtual void SAL_CALL startFastElement (sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs) override final; 55 56 virtual void SAL_CALL startUnknownElement(const OUString & Namespace, const OUString & Name, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 57 58 virtual void SAL_CALL endFastElement(sal_Int32 Element) override; 59 60 virtual void SAL_CALL endUnknownElement(const OUString & Namespace, const OUString & Name) override; 61 62 virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(sal_Int32 Element, 63 const css::uno::Reference<css::xml::sax::XFastAttributeList>& Attribs) override; 64 65 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createUnknownChildContext(const OUString & Namespace, const OUString & Name, 66 const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 67 68 virtual void SAL_CALL characters(const OUString & aChars) override; 69 70 // local 71 72 void setStream(Stream * pStream); 73 74 /** 75 Return value of this context(element). 76 77 @return the value 78 */ 79 virtual OOXMLValue::Pointer_t getValue() const; 80 81 /** 82 Returns a string describing the type of the context. 83 84 This is the name of the define normally. 85 86 @return type string 87 */ getType() const88 virtual std::string getType() const { return "??"; } 89 getResource() const90 virtual ResourceEnum_t getResource() const { return STREAM; } 91 92 /// @throws css::uno::RuntimeException 93 /// @throws css::xml::sax::SAXException 94 virtual void attributes(const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs); 95 96 virtual void newProperty(Id aId, const OOXMLValue::Pointer_t& pVal); 97 virtual void setPropertySet(const OOXMLPropertySet::Pointer_t& pPropertySet); 98 virtual OOXMLPropertySet::Pointer_t getPropertySet() const; 99 100 virtual void setToken(Token_t nToken); 101 virtual Token_t getToken() const; 102 103 void resolveFootnote(const sal_Int32 nId); 104 void resolveEndnote(const sal_Int32 nId); 105 void resolveComment(const sal_Int32 nId); 106 void resolvePicture(const OUString & rId); 107 void resolveHeader(const sal_Int32 type, 108 const OUString & rId); 109 void resolveFooter(const sal_Int32 type, 110 const OUString & rId); 111 void resolveData(const OUString & rId); 112 113 OUString getTargetForId(const OUString & rId); 114 115 void setDocument(OOXMLDocumentImpl* pDocument); 116 OOXMLDocumentImpl* getDocument(); 117 void setXNoteId(const OOXMLValue::Pointer_t& pValue); 118 void setXNoteId(const sal_Int32 nId); 119 sal_Int32 getXNoteId() const; 120 void setForwardEvents(bool bForwardEvents); 121 bool isForwardEvents() const; 122 virtual void setId(Id nId); 123 virtual Id getId() const; 124 125 void setDefine(Id nDefine); getDefine() const126 Id getDefine() const { return mnDefine;} 127 getParserState() const128 const OOXMLParserState::Pointer_t& getParserState() const { return mpParserState;} 129 130 void sendTableDepth() const; 131 void setHandle(); 132 133 void startSectionGroup(); 134 void setLastParagraphInSection(); 135 void setLastSectionGroup(); 136 void endSectionGroup(); 137 void startParagraphGroup(); 138 void endParagraphGroup(); 139 void startCharacterGroup(); 140 void endCharacterGroup(); 141 virtual void pushBiDiEmbedLevel(); 142 virtual void popBiDiEmbedLevel(); 143 void startSdt(); 144 void endSdt(); 145 146 void startField(); 147 void fieldSeparator(); 148 void endField(); 149 void lockField(); 150 void ftnednref(); 151 void ftnedncont(); 152 void ftnednsep(); 153 void pgNum(); 154 void tab(); 155 void symbol(); 156 void cr(); 157 void noBreakHyphen(); 158 void softHyphen(); 159 void handleLastParagraphInSection(); 160 void endOfParagraph(); 161 void text(const OUString & sText); 162 void positionOffset(const OUString & sText); 163 static void ignore(); 164 void alignH(const OUString & sText); 165 void alignV(const OUString & sText); 166 void positivePercentage(const OUString& rText); 167 void startGlossaryEntry(); 168 void endGlossaryEntry(); 169 void startTxbxContent(); 170 void endTxbxContent(); 171 void propagateCharacterProperties(); 172 void propagateTableProperties(); 173 void propagateRowProperties(); 174 void propagateCellProperties(); 175 void sendPropertiesWithId(Id nId); 176 void sendPropertiesToParent(); 177 void sendCellProperties(); 178 void sendRowProperties(); 179 void sendTableProperties(); 180 void clearTableProps(); 181 void clearProps(); 182 183 virtual void setDefaultBooleanValue(); 184 virtual void setDefaultIntegerValue(); 185 virtual void setDefaultHexValue(); 186 virtual void setDefaultStringValue(); 187 188 void sendPropertyToParent(); getParent() const189 OOXMLFastContextHandler* getParent() const { return mpParent; } setGridAfter(const OOXMLValue::Pointer_t & pGridAfter)190 void setGridAfter(const OOXMLValue::Pointer_t& pGridAfter) { mpGridAfter = pGridAfter; } 191 192 protected: 193 OOXMLFastContextHandler * mpParent; 194 Id mId; 195 Id mnDefine; 196 Token_t mnToken; 197 198 // the stream to send the stream events to. 199 Stream * mpStream; 200 201 // the current global parser state 202 OOXMLParserState::Pointer_t mpParserState; 203 204 // the table depth of this context 205 unsigned int mnTableDepth; 206 207 /// @throws css::uno::RuntimeException 208 /// @throws css::xml::sax::SAXException 209 virtual void lcl_startFastElement(Token_t Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs); 210 211 /// @throws css::uno::RuntimeException 212 /// @throws css::xml::sax::SAXException 213 virtual void lcl_endFastElement(Token_t Element); 214 215 /// @throws css::uno::RuntimeException 216 /// @throws css::xml::sax::SAXException 217 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > lcl_createFastChildContext(Token_t Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs); 218 219 /// @throws css::uno::RuntimeException 220 /// @throws css::xml::sax::SAXException 221 virtual void lcl_characters(const OUString & aChars); 222 223 void startAction(); 224 void endAction(); 225 getComponentContext() const226 const css::uno::Reference< css::uno::XComponentContext >& getComponentContext() const { return m_xContext;} 227 228 bool inPositionV; 229 bool mbLayoutInCell; // o:allowincell 230 OOXMLValue::Pointer_t mpGridAfter; 231 232 private: 233 void operator =(OOXMLFastContextHandler const &) = delete; 234 /// Handles AlternateContent. Returns true, if children of the current element should be ignored. 235 bool prepareMceContext(Token_t nElement, const css::uno::Reference<css::xml::sax::XFastAttributeList>& Attribs); 236 237 // 2.10 of XML 1.0 specification 238 bool IsPreserveSpace() const; 239 240 css::uno::Reference< css::uno::XComponentContext > m_xContext; 241 bool m_bDiscardChildren; 242 bool m_bTookChoice; ///< Did we take the Choice or want Fallback instead? 243 bool mbPreserveSpace = false; 244 bool mbPreserveSpaceSet = false; 245 246 }; 247 248 class OOXMLFastContextHandlerStream : public OOXMLFastContextHandler 249 { 250 public: 251 explicit OOXMLFastContextHandlerStream(OOXMLFastContextHandler * pContext); 252 virtual ~OOXMLFastContextHandlerStream() override; 253 getResource() const254 virtual ResourceEnum_t getResource() const override { return STREAM; } 255 getPropertySetAttrs() const256 const OOXMLPropertySet::Pointer_t& getPropertySetAttrs() const { return mpPropertySetAttrs;} 257 258 virtual void newProperty(Id aId, const OOXMLValue::Pointer_t& pVal) override; 259 void sendProperty(Id nId); 260 virtual OOXMLPropertySet::Pointer_t getPropertySet() const override; 261 262 void handleHyperlink(); 263 264 private: 265 mutable OOXMLPropertySet::Pointer_t mpPropertySetAttrs; 266 }; 267 268 class OOXMLFastContextHandlerProperties : public OOXMLFastContextHandler 269 { 270 public: 271 explicit OOXMLFastContextHandlerProperties(OOXMLFastContextHandler * pContext); 272 virtual ~OOXMLFastContextHandlerProperties() override; 273 274 virtual OOXMLValue::Pointer_t getValue() const override; getResource() const275 virtual ResourceEnum_t getResource() const override { return PROPERTIES; } 276 277 virtual void newProperty(Id nId, const OOXMLValue::Pointer_t& pVal) override; 278 279 void handleXNotes(); 280 void handleHdrFtr(); 281 void handleComment(); 282 void handlePicture(); 283 void handleBreak(); 284 void handleOutOfOrderBreak(); 285 void handleOLE(); 286 void handleFontRel(); 287 void handleHyperlinkURL(); 288 289 virtual void setPropertySet(const OOXMLPropertySet::Pointer_t& pPropertySet) override; 290 virtual OOXMLPropertySet::Pointer_t getPropertySet() const override; 291 292 protected: 293 /// the properties 294 OOXMLPropertySet::Pointer_t mpPropertySet; 295 296 virtual void lcl_endFastElement(Token_t Element) override; 297 298 private: 299 300 bool mbResolve; 301 }; 302 303 class OOXMLFastContextHandlerPropertyTable : 304 public OOXMLFastContextHandlerProperties 305 { 306 public: 307 explicit OOXMLFastContextHandlerPropertyTable(OOXMLFastContextHandler * pContext); 308 virtual ~OOXMLFastContextHandlerPropertyTable() override; 309 310 private: 311 OOXMLTable mTable; 312 313 virtual void lcl_endFastElement(Token_t Element) override; 314 }; 315 316 class OOXMLFastContextHandlerValue : 317 public OOXMLFastContextHandler 318 { 319 public: 320 explicit OOXMLFastContextHandlerValue(OOXMLFastContextHandler * pContext); 321 virtual ~OOXMLFastContextHandlerValue() override; 322 323 void setValue(const OOXMLValue::Pointer_t& pValue); 324 virtual OOXMLValue::Pointer_t getValue() const override; 325 326 virtual void lcl_endFastElement(Token_t Element) override; 327 getType() const328 virtual std::string getType() const override { return "Value"; } 329 330 virtual void setDefaultBooleanValue() override; 331 virtual void setDefaultIntegerValue() override; 332 virtual void setDefaultHexValue() override; 333 virtual void setDefaultStringValue() override; 334 335 virtual void pushBiDiEmbedLevel() override; 336 virtual void popBiDiEmbedLevel() override; 337 338 private: 339 OOXMLValue::Pointer_t mpValue; 340 }; 341 342 class OOXMLFastContextHandlerTable : public OOXMLFastContextHandler 343 { 344 public: 345 explicit OOXMLFastContextHandlerTable(OOXMLFastContextHandler * pContext); 346 virtual ~OOXMLFastContextHandlerTable() override; 347 348 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext (sal_Int32 Element, 349 const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 350 351 private: 352 OOXMLTable mTable; 353 354 css::uno::Reference<css::xml::sax::XFastContextHandler> mCurrentChild; 355 356 virtual void lcl_endFastElement(Token_t Element) override; 357 getResource() const358 virtual ResourceEnum_t getResource() const override { return TABLE; } 359 getType() const360 virtual std::string getType() const override { return "Table"; } 361 362 void addCurrentChild(); 363 }; 364 365 class OOXMLFastContextHandlerXNote : public OOXMLFastContextHandlerProperties 366 { 367 public: 368 explicit OOXMLFastContextHandlerXNote(OOXMLFastContextHandler * pContext); 369 virtual ~OOXMLFastContextHandlerXNote() override; 370 371 void checkId(const OOXMLValue::Pointer_t& pValue); 372 373 void checkType(const OOXMLValue::Pointer_t& pValue); 374 getType() const375 virtual std::string getType() const override { return "XNote"; } 376 377 private: 378 bool mbForwardEventsSaved; 379 sal_Int32 mnMyXNoteId; 380 sal_Int32 mnMyXNoteType; 381 382 virtual void lcl_startFastElement(Token_t Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 383 384 virtual void lcl_endFastElement(Token_t Element) override; 385 getResource() const386 virtual ResourceEnum_t getResource() const override { return STREAM; } 387 }; 388 389 class OOXMLFastContextHandlerTextTableCell : public OOXMLFastContextHandler 390 { 391 public: 392 explicit OOXMLFastContextHandlerTextTableCell(OOXMLFastContextHandler * pContext); 393 virtual ~OOXMLFastContextHandlerTextTableCell() override; 394 getType() const395 virtual std::string getType() const override { return "TextTableCell"; } 396 397 void startCell(); 398 void endCell(); 399 }; 400 401 class OOXMLFastContextHandlerTextTableRow : public OOXMLFastContextHandler 402 { 403 public: 404 explicit OOXMLFastContextHandlerTextTableRow(OOXMLFastContextHandler * pContext); 405 virtual ~OOXMLFastContextHandlerTextTableRow() override; 406 getType() const407 virtual std::string getType() const override { return "TextTableRow"; } 408 409 static void startRow(); 410 void endRow(); 411 void handleGridBefore( const OOXMLValue::Pointer_t& val ); 412 void handleGridAfter(const OOXMLValue::Pointer_t& rValue); 413 }; 414 415 class OOXMLFastContextHandlerTextTable : public OOXMLFastContextHandler 416 { 417 public: 418 explicit OOXMLFastContextHandlerTextTable(OOXMLFastContextHandler * pContext); 419 420 virtual ~OOXMLFastContextHandlerTextTable() override; 421 getType() const422 virtual std::string getType() const override { return "TextTable"; } 423 424 // tdf#111550 425 // when <w:tbl> appears as direct child of <w:p>, we need to rearrange this paragraph 426 // to merge with the table's first paragraph (that's what Word does in this case) 427 void start_P_Tbl(); 428 protected: 429 virtual void lcl_startFastElement(Token_t Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 430 431 virtual void lcl_endFastElement(Token_t Element) override; 432 }; 433 434 class OOXMLFastContextHandlerShape: public OOXMLFastContextHandlerProperties 435 { 436 bool m_bShapeSent; 437 bool m_bShapeStarted; 438 /// Is it necessary to pop the stack in the dtor? 439 bool m_bShapeContextPushed; 440 css::uno::Reference<css::xml::sax::XFastShapeContextHandler> mrShapeContext; 441 442 public: 443 explicit OOXMLFastContextHandlerShape(OOXMLFastContextHandler * pContext); 444 virtual ~OOXMLFastContextHandlerShape() override; 445 getType() const446 virtual std::string getType() const override { return "Shape"; } 447 448 // css::xml::sax::XFastContextHandler: 449 virtual void SAL_CALL startUnknownElement (const OUString & Namespace, const OUString & Name, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 450 451 virtual void SAL_CALL endUnknownElement(const OUString & Namespace, const OUString & Name) override; 452 453 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createUnknownChildContext(const OUString & Namespace, const OUString & Name, 454 const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 455 456 virtual void setToken(Token_t nToken) override; 457 getResource() const458 virtual ResourceEnum_t getResource() const override { return SHAPE; } 459 460 void sendShape( Token_t Element ); isShapeSent() const461 bool isShapeSent( ) const { return m_bShapeSent; } 462 463 protected: 464 virtual void lcl_startFastElement(Token_t Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 465 466 virtual void lcl_endFastElement(Token_t Element) override; 467 468 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > lcl_createFastChildContext (Token_t Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 469 470 virtual void lcl_characters(const OUString & aChars) override; 471 472 }; 473 474 /** 475 OOXMLFastContextHandlerWrapper wraps an OOXMLFastContextHandler. 476 477 The method calls for the interface css::xml::sax::XFastContextHandler are 478 forwarded to the wrapped OOXMLFastContextHandler. 479 */ 480 class OOXMLFastContextHandlerWrapper : public OOXMLFastContextHandler 481 { 482 public: 483 OOXMLFastContextHandlerWrapper(OOXMLFastContextHandler * pParent, 484 css::uno::Reference<css::xml::sax::XFastContextHandler> const & xContext, 485 rtl::Reference<OOXMLFastContextHandlerShape> const & xShapeHandler); 486 virtual ~OOXMLFastContextHandlerWrapper() override; 487 488 // css::xml::sax::XFastContextHandler: 489 virtual void SAL_CALL startUnknownElement(const OUString & Namespace, const OUString & Name, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 490 491 virtual void SAL_CALL endUnknownElement(const OUString & Namespace, const OUString & Name) override; 492 493 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createUnknownChildContext (const OUString & Namespace, const OUString & Name, 494 const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 495 496 virtual void attributes(const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 497 498 virtual ResourceEnum_t getResource() const override; 499 500 void addNamespace(Id nId); 501 void addToken( Token_t Element ); 502 503 virtual void newProperty(Id nId, const OOXMLValue::Pointer_t& pVal) override; 504 virtual void setPropertySet(const OOXMLPropertySet::Pointer_t& pPropertySet) override; 505 virtual OOXMLPropertySet::Pointer_t getPropertySet() const override; 506 507 virtual std::string getType() const override; 508 509 protected: 510 virtual void lcl_startFastElement(Token_t Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 511 512 virtual void lcl_endFastElement(Token_t Element) override; 513 514 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > lcl_createFastChildContext(Token_t Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 515 516 virtual void lcl_characters(const OUString & aChars) override; 517 518 virtual void setId(Id nId) override; 519 virtual Id getId() const override; 520 521 virtual void setToken(Token_t nToken) override; 522 virtual Token_t getToken() const override; 523 524 private: 525 css::uno::Reference<css::xml::sax::XFastContextHandler> mxWrappedContext; 526 rtl::Reference<OOXMLFastContextHandlerShape> mxShapeHandler; 527 std::set<Id> mMyNamespaces; 528 std::set<Token_t> mMyTokens; 529 OOXMLPropertySet::Pointer_t mpPropertySet; 530 531 OOXMLFastContextHandler * getFastContextHandler() const; 532 }; 533 534 /** 535 A class that converts from XFastParser/XFastContextHandler usage to a liner XML stream of data. 536 537 The purpose of this class is to convert the rather complex XFastContextHandler-based XML 538 processing that requires context subclasses, callbacks, etc. into a linear stream of XML tokens 539 that can be handled simply by reading the tokens one by one and directly processing them. 540 See the oox::formulaimport::XmlStream class documentation for more information. 541 542 Usage: Create a subclass of OOXMLFastContextHandlerLinear, reimplemented getType() to provide 543 type of the subclass and process() to actually process the XML stream. Also make sure to 544 add a line like the following to model.xml (for class OOXMLFastContextHandlerMath): 545 546 <resource name="CT_OMath" resource="Math"/> 547 548 @since 3.5 549 */ 550 class OOXMLFastContextHandlerLinear: public OOXMLFastContextHandlerProperties 551 { 552 public: 553 explicit OOXMLFastContextHandlerLinear(OOXMLFastContextHandler * pContext); 554 /** 555 Return the type of the class, as written in model.xml . 556 */ 557 virtual std::string getType() const override = 0; 558 559 protected: 560 /** 561 Called when the tokens for the element, its content and sub-elements have been linearized 562 and should be processed. The data member @ref buffer contains the converted data. 563 */ 564 virtual void process() = 0; 565 566 virtual void lcl_startFastElement(Token_t Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 567 568 virtual void lcl_endFastElement(Token_t Element) override; 569 570 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > lcl_createFastChildContext(Token_t Element, 571 const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override; 572 573 virtual void lcl_characters(const OUString & aChars) override; 574 575 // should be private, but not much point in making deep copies of it 576 oox::formulaimport::XmlStreamBuilder buffer; 577 578 private: 579 int depthCount; 580 }; 581 582 class OOXMLFastContextHandlerMath: public OOXMLFastContextHandlerLinear 583 { 584 public: 585 explicit OOXMLFastContextHandlerMath(OOXMLFastContextHandler * pContext); getType() const586 virtual std::string getType() const override { return "Math"; } 587 protected: 588 virtual void process() override; 589 }; 590 591 }} 592 #endif // INCLUDED_WRITERFILTER_SOURCE_OOXML_OOXMLFASTCONTEXTHANDLER_HXX 593 594 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 595