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 <com/sun/star/text/HoriOrientation.hpp>
21 #include <com/sun/star/text/VertOrientation.hpp>
22 #include <rtl/uri.hxx>
23
24 #include <svl/urihelper.hxx>
25 #include <vcl/svapp.hxx>
26 #include <editeng/adjustitem.hxx>
27 #include <editeng/ulspitem.hxx>
28 #include <editeng/formatbreakitem.hxx>
29 #include <svtools/htmltokn.h>
30 #include <svtools/htmlkywd.hxx>
31 #include <sfx2/linkmgr.hxx>
32 #include <osl/diagnose.h>
33
34 #include <hintids.hxx>
35 #include <fmthdft.hxx>
36 #include <fmtcntnt.hxx>
37 #include <fmtclds.hxx>
38 #include <fmtanchr.hxx>
39 #include <fmtpdsc.hxx>
40 #include <frmatr.hxx>
41 #include <doc.hxx>
42 #include <pam.hxx>
43 #include <ndtxt.hxx>
44 #include <shellio.hxx>
45 #include <section.hxx>
46 #include <poolfmt.hxx>
47 #include <pagedesc.hxx>
48 #include <swtable.hxx>
49 #include "swcss1.hxx"
50 #include "swhtml.hxx"
51
52
53 using namespace ::com::sun::star;
54
NewDivision(HtmlTokenId nToken)55 void SwHTMLParser::NewDivision( HtmlTokenId nToken )
56 {
57 OUString aId, aHRef;
58 OUString aStyle, aLang, aDir;
59 OUString aClass;
60 SvxAdjust eAdjust = HtmlTokenId::CENTER_ON==nToken ? SvxAdjust::Center
61 : SvxAdjust::End;
62
63 bool bHeader=false, bFooter=false;
64 const HTMLOptions& rHTMLOptions = GetOptions();
65 for (size_t i = rHTMLOptions.size(); i; )
66 {
67 const HTMLOption& rOption = rHTMLOptions[--i];
68 switch( rOption.GetToken() )
69 {
70 case HtmlOptionId::ID:
71 aId = rOption.GetString();
72 break;
73 case HtmlOptionId::ALIGN:
74 if( HtmlTokenId::DIVISION_ON==nToken )
75 eAdjust = rOption.GetEnum( aHTMLPAlignTable, eAdjust );
76 break;
77 case HtmlOptionId::STYLE:
78 aStyle = rOption.GetString();
79 break;
80 case HtmlOptionId::CLASS:
81 aClass = rOption.GetString();
82 break;
83 case HtmlOptionId::LANG:
84 aLang = rOption.GetString();
85 break;
86 case HtmlOptionId::DIR:
87 aDir = rOption.GetString();
88 break;
89 case HtmlOptionId::HREF:
90 aHRef = rOption.GetString();
91 break;
92 case HtmlOptionId::TITLE:
93 {
94 const OUString& rType = rOption.GetString();
95 if( rType.equalsIgnoreAsciiCase("header") )
96 bHeader = true;
97 else if( rType.equalsIgnoreAsciiCase("footer") )
98 bFooter = true;
99 }
100 break;
101 default: break;
102 }
103 }
104
105 bool bAppended = false;
106 if( m_pPam->GetPoint()->nContent.GetIndex() )
107 {
108 AppendTextNode( bHeader||bFooter||!aId.isEmpty()|| !aHRef.isEmpty() ? AM_NORMAL
109 : AM_NOSPACE );
110 bAppended = true;
111 }
112
113 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(nToken));
114
115 bool bStyleParsed = false, bPositioned = false;
116 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
117 SvxCSS1PropertyInfo aPropInfo;
118 if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
119 {
120 bStyleParsed = ParseStyleOptions( aStyle, aId, aClass,
121 aItemSet, aPropInfo, &aLang, &aDir );
122 if( bStyleParsed )
123 {
124 if ( aPropInfo.m_nColumnCount >= 2 )
125 {
126 xCntxt.reset();
127 NewMultiCol( aPropInfo.m_nColumnCount );
128 return;
129 }
130 bPositioned = HtmlTokenId::DIVISION_ON == nToken && !aClass.isEmpty() &&
131 CreateContainer(aClass, aItemSet, aPropInfo,
132 xCntxt.get());
133 if( !bPositioned )
134 {
135 if (aPropInfo.m_bVisible && m_aContexts.size())
136 {
137 const std::unique_ptr<HTMLAttrContext>& pParent
138 = m_aContexts[m_aContexts.size() - 1];
139 if (!pParent->IsVisible())
140 {
141 // If the parent context is hidden, we are not visible, either.
142 aPropInfo.m_bVisible = false;
143 }
144 }
145 bPositioned = DoPositioning(aItemSet, aPropInfo, xCntxt.get());
146 }
147 }
148 }
149
150 if (!bPositioned && (bHeader || bFooter) && IsNewDoc() && !m_bReadingHeaderOrFooter)
151 {
152 m_bReadingHeaderOrFooter = true;
153 xCntxt->SetHeaderOrFooter(true);
154
155 SwPageDesc *pPageDesc = m_pCSS1Parser->GetMasterPageDesc();
156 SwFrameFormat& rPageFormat = pPageDesc->GetMaster();
157
158 SwFrameFormat *pHdFtFormat;
159 bool bNew = false;
160 HtmlContextFlags nFlags = HtmlContextFlags::MultiColMask;
161 if( bHeader )
162 {
163 pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetHeader().GetHeaderFormat());
164 if( !pHdFtFormat )
165 {
166 // still no header, then create one
167 rPageFormat.SetFormatAttr( SwFormatHeader( true ));
168 pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetHeader().GetHeaderFormat());
169 bNew = true;
170 }
171 nFlags |= HtmlContextFlags::HeaderDist;
172 }
173 else
174 {
175 pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetFooter().GetFooterFormat());
176 if( !pHdFtFormat )
177 {
178 // still no footer, then create one
179 rPageFormat.SetFormatAttr( SwFormatFooter( true ));
180 pHdFtFormat = const_cast<SwFrameFormat*>(rPageFormat.GetFooter().GetFooterFormat());
181 bNew = true;
182 }
183 nFlags |= HtmlContextFlags::FooterDist;
184 }
185
186 const SwFormatContent& rFlyContent = pHdFtFormat->GetContent();
187 const SwNodeIndex& rContentStIdx = *rFlyContent.GetContentIdx();
188 SwContentNode *pCNd;
189
190 if( bNew )
191 {
192 pCNd = m_xDoc->GetNodes()[rContentStIdx.GetIndex()+1]
193 ->GetContentNode();
194 }
195 else
196 {
197 // Create a new node at the beginning of the section
198 SwNodeIndex aSttIdx( rContentStIdx, 1 );
199 pCNd = m_xDoc->GetNodes().MakeTextNode( aSttIdx,
200 m_pCSS1Parser->GetTextCollFromPool(RES_POOLCOLL_TEXT));
201
202 // delete the current content of the section
203 SwPaM aDelPam( aSttIdx );
204 aDelPam.SetMark();
205
206 const SwStartNode *pStNd =
207 static_cast<const SwStartNode *>( &rContentStIdx.GetNode() );
208 aDelPam.GetPoint()->nNode = pStNd->EndOfSectionIndex() - 1;
209
210 if (!PendingObjectsInPaM(aDelPam))
211 {
212 ClearFootnotesMarksInRange(aDelPam.GetMark()->nNode, aDelPam.GetPoint()->nNode);
213 m_xDoc->getIDocumentContentOperations().DelFullPara(aDelPam);
214 }
215
216 // update page style
217 for( size_t i=0; i < m_xDoc->GetPageDescCnt(); i++ )
218 {
219 if( RES_POOLPAGE_HTML == m_xDoc->GetPageDesc(i).GetPoolFormatId() )
220 {
221 m_xDoc->ChgPageDesc( i, *pPageDesc );
222 break;
223 }
224 }
225 }
226
227 SwPosition aNewPos( SwNodeIndex( rContentStIdx, 1 ), SwIndex( pCNd, 0 ) );
228 SaveDocContext(xCntxt.get(), nFlags, &aNewPos);
229 }
230 else if( !bPositioned && aId.getLength() > 9 &&
231 (aId[0] == 's' || aId[0] == 'S' ) &&
232 (aId[1] == 'd' || aId[1] == 'D' ) )
233 {
234 bool bEndNote = false, bFootNote = false;
235 if( aId.startsWithIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdendnote ) )
236 bEndNote = true;
237 else if( aId.startsWithIgnoreAsciiCase( OOO_STRING_SVTOOLS_HTML_sdfootnote ) )
238 bFootNote = true;
239 if( bFootNote || bEndNote )
240 {
241 SwNodeIndex *pStartNdIdx = GetFootEndNoteSection( aId );
242 if( pStartNdIdx )
243 {
244 SwContentNode *pCNd =
245 m_xDoc->GetNodes()[pStartNdIdx->GetIndex()+1]->GetContentNode();
246 SwNodeIndex aTmpSwNodeIndex(*pCNd);
247 SwPosition aNewPos( aTmpSwNodeIndex, SwIndex( pCNd, 0 ) );
248 SaveDocContext(xCntxt.get(), HtmlContextFlags::MultiColMask, &aNewPos);
249 aId.clear();
250 aPropInfo.m_aId.clear();
251 }
252 }
253 }
254
255 // We only insert sections into frames if the section is linked.
256 if( (!aId.isEmpty() && !bPositioned) || !aHRef.isEmpty() )
257 {
258 // Insert section (has to be done before setting of attributes,
259 // because the section is inserted before the PaM position.
260
261 // If we are in the first node of a section, we insert the section
262 // before the current section and not in the current section.
263 // Therefore we have to add a node and delete it again!
264 if( !bAppended )
265 {
266 SwNodeIndex aPrvNdIdx( m_pPam->GetPoint()->nNode, -1 );
267 if (aPrvNdIdx.GetNode().IsSectionNode())
268 {
269 AppendTextNode();
270 bAppended = true;
271 }
272 }
273 std::unique_ptr<std::deque<std::unique_ptr<HTMLAttr>>> pPostIts(bAppended ? nullptr : new std::deque<std::unique_ptr<HTMLAttr>>);
274 SetAttr( true, true, pPostIts.get() );
275
276 // make name of section unique
277 const OUString aName( m_xDoc->GetUniqueSectionName( !aId.isEmpty() ? &aId : nullptr ) );
278
279 if( !aHRef.isEmpty() )
280 {
281 sal_Unicode cDelim = 255U;
282 sal_Int32 nPos = aHRef.lastIndexOf( cDelim );
283 sal_Int32 nPos2 = -1;
284 if( nPos != -1 )
285 {
286 nPos2 = aHRef.lastIndexOf( cDelim, nPos );
287 if( nPos2 != -1 )
288 {
289 sal_Int32 nTmp = nPos;
290 nPos = nPos2;
291 nPos2 = nTmp;
292 }
293 }
294 OUString aURL;
295 if( nPos == -1 )
296 {
297 aURL = URIHelper::SmartRel2Abs(INetURLObject( m_sBaseURL ), aHRef, Link<OUString *, bool>(), false);
298 }
299 else
300 {
301 aURL = URIHelper::SmartRel2Abs(INetURLObject( m_sBaseURL ), aHRef.copy( 0, nPos ), Link<OUString *, bool>(), false )
302 + OUStringChar(sfx2::cTokenSeparator);
303 if( nPos2 == -1 )
304 {
305 aURL += aHRef.subView( nPos+1 );
306 }
307 else
308 {
309 aURL += aHRef.subView( nPos+1, nPos2 - (nPos+1) )
310 + OUStringChar(sfx2::cTokenSeparator)
311 + rtl::Uri::decode( aHRef.copy( nPos2+1 ),
312 rtl_UriDecodeWithCharset,
313 RTL_TEXTENCODING_ISO_8859_1 );
314 }
315 }
316 aHRef = aURL;
317 }
318
319 SwSectionData aSection( (!aHRef.isEmpty()) ? SectionType::FileLink
320 : SectionType::Content, aName );
321 if( !aHRef.isEmpty() )
322 {
323 aSection.SetLinkFileName( aHRef );
324 aSection.SetProtectFlag(true);
325 }
326
327 SfxItemSet aFrameItemSet( m_xDoc->GetAttrPool(),
328 svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} );
329 if( !IsNewDoc() )
330 Reader::ResetFrameFormatAttrs(aFrameItemSet );
331
332 const SfxPoolItem *pItem;
333 if( SfxItemState::SET == aItemSet.GetItemState( RES_BACKGROUND, false,
334 &pItem ) )
335 {
336 aFrameItemSet.Put( *pItem );
337 aItemSet.ClearItem( RES_BACKGROUND );
338 }
339 if( SfxItemState::SET == aItemSet.GetItemState( RES_FRAMEDIR, false,
340 &pItem ) )
341 {
342 aFrameItemSet.Put( *pItem );
343 aItemSet.ClearItem( RES_FRAMEDIR );
344 }
345
346 m_xDoc->InsertSwSection( *m_pPam, aSection, nullptr, &aFrameItemSet, false );
347
348 // maybe jump to section
349 if( JumpToMarks::Region == m_eJumpTo && aName == m_sJmpMark )
350 {
351 m_bChkJumpMark = true;
352 m_eJumpTo = JumpToMarks::NONE;
353 }
354
355 SwTextNode* pOldTextNd =
356 bAppended ? nullptr : m_pPam->GetPoint()->nNode.GetNode().GetTextNode();
357
358 m_pPam->Move( fnMoveBackward );
359
360 // move PageDesc and SwFormatBreak attribute from current node into
361 // (first) node of the section
362 if( pOldTextNd )
363 MovePageDescAttrs( pOldTextNd, m_pPam->GetPoint()->nNode.GetIndex(),
364 true );
365
366 if( pPostIts )
367 {
368 // move still existing PostIts in the first paragraph of the table
369 InsertAttrs( std::move(*pPostIts) );
370 pPostIts.reset();
371 }
372
373 xCntxt->SetSpansSection( true );
374
375 // don't insert Bookmarks with same name as sections
376 if( !aPropInfo.m_aId.isEmpty() && aPropInfo.m_aId==aName )
377 aPropInfo.m_aId.clear();
378 }
379 else
380 {
381 xCntxt->SetAppendMode( AM_NOSPACE );
382 }
383
384 if( SvxAdjust::End != eAdjust )
385 {
386 InsertAttr(&m_xAttrTab->pAdjust, SvxAdjustItem(eAdjust, RES_PARATR_ADJUST), xCntxt.get());
387 }
388
389 // parse style
390 if( bStyleParsed )
391 InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
392
393 xCntxt->SetVisible(aPropInfo.m_bVisible);
394 PushContext(xCntxt);
395 }
396
EndDivision()397 void SwHTMLParser::EndDivision()
398 {
399 // search for the stack entry of the token (because we still have the div stack
400 // we don't make a difference between DIV and CENTER)
401 std::unique_ptr<HTMLAttrContext> xCntxt;
402 auto nPos = m_aContexts.size();
403 while (!xCntxt && nPos>m_nContextStMin)
404 {
405 switch( m_aContexts[--nPos]->GetToken() )
406 {
407 case HtmlTokenId::CENTER_ON:
408 case HtmlTokenId::DIVISION_ON:
409 xCntxt = std::move(m_aContexts[nPos]);
410 m_aContexts.erase( m_aContexts.begin() + nPos );
411 break;
412 default: break;
413 }
414 }
415
416 if (xCntxt)
417 {
418 // close attribute
419 EndContext(xCntxt.get());
420 SetAttr(); // set paragraph attributes really fast because of JavaScript
421 if (xCntxt->IsHeaderOrFooter())
422 m_bReadingHeaderOrFooter = false;
423 }
424 }
425
FixHeaderFooterDistance(bool bHeader,const SwPosition * pOldPos)426 void SwHTMLParser::FixHeaderFooterDistance( bool bHeader,
427 const SwPosition *pOldPos )
428 {
429 SwPageDesc *pPageDesc = m_pCSS1Parser->GetMasterPageDesc();
430 SwFrameFormat& rPageFormat = pPageDesc->GetMaster();
431
432 SwFrameFormat *pHdFtFormat =
433 bHeader ? const_cast<SwFrameFormat*>(rPageFormat.GetHeader().GetHeaderFormat())
434 : const_cast<SwFrameFormat*>(rPageFormat.GetFooter().GetFooterFormat());
435 OSL_ENSURE( pHdFtFormat, "No header or footer" );
436
437 const SwFormatContent& rFlyContent = pHdFtFormat->GetContent();
438 const SwNodeIndex& rContentStIdx = *rFlyContent.GetContentIdx();
439
440 sal_uLong nPrvNxtIdx;
441 if( bHeader )
442 {
443 nPrvNxtIdx = rContentStIdx.GetNode().EndOfSectionIndex()-1;
444 }
445 else
446 {
447 nPrvNxtIdx = pOldPos->nNode.GetIndex() - 1;
448 }
449
450 sal_uInt16 nSpace = 0;
451 SwTextNode *pTextNode = m_xDoc->GetNodes()[nPrvNxtIdx]->GetTextNode();
452 if( pTextNode )
453 {
454 const SvxULSpaceItem& rULSpace =
455 static_cast<const SvxULSpaceItem&>(pTextNode
456 ->SwContentNode::GetAttr( RES_UL_SPACE ));
457
458 // The bottom paragraph padding becomes the padding
459 // to header or footer
460 nSpace = rULSpace.GetLower();
461
462 // and afterwards set to a valid value
463 const SvxULSpaceItem& rCollULSpace =
464 pTextNode->GetAnyFormatColl().GetULSpace();
465 if( rCollULSpace.GetUpper() == rULSpace.GetUpper() )
466 pTextNode->ResetAttr( RES_UL_SPACE );
467 else
468 pTextNode->SetAttr(
469 SvxULSpaceItem( rULSpace.GetUpper(),
470 rCollULSpace.GetLower(), RES_UL_SPACE ) );
471 }
472
473 if( bHeader )
474 {
475 nPrvNxtIdx = pOldPos->nNode.GetIndex();
476 }
477 else
478 {
479 nPrvNxtIdx = rContentStIdx.GetIndex() + 1;
480 }
481
482 pTextNode = m_xDoc->GetNodes()[nPrvNxtIdx]
483 ->GetTextNode();
484 if( pTextNode )
485 {
486 const SvxULSpaceItem& rULSpace =
487 static_cast<const SvxULSpaceItem&>(pTextNode
488 ->SwContentNode::GetAttr( RES_UL_SPACE ));
489
490 // The top paragraph padding becomes the padding
491 // to headline or footer if it is greater than the
492 // bottom padding of the paragraph beforehand
493 if( rULSpace.GetUpper() > nSpace )
494 nSpace = rULSpace.GetUpper();
495
496 // and afterwards set to a valid value
497 const SvxULSpaceItem& rCollULSpace =
498 pTextNode->GetAnyFormatColl().GetULSpace();
499 if( rCollULSpace.GetLower() == rULSpace.GetLower() )
500 pTextNode->ResetAttr( RES_UL_SPACE );
501 else
502 pTextNode->SetAttr(
503 SvxULSpaceItem( rCollULSpace.GetUpper(),
504 rULSpace.GetLower(), RES_UL_SPACE ) );
505 }
506
507 SvxULSpaceItem aULSpace( RES_UL_SPACE );
508 if( bHeader )
509 aULSpace.SetLower( nSpace );
510 else
511 aULSpace.SetUpper( nSpace );
512
513 pHdFtFormat->SetFormatAttr( aULSpace );
514 }
515
EndSection(bool bLFStripped)516 bool SwHTMLParser::EndSection( bool bLFStripped )
517 {
518 SwEndNode *pEndNd = m_xDoc->GetNodes()[m_pPam->GetPoint()->nNode.GetIndex()+1]
519 ->GetEndNode();
520 if( pEndNd && pEndNd->StartOfSectionNode()->IsSectionNode() )
521 {
522 // close the section
523 if( !bLFStripped )
524 StripTrailingPara();
525 m_pPam->Move( fnMoveForward );
526 return true;
527 }
528
529 OSL_ENSURE( false, "Wrong PaM position at end of section" );
530
531 return false;
532 }
533
EndSections(bool bLFStripped)534 bool SwHTMLParser::EndSections( bool bLFStripped )
535 {
536 bool bSectionClosed = false;
537 auto nPos = m_aContexts.size();
538 while( nPos>m_nContextStMin )
539 {
540 HTMLAttrContext *pCntxt = m_aContexts[--nPos].get();
541 if( pCntxt->GetSpansSection() && EndSection( bLFStripped ) )
542 {
543 bSectionClosed = true;
544 pCntxt->SetSpansSection( false );
545 bLFStripped = false;
546 }
547 }
548
549 return bSectionClosed;
550 }
551
NewMultiCol(sal_uInt16 columnsFromCss)552 void SwHTMLParser::NewMultiCol( sal_uInt16 columnsFromCss )
553 {
554 OUString aId;
555 OUString aStyle, aClass, aLang, aDir;
556 tools::Long nWidth = 100;
557 sal_uInt16 nCols = columnsFromCss, nGutter = 10;
558 bool bPercentWidth = true;
559
560 const HTMLOptions& rHTMLOptions = GetOptions();
561 for (size_t i = rHTMLOptions.size(); i; )
562 {
563 const HTMLOption& rOption = rHTMLOptions[--i];
564 switch( rOption.GetToken() )
565 {
566 case HtmlOptionId::ID:
567 aId = rOption.GetString();
568 break;
569 case HtmlOptionId::STYLE:
570 aStyle = rOption.GetString();
571 break;
572 case HtmlOptionId::CLASS:
573 aClass = rOption.GetString();
574 break;
575 case HtmlOptionId::LANG:
576 aLang = rOption.GetString();
577 break;
578 case HtmlOptionId::DIR:
579 aDir = rOption.GetString();
580 break;
581 case HtmlOptionId::COLS:
582 nCols = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
583 break;
584 case HtmlOptionId::WIDTH:
585 nWidth = rOption.GetNumber();
586 bPercentWidth = (rOption.GetString().indexOf('%') != -1);
587 if( bPercentWidth && nWidth>100 )
588 nWidth = 100;
589 break;
590 case HtmlOptionId::GUTTER:
591 nGutter = o3tl::narrowing<sal_uInt16>(rOption.GetNumber());
592 break;
593 default: break;
594 }
595 }
596
597 std::unique_ptr<HTMLAttrContext> xCntxt(new HTMLAttrContext(HtmlTokenId::MULTICOL_ON));
598
599 //.is the multicol element contained in a container? That may be the
600 // case for 5.0 documents.
601 bool bInCntnr = false;
602 auto i = m_aContexts.size();
603 while( !bInCntnr && i > m_nContextStMin )
604 bInCntnr = nullptr != m_aContexts[--i]->GetFrameItemSet();
605
606 // Parse style sheets, but don't position anything by now.
607 bool bStyleParsed = false;
608 SfxItemSet aItemSet( m_xDoc->GetAttrPool(), m_pCSS1Parser->GetWhichMap() );
609 SvxCSS1PropertyInfo aPropInfo;
610 if( HasStyleOptions( aStyle, aId, aClass, &aLang, &aDir ) )
611 bStyleParsed = ParseStyleOptions( aStyle, aId, aClass,
612 aItemSet, aPropInfo, &aLang, &aDir );
613
614 // Calculate width.
615 sal_uInt8 nPercentWidth = bPercentWidth ? static_cast<sal_uInt8>(nWidth) : 0;
616 SwTwips nTwipWidth = 0;
617 if( !bPercentWidth && nWidth && Application::GetDefaultDevice() )
618 {
619 nTwipWidth = Application::GetDefaultDevice()
620 ->PixelToLogic( Size(nWidth, 0),
621 MapMode(MapUnit::MapTwip) ).Width();
622 }
623
624 if( !nPercentWidth && nTwipWidth < MINFLY )
625 nTwipWidth = MINFLY;
626
627 // Do positioning.
628 bool bPositioned = false;
629 if( bInCntnr || SwCSS1Parser::MayBePositioned( aPropInfo, true ) )
630 {
631 SfxItemSet aFrameItemSet( m_xDoc->GetAttrPool(),
632 svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} );
633 if( !IsNewDoc() )
634 Reader::ResetFrameFormatAttrs(aFrameItemSet );
635
636 SetAnchorAndAdjustment( text::VertOrientation::NONE, text::HoriOrientation::NONE, aPropInfo,
637 aFrameItemSet );
638
639 // The width is either the WIDTH attribute's value or contained
640 // in some style option.
641 SetVarSize( aPropInfo, aFrameItemSet, nTwipWidth, nPercentWidth );
642
643 SetSpace( Size(0,0), aItemSet, aPropInfo, aFrameItemSet );
644
645 // Set some other frame attributes. If the background is set, its
646 // it will be cleared here. That for, it won't be set at the section,
647 // too.
648 SetFrameFormatAttrs( aItemSet,
649 HtmlFrameFormatFlags::Box|HtmlFrameFormatFlags::Background|HtmlFrameFormatFlags::Padding|HtmlFrameFormatFlags::Direction,
650 aFrameItemSet );
651
652 // Insert fly frame. If the are columns, the fly frame's name is not
653 // the sections name but a generated one.
654 OUString aFlyName;
655 if( nCols < 2 )
656 {
657 aFlyName = aId;
658 aPropInfo.m_aId.clear();
659 }
660
661 InsertFlyFrame(aFrameItemSet, xCntxt.get(), aFlyName);
662
663 xCntxt->SetPopStack( true );
664 bPositioned = true;
665 }
666
667 bool bAppended = false;
668 if( !bPositioned )
669 {
670 if( m_pPam->GetPoint()->nContent.GetIndex() )
671 {
672 AppendTextNode( AM_SPACE );
673 bAppended = true;
674 }
675 else
676 {
677 AddParSpace();
678 }
679 }
680
681 // If there are less than 2 columns, no section is inserted.
682 if( nCols >= 2 )
683 {
684 if( !bAppended )
685 {
686 // If the pam is at the start of a section, an additional text
687 // node must be inserted. Otherwise, the new section will be
688 // inserted in front of the old one.
689 SwNodeIndex aPrvNdIdx( m_pPam->GetPoint()->nNode, -1 );
690 if (aPrvNdIdx.GetNode().IsSectionNode())
691 {
692 AppendTextNode();
693 bAppended = true;
694 }
695 }
696 std::unique_ptr<std::deque<std::unique_ptr<HTMLAttr>>> pPostIts(bAppended ? nullptr : new std::deque<std::unique_ptr<HTMLAttr>>);
697 SetAttr( true, true, pPostIts.get() );
698
699 // Make section name unique.
700 OUString aName( m_xDoc->GetUniqueSectionName( !aId.isEmpty() ? &aId : nullptr ) );
701 SwSectionData aSection( SectionType::Content, aName );
702
703 SfxItemSet aFrameItemSet( m_xDoc->GetAttrPool(),
704 svl::Items<RES_FRMATR_BEGIN, RES_FRMATR_END-1>{} );
705 if( !IsNewDoc() )
706 Reader::ResetFrameFormatAttrs(aFrameItemSet );
707
708 if( nGutter && Application::GetDefaultDevice() )
709 {
710 nGutter = o3tl::narrowing<sal_uInt16>(Application::GetDefaultDevice()
711 ->PixelToLogic( Size(nGutter, 0),
712 MapMode(MapUnit::MapTwip) ).Width());
713 }
714
715 SwFormatCol aFormatCol;
716
717 aFormatCol.Init( nCols, nGutter, USHRT_MAX );
718 aFrameItemSet.Put( aFormatCol );
719
720 const SfxPoolItem *pItem;
721 if( SfxItemState::SET == aItemSet.GetItemState( RES_BACKGROUND, false,
722 &pItem ) )
723 {
724 aFrameItemSet.Put( *pItem );
725 aItemSet.ClearItem( RES_BACKGROUND );
726 }
727 if( SfxItemState::SET == aItemSet.GetItemState( RES_FRAMEDIR, false,
728 &pItem ) )
729 {
730 aFrameItemSet.Put( *pItem );
731 aItemSet.ClearItem( RES_FRAMEDIR );
732 }
733 m_xDoc->InsertSwSection( *m_pPam, aSection, nullptr, &aFrameItemSet, false );
734
735 // Jump to section, if this is requested.
736 if( JumpToMarks::Region == m_eJumpTo && aName == m_sJmpMark )
737 {
738 m_bChkJumpMark = true;
739 m_eJumpTo = JumpToMarks::NONE;
740 }
741
742 SwTextNode* pOldTextNd =
743 bAppended ? nullptr : m_pPam->GetPoint()->nNode.GetNode().GetTextNode();
744
745 m_pPam->Move( fnMoveBackward );
746
747 // Move PageDesc and SwFormatBreak attributes of the current node
748 // to the section's first node.
749 if( pOldTextNd )
750 MovePageDescAttrs( pOldTextNd, m_pPam->GetPoint()->nNode.GetIndex(),
751 true );
752
753 if( pPostIts )
754 {
755 // Move pending PostIts into the section.
756 InsertAttrs( std::move(*pPostIts) );
757 pPostIts.reset();
758 }
759
760 xCntxt->SetSpansSection( true );
761
762 // Insert a bookmark if its name differs from the section's name only.
763 if( !aPropInfo.m_aId.isEmpty() && aPropInfo.m_aId==aName )
764 aPropInfo.m_aId.clear();
765 }
766
767 // Additional attributes must be set as hard ones.
768 if( bStyleParsed )
769 InsertAttrs( aItemSet, aPropInfo, xCntxt.get(), true );
770
771 PushContext(xCntxt);
772 }
773
InsertFlyFrame(const SfxItemSet & rItemSet,HTMLAttrContext * pCntxt,const OUString & rName)774 void SwHTMLParser::InsertFlyFrame( const SfxItemSet& rItemSet,
775 HTMLAttrContext *pCntxt,
776 const OUString& rName )
777 {
778 RndStdIds eAnchorId =
779 rItemSet.Get( RES_ANCHOR ).GetAnchorId();
780
781 // create frame
782 SwFlyFrameFormat* pFlyFormat = m_xDoc->MakeFlySection( eAnchorId, m_pPam->GetPoint(),
783 &rItemSet );
784 if( !rName.isEmpty() )
785 pFlyFormat->SetName( rName );
786
787 RegisterFlyFrame( pFlyFormat );
788
789 const SwFormatContent& rFlyContent = pFlyFormat->GetContent();
790 const SwNodeIndex& rFlyCntIdx = *rFlyContent.GetContentIdx();
791 SwContentNode *pCNd = m_xDoc->GetNodes()[rFlyCntIdx.GetIndex()+1]
792 ->GetContentNode();
793
794 SwPosition aNewPos( SwNodeIndex( rFlyCntIdx, 1 ), SwIndex( pCNd, 0 ) );
795 const HtmlContextFlags nFlags = HtmlContextFlags::ProtectStack|HtmlContextFlags::StripPara;
796 SaveDocContext( pCntxt, nFlags, &aNewPos );
797 }
798
MovePageDescAttrs(SwNode * pSrcNd,sal_uLong nDestIdx,bool bFormatBreak)799 void SwHTMLParser::MovePageDescAttrs( SwNode *pSrcNd,
800 sal_uLong nDestIdx,
801 bool bFormatBreak )
802 {
803 SwContentNode* pDestContentNd =
804 m_xDoc->GetNodes()[nDestIdx]->GetContentNode();
805
806 OSL_ENSURE( pDestContentNd, "Why is the target not a Content-Node?" );
807
808 if( pSrcNd->IsContentNode() )
809 {
810 SwContentNode* pSrcContentNd = pSrcNd->GetContentNode();
811
812 const SfxPoolItem* pItem;
813 if( SfxItemState::SET == pSrcContentNd->GetSwAttrSet()
814 .GetItemState( RES_PAGEDESC, false, &pItem ) &&
815 static_cast<const SwFormatPageDesc *>(pItem)->GetPageDesc() )
816 {
817 pDestContentNd->SetAttr( *pItem );
818 pSrcContentNd->ResetAttr( RES_PAGEDESC );
819 }
820 if( SfxItemState::SET == pSrcContentNd->GetSwAttrSet()
821 .GetItemState( RES_BREAK, false, &pItem ) )
822 {
823 switch( static_cast<const SvxFormatBreakItem *>(pItem)->GetBreak() )
824 {
825 case SvxBreak::PageBefore:
826 case SvxBreak::PageAfter:
827 case SvxBreak::PageBoth:
828 if( bFormatBreak )
829 pDestContentNd->SetAttr( *pItem );
830 pSrcContentNd->ResetAttr( RES_BREAK );
831 break;
832 default:
833 break;
834 }
835 }
836 }
837 else if( pSrcNd->IsTableNode() )
838 {
839 SwFrameFormat *pFrameFormat = pSrcNd->GetTableNode()->GetTable().GetFrameFormat();
840
841 const SfxPoolItem* pItem;
842 if( SfxItemState::SET == pFrameFormat->GetAttrSet().
843 GetItemState( RES_PAGEDESC, false, &pItem ) )
844 {
845 if (pDestContentNd)
846 pDestContentNd->SetAttr(*pItem);
847 pFrameFormat->ResetFormatAttr( RES_PAGEDESC );
848 }
849 }
850 }
851
852 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
853