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