LibreOffice Module sw (master) 1
swhtml.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 <sal/config.h>
21
22#include <algorithm>
23#include <memory>
24#include <config_java.h>
25
26#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
27#include <com/sun/star/document/XDocumentProperties.hpp>
28#include <com/sun/star/i18n/ScriptType.hpp>
29#include <com/sun/star/i18n/XBreakIterator.hpp>
30#include <comphelper/string.hxx>
31#include <o3tl/safeint.hxx>
32#include <rtl/ustrbuf.hxx>
33#include <svx/svxids.hrc>
34#if OSL_DEBUG_LEVEL > 0
35#include <stdlib.h>
36#endif
37#include <hintids.hxx>
38
39#include <utility>
40#include <vcl/errinf.hxx>
41#include <svl/stritem.hxx>
42#include <vcl/imap.hxx>
43#include <svtools/htmltokn.h>
44#include <svtools/htmlkywd.hxx>
45#include <svtools/ctrltool.hxx>
47#include <vcl/svapp.hxx>
48#include <sfx2/event.hxx>
49#include <sfx2/docfile.hxx>
50
51#include <sfx2/linkmgr.hxx>
52#include <editeng/kernitem.hxx>
53#include <editeng/boxitem.hxx>
54#include <editeng/fhgtitem.hxx>
56#include <editeng/postitem.hxx>
57#include <editeng/wghtitem.hxx>
59#include <editeng/udlnitem.hxx>
61#include <editeng/blinkitem.hxx>
62#include <editeng/ulspitem.hxx>
63#include <editeng/colritem.hxx>
64#include <editeng/fontitem.hxx>
66#include <editeng/lrspitem.hxx>
67#include <editeng/protitem.hxx>
68#include <editeng/flstitem.hxx>
70
71#include <frmatr.hxx>
72#include <charatr.hxx>
73#include <fmtfld.hxx>
74#include <fmtpdsc.hxx>
75#include <fmtanchr.hxx>
76#include <fmtsrnd.hxx>
77#include <fmtfsize.hxx>
78#include <fmtclds.hxx>
79#include <fchrfmt.hxx>
80#include <fmtinfmt.hxx>
81#include <fmtfollowtextflow.hxx>
82#include <fmtornt.hxx>
83#include <doc.hxx>
84#include <IDocumentUndoRedo.hxx>
92#include <IDocumentState.hxx>
93#include <pam.hxx>
94#include <ndtxt.hxx>
95#include <mdiexp.hxx>
96#include <poolfmt.hxx>
97#include <pagedesc.hxx>
98#include <IMark.hxx>
99#include <docsh.hxx>
100#include <editsh.hxx>
101#include <docufld.hxx>
102#include "swcss1.hxx"
103#include <fltini.hxx>
104#include <htmltbl.hxx>
105#include "htmlnum.hxx"
106#include "swhtml.hxx"
107#include "wrthtml.hxx"
108#include <linkenum.hxx>
109#include <breakit.hxx>
110#include <SwAppletImpl.hxx>
111#include <swdll.hxx>
112#include <txatbase.hxx>
113
114#include <sfx2/viewfrm.hxx>
115#include <svx/svdobj.hxx>
116#include <officecfg/Office/Writer.hxx>
119#include <officecfg/Office/Common.hxx>
120
121#include <swerror.h>
122#include <ndole.hxx>
123#include <unoframe.hxx>
124#include "css1atr.hxx"
125#include <frameformats.hxx>
126
127#define FONTSIZE_MASK 7
128
129#define HTML_ESC_PROP 80
130#define HTML_ESC_SUPER DFLT_ESC_SUPER
131#define HTML_ESC_SUB DFLT_ESC_SUB
132
133#define HTML_SPTYPE_BLOCK 1
134#define HTML_SPTYPE_HORI 2
135#define HTML_SPTYPE_VERT 3
136
138using namespace ::com::sun::star;
139
140// <P ALIGN=xxx>, <Hn ALIGN=xxx>, <TD ALIGN=xxx> etc.
142{
143 { OOO_STRING_SVTOOLS_HTML_AL_left, SvxAdjust::Left },
144 { OOO_STRING_SVTOOLS_HTML_AL_center, SvxAdjust::Center },
145 { OOO_STRING_SVTOOLS_HTML_AL_middle, SvxAdjust::Center }, // Netscape
146 { OOO_STRING_SVTOOLS_HTML_AL_right, SvxAdjust::Right },
147 { OOO_STRING_SVTOOLS_HTML_AL_justify, SvxAdjust::Block },
148 { OOO_STRING_SVTOOLS_HTML_AL_char, SvxAdjust::Left },
149 { nullptr, SvxAdjust(0) }
150};
151
152// <SPACER TYPE=...>
154{
158 { nullptr, 0 }
159};
160
162{
164}
165
167{
169 // HTML import into Writer, avoid loading the Writer/Web template.
170 return OUString();
171
172 static const OUStringLiteral sTemplateWithoutExt(u"internal/html");
173 SvtPathOptions aPathOpt;
174
175 // first search for OpenDocument Writer/Web template
176 // OpenDocument Writer/Web template (extension .oth)
177 OUString sTemplate( sTemplateWithoutExt + ".oth" );
178 if (aPathOpt.SearchFile( sTemplate, SvtPathOptions::Paths::Template ))
179 return sTemplate;
180
181 // no OpenDocument Writer/Web template found.
182 // search for OpenOffice.org Writer/Web template
183 sTemplate = sTemplateWithoutExt + ".stw";
184 if (aPathOpt.SearchFile( sTemplate, SvtPathOptions::Paths::Template ))
185 return sTemplate;
186
187 OSL_ENSURE( false, "The default HTML template cannot be found in the defined template directories!");
188
189 return OUString();
190}
191
193{
194 OSL_ENSURE( m_pMedium, "Where is the medium??" );
195
196 if( m_pMedium->IsRemote() || !m_pMedium->IsStorage() )
197 {
199 return true;
200 }
201 return false;
202
203}
204
205// Call for the general Reader-Interface
206ErrCode HTMLReader::Read( SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPam, const OUString & rName )
207{
209
210 if( !m_pStream )
211 {
212 OSL_ENSURE( m_pStream, "HTML-Read without stream" );
213 return ERR_SWG_READ_ERROR;
214 }
215
216 if( !m_bInsertMode )
217 {
219
220 // Set the HTML page style, when it isn't a HTML document,
221 // otherwise it's already set.
223 {
226 }
227 }
228
229 // so nobody steals the document!
230 rtl::Reference<SwDoc> xHoldAlive(&rDoc);
231 ErrCode nRet = ERRCODE_NONE;
232 tools::SvRef<SwHTMLParser> xParser = new SwHTMLParser( &rDoc, rPam, *m_pStream,
233 rName, rBaseURL, !m_bInsertMode, m_pMedium,
234 IsReadUTF8(),
236
237 SvParserState eState = xParser->CallParser();
238
239 if( SvParserState::Pending == eState )
241 else if( SvParserState::Accepted != eState )
242 {
243 const OUString sErr(OUString::number(static_cast<sal_Int32>(xParser->GetLineNr()))
244 + "," + OUString::number(static_cast<sal_Int32>(xParser->GetLinePos())));
245
246 // use the stream as transport for error number
247 nRet = *new StringErrorInfo( ERR_FORMAT_ROWCOL, sErr,
248 DialogMask::ButtonsOk | DialogMask::MessageError );
249 }
250
251 return nRet;
252}
253
255 OUString aPath,
256 OUString aBaseURL,
257 bool bReadNewDoc,
258 SfxMedium* pMed, bool bReadUTF8,
259 bool bNoHTMLComments,
260 const OUString& rNamespace )
261 : SfxHTMLParser( rIn, bReadNewDoc, pMed ),
262 m_aPathToFile(std::move( aPath )),
263 m_sBaseURL(std::move( aBaseURL )),
264 m_xAttrTab(std::make_shared<HTMLAttrTable>()),
265 m_pNumRuleInfo( new SwHTMLNumRuleInfo ),
266 m_xDoc( pD ),
267 m_pActionViewShell( nullptr ),
268 m_pSttNdIdx( nullptr ),
269 m_pFormImpl( nullptr ),
270 m_pImageMap( nullptr ),
271 m_nBaseFontStMin( 0 ),
272 m_nFontStMin( 0 ),
273 m_nDefListDeep( 0 ),
274 m_nFontStHeadStart( 0 ),
275 m_nSBModuleCnt( 0 ),
276 m_nMissingImgMaps( 0 ),
277 m_nParaCnt( 5 ),
278 // #i83625#
279 m_nContextStMin( 0 ),
280 m_nContextStAttrMin( 0 ),
281 m_nSelectEntryCnt( 0 ),
282 m_nOpenParaToken( HtmlTokenId::NONE ),
283 m_eJumpTo( JumpToMarks::NONE ),
284#ifdef DBG_UTIL
285 m_nContinue( 0 ),
286#endif
287 m_eParaAdjust( SvxAdjust::End ),
288 m_bDocInitialized( false ),
289 m_bSetModEnabled( false ),
290 m_bInFloatingFrame( false ),
291 m_bInField( false ),
292 m_bKeepUnknown( false ),
293 m_bCallNextToken( false ),
294 m_bIgnoreRawData( false ),
295 m_bLBEntrySelected ( false ),
296 m_bTAIgnoreNewPara ( false ),
297 m_bFixMarqueeWidth ( false ),
298 m_bNoParSpace( false ),
299 m_bInNoEmbed( false ),
300 m_bInTitle( false ),
301 m_bUpdateDocStat( false ),
302 m_bFixSelectWidth( false ),
303 m_bTextArea( false ),
304 m_bSelect( false ),
305 m_bInFootEndNoteAnchor( false ),
306 m_bInFootEndNoteSymbol( false ),
307 m_bIgnoreHTMLComments( bNoHTMLComments ),
308 m_bRemoveHidden( false ),
309 m_bBodySeen( false ),
310 m_bReadingHeaderOrFooter( false ),
311 m_bNotifyMacroEventRead( false ),
312 m_isInTableStructure(false),
313 m_nTableDepth( 0 ),
314 m_nFloatingFrames( 0 ),
315 m_nListItems( 0 ),
316 m_pTempViewFrame(nullptr)
317{
318 // If requested explicitly, then force ignoring of comments (don't create postits for them).
319 if (!bFuzzing)
320 {
324 }
325
326 m_nEventId = nullptr;
328
329 m_eScriptLang = HTMLScriptLanguage::Unknown;
330
331 rCursor.DeleteMark();
332 m_pPam = &rCursor; // re-use existing cursor: avoids spurious ~SwContentIndexReg assert
333 memset(m_xAttrTab.get(), 0, sizeof(HTMLAttrTable));
334
335 // Read the font sizes 1-7 from the INI file
336 if (!bFuzzing)
337 {
345 }
346 else
347 {
349 m_aFontHeights[4] = m_aFontHeights[5] = m_aFontHeights[6] = 12 * 20;
350 }
351
352 if(bReadNewDoc)
353 {
354 //CJK has different defaults, so a different object should be used for this
355 //RES_CHARTR_CJK_FONTSIZE is a valid value
357 m_xDoc->SetDefault( aFontHeight );
359 m_xDoc->SetDefault( aFontHeightCJK );
361 m_xDoc->SetDefault( aFontHeightCTL );
362
363 // #i18732# - adjust default of option 'FollowTextFlow'
364 // TODO: not sure what the appropriate default for HTML should be?
365 m_xDoc->SetDefault( SwFormatFollowTextFlow(true) );
366 }
367
368 // Change to HTML mode during the import, so that the right styles are created
369 m_bOldIsHTMLMode = m_xDoc->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE);
370 m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, true);
371
372 m_pCSS1Parser.reset(new SwCSS1Parser(m_xDoc.get(), *this, m_aFontHeights, m_sBaseURL, IsNewDoc()));
373 if (!bFuzzing)
375
376 if( bReadUTF8 )
377 {
378 SetSrcEncoding( RTL_TEXTENCODING_UTF8 );
379 }
380 else
381 {
382 SwDocShell *pDocSh = m_xDoc->GetDocShell();
383 SvKeyValueIterator *pHeaderAttrs =
384 pDocSh->GetHeaderAttributes();
385 if( pHeaderAttrs )
386 SetEncodingByHTTPHeader( pHeaderAttrs );
387 }
388 m_pCSS1Parser->SetDfltEncoding( osl_getThreadTextEncoding() );
389
390 SwDocShell* pDocSh = m_xDoc->GetDocShell();
391 if( pDocSh )
392 {
393 m_bViewCreated = true; // not, load synchronous
394
395 // a jump mark is present
396
397 if( pMed )
398 {
399 m_sJmpMark = pMed->GetURLObject().GetMark();
400 if( !m_sJmpMark.isEmpty() )
401 {
403 sal_Int32 nLastPos = m_sJmpMark.lastIndexOf( cMarkSeparator );
404 sal_Int32 nPos = nLastPos != -1 ? nLastPos : 0;
405
406 OUString sCmp;
407 if (nPos)
408 {
409 sCmp = m_sJmpMark.copy(nPos + 1).replaceAll(" ", "");
410 }
411
412 if( !sCmp.isEmpty() )
413 {
414 sCmp = sCmp.toAsciiLowerCase();
415 if( sCmp == "region" )
417 else if( sCmp == "table" )
419 else if( sCmp == "graphic" )
421 else if( sCmp == "outline" ||
422 sCmp == "text" ||
423 sCmp == "frame" )
424 m_eJumpTo = JumpToMarks::NONE; // this is nothing valid!
425 else
426 // otherwise this is a normal (book)mark
427 nPos = -1;
428 }
429 else
430 nPos = -1;
431
432 if( nPos != -1 )
433 m_sJmpMark = m_sJmpMark.copy( 0, nPos );
434 if( m_sJmpMark.isEmpty() )
436 }
437 }
438 }
439
440 if (!rNamespace.isEmpty())
441 {
442 SetNamespace(rNamespace);
443 m_bXHTML = true;
444 if (rNamespace == "reqif-xhtml")
445 m_bReqIF = true;
446 }
447
448 // Extract load parameters which are specific to this filter.
449 if (!pMed)
450 {
451 return;
452 }
453
454 comphelper::SequenceAsHashMap aLoadMap(pMed->GetArgs());
455 auto it = aLoadMap.find("AllowedRTFOLEMimeTypes");
456 if (it == aLoadMap.end())
457 {
458 return;
459 }
460
461 uno::Sequence<OUString> aTypes;
462 it->second >>= aTypes;
463 m_aAllowedRTFOLEMimeTypes = comphelper::sequenceToContainer<std::set<OUString>>(aTypes);
464}
465
467{
468#ifdef DBG_UTIL
469 OSL_ENSURE( !m_nContinue, "DTOR in continue!" );
470#endif
471
472 OSL_ENSURE(m_aContexts.empty(), "There are still contexts on the stack");
473 OSL_ENSURE(!m_nContextStMin, "There are protected contexts");
474 m_nContextStMin = 0;
475 while (!m_aContexts.empty())
476 {
477 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
478 ClearContext(xCntxt.get());
479 }
480
481 bool bAsync = m_xDoc->IsInLoadAsynchron();
482 m_xDoc->SetInLoadAsynchron( false );
483 m_xDoc->getIDocumentSettingAccess().set(DocumentSettingId::HTML_MODE, m_bOldIsHTMLMode);
484
485 if( m_xDoc->GetDocShell() && m_nEventId )
487
488 // the DocumentDetected maybe can delete the DocShells, therefore fetch again
489 if( m_xDoc->GetDocShell() )
490 {
491 // update linked sections
492 sal_uInt16 nLinkMode = m_xDoc->getIDocumentSettingAccess().getLinkUpdateMode( true );
493 if( nLinkMode != NEVER && bAsync &&
494 SfxObjectCreateMode::INTERNAL!=m_xDoc->GetDocShell()->GetCreateMode() )
495 m_xDoc->getIDocumentLinksAdministration().GetLinkManager().UpdateAllLinks( nLinkMode == MANUAL, false, nullptr );
496
497 if ( m_xDoc->GetDocShell()->IsLoading() )
498 {
499 // #i59688#
500 m_xDoc->GetDocShell()->LoadingFinished();
501 }
502 }
503
504 delete m_pSttNdIdx;
505
506 if( !m_aSetAttrTab.empty() )
507 {
508 OSL_ENSURE( m_aSetAttrTab.empty(),"There are still attributes on the stack" );
509 for ( const auto& rpAttr : m_aSetAttrTab )
510 delete rpAttr;
511 m_aSetAttrTab.clear();
512 }
513
514 m_pCSS1Parser.reset();
515 m_pNumRuleInfo.reset();
517 m_pFootEndNoteImpl.reset();
518
519 OSL_ENSURE(!m_xTable, "It exists still an open table");
520 m_pImageMaps.reset();
521
522 OSL_ENSURE( m_vPendingStack.empty(),
523 "SwHTMLParser::~SwHTMLParser: Here should not be Pending-Stack anymore" );
524 m_vPendingStack.clear();
525
526 m_xDoc.clear();
527
528 if ( m_pTempViewFrame )
529 {
531
532 // the temporary view frame is hidden, so the hidden flag might need to be removed
533 if ( m_bRemoveHidden && m_xDoc.is() && m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->GetMedium() )
534 m_xDoc->GetDocShell()->GetMedium()->GetItemSet()->ClearItem( SID_HIDDEN );
535 }
536}
537
538IMPL_LINK_NOARG( SwHTMLParser, AsyncCallback, void*, void )
539{
540 m_nEventId=nullptr;
541
542 // #i47907# - If the document has already been destructed,
543 // the parser should be aware of this:
544 if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
545 || 1 == m_xDoc->getReferenceCount() )
546 {
547 // was the import aborted by SFX?
548 eState = SvParserState::Error;
549 }
550
551 GetAsynchCallLink().Call(nullptr);
552}
553
555{
556 // create temporary index on position 0, so it won't be moved!
557 m_pSttNdIdx = new SwNodeIndex( m_xDoc->GetNodes() );
558 if( !IsNewDoc() ) // insert into existing document ?
559 {
560 const SwPosition* pPos = m_pPam->GetPoint();
561
562 m_xDoc->getIDocumentContentOperations().SplitNode( *pPos, false );
563
564 *m_pSttNdIdx = pPos->GetNodeIndex()-1;
565 m_xDoc->getIDocumentContentOperations().SplitNode( *pPos, false );
566
567 SwPaM aInsertionRangePam( *pPos );
568
570
571 // split any redline over the insertion point
572 aInsertionRangePam.SetMark();
573 *aInsertionRangePam.GetPoint() = *m_pPam->GetPoint();
574 aInsertionRangePam.Move( fnMoveBackward );
575 m_xDoc->getIDocumentRedlineAccess().SplitRedline( aInsertionRangePam );
576
577 m_xDoc->SetTextFormatColl( *m_pPam,
578 m_pCSS1Parser->GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
579 }
580
581 if( GetMedium() )
582 {
583 if( !m_bViewCreated )
584 {
585 m_nEventId = Application::PostUserEvent( LINK( this, SwHTMLParser, AsyncCallback ) );
586 }
587 else
588 {
589 m_bViewCreated = true;
590 m_nEventId = nullptr;
591 }
592 }
593 else // show progress bar
594 {
595 rInput.Seek(STREAM_SEEK_TO_END);
596 rInput.ResetError();
597
598 m_xProgress.reset(new ImportProgress(m_xDoc->GetDocShell(), 0, rInput.Tell()));
599
600 rInput.Seek(STREAM_SEEK_TO_BEGIN);
601 rInput.ResetError();
602 }
603
604 StartListening(m_xDoc->GetPageDesc( 0 ).GetNotifier());
605
607 return eRet;
608}
609
611{
612 const SwNode *pPrev = m_xDoc->GetNodes()[nNodeIdx - 1];
613 return pPrev->IsContentNode() || (pPrev->IsEndNode() && pPrev->StartOfSectionNode()->IsSectionNode());
614}
615
617{
618#ifdef DBG_UTIL
619 OSL_ENSURE(!m_nContinue, "Continue in Continue - not supposed to happen");
620 m_nContinue++;
621#endif
622
623 // When the import (of SFX) is aborted, an error will be set but
624 // we still continue, so that we clean up properly.
625 OSL_ENSURE( SvParserState::Error!=eState,
626 "SwHTMLParser::Continue: already set an error" );
627 if( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
628 eState = SvParserState::Error;
629
630 // Fetch SwViewShell from document, save it and set as current.
631 SwViewShell *pInitVSh = CallStartAction();
632
633 if( SvParserState::Error != eState && GetMedium() && !m_bViewCreated )
634 {
635 // At first call first return, show document and wait for callback
636 // time.
637 // At this point in CallParser only one digit was read and
638 // a SaveState(0) was called.
639 eState = SvParserState::Pending;
640 m_bViewCreated = true;
641 m_xDoc->SetInLoadAsynchron( true );
642
643#ifdef DBG_UTIL
644 m_nContinue--;
645#endif
646
647 return;
648 }
649
650 m_bSetModEnabled = false;
651 if( m_xDoc->GetDocShell() )
652 {
653 m_bSetModEnabled = m_xDoc->GetDocShell()->IsEnableSetModified();
654 if( m_bSetModEnabled )
655 {
656 m_xDoc->GetDocShell()->EnableSetModified( false );
657 }
658 }
659
660 // during import don't call OLE-Modified
661 Link<bool,void> aOLELink( m_xDoc->GetOle2Link() );
662 m_xDoc->SetOle2Link( Link<bool,void>() );
663
664 bool bModified = m_xDoc->getIDocumentState().IsModified();
665 bool const bWasUndo = m_xDoc->GetIDocumentUndoRedo().DoesUndo();
666 m_xDoc->GetIDocumentUndoRedo().DoUndo(false);
667
668 // When the import will be aborted, don't call Continue anymore.
669 // If a Pending-Stack exists make sure the stack is ended with a call
670 // of NextToken.
671 if( SvParserState::Error == eState )
672 {
673 OSL_ENSURE( m_vPendingStack.empty() || m_vPendingStack.back().nToken != HtmlTokenId::NONE,
674 "SwHTMLParser::Continue: Pending-Stack without Token" );
675 if( !m_vPendingStack.empty() && m_vPendingStack.back().nToken != HtmlTokenId::NONE )
676 NextToken( m_vPendingStack.back().nToken );
677 OSL_ENSURE( m_vPendingStack.empty(),
678 "SwHTMLParser::Continue: There is again a Pending-Stack" );
679 }
680 else
681 {
682 HTMLParser::Continue( !m_vPendingStack.empty() ? m_vPendingStack.back().nToken : nToken );
683 }
684
685 // disable progress bar again
686 m_xProgress.reset();
687
688 bool bLFStripped = false;
689 if( SvParserState::Pending != GetStatus() )
690 {
691 // set the last attributes yet
692 {
693 if( !m_aScriptSource.isEmpty() )
694 {
695 SwScriptFieldType *pType =
696 static_cast<SwScriptFieldType*>(m_xDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Script ));
697
699 false );
700 InsertAttr( SwFormatField( aField ), false );
701 }
702
703 if( m_pAppletImpl )
704 {
705 if( m_pAppletImpl->GetApplet().is() )
706 EndApplet();
707 else
708 EndObject();
709 }
710
711 // maybe remove an existing LF after the last paragraph
712 if( IsNewDoc() )
713 bLFStripped = StripTrailingLF() > 0;
714
715 // close still open numbering
716 while( GetNumInfo().GetNumRule() )
718
719 OSL_ENSURE( !m_nContextStMin, "There are protected contexts" );
720 // try this twice, first normally to let m_nContextStMin decrease
721 // naturally and get contexts popped in desired order, and if that
722 // fails force it
723 for (int i = 0; i < 2; ++i)
724 {
725 while (m_aContexts.size() > m_nContextStMin)
726 {
727 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext());
728 if (xCntxt)
729 EndContext(xCntxt.get());
730 }
731 if (!m_nContextStMin)
732 break;
733 OSL_ENSURE(!m_nContextStMin, "There are still protected contexts");
734 m_nContextStMin = 0;
735 }
736
737 m_aParaAttrs.clear();
738
739 SetAttr( false );
740
741 // set the first delayed styles
742 m_pCSS1Parser->SetDelayedStyles();
743 }
744
745 // again correct the start
746 if( !IsNewDoc() && m_pSttNdIdx->GetIndex() )
747 {
748 SwTextNode* pTextNode = m_pSttNdIdx->GetNode().GetTextNode();
749 SwNodeIndex aNxtIdx( *m_pSttNdIdx );
750 if( pTextNode && pTextNode->CanJoinNext( &aNxtIdx ))
751 {
752 const sal_Int32 nStt = pTextNode->GetText().getLength();
753 // when the cursor is still in the node, then set him at the end
754 if( m_pPam->GetPoint()->GetNode() == aNxtIdx.GetNode() )
755 {
756 m_pPam->GetPoint()->Assign( *pTextNode, nStt );
757 }
758
759#if OSL_DEBUG_LEVEL > 0
760// !!! shouldn't be possible, or ??
761 OSL_ENSURE( m_pSttNdIdx->GetIndex()+1 != m_pPam->GetBound().GetNodeIndex(),
762 "Pam.Bound1 is still in the node" );
763 OSL_ENSURE( m_pSttNdIdx->GetIndex()+1 != m_pPam->GetBound( false ).GetNodeIndex(),
764 "Pam.Bound2 is still in the node" );
765
767 {
768 const sal_Int32 nCntPos = m_pPam->GetBound().GetContentIndex();
770 pTextNode->GetText().getLength() + nCntPos );
771 }
772 if( m_pSttNdIdx->GetIndex()+1 == m_pPam->GetBound( false ).GetNodeIndex() )
773 {
774 const sal_Int32 nCntPos = m_pPam->GetBound( false ).GetContentIndex();
775 m_pPam->GetBound( false ).SetContent(
776 pTextNode->GetText().getLength() + nCntPos );
777 }
778#endif
779 // Keep character attribute!
780 SwTextNode* pDelNd = aNxtIdx.GetNode().GetTextNode();
781 if (pTextNode->GetText().getLength())
782 pDelNd->FormatToTextAttr( pTextNode );
783 else
784 pTextNode->ChgFormatColl( pDelNd->GetTextColl() );
785 pTextNode->JoinNext();
786 }
787 }
788 }
789
790 if( SvParserState::Accepted == eState )
791 {
793 {
794 // Some Image-Map relations are still missing.
795 // Maybe now the Image-Maps are there?
797 }
798
799 // now remove the last useless paragraph
800 SwPosition* pPos = m_pPam->GetPoint();
801 if( !pPos->GetContentIndex() && !bLFStripped )
802 {
803 SwTextNode* pCurrentNd;
804 SwNodeOffset nNodeIdx = pPos->GetNodeIndex();
805
806 bool bHasFlysOrMarks =
808
809 if( IsNewDoc() )
810 {
811 if (!m_pPam->GetPoint()->GetContentIndex() && CanRemoveNode(nNodeIdx))
812 {
814 if( pCNd && pCNd->StartOfSectionIndex()+2 <
815 pCNd->EndOfSectionIndex() && !bHasFlysOrMarks )
816 {
818 SwCursorShell *pCursorSh = dynamic_cast<SwCursorShell *>( pVSh );
819 if( pCursorSh &&
820 pCursorSh->GetCursor()->GetPoint()
821 ->GetNodeIndex() == nNodeIdx )
822 {
823 pCursorSh->MovePara(GoPrevPara, fnParaEnd );
824 pCursorSh->SetMark();
825 pCursorSh->ClearMark();
826 }
827 SwNode& rDelNode = m_pPam->GetPoint()->GetNode();
828 // move so we don't have a dangling SwContentIndex to the deleted node
830 if (m_pPam->HasMark())
832 m_xDoc->GetNodes().Delete( rDelNode );
833 }
834 }
835 }
836 else if( nullptr != ( pCurrentNd = m_xDoc->GetNodes()[ nNodeIdx ]->GetTextNode()) && !bHasFlysOrMarks )
837 {
838 if( pCurrentNd->CanJoinNext( pPos ))
839 {
840 SwTextNode* pNextNd = pPos->GetNode().GetTextNode();
842 pNextNd->JoinPrev();
843 }
844 else if (pCurrentNd->GetText().isEmpty())
845 {
847 SwNode& rDelNode = pPos->GetNode();
848 // move so we don't have a dangling SwContentIndex to the deleted node
850 m_xDoc->GetNodes().Delete( rDelNode );
852 }
853 }
854 }
855
856 // annul the SplitNode from the beginning
857 else if( !IsNewDoc() )
858 {
859 if( pPos->GetContentIndex() ) // then there was no <p> at the end
860 m_pPam->Move( fnMoveForward, GoInNode ); // therefore to the next
861 SwTextNode* pTextNode = pPos->GetNode().GetTextNode();
862 SwNodeIndex aPrvIdx( pPos->GetNode() );
863 if( pTextNode && pTextNode->CanJoinPrev( &aPrvIdx ) &&
864 *m_pSttNdIdx <= aPrvIdx )
865 {
866 // Normally here should take place a JoinNext, but all cursors and
867 // so are registered in pTextNode, so that it MUST remain.
868
869 // Convert paragraph to character attribute, from Prev adopt
870 // the paragraph attribute and the template!
871 SwTextNode* pPrev = aPrvIdx.GetNode().GetTextNode();
872 pTextNode->ChgFormatColl( pPrev->GetTextColl() );
873 pTextNode->FormatToTextAttr( pPrev );
874 pTextNode->ResetAllAttr();
875
876 if( pPrev->HasSwAttrSet() )
877 pTextNode->SetAttr( *pPrev->GetpSwAttrSet() );
878
879 if( &m_pPam->GetBound().GetNode() == pPrev )
880 m_pPam->GetBound().nContent.Assign( pTextNode, 0 );
881 if( &m_pPam->GetBound(false).GetNode() == pPrev )
882 m_pPam->GetBound(false).nContent.Assign( pTextNode, 0 );
883
884 pTextNode->JoinPrev();
885 }
886 }
887
888 // adjust AutoLoad in DocumentProperties
889 if (!bFuzzing && IsNewDoc())
890 {
891 SwDocShell *pDocShell(m_xDoc->GetDocShell());
892 OSL_ENSURE(pDocShell, "no SwDocShell");
893 if (pDocShell) {
894 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
895 pDocShell->GetModel(), uno::UNO_QUERY_THROW);
896 uno::Reference<document::XDocumentProperties> xDocProps(
897 xDPS->getDocumentProperties());
898 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
899 if ( xDocProps.is() && (xDocProps->getAutoloadSecs() > 0) &&
900 (xDocProps->getAutoloadURL().isEmpty()) )
901 {
902 xDocProps->setAutoloadURL(m_aPathToFile);
903 }
904 }
905 }
906
907 if( m_bUpdateDocStat )
908 {
909 m_xDoc->getIDocumentStatistics().UpdateDocStat( false, true );
910 }
911 }
912
913 if( SvParserState::Pending != GetStatus() )
914 {
915 delete m_pSttNdIdx;
916 m_pSttNdIdx = nullptr;
917 }
918
919 // should the parser be the last one who hold the document, then nothing
920 // has to be done anymore, document will be destroyed shortly!
921 if( 1 < m_xDoc->getReferenceCount() )
922 {
923 if( bWasUndo )
924 {
925 m_xDoc->GetIDocumentUndoRedo().DelAllUndoObj();
926 m_xDoc->GetIDocumentUndoRedo().DoUndo(true);
927 }
928 else if( !pInitVSh )
929 {
930 // When at the beginning of Continue no Shell was available,
931 // it's possible in the meantime one was created.
932 // In that case the bWasUndo flag is wrong and we must
933 // enable Undo.
935 if( pTmpVSh )
936 {
937 m_xDoc->GetIDocumentUndoRedo().DoUndo(true);
938 }
939 }
940
941 m_xDoc->SetOle2Link( aOLELink );
942 if( !bModified )
943 m_xDoc->getIDocumentState().ResetModified();
944 if( m_bSetModEnabled && m_xDoc->GetDocShell() )
945 {
946 m_xDoc->GetDocShell()->EnableSetModified();
947 m_bSetModEnabled = false; // this is unnecessary here
948 }
949 }
950
951 // When the Document-SwVievShell still exists and an Action is open
952 // (doesn't have to be by abort), end the Action, disconnect from Shell
953 // and finally reconstruct the old Shell.
954 CallEndAction( true );
955
956#ifdef DBG_UTIL
957 m_nContinue--;
958#endif
959}
960
962{
963 if(rHint.GetId() == SfxHintId::Dying)
964 {
966 ReleaseRef();
967 }
968}
969
971{
972 OSL_ENSURE( !m_bDocInitialized, "DocumentDetected called multiple times" );
973 m_bDocInitialized = true;
974 if( IsNewDoc() )
975 {
976 if( IsInHeader() )
977 FinishHeader();
978
979 CallEndAction( true );
980
981 m_xDoc->GetIDocumentUndoRedo().DoUndo(false);
982 // For DocumentDetected in general a SwViewShell is created.
983 // But it also can be created later, in case the UI is captured.
985 }
986}
987
988// is called for every token that is recognised in CallParser
990{
991 if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
992 || 1 == m_xDoc->getReferenceCount() )
993 {
994 // Was the import cancelled by SFX? If a pending stack
995 // exists, clean it.
996 eState = SvParserState::Error;
997 OSL_ENSURE( m_vPendingStack.empty() || m_vPendingStack.back().nToken != HtmlTokenId::NONE,
998 "SwHTMLParser::NextToken: Pending-Stack without token" );
999 if( 1 == m_xDoc->getReferenceCount() || m_vPendingStack.empty() )
1000 return ;
1001 }
1002
1003#if OSL_DEBUG_LEVEL > 0
1004 if( !m_vPendingStack.empty() )
1005 {
1006 switch( nToken )
1007 {
1008 // tables are read by recursive method calls
1009 case HtmlTokenId::TABLE_ON:
1010 // For CSS declarations we might have to wait
1011 // for a file download to finish
1012 case HtmlTokenId::LINK:
1013 // For controls we might have to set the size.
1014 case HtmlTokenId::INPUT:
1015 case HtmlTokenId::TEXTAREA_ON:
1016 case HtmlTokenId::SELECT_ON:
1017 case HtmlTokenId::SELECT_OFF:
1018 break;
1019 default:
1020 OSL_ENSURE( m_vPendingStack.empty(), "Unknown token for Pending-Stack" );
1021 break;
1022 }
1023 }
1024#endif
1025
1026 // The following special cases have to be treated before the
1027 // filter detection, because Netscape doesn't reference the content
1028 // of the title for filter detection either.
1029 if( m_vPendingStack.empty() )
1030 {
1031 if( m_bInTitle )
1032 {
1033 switch( nToken )
1034 {
1035 case HtmlTokenId::TITLE_OFF:
1036 {
1037 OUString sTitle = m_sTitle.makeStringAndClear();
1038 if( IsNewDoc() && !sTitle.isEmpty() )
1039 {
1040 if( m_xDoc->GetDocShell() ) {
1041 uno::Reference<document::XDocumentPropertiesSupplier>
1042 xDPS(m_xDoc->GetDocShell()->GetModel(),
1043 uno::UNO_QUERY_THROW);
1044 uno::Reference<document::XDocumentProperties> xDocProps(
1045 xDPS->getDocumentProperties());
1046 OSL_ENSURE(xDocProps.is(), "no DocumentProperties");
1047 if (xDocProps.is()) {
1048 xDocProps->setTitle(sTitle);
1049 }
1050
1051 m_xDoc->GetDocShell()->SetTitle(sTitle);
1052 }
1053 }
1054 m_bInTitle = false;
1055 break;
1056 }
1057
1058 case HtmlTokenId::NONBREAKSPACE:
1059 m_sTitle.append(" ");
1060 break;
1061
1062 case HtmlTokenId::SOFTHYPH:
1063 m_sTitle.append("-");
1064 break;
1065
1066 case HtmlTokenId::TEXTTOKEN:
1067 m_sTitle.append(aToken);
1068 break;
1069
1070 default:
1071 m_sTitle.append("<");
1072 if( (nToken >= HtmlTokenId::ONOFF_START) && isOffToken(nToken) )
1073 m_sTitle.append("/");
1074 m_sTitle.append(sSaveToken);
1075 if( !aToken.isEmpty() )
1076 {
1077 m_sTitle.append(" ");
1078 m_sTitle.append(aToken);
1079 }
1080 m_sTitle.append(">");
1081 break;
1082 }
1083
1084 return;
1085 }
1086 }
1087
1088 // Find out what type of document it is if we don't know already.
1089 // For Controls this has to be finished before the control is inserted
1090 // because for inserting a View is needed.
1091 if( !m_bDocInitialized )
1093
1094 bool bGetIDOption = false, bInsertUnknown = false;
1095 bool bUpperSpaceSave = m_bUpperSpace;
1096 m_bUpperSpace = false;
1097
1098 // The following special cases may or have to be treated after the
1099 // filter detection
1100 if( m_vPendingStack.empty() )
1101 {
1102 if( m_bInFloatingFrame )
1103 {
1104 // <SCRIPT> is ignored here (from us), because it is ignored in
1105 // Applets as well
1106 if( HtmlTokenId::IFRAME_OFF == nToken )
1107 {
1108 m_bCallNextToken = false;
1109 m_bInFloatingFrame = false;
1110 }
1111
1112 return;
1113 }
1114 else if( m_bInNoEmbed )
1115 {
1116 switch( nToken )
1117 {
1118 case HtmlTokenId::NOEMBED_OFF:
1121 m_aContents.clear();
1122 m_bCallNextToken = false;
1123 m_bInNoEmbed = false;
1124 break;
1125
1126 case HtmlTokenId::RAWDATA:
1128 break;
1129
1130 default:
1131 OSL_ENSURE( false, "SwHTMLParser::NextToken: invalid tag" );
1132 break;
1133 }
1134
1135 return;
1136 }
1137 else if( m_pAppletImpl )
1138 {
1139 // in an applet only <PARAM> tags and the </APPLET> tag
1140 // are of interest for us (for the moment)
1141 // <SCRIPT> is ignored here (from Netscape)!
1142
1143 switch( nToken )
1144 {
1145 case HtmlTokenId::APPLET_OFF:
1146 m_bCallNextToken = false;
1147 EndApplet();
1148 break;
1149 case HtmlTokenId::OBJECT_OFF:
1150 m_bCallNextToken = false;
1151 EndObject();
1152 break;
1153 case HtmlTokenId::PARAM:
1154 InsertParam();
1155 break;
1156 default: break;
1157 }
1158
1159 return;
1160 }
1161 else if( m_bTextArea )
1162 {
1163 // in a TextArea everything up to </TEXTAREA> is inserted as text.
1164 // <SCRIPT> is ignored here (from Netscape)!
1165
1166 switch( nToken )
1167 {
1168 case HtmlTokenId::TEXTAREA_OFF:
1169 m_bCallNextToken = false;
1170 EndTextArea();
1171 break;
1172
1173 default:
1175 break;
1176 }
1177
1178 return;
1179 }
1180 else if( m_bSelect )
1181 {
1182 // HAS to be treated after bNoScript!
1183 switch( nToken )
1184 {
1185 case HtmlTokenId::SELECT_OFF:
1186 m_bCallNextToken = false;
1187 EndSelect();
1188 return;
1189
1190 case HtmlTokenId::OPTION:
1192 return;
1193
1194 case HtmlTokenId::TEXTTOKEN:
1196 return;
1197
1198 case HtmlTokenId::INPUT:
1199 case HtmlTokenId::SCRIPT_ON:
1200 case HtmlTokenId::SCRIPT_OFF:
1201 case HtmlTokenId::NOSCRIPT_ON:
1202 case HtmlTokenId::NOSCRIPT_OFF:
1203 case HtmlTokenId::RAWDATA:
1204 // treat in normal switch
1205 break;
1206
1207 default:
1208 // ignore
1209 return;
1210 }
1211 }
1212 else if( m_pMarquee )
1213 {
1214 // in a TextArea everything up to </TEXTAREA> is inserted as text.
1215 // The <SCRIPT> tags are ignored from MS-IE, we ignore the whole
1216 // script.
1217 switch( nToken )
1218 {
1219 case HtmlTokenId::MARQUEE_OFF:
1220 m_bCallNextToken = false;
1221 EndMarquee();
1222 break;
1223
1224 case HtmlTokenId::TEXTTOKEN:
1226 break;
1227 default: break;
1228 }
1229
1230 return;
1231 }
1232 else if( m_bInField )
1233 {
1234 switch( nToken )
1235 {
1236 case HtmlTokenId::SDFIELD_OFF:
1237 m_bCallNextToken = false;
1238 EndField();
1239 break;
1240
1241 case HtmlTokenId::TEXTTOKEN:
1243 break;
1244 default: break;
1245 }
1246
1247 return;
1248 }
1250 {
1251 switch( nToken )
1252 {
1253 case HtmlTokenId::ANCHOR_OFF:
1254 EndAnchor();
1255 m_bCallNextToken = false;
1256 break;
1257
1258 case HtmlTokenId::TEXTTOKEN:
1260 break;
1261 default: break;
1262 }
1263 return;
1264 }
1265 else if( !m_aUnknownToken.isEmpty() )
1266 {
1267 // Paste content of unknown tags.
1268 // (but surely if we are not in the header section) fdo#36080 fdo#34666
1269 if (!aToken.isEmpty() && !IsInHeader() )
1270 {
1271 if( !m_bDocInitialized )
1273 m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, aToken.toString());
1274
1275 // if there are temporary paragraph attributes and the
1276 // paragraph isn't empty then the paragraph attributes
1277 // are final.
1278 m_aParaAttrs.clear();
1279
1280 SetAttr();
1281 }
1282
1283 // Unknown token in the header are only closed by a matching
1284 // end-token, </HEAD> or <BODY>. Text inside is ignored.
1285 switch( nToken )
1286 {
1287 case HtmlTokenId::UNKNOWNCONTROL_OFF:
1288 if( m_aUnknownToken != sSaveToken )
1289 return;
1290 [[fallthrough]];
1291 case HtmlTokenId::FRAMESET_ON:
1292 case HtmlTokenId::HEAD_OFF:
1293 case HtmlTokenId::BODY_ON:
1294 case HtmlTokenId::IMAGE: // Don't know why Netscape acts this way.
1295 m_aUnknownToken.clear();
1296 break;
1297 case HtmlTokenId::TEXTTOKEN:
1298 return;
1299 default:
1300 m_aUnknownToken.clear();
1301 break;
1302 }
1303 }
1304 }
1305
1306 switch( nToken )
1307 {
1308 case HtmlTokenId::BODY_ON:
1309 if (!m_bBodySeen)
1310 {
1311 m_bBodySeen = true;
1312 if( !m_aStyleSource.isEmpty() )
1313 {
1314 m_pCSS1Parser->ParseStyleSheet( m_aStyleSource );
1315 m_aStyleSource.clear();
1316 }
1317 if( IsNewDoc() )
1318 {
1320 // If there is a template for the first or the right page,
1321 // it is set here.
1322 const SwPageDesc *pPageDesc = nullptr;
1323 if( m_pCSS1Parser->IsSetFirstPageDesc() )
1324 pPageDesc = m_pCSS1Parser->GetFirstPageDesc();
1325 else if( m_pCSS1Parser->IsSetRightPageDesc() )
1326 pPageDesc = m_pCSS1Parser->GetRightPageDesc();
1327
1328 if( pPageDesc )
1329 {
1330 m_xDoc->getIDocumentContentOperations().InsertPoolItem( *m_pPam, SwFormatPageDesc( pPageDesc ) );
1331 }
1332 }
1333 }
1334 break;
1335
1336 case HtmlTokenId::LINK:
1337 InsertLink();
1338 break;
1339
1340 case HtmlTokenId::BASE:
1341 {
1342 const HTMLOptions& rHTMLOptions = GetOptions();
1343 for (size_t i = rHTMLOptions.size(); i; )
1344 {
1345 const HTMLOption& rOption = rHTMLOptions[--i];
1346 switch( rOption.GetToken() )
1347 {
1348 case HtmlOptionId::HREF:
1349 m_sBaseURL = rOption.GetString();
1350 break;
1351 case HtmlOptionId::TARGET:
1352 if( IsNewDoc() )
1353 {
1354 SwDocShell *pDocShell(m_xDoc->GetDocShell());
1355 OSL_ENSURE(pDocShell, "no SwDocShell");
1356 if (pDocShell) {
1357 uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
1358 pDocShell->GetModel(), uno::UNO_QUERY_THROW);
1359 uno::Reference<document::XDocumentProperties>
1360 xDocProps(xDPS->getDocumentProperties());
1361 OSL_ENSURE(xDocProps.is(),"no DocumentProperties");
1362 if (xDocProps.is()) {
1363 xDocProps->setDefaultTarget(
1364 rOption.GetString());
1365 }
1366 }
1367 }
1368 break;
1369 default: break;
1370 }
1371 }
1372 }
1373 break;
1374
1375 case HtmlTokenId::META:
1376 {
1377 SvKeyValueIterator *pHTTPHeader = nullptr;
1378 if( IsNewDoc() )
1379 {
1380 SwDocShell *pDocSh = m_xDoc->GetDocShell();
1381 if( pDocSh )
1382 pHTTPHeader = pDocSh->GetHeaderAttributes();
1383 }
1384 SwDocShell *pDocShell(m_xDoc->GetDocShell());
1385 OSL_ENSURE(pDocShell, "no SwDocShell");
1386 if (pDocShell)
1387 {
1388 uno::Reference<document::XDocumentProperties> xDocProps;
1389 if (IsNewDoc())
1390 {
1391 const uno::Reference<document::XDocumentPropertiesSupplier>
1392 xDPS( pDocShell->GetModel(), uno::UNO_QUERY_THROW );
1393 xDocProps = xDPS->getDocumentProperties();
1394 OSL_ENSURE(xDocProps.is(), "DocumentProperties is null");
1395 }
1396 ParseMetaOptions( xDocProps, pHTTPHeader );
1397 }
1398 }
1399 break;
1400
1401 case HtmlTokenId::TITLE_ON:
1402 m_bInTitle = true;
1403 break;
1404
1405 case HtmlTokenId::SCRIPT_ON:
1406 NewScript();
1407 break;
1408
1409 case HtmlTokenId::SCRIPT_OFF:
1410 EndScript();
1411 break;
1412
1413 case HtmlTokenId::NOSCRIPT_ON:
1414 case HtmlTokenId::NOSCRIPT_OFF:
1415 bInsertUnknown = true;
1416 break;
1417
1418 case HtmlTokenId::STYLE_ON:
1419 NewStyle();
1420 break;
1421
1422 case HtmlTokenId::STYLE_OFF:
1423 EndStyle();
1424 break;
1425
1426 case HtmlTokenId::RAWDATA:
1427 if( !m_bIgnoreRawData )
1428 {
1429 if( IsReadScript() )
1430 {
1432 }
1433 else if( IsReadStyle() )
1434 {
1435 if( !m_aStyleSource.isEmpty() )
1436 m_aStyleSource += "\n";
1437 m_aStyleSource += aToken;
1438 }
1439 }
1440 break;
1441
1442 case HtmlTokenId::OBJECT_ON:
1443 if (m_bXHTML)
1444 {
1445 if (!InsertEmbed())
1446 InsertImage();
1447 break;
1448 }
1449#if HAVE_FEATURE_JAVA
1450 NewObject();
1452#endif
1453 break;
1454
1455 case HtmlTokenId::OBJECT_OFF:
1456 if (!m_aEmbeds.empty())
1457 m_aEmbeds.pop();
1458 break;
1459
1460 case HtmlTokenId::APPLET_ON:
1461#if HAVE_FEATURE_JAVA
1462 InsertApplet();
1464#endif
1465 break;
1466
1467 case HtmlTokenId::IFRAME_ON:
1468 if (bFuzzing && m_nFloatingFrames > 64)
1469 SAL_WARN("sw.html", "Not importing any more FloatingFrames for fuzzing performance");
1470 else
1471 {
1474 }
1475 break;
1476
1477 case HtmlTokenId::LINEBREAK:
1478 if( !IsReadPRE() )
1479 {
1481 break;
1482 }
1483 else
1484 bGetIDOption = true;
1485 // <BR>s in <PRE> resemble true LFs, hence no break
1486 [[fallthrough]];
1487
1488 case HtmlTokenId::NEWPARA:
1489 // CR in PRE/LISTING/XMP
1490 {
1491 if( HtmlTokenId::NEWPARA==nToken ||
1493 {
1494 AppendTextNode(); // there is no LF at this place
1495 // therefore it will cause no problems
1497 }
1498 // progress bar
1499 if (m_xProgress)
1500 m_xProgress->Update(rInput.Tell());
1501 }
1502 break;
1503
1504 case HtmlTokenId::NONBREAKSPACE:
1505 m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, OUString(CHAR_HARDBLANK) );
1506 break;
1507
1508 case HtmlTokenId::SOFTHYPH:
1509 m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, OUString(CHAR_SOFTHYPHEN) );
1510 break;
1511
1512 case HtmlTokenId::LINEFEEDCHAR:
1513 if( m_pPam->GetPoint()->GetContentIndex() )
1515 if (!m_xTable && !m_xDoc->IsInHeaderFooter(m_pPam->GetPoint()->GetNode()))
1516 {
1517 NewAttr(m_xAttrTab, &m_xAttrTab->pBreak, SvxFormatBreakItem(SvxBreak::PageBefore, RES_BREAK));
1518 EndAttr( m_xAttrTab->pBreak, false );
1519 }
1520 break;
1521
1522 case HtmlTokenId::TEXTTOKEN:
1523 case HtmlTokenId::CDATA:
1524 // insert string without spanning attributes at the end.
1525 if( !aToken.isEmpty() && ' '==aToken[0] && !IsReadPRE() )
1526 {
1527 sal_Int32 nPos = m_pPam->GetPoint()->GetContentIndex();
1528 const SwTextNode* pTextNode = nPos ? m_pPam->GetPoint()->GetNode().GetTextNode() : nullptr;
1529 if (pTextNode)
1530 {
1531 const OUString& rText = pTextNode->GetText();
1532 sal_Unicode cLast = rText[--nPos];
1533 if( ' ' == cLast || '\x0a' == cLast)
1534 aToken.remove(0, 1);
1535 }
1536 else
1537 aToken.remove(0, 1);
1538
1539 if( aToken.isEmpty() )
1540 {
1541 m_bUpperSpace = bUpperSpaceSave;
1542 break;
1543 }
1544 }
1545
1546 if( !aToken.isEmpty() )
1547 {
1548 if( !m_bDocInitialized )
1550
1551 if (!m_aEmbeds.empty())
1552 {
1553 // The text token is inside an OLE object, which means
1554 // alternate text.
1555 SwOLENode* pOLENode = m_aEmbeds.top();
1556 if (!pOLENode)
1557 {
1558 // <object> is mapped to an image -> ignore.
1559 break;
1560 }
1561
1562 if (SwFlyFrameFormat* pFormat
1563 = dynamic_cast<SwFlyFrameFormat*>(pOLENode->GetFlyFormat()))
1564 {
1565 if (SdrObject* pObject = SwXFrame::GetOrCreateSdrObject(*pFormat))
1566 {
1567 pObject->SetTitle(pObject->GetTitle() + aToken);
1568 break;
1569 }
1570 }
1571 }
1572
1573 m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, aToken.toString());
1574
1575 // if there are temporary paragraph attributes and the
1576 // paragraph isn't empty then the paragraph attributes
1577 // are final.
1578 m_aParaAttrs.clear();
1579
1580 SetAttr();
1581 }
1582 break;
1583
1584 case HtmlTokenId::HORZRULE:
1586 break;
1587
1588 case HtmlTokenId::IMAGE:
1589 InsertImage();
1590 // if only the parser references the doc, we can break and set
1591 // an error code
1592 if( 1 == m_xDoc->getReferenceCount() )
1593 {
1594 eState = SvParserState::Error;
1595 }
1596 break;
1597
1598 case HtmlTokenId::SPACER:
1599 InsertSpacer();
1600 break;
1601
1602 case HtmlTokenId::EMBED:
1603 InsertEmbed();
1604 break;
1605
1606 case HtmlTokenId::NOEMBED_ON:
1607 m_bInNoEmbed = true;
1608 m_bCallNextToken = bool(m_xTable);
1609 ReadRawData( OOO_STRING_SVTOOLS_HTML_noembed );
1610 break;
1611
1612 case HtmlTokenId::DEFLIST_ON:
1613 if( m_nOpenParaToken != HtmlTokenId::NONE )
1614 EndPara();
1615 NewDefList();
1616 break;
1617 case HtmlTokenId::DEFLIST_OFF:
1618 if( m_nOpenParaToken != HtmlTokenId::NONE )
1619 EndPara();
1620 EndDefListItem( HtmlTokenId::NONE );
1621 EndDefList();
1622 break;
1623
1624 case HtmlTokenId::DD_ON:
1625 case HtmlTokenId::DT_ON:
1626 if( m_nOpenParaToken != HtmlTokenId::NONE )
1627 EndPara();
1628 EndDefListItem();// close <DD>/<DT> and set no template
1630 break;
1631
1632 case HtmlTokenId::DD_OFF:
1633 case HtmlTokenId::DT_OFF:
1634 // c.f. HtmlTokenId::LI_OFF
1635 // Actually we should close a DD/DT now.
1636 // But neither Netscape nor Microsoft do this and so don't we.
1638 break;
1639
1640 // divisions
1641 case HtmlTokenId::DIVISION_ON:
1642 case HtmlTokenId::CENTER_ON:
1644 {
1645 if (m_nOpenParaToken != HtmlTokenId::NONE)
1646 {
1647 if (IsReadPRE())
1648 m_nOpenParaToken = HtmlTokenId::NONE;
1649 else
1650 EndPara();
1651 }
1653 }
1654 break;
1655
1656 case HtmlTokenId::DIVISION_OFF:
1657 case HtmlTokenId::CENTER_OFF:
1659 {
1660 if (m_nOpenParaToken != HtmlTokenId::NONE)
1661 {
1662 if (IsReadPRE())
1663 m_nOpenParaToken = HtmlTokenId::NONE;
1664 else
1665 EndPara();
1666 }
1667 EndDivision();
1668 }
1669 break;
1670
1671 case HtmlTokenId::MULTICOL_ON:
1672 if( m_nOpenParaToken != HtmlTokenId::NONE )
1673 EndPara();
1674 NewMultiCol();
1675 break;
1676
1677 case HtmlTokenId::MULTICOL_OFF:
1678 if( m_nOpenParaToken != HtmlTokenId::NONE )
1679 EndPara();
1680 EndTag( HtmlTokenId::MULTICOL_ON );
1681 break;
1682
1683 case HtmlTokenId::MARQUEE_ON:
1684 NewMarquee();
1685 m_bCallNextToken = m_pMarquee!=nullptr && m_xTable;
1686 break;
1687
1688 case HtmlTokenId::FORM_ON:
1689 NewForm();
1690 break;
1691 case HtmlTokenId::FORM_OFF:
1692 EndForm();
1693 break;
1694
1695 // templates
1696 case HtmlTokenId::PARABREAK_ON:
1697 if( m_nOpenParaToken != HtmlTokenId::NONE )
1698 EndPara( true );
1699 NewPara();
1700 break;
1701
1702 case HtmlTokenId::PARABREAK_OFF:
1703 EndPara( true );
1704 break;
1705
1706 case HtmlTokenId::ADDRESS_ON:
1707 if( m_nOpenParaToken != HtmlTokenId::NONE )
1708 EndPara();
1709 NewTextFormatColl(HtmlTokenId::ADDRESS_ON, RES_POOLCOLL_SEND_ADDRESS);
1710 break;
1711
1712 case HtmlTokenId::ADDRESS_OFF:
1713 if( m_nOpenParaToken != HtmlTokenId::NONE )
1714 EndPara();
1715 EndTextFormatColl( HtmlTokenId::ADDRESS_OFF );
1716 break;
1717
1718 case HtmlTokenId::BLOCKQUOTE_ON:
1719 case HtmlTokenId::BLOCKQUOTE30_ON:
1720 if( m_nOpenParaToken != HtmlTokenId::NONE )
1721 EndPara();
1722 NewTextFormatColl( HtmlTokenId::BLOCKQUOTE_ON, RES_POOLCOLL_HTML_BLOCKQUOTE );
1723 break;
1724
1725 case HtmlTokenId::BLOCKQUOTE_OFF:
1726 case HtmlTokenId::BLOCKQUOTE30_OFF:
1727 if( m_nOpenParaToken != HtmlTokenId::NONE )
1728 EndPara();
1729 EndTextFormatColl( HtmlTokenId::BLOCKQUOTE_ON );
1730 break;
1731
1732 case HtmlTokenId::PREFORMTXT_ON:
1733 case HtmlTokenId::LISTING_ON:
1734 case HtmlTokenId::XMP_ON:
1735 if( m_nOpenParaToken != HtmlTokenId::NONE )
1736 EndPara();
1738 break;
1739
1740 case HtmlTokenId::PREFORMTXT_OFF:
1741 m_bNoParSpace = true; // the last PRE-paragraph gets a spacing
1742 EndTextFormatColl( HtmlTokenId::PREFORMTXT_OFF );
1743 break;
1744
1745 case HtmlTokenId::LISTING_OFF:
1746 case HtmlTokenId::XMP_OFF:
1748 break;
1749
1750 case HtmlTokenId::HEAD1_ON:
1751 case HtmlTokenId::HEAD2_ON:
1752 case HtmlTokenId::HEAD3_ON:
1753 case HtmlTokenId::HEAD4_ON:
1754 case HtmlTokenId::HEAD5_ON:
1755 case HtmlTokenId::HEAD6_ON:
1756 if( m_nOpenParaToken != HtmlTokenId::NONE )
1757 {
1758 if( IsReadPRE() )
1759 m_nOpenParaToken = HtmlTokenId::NONE;
1760 else
1761 EndPara();
1762 }
1763 NewHeading( nToken );
1764 break;
1765
1766 case HtmlTokenId::HEAD1_OFF:
1767 case HtmlTokenId::HEAD2_OFF:
1768 case HtmlTokenId::HEAD3_OFF:
1769 case HtmlTokenId::HEAD4_OFF:
1770 case HtmlTokenId::HEAD5_OFF:
1771 case HtmlTokenId::HEAD6_OFF:
1772 EndHeading();
1773 break;
1774
1775 case HtmlTokenId::TABLE_ON:
1776 if( !m_vPendingStack.empty() )
1777 BuildTable( SvxAdjust::End );
1778 else
1779 {
1780 if( m_nOpenParaToken != HtmlTokenId::NONE )
1781 EndPara();
1782 OSL_ENSURE(!m_xTable, "table in table not allowed here");
1783 if( !m_xTable && (IsNewDoc() || !m_pPam->GetPointNode().FindTableNode()) &&
1785 m_xDoc->GetNodes().GetEndOfExtras().GetIndex() ||
1787 {
1788 if ( m_nParaCnt < 5 )
1789 Show(); // show what we have up to here
1790
1791 SvxAdjust eAdjust = m_xAttrTab->pAdjust
1792 ? static_cast<const SvxAdjustItem&>(m_xAttrTab->pAdjust->GetItem()).
1793 GetAdjust()
1794 : SvxAdjust::End;
1795 BuildTable( eAdjust );
1796 }
1797 else
1798 bInsertUnknown = m_bKeepUnknown;
1799 }
1800 break;
1801
1802 // lists
1803 case HtmlTokenId::DIRLIST_ON:
1804 case HtmlTokenId::MENULIST_ON:
1805 case HtmlTokenId::ORDERLIST_ON:
1806 case HtmlTokenId::UNORDERLIST_ON:
1807 if( m_nOpenParaToken != HtmlTokenId::NONE )
1808 EndPara();
1810 break;
1811
1812 case HtmlTokenId::DIRLIST_OFF:
1813 case HtmlTokenId::MENULIST_OFF:
1814 case HtmlTokenId::ORDERLIST_OFF:
1815 case HtmlTokenId::UNORDERLIST_OFF:
1816 if( m_nOpenParaToken != HtmlTokenId::NONE )
1817 EndPara();
1818 EndNumberBulletListItem( HtmlTokenId::NONE, true );
1820 break;
1821
1822 case HtmlTokenId::LI_ON:
1823 case HtmlTokenId::LISTHEADER_ON:
1824 if( m_nOpenParaToken != HtmlTokenId::NONE &&
1826 || HtmlTokenId::PARABREAK_ON==m_nOpenParaToken) )
1827 {
1828 // only finish paragraph for <P><LI>, not for <DD><LI>
1829 EndPara();
1830 }
1831
1832 if (bFuzzing && m_nListItems > 1024)
1833 {
1834 SAL_WARN("sw.html", "skipping remaining bullet import for performance during fuzzing");
1835 }
1836 else
1837 {
1838 EndNumberBulletListItem( HtmlTokenId::NONE, false );// close <LI>/<LH> and don't set a template
1840 }
1841
1842 ++m_nListItems;
1843
1844 break;
1845 case HtmlTokenId::LI_OFF:
1846 case HtmlTokenId::LISTHEADER_OFF:
1848 break;
1849
1850 // Attribute :
1851 case HtmlTokenId::ITALIC_ON:
1852 {
1856 NewStdAttr( HtmlTokenId::ITALIC_ON,
1857 &m_xAttrTab->pItalic, aPosture,
1858 &m_xAttrTab->pItalicCJK, &aPostureCJK,
1859 &m_xAttrTab->pItalicCTL, &aPostureCTL );
1860 }
1861 break;
1862
1863 case HtmlTokenId::BOLD_ON:
1864 {
1868 NewStdAttr( HtmlTokenId::BOLD_ON,
1869 &m_xAttrTab->pBold, aWeight,
1870 &m_xAttrTab->pBoldCJK, &aWeightCJK,
1871 &m_xAttrTab->pBoldCTL, &aWeightCTL );
1872 }
1873 break;
1874
1875 case HtmlTokenId::STRIKE_ON:
1876 case HtmlTokenId::STRIKETHROUGH_ON:
1877 {
1878 NewStdAttr( HtmlTokenId::STRIKE_ON, &m_xAttrTab->pStrike,
1880 }
1881 break;
1882
1883 case HtmlTokenId::UNDERLINE_ON:
1884 {
1885 NewStdAttr( HtmlTokenId::UNDERLINE_ON, &m_xAttrTab->pUnderline,
1887 }
1888 break;
1889
1890 case HtmlTokenId::SUPERSCRIPT_ON:
1891 {
1892 NewStdAttr( HtmlTokenId::SUPERSCRIPT_ON, &m_xAttrTab->pEscapement,
1894 }
1895 break;
1896
1897 case HtmlTokenId::SUBSCRIPT_ON:
1898 {
1899 NewStdAttr( HtmlTokenId::SUBSCRIPT_ON, &m_xAttrTab->pEscapement,
1901 }
1902 break;
1903
1904 case HtmlTokenId::BLINK_ON:
1905 {
1906 NewStdAttr( HtmlTokenId::BLINK_ON, &m_xAttrTab->pBlink,
1907 SvxBlinkItem( true, RES_CHRATR_BLINK ) );
1908 }
1909 break;
1910
1911 case HtmlTokenId::SPAN_ON:
1912 NewStdAttr( HtmlTokenId::SPAN_ON );
1913 break;
1914
1915 case HtmlTokenId::ITALIC_OFF:
1916 case HtmlTokenId::BOLD_OFF:
1917 case HtmlTokenId::STRIKE_OFF:
1918 case HtmlTokenId::UNDERLINE_OFF:
1919 case HtmlTokenId::SUPERSCRIPT_OFF:
1920 case HtmlTokenId::SUBSCRIPT_OFF:
1921 case HtmlTokenId::BLINK_OFF:
1922 case HtmlTokenId::SPAN_OFF:
1923 EndTag( nToken );
1924 break;
1925
1926 case HtmlTokenId::STRIKETHROUGH_OFF:
1927 EndTag( HtmlTokenId::STRIKE_OFF );
1928 break;
1929
1930 case HtmlTokenId::BASEFONT_ON:
1932 break;
1933 case HtmlTokenId::BASEFONT_OFF:
1935 break;
1936 case HtmlTokenId::FONT_ON:
1937 case HtmlTokenId::BIGPRINT_ON:
1938 case HtmlTokenId::SMALLPRINT_ON:
1940 break;
1941 case HtmlTokenId::FONT_OFF:
1942 case HtmlTokenId::BIGPRINT_OFF:
1943 case HtmlTokenId::SMALLPRINT_OFF:
1945 break;
1946
1947 case HtmlTokenId::EMPHASIS_ON:
1948 case HtmlTokenId::CITATION_ON:
1949 case HtmlTokenId::STRONG_ON:
1950 case HtmlTokenId::CODE_ON:
1951 case HtmlTokenId::SAMPLE_ON:
1952 case HtmlTokenId::KEYBOARD_ON:
1953 case HtmlTokenId::VARIABLE_ON:
1954 case HtmlTokenId::DEFINSTANCE_ON:
1955 case HtmlTokenId::SHORTQUOTE_ON:
1956 case HtmlTokenId::LANGUAGE_ON:
1957 case HtmlTokenId::AUTHOR_ON:
1958 case HtmlTokenId::PERSON_ON:
1959 case HtmlTokenId::ACRONYM_ON:
1960 case HtmlTokenId::ABBREVIATION_ON:
1961 case HtmlTokenId::INSERTEDTEXT_ON:
1962 case HtmlTokenId::DELETEDTEXT_ON:
1963
1964 case HtmlTokenId::TELETYPE_ON:
1966 break;
1967
1968 case HtmlTokenId::SDFIELD_ON:
1969 NewField();
1971 break;
1972
1973 case HtmlTokenId::EMPHASIS_OFF:
1974 case HtmlTokenId::CITATION_OFF:
1975 case HtmlTokenId::STRONG_OFF:
1976 case HtmlTokenId::CODE_OFF:
1977 case HtmlTokenId::SAMPLE_OFF:
1978 case HtmlTokenId::KEYBOARD_OFF:
1979 case HtmlTokenId::VARIABLE_OFF:
1980 case HtmlTokenId::DEFINSTANCE_OFF:
1981 case HtmlTokenId::SHORTQUOTE_OFF:
1982 case HtmlTokenId::LANGUAGE_OFF:
1983 case HtmlTokenId::AUTHOR_OFF:
1984 case HtmlTokenId::PERSON_OFF:
1985 case HtmlTokenId::ACRONYM_OFF:
1986 case HtmlTokenId::ABBREVIATION_OFF:
1987 case HtmlTokenId::INSERTEDTEXT_OFF:
1988 case HtmlTokenId::DELETEDTEXT_OFF:
1989
1990 case HtmlTokenId::TELETYPE_OFF:
1991 EndTag( nToken );
1992 break;
1993
1994 case HtmlTokenId::HEAD_OFF:
1995 if( !m_aStyleSource.isEmpty() )
1996 {
1997 m_pCSS1Parser->ParseStyleSheet( m_aStyleSource );
1998 m_aStyleSource.clear();
1999 }
2000 break;
2001
2002 case HtmlTokenId::DOCTYPE:
2003 case HtmlTokenId::BODY_OFF:
2004 case HtmlTokenId::HTML_OFF:
2005 case HtmlTokenId::HEAD_ON:
2006 case HtmlTokenId::TITLE_OFF:
2007 break; // don't evaluate further???
2008 case HtmlTokenId::HTML_ON:
2009 {
2010 const HTMLOptions& rHTMLOptions = GetOptions();
2011 for (size_t i = rHTMLOptions.size(); i; )
2012 {
2013 const HTMLOption& rOption = rHTMLOptions[--i];
2014 if( HtmlOptionId::DIR == rOption.GetToken() )
2015 {
2016 const OUString& rDir = rOption.GetString();
2017 SfxItemSet aItemSet( m_xDoc->GetAttrPool(),
2018 m_pCSS1Parser->GetWhichMap() );
2019 SvxCSS1PropertyInfo aPropInfo;
2020 OUString aDummy;
2021 ParseStyleOptions( aDummy, aDummy, aDummy, aItemSet,
2022 aPropInfo, nullptr, &rDir );
2023
2024 m_pCSS1Parser->SetPageDescAttrs( nullptr, &aItemSet );
2025 break;
2026 }
2027 }
2028 }
2029 break;
2030
2031 case HtmlTokenId::INPUT:
2032 InsertInput();
2033 break;
2034
2035 case HtmlTokenId::TEXTAREA_ON:
2036 NewTextArea();
2038 break;
2039
2040 case HtmlTokenId::SELECT_ON:
2041 NewSelect();
2043 break;
2044
2045 case HtmlTokenId::ANCHOR_ON:
2046 NewAnchor();
2047 break;
2048
2049 case HtmlTokenId::ANCHOR_OFF:
2050 EndAnchor();
2051 break;
2052
2053 case HtmlTokenId::COMMENT:
2054 if( ( aToken.getLength() > 5 ) && ( ! m_bIgnoreHTMLComments ) )
2055 {
2056 // insert as Post-It
2057 // If there are no space characters right behind
2058 // the <!-- and on front of the -->, leave the comment untouched.
2059 if( ' ' == aToken[ 3 ] &&
2060 ' ' == aToken[ aToken.getLength()-3 ] )
2061 {
2062 std::u16string_view aComment( aToken.subView( 3, aToken.getLength()-5 ) );
2063 InsertComment(OUString(comphelper::string::strip(aComment, ' ')));
2064 }
2065 else
2066 {
2067 OUString aComment = "<" + aToken + ">";
2068 InsertComment( aComment );
2069 }
2070 }
2071 break;
2072
2073 case HtmlTokenId::MAP_ON:
2074 // Image Maps are read asynchronously: At first only an image map is created
2075 // Areas are processed later. Nevertheless the
2076 // ImageMap is inserted into the IMap-Array, because it might be used
2077 // already.
2078 m_pImageMap = new ImageMap;
2080 {
2081 if (!m_pImageMaps)
2082 m_pImageMaps.reset( new ImageMaps );
2083 m_pImageMaps->push_back(std::unique_ptr<ImageMap>(m_pImageMap));
2084 }
2085 else
2086 {
2087 delete m_pImageMap;
2088 m_pImageMap = nullptr;
2089 }
2090 break;
2091
2092 case HtmlTokenId::MAP_OFF:
2093 // there is no ImageMap anymore (don't delete IMap, because it's
2094 // already contained in the array!)
2095 m_pImageMap = nullptr;
2096 break;
2097
2098 case HtmlTokenId::AREA:
2099 if( m_pImageMap )
2100 ParseAreaOptions( m_pImageMap, m_sBaseURL, SvMacroItemId::OnMouseOver,
2101 SvMacroItemId::OnMouseOut );
2102 break;
2103
2104 case HtmlTokenId::FRAMESET_ON:
2105 bInsertUnknown = m_bKeepUnknown;
2106 break;
2107
2108 case HtmlTokenId::NOFRAMES_ON:
2109 if( IsInHeader() )
2110 FinishHeader();
2111 bInsertUnknown = m_bKeepUnknown;
2112 break;
2113
2114 case HtmlTokenId::UNKNOWNCONTROL_ON:
2115 // Ignore content of unknown token in the header, if the token
2116 // does not start with a '!'.
2117 // (but judging from the code, also if does not start with a '%')
2118 // (and also if we're not somewhere we consider PRE)
2119 if( IsInHeader() && !IsReadPRE() && m_aUnknownToken.isEmpty() &&
2120 !sSaveToken.isEmpty() && '!' != sSaveToken[0] &&
2121 '%' != sSaveToken[0] )
2122 m_aUnknownToken = sSaveToken;
2123 [[fallthrough]];
2124
2125 default:
2126 bInsertUnknown = m_bKeepUnknown;
2127 break;
2128 }
2129
2130 if( bGetIDOption )
2132
2133 if( bInsertUnknown )
2134 {
2135 OUStringBuffer aComment("HTML: <");
2136 if( (nToken >= HtmlTokenId::ONOFF_START) && isOffToken(nToken) )
2137 aComment.append("/");
2138 aComment.append(sSaveToken);
2139 if( !aToken.isEmpty() )
2140 {
2141 UnescapeToken();
2142 aComment.append(" " + aToken);
2143 }
2144 aComment.append(">");
2145 InsertComment( aComment.makeStringAndClear() );
2146 }
2147
2148 // if there are temporary paragraph attributes and the
2149 // paragraph isn't empty then the paragraph attributes are final.
2150 if( !m_aParaAttrs.empty() && m_pPam->GetPoint()->GetContentIndex() )
2151 m_aParaAttrs.clear();
2152}
2153
2154static void lcl_swhtml_getItemInfo( const HTMLAttr& rAttr,
2155 bool& rScriptDependent,
2156 sal_uInt16& rScriptType )
2157{
2158 switch( rAttr.GetItem().Which() )
2159 {
2160 case RES_CHRATR_FONT:
2163 case RES_CHRATR_POSTURE:
2164 case RES_CHRATR_WEIGHT:
2165 rScriptType = i18n::ScriptType::LATIN;
2166 rScriptDependent = true;
2167 break;
2173 rScriptType = i18n::ScriptType::ASIAN;
2174 rScriptDependent = true;
2175 break;
2181 rScriptType = i18n::ScriptType::COMPLEX;
2182 rScriptDependent = true;
2183 break;
2184 default:
2185 rScriptDependent = false;
2186 break;
2187 }
2188}
2189
2191{
2192 // A hard line break at the end always must be removed.
2193 // A second one we replace with paragraph spacing.
2194 sal_Int32 nLFStripped = StripTrailingLF();
2195 if( (AM_NOSPACE==eMode || AM_SOFTNOSPACE==eMode) && nLFStripped > 1 )
2196 eMode = AM_SPACE;
2197
2198 // the hard attributes of this paragraph will never be invalid again
2199 m_aParaAttrs.clear();
2200
2201 SwTextNode *pTextNode = (AM_SPACE==eMode || AM_NOSPACE==eMode) ?
2202 m_pPam->GetPoint()->GetNode().GetTextNode() : nullptr;
2203
2204 if (pTextNode)
2205 {
2206 const SvxULSpaceItem& rULSpace =
2207 pTextNode->SwContentNode::GetAttr( RES_UL_SPACE );
2208
2209 bool bChange = AM_NOSPACE==eMode ? rULSpace.GetLower() > 0
2210 : rULSpace.GetLower() == 0;
2211
2212 if( bChange )
2213 {
2214 const SvxULSpaceItem& rCollULSpace =
2215 pTextNode->GetAnyFormatColl().GetULSpace();
2216
2217 bool bMayReset = AM_NOSPACE==eMode ? rCollULSpace.GetLower() == 0
2218 : rCollULSpace.GetLower() > 0;
2219
2220 if( bMayReset &&
2221 rCollULSpace.GetUpper() == rULSpace.GetUpper() )
2222 {
2223 pTextNode->ResetAttr( RES_UL_SPACE );
2224 }
2225 else
2226 {
2227 pTextNode->SetAttr(
2228 SvxULSpaceItem( rULSpace.GetUpper(),
2230 }
2231 }
2232 }
2234
2235 SwPosition aOldPos( *m_pPam->GetPoint() );
2236
2237 bool bRet = m_xDoc->getIDocumentContentOperations().AppendTextNode( *m_pPam->GetPoint() );
2238
2239 // split character attributes and maybe set none,
2240 // which are set for the whole paragraph
2241 const sal_Int32 nEndCnt = aOldPos.GetContentIndex();
2242 const SwPosition& rPos = *m_pPam->GetPoint();
2243
2244 HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
2245 for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes)
2246 {
2247 HTMLAttr *pAttr = *pHTMLAttributes;
2248 if( pAttr && pAttr->GetItem().Which() < RES_PARATR_BEGIN )
2249 {
2250 bool bWholePara = false;
2251
2252 while( pAttr )
2253 {
2254 HTMLAttr *pNext = pAttr->GetNext();
2255 if( pAttr->GetStartParagraphIdx() < aOldPos.GetNodeIndex() ||
2256 (!bWholePara &&
2257 pAttr->GetStartParagraph() == aOldPos.GetNode() &&
2258 pAttr->GetStartContent() != nEndCnt) )
2259 {
2260 bWholePara =
2261 pAttr->GetStartParagraph() == aOldPos.GetNode() &&
2262 pAttr->GetStartContent() == 0;
2263
2264 sal_Int32 nStt = pAttr->m_nStartContent;
2265 bool bScript = false;
2266 sal_uInt16 nScriptItem;
2267 bool bInsert = true;
2268 lcl_swhtml_getItemInfo( *pAttr, bScript,
2269 nScriptItem );
2270 // set previous part
2271 if( bScript )
2272 {
2273 const SwTextNode *pTextNd =
2275 OSL_ENSURE( pTextNd, "No text node" );
2276 if( pTextNd )
2277 {
2278 const OUString& rText = pTextNd->GetText();
2279 sal_uInt16 nScriptText =
2280 g_pBreakIt->GetBreakIter()->getScriptType(
2281 rText, pAttr->GetStartContent() );
2282 sal_Int32 nScriptEnd = g_pBreakIt->GetBreakIter()
2283 ->endOfScript( rText, nStt, nScriptText );
2284 while (nScriptEnd < nEndCnt && nScriptEnd != -1)
2285 {
2286 if( nScriptItem == nScriptText )
2287 {
2288 HTMLAttr *pSetAttr =
2289 pAttr->Clone( aOldPos.GetNode(), nScriptEnd );
2290 pSetAttr->m_nStartContent = nStt;
2291 pSetAttr->ClearPrev();
2292 if( !pNext || bWholePara )
2293 {
2294 if (pSetAttr->m_bInsAtStart)
2295 m_aSetAttrTab.push_front( pSetAttr );
2296 else
2297 m_aSetAttrTab.push_back( pSetAttr );
2298 }
2299 else
2300 pNext->InsertPrev( pSetAttr );
2301 }
2302 nStt = nScriptEnd;
2303 nScriptText = g_pBreakIt->GetBreakIter()->getScriptType(
2304 rText, nStt );
2305 nScriptEnd = g_pBreakIt->GetBreakIter()
2306 ->endOfScript( rText, nStt, nScriptText );
2307 }
2308 bInsert = nScriptItem == nScriptText;
2309 }
2310 }
2311 if( bInsert )
2312 {
2313 HTMLAttr *pSetAttr =
2314 pAttr->Clone( aOldPos.GetNode(), nEndCnt );
2315 pSetAttr->m_nStartContent = nStt;
2316
2317 // When the attribute is for the whole paragraph, the outer
2318 // attributes aren't effective anymore. Hence it may not be inserted
2319 // in the Prev-List of an outer attribute, because that won't be
2320 // set. That leads to shifting when fields are used.
2321 if( !pNext || bWholePara )
2322 {
2323 if (pSetAttr->m_bInsAtStart)
2324 m_aSetAttrTab.push_front( pSetAttr );
2325 else
2326 m_aSetAttrTab.push_back( pSetAttr );
2327 }
2328 else
2329 pNext->InsertPrev( pSetAttr );
2330 }
2331 else
2332 {
2333 HTMLAttr *pPrev = pAttr->GetPrev();
2334 if( pPrev )
2335 {
2336 // the previous attributes must be set anyway
2337 if( !pNext || bWholePara )
2338 {
2339 if (pPrev->m_bInsAtStart)
2340 m_aSetAttrTab.push_front( pPrev );
2341 else
2342 m_aSetAttrTab.push_back( pPrev );
2343 }
2344 else
2345 pNext->InsertPrev( pPrev );
2346 }
2347 }
2348 pAttr->ClearPrev();
2349 }
2350
2351 pAttr->SetStart( rPos );
2352 pAttr = pNext;
2353 }
2354 }
2355 }
2356
2357 if( bUpdateNum )
2358 {
2359 if( GetNumInfo().GetDepth() )
2360 {
2361 sal_uInt8 nLvl = GetNumInfo().GetLevel();
2362 SetNodeNum( nLvl );
2363 }
2364 else
2366 }
2367
2368 // We must set the attribute of the paragraph before now (because of JavaScript)
2369 SetAttr();
2370
2371 // Now it is time to get rid of all script dependent hints that are
2372 // equal to the settings in the style
2373 SwTextNode *pTextNd = aOldPos.GetNode().GetTextNode();
2374 OSL_ENSURE( pTextNd, "There is the txt node" );
2375 size_t nCntAttr = (pTextNd && pTextNd->GetpSwpHints())
2376 ? pTextNd->GetSwpHints().Count() : 0;
2377 if( nCntAttr )
2378 {
2379 // These are the end position of all script dependent hints.
2380 // If we find a hint that starts before the current end position,
2381 // we have to set it. If we find a hint that start behind or at
2382 // that position, we have to take the hint value into account.
2383 // If it is equal to the style, or in fact the paragraph value
2384 // for that hint, the hint is removed. Otherwise its end position
2385 // is remembered.
2386 sal_Int32 aEndPos[15] =
2387 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
2388 SwpHints& rHints = pTextNd->GetSwpHints();
2389 for( size_t i=0; i < nCntAttr; i++ )
2390 {
2391 SwTextAttr *pHt = rHints.Get( i );
2392 sal_uInt16 nWhich = pHt->Which();
2393 sal_Int16 nIdx = 0;
2394 bool bFont = false;
2395 switch( nWhich )
2396 {
2397 case RES_CHRATR_FONT:
2398 nIdx = 0;
2399 bFont = true;
2400 break;
2402 nIdx = 1;
2403 break;
2405 nIdx = 2;
2406 break;
2407 case RES_CHRATR_POSTURE:
2408 nIdx = 3;
2409 break;
2410 case RES_CHRATR_WEIGHT:
2411 nIdx = 4;
2412 break;
2414 nIdx = 5;
2415 bFont = true;
2416 break;
2418 nIdx = 6;
2419 break;
2421 nIdx = 7;
2422 break;
2424 nIdx = 8;
2425 break;
2427 nIdx = 9;
2428 break;
2430 nIdx = 10;
2431 bFont = true;
2432 break;
2434 nIdx = 11;
2435 break;
2437 nIdx = 12;
2438 break;
2440 nIdx = 13;
2441 break;
2443 nIdx = 14;
2444 break;
2445 default:
2446 // Skip to next attribute
2447 continue;
2448 }
2449 const sal_Int32 nStt = pHt->GetStart();
2450 if( nStt >= aEndPos[nIdx] )
2451 {
2452 const SfxPoolItem& rItem =
2453 static_cast<const SwContentNode *>(pTextNd)->GetAttr( nWhich );
2454 if( bFont ? swhtml_css1atr_equalFontItems(rItem,pHt->GetAttr())
2455 : rItem == pHt->GetAttr() )
2456 {
2457 // The hint is the same as set in the paragraph and
2458 // therefore, it can be deleted
2459 // CAUTION!!! This WILL delete the hint and it MAY
2460 // also delete the SwpHints!!! To avoid any trouble
2461 // we leave the loop immediately if this is the last
2462 // hint.
2463 pTextNd->DeleteAttribute( pHt );
2464 if( 1 == nCntAttr )
2465 break;
2466 i--;
2467 nCntAttr--;
2468 }
2469 else
2470 {
2471 // The hint is different. Therefore all hints within that
2472 // hint have to be ignored.
2473 aEndPos[nIdx] = pHt->GetEnd() ? *pHt->GetEnd() : nStt;
2474 }
2475 }
2476 else
2477 {
2478 // The hint starts before another one ends.
2479 // The hint in this case is not deleted
2480 OSL_ENSURE( pHt->GetEnd() && *pHt->GetEnd() <= aEndPos[nIdx],
2481 "hints aren't nested properly!" );
2482 }
2483 }
2484 }
2485
2486 if (!m_xTable && !--m_nParaCnt)
2487 Show();
2488
2489 return bRet;
2490}
2491
2493{
2494 //If it already has ParSpace, return
2495 if( !m_bNoParSpace )
2496 return;
2497
2498 m_bNoParSpace = false;
2499
2500 SwNodeOffset nNdIdx = m_pPam->GetPoint()->GetNodeIndex() - 1;
2501
2502 SwTextNode *pTextNode = m_xDoc->GetNodes()[nNdIdx]->GetTextNode();
2503 if( !pTextNode )
2504 return;
2505
2506 SvxULSpaceItem rULSpace =
2507 pTextNode->SwContentNode::GetAttr( RES_UL_SPACE );
2508 if( rULSpace.GetLower() )
2509 return;
2510
2511 const SvxULSpaceItem& rCollULSpace =
2512 pTextNode->GetAnyFormatColl().GetULSpace();
2513 if( rCollULSpace.GetLower() &&
2514 rCollULSpace.GetUpper() == rULSpace.GetUpper() )
2515 {
2516 pTextNode->ResetAttr( RES_UL_SPACE );
2517 }
2518 else
2519 {
2520 //What I do here, is that I examine the attributes, and if
2521 //I find out, that it's CJK/CTL, then I set the paragraph space
2522 //to the value set in HTML_CJK_PARSPACE/HTML_CTL_PARSPACE.
2523
2524 bool bIsCJK = false;
2525 bool bIsCTL = false;
2526
2527 const size_t nCntAttr = pTextNode->GetpSwpHints()
2528 ? pTextNode->GetSwpHints().Count() : 0;
2529
2530 for(size_t i = 0; i < nCntAttr; ++i)
2531 {
2532 SwTextAttr *const pHt = pTextNode->GetSwpHints().Get(i);
2533 sal_uInt16 const nWhich = pHt->Which();
2534 if (RES_CHRATR_CJK_FONT == nWhich ||
2535 RES_CHRATR_CJK_FONTSIZE == nWhich ||
2536 RES_CHRATR_CJK_LANGUAGE == nWhich ||
2537 RES_CHRATR_CJK_POSTURE == nWhich ||
2538 RES_CHRATR_CJK_WEIGHT == nWhich)
2539 {
2540 bIsCJK = true;
2541 break;
2542 }
2543 if (RES_CHRATR_CTL_FONT == nWhich ||
2544 RES_CHRATR_CTL_FONTSIZE == nWhich ||
2545 RES_CHRATR_CTL_LANGUAGE == nWhich ||
2546 RES_CHRATR_CTL_POSTURE == nWhich ||
2547 RES_CHRATR_CTL_WEIGHT == nWhich)
2548 {
2549 bIsCTL = true;
2550 break;
2551 }
2552 }
2553
2554 if( bIsCTL )
2555 {
2556 pTextNode->SetAttr(
2558 }
2559 else if( bIsCJK )
2560 {
2561 pTextNode->SetAttr(
2563 } else {
2564 pTextNode->SetAttr(
2566 }
2567 }
2568}
2569
2571{
2572 // Here
2573 // - a EndAction is called, so the document is formatted
2574 // - a Reschedule is called,
2575 // - the own View-Shell is set again
2576 // - and a StartAction is called
2577
2578 OSL_ENSURE( SvParserState::Working==eState, "Show not in working state - That can go wrong" );
2579 SwViewShell *pOldVSh = CallEndAction();
2580
2582
2583 if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
2584 || 1 == m_xDoc->getReferenceCount() )
2585 {
2586 // was the import aborted by SFX?
2587 eState = SvParserState::Error;
2588 }
2589
2590 // Fetch the SwViewShell again, as it could be destroyed in Reschedule.
2591 SwViewShell *pVSh = CallStartAction( pOldVSh );
2592
2593 // is the current node not visible anymore, then we use a bigger increment
2594 if( pVSh )
2595 {
2597 ? 5 : 50;
2598 }
2599}
2600
2602{
2603 // Here
2604 // - a Reschedule is called, so it can be scrolled
2605 // - the own View-Shell is set again
2606 // - a StartAction/EndAction is called, when there was scrolling.
2607
2608 OSL_ENSURE( SvParserState::Working==eState, "ShowStatLine not in working state - That can go wrong" );
2609
2610 // scroll bar
2611 if (m_xProgress)
2612 {
2613 m_xProgress->Update(rInput.Tell());
2615 }
2616 else
2617 {
2619
2620 if( ( m_xDoc->GetDocShell() && m_xDoc->GetDocShell()->IsAbortingImport() )
2621 || 1 == m_xDoc->getReferenceCount() )
2622 // was the import aborted by SFX?
2623 eState = SvParserState::Error;
2624
2626 if( pVSh && pVSh->HasInvalidRect() )
2627 {
2628 CallEndAction( false, false );
2629 CallStartAction( pVSh, false );
2630 }
2631 }
2632}
2633
2635{
2636 OSL_ENSURE( !m_pActionViewShell, "CallStartAction: SwViewShell already set" );
2637
2638 if( !pVSh || bChkPtr )
2639 {
2640#if OSL_DEBUG_LEVEL > 0
2641 SwViewShell *pOldVSh = pVSh;
2642#endif
2643 pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
2644#if OSL_DEBUG_LEVEL > 0
2645 OSL_ENSURE( !pVSh || !pOldVSh || pOldVSh == pVSh, "CallStartAction: Who swapped the SwViewShell?" );
2646 if( pOldVSh && !pVSh )
2647 pVSh = nullptr;
2648#endif
2649 }
2650 m_pActionViewShell = pVSh;
2651
2652 if( m_pActionViewShell )
2653 {
2654 if( auto pEditShell = dynamic_cast< SwEditShell *>( m_pActionViewShell ) )
2655 pEditShell->StartAction();
2656 else
2658 }
2659
2660 return m_pActionViewShell;
2661}
2662
2663SwViewShell *SwHTMLParser::CallEndAction( bool bChkAction, bool bChkPtr )
2664{
2665 if( bChkPtr )
2666 {
2667 SwViewShell *pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
2668 OSL_ENSURE( !pVSh || m_pActionViewShell == pVSh,
2669 "CallEndAction: Who swapped the SwViewShell?" );
2670#if OSL_DEBUG_LEVEL > 0
2671 if( m_pActionViewShell && !pVSh )
2672 pVSh = nullptr;
2673#endif
2674 if( pVSh != m_pActionViewShell )
2675 m_pActionViewShell = nullptr;
2676 }
2677
2678 if( !m_pActionViewShell || (bChkAction && !m_pActionViewShell->ActionPend()) )
2679 return m_pActionViewShell;
2680
2681 if( dynamic_cast< const SwEditShell *>( m_pActionViewShell ) != nullptr )
2682 {
2683 // Already scrolled?, then make sure that the view doesn't move!
2684 const bool bOldLock = m_pActionViewShell->IsViewLocked();
2686 const bool bOldEndActionByVirDev = m_pActionViewShell->IsEndActionByVirDev();
2688 static_cast<SwEditShell*>(m_pActionViewShell)->EndAction();
2689 m_pActionViewShell->SetEndActionByVirDev( bOldEndActionByVirDev );
2690 m_pActionViewShell->LockView( bOldLock );
2691
2692 // bChkJumpMark is only set when the object was also found
2693 if( m_bChkJumpMark )
2694 {
2695 const Point aVisSttPos( DOCUMENTBORDER, DOCUMENTBORDER );
2696 if( GetMedium() && aVisSttPos == m_pActionViewShell->VisArea().Pos() )
2698 GetMedium()->GetURLObject().GetMark() );
2699 m_bChkJumpMark = false;
2700 }
2701 }
2702 else
2704
2705 // if the parser holds the last reference to the document, then we can
2706 // abort here and set an error.
2707 if( 1 == m_xDoc->getReferenceCount() )
2708 {
2709 eState = SvParserState::Error;
2710 }
2711
2713 m_pActionViewShell = nullptr;
2714
2715 return pVSh;
2716}
2717
2719{
2720 SwViewShell *pVSh = m_xDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
2721 OSL_ENSURE( !pVSh || m_pActionViewShell == pVSh,
2722 "CheckActionViewShell: Who has swapped SwViewShell?" );
2723#if OSL_DEBUG_LEVEL > 0
2724 if( m_pActionViewShell && !pVSh )
2725 pVSh = nullptr;
2726#endif
2727 if( pVSh != m_pActionViewShell )
2728 m_pActionViewShell = nullptr;
2729
2730 return m_pActionViewShell;
2731}
2732
2734 : m_pFrameFormat(pFrameFormat)
2735{
2736 StartListening(m_pFrameFormat->GetNotifier());
2737}
2738
2740{
2741 if (rHint.GetId() == SfxHintId::Dying)
2742 m_pFrameFormat = nullptr;
2743}
2744
2745void SwHTMLParser::SetAttr_( bool bChkEnd, bool bBeforeTable,
2746 std::deque<std::unique_ptr<HTMLAttr>> *pPostIts )
2747{
2748 SwPaM aAttrPam( *m_pPam->GetPoint() );
2749 const SwPosition& rEndPos = *m_pPam->GetPoint();
2750 const sal_Int32 nEndCnt = m_pPam->GetPoint()->GetContentIndex();
2751 HTMLAttr* pAttr;
2752 SwContentNode* pCNd;
2753
2754 std::vector<std::unique_ptr<HTMLAttr>> aFields;
2755
2756 for( auto n = m_aSetAttrTab.size(); n; )
2757 {
2758 pAttr = m_aSetAttrTab[ --n ];
2759 sal_uInt16 nWhich = pAttr->m_pItem->Which();
2760
2761 SwNodeOffset nEndParaIdx = pAttr->GetEndParagraphIdx();
2762 bool bSetAttr;
2763 if( bChkEnd )
2764 {
2765 // Set character attribute with end early on, so set them still in
2766 // the current paragraph (because of JavaScript and various "chats"(?)).
2767 // This shouldn't be done for attributes which are used for
2768 // the whole paragraph, because they could be from a paragraph style
2769 // which can't be set. Because the attributes are inserted with
2770 // SETATTR_DONTREPLACE, they should be able to be set later.
2771 bSetAttr = ( nEndParaIdx < rEndPos.GetNodeIndex() &&
2772 ((RES_MARGIN_FIRSTLINE != nWhich && RES_MARGIN_TEXTLEFT != nWhich) || !GetNumInfo().GetNumRule()) ) ||
2773 ( !pAttr->IsLikePara() &&
2774 nEndParaIdx == rEndPos.GetNodeIndex() &&
2775 pAttr->GetEndContent() < nEndCnt &&
2776 (isCHRATR(nWhich) || isTXTATR_WITHEND(nWhich)) ) ||
2777 ( bBeforeTable &&
2778 nEndParaIdx == rEndPos.GetNodeIndex() &&
2779 !pAttr->GetEndContent() );
2780 }
2781 else
2782 {
2783 // Attributes in body nodes array section shouldn't be set if we are in a
2784 // special nodes array section, but vice versa it's possible.
2785 SwNodeOffset nEndOfIcons = m_xDoc->GetNodes().GetEndOfExtras().GetIndex();
2786 bSetAttr = nEndParaIdx < rEndPos.GetNodeIndex() ||
2787 rEndPos.GetNodeIndex() > nEndOfIcons ||
2788 nEndParaIdx <= nEndOfIcons;
2789 }
2790
2791 if( bSetAttr )
2792 {
2793 // The attribute shouldn't be in the list of temporary paragraph
2794 // attributes, because then it would be deleted.
2795 while( !m_aParaAttrs.empty() )
2796 {
2797 OSL_ENSURE( pAttr != m_aParaAttrs.back(),
2798 "SetAttr: Attribute must not yet be set" );
2799 m_aParaAttrs.pop_back();
2800 }
2801
2802 // then set it
2803 m_aSetAttrTab.erase( m_aSetAttrTab.begin() + n );
2804
2805 while( pAttr )
2806 {
2807 HTMLAttr *pPrev = pAttr->GetPrev();
2808 if( !pAttr->m_bValid )
2809 {
2810 // invalid attributes can be deleted
2811 delete pAttr;
2812 pAttr = pPrev;
2813 continue;
2814 }
2815
2816 pCNd = pAttr->m_nStartPara.GetNode().GetContentNode();
2817 if( !pCNd )
2818 {
2819 // because of the awful deleting of nodes an index can also
2820 // point to an end node :-(
2821 if ( (pAttr->GetStartParagraph() == pAttr->GetEndParagraph()) &&
2822 !isTXTATR_NOEND(nWhich) )
2823 {
2824 // when the end index also points to the node, we don't
2825 // need to set attributes anymore, except if it's a text attribute.
2826 delete pAttr;
2827 pAttr = pPrev;
2828 continue;
2829 }
2830 pCNd = m_xDoc->GetNodes().GoNext( &(pAttr->m_nStartPara) );
2831 if( pCNd )
2832 pAttr->m_nStartContent = 0;
2833 else
2834 {
2835 OSL_ENSURE( false, "SetAttr: GoNext() failed!" );
2836 delete pAttr;
2837 pAttr = pPrev;
2838 continue;
2839 }
2840 }
2841
2842 // because of the deleting of BRs the start index can also
2843 // point behind the end the text
2844 if( pAttr->m_nStartContent > pCNd->Len() )
2845 pAttr->m_nStartContent = pCNd->Len();
2846 aAttrPam.GetPoint()->Assign( *pCNd, pAttr->m_nStartContent );
2847
2848 aAttrPam.SetMark();
2849 if ( (pAttr->GetStartParagraph() != pAttr->GetEndParagraph()) &&
2850 !isTXTATR_NOEND(nWhich) )
2851 {
2852 pCNd = pAttr->m_nEndPara.GetNode().GetContentNode();
2853 if( !pCNd )
2854 {
2855 pCNd = SwNodes::GoPrevious( &(pAttr->m_nEndPara) );
2856 if( pCNd )
2857 pAttr->m_nEndContent = pCNd->Len();
2858 else
2859 {
2860 OSL_ENSURE( false, "SetAttr: GoPrevious() failed!" );
2861 aAttrPam.DeleteMark();
2862 delete pAttr;
2863 pAttr = pPrev;
2864 continue;
2865 }
2866 }
2867 }
2868 else if( pAttr->IsLikePara() )
2869 {
2870 pAttr->m_nEndContent = pCNd->Len();
2871 }
2872
2873 // because of the deleting of BRs the start index can also
2874 // point behind the end the text
2875 if( pAttr->m_nEndContent > pCNd->Len() )
2876 pAttr->m_nEndContent = pCNd->Len();
2877
2878 aAttrPam.GetPoint()->Assign( *pCNd, pAttr->m_nEndContent );
2879 if( bBeforeTable &&
2880 aAttrPam.GetPoint()->GetNodeIndex() ==
2881 rEndPos.GetNodeIndex() )
2882 {
2883 // If we're before inserting a table and the attribute ends
2884 // in the current node, then we must end it in the previous
2885 // node or discard it, if it starts in that node.
2886 if( nWhich != RES_BREAK && nWhich != RES_PAGEDESC &&
2887 !isTXTATR_NOEND(nWhich) )
2888 {
2889 if( aAttrPam.GetMark()->GetNodeIndex() !=
2890 rEndPos.GetNodeIndex() )
2891 {
2892 OSL_ENSURE( !aAttrPam.GetPoint()->GetContentIndex(),
2893 "Content-Position before table not 0???" );
2894 aAttrPam.Move( fnMoveBackward );
2895 }
2896 else
2897 {
2898 aAttrPam.DeleteMark();
2899 delete pAttr;
2900 pAttr = pPrev;
2901 continue;
2902 }
2903 }
2904 }
2905
2906 switch( nWhich )
2907 {
2908 case RES_FLTR_BOOKMARK: // insert bookmark
2909 {
2910 const OUString sName( static_cast<SfxStringItem*>(pAttr->m_pItem.get())->GetValue() );
2911 IDocumentMarkAccess* const pMarkAccess = m_xDoc->getIDocumentMarkAccess();
2912 IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findMark( sName );
2913 if( ppBkmk != pMarkAccess->getAllMarksEnd() &&
2914 (*ppBkmk)->GetMarkStart() == *aAttrPam.GetPoint() )
2915 break; // do not generate duplicates on this position
2916 aAttrPam.DeleteMark();
2917 const ::sw::mark::IMark* const pNewMark = pMarkAccess->makeMark(
2918 aAttrPam,
2919 sName,
2921 ::sw::mark::InsertMode::New);
2922
2923 // jump to bookmark
2924 if( JumpToMarks::Mark == m_eJumpTo && pNewMark->GetName() == m_sJmpMark )
2925 {
2926 m_bChkJumpMark = true;
2928 }
2929 }
2930 break;
2931 case RES_TXTATR_FIELD:
2934 {
2935 SwFieldIds nFieldWhich =
2936 pPostIts
2937 ? static_cast<const SwFormatField *>(pAttr->m_pItem.get())->GetField()->GetTyp()->Which()
2939 if( pPostIts && (SwFieldIds::Postit == nFieldWhich ||
2940 SwFieldIds::Script == nFieldWhich) )
2941 {
2942 pPostIts->emplace_front( pAttr );
2943 }
2944 else
2945 {
2946 aFields.emplace_back( pAttr);
2947 }
2948 }
2949 aAttrPam.DeleteMark();
2950 pAttr = pPrev;
2951 continue;
2952
2953 // tdf#94088 expand RES_BACKGROUND to the new fill attribute
2954 // definitions in the range [XATTR_FILL_FIRST .. XATTR_FILL_LAST].
2955 // This is the right place in the future if the adapted fill attributes
2956 // may be handled more directly in HTML import to handle them.
2957 case RES_BACKGROUND:
2958 {
2959 const SvxBrushItem& rBrush = static_cast< SvxBrushItem& >(*pAttr->m_pItem);
2961
2963 m_xDoc->getIDocumentContentOperations().InsertItemSet(aAttrPam, aNewSet, SetAttrMode::DONTREPLACE);
2964 break;
2965 }
2966
2967 case RES_LR_SPACE:
2968 assert(false);
2969 break;
2970
2973 case RES_MARGIN_RIGHT:
2974 if( aAttrPam.GetPoint()->GetNodeIndex() ==
2975 aAttrPam.GetMark()->GetNodeIndex())
2976 {
2977 // because of numbering set this attribute directly at node
2978 pCNd->SetAttr( *pAttr->m_pItem );
2979 break;
2980 }
2981 OSL_ENSURE( false,
2982 "LRSpace set over multiple paragraphs!" );
2983 [[fallthrough]]; // (shouldn't reach this point anyway)
2984 default:
2985
2986 // maybe jump to a bookmark
2987 if( RES_TXTATR_INETFMT == nWhich &&
2989 m_sJmpMark == static_cast<SwFormatINetFormat*>(pAttr->m_pItem.get())->GetName() )
2990 {
2991 m_bChkJumpMark = true;
2993 }
2994
2995 m_xDoc->getIDocumentContentOperations().InsertPoolItem( aAttrPam, *pAttr->m_pItem, SetAttrMode::DONTREPLACE );
2996 }
2997 aAttrPam.DeleteMark();
2998
2999 delete pAttr;
3000 pAttr = pPrev;
3001 }
3002 }
3003 }
3004
3005 for( auto n = m_aMoveFlyFrames.size(); n; )
3006 {
3007 SwFrameFormat *pFrameFormat = m_aMoveFlyFrames[--n]->GetFrameFormat();
3008 if (!pFrameFormat)
3009 {
3010 SAL_WARN("sw.html", "SwFrameFormat deleted during import");
3011 m_aMoveFlyFrames.erase( m_aMoveFlyFrames.begin() + n );
3012 m_aMoveFlyCnts.erase( m_aMoveFlyCnts.begin() + n );
3013 continue;
3014 }
3015
3016 const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
3017 OSL_ENSURE( RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId(),
3018 "Only At-Para flys need special handling" );
3019 SwNodeOffset nFlyParaIdx = rAnchor.GetAnchorNode()->GetIndex();
3020 bool bMoveFly;
3021 if( bChkEnd )
3022 {
3023 bMoveFly = nFlyParaIdx < rEndPos.GetNodeIndex() ||
3024 ( nFlyParaIdx == rEndPos.GetNodeIndex() &&
3025 m_aMoveFlyCnts[n] < nEndCnt );
3026 }
3027 else
3028 {
3029 SwNodeOffset nEndOfIcons = m_xDoc->GetNodes().GetEndOfExtras().GetIndex();
3030 bMoveFly = nFlyParaIdx < rEndPos.GetNodeIndex() ||
3031 rEndPos.GetNodeIndex() > nEndOfIcons ||
3032 nFlyParaIdx <= nEndOfIcons;
3033 }
3034 if( bMoveFly )
3035 {
3036 pFrameFormat->DelFrames();
3037 *aAttrPam.GetPoint() = *rAnchor.GetContentAnchor();
3038 aAttrPam.GetPoint()->SetContent( m_aMoveFlyCnts[n] );
3039 SwFormatAnchor aAnchor( rAnchor );
3040 aAnchor.SetType( RndStdIds::FLY_AT_CHAR );
3041 aAnchor.SetAnchor( aAttrPam.GetPoint() );
3042 pFrameFormat->SetFormatAttr( aAnchor );
3043
3044 const SwFormatHoriOrient& rHoriOri = pFrameFormat->GetHoriOrient();
3045 if( text::HoriOrientation::LEFT == rHoriOri.GetHoriOrient() )
3046 {
3047 SwFormatHoriOrient aHoriOri( rHoriOri );
3048 aHoriOri.SetRelationOrient( text::RelOrientation::CHAR );
3049 pFrameFormat->SetFormatAttr( aHoriOri );
3050 }
3051 const SwFormatVertOrient& rVertOri = pFrameFormat->GetVertOrient();
3052 if( text::VertOrientation::TOP == rVertOri.GetVertOrient() )
3053 {
3054 SwFormatVertOrient aVertOri( rVertOri );
3055 aVertOri.SetRelationOrient( text::RelOrientation::CHAR );
3056 pFrameFormat->SetFormatAttr( aVertOri );
3057 }
3058
3059 pFrameFormat->MakeFrames();
3060 m_aMoveFlyFrames.erase( m_aMoveFlyFrames.begin() + n );
3061 m_aMoveFlyCnts.erase( m_aMoveFlyCnts.begin() + n );
3062 }
3063 }
3064 for (auto & field : aFields)
3065 {
3066 pCNd = field->m_nStartPara.GetNode().GetContentNode();
3067 aAttrPam.GetPoint()->Assign( *pCNd, field->m_nStartContent );
3068
3069 if( bBeforeTable &&
3070 aAttrPam.GetPoint()->GetNodeIndex() == rEndPos.GetNodeIndex() )
3071 {
3072 OSL_ENSURE( !bBeforeTable, "Aha, the case does occur" );
3073 OSL_ENSURE( !aAttrPam.GetPoint()->GetContentIndex(),
3074 "Content-Position before table not 0???" );
3075 // !!!
3076 aAttrPam.Move( fnMoveBackward );
3077 }
3078
3079 m_xDoc->getIDocumentContentOperations().InsertPoolItem( aAttrPam, *field->m_pItem );
3080
3081 field.reset();
3082 }
3083 aFields.clear();
3084}
3085
3086void SwHTMLParser::NewAttr(const std::shared_ptr<HTMLAttrTable>& rAttrTable, HTMLAttr **ppAttr, const SfxPoolItem& rItem )
3087{
3088 // Font height and font colour as well as escape attributes may not be
3089 // combined. Therefore they're saved in a list and in it the last opened
3090 // attribute is at the beginning and count is always one. For all other
3091 // attributes count is just incremented.
3092 if( *ppAttr )
3093 {
3094 HTMLAttr *pAttr = new HTMLAttr(*m_pPam->GetPoint(), rItem, ppAttr, rAttrTable);
3095 pAttr->InsertNext( *ppAttr );
3096 (*ppAttr) = pAttr;
3097 }
3098 else
3099 (*ppAttr) = new HTMLAttr(*m_pPam->GetPoint(), rItem, ppAttr, rAttrTable);
3100}
3101
3102bool SwHTMLParser::EndAttr( HTMLAttr* pAttr, bool bChkEmpty )
3103{
3104 bool bRet = true;
3105
3106 // The list header is saved in the attribute.
3107 HTMLAttr **ppHead = pAttr->m_ppHead;
3108
3109 OSL_ENSURE( ppHead, "No list header attribute found!" );
3110
3111 // save the current position as end position
3112 const SwPosition* pEndPos = m_pPam->GetPoint();
3113 sal_Int32 nEndCnt = m_pPam->GetPoint()->GetContentIndex();
3114
3115 // Is the last started or an earlier started attribute being ended?
3116 HTMLAttr *pLast = nullptr;
3117 if( ppHead && pAttr != *ppHead )
3118 {
3119 // The last started attribute isn't being ended
3120
3121 // Then we look for attribute which was started immediately afterwards,
3122 // which has also not yet been ended (otherwise it would no longer be
3123 // in the list).
3124 pLast = *ppHead;
3125 while( pLast && pLast->GetNext() != pAttr )
3126 pLast = pLast->GetNext();
3127
3128 OSL_ENSURE( pLast, "Attribute not found in own list!" );
3129 }
3130
3131 bool bMoveBack = false;
3132 sal_uInt16 nWhich = pAttr->m_pItem->Which();
3133 if( !nEndCnt && RES_PARATR_BEGIN <= nWhich &&
3134 pEndPos->GetNodeIndex() != pAttr->GetStartParagraph().GetIndex() )
3135 {
3136 // Then move back one position in the content!
3137 bMoveBack = m_pPam->Move( fnMoveBackward );
3138 nEndCnt = m_pPam->GetPoint()->GetContentIndex();
3139 }
3140
3141 // now end the attribute
3142 HTMLAttr *pNext = pAttr->GetNext();
3143
3144 bool bInsert;
3145 sal_uInt16 nScriptItem = 0;
3146 bool bScript = false;
3147 // does it have a non-empty range?
3148 if( !bChkEmpty || (RES_PARATR_BEGIN <= nWhich && bMoveBack) ||
3149 RES_PAGEDESC == nWhich || RES_BREAK == nWhich ||
3150 pEndPos->GetNodeIndex() != pAttr->GetStartParagraph().GetIndex() ||
3151 nEndCnt != pAttr->GetStartContent() )
3152 {
3153 bInsert = true;
3154 // We do some optimization for script dependent attributes here.
3155 if( pEndPos->GetNodeIndex() == pAttr->GetStartParagraph().GetIndex() )
3156 {
3157 lcl_swhtml_getItemInfo( *pAttr, bScript, nScriptItem );
3158 }
3159 }
3160 else
3161 {
3162 bInsert = false;
3163 }
3164
3165 const SwTextNode *pTextNd = (bInsert && bScript) ?
3167 nullptr;
3168
3169 if (pTextNd)
3170 {
3171 const OUString& rText = pTextNd->GetText();
3172 sal_uInt16 nScriptText = g_pBreakIt->GetBreakIter()->getScriptType(
3173 rText, pAttr->GetStartContent() );
3174 sal_Int32 nScriptEnd = g_pBreakIt->GetBreakIter()
3175 ->endOfScript( rText, pAttr->GetStartContent(), nScriptText );
3176 while (nScriptEnd < nEndCnt && nScriptEnd != -1)
3177 {
3178 if( nScriptItem == nScriptText )
3179 {
3180 HTMLAttr *pSetAttr = pAttr->Clone( pEndPos->GetNode(), nScriptEnd );
3181 pSetAttr->ClearPrev();
3182 if( pNext )
3183 pNext->InsertPrev( pSetAttr );
3184 else
3185 {
3186 if (pSetAttr->m_bInsAtStart)
3187 m_aSetAttrTab.push_front( pSetAttr );
3188 else
3189 m_aSetAttrTab.push_back( pSetAttr );
3190 }
3191 }
3192 pAttr->m_nStartContent = nScriptEnd;
3193 nScriptText = g_pBreakIt->GetBreakIter()->getScriptType(
3194 rText, nScriptEnd );
3195 nScriptEnd = g_pBreakIt->GetBreakIter()
3196 ->endOfScript( rText, nScriptEnd, nScriptText );
3197 }
3198 bInsert = nScriptItem == nScriptText;
3199 }
3200 if( bInsert )
3201 {
3202 pAttr->m_nEndPara = pEndPos->GetNode();
3203 pAttr->m_nEndContent = nEndCnt;
3204 pAttr->m_bInsAtStart = RES_TXTATR_INETFMT != nWhich &&
3205 RES_TXTATR_CHARFMT != nWhich;
3206
3207 if( !pNext )
3208 {
3209 // No open attributes of that type exists any longer, so all
3210 // can be set. Except they depend on another attribute, then
3211 // they're appended there.
3212 if (pAttr->m_bInsAtStart)
3213 m_aSetAttrTab.push_front( pAttr );
3214 else
3215 m_aSetAttrTab.push_back( pAttr );
3216 }
3217 else
3218 {
3219 // There are other open attributes of that type,
3220 // therefore the setting must be postponed.
3221 // Hence the current attribute is added at the end
3222 // of the Prev-List of the successor.
3223 pNext->InsertPrev( pAttr );
3224 }
3225 }
3226 else
3227 {
3228 // Then don't insert, but delete. Because of the "faking" of styles
3229 // by hard attributing there can be also other empty attributes in the
3230 // Prev-List, which must be set anyway.
3231 HTMLAttr *pPrev = pAttr->GetPrev();
3232 bRet = false;
3233 delete pAttr;
3234
3235 if( pPrev )
3236 {
3237 // The previous attributes must be set anyway.
3238 if( pNext )
3239 pNext->InsertPrev( pPrev );
3240 else
3241 {
3242 if (pPrev->m_bInsAtStart)
3243 m_aSetAttrTab.push_front( pPrev );
3244 else
3245 m_aSetAttrTab.push_back( pPrev );
3246 }
3247 }
3248
3249 }
3250
3251 // If the first attribute of the list was set, then the list header
3252 // must be corrected as well.
3253 if( pLast )
3254 pLast->m_pNext = pNext;
3255 else if( ppHead )
3256 *ppHead = pNext;
3257
3258 if( bMoveBack )
3260
3261 return bRet;
3262}
3263
3265{
3266 // preliminary paragraph attributes are not allowed here, they could
3267 // be set here and then the pointers become invalid!
3268 OSL_ENSURE(m_aParaAttrs.empty(),
3269 "Danger: there are non-final paragraph attributes");
3270 m_aParaAttrs.clear();
3271
3272 // The list header is saved in the attribute
3273 HTMLAttr **ppHead = pAttr->m_ppHead;
3274
3275 OSL_ENSURE( ppHead, "no list header attribute found!" );
3276
3277 // Is the last started or an earlier started attribute being removed?
3278 HTMLAttr *pLast = nullptr;
3279 if( ppHead && pAttr != *ppHead )
3280 {
3281 // The last started attribute isn't being ended
3282
3283 // Then we look for attribute which was started immediately afterwards,
3284 // which has also not yet been ended (otherwise it would no longer be
3285 // in the list).
3286 pLast = *ppHead;
3287 while( pLast && pLast->GetNext() != pAttr )
3288 pLast = pLast->GetNext();
3289
3290 OSL_ENSURE( pLast, "Attribute not found in own list!" );
3291 }
3292
3293 // now delete the attribute
3294 HTMLAttr *pNext = pAttr->GetNext();
3295 HTMLAttr *pPrev = pAttr->GetPrev();
3296 //hold ref to xAttrTab until end of scope to ensure *ppHead validity
3297 std::shared_ptr<HTMLAttrTable> xKeepAlive(pAttr->m_xAttrTab);
3298 delete pAttr;
3299
3300 if( pPrev )
3301 {
3302 // The previous attributes must be set anyway.
3303 if( pNext )
3304 pNext->InsertPrev( pPrev );
3305 else
3306 {
3307 if (pPrev->m_bInsAtStart)
3308 m_aSetAttrTab.push_front( pPrev );
3309 else
3310 m_aSetAttrTab.push_back( pPrev );
3311 }
3312 }
3313
3314 // If the first attribute of the list was deleted, then the list header
3315 // must be corrected as well.
3316 if( pLast )
3317 pLast->m_pNext = pNext;
3318 else if( ppHead )
3319 *ppHead = pNext;
3320}
3321
3322void SwHTMLParser::SaveAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab)
3323{
3324 // preliminary paragraph attributes are not allowed here, they could
3325 // be set here and then the pointers become invalid!
3326 OSL_ENSURE(m_aParaAttrs.empty(),
3327 "Danger: there are non-final paragraph attributes");
3328 m_aParaAttrs.clear();
3329
3330 HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
3331 HTMLAttr** pSaveAttributes = reinterpret_cast<HTMLAttr**>(rNewAttrTab.get());
3332
3333 for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes, ++pSaveAttributes)
3334 {
3335 *pSaveAttributes = *pHTMLAttributes;
3336
3337 HTMLAttr *pAttr = *pSaveAttributes;
3338 while (pAttr)
3339 {
3340 pAttr->SetHead(pSaveAttributes, rNewAttrTab);
3341 pAttr = pAttr->GetNext();
3342 }
3343
3344 *pHTMLAttributes = nullptr;
3345 }
3346}
3347
3348void SwHTMLParser::SplitAttrTab( std::shared_ptr<HTMLAttrTable> const & rNewAttrTab,
3349 bool bMoveEndBack )
3350{
3351 // preliminary paragraph attributes are not allowed here, they could
3352 // be set here and then the pointers become invalid!
3353 OSL_ENSURE(m_aParaAttrs.empty(),
3354 "Danger: there are non-final paragraph attributes");
3355 m_aParaAttrs.clear();
3356
3357 SwNodeIndex nEndIdx( m_pPam->GetPoint()->GetNode() );
3358
3359 // close all still open attributes and re-open them after the table
3360 HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
3361 HTMLAttr** pSaveAttributes = reinterpret_cast<HTMLAttr**>(rNewAttrTab.get());
3362 bool bSetAttr = true;
3363 const sal_Int32 nSttCnt = m_pPam->GetPoint()->GetContentIndex();
3364 sal_Int32 nEndCnt = nSttCnt;
3365
3366 if( bMoveEndBack )
3367 {
3368 SwNodeOffset nOldEnd = nEndIdx.GetIndex();
3369 SwNodeOffset nTmpIdx;
3370 if( ( nTmpIdx = m_xDoc->GetNodes().GetEndOfExtras().GetIndex()) >= nOldEnd ||
3371 ( nTmpIdx = m_xDoc->GetNodes().GetEndOfAutotext().GetIndex()) >= nOldEnd )
3372 {
3373 nTmpIdx = m_xDoc->GetNodes().GetEndOfInserts().GetIndex();
3374 }
3375 SwContentNode* pCNd = SwNodes::GoPrevious(&nEndIdx);
3376
3377 // Don't set attributes, when the PaM was moved outside of the content area.
3378 bSetAttr = pCNd && nTmpIdx < nEndIdx.GetIndex();
3379
3380 nEndCnt = (bSetAttr ? pCNd->Len() : 0);
3381 }
3382 for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; (++pHTMLAttributes, ++pSaveAttributes))
3383 {
3384 HTMLAttr *pAttr = *pHTMLAttributes;
3385 *pSaveAttributes = nullptr;
3386 while( pAttr )
3387 {
3388 HTMLAttr *pNext = pAttr->GetNext();
3389 HTMLAttr *pPrev = pAttr->GetPrev();
3390
3391 if( bSetAttr &&
3392 ( pAttr->GetStartParagraphIdx() < nEndIdx.GetIndex() ||
3393 (pAttr->GetStartParagraph() == nEndIdx &&
3394 pAttr->GetStartContent() != nEndCnt) ) )
3395 {
3396 // The attribute must be set before the list. We need the
3397 // original and therefore we clone it, because pointer to the
3398 // attribute exist in the other contexts. The Next-List is lost
3399 // in doing so, but the Previous-List is preserved.
3400 HTMLAttr *pSetAttr = pAttr->Clone( nEndIdx.GetNode(), nEndCnt );
3401
3402 if( pNext )
3403 pNext->InsertPrev( pSetAttr );
3404 else
3405 {
3406 if (pSetAttr->m_bInsAtStart)
3407 m_aSetAttrTab.push_front( pSetAttr );
3408 else
3409 m_aSetAttrTab.push_back( pSetAttr );
3410 }
3411 }
3412 else if( pPrev )
3413 {
3414 // If the attribute doesn't need to be set before the table, then
3415 // the previous attributes must still be set.
3416 if( pNext )
3417 pNext->InsertPrev( pPrev );
3418 else
3419 {
3420 if (pPrev->m_bInsAtStart)
3421 m_aSetAttrTab.push_front( pPrev );
3422 else
3423 m_aSetAttrTab.push_back( pPrev );
3424 }
3425 }
3426
3427 // set the start of the attribute anew and break link
3428 pAttr->Reset(m_pPam->GetPoint()->GetNode(), nSttCnt, pSaveAttributes, rNewAttrTab);
3429
3430 if (*pSaveAttributes)
3431 {
3432 HTMLAttr *pSAttr = *pSaveAttributes;
3433 while( pSAttr->GetNext() )
3434 pSAttr = pSAttr->GetNext();
3435 pSAttr->InsertNext( pAttr );
3436 }
3437 else
3438 *pSaveAttributes = pAttr;
3439
3440 pAttr = pNext;
3441 }
3442
3443 *pHTMLAttributes = nullptr;
3444 }
3445}
3446
3447void SwHTMLParser::RestoreAttrTab(std::shared_ptr<HTMLAttrTable> const & rNewAttrTab)
3448{
3449 // preliminary paragraph attributes are not allowed here, they could
3450 // be set here and then the pointers become invalid!
3451 OSL_ENSURE(m_aParaAttrs.empty(),
3452 "Danger: there are non-final paragraph attributes");
3453 m_aParaAttrs.clear();
3454
3455 HTMLAttr** pHTMLAttributes = reinterpret_cast<HTMLAttr**>(m_xAttrTab.get());
3456 HTMLAttr** pSaveAttributes = reinterpret_cast<HTMLAttr**>(rNewAttrTab.get());
3457
3458 for (auto nCnt = sizeof(HTMLAttrTable) / sizeof(HTMLAttr*); nCnt--; ++pHTMLAttributes, ++pSaveAttributes)
3459 {
3460 OSL_ENSURE(!*pHTMLAttributes, "The attribute table is not empty!");
3461
3462 *pHTMLAttributes = *pSaveAttributes;
3463
3464 HTMLAttr *pAttr = *pHTMLAttributes;
3465 while (pAttr)
3466 {
3467 OSL_ENSURE( !pAttr->GetPrev() || !pAttr->GetPrev()->m_ppHead,
3468 "Previous attribute has still a header" );
3469 pAttr->SetHead(pHTMLAttributes, m_xAttrTab);
3470 pAttr = pAttr->GetNext();
3471 }
3472
3473 *pSaveAttributes = nullptr;
3474 }
3475}
3476
3477void SwHTMLParser::InsertAttr( const SfxPoolItem& rItem, bool bInsAtStart )
3478{
3479 HTMLAttr* pTmp = new HTMLAttr(*m_pPam->GetPoint(), rItem, nullptr, std::shared_ptr<HTMLAttrTable>());
3480 if (bInsAtStart)
3481 m_aSetAttrTab.push_front( pTmp );
3482 else
3483 m_aSetAttrTab.push_back( pTmp );
3484}
3485
3486void SwHTMLParser::InsertAttrs( std::deque<std::unique_ptr<HTMLAttr>> rAttrs )
3487{
3488 while( !rAttrs.empty() )
3489 {
3490 std::unique_ptr<HTMLAttr> pAttr = std::move(rAttrs.front());
3491 InsertAttr( pAttr->GetItem(), false );
3492 rAttrs.pop_front();
3493 }
3494}
3495
3497{
3498 OUString aId, aStyle, aLang, aDir;
3499 OUString aClass;
3500
3501 const HTMLOptions& rHTMLOptions = GetOptions();
3502 for (size_t i = rHTMLOptions.size(); i; )
3503 {
3504 const HTMLOption& rOption = rHTMLOptions[--i];
3505 switch( rOption.GetToken() )
3506 {
3507 case HtmlOptionId::ID:
3508 aId = rOption.GetString();
3509 break;
3510 case HtmlOptionId::STYLE:
3511 aStyle = rOption.GetString();
3512 break;
3513 case HtmlOptionId::CLASS:
3514 aClass = rOption.GetString();
3515 break;
3516 case HtmlOptionId::LANG:
3517 aLang = rOption.GetString();
3518 break;
3519 case HtmlOptionId::DIR:
3520 aDir = rOption.GetString();
3521 break;
3522 default: break;
3523 }
3524 }
3525
3526 // create a new context
3527 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
3528
3529 // parse styles
3530 if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
3531 {
3532 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
3533 SvxCSS1PropertyInfo aPropInfo;
3534
3535 if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
3536 {
3537 if( HtmlTokenId::SPAN_ON != nToken || aClass.isEmpty() ||
3538 !CreateContainer( aClass, aItemSet, aPropInfo, xCntxt.get() ) )
3539 DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
3540 InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
3541 }
3542 }
3543
3544 // save the context
3545 PushContext(xCntxt);
3546}
3547
3549 HTMLAttr **ppAttr, const SfxPoolItem & rItem,
3550 HTMLAttr **ppAttr2, const SfxPoolItem *pItem2,
3551 HTMLAttr **ppAttr3, const SfxPoolItem *pItem3 )
3552{
3553 OUString aId, aStyle, aClass, aLang, aDir;
3554
3555 const HTMLOptions& rHTMLOptions = GetOptions();
3556 for (size_t i = rHTMLOptions.size(); i; )
3557 {
3558 const HTMLOption& rOption = rHTMLOptions[--i];
3559 switch( rOption.GetToken() )
3560 {
3561 case HtmlOptionId::ID:
3562 aId = rOption.GetString();
3563 break;
3564 case HtmlOptionId::STYLE:
3565 aStyle = rOption.GetString();
3566 break;
3567 case HtmlOptionId::CLASS:
3568 aClass = rOption.GetString();
3569 break;
3570 case HtmlOptionId::LANG:
3571 aLang = rOption.GetString();
3572 break;
3573 case HtmlOptionId::DIR:
3574 aDir = rOption.GetString();
3575 break;
3576 default: break;
3577 }
3578 }
3579
3580 // create a new context
3581 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
3582
3583 // parse styles
3584 if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
3585 {
3586 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
3587 SvxCSS1PropertyInfo aPropInfo;
3588
3589 aItemSet.Put( rItem );
3590 if( pItem2 )
3591 aItemSet.Put( *pItem2 );
3592 if( pItem3 )
3593 aItemSet.Put( *pItem3 );
3594
3595 if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
3596 DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
3597
3598 InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
3599 }
3600 else
3601 {
3602 InsertAttr( ppAttr ,rItem, xCntxt.get() );
3603 if( pItem2 )
3604 {
3605 OSL_ENSURE( ppAttr2, "missing table entry for item2" );
3606 InsertAttr( ppAttr2, *pItem2, xCntxt.get() );
3607 }
3608 if( pItem3 )
3609 {
3610 OSL_ENSURE( ppAttr3, "missing table entry for item3" );
3611 InsertAttr( ppAttr3, *pItem3, xCntxt.get() );
3612 }
3613 }
3614
3615 // save the context
3616 PushContext(xCntxt);
3617}
3618
3620{
3621 // fetch context
3622 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(getOnToken(nToken)));
3623 if (xCntxt)
3624 {
3625 // and maybe end the attributes
3626 EndContext(xCntxt.get());
3627 }
3628}
3629
3631{
3632 OUString aId, aStyle, aClass, aLang, aDir;
3633 sal_uInt16 nSize = 3;
3634
3635 const HTMLOptions& rHTMLOptions = GetOptions();
3636 for (size_t i = rHTMLOptions.size(); i; )
3637 {
3638 const HTMLOption& rOption = rHTMLOptions[--i];
3639 switch( rOption.GetToken() )
3640 {
3641 case HtmlOptionId::SIZE:
3642 nSize = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
3643 break;
3644 case HtmlOptionId::ID:
3645 aId = rOption.GetString();
3646 break;
3647 case HtmlOptionId::STYLE:
3648 aStyle = rOption.GetString();
3649 break;
3650 case HtmlOptionId::CLASS:
3651 aClass = rOption.GetString();
3652 break;
3653 case HtmlOptionId::LANG:
3654 aLang = rOption.GetString();
3655 break;
3656 case HtmlOptionId::DIR:
3657 aDir = rOption.GetString();
3658 break;
3659 default: break;
3660 }
3661 }
3662
3663 if( nSize < 1 )
3664 nSize = 1;
3665
3666 if( nSize > 7 )
3667 nSize = 7;
3668
3669 // create a new context
3670 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::BASEFONT_ON));
3671
3672 // parse styles
3673 if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
3674 {
3675 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
3676 SvxCSS1PropertyInfo aPropInfo;
3677
3678 //CJK has different defaults
3679 SvxFontHeightItem aFontHeight( m_aFontHeights[nSize-1], 100, RES_CHRATR_FONTSIZE );
3680 aItemSet.Put( aFontHeight );
3681 SvxFontHeightItem aFontHeightCJK( m_aFontHeights[nSize-1], 100, RES_CHRATR_CJK_FONTSIZE );
3682 aItemSet.Put( aFontHeightCJK );
3683 //Complex type can contain so many types of letters,
3684 //that it's not really worthy to bother, IMO.
3685 //Still, I have set a default.
3686 SvxFontHeightItem aFontHeightCTL( m_aFontHeights[nSize-1], 100, RES_CHRATR_CTL_FONTSIZE );
3687 aItemSet.Put( aFontHeightCTL );
3688
3689 if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
3690 DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
3691
3692 InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
3693 }
3694 else
3695 {
3696 SvxFontHeightItem aFontHeight( m_aFontHeights[nSize-1], 100, RES_CHRATR_FONTSIZE );
3697 InsertAttr( &m_xAttrTab->pFontHeight, aFontHeight, xCntxt.get() );
3698 SvxFontHeightItem aFontHeightCJK( m_aFontHeights[nSize-1], 100, RES_CHRATR_CJK_FONTSIZE );
3699 InsertAttr( &m_xAttrTab->pFontHeightCJK, aFontHeightCJK, xCntxt.get() );
3700 SvxFontHeightItem aFontHeightCTL( m_aFontHeights[nSize-1], 100, RES_CHRATR_CTL_FONTSIZE );
3701 InsertAttr( &m_xAttrTab->pFontHeightCTL, aFontHeightCTL, xCntxt.get() );
3702 }
3703
3704 // save the context
3705 PushContext(xCntxt);
3706
3707 // save the font size
3708 m_aBaseFontStack.push_back( nSize );
3709}
3710
3712{
3713 EndTag( HtmlTokenId::BASEFONT_ON );
3714
3715 // avoid stack underflow in tables
3716 if( m_aBaseFontStack.size() > m_nBaseFontStMin )
3717 m_aBaseFontStack.erase( m_aBaseFontStack.begin() + m_aBaseFontStack.size() - 1 );
3718}
3719
3721{
3722 sal_uInt16 nBaseSize =
3725 : 3 );
3726 sal_uInt16 nFontSize =
3727 ( m_aFontStack.size() > m_nFontStMin
3729 : nBaseSize );
3730
3731 OUString aFace, aId, aStyle, aClass, aLang, aDir;
3732 Color aColor;
3733 sal_uLong nFontHeight = 0; // actual font height to set
3734 sal_uInt16 nSize = 0; // font height in Netscape notation (1-7)
3735 bool bColor = false;
3736
3737 const HTMLOptions& rHTMLOptions = GetOptions();
3738 for (size_t i = rHTMLOptions.size(); i; )
3739 {
3740 const HTMLOption& rOption = rHTMLOptions[--i];
3741 switch( rOption.GetToken() )
3742 {
3743 case HtmlOptionId::SIZE:
3744 if( HtmlTokenId::FONT_ON==nToken && !rOption.GetString().isEmpty() )
3745 {
3746 sal_Int32 nSSize;
3747 if( '+' == rOption.GetString()[0] ||
3748 '-' == rOption.GetString()[0] )
3749 nSSize = o3tl::saturating_add<sal_Int32>(nBaseSize, rOption.GetSNumber());
3750 else
3751 nSSize = static_cast<sal_Int32>(rOption.GetNumber());
3752
3753 if( nSSize < 1 )
3754 nSSize = 1;
3755 else if( nSSize > 7 )
3756 nSSize = 7;
3757
3758 nSize = o3tl::narrowing<sal_uInt16>(nSSize);
3759 nFontHeight = m_aFontHeights[nSize-1];
3760 }
3761 break;
3762 case HtmlOptionId::COLOR:
3763 if( HtmlTokenId::FONT_ON==nToken )
3764 {
3765 rOption.GetColor( aColor );
3766 bColor = true;
3767 }
3768 break;
3769 case HtmlOptionId::FACE:
3770 if( HtmlTokenId::FONT_ON==nToken )
3771 aFace = rOption.GetString();
3772 break;
3773 case HtmlOptionId::ID:
3774 aId = rOption.GetString();
3775 break;
3776 case HtmlOptionId::STYLE:
3777 aStyle = rOption.GetString();
3778 break;
3779 case HtmlOptionId::CLASS:
3780 aClass = rOption.GetString();
3781 break;
3782 case HtmlOptionId::LANG:
3783 aLang = rOption.GetString();
3784 break;
3785 case HtmlOptionId::DIR:
3786 aDir = rOption.GetString();
3787 break;
3788 default: break;
3789 }
3790 }
3791
3792 if( HtmlTokenId::FONT_ON != nToken )
3793 {
3794 // HTML_BIGPRINT_ON or HTML_SMALLPRINT_ON
3795
3796 // In headings the current heading sets the font height
3797 // and not BASEFONT.
3798 const SwFormatColl *pColl = GetCurrFormatColl();
3799 sal_uInt16 nPoolId = pColl ? pColl->GetPoolFormatId() : 0;
3800 if( nPoolId>=RES_POOLCOLL_HEADLINE1 &&
3801 nPoolId<=RES_POOLCOLL_HEADLINE6 )
3802 {
3803 // If the font height in the heading wasn't changed yet,
3804 // then take the one from the style.
3805 if( m_nFontStHeadStart==m_aFontStack.size() )
3806 nFontSize = static_cast< sal_uInt16 >(6 - (nPoolId - RES_POOLCOLL_HEADLINE1));
3807 }
3808 else
3809 nPoolId = 0;
3810
3811 if( HtmlTokenId::BIGPRINT_ON == nToken )
3812 nSize = ( nFontSize<7 ? nFontSize+1 : 7 );
3813 else
3814 nSize = ( nFontSize>1 ? nFontSize-1 : 1 );
3815
3816 // If possible in headlines we fetch the new font height
3817 // from the style.
3818 if( nPoolId && nSize>=1 && nSize <=6 )
3819 nFontHeight =
3820 m_pCSS1Parser->GetTextCollFromPool(
3821 RES_POOLCOLL_HEADLINE1+6-nSize )->GetSize().GetHeight();
3822 else
3823 nFontHeight = m_aFontHeights[nSize-1];
3824 }
3825
3826 OSL_ENSURE( !nSize == !nFontHeight, "HTML-Font-Size != Font-Height" );
3827
3828 OUString aFontName;
3829 const OUString aStyleName;
3830 FontFamily eFamily = FAMILY_DONTKNOW; // family and pitch,
3831 FontPitch ePitch = PITCH_DONTKNOW; // if not found
3832 rtl_TextEncoding eEnc = osl_getThreadTextEncoding();
3833
3834 if( !aFace.isEmpty() && !m_pCSS1Parser->IsIgnoreFontFamily() )
3835 {
3836 const FontList *pFList = nullptr;
3837 SwDocShell *pDocSh = m_xDoc->GetDocShell();
3838 if( pDocSh )
3839 {
3840 const SvxFontListItem *pFListItem =
3841 static_cast<const SvxFontListItem *>(pDocSh->GetItem(SID_ATTR_CHAR_FONTLIST));
3842 if( pFListItem )
3843 pFList = pFListItem->GetFontList();
3844 }
3845
3846 bool bFound = false;
3847 sal_Int32 nStrPos = 0;
3848 while( nStrPos!= -1 )
3849 {
3850 OUString aFName = aFace.getToken( 0, ',', nStrPos );
3851 aFName = comphelper::string::strip(aFName, ' ');
3852 if( !aFName.isEmpty() )
3853 {
3854 if( !bFound && pFList )
3855 {
3856 sal_Handle hFont = pFList->GetFirstFontMetric( aFName );
3857 if( nullptr != hFont )
3858 {
3859 const FontMetric& rFMetric = FontList::GetFontMetric( hFont );
3860 if( RTL_TEXTENCODING_DONTKNOW != rFMetric.GetCharSet() )
3861 {
3862 bFound = true;
3863 if( RTL_TEXTENCODING_SYMBOL == rFMetric.GetCharSet() )
3864 eEnc = RTL_TEXTENCODING_SYMBOL;
3865 }
3866 }
3867 }
3868 if( !aFontName.isEmpty() )
3869 aFontName += ";";
3870 aFontName += aFName;
3871 }
3872 }
3873 }
3874
3875 // create a new context
3876 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
3877
3878 // parse styles
3879 if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
3880 {
3881 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
3882 SvxCSS1PropertyInfo aPropInfo;
3883
3884 if( nFontHeight )
3885 {
3886 SvxFontHeightItem aFontHeight( nFontHeight, 100, RES_CHRATR_FONTSIZE );
3887 aItemSet.Put( aFontHeight );
3888 SvxFontHeightItem aFontHeightCJK( nFontHeight, 100, RES_CHRATR_CJK_FONTSIZE );
3889 aItemSet.Put( aFontHeightCJK );
3890 SvxFontHeightItem aFontHeightCTL( nFontHeight, 100, RES_CHRATR_CTL_FONTSIZE );
3891 aItemSet.Put( aFontHeightCTL );
3892 }
3893 if( bColor )
3894 aItemSet.Put( SvxColorItem(aColor, RES_CHRATR_COLOR) );
3895 if( !aFontName.isEmpty() )
3896 {
3897 SvxFontItem aFont( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_FONT );
3898 aItemSet.Put( aFont );
3899 SvxFontItem aFontCJK( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CJK_FONT );
3900 aItemSet.Put( aFontCJK );
3901 SvxFontItem aFontCTL( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CTL_FONT );
3902 aItemSet.Put( aFontCTL );
3903 }
3904
3905 if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
3906 DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
3907
3908 InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
3909 }
3910 else
3911 {
3912 if( nFontHeight )
3913 {
3914 SvxFontHeightItem aFontHeight( nFontHeight, 100, RES_CHRATR_FONTSIZE );
3915 InsertAttr( &m_xAttrTab->pFontHeight, aFontHeight, xCntxt.get() );
3916 SvxFontHeightItem aFontHeightCJK( nFontHeight, 100, RES_CHRATR_CJK_FONTSIZE );
3917 InsertAttr( &m_xAttrTab->pFontHeight, aFontHeightCJK, xCntxt.get() );
3918 SvxFontHeightItem aFontHeightCTL( nFontHeight, 100, RES_CHRATR_CTL_FONTSIZE );
3919 InsertAttr( &m_xAttrTab->pFontHeight, aFontHeightCTL, xCntxt.get() );
3920 }
3921 if( bColor )
3922 InsertAttr( &m_xAttrTab->pFontColor, SvxColorItem(aColor, RES_CHRATR_COLOR), xCntxt.get() );
3923 if( !aFontName.isEmpty() )
3924 {
3925 SvxFontItem aFont( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_FONT );
3926 InsertAttr( &m_xAttrTab->pFont, aFont, xCntxt.get() );
3927 SvxFontItem aFontCJK( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CJK_FONT );
3928 InsertAttr( &m_xAttrTab->pFont, aFontCJK, xCntxt.get() );
3929 SvxFontItem aFontCTL( eFamily, aFontName, aStyleName, ePitch, eEnc, RES_CHRATR_CTL_FONT );
3930 InsertAttr( &m_xAttrTab->pFont, aFontCTL, xCntxt.get() );
3931 }
3932 }
3933
3934 // save the context
3935 PushContext(xCntxt);
3936
3937 m_aFontStack.push_back( nSize );
3938}
3939
3941{
3942 EndTag( nToken );
3943
3944 // avoid stack underflow in tables
3945 if( m_aFontStack.size() > m_nFontStMin )
3946 m_aFontStack.erase( m_aFontStack.begin() + m_aFontStack.size() - 1 );
3947}
3948
3950{
3951 if( m_pPam->GetPoint()->GetContentIndex() )
3953 else
3954 AddParSpace();
3955
3956 m_eParaAdjust = SvxAdjust::End;
3957 OUString aId, aStyle, aClass, aLang, aDir;
3958
3959 const HTMLOptions& rHTMLOptions = GetOptions();
3960 for (size_t i = rHTMLOptions.size(); i; )
3961 {
3962 const HTMLOption& rOption = rHTMLOptions[--i];
3963 switch( rOption.GetToken() )
3964 {
3965 case HtmlOptionId::ID:
3966 aId = rOption.GetString();
3967 break;
3968 case HtmlOptionId::ALIGN:
3970 break;
3971 case HtmlOptionId::STYLE:
3972 aStyle = rOption.GetString();
3973 break;
3974 case HtmlOptionId::CLASS:
3975 aClass = rOption.GetString();
3976 break;
3977 case HtmlOptionId::LANG:
3978 aLang = rOption.GetString();
3979 break;
3980 case HtmlOptionId::DIR:
3981 aDir = rOption.GetString();
3982 break;
3983 default: break;
3984 }
3985 }
3986
3987 // create a new context
3988 std::unique_ptr<HTMLAttrContext> xCntxt(
3989 !aClass.isEmpty() ? new HTMLAttrContext( HtmlTokenId::PARABREAK_ON,
3990 RES_POOLCOLL_TEXT, aClass )
3991 : new HTMLAttrContext( HtmlTokenId::PARABREAK_ON ));
3992
3993 // parse styles (Don't consider class. This is only possible as long as none of
3994 // the CSS1 properties of the class must be formatted hard!!!)
3995 if (HasStyleOptions(aStyle, aId, {}, &aLang, &aDir))
3996 {
3997 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
3998 SvxCSS1PropertyInfo aPropInfo;
3999
4000 if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir))
4001 {
4002 OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ),
4003 "Class is not considered" );
4004 DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
4005 InsertAttrs( aItemSet, aPropInfo, xCntxt.get() );
4006 }
4007 }
4008
4009 if( SvxAdjust::End != m_eParaAdjust )
4011
4012 // and push on stack
4013 PushContext( xCntxt );
4014
4015 // set the current style or its attributes
4016 SetTextCollAttrs( !aClass.isEmpty() ? m_aContexts.back().get() : nullptr );
4017
4018 // progress bar
4019 ShowStatline();
4020
4021 OSL_ENSURE( m_nOpenParaToken == HtmlTokenId::NONE, "Now an open paragraph element will be lost." );
4022 m_nOpenParaToken = HtmlTokenId::PARABREAK_ON;
4023}
4024
4025void SwHTMLParser::EndPara( bool bReal )
4026{
4027 if (HtmlTokenId::LI_ON==m_nOpenParaToken && m_xTable)
4028 {
4029#if OSL_DEBUG_LEVEL > 0
4030 const SwNumRule *pNumRule = m_pPam->GetPointNode().GetTextNode()->GetNumRule();
4031 OSL_ENSURE( pNumRule, "Where is the NumRule" );
4032#endif
4033 }
4034
4035 // Netscape skips empty paragraphs, we do the same; unless in XHTML mode, which prefers mapping
4036 // the source document to the doc model 1:1 if possible.
4037 if( bReal )
4038 {
4041 else
4042 AddParSpace();
4043 }
4044
4045 // If a DD or DT was open, it's an implied definition list,
4046 // which must be closed now.
4047 if( (m_nOpenParaToken == HtmlTokenId::DT_ON || m_nOpenParaToken == HtmlTokenId::DD_ON) &&
4049 {
4051 }
4052
4053 // Pop the context of the stack. It can also be from an
4054 // implied opened definition list.
4055 std::unique_ptr<HTMLAttrContext> xCntxt(
4056 PopContext( m_nOpenParaToken != HtmlTokenId::NONE ? getOnToken(m_nOpenParaToken) : HtmlTokenId::PARABREAK_ON ));
4057
4058 // close attribute
4059 if (xCntxt)
4060 {
4061 EndContext(xCntxt.get());
4062 SetAttr(); // because of JavaScript set paragraph attributes as fast as possible
4063 xCntxt.reset();
4064 }
4065
4066 // reset the existing style
4067 if( bReal )
4069
4070 m_nOpenParaToken = HtmlTokenId::NONE;
4071}
4072
4074{
4075 m_eParaAdjust = SvxAdjust::End;
4076
4077 OUString aId, aStyle, aClass, aLang, aDir;
4078
4079 const HTMLOptions& rHTMLOptions = GetOptions();
4080 for (size_t i = rHTMLOptions.size(); i; )
4081 {
4082 const HTMLOption& rOption = rHTMLOptions[--i];
4083 switch( rOption.GetToken() )
4084 {
4085 case HtmlOptionId::ID:
4086 aId = rOption.GetString();
4087 break;
4088 case HtmlOptionId::ALIGN:
4090 break;
4091 case HtmlOptionId::STYLE:
4092 aStyle = rOption.GetString();
4093 break;
4094 case HtmlOptionId::CLASS:
4095 aClass = rOption.GetString();
4096 break;
4097 case HtmlOptionId::LANG:
4098 aLang = rOption.GetString();
4099 break;
4100 case HtmlOptionId::DIR:
4101 aDir = rOption.GetString();
4102 break;
4103 default: break;
4104 }
4105 }
4106
4107 // open a new paragraph
4108 if( m_pPam->GetPoint()->GetContentIndex() )
4110 else
4111 AddParSpace();
4112
4113 // search for the matching style
4114 sal_uInt16 nTextColl;
4115 switch( nToken )
4116 {
4117 case HtmlTokenId::HEAD1_ON: nTextColl = RES_POOLCOLL_HEADLINE1; break;
4118 case HtmlTokenId::HEAD2_ON: nTextColl = RES_POOLCOLL_HEADLINE2; break;
4119 case HtmlTokenId::HEAD3_ON: nTextColl = RES_POOLCOLL_HEADLINE3; break;
4120 case HtmlTokenId::HEAD4_ON: nTextColl = RES_POOLCOLL_HEADLINE4; break;
4121 case HtmlTokenId::HEAD5_ON: nTextColl = RES_POOLCOLL_HEADLINE5; break;
4122 case HtmlTokenId::HEAD6_ON: nTextColl = RES_POOLCOLL_HEADLINE6; break;
4123 default: nTextColl = RES_POOLCOLL_STANDARD; break;
4124 }
4125
4126 // create the context
4127 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken, nTextColl, aClass));
4128
4129 // parse styles (regarding class see also NewPara)
4130 if (HasStyleOptions(aStyle, aId, {}, &aLang, &aDir))
4131 {
4132 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
4133 SvxCSS1PropertyInfo aPropInfo;
4134
4135 if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir))
4136 {
4137 OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ),
4138 "Class is not considered" );
4139 DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
4140 InsertAttrs( aItemSet, aPropInfo, xCntxt.get() );
4141 }
4142 }
4143
4144 if( SvxAdjust::End != m_eParaAdjust )
4146
4147 // and push on stack
4148 PushContext(xCntxt);
4149
4150 // set the current style or its attributes
4151 SetTextCollAttrs(m_aContexts.back().get());
4152
4154
4155 // progress bar
4156 ShowStatline();
4157}
4158
4160{
4161 // open a new paragraph
4162 if( m_pPam->GetPoint()->GetContentIndex() )
4164 else
4165 AddParSpace();
4166
4167 // search context matching the token and fetch it from stack
4168 std::unique_ptr<HTMLAttrContext> xCntxt;
4169 auto nPos = m_aContexts.size();
4170 while( !xCntxt && nPos>m_nContextStMin )
4171 {
4172 switch( m_aContexts[--nPos]->GetToken() )
4173 {
4174 case HtmlTokenId::HEAD1_ON:
4175 case HtmlTokenId::HEAD2_ON:
4176 case HtmlTokenId::HEAD3_ON:
4177 case HtmlTokenId::HEAD4_ON:
4178 case HtmlTokenId::HEAD5_ON:
4179 case HtmlTokenId::HEAD6_ON:
4180 xCntxt = std::move(m_aContexts[nPos]);
4181 m_aContexts.erase( m_aContexts.begin() + nPos );
4182 break;
4183 default: break;
4184 }
4185 }
4186
4187 // and now end attributes
4188 if (xCntxt)
4189 {
4190 EndContext(xCntxt.get());
4191 SetAttr(); // because of JavaScript set paragraph attributes as fast as possible
4192 xCntxt.reset();
4193 }
4194
4195 // reset existing style
4197
4199}
4200
4201void SwHTMLParser::NewTextFormatColl( HtmlTokenId nToken, sal_uInt16 nColl )
4202{
4203 OUString aId, aStyle, aClass, aLang, aDir;
4204
4205 const HTMLOptions& rHTMLOptions = GetOptions();
4206 for (size_t i = rHTMLOptions.size(); i; )
4207 {
4208 const HTMLOption& rOption = rHTMLOptions[--i];
4209 switch( rOption.GetToken() )
4210 {
4211 case HtmlOptionId::ID:
4212 aId = rOption.GetString();
4213 break;
4214 case HtmlOptionId::STYLE:
4215 aStyle = rOption.GetString();
4216 break;
4217 case HtmlOptionId::CLASS:
4218 aClass = rOption.GetString();
4219 break;
4220 case HtmlOptionId::LANG:
4221 aLang = rOption.GetString();
4222 break;
4223 case HtmlOptionId::DIR:
4224 aDir = rOption.GetString();
4225 break;
4226 default: break;
4227 }
4228 }
4229
4230 // open a new paragraph
4232 switch( nToken )
4233 {
4234 case HtmlTokenId::LISTING_ON:
4235 case HtmlTokenId::XMP_ON:
4236 // These both tags will be mapped to the PRE style. For the case that a
4237 // a CLASS exists we will delete it so that we don't get the CLASS of
4238 // the PRE style.
4239 aClass.clear();
4240 [[fallthrough]];
4241 case HtmlTokenId::BLOCKQUOTE_ON:
4242 case HtmlTokenId::BLOCKQUOTE30_ON:
4243 case HtmlTokenId::PREFORMTXT_ON:
4244 eMode = AM_SPACE;
4245 break;
4246 case HtmlTokenId::ADDRESS_ON:
4247 eMode = AM_NOSPACE; // ADDRESS can follow on a <P> without </P>
4248 break;
4249 case HtmlTokenId::DT_ON:
4250 case HtmlTokenId::DD_ON:
4252 break;
4253 default:
4254 OSL_ENSURE( false, "unknown style" );
4255 break;
4256 }
4257 if( m_pPam->GetPoint()->GetContentIndex() )
4259 else if( AM_SPACE==eMode )
4260 AddParSpace();
4261
4262 // ... and save in a context
4263 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken, nColl, aClass));
4264
4265 // parse styles (regarding class see also NewPara)
4266 if (HasStyleOptions(aStyle, aId, {}, &aLang, &aDir))
4267 {
4268 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
4269 SvxCSS1PropertyInfo aPropInfo;
4270
4271 if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir))
4272 {
4273 OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ),
4274 "Class is not considered" );
4275 DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
4276 InsertAttrs( aItemSet, aPropInfo, xCntxt.get() );
4277 }
4278 }
4279
4280 PushContext(xCntxt);
4281
4282 // set the new style
4283 SetTextCollAttrs(m_aContexts.back().get());
4284
4285 // update progress bar
4286 ShowStatline();
4287}
4288
4290{
4292 switch( getOnToken(nToken) )
4293 {
4294 case HtmlTokenId::BLOCKQUOTE_ON:
4295 case HtmlTokenId::BLOCKQUOTE30_ON:
4296 case HtmlTokenId::PREFORMTXT_ON:
4297 case HtmlTokenId::LISTING_ON:
4298 case HtmlTokenId::XMP_ON:
4299 eMode = AM_SPACE;
4300 break;
4301 case HtmlTokenId::ADDRESS_ON:
4302 case HtmlTokenId::DT_ON:
4303 case HtmlTokenId::DD_ON:
4305 break;
4306 default:
4307 OSL_ENSURE( false, "unknown style" );
4308 break;
4309 }
4310 if( m_pPam->GetPoint()->GetContentIndex() )
4312 else if( AM_SPACE==eMode )
4313 AddParSpace();
4314
4315 // pop current context of stack
4316 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(getOnToken(nToken)));
4317
4318 // and now end attributes
4319 if (xCntxt)
4320 {
4321 EndContext(xCntxt.get());
4322 SetAttr(); // because of JavaScript set paragraph attributes as fast as possible
4323 xCntxt.reset();
4324 }
4325
4326 // reset existing style
4328}
4329
4331{
4332 OUString aId, aStyle, aClass, aLang, aDir;
4333
4334 const HTMLOptions& rHTMLOptions = GetOptions();
4335 for (size_t i = rHTMLOptions.size(); i; )
4336 {
4337 const HTMLOption& rOption = rHTMLOptions[--i];
4338 switch( rOption.GetToken() )
4339 {
4340 case HtmlOptionId::ID:
4341 aId = rOption.GetString();
4342 break;
4343 case HtmlOptionId::STYLE:
4344 aStyle = rOption.GetString();
4345 break;
4346 case HtmlOptionId::CLASS:
4347 aClass = rOption.GetString();
4348 break;
4349 case HtmlOptionId::LANG:
4350 aLang = rOption.GetString();
4351 break;
4352 case HtmlOptionId::DIR:
4353 aDir = rOption.GetString();
4354 break;
4355 default: break;
4356 }
4357 }
4358
4359 // open a new paragraph
4360 bool bSpace = (GetNumInfo().GetDepth() + m_nDefListDeep) == 0;
4361 if( m_pPam->GetPoint()->GetContentIndex() )
4363 else if( bSpace )
4364 AddParSpace();
4365
4366 // one level more
4368
4369 bool bInDD = false, bNotInDD = false;
4370 auto nPos = m_aContexts.size();
4371 while( !bInDD && !bNotInDD && nPos>m_nContextStMin )
4372 {
4373 HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken();
4374 switch( nCntxtToken )
4375 {
4376 case HtmlTokenId::DEFLIST_ON:
4377 case HtmlTokenId::DIRLIST_ON:
4378 case HtmlTokenId::MENULIST_ON:
4379 case HtmlTokenId::ORDERLIST_ON:
4380 case HtmlTokenId::UNORDERLIST_ON:
4381 bNotInDD = true;
4382 break;
4383 case HtmlTokenId::DD_ON:
4384 bInDD = true;
4385 break;
4386 default: break;
4387 }
4388 }
4389
4390 // ... and save in a context
4391 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::DEFLIST_ON));
4392
4393 // in it save also the margins
4394 sal_uInt16 nLeft=0, nRight=0;
4395 short nIndent=0;
4396 GetMarginsFromContext( nLeft, nRight, nIndent );
4397
4398 // The indentation, which already results from a DL, correlates with a DT
4399 // on the current level and this correlates to a DD from the previous level.
4400 // For a level >=2 we must add DD distance.
4401 if( !bInDD && m_nDefListDeep > 1 )
4402 {
4403
4404 // and the one of the DT-style of the current level
4405 SvxTextLeftMarginItem const& rTextLeftMargin =
4406 m_pCSS1Parser->GetTextFormatColl(RES_POOLCOLL_HTML_DD, OUString())
4407 ->GetTextLeftMargin();
4408 nLeft = nLeft + static_cast<sal_uInt16>(rTextLeftMargin.GetTextLeft());
4409 }
4410
4411 xCntxt->SetMargins( nLeft, nRight, nIndent );
4412
4413 // parse styles
4414 if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
4415 {
4416 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
4417 SvxCSS1PropertyInfo aPropInfo;
4418
4419 if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo, &aLang, &aDir ) )
4420 {
4421 DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
4422 InsertAttrs( aItemSet, aPropInfo, xCntxt.get() );
4423 }
4424 }
4425
4426 PushContext(xCntxt);
4427
4428 // set the attributes of the new style
4429 if( m_nDefListDeep > 1 )
4430 SetTextCollAttrs(m_aContexts.back().get());
4431}
4432
4434{
4435 bool bSpace = (GetNumInfo().GetDepth() + m_nDefListDeep) == 1;
4436 if( m_pPam->GetPoint()->GetContentIndex() )
4438 else if( bSpace )
4439 AddParSpace();
4440
4441 // one level less
4442 if( m_nDefListDeep > 0 )
4444
4445 // pop current context of stack
4446 std::unique_ptr<HTMLAttrContext> xCntxt(PopContext(HtmlTokenId::DEFLIST_ON));
4447
4448 // and now end attributes
4449 if (xCntxt)
4450 {
4451 EndContext(xCntxt.get());
4452 SetAttr(); // because of JavaScript set paragraph attributes as fast as possible
4453 xCntxt.reset();
4454 }
4455
4456 // and set style
4458}
4459
4461{
4462 // determine if the DD/DT exist in a DL
4463 bool bInDefList = false, bNotInDefList = false;
4464 auto nPos = m_aContexts.size();
4465 while( !bInDefList && !bNotInDefList && nPos>m_nContextStMin )
4466 {
4467 HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken();
4468 switch( nCntxtToken )
4469 {
4470 case HtmlTokenId::DEFLIST_ON:
4471 bInDefList = true;
4472 break;
4473 case HtmlTokenId::DIRLIST_ON:
4474 case HtmlTokenId::MENULIST_ON:
4475 case HtmlTokenId::ORDERLIST_ON:
4476 case HtmlTokenId::UNORDERLIST_ON:
4477 bNotInDefList = true;
4478 break;
4479 default: break;
4480 }
4481 }
4482
4483 // if not, then implicitly open a new DL
4484 if( !bInDefList )
4485 {
4487 OSL_ENSURE( m_nOpenParaToken == HtmlTokenId::NONE,
4488 "Now an open paragraph element will be lost." );
4490 }
4491
4492 NewTextFormatColl( nToken, static_cast< sal_uInt16 >(nToken==HtmlTokenId::DD_ON ? RES_POOLCOLL_HTML_DD
4494}
4495
4497{
4498 // open a new paragraph
4499 if( nToken == HtmlTokenId::NONE && m_pPam->GetPoint()->GetContentIndex() )
4501
4502 // search context matching the token and fetch it from stack
4504 std::unique_ptr<HTMLAttrContext> xCntxt;
4505 auto nPos = m_aContexts.size();
4506 while( !xCntxt && nPos>m_nContextStMin )
4507 {
4508 HtmlTokenId nCntxtToken = m_aContexts[--nPos]->GetToken();
4509 switch( nCntxtToken )
4510 {
4511 case HtmlTokenId::DD_ON:
4512 case HtmlTokenId::DT_ON:
4513 if( nToken == HtmlTokenId::NONE || nToken == nCntxtToken )
4514 {
4515 xCntxt = std::move(m_aContexts[nPos]);
4516 m_aContexts.erase( m_aContexts.begin() + nPos );
4517 }
4518 break;
4519 case HtmlTokenId::DEFLIST_ON:
4520 // don't look at DD/DT outside the current DefList
4521 case HtmlTokenId::DIRLIST_ON:
4522 case HtmlTokenId::MENULIST_ON:
4523 case HtmlTokenId::ORDERLIST_ON:
4524 case HtmlTokenId::UNORDERLIST_ON:
4525 // and also not outside another list
4527 break;
4528 default: break;
4529 }
4530 }
4531
4532 // and now end attributes
4533 if (xCntxt)
4534 {
4535 EndContext(xCntxt.get());
4536 SetAttr(); // because of JavaScript set paragraph attributes as fast as possible
4537 }
4538}
4539
4549bool SwHTMLParser::HasCurrentParaFlys( bool bNoSurroundOnly,
4550 bool bSurroundOnly ) const
4551{
4552 SwNode& rNode = m_pPam->GetPoint()->GetNode();
4553
4554 const SwFrameFormats& rFrameFormatTable = *m_xDoc->GetSpzFrameFormats();
4555
4556 bool bFound = false;
4557 for ( size_t i=0; i<rFrameFormatTable.size(); i++ )
4558 {
4559 const SwFrameFormat *const pFormat = rFrameFormatTable[i];
4560 SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
4561 // A frame was found, when
4562 // - it is paragraph-bound, and
4563 // - is anchored in current paragraph, and
4564 // - every paragraph-bound frame counts, or
4565 // - (only frames without wrapping count and) the frame doesn't have
4566 // a wrapping
4567 SwNode const*const pAnchorNode = pAnchor->GetAnchorNode();
4568 if (pAnchorNode &&
4569 ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
4570 (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
4571 *pAnchorNode == rNode )
4572 {
4573 if( !(bNoSurroundOnly || bSurroundOnly) )
4574 {
4575 bFound = true;
4576 break;
4577 }
4578 else
4579 {
4580 // When looking for frames with wrapping, also disregard
4581 // ones with wrap-through. In this case it's (still) HIDDEN-Controls,
4582 // and you don't want to evade those when positioning.
4583 css::text::WrapTextMode eSurround = pFormat->GetSurround().GetSurround();
4584 if( bNoSurroundOnly )
4585 {
4586 if( css::text::WrapTextMode_NONE==eSurround )
4587 {
4588 bFound = true;
4589 break;
4590 }
4591 }
4592 if( bSurroundOnly )
4593 {
4594 if( css::text::WrapTextMode_NONE==eSurround )
4595 {
4596 bFound = false;
4597 break;
4598 }
4599 else if( css::text::WrapTextMode_THROUGH!=eSurround )
4600 {
4601 bFound = true;
4602 // Continue searching: It's possible that some without
4603 // wrapping will follow...
4604 }
4605 }
4606 }
4607 }
4608 }
4609
4610 return bFound;
4611}
4612
4613// the special methods for inserting of objects
4614
4616{
4617 const SwContentNode* pCNd = m_pPam->GetPointContentNode();
4618 return pCNd ? &pCNd->GetAnyFormatColl() : nullptr;
4619}
4620
4622{
4623 SwTextFormatColl *pCollToSet = nullptr; // the style to set
4624 SfxItemSet *pItemSet = nullptr; // set of hard attributes
4625 sal_uInt16 nTopColl = pContext ? pContext->GetTextFormatColl() : 0;
4626 const OUString rTopClass = pContext ? pContext->GetClass() : OUString();
4627 sal_uInt16 nDfltColl = RES_POOLCOLL_TEXT;
4628
4629 bool bInPRE=false; // some context info
4630
4631 sal_uInt16 nLeftMargin = 0, nRightMargin = 0; // the margins and
4632 short nFirstLineIndent = 0; // indentations
4633
4634 auto nDepth = m_aContexts.size();
4635 if (bFuzzing && nDepth > 128)
4636 {
4637 SAL_WARN("sw.html", "Not applying any more text collection attributes to a deeply nested node for fuzzing performance");
4638 nDepth = 0;
4639 }
4640
4641 for (auto i = m_nContextStAttrMin; i < nDepth; ++i)
4642 {
4643 const HTMLAttrContext *pCntxt = m_aContexts[i].get();
4644
4645 sal_uInt16 nColl = pCntxt->GetTextFormatColl();
4646 if( nColl )
4647 {
4648 // There is a style to set. Then at first we must decide,
4649 // if the style can be set.
4650 bool bSetThis = true;
4651 switch( nColl )
4652 {
4654 bInPRE = true;
4655 break;
4656 case RES_POOLCOLL_TEXT:
4657 // <TD><P CLASS=xxx> must become TD.xxx
4658 if( nDfltColl==RES_POOLCOLL_TABLE ||
4659 nDfltColl==RES_POOLCOLL_TABLE_HDLN )
4660 nColl = nDfltColl;
4661 break;
4663 // also <HR> in <PRE> set as style, otherwise it can't
4664 // be exported anymore
4665 break;
4666 default:
4667 if( bInPRE )
4668 bSetThis = false;
4669 break;
4670 }
4671
4672 SwTextFormatColl *pNewColl =
4673 m_pCSS1Parser->GetTextFormatColl( nColl, pCntxt->GetClass() );
4674
4675 if( bSetThis )
4676 {
4677 // If now a different style should be set as previously, the
4678 // previous style must be replaced by hard attribution.
4679
4680 if( pCollToSet )
4681 {
4682 // insert the attributes hard, which previous style sets
4683 if( !pItemSet )
4684 pItemSet = new SfxItemSet( pCollToSet->GetAttrSet() );
4685 else
4686 {
4687 const SfxItemSet& rCollSet = pCollToSet->GetAttrSet();
4688 SfxItemSet aItemSet( *rCollSet.GetPool(),
4689 rCollSet.GetRanges() );
4690 aItemSet.Set( rCollSet );
4691 pItemSet->Put( aItemSet );
4692 }
4693 // but remove the attributes, which the current style sets,
4694 // because otherwise they will be overwritten later
4695 pItemSet->Differentiate( pNewColl->GetAttrSet() );
4696 }
4697
4698 pCollToSet = pNewColl;
4699 }
4700 else
4701 {
4702 // hard attribution
4703 if( !pItemSet )
4704 pItemSet = new SfxItemSet( pNewColl->GetAttrSet() );
4705 else
4706 {
4707 const SfxItemSet& rCollSet = pNewColl->GetAttrSet();
4708 SfxItemSet aItemSet( *rCollSet.GetPool(),
4709 rCollSet.GetRanges() );
4710 aItemSet.Set( rCollSet );
4711 pItemSet->Put( aItemSet );
4712 }
4713 }
4714 }
4715 else
4716 {
4717 // Maybe a default style exists?
4718 nColl = pCntxt->GetDefaultTextFormatColl();
4719 if( nColl )
4720 nDfltColl = nColl;
4721 }
4722
4723 // if applicable fetch new paragraph indents
4724 if( pCntxt->IsLRSpaceChanged() )
4725 {
4726 sal_uInt16 nLeft=0, nRight=0;
4727
4728 pCntxt->GetMargins( nLeft, nRight, nFirstLineIndent );
4729 nLeftMargin = nLeft;
4730 nRightMargin = nRight;
4731 }
4732 }
4733
4734 // If in current context a new style should be set,
4735 // its paragraph margins must be inserted in the context.
4736 if( pContext && nTopColl )
4737 {
4738 // <TD><P CLASS=xxx> must become TD.xxx
4739 if( nTopColl==RES_POOLCOLL_TEXT &&
4740 (nDfltColl==RES_POOLCOLL_TABLE ||
4741 nDfltColl==RES_POOLCOLL_TABLE_HDLN) )
4742 nTopColl = nDfltColl;
4743
4744 const SwTextFormatColl *pTopColl =
4745 m_pCSS1Parser->GetTextFormatColl( nTopColl, rTopClass );
4746 const SfxItemSet& rItemSet = pTopColl->GetAttrSet();
4749 || rItemSet.GetItemIfSet(RES_MARGIN_RIGHT))
4750 {
4751 sal_Int32 nLeft = rItemSet.Get(RES_MARGIN_TEXTLEFT).GetTextLeft();
4752 sal_Int32 nRight = rItemSet.Get(RES_MARGIN_RIGHT).GetRight();
4753 nFirstLineIndent = rItemSet.Get(RES_MARGIN_FIRSTLINE).GetTextFirstLineOffset();
4754
4755 // In Definition lists the margins also contain the margins from the previous levels
4756 if( RES_POOLCOLL_HTML_DD == nTopColl )
4757 {
4758 auto const*const pColl(m_pCSS1Parser->GetTextFormatColl(RES_POOLCOLL_HTML_DT, OUString()));
4759 nLeft -= pColl->GetTextLeftMargin().GetTextLeft();
4760 nRight -= pColl->GetRightMargin().GetRight();
4761 }
4762 else if( RES_POOLCOLL_HTML_DT == nTopColl )
4763 {
4764 nLeft = 0;
4765 nRight = 0;
4766 }
4767
4768 // the paragraph margins add up
4769 nLeftMargin = nLeftMargin + static_cast< sal_uInt16 >(nLeft);
4770 nRightMargin = nRightMargin + static_cast< sal_uInt16 >(nRight);
4771
4773 nFirstLineIndent );
4774 }
4775 if( const SvxULSpaceItem* pULItem = rItemSet.GetItemIfSet(RES_UL_SPACE) )
4776 {
4777 pContext->SetULSpace( pULItem->GetUpper(), pULItem->GetLower() );
4778 }
4779 }
4780
4781 // If no style is set in the context use the text body.
4782 if( !pCollToSet )
4783 {
4784 pCollToSet = m_pCSS1Parser->GetTextCollFromPool( nDfltColl );
4785 if( !nLeftMargin )
4786 {
4787 nLeftMargin = static_cast<sal_uInt16>(pCollToSet->GetTextLeftMargin().GetTextLeft());
4788 }
4789 if( !nRightMargin )
4790 {
4791 nRightMargin = static_cast<sal_uInt16>(pCollToSet->GetRightMargin().GetRight());
4792 }
4793 if( !nFirstLineIndent )
4794 {
4795 nFirstLineIndent = pCollToSet->GetFirstLineIndent().GetTextFirstLineOffset();
4796 }
4797 }
4798
4799 // remove previous hard attribution of paragraph
4800 for( auto pParaAttr : m_aParaAttrs )
4801 pParaAttr->Invalidate();
4802 m_aParaAttrs.clear();
4803
4804 // set the style
4805 m_xDoc->SetTextFormatColl( *m_pPam, pCollToSet );
4806
4807 // if applicable correct the paragraph indent
4808 const SvxFirstLineIndentItem & rFirstLine = pCollToSet->GetFirstLineIndent();
4809 const SvxTextLeftMarginItem & rTextLeftMargin = pCollToSet->GetTextLeftMargin();
4810 const SvxRightMarginItem & rRightMargin = pCollToSet->GetRightMargin();
4811 bool bSetLRSpace = nLeftMargin != rTextLeftMargin.GetTextLeft() ||
4812 nFirstLineIndent != rFirstLine.GetTextFirstLineOffset() ||
4813 nRightMargin != rRightMargin.GetRight();
4814
4815 if( bSetLRSpace )
4816 {
4817 SvxFirstLineIndentItem firstLine(rFirstLine);
4818 SvxTextLeftMarginItem leftMargin(rTextLeftMargin);
4819 SvxRightMarginItem rightMargin(rRightMargin);
4820 firstLine.SetTextFirstLineOffset(nFirstLineIndent);
4821 leftMargin.SetTextLeft(nLeftMargin);
4822 rightMargin.SetRight(nRightMargin);
4823 if( pItemSet )
4824 {
4825 pItemSet->Put(firstLine);
4826 pItemSet->Put(leftMargin);
4827 pItemSet->Put(rightMargin);
4828 }
4829 else
4830 {
4831 NewAttr(m_xAttrTab, &m_xAttrTab->pFirstLineIndent, firstLine);
4832 m_xAttrTab->pFirstLineIndent->SetLikePara();
4833 m_aParaAttrs.push_back(m_xAttrTab->pFirstLineIndent);
4834 EndAttr(m_xAttrTab->pFirstLineIndent, false);
4835 NewAttr(m_xAttrTab, &m_xAttrTab->pTextLeftMargin, leftMargin);
4836 m_xAttrTab->pTextLeftMargin->SetLikePara();
4837 m_aParaAttrs.push_back(m_xAttrTab->pTextLeftMargin);
4838 EndAttr(m_xAttrTab->pTextLeftMargin, false);
4839 NewAttr(m_xAttrTab, &m_xAttrTab->pRightMargin, rightMargin);
4840 m_xAttrTab->pRightMargin->SetLikePara();
4841 m_aParaAttrs.push_back(m_xAttrTab->pRightMargin);
4842 EndAttr(m_xAttrTab->pRightMargin, false);
4843 }
4844 }
4845
4846 // and now set the attributes
4847 if( pItemSet )
4848 {
4849 InsertParaAttrs( *pItemSet );
4850 delete pItemSet;
4851 }
4852}
4853
4855{
4856 OUString aId, aStyle, aLang, aDir;
4857 OUString aClass;
4858
4859 const HTMLOptions& rHTMLOptions = GetOptions();
4860 for (size_t i = rHTMLOptions.size(); i; )
4861 {
4862 const HTMLOption& rOption = rHTMLOptions[--i];
4863 switch( rOption.GetToken() )
4864 {
4865 case HtmlOptionId::ID:
4866 aId = rOption.GetString();
4867 break;
4868 case HtmlOptionId::STYLE:
4869 aStyle = rOption.GetString();
4870 break;
4871 case HtmlOptionId::CLASS:
4872 aClass = rOption.GetString();
4873 break;
4874 case HtmlOptionId::LANG:
4875 aLang = rOption.GetString();
4876 break;
4877 case HtmlOptionId::DIR:
4878 aDir = rOption.GetString();
4879 break;
4880 default: break;
4881 }
4882 }
4883
4884 // create a new context
4885 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
4886
4887 // set the style and save it in the context
4888 SwCharFormat* pCFormat = m_pCSS1Parser->GetChrFormat( nToken, aClass );
4889 OSL_ENSURE( pCFormat, "No character format found for token" );
4890
4891 // parse styles (regarding class see also NewPara)
4892 if (HasStyleOptions(aStyle, aId, {}, &aLang, &aDir))
4893 {
4894 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
4895 SvxCSS1PropertyInfo aPropInfo;
4896
4897 if (ParseStyleOptions(aStyle, aId, OUString(), aItemSet, aPropInfo, &aLang, &aDir))
4898 {
4899 OSL_ENSURE( aClass.isEmpty() || !m_pCSS1Parser->GetClass( aClass ),
4900 "Class is not considered" );
4901 DoPositioning( aItemSet, aPropInfo, xCntxt.get() );
4902 InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
4903 }
4904 }
4905
4906 // Character formats are stored in their own stack and can never be inserted
4907 // by styles. Therefore the attribute doesn't exist in CSS1-Which-Range.
4908 if( pCFormat )
4909 InsertAttr( &m_xAttrTab->pCharFormats, SwFormatCharFormat( pCFormat ), xCntxt.get() );
4910
4911 // save the context
4912 PushContext(xCntxt);
4913}
4914
4916{
4917 // and if applicable change it via the options
4918 sal_Int16 eVertOri = text::VertOrientation::TOP;
4919 sal_Int16 eHoriOri = text::HoriOrientation::NONE;
4920 Size aSize( 0, 0);
4921 tools::Long nSize = 0;
4922 bool bPercentWidth = false;
4923 bool bPercentHeight = false;
4924 sal_uInt16 nType = HTML_SPTYPE_HORI;
4925
4926 const HTMLOptions& rHTMLOptions = GetOptions();
4927 for (size_t i = rHTMLOptions.size(); i; )
4928 {
4929 const HTMLOption& rOption = rHTMLOptions[--i];
4930 switch( rOption.GetToken() )
4931 {
4932 case HtmlOptionId::TYPE:
4934 break;
4935 case HtmlOptionId::ALIGN:
4936 eVertOri =
4938 eVertOri );
4939 eHoriOri =
4941 eHoriOri );
4942 break;
4943 case HtmlOptionId::WIDTH:
4944 // First only save as pixel value!
4945 bPercentWidth = (rOption.GetString().indexOf('%') != -1);
4946 aSize.setWidth( static_cast<tools::Long>(rOption.GetNumber()) );
4947 break;
4948 case HtmlOptionId::HEIGHT:
4949 // First only save as pixel value!
4950 bPercentHeight = (rOption.GetString().indexOf('%') != -1);
4951 aSize.setHeight( static_cast<tools::Long>(rOption.GetNumber()) );
4952 break;
4953 case HtmlOptionId::SIZE:
4954 // First only save as pixel value!
4955 nSize = rOption.GetNumber();
4956 break;
4957 default: break;
4958 }
4959 }
4960
4961 switch( nType )
4962 {
4963 case HTML_SPTYPE_BLOCK:
4964 {
4965 // create an empty text frame
4966
4967 // fetch the ItemSet
4968 SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aFrameSet( m_xDoc->GetAttrPool() );
4969 if( !IsNewDoc() )
4970 Reader::ResetFrameFormatAttrs( aFrameSet );
4971
4972 // set the anchor and the adjustment
4973 SetAnchorAndAdjustment( eVertOri, eHoriOri, aFrameSet );
4974
4975 // and the size of the frame
4976 Size aDfltSz( MINFLY, MINFLY );
4977 Size aSpace( 0, 0 );
4978 SfxItemSet aDummyItemSet( m_xDoc->GetAttrPool(),
4979 m_pCSS1Parser->GetWhichMap() );
4980 SvxCSS1PropertyInfo aDummyPropInfo;
4981
4982 SetFixSize( aSize, aDfltSz, bPercentWidth, bPercentHeight,
4983 aDummyPropInfo, aFrameSet );
4984 SetSpace( aSpace, aDummyItemSet, aDummyPropInfo, aFrameSet );
4985
4986 // protect the content
4987 SvxProtectItem aProtectItem( RES_PROTECT) ;
4988 aProtectItem.SetContentProtect( true );
4989 aFrameSet.Put( aProtectItem );
4990
4991 // create the frame
4992 RndStdIds eAnchorId =
4993 aFrameSet.Get(RES_ANCHOR).GetAnchorId();
4994 SwFrameFormat *pFlyFormat = m_xDoc->MakeFlySection( eAnchorId,
4995 m_pPam->GetPoint(), &aFrameSet );
4996 // Possibly create frames and register auto-bound frames.
4997 RegisterFlyFrame( pFlyFormat );
4998 }
4999 break;
5000 case HTML_SPTYPE_VERT:
5001 if( nSize > 0 )
5002 {
5004 {
5006 ->PixelToLogic( Size(0,nSize),
5007 MapMode(MapUnit::MapTwip) ).Height();
5008 }
5009
5010 // set a paragraph margin
5011 SwTextNode *pTextNode = nullptr;
5012 if( !m_pPam->GetPoint()->GetContentIndex() )
5013 {
5014 // if possible change the bottom paragraph margin
5015 // of previous node
5016
5017 SetAttr(); // set still open paragraph attributes
5018
5019 pTextNode = m_xDoc->GetNodes()[m_pPam->GetPoint()->GetNodeIndex()-1]
5020 ->GetTextNode();
5021
5022 // If the previous paragraph isn't a text node, then now an
5023 // empty paragraph is created, which already generates a single
5024 // line of spacing.
5025 if( !pTextNode )
5026 nSize = nSize>HTML_PARSPACE ? nSize-HTML_PARSPACE : 0;
5027 }
5028
5029 if( pTextNode )
5030 {
5031 SvxULSpaceItem aULSpace( pTextNode->SwContentNode::GetAttr( RES_UL_SPACE ) );
5032 aULSpace.SetLower( aULSpace.GetLower() + o3tl::narrowing<sal_uInt16>(nSize) );
5033 pTextNode->SetAttr( aULSpace );
5034 }
5035 else
5036 {
5037 NewAttr(m_xAttrTab, &m_xAttrTab->pULSpace, SvxULSpaceItem(0, o3tl::narrowing<sal_uInt16>(nSize), RES_UL_SPACE));
5038 EndAttr( m_xAttrTab->pULSpace, false );
5039
5040 AppendTextNode(); // Don't change spacing!
5041 }
5042 }
5043 break;
5044 case HTML_SPTYPE_HORI:
5045 if( nSize > 0 )
5046 {
5047 // If the paragraph is still empty, set first line
5048 // indentation, otherwise apply letter spacing over a space.
5049
5051 {
5053 ->PixelToLogic( Size(nSize,0),
5054 MapMode(MapUnit::MapTwip) ).Width();
5055 }
5056
5057 if( !m_pPam->GetPoint()->GetContentIndex() )
5058 {
5059 sal_uInt16 nLeft=0, nRight=0;
5060 short nIndent = 0;
5061
5062 GetMarginsFromContextWithNumberBullet( nLeft, nRight, nIndent );
5063 nIndent = nIndent + static_cast<short>(nSize);
5064
5065 SvxFirstLineIndentItem const firstLine(nIndent, RES_MARGIN_FIRSTLINE);
5066 SvxTextLeftMarginItem const leftMargin(nLeft, RES_MARGIN_TEXTLEFT);
5067 SvxRightMarginItem const rightMargin(nRight, RES_MARGIN_RIGHT);
5068
5069 NewAttr(m_xAttrTab, &m_xAttrTab->pFirstLineIndent, firstLine);
5070 EndAttr(m_xAttrTab->pFirstLineIndent, false);
5071 NewAttr(m_xAttrTab, &m_xAttrTab->pTextLeftMargin, leftMargin);
5072 EndAttr(m_xAttrTab->pTextLeftMargin, false);
5073 NewAttr(m_xAttrTab, &m_xAttrTab->pRightMargin, rightMargin);
5074 EndAttr(m_xAttrTab->pRightMargin, false);
5075 }
5076 else
5077 {
5078 NewAttr(m_xAttrTab, &m_xAttrTab->pKerning, SvxKerningItem( static_cast<short>(nSize), RES_CHRATR_KERNING ));
5079 m_xDoc->getIDocumentContentOperations().InsertString( *m_pPam, " " );
5080 EndAttr( m_xAttrTab->pKerning );
5081 }
5082 }
5083 }
5084}
5085
5086sal_uInt16 SwHTMLParser::ToTwips( sal_uInt16 nPixel )
5087{
5089 {
5091 Size( nPixel, nPixel ), MapMode( MapUnit::MapTwip ) ).Width();
5092 return o3tl::narrowing<sal_uInt16>(std::min(nTwips, SwTwips(SAL_MAX_UINT16)));
5093 }
5094 else
5095 return nPixel;
5096}
5097
5099{
5101 if( nWidth )
5102 return nWidth;
5103
5104 if( !m_aHTMLPageSize.Width() )
5105 {
5106 const SwFrameFormat& rPgFormat = m_pCSS1Parser->GetMasterPageDesc()->GetMaster();
5107
5108 const SwFormatFrameSize& rSz = rPgFormat.GetFrameSize();
5109 const SvxLRSpaceItem& rLR = rPgFormat.GetLRSpace();
5110 const SvxULSpaceItem& rUL = rPgFormat.GetULSpace();
5111 const SwFormatCol& rCol = rPgFormat.GetCol();
5112
5113 m_aHTMLPageSize.setWidth( rSz.GetWidth() - rLR.GetLeft() - rLR.GetRight() );
5114 m_aHTMLPageSize.setHeight( rSz.GetHeight() - rUL.GetUpper() - rUL.GetLower() );
5115
5116 if( 1 < rCol.GetNumCols() )
5118 }
5119
5120 return m_aHTMLPageSize.Width();
5121}
5122
5124{
5125 OUString aId;
5126 const HTMLOptions& rHTMLOptions = GetOptions();
5127 for (size_t i = rHTMLOptions.size(); i; )
5128 {
5129 const HTMLOption& rOption = rHTMLOptions[--i];
5130 if( HtmlOptionId::ID==rOption.GetToken() )
5131 {
5132 aId = rOption.GetString();
5133 break;
5134 }
5135 }
5136
5137 if( !aId.isEmpty() )
5138 InsertBookmark( aId );
5139}
5140
5142{
5143 OUString aId, aStyle, aClass; // the id of bookmark
5145
5146 // then we fetch the options
5147 const HTMLOptions& rHTMLOptions = GetOptions();
5148 for (size_t i = rHTMLOptions.size(); i; )
5149 {
5150 const HTMLOption& rOption = rHTMLOptions[--i];
5151 switch( rOption.GetToken() )
5152 {
5153 case HtmlOptionId::CLEAR:
5154 {
5155 const OUString &rClear = rOption.GetString();
5156 if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_all ) )
5157 {
5158 eClear = SwLineBreakClear::ALL;
5159 }
5160 else if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_left ) )
5161 {
5162 eClear = SwLineBreakClear::LEFT;
5163 }
5164 else if( rClear.equalsIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_AL_right ) )
5165 {
5166 eClear = SwLineBreakClear::LEFT;
5167 }
5168 }
5169 break;
5170 case HtmlOptionId::ID:
5171 aId = rOption.GetString();
5172 break;
5173 case HtmlOptionId::STYLE:
5174 aStyle = rOption.GetString();
5175 break;
5176 case HtmlOptionId::CLASS:
5177 aClass = rOption.GetString();
5178 break;
5179 default: break;
5180 }
5181 }
5182
5183 // parse styles
5184 std::shared_ptr<SvxFormatBreakItem> aBreakItem(std::make_shared<SvxFormatBreakItem>(SvxBreak::NONE, RES_BREAK));
5185 bool bBreakItem = false;
5186 if( HasStyleOptions( aStyle, aId, aClass ) )
5187 {
5188 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
5189 SvxCSS1PropertyInfo aPropInfo;
5190
5191 if( ParseStyleOptions( aStyle, aId, aClass, aItemSet, aPropInfo ) )
5192 {
5193 if( m_pCSS1Parser->SetFormatBreak( aItemSet, aPropInfo ) )
5194 {
5195 aBreakItem.reset(aItemSet.Get(RES_BREAK).Clone());
5196 bBreakItem = true;
5197 }
5198 if( !aPropInfo.m_aId.isEmpty() )
5199 InsertBookmark( aPropInfo.m_aId );
5200 }
5201 }
5202
5203 if( bBreakItem && SvxBreak::PageAfter == aBreakItem->GetBreak() )
5204 {
5205 NewAttr(m_xAttrTab, &m_xAttrTab->pBreak, *aBreakItem);
5206 EndAttr( m_xAttrTab->pBreak, false );
5207 }
5208
5209 if (!bBreakItem)
5210 {
5211 if (eClear == SwLineBreakClear::NONE)
5212 {
5213 // If no CLEAR could or should be executed, a line break will be inserted
5214 m_xDoc->getIDocumentContentOperations().InsertString(*m_pPam, "\x0A");
5215 }
5216 else
5217 {
5218 // <BR CLEAR=xxx> is mapped an SwFormatLineBreak.
5219 SwTextNode* pTextNode = m_pPam->GetPointNode().GetTextNode();
5220 if (pTextNode)
5221 {
5222 SwFormatLineBreak aLineBreak(eClear);
5223 sal_Int32 nPos = m_pPam->GetPoint()->GetContentIndex();
5224 pTextNode->InsertItem(aLineBreak, nPos, nPos);
5225 }
5226 }
5227 }
5228 else if( m_pPam->GetPoint()->GetContentIndex() )
5229 {
5230 // If a CLEAR is executed in a non-empty paragraph, then after it
5231 // a new paragraph has to be opened.
5232 // MIB 21.02.97: Here actually we should change the bottom paragraph
5233 // margin to zero. This will fail for something like this <BR ..><P>
5234 // (>Netscape). That's why we don't do it.
5236 }
5237 if( bBreakItem && SvxBreak::PageBefore == aBreakItem->GetBreak() )
5238 {
5239 NewAttr(m_xAttrTab, &m_xAttrTab->pBreak, *aBreakItem);
5240 EndAttr( m_xAttrTab->pBreak, false );
5241 }
5242}
5243
5245{
5246 sal_uInt16 nSize = 0;
5247 sal_uInt16 nWidth = 0;
5248
5249 SvxAdjust eAdjust = SvxAdjust::End;
5250
5251 bool bPercentWidth = false;
5252 bool bNoShade = false;
5253 bool bColor = false;
5254
5255 Color aColor;
5256 OUString aId;
5257
5258 // let's fetch the options
5259 const HTMLOptions& rHTMLOptions = GetOptions();
5260 for (size_t i = rHTMLOptions.size(); i; )
5261 {
5262 const HTMLOption& rOption = rHTMLOptions[--i];
5263 switch( rOption.GetToken() )
5264 {
5265 case HtmlOptionId::ID:
5266 aId = rOption.GetString();
5267 break;
5268 case HtmlOptionId::SIZE:
5269 nSize = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
5270 break;
5271 case HtmlOptionId::WIDTH:
5272 bPercentWidth = (rOption.GetString().indexOf('%') != -1);
5273 nWidth = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
5274 if( bPercentWidth && nWidth>=100 )
5275 {
5276 // the default case are 100% lines (no attributes necessary)
5277 nWidth = 0;
5278 bPercentWidth = false;
5279 }
5280 break;
5281 case HtmlOptionId::ALIGN:
5282 eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust );
5283 break;
5284 case HtmlOptionId::NOSHADE:
5285 bNoShade = true;
5286 break;
5287 case HtmlOptionId::COLOR:
5288 rOption.GetColor( aColor );
5289 bColor = true;
5290 break;
5291 default: break;
5292 }
5293 }
5294
5295 if( m_pPam->GetPoint()->GetContentIndex() )
5297 if( m_nOpenParaToken != HtmlTokenId::NONE )
5298 EndPara();
5301
5302 // ...and save in a context
5303 std::unique_ptr<HTMLAttrContext> xCntxt(
5304 new HTMLAttrContext(HtmlTokenId::HORZRULE, RES_POOLCOLL_HTML_HR, OUString()));
5305
5306 PushContext(xCntxt);
5307
5308 // set the new style
5309 SetTextCollAttrs(m_aContexts.back().get());
5310
5311 // the hard attributes of the current paragraph will never become invalid
5312 m_aParaAttrs.clear();
5313
5314 if( nSize>0 || bColor || bNoShade )
5315 {
5316 // set line colour and/or width
5