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 <memory>
21 #include <svl/itemiter.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/outdev.hxx>
24 #include <sal/log.hxx>
25 
26 #include <vcl/unohelp.hxx>
27 #include <com/sun/star/form/XForm.hpp>
28 #include <com/sun/star/drawing/XShape.hpp>
29 #include <com/sun/star/drawing/XShapes.hpp>
30 #include <com/sun/star/drawing/XControlShape.hpp>
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <com/sun/star/container/XIndexContainer.hpp>
33 #include <com/sun/star/text/VertOrientation.hpp>
34 #include <com/sun/star/text/TextContentAnchorType.hpp>
35 #include <com/sun/star/beans/XPropertyContainer.hpp>
36 #include <com/sun/star/beans/PropertyAttribute.hpp>
37 
38 #include <algorithm>
39 #include <hintids.hxx>
40 #include <editeng/fontitem.hxx>
41 #include <editeng/lrspitem.hxx>
42 #include <editeng/fhgtitem.hxx>
43 #include <editeng/colritem.hxx>
44 #include <editeng/wghtitem.hxx>
45 #include <editeng/crossedoutitem.hxx>
46 #include <editeng/udlnitem.hxx>
47 #include <editeng/postitem.hxx>
48 #include <o3tl/safeint.hxx>
49 #include <unotextrange.hxx>
50 #include <doc.hxx>
51 #include <docary.hxx>
52 #include <IDocumentFieldsAccess.hxx>
53 #include <IDocumentMarkAccess.hxx>
54 #include <docsh.hxx>
55 #include <numrule.hxx>
56 #include <paratr.hxx>
57 #include <charatr.hxx>
58 #include <charfmt.hxx>
59 #include <ndtxt.hxx>
60 #include <expfld.hxx>
61 #include <fmtfld.hxx>
62 #include <flddropdown.hxx>
63 #include "sprmids.hxx"
64 #include "writerhelper.hxx"
65 #include "writerwordglue.hxx"
66 #include "ww8par.hxx"
67 #include "ww8par2.hxx"
68 
69 #include <IMark.hxx>
70 #include <unotools/fltrcfg.hxx>
71 #include <rtl/character.hxx>
72 #include <xmloff/odffields.hxx>
73 
74 using namespace com::sun::star;
75 using namespace sw::util;
76 using namespace sw::types;
77 using namespace sw::mark;
78 
79 //            UNO-Controls
80 
81 // OCX i.e. word 97 form controls
Read_F_OCX(WW8FieldDesc *,OUString &)82 eF_ResT SwWW8ImplReader::Read_F_OCX( WW8FieldDesc*, OUString& )
83 {
84     if( m_bObj && m_nPicLocFc )
85         m_nObjLocFc = m_nPicLocFc;
86     m_bEmbeddObj = true;
87     return eF_ResT::TEXT;
88 }
89 
Read_F_FormTextBox(WW8FieldDesc * pF,OUString & rStr)90 eF_ResT SwWW8ImplReader::Read_F_FormTextBox( WW8FieldDesc* pF, OUString& rStr )
91 {
92     WW8FormulaEditBox aFormula(*this);
93 
94     sal_Int32 const nPos(rStr.indexOf(0x01));
95     if (pF->nLCode && nPos != -1 && nPos < pF->nLCode) {
96         ImportFormulaControl(aFormula, pF->nSCode + nPos, WW8_CT_EDIT);
97     }
98 
99     /*
100     Here we have a small complication. This formula control contains
101     the default text that is displayed if you edit the form field in
102     the "default text" area. But MSOffice does not display that
103     information, instead it display the result of the field,
104     MSOffice just uses the default text of the control as its
105     initial value for the displayed default text. So we will swap in
106     the field result into the formula here in place of the default
107     text.
108     */
109 
110     const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
111     const bool bUseEnhFields = rOpt.IsUseEnhancedFields();
112 
113     if (!bUseEnhFields)
114     {
115         aFormula.msDefault = GetFieldResult(pF);
116 
117         SwInputField aField(
118             static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )),
119             aFormula.msDefault,
120             aFormula.msTitle,
121             INP_TXT,
122             0 );
123         aField.SetHelp(aFormula.msHelp);
124         aField.SetToolTip(aFormula.msToolTip);
125 
126         m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
127         return eF_ResT::OK;
128     }
129     else
130     {
131         WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
132         OUString aBookmarkName;
133         if (pB!=nullptr) {
134             WW8_CP currentCP=pF->nSCode;
135             WW8_CP currentLen=pF->nLen;
136 
137             WW8_CP nEnd;
138             if (o3tl::checked_add(currentCP, currentLen-1, nEnd)) {
139                 SAL_WARN("sw.ww8", "broken offset, ignoring");
140             }
141             else
142             {
143                 sal_uInt16 bkmFindIdx;
144                 OUString aBookmarkFind=pB->GetBookmark(currentCP-1, nEnd, bkmFindIdx);
145 
146                 if (!aBookmarkFind.isEmpty()) {
147                     pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark bookmark as consumed, such that tl'll not get inserted as a "normal" bookmark again
148                     if (!aBookmarkFind.isEmpty()) {
149                         aBookmarkName=aBookmarkFind;
150                     }
151                 }
152             }
153         }
154 
155         if (pB!=nullptr && aBookmarkName.isEmpty()) {
156             aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle);
157         }
158 
159         if (!aBookmarkName.isEmpty()) {
160             m_aFieldStack.back().SetBookmarkName(aBookmarkName);
161             m_aFieldStack.back().SetBookmarkType(ODF_FORMTEXT);
162             if ( aFormula.msToolTip.getLength() < 139 )
163                 m_aFieldStack.back().getParameters()["Description"] <<= aFormula.msToolTip;
164             m_aFieldStack.back().getParameters()["Name"] <<= aFormula.msTitle;
165             if (aFormula.mnMaxLen && aFormula.mnMaxLen < 32768 )
166                 m_aFieldStack.back().getParameters()["MaxLength"] <<= aFormula.mnMaxLen;
167 
168             if ( aFormula.mfType == 1 )
169                 m_aFieldStack.back().getParameters()["Type"] <<= OUString("number");
170             else if ( aFormula.mfType == 2 )
171                 m_aFieldStack.back().getParameters()["Type"] <<= OUString("date");
172             else if ( aFormula.mfType == 3 )
173                 m_aFieldStack.back().getParameters()["Type"] <<= OUString("currentTime");
174             else if ( aFormula.mfType == 4 )
175                 m_aFieldStack.back().getParameters()["Type"] <<= OUString("currentDate");
176             else if ( aFormula.mfType == 5 )
177                 m_aFieldStack.back().getParameters()["Type"] <<= OUString("calculated");
178         }
179         return eF_ResT::TEXT;
180     }
181 }
182 
Read_F_FormCheckBox(WW8FieldDesc * pF,OUString & rStr)183 eF_ResT SwWW8ImplReader::Read_F_FormCheckBox( WW8FieldDesc* pF, OUString& rStr )
184 {
185     WW8FormulaCheckBox aFormula(*this);
186 
187     if (!m_xFormImpl)
188         m_xFormImpl.reset(new SwMSConvertControls(m_pDocShell, m_pPaM));
189 
190     if (rStr[pF->nLCode-1]==0x01)
191         ImportFormulaControl(aFormula,pF->nSCode+pF->nLCode-1, WW8_CT_CHECKBOX);
192     const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
193     const bool bUseEnhFields = rOpt.IsUseEnhancedFields();
194 
195     if (!bUseEnhFields)
196     {
197         m_xFormImpl->InsertFormula(aFormula);
198         return eF_ResT::OK;
199     }
200 
201     OUString aBookmarkName;
202     WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
203     if (pB!=nullptr) {
204         WW8_CP currentCP=pF->nSCode;
205         WW8_CP currentLen=pF->nLen;
206 
207         sal_uInt16 bkmFindIdx;
208         OUString aBookmarkFind=pB->GetBookmark(currentCP-1, currentCP+currentLen-1, bkmFindIdx);
209 
210         if (!aBookmarkFind.isEmpty()) {
211             pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark as consumed by field
212             if (!aBookmarkFind.isEmpty()) {
213                 aBookmarkName=aBookmarkFind;
214             }
215         }
216     }
217 
218     if (pB!=nullptr && aBookmarkName.isEmpty()) {
219         aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle);
220     }
221 
222     if (!aBookmarkName.isEmpty())
223     {
224         IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
225         IFieldmark* pFieldmark = pMarksAccess->makeNoTextFieldBookmark(
226                 *m_pPaM, aBookmarkName, ODF_FORMCHECKBOX );
227         OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?");
228         if (pFieldmark!=nullptr) {
229             IFieldmark::parameter_map_t* const pParameters = pFieldmark->GetParameters();
230             ICheckboxFieldmark* pCheckboxFm = dynamic_cast<ICheckboxFieldmark*>(pFieldmark);
231             (*pParameters)[ODF_FORMCHECKBOX_NAME] <<= aFormula.msTitle;
232             (*pParameters)[ODF_FORMCHECKBOX_HELPTEXT] <<= aFormula.msToolTip;
233 
234             if(pCheckboxFm)
235                 pCheckboxFm->SetChecked(aFormula.mnChecked != 0);
236             // set field data here...
237         }
238     }
239     return eF_ResT::OK;
240 }
241 
Read_F_FormListBox(WW8FieldDesc * pF,OUString & rStr)242 eF_ResT SwWW8ImplReader::Read_F_FormListBox( WW8FieldDesc* pF, OUString& rStr)
243 {
244     WW8FormulaListBox aFormula(*this);
245 
246     if (pF->nLCode > 0 && rStr.getLength() >= pF->nLCode && rStr[pF->nLCode-1] == 0x01)
247         ImportFormulaControl(aFormula,pF->nSCode+pF->nLCode-1, WW8_CT_DROPDOWN);
248 
249     const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
250     bool bUseEnhFields = rOpt.IsUseEnhancedFields();
251 
252     if (!bUseEnhFields)
253     {
254         SwDropDownField aField(static_cast<SwDropDownFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Dropdown)));
255 
256         aField.SetName(aFormula.msTitle);
257         aField.SetHelp(aFormula.msHelp);
258         aField.SetToolTip(aFormula.msToolTip);
259 
260         if (!aFormula.maListEntries.empty())
261         {
262             aField.SetItems(aFormula.maListEntries);
263             int nIndex = aFormula.mfDropdownIndex  < aFormula.maListEntries.size() ? aFormula.mfDropdownIndex : 0;
264             aField.SetSelectedItem(aFormula.maListEntries[nIndex]);
265         }
266 
267         m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
268         return eF_ResT::OK;
269     }
270     else
271     {
272         // TODO: review me
273         OUString aBookmarkName;
274         WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
275         if (pB!=nullptr)
276         {
277             WW8_CP currentCP=pF->nSCode;
278             WW8_CP currentLen=pF->nLen;
279 
280             sal_uInt16 bkmFindIdx;
281             OUString aBookmarkFind=pB->GetBookmark(currentCP-1, currentCP+currentLen-1, bkmFindIdx);
282 
283             if (!aBookmarkFind.isEmpty())
284             {
285                 pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark as consumed by field
286                 if (!aBookmarkFind.isEmpty())
287                     aBookmarkName=aBookmarkFind;
288             }
289         }
290 
291         if (pB!=nullptr && aBookmarkName.isEmpty())
292             aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle);
293 
294         if (!aBookmarkName.isEmpty())
295         {
296             IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
297             IFieldmark *pFieldmark =
298                     pMarksAccess->makeNoTextFieldBookmark( *m_pPaM, aBookmarkName, ODF_FORMDROPDOWN );
299             OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?");
300             if ( pFieldmark != nullptr )
301             {
302                 uno::Sequence< OUString > vListEntries(aFormula.maListEntries.size());
303                 std::copy(aFormula.maListEntries.begin(), aFormula.maListEntries.end(), vListEntries.begin());
304                 (*pFieldmark->GetParameters())[ODF_FORMDROPDOWN_LISTENTRY] <<= vListEntries;
305                 sal_Int32 nIndex = aFormula.mfDropdownIndex  < aFormula.maListEntries.size() ? aFormula.mfDropdownIndex : 0;
306                 (*pFieldmark->GetParameters())[ODF_FORMDROPDOWN_RESULT] <<= nIndex;
307                 // set field data here...
308             }
309         }
310 
311         return eF_ResT::OK;
312     }
313 }
314 
Read_F_HTMLControl(WW8FieldDesc *,OUString &)315 eF_ResT SwWW8ImplReader::Read_F_HTMLControl(WW8FieldDesc*, OUString&)
316 {
317     if( m_bObj && m_nPicLocFc )
318         m_nObjLocFc = m_nPicLocFc;
319     m_bEmbeddObj = true;
320     return eF_ResT::TEXT;
321 }
322 
323 // Helper declarations
324 
325 // Style Id's for each level
326 typedef sal_uInt16 WW8aIdSty[WW8ListManager::nMaxLevel];
327 // Character Style Pointer
328 typedef SwCharFormat* WW8aCFormat[WW8ListManager::nMaxLevel];
329 
330 struct WW8LST   // only THOSE entries, WE need!
331 {
332     WW8aIdSty aIdSty;     // Style Id's for each level,
333                             //   nIStDNil if no style linked
334     sal_uInt32 nIdLst;     // Unique List ID
335     sal_uInt32 nTplC;      // Unique template code - What is this?
336     bool bSimpleList:1;    // Flag: List only has ONE level
337     bool bRestartHdn:1;    // WW6-Compatibility-Flag:
338                                                         //   true if the list should start numbering over
339 };                                                      //   at the beginning of each section
340 
341 const sal_uInt32 cbLSTF=28;
342 
343 struct WW8LFO   // only THOSE entries, WE need!
344 {
345     SwNumRule*      pNumRule;   // Parent NumRule
346     sal_uInt32      nIdLst;     // Unique List ID
347     sal_uInt8       nLfoLvl;    // count of levels whose format is overridden
348     bool bSimpleList;
349 };
350 
351 struct WW8LVL   // only THE entries, WE need!
352 {
353     sal_Int32 nStartAt;       // start at value for this value
354     sal_Int32 nV6DxaSpace;// Ver6-Compatible: min Space between Num and text::Paragraph
355     sal_Int32 nV6Indent;  // Ver6-Compatible: Width of prefix text;
356                         // Use definition of first line indent if appropriate!
357     // Paragraph attributes from GrpprlPapx
358     sal_uInt16  nDxaLeft;               // left indent
359     short   nDxaLeft1;          // first line indent
360 
361     sal_uInt8   nNFC;               // number format code
362     // Offset of fieldcodes in Num-X-String
363     sal_uInt8   aOfsNumsXCH[WW8ListManager::nMaxLevel];
364     sal_uInt8   nLenGrpprlChpx; // length, in bytes, of the LVL's grpprlChpx
365     sal_uInt8   nLenGrpprlPapx; // length, in bytes, of the LVL's grpprlPapx
366     sal_uInt8   nAlign: 2;  // alignment (left, right, centered) of the number
367     sal_uInt8 bLegal:    1;  // doesn't matter
368     sal_uInt8 bNoRest:1; // doesn't matter
369     sal_uInt8 bV6Prev:1; // Ver6-Compatible: number will include previous levels
370     sal_uInt8 bV6PrSp:1; // Ver6-Compatible: doesn't matter
371     sal_uInt8 bV6:       1;  // if true, pay attention to the V6-Compatible Entries!
372     sal_uInt8   bDummy: 1;  // (fills the byte)
373 
374 };
375 
376 struct WW8LFOLVL
377 {
378     sal_Int32 nStartAt;          // start-at value if bFormat==false and bStartAt == true
379                                             // (if bFormat==true, the start-at is stored in the LVL)
380     sal_uInt8 nLevel;               // the level to be overridden
381     // this byte has not been packed into the following byte on _purpose_ !!
382     // (see comment of struct WW8LFOInfo)
383 
384     bool bStartAt :1;       // true if the start-at value is overridden
385     bool bFormat :1;        // true if the formatting is overridden
386 
WW8LFOLVLWW8LFOLVL387     WW8LFOLVL() :
388         nStartAt(1), nLevel(0), bStartAt(true), bFormat(false) {}
389 };
390 
391 // Data to be saved in ListInfo
392 
393 struct WW8LSTInfo   // sorted by nIdLst (in WW8 used list-Id)
394 {
395     std::vector<ww::bytes> maParaSprms;
396     WW8aIdSty   aIdSty;          // Style Id's for each level
397     WW8aCFormat    aCharFormat = {};   // Character Style Pointer
398 
399     SwNumRule*  pNumRule;        // Pointer to list-template in Writer
400     sal_uInt32      nIdLst;          // WW8Id of this list
401     bool bSimpleList:1;// Flag, if this NumRule only uses one Level
402     bool bUsedInDoc :1;// Flag, if this NumRule is used in the Doc,
403                                                      //   or is supposed to be deleted on Reader-End
404 
WW8LSTInfoWW8LSTInfo405     WW8LSTInfo(SwNumRule* pNumRule_, const WW8LST& aLST)
406         : pNumRule(pNumRule_), nIdLst(aLST.nIdLst),
407         bSimpleList(aLST.bSimpleList), bUsedInDoc(false)
408     {
409         memcpy( aIdSty, aLST.aIdSty, sizeof( aIdSty   ));
410     }
411 
412 };
413 
414 // Data to be saved in ListenFormatOverrideInfos
415 
416 struct WW8LFOInfo   // unordered, means ordered like in WW8 Stream
417 {
418     std::vector<ww::bytes> maParaSprms;
419     std::vector<WW8LFOLVL> maOverrides;
420     SwNumRule* pNumRule;         // Pointer to list template in Writer
421                                                      // either List in LSTInfos or own List
422                                                      // (in Ctor use the list from LSTInfos first)
423 
424     sal_uInt32  nIdLst;          // WW8-Id of the relevant list
425     sal_uInt8   nLfoLvl;             // count of levels whose format is overridden
426     // yes we could include nLfoLvl (via :4) into the following byte,
427     // but it probably would be a source of error once MS increases their Listformat
428     // to more than 15 levels
429 
430     bool bOverride  :1;// Flag if NumRule is not included in maLSTInfos,
431                         //   but was created for m_LFOInfos
432     bool bUsedInDoc :1;// Flag if NumRule is used in Doc,
433                                                      //  or should be deleted on Reader-End
434     bool bLSTbUIDSet    :1;// Flag, if bUsedInDoc is set in maLSTInfos
435 
436     explicit WW8LFOInfo(const WW8LFO& rLFO);
437 };
438 
WW8LFOInfo(const WW8LFO & rLFO)439 WW8LFOInfo::WW8LFOInfo(const WW8LFO& rLFO)
440     : maParaSprms(WW8ListManager::nMaxLevel)
441     , maOverrides(WW8ListManager::nMaxLevel)
442     , pNumRule(rLFO.pNumRule)
443     , nIdLst(rLFO.nIdLst)
444     , nLfoLvl(rLFO.nLfoLvl)
445     , bOverride(rLFO.nLfoLvl != 0)
446     , bUsedInDoc(false)
447     , bLSTbUIDSet(false)
448 {
449 }
450 
451 // Helper methods
452 
453 // find Sprm-Parameter-Data, if Sprm is included in Grpprl
GrpprlHasSprm(sal_uInt16 nId,sal_uInt8 & rSprms,sal_uInt8 nLen)454 SprmResult WW8ListManager::GrpprlHasSprm(sal_uInt16 nId, sal_uInt8& rSprms,
455     sal_uInt8 nLen)
456 {
457     return maSprmParser.findSprmData(nId, &rSprms, nLen);
458 }
459 
460 class ListWithId
461 {
462 private:
463     sal_uInt32 mnIdLst;
464 public:
ListWithId(sal_uInt32 nIdLst)465     explicit ListWithId(sal_uInt32 nIdLst) : mnIdLst(nIdLst) {}
operator ()(const std::unique_ptr<WW8LSTInfo> & pEntry) const466     bool operator() (const std::unique_ptr<WW8LSTInfo>& pEntry) const
467         { return (pEntry->nIdLst == mnIdLst); }
468 };
469 
470 // Access via List-Id of LST Entry
GetLSTByListId(sal_uInt32 nIdLst) const471 WW8LSTInfo* WW8ListManager::GetLSTByListId( sal_uInt32 nIdLst ) const
472 {
473     auto aResult =
474         std::find_if(maLSTInfos.begin(),maLSTInfos.end(),ListWithId(nIdLst));
475     if (aResult == maLSTInfos.end())
476         return nullptr;
477     return aResult->get();
478 }
479 
lcl_CopyGreaterEight(OUString & rDest,OUString const & rSrc,sal_Int32 nStart,sal_Int32 nLen=SAL_MAX_INT32)480 static void lcl_CopyGreaterEight(OUString &rDest, OUString const &rSrc,
481     sal_Int32 nStart, sal_Int32 nLen = SAL_MAX_INT32)
482 {
483     const sal_Int32 nMaxLen = std::min(rSrc.getLength(), nLen);
484     for( sal_Int32 nI = nStart; nI < nMaxLen; ++nI)
485     {
486         sal_Unicode nChar = rSrc[nI];
487         if (nChar > WW8ListManager::nMaxLevel)
488             rDest += OUStringChar(nChar);
489     }
490 }
491 
sanitizeString(const OUString & rString)492 static OUString sanitizeString(const OUString& rString)
493 {
494     sal_Int32 i=0;
495     while (i < rString.getLength())
496     {
497         sal_Unicode c = rString[i];
498         if (rtl::isHighSurrogate(c))
499         {
500             if (i+1 == rString.getLength()
501                 || !rtl::isLowSurrogate(rString[i+1]))
502             {
503                 SAL_WARN("sw.ww8", "Surrogate error: high without low");
504                 return rString.copy(0, i);
505             }
506             ++i;    //skip correct low
507         }
508         if (rtl::isLowSurrogate(c)) //bare low without preceding high
509         {
510             SAL_WARN("sw.ww8", "Surrogate error: low without high");
511             return rString.copy(0, i);
512         }
513         ++i;
514     }
515     return rString;
516 }
517 
ReadLVL(SwNumFormat & rNumFormat,std::unique_ptr<SfxItemSet> & rpItemSet,sal_uInt16 nLevelStyle,bool bSetStartNo,std::deque<bool> & rNotReallyThere,sal_uInt16 nLevel,ww::bytes & rParaSprms)518 bool WW8ListManager::ReadLVL(SwNumFormat& rNumFormat, std::unique_ptr<SfxItemSet>& rpItemSet,
519     sal_uInt16 nLevelStyle, bool bSetStartNo,
520     std::deque<bool> &rNotReallyThere, sal_uInt16 nLevel,
521     ww::bytes &rParaSprms)
522 {
523     sal_uInt8       aBits1(0);
524     sal_uInt16      nStartNo(0);        // Start-No. for Writer
525     SvxNumType      nType(SVX_NUM_ARABIC);
526     SvxAdjust       eAdj;               // Alignment (Left/right/centered)
527     sal_Unicode     cBullet(0x2190);    // default safe bullet
528 
529     sal_Unicode     cGrfBulletCP(USHRT_MAX);
530 
531     OUString        sPrefix;
532     OUString        sPostfix;
533     WW8LVL          aLVL = {};
534 
535     // 1. read LVLF
536 
537     rSt.ReadInt32( aLVL.nStartAt );
538     rSt.ReadUChar( aLVL.nNFC );
539     rSt.ReadUChar( aBits1 );
540     if( ERRCODE_NONE != rSt.GetError() ) return false;
541     aLVL.nAlign = (aBits1 & 0x03);
542     if( aBits1 & 0x10 ) aLVL.bV6Prev    = sal_uInt8(true);
543     if( aBits1 & 0x20 ) aLVL.bV6PrSp    = sal_uInt8(true);
544     if( aBits1 & 0x40 ) aLVL.bV6        = sal_uInt8(true);
545     bool bLVLOkB = true;
546     for(sal_uInt8 nLevelB = 0; nLevelB < nMaxLevel; ++nLevelB)
547     {
548         rSt.ReadUChar( aLVL.aOfsNumsXCH[ nLevelB ] );
549         if( ERRCODE_NONE != rSt.GetError() )
550         {
551             bLVLOkB = false;
552             break;
553         }
554     }
555 
556     if( !bLVLOkB )
557         return false;
558 
559     sal_uInt8 ixchFollow(0);
560     rSt.ReadUChar( ixchFollow );
561     rSt.ReadInt32( aLVL.nV6DxaSpace );
562     rSt.ReadInt32( aLVL.nV6Indent );
563     rSt.ReadUChar( aLVL.nLenGrpprlChpx );
564     rSt.ReadUChar( aLVL.nLenGrpprlPapx );
565     rSt.SeekRel( 2 );
566     if( ERRCODE_NONE != rSt.GetError()) return false;
567 
568     // 2. read PAPx if needed and search for indent values
569 
570     short nTabPos = 0; // #i86652# - read tab setting
571     if( aLVL.nLenGrpprlPapx )
572     {
573         sal_uInt8 aGrpprlPapx[ 255 ];
574         if (aLVL.nLenGrpprlPapx != rSt.ReadBytes(&aGrpprlPapx, aLVL.nLenGrpprlPapx))
575             return false;
576         // "sprmPDxaLeft"  pap.dxaLeft;dxa;word;
577         SprmResult aSprm = GrpprlHasSprm(0x840F,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
578         if (!aSprm.pSprm)
579             aSprm = GrpprlHasSprm(0x845E,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
580 
581         if (aSprm.pSprm && aSprm.nRemainingData >= 2)
582         {
583             const sal_uInt8 *pBegin = aSprm.pSprm - 2;
584             for(int i=0;i<4;++i)
585                 rParaSprms.push_back(*pBegin++);
586             short nDxaLeft = SVBT16ToUInt16(aSprm.pSprm);
587             aLVL.nDxaLeft = (0 < nDxaLeft) ? static_cast<sal_uInt16>(nDxaLeft)
588                             : static_cast<sal_uInt16>(-nDxaLeft);
589         }
590 
591         // "sprmPDxaLeft1" pap.dxaLeft1;dxa;word;
592         aSprm = GrpprlHasSprm(0x8411,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
593         if (!aSprm.pSprm)
594             aSprm = GrpprlHasSprm(0x8460,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
595 
596         if (aSprm.pSprm && aSprm.nRemainingData >= 2)
597         {
598             const sal_uInt8 *pBegin = aSprm.pSprm - 2;
599             for(int i=0;i<4;++i)
600                 rParaSprms.push_back(*pBegin++);
601             aLVL.nDxaLeft1 = SVBT16ToUInt16(aSprm.pSprm);
602         }
603 
604         // #i86652# - read tab setting
605         aSprm = GrpprlHasSprm(0xC615,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
606         const sal_uInt8* pSprm = aSprm.pSprm;
607         if (pSprm && aSprm.nRemainingData >= 5)
608         {
609             bool bDone = false;
610             if (*(pSprm-1) == 5)
611             {
612                 if (*pSprm++ == 0) //nDel
613                 {
614                     if (*pSprm++ == 1) //nIns
615                     {
616                         nTabPos = SVBT16ToUInt16(pSprm);
617                         pSprm+=2;
618                         if (*pSprm == 6) //type
619                         {
620                             bDone = true;
621                         }
622                     }
623                 }
624             }
625             OSL_ENSURE(bDone, "tab setting in numbering is "
626                 "of unexpected configuration");
627         }
628         if ( rNumFormat.GetPositionAndSpaceMode() ==
629                                   SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
630         {
631             // If there is a tab setting with a larger value, then use that.
632             // Ideally we would allow tabs to be used in numbering fields and set
633             // this on the containing paragraph which would make it actually work
634             // most of the time.
635             if ( nTabPos != 0 )
636             {
637                 const sal_uInt16 nDesired = aLVL.nDxaLeft + aLVL.nDxaLeft1;
638 
639                 bool bDoAdjust = false;
640                 if ( nDesired < aLVL.nDxaLeft )
641                 {
642                     if ( nDesired < nTabPos && nTabPos < aLVL.nDxaLeft )
643                     {
644                         bDoAdjust = true;
645                     }
646                 }
647                 else
648                 {
649                     if ( aLVL.nDxaLeft < nTabPos && nTabPos < nDesired )
650                     {
651                         bDoAdjust = true;
652                     }
653                 }
654 
655                 if (bDoAdjust)
656                 {
657                     aLVL.nDxaLeft = (0 < nTabPos)
658                                     ? static_cast<sal_uInt16>(nTabPos)
659                                     : static_cast<sal_uInt16>(-nTabPos);
660 
661                     aLVL.nDxaLeft1 = nDesired - aLVL.nDxaLeft;
662                 }
663             }
664         }
665     }
666 
667     // 3. read CHPx if needed
668 
669     sal_uInt16 nWitchPicIsBullet = USHRT_MAX;
670     bool bIsPicBullet = false;
671 
672     if( aLVL.nLenGrpprlChpx )
673     {
674         sal_uInt8 aGrpprlChpx[ 255 ] = {};
675         if (aLVL.nLenGrpprlChpx != rSt.ReadBytes(&aGrpprlChpx, aLVL.nLenGrpprlChpx))
676             return false;
677 
678         //For i120928,parse the graphic info of bullets
679         SprmResult aSprmWhichPis = GrpprlHasSprm(NS_sprm::sprmCPbiIBullet, aGrpprlChpx[0],aLVL.nLenGrpprlChpx);
680         SprmResult aSprmIsPicBullet = GrpprlHasSprm(NS_sprm::sprmCPbiGrf, aGrpprlChpx[0],aLVL.nLenGrpprlChpx);
681         if (aSprmWhichPis.pSprm && aSprmWhichPis.nRemainingData >= 1)
682         {
683             nWitchPicIsBullet = *aSprmWhichPis.pSprm;
684         }
685         if (aSprmIsPicBullet.pSprm && aSprmIsPicBullet.nRemainingData >= 1)
686         {
687             bIsPicBullet = (*aSprmIsPicBullet.pSprm) & 0x0001;
688         }
689 
690         // create new Itemset for character attributes
691         rpItemSet.reset(new SfxItemSet( rDoc.GetAttrPool(), svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>{}));
692 
693         // Set Reader-ItemSet-Pointer to the newly created set
694         rReader.SetCurrentItemSet(std::move(rpItemSet));
695         // Set Reader-Style to Style of this Level
696         sal_uInt16 nOldColl = rReader.GetCurrentColl();
697         sal_uInt16 nNewColl = nLevelStyle;
698         if (ww::stiNil == nNewColl)
699             nNewColl = 0;
700         rReader.SetNCurrentColl( nNewColl );
701 
702         // The Read_xy() methods in WW8PAR6.cxx are calling their respective
703         // NewAttr() or GetFormatAttr() which can determine, by using the assigned
704         // Reader-ItemSet-Pointer, whether this specific ItemSet is relevant
705         // and not a Stack or Style!
706         sal_uInt16 nOldFlags1 = rReader.GetToggleAttrFlags();
707         sal_uInt16 nOldFlags2 = rReader.GetToggleBiDiAttrFlags();
708 
709         WW8SprmIter aSprmIter(&aGrpprlChpx[0], aLVL.nLenGrpprlChpx,
710             maSprmParser);
711         while (const sal_uInt8* pSprm = aSprmIter.GetSprms())
712         {
713             rReader.ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetCurrentId());
714             aSprmIter.advance();
715         }
716 
717         // Reset Reader-ItemSet-Pointer and Reader-Style
718         rpItemSet = rReader.SetCurrentItemSet(nullptr);
719         rReader.SetNCurrentColl( nOldColl );
720         rReader.SetToggleAttrFlags(nOldFlags1);
721         rReader.SetToggleBiDiAttrFlags(nOldFlags2);
722     }
723 
724     // 4. Read numbering String. Results in prefix and postfix
725 
726     OUString sNumString(sanitizeString(read_uInt16_PascalString(rSt)));
727 
728     // 5. convert read values into Writer syntax
729 
730     if( 0 <= aLVL.nStartAt )
731         nStartNo = static_cast<sal_uInt16>(aLVL.nStartAt);
732 
733     switch( aLVL.nNFC )
734     {
735         case 0:
736             nType = SVX_NUM_ARABIC;
737             break;
738         case 1:
739             nType = SVX_NUM_ROMAN_UPPER;
740             break;
741         case 2:
742             nType = SVX_NUM_ROMAN_LOWER;
743             break;
744         case 3:
745             nType = SVX_NUM_CHARS_UPPER_LETTER_N;
746             break;
747         case 4:
748             nType = SVX_NUM_CHARS_LOWER_LETTER_N;
749             break;
750         case 5:
751             // actually: ORDINAL
752             nType = SVX_NUM_ARABIC;
753             break;
754         case 23:
755             nType = SVX_NUM_CHAR_SPECIAL;
756             //For i120928,type info
757             if (bIsPicBullet)
758             {
759                 nType = SVX_NUM_BITMAP;
760             }
761 
762             break;
763         case 255:
764             nType = SVX_NUM_NUMBER_NONE;
765             break;
766         case 14:
767         case 19:nType = SVX_NUM_FULL_WIDTH_ARABIC; break;
768         case 30:nType = SVX_NUM_TIAN_GAN_ZH; break;
769         case 31:nType = SVX_NUM_DI_ZI_ZH; break;
770         case 35:
771         case 36:
772         case 37:
773         case 11:
774         case 39:nType = SVX_NUM_NUMBER_LOWER_ZH; break;
775         case 34:nType = SVX_NUM_NUMBER_UPPER_ZH_TW; break;
776         case 38:nType = SVX_NUM_NUMBER_UPPER_ZH; break;
777         case 10:nType = SVX_NUM_NUMBER_TRADITIONAL_JA; break;
778         case 20:nType = SVX_NUM_AIU_FULLWIDTH_JA; break;
779         case 12:nType = SVX_NUM_AIU_HALFWIDTH_JA; break;
780         case 21:nType = SVX_NUM_IROHA_FULLWIDTH_JA; break;
781         case 13:nType = SVX_NUM_IROHA_HALFWIDTH_JA; break;
782         case 24:nType = SVX_NUM_HANGUL_SYLLABLE_KO; break;
783         case 25:nType = SVX_NUM_HANGUL_JAMO_KO; break;
784         case 41:nType = SVX_NUM_NUMBER_HANGUL_KO; break;
785         //case 42:
786         //case 43:
787         case 44:nType = SVX_NUM_NUMBER_UPPER_KO; break;
788         default:
789                 nType= SVX_NUM_ARABIC; break;
790     }
791 
792     //If a number level is not going to be used, then record this fact
793     if (SVX_NUM_NUMBER_NONE == nType)
794         rNotReallyThere[nLevel] = true;
795 
796     /*
797      If a number level was not used (i.e. is in NotReallyThere), and that
798      number level appears at one of the positions in the display string of the
799      list, then it effectively is not there at all. So remove that level entry
800      from a copy of the aOfsNumsXCH.
801     */
802     std::vector<sal_uInt8> aOfsNumsXCH;
803     aOfsNumsXCH.reserve(nMaxLevel);
804 
805     for(sal_uInt8 nLevelB = 0; nLevelB < nMaxLevel; ++nLevelB)
806         aOfsNumsXCH.push_back(aLVL.aOfsNumsXCH[nLevelB]);
807 
808     // nLevelB is an index in the aOfsNumsXCH array.
809     for(sal_uInt16 nLevelB = 0; nLevelB <= nLevel; ++nLevelB)
810     {
811         // nPos is a one-based character offset to a level placeholder in
812         // sNumString.
813         sal_uInt8 nPos = aOfsNumsXCH[nLevelB];
814         if (nPos && nPos < sNumString.getLength())
815         {
816             // nPosValue is the actual numbering level.
817             sal_Unicode nPosValue = sNumString[nPos-1];
818             if (nPosValue < nMaxLevel)
819             {
820                 if (rNotReallyThere[nPosValue])
821                     aOfsNumsXCH[nLevelB] = 0;
822             }
823         }
824     }
825     auto aIter = std::remove(aOfsNumsXCH.begin(), aOfsNumsXCH.end(), 0);
826     auto aEnd = aOfsNumsXCH.end();
827     // #i60633# - suppress access on <aOfsNumsXCH.end()>
828     if ( aIter != aEnd )
829     {
830         // Somehow the first removed vector element, at which <aIter>
831         // points to, isn't reset to zero.
832         // Investigation is needed to clarify why. It seems that only
833         // special arrays are handled correctly by this code.
834         ++aIter;
835         while (aIter != aEnd)
836         {
837             (*aIter) = 0;
838             ++aIter;
839         }
840     }
841 
842     sal_uInt8 nUpperLevel = 0;  // current displaydepth for Writer
843     for(sal_uInt8 nLevelB = 0; nLevelB < nMaxLevel; ++nLevelB)
844     {
845         if (!nUpperLevel && !aOfsNumsXCH[nLevelB])
846             nUpperLevel = nLevelB;
847     }
848 
849     // If the terminating char was not NULL, all indices of the list are
850     // filled, so the levels have to be displayed.
851     if (!nUpperLevel)
852         nUpperLevel = nMaxLevel;
853 
854     if (style::NumberingType::CHAR_SPECIAL == nType)
855     {
856         cBullet = !sNumString.isEmpty() ? sNumString[0] : 0x2190;
857 
858         if (!cBullet)  // unsave control code?
859             cBullet = 0x2190;
860     }
861     else if (style::NumberingType::BITMAP == nType)   //For i120928,position index info of graphic
862     {
863         cGrfBulletCP = nWitchPicIsBullet;       // This is a bullet picture ID
864     }
865     else
866     {
867         /*
868         #i173#
869         Our aOfsNumsXCH seems generally to be an array that contains the
870         offset into sNumString of locations where the numbers should be
871         filled in, so if the first "fill in a number" slot is greater than
872         1 there is a "prefix" before the number
873         */
874         //First number appears at
875         sal_uInt8 nOneBasedFirstNoIndex = aOfsNumsXCH[0];
876         const sal_Int32 nFirstNoIndex =
877             nOneBasedFirstNoIndex > 0 ? nOneBasedFirstNoIndex -1 : SAL_MAX_INT32;
878         lcl_CopyGreaterEight(sPrefix, sNumString, 0, nFirstNoIndex);
879 
880         //Next number appears at
881         assert(nUpperLevel > 0);
882         sal_uInt8 nOneBasedNextNoIndex = aOfsNumsXCH[nUpperLevel-1];
883         const sal_Int32 nNextNoIndex =
884             nOneBasedNextNoIndex > 0 ? nOneBasedNextNoIndex : SAL_MAX_INT32;
885         if (sNumString.getLength() > nNextNoIndex)
886             lcl_CopyGreaterEight(sPostfix, sNumString, nNextNoIndex);
887 
888         /*
889          We use lcl_CopyGreaterEight because once if we have removed unused
890          number indexes from the aOfsNumsXCH then placeholders remain in
891          sNumString which must not be copied into the final numbering strings
892         */
893     }
894 
895     switch( aLVL.nAlign )
896     {
897         case 0:
898             eAdj = SvxAdjust::Left;
899             break;
900         case 1:
901             eAdj = SvxAdjust::Center;
902             break;
903         case 2:
904             eAdj = SvxAdjust::Right;
905             break;
906         case 3:
907             // Writer here cannot do block justification
908             eAdj = SvxAdjust::Left;
909             break;
910          default:
911             // undefined value
912             OSL_ENSURE( false, "Value of aLVL.nAlign is not supported" );
913             // take default
914             eAdj = SvxAdjust::Left;
915             break;
916     }
917 
918     // 6. Configure NumFormat
919     if( bSetStartNo )
920         rNumFormat.SetStart( nStartNo );
921     rNumFormat.SetNumberingType( nType );
922     rNumFormat.SetNumAdjust( eAdj );
923 
924     if( style::NumberingType::CHAR_SPECIAL == nType )
925     {
926         // first character of the Prefix-Text is the Bullet
927         rNumFormat.SetBulletChar(cBullet);
928         // Don't forget: further below, after building styles
929         // Call SetBulletFont() !!!
930     }
931     //For i120928,position index info
932     else if (style::NumberingType::BITMAP == nType)
933     {
934         rNumFormat.SetGrfBulletCP(cGrfBulletCP);
935     }
936     else
937     {
938         // reminder: Garnix is default Prefix
939         if( !sPrefix.isEmpty() )
940             rNumFormat.SetPrefix( sPrefix );
941         // reminder: Point is default Postfix
942         rNumFormat.SetSuffix( sPostfix );
943         rNumFormat.SetIncludeUpperLevels( nUpperLevel );
944     }
945 
946     // #i89181#
947     if ( rNumFormat.GetPositionAndSpaceMode() ==
948                               SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
949     {
950         if (eAdj == SvxAdjust::Right)
951         {
952             rNumFormat.SetAbsLSpace(aLVL.nDxaLeft);
953             rNumFormat.SetFirstLineOffset(-aLVL.nDxaLeft);
954             rNumFormat.SetCharTextDistance(-aLVL.nDxaLeft1);
955         }
956         else
957         {
958             rNumFormat.SetAbsLSpace( aLVL.nDxaLeft );
959             rNumFormat.SetFirstLineOffset(aLVL.nDxaLeft1);
960         }
961     }
962     else
963     {
964         rNumFormat.SetIndentAt( aLVL.nDxaLeft );
965         rNumFormat.SetFirstLineIndent(aLVL.nDxaLeft1);
966         if ( !aLVL.bV6 )
967             rNumFormat.SetListtabPos( nTabPos );
968         else
969             rNumFormat.SetListtabPos( aLVL.nV6Indent );
970         SvxNumberFormat::LabelFollowedBy eNumLabelFollowedBy = SvxNumberFormat::LISTTAB;
971         switch ( ixchFollow )
972         {
973             case 0:
974             {
975                 eNumLabelFollowedBy = SvxNumberFormat::LISTTAB;
976             }
977             break;
978             case 1:
979             {
980                 eNumLabelFollowedBy = SvxNumberFormat::SPACE;
981             }
982             break;
983             case 2:
984             {
985                 eNumLabelFollowedBy = SvxNumberFormat::NOTHING;
986             }
987             break;
988         }
989         rNumFormat.SetLabelFollowedBy( eNumLabelFollowedBy );
990     }
991 
992     return true;
993 }
994 
AdjustLVL(sal_uInt8 nLevel,SwNumRule & rNumRule,WW8aISet const & rListItemSet,WW8aCFormat & rCharFormat,bool & bNewCharFormatCreated,const OUString & sPrefix)995 void WW8ListManager::AdjustLVL( sal_uInt8 nLevel, SwNumRule& rNumRule,
996     WW8aISet const & rListItemSet, WW8aCFormat& rCharFormat, bool& bNewCharFormatCreated,
997     const OUString& sPrefix )
998 {
999     bNewCharFormatCreated = false;
1000     sal_uInt8 nIdenticalItemSetLevel;
1001     const SfxPoolItem* pItem;
1002 
1003     SwNumFormat aNumFormat  = rNumRule.Get( nLevel );
1004 
1005     SfxItemSet* pThisLevelItemSet = rListItemSet[nLevel].get();
1006 
1007     if( pThisLevelItemSet && pThisLevelItemSet->Count())
1008     {
1009         nIdenticalItemSetLevel = nMaxLevel;
1010         SfxItemIter aIter( *pThisLevelItemSet );
1011         for (sal_uInt8 nLowerLevel = 0; nLowerLevel < nLevel; ++nLowerLevel)
1012         {
1013             SfxItemSet* pLowerLevelItemSet = rListItemSet[nLowerLevel].get();
1014             if(     pLowerLevelItemSet
1015                 && (pLowerLevelItemSet->Count() == pThisLevelItemSet->Count()) )
1016             {
1017                 nIdenticalItemSetLevel = nLowerLevel;
1018                 const SfxPoolItem* pItemIter = aIter.GetCurItem();
1019                 do
1020                 {
1021                     if(  // search for appropriate pItem in pLowerLevelItemSet
1022                          (SfxItemState::SET != pLowerLevelItemSet->GetItemState(
1023                                             pItemIter->Which(), false, &pItem ) )
1024                         || // use virtual "!=" Operator
1025                          (*pItem != *pItemIter) )
1026                     // if no Item with equal nWhich was found or Item value was not equal
1027                     // store inequality and break!
1028                     {
1029                         nIdenticalItemSetLevel = nMaxLevel;
1030                         break;
1031                     }
1032                     pItemIter = aIter.NextItem();
1033                 } while (pItemIter);
1034 
1035                 if( nIdenticalItemSetLevel != nMaxLevel )
1036                     break;
1037             }
1038         }
1039 
1040         SwCharFormat* pFormat;
1041         if (nMaxLevel == nIdenticalItemSetLevel)
1042         {
1043             // Define Style
1044             const OUString aName( (!sPrefix.isEmpty() ? sPrefix : rNumRule.GetName())
1045                                   + "z" + OUString::number( nLevel ) );
1046 
1047             // remove const by casting
1048             pFormat = rDoc.MakeCharFormat(aName, rDoc.GetDfltCharFormat());
1049             bNewCharFormatCreated = true;
1050             // Set Attributes
1051             pFormat->SetFormatAttr( *pThisLevelItemSet );
1052         }
1053         else
1054         {
1055             // append Style
1056             pFormat = rCharFormat[ nIdenticalItemSetLevel ];
1057         }
1058 
1059         // store
1060         rCharFormat[ nLevel ] = pFormat;
1061 
1062         // Append Style to NumFormat
1063 
1064         aNumFormat.SetCharFormat( pFormat );
1065     }
1066     //Ensure the default char fmt is initialized for any level of num ruler if no customized attr
1067     else
1068     {
1069         SwCharFormat* pFormat = aNumFormat.GetCharFormat();
1070         if ( !pFormat)
1071         {
1072             const OUString aName( (!sPrefix.isEmpty() ? sPrefix : rNumRule.GetName())
1073                                   + "z" + OUString::number( nLevel ) );
1074 
1075             pFormat = rDoc.MakeCharFormat(aName, rDoc.GetDfltCharFormat());
1076             bNewCharFormatCreated = true;
1077             rCharFormat[ nLevel ] = pFormat;
1078             aNumFormat.SetCharFormat( pFormat );
1079         }
1080     }
1081 
1082     // if necessary: Append Bullet Font to NumFormat
1083 
1084     if( SVX_NUM_CHAR_SPECIAL == aNumFormat.GetNumberingType() )
1085     {
1086         SwCharFormat* pFormat = aNumFormat.GetCharFormat();
1087         vcl::Font aFont;
1088         if( !pFormat )
1089         {
1090             aFont = numfunc::GetDefBulletFont();
1091         }
1092         else
1093         {
1094             const SvxFontItem& rFontItem = pFormat->GetFont();
1095             aFont.SetFamily(        rFontItem.GetFamily()     );
1096             aFont.SetFamilyName(    rFontItem.GetFamilyName() );
1097             aFont.SetStyleName(     rFontItem.GetStyleName()  );
1098             aFont.SetPitch(         rFontItem.GetPitch()      );
1099             aFont.SetCharSet(       rFontItem.GetCharSet()    );
1100         }
1101         aNumFormat.SetBulletFont( &aFont );
1102     }
1103 
1104     // Set NumFormat in NumRule
1105 
1106     rNumRule.Set(nLevel, aNumFormat);
1107 }
1108 
CreateNextRule(bool bSimple)1109 SwNumRule* WW8ListManager::CreateNextRule(bool bSimple)
1110 {
1111     // Used to build the Style Name
1112     const OUString sPrefix("WW8Num" + OUString::number(nUniqueList++));
1113     // #i86652#
1114     sal_uInt16 nRul =
1115             rDoc.MakeNumRule( rDoc.GetUniqueNumRuleName(&sPrefix), nullptr, false,
1116                               SvxNumberFormat::LABEL_ALIGNMENT );
1117     SwNumRule* pMyNumRule = rDoc.GetNumRuleTable()[nRul];
1118     pMyNumRule->SetAutoRule(false);
1119     pMyNumRule->SetContinusNum(bSimple);
1120     return pMyNumRule;
1121 }
1122 
GetNumRule(size_t i)1123 SwNumRule* WW8ListManager::GetNumRule(size_t i)
1124 {
1125     if (i < maLSTInfos.size())
1126         return maLSTInfos[i]->pNumRule;
1127     else
1128         return nullptr;
1129 }
1130 
1131 // public methods
1132 
WW8ListManager(SvStream & rSt_,SwWW8ImplReader & rReader_)1133 WW8ListManager::WW8ListManager(SvStream& rSt_, SwWW8ImplReader& rReader_)
1134     : maSprmParser(rReader_.GetFib()), rReader(rReader_)
1135     , rDoc(rReader.GetDoc())
1136     , rFib(rReader.GetFib()), rSt(rSt_)
1137     , nUniqueList(1)
1138     , nLastLFOPosition(USHRT_MAX)
1139 {
1140 
1141     // LST and LFO only since WW8
1142     if(    ( 8 > rFib.m_nVersion )
1143             || ( rFib.m_fcPlcfLst == rFib.m_fcPlfLfo )
1144             || ( rFib.m_lcbPlcfLst < 2 )
1145             || ( rFib.m_lcbPlfLfo < 2) ) return; // no public lists
1146 
1147     // create Arrays
1148     bool bLVLOk = true;
1149 
1150     long nOriginalPos = rSt.Tell();
1151 
1152     // 1. read PLCF LST and create list templates in Writer
1153 
1154     bool bOk = checkSeek(rSt, rFib.m_fcPlcfLst);
1155 
1156     if (!bOk)
1157         return;
1158 
1159     sal_uInt32 nRemainingPlcfLst = rFib.m_lcbPlcfLst;
1160 
1161     sal_uInt16 nListCount(0);
1162     rSt.ReadUInt16( nListCount );
1163     nRemainingPlcfLst -= 2;
1164     bOk = nListCount > 0;
1165 
1166     if (!bOk)
1167         return;
1168 
1169     // 1.1 read all LST
1170     const size_t nMinRecordSize = 10 + 2*nMaxLevel;
1171     const size_t nMaxRecords = rSt.remainingSize() / nMinRecordSize;
1172     if (nListCount > nMaxRecords)
1173     {
1174         SAL_WARN("sw.ww8", "Parsing error: " << nMaxRecords <<
1175                  " max possible entries, but " << nListCount << " claimed, truncating");
1176         nListCount = nMaxRecords;
1177     }
1178     for (sal_uInt16 nList=0; nList < nListCount; ++nList)
1179     {
1180         if (nRemainingPlcfLst < cbLSTF)
1181             break;
1182 
1183         WW8LST aLST = {};
1184 
1185         // 1.1.1 read Data
1186 
1187         rSt.ReadUInt32( aLST.nIdLst );
1188         rSt.ReadUInt32( aLST.nTplC );
1189         for (sal_uInt16 & nLevel : aLST.aIdSty)
1190             rSt.ReadUInt16( nLevel );
1191 
1192         sal_uInt8 aBits1(0);
1193         rSt.ReadUChar( aBits1 );
1194 
1195         rSt.SeekRel( 1 );
1196 
1197         if( aBits1 & 0x01 )
1198             aLST.bSimpleList = true;
1199         if( aBits1 & 0x02 )
1200             aLST.bRestartHdn = true;
1201 
1202         // 1.1.2 new NumRule inserted in Doc and  WW8LSTInfo marked
1203 
1204         /*
1205         #i1869#
1206         In word 2000 microsoft got rid of creating new "simple lists" with
1207         only 1 level, all new lists are created with 9 levels. To hack it
1208         so that the list types formerly known as simple lists still have
1209         their own tab page to themselves one of the reserved bits is used
1210         to show that a given list is to be in the simple list tabpage.
1211         This has now nothing to do with the actual number of list level a
1212         list has, only how many will be shown in the user interface.
1213 
1214         i.e. create a simple list in 2000 and open it in 97 and 97 will
1215         claim (correctly) that it is an outline list. We can set our
1216         continuous flag in these lists to store this information.
1217         */
1218         SwNumRule* pMyNumRule = CreateNextRule(
1219             aLST.bSimpleList || (aBits1 & 0x10));
1220 
1221         WW8LSTInfo* pLSTInfo = new WW8LSTInfo(pMyNumRule, aLST);
1222         maLSTInfos.emplace_back(pLSTInfo);
1223 
1224         nRemainingPlcfLst -= cbLSTF;
1225     }
1226 
1227     // 1.2 read all LVL of all aLST
1228 
1229     sal_uInt16 nLSTInfos = static_cast< sal_uInt16 >(maLSTInfos.size());
1230     for (sal_uInt16 nList = 0; nList < nLSTInfos; ++nList)
1231     {
1232         WW8aISet aItemSet;        // Character attributes from GrpprlChpx
1233 
1234         WW8LSTInfo* pListInfo = maLSTInfos[nList].get();
1235         if( !pListInfo || !pListInfo->pNumRule ) break;
1236         SwNumRule& rMyNumRule = *pListInfo->pNumRule;
1237 
1238         // 1.2.1 read specific LVL(s) for this aLST
1239 
1240         sal_uInt16 nLvlCount = static_cast< sal_uInt16 >(pListInfo->bSimpleList ? nMinLevel : nMaxLevel);
1241         std::deque<bool> aNotReallyThere;
1242         aNotReallyThere.resize(nMaxLevel);
1243         pListInfo->maParaSprms.resize(nMaxLevel);
1244         for (sal_uInt16 nLevel = 0; nLevel < nLvlCount; ++nLevel)
1245         {
1246             SwNumFormat aNumFormat( rMyNumRule.Get( nLevel ) );
1247             // read LVLF
1248             bLVLOk = ReadLVL( aNumFormat, aItemSet[nLevel],
1249                 pListInfo->aIdSty[nLevel], true, aNotReallyThere, nLevel,
1250                 pListInfo->maParaSprms[nLevel]);
1251             if( !bLVLOk )
1252                 break;
1253             // and set in rMyNumRule
1254             rMyNumRule.Set( nLevel, aNumFormat );
1255         }
1256         if( !bLVLOk )
1257             break;
1258 
1259         // 1.2.2 compare ItemPools and CHPx Settings of different Levels
1260         //       and create Style(s) if necessary
1261 
1262         for (sal_uInt16 nLevel = 0; nLevel < nLvlCount; ++nLevel)
1263         {
1264             bool bDummy;
1265             AdjustLVL( nLevel, rMyNumRule, aItemSet,
1266                                            pListInfo->aCharFormat, bDummy );
1267         }
1268     }
1269 
1270     // 2. read and save PLF LFO
1271 
1272     bOk = checkSeek(rSt, rFib.m_fcPlfLfo);
1273 
1274     if (!bOk)
1275         return;
1276 
1277     sal_Int32 nLfoCount(0);
1278     rSt.ReadInt32( nLfoCount );
1279     bOk = nLfoCount > 0;
1280 
1281     if (!bOk)
1282         return;
1283 
1284     // 2.1 read all LFO
1285 
1286     for (sal_Int32 nLfo = 0; nLfo < nLfoCount; ++nLfo)
1287     {
1288         bOk = false;
1289 
1290         WW8LFO aLFO = {};
1291 
1292         rSt.ReadUInt32( aLFO.nIdLst );
1293         rSt.SeekRel( 8 );
1294         rSt.ReadUChar( aLFO.nLfoLvl );
1295         if (!rSt.good())
1296             break;
1297         rSt.SeekRel( 3 );
1298         // as many Overrides as there are
1299         if ((nMaxLevel < aLFO.nLfoLvl) || rSt.GetError())
1300             break;
1301 
1302         // get the Parent NumRule of the current List
1303         WW8LSTInfo* pParentListInfo = GetLSTByListId(aLFO.nIdLst);
1304         if (pParentListInfo)
1305         {
1306             // Save the NumRule in this first step
1307             aLFO.pNumRule = pParentListInfo->pNumRule;
1308 
1309             // are there multiple Levels in the List?
1310             aLFO.bSimpleList = pParentListInfo->bSimpleList;
1311         }
1312         // store in Array
1313         std::unique_ptr<WW8LFOInfo> pLFOInfo(new WW8LFOInfo(aLFO));
1314         if (pParentListInfo)
1315         {
1316             //Copy the basic paragraph properties for each level from the
1317             //original list into the list format override levels.
1318             int nMaxSize = pParentListInfo->maParaSprms.size();
1319             pLFOInfo->maParaSprms.resize(nMaxSize);
1320             for (int i = 0; i < nMaxSize; ++i)
1321                 pLFOInfo->maParaSprms[i] = pParentListInfo->maParaSprms[i];
1322         }
1323         m_LFOInfos.push_back(std::move(pLFOInfo));
1324         bOk = true;
1325     }
1326 
1327     if( bOk )
1328     {
1329 
1330         // 2.2 read specific LFOLVL for all LFO
1331 
1332         size_t nLFOInfos = m_LFOInfos.size();
1333         for (size_t nLfo = 0; nLfo < nLFOInfos; ++nLfo)
1334         {
1335             WW8LFOInfo& rLFOInfo = *m_LFOInfos[nLfo];
1336             // Do LFOLVL exist?
1337             if( rLFOInfo.bOverride )
1338             {
1339                 WW8LSTInfo* pParentListInfo = GetLSTByListId(rLFOInfo.nIdLst);
1340                 if (!pParentListInfo)
1341                     break;
1342 
1343                 // 2.2.1 create new NumRule for this List
1344 
1345                 SwNumRule* pParentNumRule = rLFOInfo.pNumRule;
1346                 OSL_ENSURE(pParentNumRule, "ww: Impossible lists, please report");
1347                 if( !pParentNumRule )
1348                     break;
1349                 // create name-prefix for NumRule-Name
1350                 // and (if necessary) for Style-Name
1351                 const OUString sPrefix("WW8NumSt" + OUString::number( nLfo + 1 ));
1352                 // Now assign pNumRule its actual value!!!
1353                 // (it contained the parent NumRule up to this point)
1354 
1355                 // check if a Style is referencing this LFO
1356                 if( USHRT_MAX > rReader.StyleUsingLFO( nLfo ) )
1357                 {
1358                     sal_uInt16 nRul = rDoc.MakeNumRule(
1359                         rDoc.GetUniqueNumRuleName( &sPrefix ), pParentNumRule);
1360                     rLFOInfo.pNumRule = rDoc.GetNumRuleTable()[ nRul ];
1361                     rLFOInfo.pNumRule->SetAutoRule(false);
1362                 }
1363                 else
1364                 {
1365                     sal_uInt16 nRul = rDoc.MakeNumRule(
1366                         rDoc.GetUniqueNumRuleName(), pParentNumRule);
1367                     rLFOInfo.pNumRule = rDoc.GetNumRuleTable()[ nRul ];
1368                     rLFOInfo.pNumRule->SetAutoRule(true);  // = default
1369                 }
1370 
1371                 // 2.2.2 read all LFOLVL (and LVL) for the new NumRule
1372 
1373                 WW8aISet aItemSet;       // Character attributes from GrpprlChpx
1374                 WW8aCFormat aCharFormat = {};  // Character Style Pointer
1375 
1376                 //2.2.2.0 skip inter-group of override header ?
1377                 //See #i25438# for why I moved this here, compare
1378                 //that original bugdoc's binary to what it looks like
1379                 //when resaved with word, i.e. there is always a
1380                 //4 byte header, there might be more than one if
1381                 //that header was 0xFFFFFFFF, e.g. #114412# ?
1382                 sal_uInt32 nTest;
1383                 rSt.ReadUInt32( nTest );
1384                 do
1385                 {
1386                     nTest = 0;
1387                     rSt.ReadUInt32( nTest );
1388                 }
1389                 while (nTest == 0xFFFFFFFF);
1390                 rSt.SeekRel(-4);
1391 
1392                 std::deque<bool> aNotReallyThere(WW8ListManager::nMaxLevel);
1393                 for (sal_uInt8 nLevel = 0; nLevel < rLFOInfo.nLfoLvl; ++nLevel)
1394                 {
1395                     WW8LFOLVL aLFOLVL;
1396                     bLVLOk = false;
1397 
1398                     // 2.2.2.1 read LFOLVL
1399 
1400                     rSt.ReadInt32( aLFOLVL.nStartAt );
1401                     sal_uInt8 aBits1(0);
1402                     rSt.ReadUChar( aBits1 );
1403                     rSt.SeekRel( 3 );
1404                     if (rSt.GetError())
1405                         break;
1406 
1407                     // Note: MS writes the Override-Level-Number into 4 bit.
1408                     // We do not! (See comment at "struct WW8LFOInfo")
1409                     aLFOLVL.nLevel = aBits1 & 0x0F;
1410                     if( (0xFF > aBits1) &&
1411                         (nMaxLevel > aLFOLVL.nLevel) )
1412                     {
1413                         if (aBits1 & 0x10)
1414                             aLFOLVL.bStartAt = true;
1415                         else
1416                             aLFOLVL.bStartAt = false;
1417 
1418                         // 2.2.2.2 load dedicated LVL if necessary
1419 
1420                         SwNumFormat aNumFormat(
1421                             rLFOInfo.pNumRule->Get(aLFOLVL.nLevel));
1422                         if (aBits1 & 0x20)
1423                         {
1424                             aLFOLVL.bFormat = true;
1425                             // if bStartup is true, replace Startup-Level
1426                             // with the LVLF that is saved in the LVL
1427                             bLVLOk = nLevel < rLFOInfo.maParaSprms.size() &&
1428                                 ReadLVL(aNumFormat, aItemSet[nLevel],
1429                                 pParentListInfo->aIdSty[nLevel],
1430                                 aLFOLVL.bStartAt, aNotReallyThere, nLevel,
1431                                 rLFOInfo.maParaSprms[nLevel]);
1432 
1433                             if (!bLVLOk)
1434                                 break;
1435                         }
1436                         else if (aLFOLVL.bStartAt)
1437                         {
1438                             aNumFormat.SetStart(
1439                                 writer_cast<sal_uInt16>(aLFOLVL.nStartAt));
1440                         }
1441 
1442                         // 2.2.2.3 Set NumFormat in NumRule
1443 
1444                         rLFOInfo.pNumRule->Set(aLFOLVL.nLevel, aNumFormat);
1445                     }
1446                     bLVLOk = true;
1447 
1448                     if (nMaxLevel > aLFOLVL.nLevel)
1449                         rLFOInfo.maOverrides[aLFOLVL.nLevel] = aLFOLVL;
1450                 }
1451                 if( !bLVLOk )
1452                     break;
1453 
1454                 // 2.2.3 adjust LVL of the new NumRule
1455 
1456                 bool bNewCharFormatCreated = false;
1457                 for (sal_uInt8 nLevel = 0; nLevel < rLFOInfo.nLfoLvl; ++nLevel)
1458                 {
1459                     AdjustLVL( nLevel, *rLFOInfo.pNumRule, aItemSet, aCharFormat,
1460                         bNewCharFormatCreated, sPrefix );
1461                 }
1462             }
1463         }
1464     }
1465     // and we're done!
1466     rSt.Seek( nOriginalPos );
1467 }
1468 
~WW8ListManager()1469 WW8ListManager::~WW8ListManager() COVERITY_NOEXCEPT_FALSE
1470 {
1471     /*
1472      named lists remain in document
1473      unused automatic lists are removed from document (DelNumRule)
1474     */
1475     for(auto & rpInfo : maLSTInfos)
1476     {
1477         if (rpInfo->pNumRule && !rpInfo->bUsedInDoc &&
1478             rpInfo->pNumRule->IsAutoRule())
1479         {
1480             rDoc.DelNumRule(rpInfo->pNumRule->GetName());
1481         }
1482         rpInfo.reset();
1483     }
1484     for (auto aIter = m_LFOInfos.rbegin(); aIter != m_LFOInfos.rend(); ++aIter)
1485     {
1486         if ((*aIter)->bOverride
1487             && (*aIter)->pNumRule
1488             && !(*aIter)->bUsedInDoc
1489             && (*aIter)->pNumRule->IsAutoRule())
1490         {
1491             rDoc.DelNumRule( (*aIter)->pNumRule->GetName() );
1492         }
1493     }
1494 }
1495 
IsEqualFormatting(const SwNumRule & rOne,const SwNumRule & rTwo)1496 static bool IsEqualFormatting(const SwNumRule &rOne, const SwNumRule &rTwo)
1497 {
1498     bool bRet =
1499         (
1500           rOne.GetRuleType() == rTwo.GetRuleType() &&
1501           rOne.IsContinusNum() == rTwo.IsContinusNum() &&
1502           rOne.IsAbsSpaces() == rTwo.IsAbsSpaces() &&
1503           rOne.GetPoolFormatId() == rTwo.GetPoolFormatId() &&
1504           rOne.GetPoolHelpId() == rTwo.GetPoolHelpId() &&
1505           rOne.GetPoolHlpFileId() == rTwo.GetPoolHlpFileId()
1506         );
1507 
1508     if (bRet)
1509     {
1510         for (sal_uInt8 n = 0; n < MAXLEVEL; ++n )
1511         {
1512             //The SvxNumberFormat compare, not the SwNumFormat compare
1513             const SvxNumberFormat &rO = rOne.Get(n);
1514             const SvxNumberFormat &rT = rTwo.Get(n);
1515             if (rO != rT)
1516             {
1517                 bRet = false;
1518                 break;
1519             }
1520         }
1521     }
1522     return bRet;
1523 }
1524 
GetNumRuleForActivation(sal_uInt16 nLFOPosition,const sal_uInt8 nLevel,std::vector<sal_uInt8> & rParaSprms,SwTextNode * pNode)1525 SwNumRule* WW8ListManager::GetNumRuleForActivation(sal_uInt16 nLFOPosition,
1526     const sal_uInt8 nLevel, std::vector<sal_uInt8> &rParaSprms, SwTextNode *pNode)
1527 {
1528     if (m_LFOInfos.size() <= nLFOPosition)
1529         return nullptr;
1530 
1531     WW8LFOInfo& rLFOInfo = *m_LFOInfos[nLFOPosition];
1532 
1533     bool bFirstUse = !rLFOInfo.bUsedInDoc;
1534     rLFOInfo.bUsedInDoc = true;
1535 
1536     if( !rLFOInfo.pNumRule )
1537         return nullptr;
1538 
1539     // #i25545#
1540     // #i100132# - a number format does not have to exist on given list level
1541     SwNumFormat aFormat(rLFOInfo.pNumRule->Get(nLevel));
1542 
1543     if (rReader.IsRightToLeft() && nLastLFOPosition != nLFOPosition) {
1544         if ( aFormat.GetNumAdjust() == SvxAdjust::Right)
1545             aFormat.SetNumAdjust(SvxAdjust::Left);
1546         else if ( aFormat.GetNumAdjust() == SvxAdjust::Left)
1547             aFormat.SetNumAdjust(SvxAdjust::Right);
1548         rLFOInfo.pNumRule->Set(nLevel, aFormat);
1549     }
1550     nLastLFOPosition = nLFOPosition;
1551     /*
1552     #i1869#
1553     If this list has had its bits set in word 2000 to pretend that it is a
1554     simple list from the point of view of the user, then it is almost
1555     certainly a simple continuous list, and we will try to keep it like that.
1556     Otherwise when we save again it will be shown as the true outline list
1557     that it is, confusing the user that just wanted what they thought was a
1558     simple list. On the other hand it is possible that some of the other levels
1559     were used by the user, in which case we will not pretend anymore that it
1560     is a simple list. Something that word 2000 does anyway, that 97 didn't, to
1561     my bewilderment.
1562     */
1563     if (nLevel && rLFOInfo.pNumRule->IsContinusNum())
1564         rLFOInfo.pNumRule->SetContinusNum(false);
1565 
1566     if( (!rLFOInfo.bOverride) && (!rLFOInfo.bLSTbUIDSet) )
1567     {
1568         WW8LSTInfo* pParentListInfo = GetLSTByListId( rLFOInfo.nIdLst );
1569         if( pParentListInfo )
1570             pParentListInfo->bUsedInDoc = true;
1571         rLFOInfo.bLSTbUIDSet = true;
1572     }
1573 
1574     if (rLFOInfo.maParaSprms.size() > nLevel)
1575         rParaSprms = rLFOInfo.maParaSprms[nLevel];
1576 
1577     SwNumRule *pRet = rLFOInfo.pNumRule;
1578 
1579     bool bRestart(false);
1580     sal_uInt16 nStart(0);
1581     bool bNewstart(false);
1582     /*
1583       Note: If you fiddle with this then you have to make sure that #i18322#
1584       #i13833#, #i20095# and #112466# continue to work
1585 
1586       Check if there were overrides for this level
1587     */
1588     if (rLFOInfo.bOverride && nLevel < rLFOInfo.nLfoLvl)
1589     {
1590         WW8LSTInfo* pParentListInfo = GetLSTByListId(rLFOInfo.nIdLst);
1591         OSL_ENSURE(pParentListInfo, "ww: Impossible lists, please report");
1592         if (pParentListInfo && pParentListInfo->pNumRule)
1593         {
1594             const WW8LFOLVL &rOverride = rLFOInfo.maOverrides[nLevel];
1595             bool bNoChangeFromParent =
1596                 IsEqualFormatting(*pRet, *(pParentListInfo->pNumRule));
1597 
1598             //If so then I think word still uses the parent (maybe)
1599             if (bNoChangeFromParent)
1600             {
1601                 pRet = pParentListInfo->pNumRule;
1602 
1603                 //did it not affect start at value ?
1604                 if (bFirstUse && rOverride.bStartAt)
1605                 {
1606                     const SwNumFormat &rFormat =
1607                         pParentListInfo->pNumRule->Get(nLevel);
1608                     if (
1609                          rFormat.GetStart() ==
1610                          rLFOInfo.maOverrides[nLevel].nStartAt
1611                        )
1612                     {
1613                         bRestart = true;
1614                     }
1615                     else
1616                     {
1617                         bNewstart = true;
1618                         nStart = writer_cast<sal_uInt16>
1619                             (rLFOInfo.maOverrides[nLevel].nStartAt);
1620                     }
1621                 }
1622 
1623                 pParentListInfo->bUsedInDoc = true;
1624             }
1625         }
1626     }
1627 
1628     if (pNode)
1629     {
1630         pNode->SetAttrListLevel(nLevel);
1631 
1632         if (bRestart || bNewstart)
1633             pNode->SetListRestart(true);
1634         if (bNewstart)
1635             pNode->SetAttrListRestartValue(nStart);
1636     }
1637     return pRet;
1638 }
1639 
1640 // SwWW8ImplReader:  append a List to a Style or Paragraph
1641 
SetTextFormatCollAndListLevel(const SwPaM & rRg,SwWW8StyInf & rStyleInfo)1642 bool SwWW8ImplReader::SetTextFormatCollAndListLevel(const SwPaM& rRg,
1643     SwWW8StyInf& rStyleInfo)
1644 {
1645     bool bRes = true;
1646     if( rStyleInfo.m_pFormat && rStyleInfo.m_bColl )
1647     {
1648         bRes = m_rDoc.SetTextFormatColl(rRg, static_cast<SwTextFormatColl*>(rStyleInfo.m_pFormat));
1649         SwTextNode* pTextNode = m_pPaM->GetNode().GetTextNode();
1650         OSL_ENSURE( pTextNode, "No Text-Node at PaM-Position" );
1651         if ( !pTextNode )
1652         {
1653             // make code robust
1654             return bRes;
1655         }
1656 
1657         const SwNumRule * pNumRule = pTextNode->GetNumRule(); // #i27610#
1658 
1659         if( !IsInvalidOrToBeMergedTabCell() &&
1660             ! (pNumRule && pNumRule->IsOutlineRule()) ) // #i27610#
1661         {
1662             pTextNode->ResetAttr( RES_PARATR_NUMRULE );
1663         }
1664 
1665         if (USHRT_MAX > rStyleInfo.m_nLFOIndex && WW8ListManager::nMaxLevel
1666                                                 > rStyleInfo.m_nListLevel)
1667         {
1668             const bool bApplyListStyle = false;
1669             RegisterNumFormatOnTextNode(rStyleInfo.m_nLFOIndex, rStyleInfo.m_nListLevel,
1670                                     bApplyListStyle);
1671         }
1672     }
1673     return bRes;
1674 }
1675 
UseListIndent(SwWW8StyInf & rStyle,const SwNumFormat & rFormat)1676 void UseListIndent(SwWW8StyInf &rStyle, const SwNumFormat &rFormat)
1677 {
1678     // #i86652#
1679     if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
1680     {
1681         const auto nAbsLSpace = rFormat.GetAbsLSpace();
1682         const long nListFirstLineIndent = GetListFirstLineIndent(rFormat);
1683         SvxLRSpaceItem aLR(ItemGet<SvxLRSpaceItem>(*rStyle.m_pFormat, RES_LR_SPACE));
1684         aLR.SetTextLeft(nAbsLSpace);
1685         aLR.SetTextFirstLineOfst(writer_cast<short>(nListFirstLineIndent));
1686         rStyle.m_pFormat->SetFormatAttr(aLR);
1687         rStyle.m_bListReleventIndentSet = true;
1688     }
1689 }
1690 
SetStyleIndent(SwWW8StyInf & rStyle,const SwNumFormat & rFormat)1691 void SetStyleIndent(SwWW8StyInf &rStyle, const SwNumFormat &rFormat)
1692 {
1693     if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) // #i86652#
1694     {
1695         SvxLRSpaceItem aLR(ItemGet<SvxLRSpaceItem>(*rStyle.m_pFormat, RES_LR_SPACE));
1696         if (rStyle.m_bListReleventIndentSet)
1697         {
1698 
1699             SyncIndentWithList( aLR, rFormat, false, false ); // #i103711#, #i105414#
1700         }
1701         else
1702         {
1703             aLR.SetTextLeft(0);
1704             aLR.SetTextFirstLineOfst(0);
1705         }
1706         rStyle.m_pFormat->SetFormatAttr(aLR);
1707     }
1708 }
1709 
SetStylesList(sal_uInt16 nStyle,sal_uInt16 nCurrentLFO,sal_uInt8 nCurrentLevel)1710 void SwWW8ImplReader::SetStylesList(sal_uInt16 nStyle, sal_uInt16 nCurrentLFO,
1711     sal_uInt8 nCurrentLevel)
1712 {
1713     if (nStyle >= m_vColl.size())
1714         return;
1715 
1716     SwWW8StyInf &rStyleInf = m_vColl[nStyle];
1717     if (rStyleInf.m_bValid)
1718     {
1719         OSL_ENSURE(m_pCurrentColl, "Cannot be called outside of style import");
1720         // Phase 1: Numbering attributes when reading a StyleDef
1721         if( m_pCurrentColl )
1722         {
1723             // only save the Parameters for now. The actual List will be appended
1724             // at a later point, when the Listdefinitions is read...
1725             if (
1726                  (USHRT_MAX > nCurrentLFO) &&
1727                  (WW8ListManager::nMaxLevel > nCurrentLevel)
1728                )
1729             {
1730                 rStyleInf.m_nLFOIndex  = nCurrentLFO;
1731                 rStyleInf.m_nListLevel = nCurrentLevel;
1732 
1733                 std::vector<sal_uInt8> aParaSprms;
1734                 SwNumRule* pNmRule = m_xLstManager->GetNumRuleForActivation(
1735                     nCurrentLFO, nCurrentLevel, aParaSprms);
1736                 if (pNmRule)
1737                     UseListIndent(rStyleInf, pNmRule->Get(nCurrentLevel));
1738             }
1739         }
1740     }
1741 }
1742 
RegisterNumFormatOnStyle(sal_uInt16 nStyle)1743 void SwWW8ImplReader::RegisterNumFormatOnStyle(sal_uInt16 nStyle)
1744 {
1745 
1746     if (nStyle >= m_vColl.size())
1747         return;
1748 
1749     SwWW8StyInf &rStyleInf = m_vColl[nStyle];
1750     if (rStyleInf.m_bValid && rStyleInf.m_pFormat)
1751     {
1752         //Save old pre-list modified indent, which are the word indent values
1753         rStyleInf.maWordLR.reset(static_cast<SvxLRSpaceItem*>(ItemGet<SvxLRSpaceItem>(*rStyleInf.m_pFormat, RES_LR_SPACE).Clone()));
1754 
1755         // Phase 2: refresh StyleDef after reading all Lists
1756         SwNumRule* pNmRule = nullptr;
1757         const sal_uInt16 nLFO = rStyleInf.m_nLFOIndex;
1758         const sal_uInt8  nLevel = rStyleInf.m_nListLevel;
1759         if (
1760              (USHRT_MAX > nLFO) &&
1761              (WW8ListManager::nMaxLevel > nLevel)
1762            )
1763         {
1764             std::vector<sal_uInt8> aParaSprms;
1765             pNmRule = m_xLstManager->GetNumRuleForActivation(nLFO, nLevel,
1766                 aParaSprms);
1767 
1768             if (pNmRule != nullptr)
1769             {
1770                 if (rStyleInf.IsWW8BuiltInHeadingStyle()
1771                     && rStyleInf.HasWW8OutlineLevel())
1772                 {
1773                     rStyleInf.m_pOutlineNumrule = pNmRule;
1774                 }
1775                 else
1776                 {
1777                     rStyleInf.m_pFormat->SetFormatAttr(
1778                         SwNumRuleItem(pNmRule->GetName()));
1779                     rStyleInf.m_bHasStyNumRule = true;
1780                 }
1781             }
1782         }
1783 
1784         if (pNmRule)
1785             SetStyleIndent(rStyleInf, pNmRule->Get(nLevel));
1786     }
1787 }
1788 
RegisterNumFormatOnTextNode(sal_uInt16 nCurrentLFO,sal_uInt8 nCurrentLevel,const bool bSetAttr)1789 void SwWW8ImplReader::RegisterNumFormatOnTextNode(sal_uInt16 nCurrentLFO,
1790                                               sal_uInt8 nCurrentLevel,
1791                                               const bool bSetAttr)
1792 {
1793     // Note: the method appends NumRule to the Text Node if
1794     // bSetAttr (of course the lists have to be read before)
1795     // and only sets the Level. It does not check if there is a NumRule
1796     // attached to the STYLE !!!
1797 
1798     if (m_xLstManager) // are all list declarations read?
1799     {
1800         SwTextNode* pTextNd = m_pPaM->GetNode().GetTextNode();
1801         OSL_ENSURE(pTextNd, "No Text-Node at PaM-Position");
1802         if (!pTextNd)
1803             return;
1804 
1805         std::vector<sal_uInt8> aParaSprms;
1806         const SwNumRule* pRule = bSetAttr ?
1807             m_xLstManager->GetNumRuleForActivation( nCurrentLFO, nCurrentLevel,
1808                 aParaSprms, pTextNd) : nullptr;
1809 
1810         if (pRule != nullptr || !bSetAttr)
1811         {
1812             if (bSetAttr && pTextNd->GetNumRule() != pRule
1813                 && pTextNd->GetNumRule() != m_rDoc.GetOutlineNumRule())
1814             {
1815                 pTextNd->SetAttr(SwNumRuleItem(pRule->GetName()));
1816             }
1817             pTextNd->SetAttrListLevel(nCurrentLevel);
1818 
1819             // <IsCounted()> state of text node has to be adjusted accordingly.
1820             if ( /*nCurrentLevel >= 0 &&*/ nCurrentLevel < MAXLEVEL )
1821             {
1822                 pTextNd->SetCountedInList( true );
1823             }
1824 
1825             // #i99822#
1826             // Direct application of the list level formatting no longer
1827             // needed for list levels of mode LABEL_ALIGNMENT
1828             bool bApplyListLevelIndentDirectlyAtPara(true);
1829             {
1830                 if (pTextNd->GetNumRule() && nCurrentLevel < MAXLEVEL)
1831                 {
1832                     const SwNumFormat& rFormat = pTextNd->GetNumRule()->Get(nCurrentLevel);
1833                     if (rFormat.GetPositionAndSpaceMode()
1834                         == SvxNumberFormat::LABEL_ALIGNMENT)
1835                     {
1836                         bApplyListLevelIndentDirectlyAtPara = false;
1837                     }
1838                 }
1839             }
1840 
1841             if (bApplyListLevelIndentDirectlyAtPara)
1842             {
1843                 std::unique_ptr<SfxItemSet> xListIndent(new SfxItemSet(m_rDoc.GetAttrPool(), svl::Items<RES_LR_SPACE,
1844                                                                                                         RES_LR_SPACE>{}));
1845                 const SvxLRSpaceItem *pItem = static_cast<const SvxLRSpaceItem*>(
1846                     GetFormatAttr(RES_LR_SPACE));
1847                 OSL_ENSURE(pItem, "impossible");
1848                 if (pItem)
1849                     xListIndent->Put(*pItem);
1850 
1851                 /*
1852                  Take the original paragraph sprms attached to this list level
1853                  formatting and apply them to the paragraph. I'm convinced that
1854                  this is exactly what word does.
1855                 */
1856                 if (short nLen = static_cast< short >(aParaSprms.size()))
1857                 {
1858                     std::unique_ptr<SfxItemSet> xOldCurrentItemSet(SetCurrentItemSet(std::move(xListIndent)));
1859 
1860                     sal_uInt8* pSprms1  = aParaSprms.data();
1861                     while (0 < nLen)
1862                     {
1863                         sal_uInt16 nL1 = ImportSprm(pSprms1, nLen);
1864                         nLen = nLen - nL1;
1865                         pSprms1 += nL1;
1866                     }
1867 
1868                     xListIndent = SetCurrentItemSet(std::move(xOldCurrentItemSet));
1869                 }
1870 
1871                 if (const SvxLRSpaceItem *pLR = xListIndent->GetItem<SvxLRSpaceItem>(RES_LR_SPACE))
1872                 {
1873                     m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *pLR);
1874                     m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_LR_SPACE);
1875                 }
1876             }
1877         }
1878     }
1879 }
1880 
RegisterNumFormat(sal_uInt16 nCurrentLFO,sal_uInt8 nCurrentLevel)1881 void SwWW8ImplReader::RegisterNumFormat(sal_uInt16 nCurrentLFO, sal_uInt8 nCurrentLevel)
1882 {
1883     // Are we reading the StyleDef ?
1884     if (m_pCurrentColl)
1885         SetStylesList( m_nCurrentColl , nCurrentLFO, nCurrentLevel);
1886     else
1887         RegisterNumFormatOnTextNode(nCurrentLFO, nCurrentLevel);
1888 }
1889 
Read_ListLevel(sal_uInt16,const sal_uInt8 * pData,short nLen)1890 void SwWW8ImplReader::Read_ListLevel(sal_uInt16, const sal_uInt8* pData,
1891     short nLen)
1892 {
1893     if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox())
1894         return;
1895 
1896     if( nLen < 0 )
1897     {
1898         // the current level is finished, what should we do ?
1899         m_nListLevel = WW8ListManager::nMaxLevel;
1900         if (m_xStyles && !m_bVer67)
1901             m_xStyles->mnWwNumLevel = 0;
1902     }
1903     else
1904     {
1905         // security check
1906         if( !pData )
1907             return;
1908 
1909         // the Streamdata is zero based
1910         m_nListLevel = *pData;
1911 
1912         if (m_xStyles && !m_bVer67)
1913         {
1914             /*
1915             if this is the case, then if the numbering is actually stored in
1916             winword 6 format, and its likely that sprmPIlvl has been abused
1917             to set the ww6 list level information which we will need when we
1918             reach the true ww6 list def.  So set it now
1919             */
1920             m_xStyles->mnWwNumLevel = m_nListLevel;
1921         }
1922 
1923         if (WW8ListManager::nMaxLevel <= m_nListLevel )
1924             m_nListLevel = WW8ListManager::nMaxLevel;
1925         else if (USHRT_MAX > m_nLFOPosition)
1926         {
1927             RegisterNumFormat(m_nLFOPosition, m_nListLevel);
1928             m_nLFOPosition = USHRT_MAX;
1929             m_nListLevel  = WW8ListManager::nMaxLevel;
1930         }
1931     }
1932 }
1933 
Read_LFOPosition(sal_uInt16,const sal_uInt8 * pData,short nLen)1934 void SwWW8ImplReader::Read_LFOPosition(sal_uInt16, const sal_uInt8* pData,
1935     short nLen)
1936 {
1937     if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox())
1938         return;
1939 
1940     if( nLen < 0 )
1941     {
1942         // the current level is finished, what should we do ?
1943         m_nLFOPosition = USHRT_MAX;
1944         m_nListLevel = WW8ListManager::nMaxLevel;
1945     }
1946     else
1947     {
1948         // security check
1949         if( !pData )
1950             return;
1951 
1952         short nData = SVBT16ToUInt16( pData );
1953         if( 0 >= nData )
1954         {
1955             // disable the numbering/list style apply to the paragraph or the style
1956 
1957             /*
1958             If you have a paragraph in word with left and/or hanging indent
1959             and remove its numbering, then the indentation appears to get
1960             reset, but not back to the base style, instead it goes to a blank
1961             setting.
1962             Unless it's a broken ww6 list in 97 in which case more hackery is
1963             required, some more details about broken ww6 list in
1964             ww8par6.cxx#SwWW8ImplReader::Read_LR
1965             */
1966 
1967             if (m_pCurrentColl)
1968             {
1969                 // here a "named" style is being configured
1970 
1971                 // disable the numbering/list in the style currently configured
1972                 m_pCurrentColl->SetFormatAttr(*GetDfltAttr(RES_PARATR_NUMRULE));
1973 
1974                 // reset/blank the indent
1975                 m_pCurrentColl->SetFormatAttr(SvxLRSpaceItem(RES_LR_SPACE));
1976             }
1977             else if (SwTextNode* pTextNode = m_pPaM->GetNode().GetTextNode())
1978             {
1979                 // here a paragraph is being directly formatted
1980 
1981                 // empty the numbering/list style applied to the current paragraph
1982                 SwNumRuleItem aEmptyRule;
1983                 pTextNode->SetAttr( aEmptyRule );
1984 
1985                 // create an empty SvxLRSpaceItem
1986                 std::shared_ptr<SvxLRSpaceItem> aLR(std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE));
1987 
1988                 // replace it with the one of the current node if it exist
1989                 const SfxPoolItem* pLR = GetFormatAttr(RES_LR_SPACE);
1990                 if( pLR )
1991                     aLR.reset(static_cast<SvxLRSpaceItem*>(pLR->Clone()));
1992 
1993                 // reset/blank the left indent (and only the left)
1994                 aLR->SetTextLeft(0);
1995                 aLR->SetTextFirstLineOfst(0);
1996 
1997                 // apply the modified SvxLRSpaceItem to the current paragraph
1998                 pTextNode->SetAttr( *aLR );
1999             }
2000 
2001             m_nLFOPosition = USHRT_MAX;
2002         }
2003         else // nData in (0..0x7FFF]
2004         {
2005             m_nLFOPosition = static_cast<sal_uInt16>(nData)-1; // m_nLFOPosition in [0..0x7FFF)
2006             /*
2007             If we are a ww8+ style with ww7- style lists then there is a
2008             bizarre broken word bug where when the list is removed from a para
2009             the ww6 list first line indent still affects the first line
2010             indentation.  Setting this flag will allow us to recover from this
2011             braindeadness
2012             */
2013             if (m_pCurrentColl && (m_nLFOPosition == 2047-1) && m_nCurrentColl < m_vColl.size())
2014                 m_vColl[m_nCurrentColl].m_bHasBrokenWW6List = true;
2015 
2016             // here the stream data is 1-based, we subtract ONE
2017             if (m_nLFOPosition != 2047-1) //Normal ww8+ list behaviour
2018             {
2019                 if (WW8ListManager::nMaxLevel == m_nListLevel)
2020                     m_nListLevel = 0;
2021                 if (WW8ListManager::nMaxLevel > m_nListLevel)
2022                 {
2023                     RegisterNumFormat(m_nLFOPosition, m_nListLevel);
2024                     m_nLFOPosition = USHRT_MAX;
2025                     m_nListLevel = WW8ListManager::nMaxLevel;
2026                 }
2027             }
2028             else if (m_xPlcxMan && m_xPlcxMan->HasParaSprm(NS_sprm::LN_PAnld).pSprm)
2029             {
2030                 /*
2031                  #i8114# Horrific backwards compatible ww7- lists in ww8+
2032                  docs
2033                 */
2034                 Read_ANLevelNo(13 /*equiv ww7- sprm no*/, &m_nListLevel, 1);
2035             }
2036         }
2037     }
2038 }
2039 
2040 // Reading Controls
2041 
ImportFormulaControl(WW8FormulaControl & aFormula,WW8_CP nStart,SwWw8ControlType nWhich)2042 bool SwWW8ImplReader::ImportFormulaControl(WW8FormulaControl &aFormula,
2043     WW8_CP nStart, SwWw8ControlType nWhich )
2044 {
2045     bool bRet=false;
2046     /*
2047      * Save the reader state and process the sprms for this anchor cp.
2048      * Doing so will set the nPicLocFc to the offset to find the hypertext
2049      * data in the data stream.
2050      */
2051     WW8_CP nEndCp = nStart+1; //Only interested in the single 0x01 character
2052 
2053     WW8ReaderSave aSave(this,nStart);
2054 
2055     WW8PLCFManResult aRes;
2056     nStart = m_xPlcxMan->Where();
2057     while(nStart <= nEndCp)
2058     {
2059         if ( m_xPlcxMan->Get(&aRes)
2060             && aRes.pMemPos && aRes.nSprmId )
2061         {
2062             //only interested in sprms which would set nPicLocFc
2063             if ( (68 == aRes.nSprmId) || (0x6A03 == aRes.nSprmId) )
2064             {
2065                 Read_PicLoc( aRes.nSprmId, aRes.pMemPos +
2066                     m_xSprmParser->DistanceToData(aRes.nSprmId), 4);
2067                 break;
2068             }
2069         }
2070         m_xPlcxMan->advance();
2071         nStart = m_xPlcxMan->Where();
2072     }
2073     sal_uLong nOffset = m_nPicLocFc;
2074     aSave.Restore(this);
2075 
2076     sal_uLong nOldPos = m_pDataStream->Tell();
2077     WW8_PIC aPic;
2078     m_pDataStream->Seek( nOffset);
2079     PicRead( m_pDataStream, &aPic, m_bVer67);
2080 
2081     if((aPic.lcb > 0x3A) && !m_pDataStream->GetError() )
2082     {
2083         aFormula.FormulaRead(nWhich,m_pDataStream);
2084         bRet = true;
2085     }
2086 
2087     /*
2088      There is a problem with aPic, the WW8_PIC is always used even though it
2089      is too big for the WW95 files, it needs to be modified to check the
2090      version C.
2091      */
2092     m_pDataStream->Seek( nOldPos );
2093     return bRet;
2094 }
2095 
InsertFormula(WW8FormulaControl & rFormula)2096 void SwMSConvertControls::InsertFormula(WW8FormulaControl &rFormula)
2097 {
2098     const uno::Reference< lang::XMultiServiceFactory > & rServiceFactory =
2099         GetServiceFactory();
2100 
2101     if(!rServiceFactory.is())
2102         return;
2103 
2104     awt::Size aSz;
2105     uno::Reference< form::XFormComponent> xFComp;
2106 
2107     if (rFormula.Import(rServiceFactory, xFComp, aSz))
2108     {
2109         uno::Reference <drawing::XShape> xShapeRef;
2110         if (InsertControl(xFComp, aSz, &xShapeRef, false))
2111             GetShapes()->add(xShapeRef);
2112     }
2113 }
2114 
FormulaRead(SwWw8ControlType nWhich,SvStream * pDataStream)2115 void WW8FormulaControl::FormulaRead(SwWw8ControlType nWhich,
2116     SvStream *pDataStream)
2117 {
2118     sal_uInt8 nField;
2119 
2120     // The following is a FFData structure as described in
2121     // Microsoft's DOC specification (chapter 2.9.78)
2122     sal_uInt32 nVersion = 0;
2123     pDataStream->ReadUInt32(nVersion);
2124     // An unsigned integer that MUST be 0xFFFFFFFF
2125     if (nVersion != 0xFFFFFFFF)
2126     {
2127         SAL_WARN("sw.ww8", "Parsing error: invalid header for FFData");
2128         return; // bail out
2129     }
2130 
2131     // might be better to read the bits as a 16 bit word
2132     // ( like it is in the spec. )
2133     sal_uInt8 bits1 = 0;
2134     pDataStream->ReadUChar( bits1 );
2135     sal_uInt8 bits2 = 0;
2136     pDataStream->ReadUChar( bits2 );
2137 
2138     sal_uInt8 iType = ( bits1 & 0x3 );
2139 
2140     // we should verify that bits.iType & nWhich concur
2141     OSL_ENSURE( iType == nWhich, "something wrong, expect control type read from stream doesn't match nWhich passed in");
2142     if ( iType != nWhich )
2143         return; // bail out
2144 
2145     sal_uInt8 iRes = (bits1 & 0x7C) >> 2;
2146 
2147     pDataStream->ReadUInt16( mnMaxLen );
2148 
2149     sal_uInt16 hps = 0;
2150     pDataStream->ReadUInt16( hps );
2151 
2152     // xstzName
2153     msTitle = read_uInt16_BeltAndBracesString(*pDataStream);
2154 
2155     if (nWhich == WW8_CT_EDIT)
2156     {   // Field is a textbox
2157         // Default text
2158         // xstzTextDef
2159         msDefault = read_uInt16_BeltAndBracesString(*pDataStream);
2160     }
2161     else
2162     {
2163         // CheckBox or ComboBox
2164         sal_uInt16 wDef = 0;
2165         pDataStream->ReadUInt16( wDef );
2166         mnChecked = wDef; // default
2167         if (nWhich == WW8_CT_CHECKBOX)
2168         {
2169             if ( iRes != 25 )
2170                 mnChecked = iRes;
2171             msDefault = ( wDef == 0 ) ? OUStringLiteral( "0" ) :  OUStringLiteral( "1" );
2172         }
2173     }
2174     // xstzTextFormat
2175     msFormatting = read_uInt16_BeltAndBracesString(*pDataStream);
2176     // xstzHelpText
2177     msHelp = read_uInt16_BeltAndBracesString(*pDataStream);
2178     // xstzStatText
2179     msToolTip = read_uInt16_BeltAndBracesString(*pDataStream);
2180 
2181     // xstzEntryMcr
2182     msEntryMcr = read_uInt16_BeltAndBracesString(*pDataStream);
2183     //xstzExitMcr
2184     msExitMcr = read_uInt16_BeltAndBracesString(*pDataStream);
2185 
2186     if (nWhich == WW8_CT_DROPDOWN)
2187     {
2188         bool bAllOk = true;
2189         // SSTB (see Spec. 2.2.4)
2190         sal_uInt16 fExtend = 0;
2191         pDataStream->ReadUInt16( fExtend );
2192         sal_uInt16 nStringsCnt = 0;
2193 
2194         // Isn't it that if fExtend isn't 0xFFFF then fExtend actually
2195         // doesn't exist and we really have just read nStringsCnt ( or cData )?
2196         if (fExtend != 0xFFFF)
2197             bAllOk = false;
2198         pDataStream->ReadUInt16( nStringsCnt );
2199 
2200         // I guess this should be zero ( and we should ensure that )
2201         sal_uInt16 cbExtra = 0;
2202         pDataStream->ReadUInt16( cbExtra );
2203 
2204         OSL_ENSURE(bAllOk, "Unknown formfield dropdown list structure");
2205         if (!bAllOk)    //Not as expected, don't risk it at all.
2206             nStringsCnt = 0;
2207         const size_t nMinRecordSize = sizeof(sal_uInt16);
2208         const size_t nMaxRecords = pDataStream->remainingSize() / nMinRecordSize;
2209         if (nStringsCnt > nMaxRecords)
2210         {
2211             SAL_WARN("sw.ww8", "Parsing error: " << nMaxRecords <<
2212                      " max possible entries, but " << nStringsCnt << " claimed, truncating");
2213             nStringsCnt = nMaxRecords;
2214         }
2215         maListEntries.reserve(nStringsCnt);
2216         for (sal_uInt32 nI = 0; nI < nStringsCnt; ++nI)
2217         {
2218             OUString sEntry =  read_uInt16_PascalString(*pDataStream);
2219             maListEntries.push_back(sEntry);
2220         }
2221     }
2222     mfDropdownIndex = iRes;
2223 
2224     mbHelp = bits1 & 0x80;
2225 
2226     nField = bits2;
2227     mfToolTip = nField & 0x01;
2228     mfNoMark = (nField & 0x02)>>1;
2229     mfType = (nField & 0x38)>>3;
2230     mfUnused = (nField & 0xE0)>>5;
2231 }
2232 
WW8FormulaListBox(SwWW8ImplReader & rR)2233 WW8FormulaListBox::WW8FormulaListBox(SwWW8ImplReader &rR)
2234     : WW8FormulaControl(SL::aListBox, rR)
2235 {
2236 }
2237 
2238 //Miserable hack to get a hardcoded guesstimate of the size of a list dropdown
2239 //box's first entry to set as the lists default size
MiserableDropDownFormHack(const OUString & rString,uno::Reference<beans::XPropertySet> const & rPropSet)2240 awt::Size SwWW8ImplReader::MiserableDropDownFormHack(const OUString &rString,
2241     uno::Reference<beans::XPropertySet> const & rPropSet)
2242 {
2243     awt::Size aRet;
2244     struct CtrlFontMapEntry
2245     {
2246         sal_uInt16 nWhichId;
2247         const sal_Char* pPropNm;
2248     };
2249     const CtrlFontMapEntry aMapTable[] =
2250     {
2251         { RES_CHRATR_COLOR,           "TextColor" },
2252         { RES_CHRATR_FONT,            "FontName" },
2253         { RES_CHRATR_FONTSIZE,        "FontHeight" },
2254         { RES_CHRATR_WEIGHT,          "FontWeight" },
2255         { RES_CHRATR_UNDERLINE,       "FontUnderline" },
2256         { RES_CHRATR_CROSSEDOUT,      "FontStrikeout" },
2257         { RES_CHRATR_POSTURE,         "FontSlant" },
2258         { 0,                          nullptr }
2259     };
2260 
2261     vcl::Font aFont;
2262     uno::Reference< beans::XPropertySetInfo > xPropSetInfo =
2263         rPropSet->getPropertySetInfo();
2264 
2265     uno::Any aTmp;
2266     for (const CtrlFontMapEntry* pMap = aMapTable; pMap->nWhichId; ++pMap)
2267     {
2268         bool bSet = true;
2269         const SfxPoolItem* pItem = GetFormatAttr( pMap->nWhichId );
2270         OSL_ENSURE(pItem, "Impossible");
2271         if (!pItem)
2272             continue;
2273 
2274         switch ( pMap->nWhichId )
2275         {
2276         case RES_CHRATR_COLOR:
2277             {
2278                 OUString aNm;
2279                 if (xPropSetInfo->hasPropertyByName(aNm = "TextColor"))
2280                 {
2281                     aTmp <<= static_cast<sal_Int32>(static_cast<const SvxColorItem*>(pItem)->GetValue());
2282                     rPropSet->setPropertyValue(aNm, aTmp);
2283                 }
2284             }
2285             aFont.SetColor(static_cast<const SvxColorItem*>(pItem)->GetValue());
2286             break;
2287         case RES_CHRATR_FONT:
2288             {
2289                 const SvxFontItem *pFontItem = static_cast<const SvxFontItem *>(pItem);
2290                 OUString aNm;
2291                 if (xPropSetInfo->hasPropertyByName(aNm = "FontStyleName"))
2292                 {
2293                     aTmp <<= pFontItem->GetStyleName();
2294                     rPropSet->setPropertyValue( aNm, aTmp );
2295                 }
2296                 if (xPropSetInfo->hasPropertyByName(aNm = "FontFamily"))
2297                 {
2298                     aTmp <<= static_cast<sal_Int16>(pFontItem->GetFamily());
2299                     rPropSet->setPropertyValue( aNm, aTmp );
2300                 }
2301                 if (xPropSetInfo->hasPropertyByName(aNm = "FontCharset"))
2302                 {
2303                     aTmp <<= static_cast<sal_Int16>(pFontItem->GetCharSet());
2304                     rPropSet->setPropertyValue( aNm, aTmp );
2305                 }
2306                 if (xPropSetInfo->hasPropertyByName(aNm = "FontPitch"))
2307                 {
2308                     aTmp <<= static_cast<sal_Int16>(pFontItem->GetPitch());
2309                     rPropSet->setPropertyValue( aNm, aTmp );
2310                 }
2311 
2312                 aTmp <<= pFontItem->GetFamilyName();
2313                 aFont.SetFamilyName( pFontItem->GetFamilyName() );
2314                 aFont.SetStyleName( pFontItem->GetStyleName() );
2315                 aFont.SetFamily( pFontItem->GetFamily() );
2316                 aFont.SetCharSet( pFontItem->GetCharSet() );
2317                 aFont.SetPitch( pFontItem->GetPitch() );
2318             }
2319             break;
2320 
2321         case RES_CHRATR_FONTSIZE:
2322             {
2323                 Size aSize( aFont.GetFontSize().Width(),
2324                             static_cast<const SvxFontHeightItem*>(pItem)->GetHeight() );
2325                 aTmp <<= static_cast<float>(aSize.Height()) / 20.0;
2326 
2327                 aFont.SetFontSize(OutputDevice::LogicToLogic(aSize,
2328                     MapMode(MapUnit::MapTwip), MapMode(MapUnit::Map100thMM)));
2329             }
2330             break;
2331 
2332         case RES_CHRATR_WEIGHT:
2333             aTmp <<= vcl::unohelper::ConvertFontWeight(
2334                                         static_cast<const SvxWeightItem*>(pItem)->GetWeight() );
2335             aFont.SetWeight( static_cast<const SvxWeightItem*>(pItem)->GetWeight() );
2336             break;
2337 
2338         case RES_CHRATR_UNDERLINE:
2339             aTmp <<= static_cast<sal_Int16>(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle());
2340             aFont.SetUnderline(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle());
2341             break;
2342 
2343         case RES_CHRATR_CROSSEDOUT:
2344             aTmp <<= static_cast<sal_Int16>( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() );
2345             aFont.SetStrikeout( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() );
2346             break;
2347 
2348         case RES_CHRATR_POSTURE:
2349             aTmp <<= static_cast<sal_Int16>( static_cast<const SvxPostureItem*>(pItem)->GetPosture() );
2350             aFont.SetItalic( static_cast<const SvxPostureItem*>(pItem)->GetPosture() );
2351             break;
2352 
2353         default:
2354             bSet = false;
2355             break;
2356         }
2357 
2358         if (bSet && xPropSetInfo->hasPropertyByName(OUString::createFromAscii(pMap->pPropNm)))
2359             rPropSet->setPropertyValue(OUString::createFromAscii(pMap->pPropNm), aTmp);
2360     }
2361     // now calculate the size of the control
2362     OutputDevice* pOut = Application::GetDefaultDevice();
2363     OSL_ENSURE(pOut, "Impossible");
2364     if (pOut)
2365     {
2366         pOut->Push( PushFlags::FONT | PushFlags::MAPMODE );
2367         pOut->SetMapMode( MapMode( MapUnit::Map100thMM ));
2368         pOut->SetFont( aFont );
2369         aRet.Width  = pOut->GetTextWidth(rString);
2370         aRet.Width += 500; //plus size of button, total hack territory
2371         aRet.Height = pOut->GetTextHeight();
2372         pOut->Pop();
2373     }
2374     return aRet;
2375 }
2376 
Import(const uno::Reference<lang::XMultiServiceFactory> & rServiceFactory,uno::Reference<form::XFormComponent> & rFComp,awt::Size & rSz)2377 bool WW8FormulaListBox::Import(const uno::Reference <
2378     lang::XMultiServiceFactory> &rServiceFactory,
2379     uno::Reference <form::XFormComponent> &rFComp,awt::Size &rSz )
2380 {
2381     uno::Reference<uno::XInterface> xCreate = rServiceFactory->createInstance("com.sun.star.form.component.ComboBox");
2382     if( !xCreate.is() )
2383         return false;
2384 
2385     rFComp.set(xCreate, uno::UNO_QUERY);
2386     if( !rFComp.is() )
2387         return false;
2388 
2389     uno::Reference<beans::XPropertySet> xPropSet(xCreate, uno::UNO_QUERY);
2390 
2391     uno::Any aTmp;
2392     if (!msTitle.isEmpty())
2393         aTmp <<= msTitle;
2394     else
2395         aTmp <<= msName;
2396     xPropSet->setPropertyValue("Name", aTmp );
2397 
2398     if (!msToolTip.isEmpty())
2399     {
2400         aTmp <<= msToolTip;
2401         xPropSet->setPropertyValue("HelpText", aTmp );
2402     }
2403 
2404     xPropSet->setPropertyValue("Dropdown", css::uno::makeAny(true));
2405 
2406     if (!maListEntries.empty())
2407     {
2408         sal_uInt32 nLen = maListEntries.size();
2409         uno::Sequence< OUString > aListSource(nLen);
2410         for (sal_uInt32 nI = 0; nI < nLen; ++nI)
2411             aListSource[nI] = maListEntries[nI];
2412         aTmp <<= aListSource;
2413         xPropSet->setPropertyValue("StringItemList", aTmp );
2414 
2415         if (mfDropdownIndex < nLen)
2416         {
2417             aTmp <<= aListSource[mfDropdownIndex];
2418         }
2419         else
2420         {
2421             aTmp <<= aListSource[0];
2422         }
2423 
2424         xPropSet->setPropertyValue("DefaultText", aTmp );
2425 
2426         rSz = mrRdr.MiserableDropDownFormHack(maListEntries[0], xPropSet);
2427     }
2428     else
2429     {
2430         static const sal_Unicode aBlank[] =
2431         {
2432             0x2002,0x2002,0x2002,0x2002,0x2002
2433         };
2434         rSz = mrRdr.MiserableDropDownFormHack(OUString(aBlank, SAL_N_ELEMENTS(aBlank)), xPropSet);
2435     }
2436 
2437     return true;
2438 }
2439 
WW8FormulaCheckBox(SwWW8ImplReader & rR)2440 WW8FormulaCheckBox::WW8FormulaCheckBox(SwWW8ImplReader &rR)
2441     : WW8FormulaControl(SL::aCheckBox, rR)
2442 {
2443 }
2444 
lcl_AddToPropertyContainer(uno::Reference<beans::XPropertySet> const & xPropSet,const OUString & rPropertyName,const OUString & rValue)2445 static void lcl_AddToPropertyContainer
2446 (uno::Reference<beans::XPropertySet> const & xPropSet,
2447  const OUString & rPropertyName, const OUString & rValue)
2448 {
2449     uno::Reference<beans::XPropertySetInfo> xPropSetInfo =
2450         xPropSet->getPropertySetInfo();
2451     if (xPropSetInfo.is() &&
2452         ! xPropSetInfo->hasPropertyByName(rPropertyName))
2453     {
2454         uno::Reference<beans::XPropertyContainer>
2455             xPropContainer(xPropSet, uno::UNO_QUERY);
2456         uno::Any aAny((OUString()));
2457         xPropContainer->addProperty
2458             (rPropertyName,
2459              static_cast<sal_Int16>(beans::PropertyAttribute::BOUND |
2460                                     beans::PropertyAttribute::REMOVABLE),
2461              aAny);
2462     }
2463 
2464     uno::Any aAnyValue(rValue);
2465     xPropSet->setPropertyValue(rPropertyName, aAnyValue );
2466 }
2467 
Import(const uno::Reference<lang::XMultiServiceFactory> & rServiceFactory,uno::Reference<form::XFormComponent> & rFComp,awt::Size & rSz)2468 bool WW8FormulaCheckBox::Import(const uno::Reference <
2469     lang::XMultiServiceFactory> &rServiceFactory,
2470     uno::Reference <form::XFormComponent> &rFComp,awt::Size &rSz )
2471 {
2472     uno::Reference< uno::XInterface > xCreate = rServiceFactory->createInstance("com.sun.star.form.component.CheckBox");
2473     if( !xCreate.is() )
2474         return false;
2475 
2476     rFComp.set( xCreate, uno::UNO_QUERY );
2477     if( !rFComp.is() )
2478         return false;
2479 
2480     uno::Reference< beans::XPropertySet > xPropSet( xCreate, uno::UNO_QUERY );
2481 
2482     rSz.Width = 16 * mhpsCheckBox;
2483     rSz.Height = 16 * mhpsCheckBox;
2484 
2485     uno::Any aTmp;
2486     if (!msTitle.isEmpty())
2487         aTmp <<= msTitle;
2488     else
2489         aTmp <<= msName;
2490     xPropSet->setPropertyValue("Name", aTmp );
2491 
2492     aTmp <<= static_cast<sal_Int16>(mnChecked);
2493     xPropSet->setPropertyValue("DefaultState", aTmp);
2494 
2495     if (!msToolTip.isEmpty())
2496         lcl_AddToPropertyContainer(xPropSet, "HelpText", msToolTip);
2497 
2498     if (!msHelp.isEmpty())
2499         lcl_AddToPropertyContainer(xPropSet, "HelpF1Text", msHelp);
2500 
2501     return true;
2502 
2503 }
2504 
WW8FormulaEditBox(SwWW8ImplReader & rR)2505 WW8FormulaEditBox::WW8FormulaEditBox(SwWW8ImplReader &rR)
2506     : WW8FormulaControl(SL::aTextField ,rR)
2507 {
2508 }
2509 
InsertControl(const uno::Reference<form::XFormComponent> & rFComp,const awt::Size & rSize,uno::Reference<drawing::XShape> * pShape,bool bFloatingCtrl)2510 bool SwMSConvertControls::InsertControl(
2511     const uno::Reference< form::XFormComponent > & rFComp,
2512     const awt::Size& rSize, uno::Reference< drawing::XShape > *pShape,
2513     bool bFloatingCtrl)
2514 {
2515     const uno::Reference< container::XIndexContainer > &rComps = GetFormComps();
2516     uno::Any aTmp( &rFComp, cppu::UnoType<form::XFormComponent>::get());
2517     rComps->insertByIndex( rComps->getCount(), aTmp );
2518 
2519     const uno::Reference< lang::XMultiServiceFactory > &rServiceFactory =
2520         GetServiceFactory();
2521     if( !rServiceFactory.is() )
2522         return false;
2523 
2524     uno::Reference< uno::XInterface > xCreate = rServiceFactory->createInstance(
2525         "com.sun.star.drawing.ControlShape");
2526     if( !xCreate.is() )
2527         return false;
2528 
2529     uno::Reference< drawing::XShape > xShape(xCreate, uno::UNO_QUERY);
2530 
2531     OSL_ENSURE(xShape.is(), "Did not get XShape");
2532     xShape->setSize(rSize);
2533 
2534     uno::Reference< beans::XPropertySet > xShapePropSet(
2535         xCreate, uno::UNO_QUERY );
2536 
2537     //I lay a small bet that this will change to
2538     //sal_Int16 nTemp=TextContentAnchorType::AS_CHARACTER;
2539     text::TextContentAnchorType nTemp;
2540     if (bFloatingCtrl)
2541         nTemp = text::TextContentAnchorType_AT_PARAGRAPH;
2542     else
2543         nTemp = text::TextContentAnchorType_AS_CHARACTER;
2544 
2545     xShapePropSet->setPropertyValue("AnchorType", uno::Any(static_cast<sal_Int16>(nTemp)) );
2546 
2547     xShapePropSet->setPropertyValue("VertOrient", uno::Any(sal_Int16(text::VertOrientation::TOP)) );
2548 
2549     uno::Reference< text::XText >  xDummyTextRef;
2550     uno::Reference< text::XTextRange >  xTextRg =
2551         new SwXTextRange( *pPaM, xDummyTextRef );
2552 
2553     aTmp <<= xTextRg;
2554     xShapePropSet->setPropertyValue("TextRange", aTmp );
2555 
2556     // Set the Control-Model for the Control-Shape
2557     uno::Reference< drawing::XControlShape >  xControlShape( xShape,
2558         uno::UNO_QUERY );
2559     uno::Reference< awt::XControlModel >  xControlModel( rFComp,
2560         uno::UNO_QUERY );
2561     xControlShape->setControl( xControlModel );
2562 
2563     if (pShape)
2564         *pShape = xShape;
2565 
2566     return true;
2567 }
2568 
2569 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2570