LibreOffice Module sax (master) 1
saxwriter.cxx
Go to the documentation of this file.
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 <string.h>
21
22#include <cassert>
23#include <set>
24#include <stack>
25#include <vector>
26
27#include <com/sun/star/io/IOException.hpp>
28#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
29#include <com/sun/star/lang/XServiceInfo.hpp>
30#include <com/sun/star/uno/XComponentContext.hpp>
31#include <com/sun/star/util/XCloneable.hpp>
32#include <com/sun/star/xml/sax/SAXInvalidCharacterException.hpp>
33#include <com/sun/star/xml/sax/XWriter.hpp>
34
36#include <cppuhelper/weak.hxx>
39
40#include <osl/diagnose.h>
41#include <rtl/character.hxx>
42#include <sal/log.hxx>
43
44using namespace ::osl;
45using namespace ::cppu;
46using namespace ::com::sun::star::uno;
47using namespace ::com::sun::star::lang;
48using namespace ::com::sun::star::xml::sax;
49using namespace ::com::sun::star::util;
50using namespace ::com::sun::star::io;
51
52#include <memory>
53
54#define LINEFEED 10
55#define SEQUENCESIZE 1024
56#define MAXCOLUMNCOUNT 72
57
58/******
59*
60*
61* Character conversion functions
62*
63*
64*****/
65
66namespace
67{
68enum SaxInvalidCharacterError
69{
70 SAX_NONE,
71 SAX_WARNING,
72 SAX_ERROR
73};
74
75// Stuff for custom entity names
76struct ReplacementPair
77{
78 OUString name;
79 OUString replacement;
80};
81inline bool operator<(const ReplacementPair& lhs, const ReplacementPair& rhs)
82{
83 return lhs.replacement.compareTo(rhs.replacement) < 0;
84}
85
86class SaxWriterHelper
87{
88#ifdef DBG_UTIL
89public:
90 ::std::stack<OUString> m_DebugStartedElements;
91#endif
92
93private:
94 Reference<XOutputStream> m_out;
95 Sequence<sal_Int8> m_Sequence;
96 sal_Int8* mp_Sequence;
97
98 sal_Int32 nLastLineFeedPos; // is negative after writing a sequence
99 sal_uInt32 nCurrentPos;
100 bool m_bStartElementFinished;
101
102 std::vector<ReplacementPair> m_Replacements;
103
105 sal_uInt32 writeSequence();
106
107 // use only if to insert the bytes more space in the sequence is needed and
108 // so the sequence has to write out and reset rPos to 0
109 // writes sequence only on overflow, sequence could be full on the end (rPos == SEQUENCESIZE)
111 void AddBytes(sal_Int8* pTarget, sal_uInt32& rPos, const sal_Int8* pBytes,
112 sal_uInt32 nBytesCount);
114 bool convertToXML(const sal_Unicode* pStr, sal_Int32 nStrLen, bool bDoNormalization,
115 bool bNormalizeWhitespace, sal_Int8* pTarget, sal_uInt32& rPos);
117 void FinishStartElement();
118
119 // Search for the correct replacement
120 const ReplacementPair* findXMLReplacement(const sal_Unicode* pStr, sal_Int32 nStrLen);
121
122public:
123 explicit SaxWriterHelper(Reference<XOutputStream> const& m_TempOut)
124 : m_out(m_TempOut)
125 , m_Sequence(SEQUENCESIZE)
126 , mp_Sequence(nullptr)
127 , nLastLineFeedPos(0)
128 , nCurrentPos(0)
129 , m_bStartElementFinished(true)
130 {
131 OSL_ENSURE(SEQUENCESIZE > 50, "Sequence cache size too small");
132 mp_Sequence = m_Sequence.getArray();
133 }
134 ~SaxWriterHelper()
135 {
136 OSL_ENSURE(!nCurrentPos, "cached Sequence not written");
137 OSL_ENSURE(m_bStartElementFinished, "StartElement not completely written");
138 }
139
141 void insertIndentation(sal_uInt32 m_nLevel);
142
143 // returns whether it works correct or invalid characters were in the string
144 // If there are invalid characters in the string it returns sal_False.
145 // Than the calling method has to throw the needed Exception.
147 bool writeString(const OUString& rWriteOutString, bool bDoNormalization,
148 bool bNormalizeWhitespace);
149
150 sal_uInt32 GetLastColumnCount() const noexcept
151 {
152 return static_cast<sal_uInt32>(nCurrentPos - nLastLineFeedPos);
153 }
154
156 void startDocument();
157
158 // returns whether it works correct or invalid characters were in the strings
159 // If there are invalid characters in one of the strings it returns sal_False.
160 // Than the calling method has to throw the needed Exception.
162 SaxInvalidCharacterError startElement(const OUString& rName,
163 const Reference<XAttributeList>& xAttribs);
165 bool FinishEmptyElement();
166
167 // returns whether it works correct or invalid characters were in the string
168 // If there are invalid characters in the string it returns sal_False.
169 // Than the calling method has to throw the needed Exception.
171 bool endElement(const OUString& rName);
173 void endDocument();
174
175 // returns whether it works correct or invalid characters were in the strings
176 // If there are invalid characters in the string it returns sal_False.
177 // Than the calling method has to throw the needed Exception.
179 bool processingInstruction(const OUString& rTarget, const OUString& rData);
181 void startCDATA();
183 void endCDATA();
184
185 // returns whether it works correct or invalid characters were in the strings
186 // If there are invalid characters in the string it returns sal_False.
187 // Than the calling method has to throw the needed Exception.
189 bool comment(const OUString& rComment);
190
192 void clearBuffer();
193
194 // Use custom entity names
195 void setCustomEntityNames(
196 const ::css::uno::Sequence<::css::beans::Pair<::rtl::OUString, ::rtl::OUString>>&
197 replacements);
198
199 // Calculate length for convertToXML
200 sal_Int32 calcXMLByteLength(const OUString& rStr, bool bDoNormalization,
201 bool bNormalizeWhitespace);
202};
203
204const bool g_bValidCharsBelow32[32] = {
205 // clang-format off
206// 0 1 2 3 4 5 6 7
207 false, false, false, false, false, false, false, false, //0
208 false, true, true, false, false, true, false, false, //8
209 false, false, false, false, false, false, false, false, //16
210 false, false, false, false, false, false, false, false
211 // clang-format on
212};
213
214bool IsInvalidChar(const sal_Unicode aChar)
215{
216 bool bRet(false);
217 // check first for the most common characters
218 if (aChar < 32 || aChar >= 0xd800)
219 bRet = ((aChar < 32 && !g_bValidCharsBelow32[aChar]) || aChar == 0xffff || aChar == 0xfffe);
220 return bRet;
221}
222
223/********
224* write through to the output stream
225*
226*****/
227sal_uInt32 SaxWriterHelper::writeSequence()
228{
229 try
230 {
231 m_out->writeBytes(m_Sequence);
232 }
233 catch (const IOException&)
234 {
235 css::uno::Any anyEx = cppu::getCaughtException();
236 throw SAXException("IO exception during writing", Reference<XInterface>(), anyEx);
237 }
238 nLastLineFeedPos -= SEQUENCESIZE;
239 return 0;
240}
241
242void SaxWriterHelper::AddBytes(sal_Int8* pTarget, sal_uInt32& rPos, const sal_Int8* pBytes,
243 sal_uInt32 nBytesCount)
244{
245 OSL_ENSURE((rPos + nBytesCount) > SEQUENCESIZE, "wrong use of AddBytesMethod");
246 sal_uInt32 nCount(SEQUENCESIZE - rPos);
247 memcpy(&(pTarget[rPos]), pBytes, nCount);
248
249 OSL_ENSURE(rPos + nCount == SEQUENCESIZE, "the position should be the at the end");
250
251 rPos = writeSequence();
252 sal_uInt32 nRestCount(nBytesCount - nCount);
253 if ((rPos + nRestCount) <= SEQUENCESIZE)
254 {
255 memcpy(&(pTarget[rPos]), &pBytes[nCount], nRestCount);
256 rPos += nRestCount;
257 }
258 else
259 AddBytes(pTarget, rPos, &pBytes[nCount], nRestCount);
260}
261
262void SaxWriterHelper::setCustomEntityNames(
263 const ::css::uno::Sequence<::css::beans::Pair<::rtl::OUString, ::rtl::OUString>>& replacements)
264{
265 m_Replacements.resize(replacements.size());
266 for (size_t i = 0; i < replacements.size(); ++i)
267 {
268 m_Replacements[i].name = replacements[i].First;
269 m_Replacements[i].replacement = replacements[i].Second;
270 }
271 if (replacements.size() > 1)
272 std::sort(m_Replacements.begin(), m_Replacements.end());
273}
274
282bool SaxWriterHelper::convertToXML(const sal_Unicode* pStr, sal_Int32 nStrLen,
283 bool bDoNormalization, bool bNormalizeWhitespace,
284 sal_Int8* pTarget, sal_uInt32& rPos)
285{
286 bool bRet(true);
287 sal_uInt32 nSurrogate = 0;
288
289 for (sal_Int32 i = 0; i < nStrLen; i++)
290 {
291 sal_Unicode c = pStr[i];
292 if (IsInvalidChar(c))
293 bRet = false;
294 else if ((c >= 0x0001) && (c <= 0x007F)) // Deal with ascii
295 {
296 if (bDoNormalization)
297 {
298 switch (c)
299 {
300 case '&': // resemble to &amp;
301 {
302 if ((rPos + 5) > SEQUENCESIZE)
303 AddBytes(pTarget, rPos, reinterpret_cast<sal_Int8 const*>("&amp;"), 5);
304 else
305 {
306 memcpy(&(pTarget[rPos]), "&amp;", 5);
307 rPos += 5;
308 }
309 }
310 break;
311 case '<':
312 {
313 if ((rPos + 4) > SEQUENCESIZE)
314 AddBytes(pTarget, rPos, reinterpret_cast<sal_Int8 const*>("&lt;"), 4);
315 else
316 {
317 memcpy(&(pTarget[rPos]), "&lt;", 4);
318 rPos += 4; // &lt;
319 }
320 }
321 break;
322 case '>':
323 {
324 if ((rPos + 4) > SEQUENCESIZE)
325 AddBytes(pTarget, rPos, reinterpret_cast<sal_Int8 const*>("&gt;"), 4);
326 else
327 {
328 memcpy(&(pTarget[rPos]), "&gt;", 4);
329 rPos += 4; // &gt;
330 }
331 }
332 break;
333 case '\'':
334 {
335 if ((rPos + 6) > SEQUENCESIZE)
336 AddBytes(pTarget, rPos, reinterpret_cast<sal_Int8 const*>("&apos;"), 6);
337 else
338 {
339 memcpy(&(pTarget[rPos]), "&apos;", 6);
340 rPos += 6; // &apos;
341 }
342 }
343 break;
344 case '"':
345 {
346 if ((rPos + 6) > SEQUENCESIZE)
347 AddBytes(pTarget, rPos, reinterpret_cast<sal_Int8 const*>("&quot;"), 6);
348 else
349 {
350 memcpy(&(pTarget[rPos]), "&quot;", 6);
351 rPos += 6; // &quot;
352 }
353 }
354 break;
355 case 13:
356 {
357 if ((rPos + 6) > SEQUENCESIZE)
358 AddBytes(pTarget, rPos, reinterpret_cast<sal_Int8 const*>("&#x0d;"), 6);
359 else
360 {
361 memcpy(&(pTarget[rPos]), "&#x0d;", 6);
362 rPos += 6;
363 }
364 }
365 break;
366 case LINEFEED:
367 {
368 if (bNormalizeWhitespace)
369 {
370 if ((rPos + 6) > SEQUENCESIZE)
371 AddBytes(pTarget, rPos, reinterpret_cast<sal_Int8 const*>("&#x0a;"),
372 6);
373 else
374 {
375 memcpy(&(pTarget[rPos]), "&#x0a;", 6);
376 rPos += 6;
377 }
378 }
379 else
380 {
381 pTarget[rPos] = LINEFEED;
382 nLastLineFeedPos = rPos;
383 rPos++;
384 }
385 }
386 break;
387 case 9:
388 {
389 if (bNormalizeWhitespace)
390 {
391 if ((rPos + 6) > SEQUENCESIZE)
392 AddBytes(pTarget, rPos, reinterpret_cast<sal_Int8 const*>("&#x09;"),
393 6);
394 else
395 {
396 memcpy(&(pTarget[rPos]), "&#x09;", 6);
397 rPos += 6;
398 }
399 }
400 else
401 {
402 pTarget[rPos] = 9;
403 rPos++;
404 }
405 }
406 break;
407 default:
408 {
409 pTarget[rPos] = static_cast<sal_Int8>(c);
410 rPos++;
411 }
412 break;
413 }
414 }
415 else
416 {
417 pTarget[rPos] = static_cast<sal_Int8>(c);
418 if (static_cast<sal_Int8>(c) == LINEFEED)
419 nLastLineFeedPos = rPos;
420 rPos++;
421 }
422 }
423 else
424 {
425 // Deal with replacements
426 if (bDoNormalization && !m_Replacements.empty())
427 {
428 // search
429 const ReplacementPair* it = findXMLReplacement(&pStr[i], nStrLen - i);
430
431 // replace
432 if (it != nullptr)
433 {
434 OString name = ::rtl::OUStringToOString(it->name, RTL_TEXTENCODING_UTF8);
435 if (rPos + name.getLength() > SEQUENCESIZE)
436 AddBytes(pTarget, rPos, reinterpret_cast<sal_Int8 const*>(name.getStr()),
437 name.getLength());
438 else
439 {
440 memcpy(&(pTarget[rPos]), name.getStr(), name.getLength());
441 rPos += name.getLength();
442 }
443 i += it->replacement.getLength() - 1;
444 continue;
445 }
446 }
447
448 // Deal with other unicode cases
449 if (rtl::isHighSurrogate(c))
450 {
451 // 1. surrogate: save (until 2. surrogate)
452 if (nSurrogate != 0) // left-over lone 1st Unicode surrogate
453 {
454 OSL_FAIL("left-over Unicode surrogate");
455 bRet = false;
456 }
457 nSurrogate = c;
458 }
459 else if (rtl::isLowSurrogate(c))
460 {
461 // 2. surrogate: write as UTF-8
462 if (nSurrogate) // can only be 1st surrogate
463 {
464 nSurrogate = rtl::combineSurrogates(nSurrogate, c);
465 sal_Int8 aBytes[] = { sal_Int8(0xF0 | ((nSurrogate >> 18) & 0x0F)),
466 sal_Int8(0x80 | ((nSurrogate >> 12) & 0x3F)),
467 sal_Int8(0x80 | ((nSurrogate >> 6) & 0x3F)),
468 sal_Int8(0x80 | ((nSurrogate >> 0) & 0x3F)) };
469 if ((rPos + 4) > SEQUENCESIZE)
470 AddBytes(pTarget, rPos, aBytes, 4);
471 else
472 {
473 pTarget[rPos] = aBytes[0];
474 rPos++;
475 pTarget[rPos] = aBytes[1];
476 rPos++;
477 pTarget[rPos] = aBytes[2];
478 rPos++;
479 pTarget[rPos] = aBytes[3];
480 rPos++;
481 }
482 }
483 else // lone 2nd surrogate
484 {
485 OSL_FAIL("illegal Unicode character");
486 bRet = false;
487 }
488
489 // reset surrogate
490 nSurrogate = 0;
491 }
492 else if (c > 0x07FF)
493 {
494 sal_Int8 aBytes[]
495 = { sal_Int8(0xE0 | ((c >> 12) & 0x0F)), sal_Int8(0x80 | ((c >> 6) & 0x3F)),
496 sal_Int8(0x80 | ((c >> 0) & 0x3F)) };
497 if ((rPos + 3) > SEQUENCESIZE)
498 AddBytes(pTarget, rPos, aBytes, 3);
499 else
500 {
501 pTarget[rPos] = aBytes[0];
502 rPos++;
503 pTarget[rPos] = aBytes[1];
504 rPos++;
505 pTarget[rPos] = aBytes[2];
506 rPos++;
507 }
508 }
509 else
510 {
511 sal_Int8 aBytes[]
512 = { sal_Int8(0xC0 | ((c >> 6) & 0x1F)), sal_Int8(0x80 | ((c >> 0) & 0x3F)) };
513 if ((rPos + 2) > SEQUENCESIZE)
514 AddBytes(pTarget, rPos, aBytes, 2);
515 else
516 {
517 pTarget[rPos] = aBytes[0];
518 rPos++;
519 pTarget[rPos] = aBytes[1];
520 rPos++;
521 }
522 }
523 }
524
525 OSL_ENSURE(rPos <= SEQUENCESIZE, "not reset current position");
526 if (rPos == SEQUENCESIZE)
527 rPos = writeSequence();
528
529 // reset left-over surrogate
530 if ((nSurrogate != 0) && !rtl::isHighSurrogate(c))
531 {
532 OSL_FAIL("left-over Unicode surrogate");
533 nSurrogate = 0;
534 bRet = false;
535 }
536 }
537 if (nSurrogate != 0) // trailing lone 1st surrogate
538 {
539 OSL_FAIL("left-over Unicode surrogate");
540 bRet = false;
541 }
542 return bRet;
543}
544
545void SaxWriterHelper::FinishStartElement()
546{
547 if (!m_bStartElementFinished)
548 {
549 mp_Sequence[nCurrentPos] = '>';
550 nCurrentPos++;
551 if (nCurrentPos == SEQUENCESIZE)
552 nCurrentPos = writeSequence();
553 m_bStartElementFinished = true;
554 }
555}
556
557void SaxWriterHelper::insertIndentation(sal_uInt32 m_nLevel)
558{
559 FinishStartElement();
560 if (m_nLevel > 0)
561 {
562 if ((nCurrentPos + m_nLevel + 1) <= SEQUENCESIZE)
563 {
564 mp_Sequence[nCurrentPos] = LINEFEED;
565 nLastLineFeedPos = nCurrentPos;
566 nCurrentPos++;
567 memset(&(mp_Sequence[nCurrentPos]), 32, m_nLevel);
568 nCurrentPos += m_nLevel;
569 if (nCurrentPos == SEQUENCESIZE)
570 nCurrentPos = writeSequence();
571 }
572 else
573 {
574 sal_uInt32 nCount(m_nLevel + 1);
575 std::unique_ptr<sal_Int8[]> pBytes(new sal_Int8[nCount]);
576 pBytes[0] = LINEFEED;
577 memset(&(pBytes[1]), 32, m_nLevel);
578 AddBytes(mp_Sequence, nCurrentPos, pBytes.get(), nCount);
579 pBytes.reset();
580 nLastLineFeedPos = nCurrentPos - nCount;
581 if (nCurrentPos == SEQUENCESIZE)
582 nCurrentPos = writeSequence();
583 }
584 }
585 else
586 {
587 mp_Sequence[nCurrentPos] = LINEFEED;
588 nLastLineFeedPos = nCurrentPos;
589 nCurrentPos++;
590 if (nCurrentPos == SEQUENCESIZE)
591 nCurrentPos = writeSequence();
592 }
593}
594
595bool SaxWriterHelper::writeString(const OUString& rWriteOutString, bool bDoNormalization,
596 bool bNormalizeWhitespace)
597{
598 FinishStartElement();
599 return convertToXML(rWriteOutString.getStr(), rWriteOutString.getLength(), bDoNormalization,
600 bNormalizeWhitespace, mp_Sequence, nCurrentPos);
601}
602
603void SaxWriterHelper::startDocument()
604{
605 const char pc[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
606 const int nLen = strlen(pc);
607 if ((nCurrentPos + nLen) <= SEQUENCESIZE)
608 {
609 memcpy(mp_Sequence, pc, nLen);
610 nCurrentPos += nLen;
611 }
612 else
613 {
614 AddBytes(mp_Sequence, nCurrentPos, reinterpret_cast<sal_Int8 const*>(pc), nLen);
615 }
616 OSL_ENSURE(nCurrentPos <= SEQUENCESIZE, "not reset current position");
617 if (nCurrentPos == SEQUENCESIZE)
618 nCurrentPos = writeSequence();
619 mp_Sequence[nCurrentPos] = LINEFEED;
620 nCurrentPos++;
621 if (nCurrentPos == SEQUENCESIZE)
622 nCurrentPos = writeSequence();
623}
624
625#ifndef NDEBUG
626bool inrange(sal_Unicode c, sal_Unicode start, sal_Unicode end) { return c >= start && c <= end; }
627#endif
628
629void CheckValidName(OUString const& rName)
630{
631#ifdef NDEBUG
632 (void)rName;
633#else
634 assert(!rName.isEmpty());
635 bool hasColon(false);
636 for (sal_Int32 i = 0; i < rName.getLength(); ++i)
637 {
638 auto const c(rName[i]);
639 if (c == ':')
640 {
641 // see https://www.w3.org/TR/REC-xml-names/#ns-qualnames
642 SAL_WARN_IF(hasColon, "sax", "only one colon allowed: " << rName);
643 assert(!hasColon && "only one colon allowed");
644 hasColon = true;
645 }
646 else if (!rtl::isAsciiAlphanumeric(c) && c != '_' && c != '-' && c != '.'
647 && !inrange(c, 0x00C0, 0x00D6) && !inrange(c, 0x00D8, 0x00F6)
648 && !inrange(c, 0x00F8, 0x02FF) && !inrange(c, 0x0370, 0x037D)
649 && !inrange(c, 0x037F, 0x1FFF) && !inrange(c, 0x200C, 0x200D)
650 && !inrange(c, 0x2070, 0x218F) && !inrange(c, 0x2C00, 0x2FEF)
651 && !inrange(c, 0x3001, 0xD7FF) && !inrange(c, 0xF900, 0xFDCF)
652 && !inrange(c, 0xFDF0, 0xFFFD) && c != 0x00B7 && !inrange(c, 0x0300, 0x036F)
653 && !inrange(c, 0x203F, 0x2040))
654 {
655 // https://www.w3.org/TR/xml11/#NT-NameChar
656 // (currently we don't warn about invalid start chars)
657 SAL_WARN("sax", "unexpected character in attribute name: " << rName);
658 assert(!"unexpected character in attribute name");
659 }
660 }
661#endif
662}
663
664SaxInvalidCharacterError SaxWriterHelper::startElement(const OUString& rName,
665 const Reference<XAttributeList>& xAttribs)
666{
667 FinishStartElement();
668
669#ifdef DBG_UTIL
670 m_DebugStartedElements.push(rName);
671 ::std::set<OUString> DebugAttributes;
672#endif
673
674 mp_Sequence[nCurrentPos] = '<';
675 nCurrentPos++;
676 if (nCurrentPos == SEQUENCESIZE)
677 nCurrentPos = writeSequence();
678
679 SaxInvalidCharacterError eRet(SAX_NONE);
680 CheckValidName(rName);
681 if (!writeString(rName, false, false))
682 eRet = SAX_ERROR;
683
684 sal_Int16 nAttribCount = xAttribs.is() ? xAttribs->getLength() : 0;
685 for (sal_Int16 i = 0; i < nAttribCount; i++)
686 {
687 mp_Sequence[nCurrentPos] = ' ';
688 nCurrentPos++;
689 if (nCurrentPos == SEQUENCESIZE)
690 nCurrentPos = writeSequence();
691
692 OUString const& rAttrName(xAttribs->getNameByIndex(i));
693#ifdef DBG_UTIL
694 // Well-formedness constraint: Unique Att Spec
695 assert(DebugAttributes.find(rAttrName) == DebugAttributes.end());
696 DebugAttributes.insert(rAttrName);
697#endif
698 CheckValidName(rAttrName);
699 if (!writeString(rAttrName, false, false))
700 eRet = SAX_ERROR;
701
702 mp_Sequence[nCurrentPos] = '=';
703 nCurrentPos++;
704 if (nCurrentPos == SEQUENCESIZE)
705 nCurrentPos = writeSequence();
706 mp_Sequence[nCurrentPos] = '"';
707 nCurrentPos++;
708 if (nCurrentPos == SEQUENCESIZE)
709 nCurrentPos = writeSequence();
710
711 if (!writeString(xAttribs->getValueByIndex(i), true, true) && eRet != SAX_ERROR)
712 eRet = SAX_WARNING;
713
714 mp_Sequence[nCurrentPos] = '"';
715 nCurrentPos++;
716 if (nCurrentPos == SEQUENCESIZE)
717 nCurrentPos = writeSequence();
718 }
719
720 m_bStartElementFinished = false; // because the '>' character is not added,
721 // because it is possible, that the "/>"
722 // characters have to add
723 return eRet;
724}
725
726bool SaxWriterHelper::FinishEmptyElement()
727{
728 if (m_bStartElementFinished)
729 return false;
730
731 mp_Sequence[nCurrentPos] = '/';
732 nCurrentPos++;
733 if (nCurrentPos == SEQUENCESIZE)
734 nCurrentPos = writeSequence();
735 mp_Sequence[nCurrentPos] = '>';
736 nCurrentPos++;
737 if (nCurrentPos == SEQUENCESIZE)
738 nCurrentPos = writeSequence();
739
740 m_bStartElementFinished = true;
741
742 return true;
743}
744
745bool SaxWriterHelper::endElement(const OUString& rName)
746{
747 FinishStartElement();
748
749 mp_Sequence[nCurrentPos] = '<';
750 nCurrentPos++;
751 if (nCurrentPos == SEQUENCESIZE)
752 nCurrentPos = writeSequence();
753 mp_Sequence[nCurrentPos] = '/';
754 nCurrentPos++;
755 if (nCurrentPos == SEQUENCESIZE)
756 nCurrentPos = writeSequence();
757
758 CheckValidName(rName);
759 bool bRet(writeString(rName, false, false));
760
761 mp_Sequence[nCurrentPos] = '>';
762 nCurrentPos++;
763 if (nCurrentPos == SEQUENCESIZE)
764 nCurrentPos = writeSequence();
765
766 return bRet;
767}
768
769void SaxWriterHelper::endDocument()
770{
771 if (nCurrentPos > 0)
772 {
773 m_Sequence.realloc(nCurrentPos);
774 nCurrentPos = writeSequence();
775 //m_Sequence.realloc(SEQUENCESIZE);
776 }
777}
778
779void SaxWriterHelper::clearBuffer()
780{
781 FinishStartElement();
782 if (nCurrentPos > 0)
783 {
784 m_Sequence.realloc(nCurrentPos);
785 nCurrentPos = writeSequence();
786 m_Sequence.realloc(SEQUENCESIZE);
787 // Be sure to update the array pointer after the reallocation.
788 mp_Sequence = m_Sequence.getArray();
789 }
790}
791
792bool SaxWriterHelper::processingInstruction(const OUString& rTarget, const OUString& rData)
793{
794 FinishStartElement();
795 mp_Sequence[nCurrentPos] = '<';
796 nCurrentPos++;
797 if (nCurrentPos == SEQUENCESIZE)
798 nCurrentPos = writeSequence();
799 mp_Sequence[nCurrentPos] = '?';
800 nCurrentPos++;
801 if (nCurrentPos == SEQUENCESIZE)
802 nCurrentPos = writeSequence();
803
804 bool bRet(writeString(rTarget, false, false));
805
806 mp_Sequence[nCurrentPos] = ' ';
807 nCurrentPos++;
808 if (nCurrentPos == SEQUENCESIZE)
809 nCurrentPos = writeSequence();
810
811 if (!writeString(rData, false, false))
812 bRet = false;
813
814 mp_Sequence[nCurrentPos] = '?';
815 nCurrentPos++;
816 if (nCurrentPos == SEQUENCESIZE)
817 nCurrentPos = writeSequence();
818 mp_Sequence[nCurrentPos] = '>';
819 nCurrentPos++;
820 if (nCurrentPos == SEQUENCESIZE)
821 nCurrentPos = writeSequence();
822
823 return bRet;
824}
825
826void SaxWriterHelper::startCDATA()
827{
828 FinishStartElement();
829 if ((nCurrentPos + 9) <= SEQUENCESIZE)
830 {
831 memcpy(&(mp_Sequence[nCurrentPos]), "<![CDATA[", 9);
832 nCurrentPos += 9;
833 }
834 else
835 AddBytes(mp_Sequence, nCurrentPos, reinterpret_cast<sal_Int8 const*>("<![CDATA["), 9);
836 if (nCurrentPos == SEQUENCESIZE)
837 nCurrentPos = writeSequence();
838}
839
840void SaxWriterHelper::endCDATA()
841{
842 FinishStartElement();
843 if ((nCurrentPos + 3) <= SEQUENCESIZE)
844 {
845 memcpy(&(mp_Sequence[nCurrentPos]), "]]>", 3);
846 nCurrentPos += 3;
847 }
848 else
849 AddBytes(mp_Sequence, nCurrentPos, reinterpret_cast<sal_Int8 const*>("]]>"), 3);
850 if (nCurrentPos == SEQUENCESIZE)
851 nCurrentPos = writeSequence();
852}
853
854bool SaxWriterHelper::comment(const OUString& rComment)
855{
856 FinishStartElement();
857 mp_Sequence[nCurrentPos] = '<';
858 nCurrentPos++;
859 if (nCurrentPos == SEQUENCESIZE)
860 nCurrentPos = writeSequence();
861 mp_Sequence[nCurrentPos] = '!';
862 nCurrentPos++;
863 if (nCurrentPos == SEQUENCESIZE)
864 nCurrentPos = writeSequence();
865 mp_Sequence[nCurrentPos] = '-';
866 nCurrentPos++;
867 if (nCurrentPos == SEQUENCESIZE)
868 nCurrentPos = writeSequence();
869 mp_Sequence[nCurrentPos] = '-';
870 nCurrentPos++;
871 if (nCurrentPos == SEQUENCESIZE)
872 nCurrentPos = writeSequence();
873
874 bool bRet(writeString(rComment, false, false));
875
876 mp_Sequence[nCurrentPos] = '-';
877 nCurrentPos++;
878 if (nCurrentPos == SEQUENCESIZE)
879 nCurrentPos = writeSequence();
880 mp_Sequence[nCurrentPos] = '-';
881 nCurrentPos++;
882 if (nCurrentPos == SEQUENCESIZE)
883 nCurrentPos = writeSequence();
884 mp_Sequence[nCurrentPos] = '>';
885 nCurrentPos++;
886 if (nCurrentPos == SEQUENCESIZE)
887 nCurrentPos = writeSequence();
888
889 return bRet;
890}
891
892sal_Int32 SaxWriterHelper::calcXMLByteLength(const OUString& rStr, bool bDoNormalization,
893 bool bNormalizeWhitespace)
894{
895 sal_Int32 nOutputLength = 0;
896 sal_uInt32 nSurrogate = 0;
897
898 const sal_Unicode* pStr = rStr.getStr();
899 sal_Int32 nStrLen = rStr.getLength();
900 for (sal_Int32 i = 0; i < nStrLen; i++)
901 {
902 sal_uInt16 c = pStr[i];
903 if (!IsInvalidChar(c) && (c >= 0x0001) && (c <= 0x007F))
904 {
905 if (bDoNormalization)
906 {
907 switch (c)
908 {
909 case '&': // resemble to &amp;
910 nOutputLength += 5;
911 break;
912 case '<': // &lt;
913 case '>': // &gt;
914 nOutputLength += 4;
915 break;
916 case '\'': // &apos;
917 case '"': // &quot;
918 case 13: // &#x0d;
919 nOutputLength += 6;
920 break;
921
922 case 10: // &#x0a;
923 case 9: // &#x09;
924 if (bNormalizeWhitespace)
925 {
926 nOutputLength += 6;
927 }
928 else
929 {
930 nOutputLength++;
931 }
932 break;
933 default:
934 nOutputLength++;
935 }
936 }
937 else
938 {
939 nOutputLength++;
940 }
941 }
942 else
943 {
944 // Deal with replacements
945 if (bDoNormalization && !m_Replacements.empty())
946 {
947 // search
948 const ReplacementPair* it = findXMLReplacement(&pStr[i], nStrLen - i);
949
950 if (it != nullptr)
951 {
952 nOutputLength
953 += ::rtl::OUStringToOString(it->name, RTL_TEXTENCODING_UTF8).getLength();
954 i += it->replacement.getLength() - 1;
955 continue;
956 }
957 }
958
959 // Deal with other unicode cases
960 if (rtl::isHighSurrogate(c))
961 {
962 // save surrogate
963 nSurrogate = c;
964 }
965 else if (rtl::isLowSurrogate(c))
966 {
967 // 2. surrogate: write as UTF-8 (if range is OK
968 if (nSurrogate)
969 nOutputLength += 4;
970 nSurrogate = 0;
971 }
972 else if (c > 0x07FF)
973 {
974 nOutputLength += 3;
975 }
976 else
977 {
978 nOutputLength += 2;
979 }
980 }
981
982 // surrogate processing
983 if ((nSurrogate != 0) && !rtl::isHighSurrogate(c))
984 nSurrogate = 0;
985 }
986
987 return nOutputLength;
988}
989
990const ReplacementPair* SaxWriterHelper::findXMLReplacement(const sal_Unicode* pStr,
991 sal_Int32 nStrLen)
992{
993 for (size_t iter = 0; iter < m_Replacements.size(); ++iter)
994 {
995 if (m_Replacements[iter].replacement.getLength() > nStrLen)
996 continue;
997 sal_Int32 matches = m_Replacements[iter].replacement.compareTo(
998 std::u16string_view(pStr, m_Replacements[iter].replacement.getLength()));
999 if (matches == 0)
1000 return &m_Replacements[iter];
1001 if (matches > 0)
1002 return nullptr;
1003 }
1004 return nullptr;
1005}
1006
1007class SAXWriter : public WeakImplHelper<XWriter, XServiceInfo>
1008{
1009public:
1010 SAXWriter()
1011 : m_bDocStarted(false)
1012 , m_bIsCDATA(false)
1013 , m_bForceLineBreak(false)
1014 , m_bAllowLineBreak(false)
1015 , m_nLevel(0)
1016 {
1017 }
1018
1019public: // XActiveDataSource
1020 virtual void SAL_CALL setOutputStream(const Reference<XOutputStream>& aStream) override
1021 {
1022 try
1023 {
1024 // temporary: set same stream again to clear buffer
1025 if (m_out == aStream && m_pSaxWriterHelper && m_bDocStarted)
1026 m_pSaxWriterHelper->clearBuffer();
1027 else
1028 {
1029 m_out = aStream;
1030 m_pSaxWriterHelper.reset(new SaxWriterHelper(m_out));
1031 m_bDocStarted = false;
1032 m_nLevel = 0;
1033 m_bIsCDATA = false;
1034 }
1035 }
1036 catch (const SAXException& e)
1037 {
1038 throw css::lang::WrappedTargetRuntimeException(e.Message, getXWeak(),
1039 e.WrappedException);
1040 }
1041 }
1042 virtual Reference<XOutputStream> SAL_CALL getOutputStream() override { return m_out; }
1043
1044public: // XDocumentHandler
1045 virtual void SAL_CALL startDocument() override;
1046
1047 virtual void SAL_CALL endDocument() override;
1048
1049 virtual void SAL_CALL startElement(const OUString& aName,
1050 const Reference<XAttributeList>& xAttribs) override;
1051
1052 virtual void SAL_CALL endElement(const OUString& aName) override;
1053
1054 virtual void SAL_CALL characters(const OUString& aChars) override;
1055
1056 virtual void SAL_CALL ignorableWhitespace(const OUString& aWhitespaces) override;
1057 virtual void SAL_CALL processingInstruction(const OUString& aTarget,
1058 const OUString& aData) override;
1059 virtual void SAL_CALL setDocumentLocator(const Reference<XLocator>& xLocator) override;
1060 virtual void SAL_CALL setCustomEntityNames(
1061 const ::css::uno::Sequence<::css::beans::Pair<::rtl::OUString, ::rtl::OUString>>&
1062 replacements) override;
1063
1064public: // XExtendedDocumentHandler
1065 virtual void SAL_CALL startCDATA() override;
1066 virtual void SAL_CALL endCDATA() override;
1067 virtual void SAL_CALL comment(const OUString& sComment) override;
1068 virtual void SAL_CALL unknown(const OUString& sString) override;
1069 virtual void SAL_CALL allowLineBreak() override;
1070
1071public: // XServiceInfo
1072 OUString SAL_CALL getImplementationName() override;
1073 Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
1074 sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
1075
1076private:
1077 sal_Int32 getIndentPrefixLength(sal_Int32 nFirstLineBreakOccurrence) noexcept;
1078
1079 Reference<XOutputStream> m_out;
1080 std::unique_ptr<SaxWriterHelper> m_pSaxWriterHelper;
1081
1082 // Status information
1083 bool m_bDocStarted : 1;
1084 bool m_bIsCDATA : 1;
1085 bool m_bForceLineBreak : 1;
1086 bool m_bAllowLineBreak : 1;
1087 sal_Int32 m_nLevel;
1088};
1089
1090sal_Int32 SAXWriter::getIndentPrefixLength(sal_Int32 nFirstLineBreakOccurrence) noexcept
1091{
1092 sal_Int32 nLength = -1;
1093 if (m_pSaxWriterHelper)
1094 {
1095 if (m_bForceLineBreak
1096 || (m_bAllowLineBreak
1097 && ((nFirstLineBreakOccurrence + m_pSaxWriterHelper->GetLastColumnCount())
1098 > MAXCOLUMNCOUNT)))
1099 nLength = m_nLevel;
1100 }
1101 m_bForceLineBreak = false;
1102 m_bAllowLineBreak = false;
1103 return nLength;
1104}
1105
1106bool isFirstCharWhitespace(const sal_Unicode* p) noexcept { return *p == ' '; }
1107
1108// XServiceInfo
1109OUString SAXWriter::getImplementationName() { return "com.sun.star.extensions.xml.sax.Writer"; }
1110
1111// XServiceInfo
1112sal_Bool SAXWriter::supportsService(const OUString& ServiceName)
1113{
1114 return cppu::supportsService(this, ServiceName);
1115}
1116
1117// XServiceInfo
1118Sequence<OUString> SAXWriter::getSupportedServiceNames()
1119{
1120 return { "com.sun.star.xml.sax.Writer" };
1121}
1122
1123void SAXWriter::startDocument()
1124{
1125 if (m_bDocStarted || !m_out.is() || !m_pSaxWriterHelper)
1126 {
1127 throw SAXException();
1128 }
1129 m_bDocStarted = true;
1130 m_pSaxWriterHelper->startDocument();
1131}
1132
1133void SAXWriter::endDocument()
1134{
1135 if (!m_bDocStarted)
1136 {
1137 throw SAXException("endDocument called before startDocument", Reference<XInterface>(),
1138 Any());
1139 }
1140 if (m_nLevel)
1141 {
1142 throw SAXException("unexpected end of document", Reference<XInterface>(), Any());
1143 }
1144 m_pSaxWriterHelper->endDocument();
1145 try
1146 {
1147 m_out->closeOutput();
1148 }
1149 catch (const IOException&)
1150 {
1151 css::uno::Any anyEx = cppu::getCaughtException();
1152 throw SAXException("IO exception during closing the IO Stream", Reference<XInterface>(),
1153 anyEx);
1154 }
1155}
1156
1157void SAXWriter::startElement(const OUString& aName, const Reference<XAttributeList>& xAttribs)
1158{
1159 if (!m_bDocStarted)
1160 {
1161 SAXException except;
1162 except.Message = "startElement called before startDocument";
1163 throw except;
1164 }
1165 if (m_bIsCDATA)
1166 {
1167 SAXException except;
1168 except.Message = "startElement call not allowed with CDATA sections";
1169 throw except;
1170 }
1171
1172 sal_Int32 nLength(0);
1173 if (m_bAllowLineBreak)
1174 {
1175 sal_Int32 nAttribCount = xAttribs.is() ? xAttribs->getLength() : 0;
1176
1177 nLength++; // "<"
1178 nLength += m_pSaxWriterHelper->calcXMLByteLength(aName, false, false); // the tag name
1179
1180 sal_Int16 n;
1181 for (n = 0; n < static_cast<sal_Int16>(nAttribCount); n++)
1182 {
1183 nLength++; // " "
1184 OUString tmp = xAttribs->getNameByIndex(n);
1185
1186 nLength += m_pSaxWriterHelper->calcXMLByteLength(tmp, false, false);
1187
1188 nLength += 2; // ="
1189
1190 tmp = xAttribs->getValueByIndex(n);
1191
1192 nLength += m_pSaxWriterHelper->calcXMLByteLength(tmp, true, true);
1193
1194 nLength += 1; // "
1195 }
1196
1197 nLength++; // '>'
1198 }
1199
1200 // Is there a new indentation necessary ?
1201 sal_Int32 nPrefix(getIndentPrefixLength(nLength));
1202
1203 // write into sequence
1204 if (nPrefix >= 0)
1205 m_pSaxWriterHelper->insertIndentation(nPrefix);
1206
1207 SaxInvalidCharacterError eRet(m_pSaxWriterHelper->startElement(aName, xAttribs));
1208
1209 m_nLevel++;
1210
1211 if (eRet == SAX_WARNING)
1212 {
1213 SAXInvalidCharacterException except;
1214 except.Message = "Invalid character during XML-Export in an attribute value";
1215 throw except;
1216 }
1217 else if (eRet == SAX_ERROR)
1218 {
1219 SAXException except;
1220 except.Message = "Invalid character during XML-Export";
1221 throw except;
1222 }
1223}
1224
1225void SAXWriter::endElement(const OUString& aName)
1226{
1227 if (!m_bDocStarted)
1228 {
1229 throw SAXException();
1230 }
1231 m_nLevel--;
1232
1233 if (m_nLevel < 0)
1234 {
1235 throw SAXException();
1236 }
1237 bool bRet(true);
1238
1239 // check here because Helper's endElement is not always called
1240#ifdef DBG_UTIL
1241 assert(!m_pSaxWriterHelper->m_DebugStartedElements.empty());
1242 // Well-formedness constraint: Element Type Match
1243 assert(aName == m_pSaxWriterHelper->m_DebugStartedElements.top());
1244 m_pSaxWriterHelper->m_DebugStartedElements.pop();
1245#endif
1246
1247 if (m_pSaxWriterHelper->FinishEmptyElement())
1248 m_bForceLineBreak = false;
1249 else
1250 {
1251 // only ascii chars allowed
1252 sal_Int32 nLength(0);
1253 if (m_bAllowLineBreak)
1254 nLength = 3 + m_pSaxWriterHelper->calcXMLByteLength(aName, false, false);
1255 sal_Int32 nPrefix = getIndentPrefixLength(nLength);
1256
1257 if (nPrefix >= 0)
1258 m_pSaxWriterHelper->insertIndentation(nPrefix);
1259
1260 bRet = m_pSaxWriterHelper->endElement(aName);
1261 }
1262
1263 if (!bRet)
1264 {
1265 SAXException except;
1266 except.Message = "Invalid character during XML-Export";
1267 throw except;
1268 }
1269}
1270
1271void SAXWriter::characters(const OUString& aChars)
1272{
1273 if (!m_bDocStarted)
1274 {
1275 SAXException except;
1276 except.Message = "characters method called before startDocument";
1277 throw except;
1278 }
1279
1280 bool bThrowException(false);
1281 if (!aChars.isEmpty())
1282 {
1283 if (m_bIsCDATA)
1284 bThrowException = !m_pSaxWriterHelper->writeString(aChars, false, false);
1285 else
1286 {
1287 // Note : nFirstLineBreakOccurrence is not exact, because we don't know, how
1288 // many 2 and 3 byte chars are inbetween. However this whole stuff
1289 // is eitherway for pretty printing only, so it does not need to be exact.
1290 sal_Int32 nLength(0);
1291 sal_Int32 nIndentPrefix(-1);
1292 if (m_bAllowLineBreak)
1293 {
1294 // returns position of first ascii 10 within the string, -1 when no 10 in string.
1295 sal_Int32 nFirstLineBreakOccurrence = aChars.indexOf(LINEFEED);
1296
1297 nLength = m_pSaxWriterHelper->calcXMLByteLength(aChars, !m_bIsCDATA, false);
1298 nIndentPrefix = getIndentPrefixLength(
1299 nFirstLineBreakOccurrence >= 0 ? nFirstLineBreakOccurrence : nLength);
1300 }
1301 else
1302 nIndentPrefix = getIndentPrefixLength(nLength);
1303
1304 // insert indentation
1305 if (nIndentPrefix >= 0)
1306 {
1307 if (isFirstCharWhitespace(aChars.getStr()))
1308 m_pSaxWriterHelper->insertIndentation(nIndentPrefix - 1);
1309 else
1310 m_pSaxWriterHelper->insertIndentation(nIndentPrefix);
1311 }
1312 bThrowException = !m_pSaxWriterHelper->writeString(aChars, true, false);
1313 }
1314 }
1315 if (bThrowException)
1316 {
1317 SAXInvalidCharacterException except;
1318 except.Message = "Invalid character during XML-Export";
1319 throw except;
1320 }
1321}
1322
1323void SAXWriter::ignorableWhitespace(const OUString&)
1324{
1325 if (!m_bDocStarted)
1326 {
1327 throw SAXException();
1328 }
1329
1330 m_bForceLineBreak = true;
1331}
1332
1333void SAXWriter::processingInstruction(const OUString& aTarget, const OUString& aData)
1334{
1335 if (!m_bDocStarted || m_bIsCDATA)
1336 {
1337 throw SAXException();
1338 }
1339
1340 sal_Int32 nLength(0);
1341 if (m_bAllowLineBreak)
1342 {
1343 nLength = 2; // "<?"
1344 nLength += m_pSaxWriterHelper->calcXMLByteLength(aTarget, false, false);
1345
1346 nLength += 1; // " "
1347
1348 nLength += m_pSaxWriterHelper->calcXMLByteLength(aData, false, false);
1349
1350 nLength += 2; // "?>"
1351 }
1352
1353 sal_Int32 nPrefix = getIndentPrefixLength(nLength);
1354
1355 if (nPrefix >= 0)
1356 m_pSaxWriterHelper->insertIndentation(nPrefix);
1357
1358 if (!m_pSaxWriterHelper->processingInstruction(aTarget, aData))
1359 {
1360 SAXException except;
1361 except.Message = "Invalid character during XML-Export";
1362 throw except;
1363 }
1364}
1365
1366void SAXWriter::setDocumentLocator(const Reference<XLocator>&) {}
1367
1368void SAXWriter::setCustomEntityNames(
1369 const ::css::uno::Sequence<::css::beans::Pair<::rtl::OUString, ::rtl::OUString>>& replacements)
1370{
1371 m_pSaxWriterHelper->setCustomEntityNames(replacements);
1372}
1373
1374void SAXWriter::startCDATA()
1375{
1376 if (!m_bDocStarted || m_bIsCDATA)
1377 {
1378 throw SAXException();
1379 }
1380
1381 sal_Int32 nPrefix = getIndentPrefixLength(9);
1382 if (nPrefix >= 0)
1383 m_pSaxWriterHelper->insertIndentation(nPrefix);
1384
1385 m_pSaxWriterHelper->startCDATA();
1386
1387 m_bIsCDATA = true;
1388}
1389
1390void SAXWriter::endCDATA()
1391{
1392 if (!m_bDocStarted || !m_bIsCDATA)
1393 {
1394 SAXException except;
1395 except.Message = "endCDATA was called without startCDATA";
1396 throw except;
1397 }
1398
1399 sal_Int32 nPrefix = getIndentPrefixLength(3);
1400 if (nPrefix >= 0)
1401 m_pSaxWriterHelper->insertIndentation(nPrefix);
1402
1403 m_pSaxWriterHelper->endCDATA();
1404
1405 m_bIsCDATA = false;
1406}
1407
1408void SAXWriter::comment(const OUString& sComment)
1409{
1410 if (!m_bDocStarted || m_bIsCDATA)
1411 {
1412 throw SAXException();
1413 }
1414
1415 sal_Int32 nLength(0);
1416 if (m_bAllowLineBreak)
1417 {
1418 nLength = 4; // "<!--"
1419 nLength += m_pSaxWriterHelper->calcXMLByteLength(sComment, false, false);
1420
1421 nLength += 3;
1422 }
1423
1424 sal_Int32 nPrefix = getIndentPrefixLength(nLength);
1425 if (nPrefix >= 0)
1426 m_pSaxWriterHelper->insertIndentation(nPrefix);
1427
1428 if (!m_pSaxWriterHelper->comment(sComment))
1429 {
1430 SAXException except;
1431 except.Message = "Invalid character during XML-Export";
1432 throw except;
1433 }
1434}
1435
1436void SAXWriter::allowLineBreak()
1437{
1438 if (!m_bDocStarted || m_bAllowLineBreak)
1439 {
1440 throw SAXException();
1441 }
1442
1443 m_bAllowLineBreak = true;
1444}
1445
1446void SAXWriter::unknown(const OUString& sString)
1447{
1448 if (!m_bDocStarted)
1449 {
1450 throw SAXException();
1451 }
1452 if (m_bIsCDATA)
1453 {
1454 throw SAXException();
1455 }
1456
1457 if (sString.startsWith("<?xml"))
1458 return;
1459
1460 sal_Int32 nLength(0);
1461 if (m_bAllowLineBreak)
1462 nLength = m_pSaxWriterHelper->calcXMLByteLength(sString, false, false);
1463
1464 sal_Int32 nPrefix = getIndentPrefixLength(nLength);
1465 if (nPrefix >= 0)
1466 m_pSaxWriterHelper->insertIndentation(nPrefix);
1467
1468 if (!m_pSaxWriterHelper->writeString(sString, false, false))
1469 {
1470 SAXException except;
1471 except.Message = "Invalid character during XML-Export";
1472 throw except;
1473 }
1474}
1475
1476} // namespace
1477
1478extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
1480 css::uno::Sequence<css::uno::Any> const&)
1481{
1482 return cppu::acquire(new SAXWriter);
1483}
1484
1485/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 m_nLevel
int nCount
FilterGroup & rTarget
const char * name
OUString aName
void * p
sal_Int64 n
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
constexpr OUStringLiteral aData
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
Any SAL_CALL getCaughtException()
int i
end
bool getOutputStream(ProgramOptions const &options, OString const &extension, std::ostream **ppOutputStream, OString &targetSourceFileName, OString &tmpSourceFileName)
sal_uInt32 writeString(sal_uInt8 *buffer, const sal_Unicode *v)
#define MAXCOLUMNCOUNT
Definition: saxwriter.cxx:56
#define LINEFEED
Definition: saxwriter.cxx:54
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_extensions_xml_sax_Writer_get_implementation(css::uno::XComponentContext *, css::uno::Sequence< css::uno::Any > const &)
Definition: saxwriter.cxx:1479
#define SEQUENCESIZE
Definition: saxwriter.cxx:55
unsigned char sal_Bool
sal_uInt16 sal_Unicode
signed char sal_Int8
bool operator<(const wwFont &r1, const wwFont &r2)
sal_Int32 nLength
const sal_Int32 nBytesCount