android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / filter / ww8 / ww8par3.cxx
blobb19b2859048ea9bdbbf6e08523a44bf8df97d3b8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <memory>
21 #include <string_view>
22 #include <svl/itemiter.hxx>
23 #include <vcl/svapp.hxx>
24 #include <vcl/outdev.hxx>
25 #include <sal/log.hxx>
27 #include <vcl/unohelp.hxx>
28 #include <com/sun/star/form/XFormComponent.hpp>
29 #include <com/sun/star/drawing/XShape.hpp>
30 #include <com/sun/star/drawing/XShapes.hpp>
31 #include <com/sun/star/drawing/XControlShape.hpp>
32 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
33 #include <com/sun/star/container/XIndexContainer.hpp>
34 #include <com/sun/star/text/VertOrientation.hpp>
35 #include <com/sun/star/text/TextContentAnchorType.hpp>
36 #include <com/sun/star/beans/XPropertyContainer.hpp>
37 #include <com/sun/star/beans/PropertyAttribute.hpp>
39 #include <algorithm>
40 #include <hintids.hxx>
41 #include <editeng/fontitem.hxx>
42 #include <editeng/lrspitem.hxx>
43 #include <editeng/fhgtitem.hxx>
44 #include <editeng/colritem.hxx>
45 #include <editeng/wghtitem.hxx>
46 #include <editeng/crossedoutitem.hxx>
47 #include <editeng/udlnitem.hxx>
48 #include <editeng/postitem.hxx>
49 #include <o3tl/safeint.hxx>
50 #include <o3tl/temporary.hxx>
51 #include <unotextrange.hxx>
52 #include <doc.hxx>
53 #include <docary.hxx>
54 #include <IDocumentFieldsAccess.hxx>
55 #include <IDocumentMarkAccess.hxx>
56 #include <docsh.hxx>
57 #include <numrule.hxx>
58 #include <paratr.hxx>
59 #include <charatr.hxx>
60 #include <charfmt.hxx>
61 #include <ndtxt.hxx>
62 #include <expfld.hxx>
63 #include <fmtfld.hxx>
64 #include <flddropdown.hxx>
65 #include "sprmids.hxx"
66 #include "writerhelper.hxx"
67 #include "writerwordglue.hxx"
68 #include "ww8par.hxx"
69 #include "ww8par2.hxx"
71 #include <IMark.hxx>
72 #include <unotools/fltrcfg.hxx>
73 #include <rtl/character.hxx>
74 #include <xmloff/odffields.hxx>
75 #include <comphelper/string.hxx>
77 using namespace com::sun::star;
78 using namespace sw::util;
79 using namespace sw::types;
80 using namespace sw::mark;
82 // UNO-Controls
84 // OCX i.e. word 97 form controls
85 eF_ResT SwWW8ImplReader::Read_F_OCX( WW8FieldDesc*, OUString& )
87 if( m_bObj && m_nPicLocFc )
88 m_nObjLocFc = m_nPicLocFc;
89 m_bEmbeddObj = true;
90 return eF_ResT::TEXT;
93 eF_ResT SwWW8ImplReader::Read_F_FormTextBox( WW8FieldDesc* pF, OUString& rStr )
95 WW8FormulaEditBox aFormula(*this);
97 sal_Int32 const nPos(rStr.indexOf(0x01));
98 if (pF->nLCode && nPos != -1 && nPos < pF->nLCode) {
99 ImportFormulaControl(aFormula, pF->nSCode + nPos, WW8_CT_EDIT);
103 Here we have a small complication. This formula control contains
104 the default text that is displayed if you edit the form field in
105 the "default text" area. But MSOffice does not display that
106 information, instead it display the result of the field,
107 MSOffice just uses the default text of the control as its
108 initial value for the displayed default text. So we will swap in
109 the field result into the formula here in place of the default
110 text.
113 const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
114 const bool bUseEnhFields = rOpt.IsUseEnhancedFields();
116 if (!bUseEnhFields)
118 aFormula.msDefault = GetFieldResult(pF);
120 SwInputField aField(
121 static_cast<SwInputFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Input )),
122 aFormula.msDefault,
123 aFormula.msTitle,
124 INP_TXT,
125 0 );
126 aField.SetHelp(aFormula.msHelp);
127 aField.SetToolTip(aFormula.msToolTip);
129 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
130 return eF_ResT::OK;
132 else
134 WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
135 OUString aBookmarkName;
136 if (pB!=nullptr) {
137 WW8_CP currentCP=pF->nSCode;
138 WW8_CP currentLen=pF->nLen;
140 WW8_CP nEnd;
141 if (o3tl::checked_add(currentCP, currentLen-1, nEnd)) {
142 SAL_WARN("sw.ww8", "broken offset, ignoring");
144 else
146 sal_uInt16 bkmFindIdx;
147 OUString aBookmarkFind=pB->GetBookmark(currentCP-1, nEnd, bkmFindIdx);
149 if (!aBookmarkFind.isEmpty()) {
150 pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark bookmark as consumed, such that it'll not get inserted as a "normal" bookmark again
151 if (!aBookmarkFind.isEmpty()) {
152 aBookmarkName=aBookmarkFind;
158 if (pB!=nullptr && aBookmarkName.isEmpty()) {
159 aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle);
162 if (!aBookmarkName.isEmpty()) {
163 m_aFieldStack.back().SetBookmarkName(aBookmarkName);
164 m_aFieldStack.back().SetBookmarkType(ODF_FORMTEXT);
165 if ( aFormula.msToolTip.getLength() < 139 )
166 m_aFieldStack.back().getParameters()["Description"] <<= aFormula.msToolTip;
167 m_aFieldStack.back().getParameters()["Name"] <<= aFormula.msTitle;
168 if (aFormula.mnMaxLen && aFormula.mnMaxLen < 32768 )
169 m_aFieldStack.back().getParameters()["MaxLength"] <<= aFormula.mnMaxLen;
171 if ( aFormula.mfType == 1 )
172 m_aFieldStack.back().getParameters()["Type"] <<= OUString("number");
173 else if ( aFormula.mfType == 2 )
174 m_aFieldStack.back().getParameters()["Type"] <<= OUString("date");
175 else if ( aFormula.mfType == 3 )
176 m_aFieldStack.back().getParameters()["Type"] <<= OUString("currentTime");
177 else if ( aFormula.mfType == 4 )
178 m_aFieldStack.back().getParameters()["Type"] <<= OUString("currentDate");
179 else if ( aFormula.mfType == 5 )
180 m_aFieldStack.back().getParameters()["Type"] <<= OUString("calculated");
182 return eF_ResT::TEXT;
186 eF_ResT SwWW8ImplReader::Read_F_FormCheckBox( WW8FieldDesc* pF, OUString& rStr )
188 WW8FormulaCheckBox aFormula(*this);
190 if (!m_xFormImpl)
191 m_xFormImpl.reset(new SwMSConvertControls(m_pDocShell, m_pPaM));
193 if (rStr[pF->nLCode-1]==0x01)
194 ImportFormulaControl(aFormula,pF->nSCode+pF->nLCode-1, WW8_CT_CHECKBOX);
195 const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
196 const bool bUseEnhFields = rOpt.IsUseEnhancedFields();
198 if (!bUseEnhFields)
200 m_xFormImpl->InsertFormula(aFormula);
201 return eF_ResT::OK;
204 OUString aBookmarkName;
205 WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
206 if (pB!=nullptr) {
207 WW8_CP currentCP=pF->nSCode;
208 WW8_CP currentLen=pF->nLen;
210 sal_uInt16 bkmFindIdx;
211 OUString aBookmarkFind=pB->GetBookmark(currentCP-1, currentCP+currentLen-1, bkmFindIdx);
213 if (!aBookmarkFind.isEmpty()) {
214 pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark as consumed by field
215 if (!aBookmarkFind.isEmpty()) {
216 aBookmarkName=aBookmarkFind;
221 if (pB!=nullptr && aBookmarkName.isEmpty()) {
222 aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle);
225 if (!aBookmarkName.isEmpty())
227 IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
228 IFieldmark* pFieldmark = pMarksAccess->makeNoTextFieldBookmark(
229 *m_pPaM, aBookmarkName, ODF_FORMCHECKBOX );
230 OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?");
231 if (pFieldmark!=nullptr) {
232 IFieldmark::parameter_map_t* const pParameters = pFieldmark->GetParameters();
233 ICheckboxFieldmark* pCheckboxFm = dynamic_cast<ICheckboxFieldmark*>(pFieldmark);
234 (*pParameters)[ODF_FORMCHECKBOX_HELPTEXT] <<= aFormula.msToolTip;
236 if(pCheckboxFm)
237 pCheckboxFm->SetChecked(aFormula.mnChecked != 0);
238 // set field data here...
241 return eF_ResT::OK;
244 eF_ResT SwWW8ImplReader::Read_F_FormListBox( WW8FieldDesc* pF, OUString& rStr)
246 WW8FormulaListBox aFormula(*this);
248 if (pF->nLCode > 0 && rStr.getLength() >= pF->nLCode && rStr[pF->nLCode-1] == 0x01)
249 ImportFormulaControl(aFormula,pF->nSCode+pF->nLCode-1, WW8_CT_DROPDOWN);
251 const SvtFilterOptions& rOpt = SvtFilterOptions::Get();
252 bool bUseEnhFields = rOpt.IsUseEnhancedFields();
254 if (!bUseEnhFields)
256 SwDropDownField aField(static_cast<SwDropDownFieldType*>(m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Dropdown)));
258 aField.SetName(aFormula.msTitle);
259 aField.SetHelp(aFormula.msHelp);
260 aField.SetToolTip(aFormula.msToolTip);
262 if (!aFormula.maListEntries.empty())
264 aField.SetItems(std::vector(aFormula.maListEntries));
265 int nIndex = aFormula.mfDropdownIndex < aFormula.maListEntries.size() ? aFormula.mfDropdownIndex : 0;
266 aField.SetSelectedItem(aFormula.maListEntries[nIndex]);
269 m_rDoc.getIDocumentContentOperations().InsertPoolItem(*m_pPaM, SwFormatField(aField));
270 return eF_ResT::OK;
272 else
274 // TODO: review me
275 OUString aBookmarkName;
276 WW8PLCFx_Book* pB = m_xPlcxMan->GetBook();
277 if (pB!=nullptr)
279 WW8_CP currentCP=pF->nSCode;
280 WW8_CP currentLen=pF->nLen;
282 sal_uInt16 bkmFindIdx;
283 OUString aBookmarkFind=pB->GetBookmark(currentCP-1, currentCP+currentLen-1, bkmFindIdx);
285 if (!aBookmarkFind.isEmpty())
287 pB->SetStatus(bkmFindIdx, BOOK_FIELD); // mark as consumed by field
288 if (!aBookmarkFind.isEmpty())
289 aBookmarkName=aBookmarkFind;
293 if (pB!=nullptr && aBookmarkName.isEmpty())
294 aBookmarkName=pB->GetUniqueBookmarkName(aFormula.msTitle);
296 if (!aBookmarkName.isEmpty())
298 IDocumentMarkAccess* pMarksAccess = m_rDoc.getIDocumentMarkAccess( );
299 IFieldmark *pFieldmark =
300 pMarksAccess->makeNoTextFieldBookmark( *m_pPaM, aBookmarkName, ODF_FORMDROPDOWN );
301 OSL_ENSURE(pFieldmark!=nullptr, "hmmm; why was the bookmark not created?");
302 if ( pFieldmark != nullptr )
304 uno::Sequence< OUString > vListEntries(aFormula.maListEntries.size());
305 std::copy(aFormula.maListEntries.begin(), aFormula.maListEntries.end(), vListEntries.getArray());
306 (*pFieldmark->GetParameters())[ODF_FORMDROPDOWN_LISTENTRY] <<= vListEntries;
307 sal_Int32 nIndex = aFormula.mfDropdownIndex < aFormula.maListEntries.size() ? aFormula.mfDropdownIndex : -1;
308 if (nIndex >= 0)
309 (*pFieldmark->GetParameters())[ODF_FORMDROPDOWN_RESULT] <<= nIndex;
310 // set field data here...
314 return eF_ResT::OK;
318 eF_ResT SwWW8ImplReader::Read_F_HTMLControl(WW8FieldDesc*, OUString&)
320 if( m_bObj && m_nPicLocFc )
321 m_nObjLocFc = m_nPicLocFc;
322 m_bEmbeddObj = true;
323 return eF_ResT::TEXT;
326 // Helper declarations
328 // Style Id's for each level
329 typedef sal_uInt16 WW8aIdSty[WW8ListManager::nMaxLevel];
330 // Character Style Pointer
331 typedef SwCharFormat* WW8aCFormat[WW8ListManager::nMaxLevel];
333 namespace {
335 struct WW8LST // only THOSE entries, WE need!
337 WW8aIdSty aIdSty; // Style Id's for each level,
338 // nIStDNil if no style linked
339 sal_uInt32 nIdLst; // Unique List ID
340 sal_uInt32 nTplC; // Unique template code - What is this?
341 bool bSimpleList:1; // Flag: List only has ONE level
342 bool bRestartHdn:1; // WW6-Compatibility-Flag:
343 // true if the list should start numbering over
344 }; // at the beginning of each section
348 const sal_uInt32 cbLSTF=28;
350 namespace {
352 struct WW8LFO // only THOSE entries, WE need!
354 SwNumRule* pNumRule; // Parent NumRule
355 sal_uInt32 nIdLst; // Unique List ID
356 sal_uInt8 nLfoLvl; // count of levels whose format is overridden
357 bool bSimpleList;
360 struct WW8LVL // only THE entries, WE need!
362 sal_Int32 nStartAt; // start at value for this value
363 sal_Int32 nV6DxaSpace;// Ver6-Compatible: min Space between Num and text::Paragraph
364 sal_Int32 nV6Indent; // Ver6-Compatible: Width of prefix text;
365 // Use definition of first line indent if appropriate!
366 // Paragraph attributes from GrpprlPapx
367 sal_uInt16 nDxaLeft; // left indent
368 short nDxaLeft1; // first line indent
370 sal_uInt8 nNFC; // number format code
371 // Offset of fieldcodes in Num-X-String
372 sal_uInt8 aOfsNumsXCH[WW8ListManager::nMaxLevel];
373 sal_uInt8 nLenGrpprlChpx; // length, in bytes, of the LVL's grpprlChpx
374 sal_uInt8 nLenGrpprlPapx; // length, in bytes, of the LVL's grpprlPapx
375 sal_uInt8 nAlign; // alignment (left, right, centered) of the number
376 bool bV6Prev; // Ver6-Compatible: number will include previous levels
377 bool bV6PrSp; // Ver6-Compatible: doesn't matter
378 bool bV6; // if true, pay attention to the V6-Compatible Entries!
382 struct WW8LFOLVL
384 sal_Int32 nStartAt; // start-at value if bFormat==false and bStartAt == true
385 // (if bFormat==true, the start-at is stored in the LVL)
386 sal_uInt8 nLevel; // the level to be overridden
387 // this byte has not been packed into the following byte on _purpose_ !!
388 // (see comment of struct WW8LFOInfo)
390 bool bStartAt :1; // true if the start-at value is overridden
391 bool bFormat :1; // true if the formatting is overridden
393 WW8LFOLVL() :
394 nStartAt(1), nLevel(0), bStartAt(true), bFormat(false) {}
399 // Data to be saved in ListInfo
401 struct WW8LSTInfo // sorted by nIdLst (in WW8 used list-Id)
403 std::vector<ww::bytes> maParaSprms;
404 WW8aIdSty aIdSty; // Style Id's for each level
405 WW8aCFormat aCharFormat = {}; // Character Style Pointer
407 SwNumRule* pNumRule; // Pointer to list-template in Writer
408 sal_uInt32 nIdLst; // WW8Id of this list
409 bool bSimpleList:1;// Flag, if this NumRule only uses one Level
410 bool bUsedInDoc :1;// Flag, if this NumRule is used in the Doc,
411 // or is supposed to be deleted on Reader-End
413 WW8LSTInfo(SwNumRule* pNumRule_, const WW8LST& aLST)
414 : pNumRule(pNumRule_), nIdLst(aLST.nIdLst),
415 bSimpleList(aLST.bSimpleList), bUsedInDoc(false)
417 memcpy( aIdSty, aLST.aIdSty, sizeof( aIdSty ));
422 // Data to be saved in ListenFormatOverrideInfos
424 struct WW8LFOInfo // unordered, means ordered like in WW8 Stream
426 std::vector<ww::bytes> maParaSprms;
427 std::vector<WW8LFOLVL> maOverrides;
428 SwNumRule* pNumRule; // Pointer to list template in Writer
429 // either List in LSTInfos or own List
430 // (in Ctor use the list from LSTInfos first)
432 sal_uInt32 nIdLst; // WW8-Id of the relevant list
433 sal_uInt8 nLfoLvl; // count of levels whose format is overridden
434 // yes we could include nLfoLvl (via :4) into the following byte,
435 // but it probably would be a source of error once MS increases their Listformat
436 // to more than 15 levels
438 bool bOverride :1;// Flag if NumRule is not included in maLSTInfos,
439 // but was created for m_LFOInfos
440 bool bUsedInDoc :1;// Flag if NumRule is used in Doc,
441 // or should be deleted on Reader-End
442 bool bLSTbUIDSet :1;// Flag, if bUsedInDoc is set in maLSTInfos
444 explicit WW8LFOInfo(const WW8LFO& rLFO);
447 WW8LFOInfo::WW8LFOInfo(const WW8LFO& rLFO)
448 : maParaSprms(WW8ListManager::nMaxLevel)
449 , maOverrides(WW8ListManager::nMaxLevel)
450 , pNumRule(rLFO.pNumRule)
451 , nIdLst(rLFO.nIdLst)
452 , nLfoLvl(rLFO.nLfoLvl)
453 , bOverride(rLFO.nLfoLvl != 0)
454 , bUsedInDoc(false)
455 , bLSTbUIDSet(false)
459 // Helper methods
461 // find Sprm-Parameter-Data, if Sprm is included in Grpprl
462 SprmResult WW8ListManager::GrpprlHasSprm(sal_uInt16 nId, sal_uInt8& rSprms,
463 sal_uInt8 nLen)
465 return maSprmParser.findSprmData(nId, &rSprms, nLen);
468 namespace {
470 class ListWithId
472 private:
473 sal_uInt32 mnIdLst;
474 public:
475 explicit ListWithId(sal_uInt32 nIdLst) : mnIdLst(nIdLst) {}
476 bool operator() (const std::unique_ptr<WW8LSTInfo>& pEntry) const
477 { return (pEntry->nIdLst == mnIdLst); }
482 // Access via List-Id of LST Entry
483 WW8LSTInfo* WW8ListManager::GetLSTByListId( sal_uInt32 nIdLst ) const
485 auto aResult =
486 std::find_if(maLSTInfos.begin(),maLSTInfos.end(),ListWithId(nIdLst));
487 if (aResult == maLSTInfos.end())
488 return nullptr;
489 return aResult->get();
492 SvxNumType WW8ListManager::GetSvxNumTypeFromMSONFC(sal_uInt16 nNFC)
494 SvxNumType nType(SVX_NUM_ARABIC);
496 switch (nNFC)
498 case 0:
499 nType = SVX_NUM_ARABIC;
500 break;
501 case 1:
502 nType = SVX_NUM_ROMAN_UPPER;
503 break;
504 case 2:
505 nType = SVX_NUM_ROMAN_LOWER;
506 break;
507 case 3:
508 nType = SVX_NUM_CHARS_UPPER_LETTER_N;
509 break;
510 case 4:
511 nType = SVX_NUM_CHARS_LOWER_LETTER_N;
512 break;
513 case 5: // ordinal
514 nType = SVX_NUM_TEXT_NUMBER;
515 break;
516 case 6: // cardinalText
517 nType = SVX_NUM_TEXT_CARDINAL;
518 break;
519 case 7: // ordinalText
520 nType = SVX_NUM_TEXT_ORDINAL;
521 break;
522 //case 8: // hex
524 case 9:
525 // 0x09, msonfcChiManSty
526 nType = SVX_NUM_SYMBOL_CHICAGO;
527 break;
528 //case 15: // decimalHalfWidth
529 //case 17: // japaneseDigitalTenThousand
531 case 18: // decimalEnclosedCircle
532 case 28: // decimalEnclosedCircleChinese
533 case 29: // ideographEnclosedCircle
534 nType = SVX_NUM_CIRCLE_NUMBER;
535 break;
536 case 22:
537 // 0x16, msonfcArabicLZ
538 nType = SVX_NUM_ARABIC_ZERO;
539 break;
540 case 23:
541 nType = SVX_NUM_CHAR_SPECIAL;
543 break;
544 case 255:
545 nType = SVX_NUM_NUMBER_NONE;
546 break;
547 case 14:
548 case 19:
549 nType = SVX_NUM_FULL_WIDTH_ARABIC;
550 break;
551 case 30:
552 nType = SVX_NUM_TIAN_GAN_ZH;
553 break;
554 case 31: // ideographZodiac
555 case 32: // ideographZodiacTraditional
556 nType = SVX_NUM_DI_ZI_ZH;
557 break;
558 case 33: // taiwaneseCounting
559 case 35:
560 case 36:
561 case 37:
562 case 11:
563 case 39:
564 nType = SVX_NUM_NUMBER_LOWER_ZH;
565 break;
566 case 34:
567 nType = SVX_NUM_NUMBER_UPPER_ZH_TW;
568 break;
569 case 38:
570 nType = SVX_NUM_NUMBER_UPPER_ZH;
571 break;
572 case 10:
573 case 16: // japaneseLegal
574 nType = SVX_NUM_NUMBER_TRADITIONAL_JA;
575 break;
576 case 20:
577 nType = SVX_NUM_AIU_FULLWIDTH_JA;
578 break;
579 case 12:
580 nType = SVX_NUM_AIU_HALFWIDTH_JA;
581 break;
582 case 21:
583 nType = SVX_NUM_IROHA_FULLWIDTH_JA;
584 break;
585 case 13:
586 nType = SVX_NUM_IROHA_HALFWIDTH_JA;
587 break;
588 case 24:
589 nType = SVX_NUM_HANGUL_SYLLABLE_KO;
590 break;
591 case 25:
592 nType = SVX_NUM_HANGUL_JAMO_KO;
593 break;
594 //case 26: // decimalEnclosedFullstop
595 //case 27: // decimalEnclosedParen
596 //case 40: // decimal (Chinese)
598 case 41: // koreanDigital
599 case 42: // koreanCounting
600 case 43: // koreanLegal
601 nType = SVX_NUM_NUMBER_HANGUL_KO;
602 break;
603 case 44: // koreanDigital2
604 nType = SVX_NUM_NUMBER_UPPER_KO;
605 break;
606 case 45: // hebrew1
607 nType = SVX_NUM_NUMBER_HEBREW;
608 break;
609 case 46: // arabicAlpha
610 nType = SVX_NUM_CHARS_ARABIC;
611 break;
612 case 47: // hebrew2
613 nType = SVX_NUM_CHARS_HEBREW;
614 break;
615 case 48: // arabicAbjad
616 nType = SVX_NUM_CHARS_ARABIC_ABJAD;
617 break;
618 case 49: // hindiVowels
619 nType = SVX_NUM_CHARS_NEPALI;
620 break;
621 //case 50: // hindiConsonants
622 //case 51: // hindiNumbers
623 //case 52: // hindiCounting
625 case 53: // thaiLetters
626 nType = SVX_NUM_CHARS_THAI;
627 break;
628 //case 54: // thaiNumbers
629 //case 55: // thaiCounting
630 //case 56: // vietnameseCounting
631 //case 57: // numberInDash
633 case 58: // russianLower
634 nType = SVX_NUM_CHARS_CYRILLIC_LOWER_LETTER_RU;
635 break;
636 case 59: // russianUpper
637 nType =SVX_NUM_CHARS_CYRILLIC_UPPER_LETTER_RU;
638 break;
639 default:
640 nType = SVX_NUM_ARABIC;
641 break;
644 return nType;
647 bool WW8ListManager::ReadLVL(SwNumFormat& rNumFormat, std::unique_ptr<SfxItemSet>& rpItemSet,
648 sal_uInt16 nLevelStyle, bool bSetStartNo, sal_uInt16 /*nLevel*/, ww::bytes &rParaSprms)
650 sal_uInt8 aBits1(0);
651 SvxNumType nType(SVX_NUM_ARABIC);
652 SvxAdjust eAdj; // Alignment (Left/right/centered)
653 sal_UCS4 cBullet(0x2190); // default safe bullet
655 sal_Unicode cGrfBulletCP(USHRT_MAX);
657 WW8LVL aLVL = {};
659 // 1. read LVLF
661 m_rSt.ReadInt32( aLVL.nStartAt );
662 m_rSt.ReadUChar( aLVL.nNFC );
663 m_rSt.ReadUChar( aBits1 );
664 if( ERRCODE_NONE != m_rSt.GetError() ) return false;
665 aLVL.nAlign = (aBits1 & 0x03);
666 if( aBits1 & 0x10 ) aLVL.bV6Prev = true;
667 if( aBits1 & 0x20 ) aLVL.bV6PrSp = true;
668 if( aBits1 & 0x40 ) aLVL.bV6 = true;
669 bool bLVLOkB = true;
670 for(sal_uInt8 nLevelB = 0; nLevelB < nMaxLevel; ++nLevelB)
672 m_rSt.ReadUChar( aLVL.aOfsNumsXCH[ nLevelB ] );
673 if( ERRCODE_NONE != m_rSt.GetError() )
675 bLVLOkB = false;
676 break;
680 if( !bLVLOkB )
681 return false;
683 sal_uInt8 ixchFollow(0);
684 m_rSt.ReadUChar( ixchFollow );
685 m_rSt.ReadInt32( aLVL.nV6DxaSpace );
686 m_rSt.ReadInt32( aLVL.nV6Indent );
687 m_rSt.ReadUChar( aLVL.nLenGrpprlChpx );
688 m_rSt.ReadUChar( aLVL.nLenGrpprlPapx );
689 m_rSt.SeekRel( 2 );
690 if( ERRCODE_NONE != m_rSt.GetError()) return false;
692 // 2. read PAPx if needed and search for indent values
694 short nTabPos = 0; // #i86652# - read tab setting
695 if( aLVL.nLenGrpprlPapx )
697 sal_uInt8 aGrpprlPapx[ 255 ];
698 if (aLVL.nLenGrpprlPapx != m_rSt.ReadBytes(&aGrpprlPapx, aLVL.nLenGrpprlPapx))
699 return false;
700 // "sprmPDxaLeft" pap.dxaLeft;dxa;word;
701 SprmResult aSprm = GrpprlHasSprm(0x840F,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
702 if (!aSprm.pSprm)
703 aSprm = GrpprlHasSprm(0x845E,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
705 if (aSprm.pSprm && aSprm.nRemainingData >= 2)
707 const sal_uInt8 *pBegin = aSprm.pSprm - 2;
708 for(int i=0;i<4;++i)
709 rParaSprms.push_back(*pBegin++);
710 short nDxaLeft = SVBT16ToUInt16(aSprm.pSprm);
711 aLVL.nDxaLeft = (0 < nDxaLeft) ? o3tl::narrowing<sal_uInt16>(nDxaLeft)
712 : o3tl::narrowing<sal_uInt16>(-nDxaLeft);
715 // "sprmPDxaLeft1" pap.dxaLeft1;dxa;word;
716 aSprm = GrpprlHasSprm(0x8411,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
717 if (!aSprm.pSprm)
718 aSprm = GrpprlHasSprm(0x8460,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
720 if (aSprm.pSprm && aSprm.nRemainingData >= 2)
722 const sal_uInt8 *pBegin = aSprm.pSprm - 2;
723 for(int i=0;i<4;++i)
724 rParaSprms.push_back(*pBegin++);
725 aLVL.nDxaLeft1 = SVBT16ToUInt16(aSprm.pSprm);
728 // #i86652# - read tab setting
729 aSprm = GrpprlHasSprm(0xC615,aGrpprlPapx[0],aLVL.nLenGrpprlPapx);
730 const sal_uInt8* pSprm = aSprm.pSprm;
731 if (pSprm && aSprm.nRemainingData >= 5)
733 bool bDone = false;
734 if (*(pSprm-1) == 5)
736 if (*pSprm++ == 0) //nDel
738 if (*pSprm++ == 1) //nIns
740 nTabPos = SVBT16ToUInt16(pSprm);
741 pSprm+=2;
742 if (*pSprm == 6) //type
744 bDone = true;
749 OSL_ENSURE(bDone, "tab setting in numbering is "
750 "of unexpected configuration");
752 if ( rNumFormat.GetPositionAndSpaceMode() ==
753 SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
755 // If there is a tab setting with a larger value, then use that.
756 // Ideally we would allow tabs to be used in numbering fields and set
757 // this on the containing paragraph which would make it actually work
758 // most of the time.
759 if ( nTabPos != 0 )
761 const sal_uInt16 nDesired = aLVL.nDxaLeft + aLVL.nDxaLeft1;
763 bool bDoAdjust = false;
764 if ( nDesired < aLVL.nDxaLeft )
766 if ( nDesired < nTabPos && nTabPos < aLVL.nDxaLeft )
768 bDoAdjust = true;
771 else
773 if ( aLVL.nDxaLeft < nTabPos && nTabPos < nDesired )
775 bDoAdjust = true;
779 if (bDoAdjust)
781 aLVL.nDxaLeft = (0 < nTabPos)
782 ? o3tl::narrowing<sal_uInt16>(nTabPos)
783 : o3tl::narrowing<sal_uInt16>(-nTabPos);
785 aLVL.nDxaLeft1 = nDesired - aLVL.nDxaLeft;
791 // 3. read CHPx if needed
793 sal_uInt16 nWitchPicIsBullet = USHRT_MAX;
794 bool bIsPicBullet = false;
796 if( aLVL.nLenGrpprlChpx )
798 sal_uInt8 aGrpprlChpx[ 255 ] = {};
799 if (aLVL.nLenGrpprlChpx != m_rSt.ReadBytes(&aGrpprlChpx, aLVL.nLenGrpprlChpx))
800 return false;
802 //For i120928,parse the graphic info of bullets
803 SprmResult aSprmWhichPis = GrpprlHasSprm(NS_sprm::CPbiIBullet::val, aGrpprlChpx[0],aLVL.nLenGrpprlChpx);
804 SprmResult aSprmIsPicBullet = GrpprlHasSprm(NS_sprm::CPbiGrf::val, aGrpprlChpx[0],aLVL.nLenGrpprlChpx);
805 if (aSprmWhichPis.pSprm && aSprmWhichPis.nRemainingData >= 1)
807 nWitchPicIsBullet = *aSprmWhichPis.pSprm;
809 if (aSprmIsPicBullet.pSprm && aSprmIsPicBullet.nRemainingData >= 1)
811 bIsPicBullet = (*aSprmIsPicBullet.pSprm) & 0x0001;
814 // create new Itemset for character attributes
815 rpItemSet.reset(new SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>( m_rDoc.GetAttrPool() ));
817 // Set Reader-ItemSet-Pointer to the newly created set
818 m_rReader.SetCurrentItemSet(std::move(rpItemSet));
819 // Set Reader-Style to Style of this Level
820 sal_uInt16 nOldColl = m_rReader.GetCurrentColl();
821 sal_uInt16 nNewColl = nLevelStyle;
822 if (ww::stiNil == nNewColl)
823 nNewColl = 0;
824 m_rReader.SetNCurrentColl( nNewColl );
826 // The Read_xy() methods in WW8PAR6.cxx are calling their respective
827 // NewAttr() or GetFormatAttr() which can determine, by using the assigned
828 // Reader-ItemSet-Pointer, whether this specific ItemSet is relevant
829 // and not a Stack or Style!
830 sal_uInt16 nOldFlags1 = m_rReader.GetToggleAttrFlags();
831 sal_uInt16 nOldFlags2 = m_rReader.GetToggleBiDiAttrFlags();
833 WW8SprmIter aSprmIter(&aGrpprlChpx[0], aLVL.nLenGrpprlChpx,
834 maSprmParser);
835 while (const sal_uInt8* pSprm = aSprmIter.GetSprms())
837 m_rReader.ImportSprm(pSprm, aSprmIter.GetRemLen(), aSprmIter.GetCurrentId());
838 aSprmIter.advance();
841 // Reset Reader-ItemSet-Pointer and Reader-Style
842 rpItemSet = m_rReader.SetCurrentItemSet(nullptr);
843 m_rReader.SetNCurrentColl( nOldColl );
844 m_rReader.SetToggleAttrFlags(nOldFlags1);
845 m_rReader.SetToggleBiDiAttrFlags(nOldFlags2);
848 // 4. Read numbering String. Results in prefix and postfix
850 OUString sNumString(comphelper::string::sanitizeStringSurrogates(read_uInt16_PascalString(m_rSt)));
852 // 5. convert read values into Writer syntax
854 nType = GetSvxNumTypeFromMSONFC(aLVL.nNFC);
855 //For i120928,type info
856 if (bIsPicBullet)
858 nType = SVX_NUM_BITMAP;
861 if (style::NumberingType::CHAR_SPECIAL == nType)
863 cBullet = !sNumString.isEmpty()
864 ? sNumString.iterateCodePoints(&o3tl::temporary(sal_Int32(0))) : 0x2190;
866 if (!cBullet) // unsave control code?
867 cBullet = 0x2190;
869 else if (style::NumberingType::BITMAP == nType) //For i120928,position index info of graphic
871 cGrfBulletCP = nWitchPicIsBullet; // This is a bullet picture ID
874 switch( aLVL.nAlign )
876 case 0:
877 eAdj = SvxAdjust::Left;
878 break;
879 case 1:
880 eAdj = SvxAdjust::Center;
881 break;
882 case 2:
883 eAdj = SvxAdjust::Right;
884 break;
885 case 3:
886 // Writer here cannot do block justification
887 eAdj = SvxAdjust::Left;
888 break;
889 default:
890 // undefined value
891 OSL_ENSURE( false, "Value of aLVL.nAlign is not supported" );
892 // take default
893 eAdj = SvxAdjust::Left;
894 break;
897 // 6. Configure NumFormat
898 if( bSetStartNo && 0 <= aLVL.nStartAt)
899 rNumFormat.SetStart(o3tl::narrowing<sal_uInt16>(aLVL.nStartAt));
900 rNumFormat.SetNumberingType( nType );
901 rNumFormat.SetNumAdjust( eAdj );
903 if( style::NumberingType::CHAR_SPECIAL == nType )
905 // first character of the Prefix-Text is the Bullet
906 rNumFormat.SetBulletChar(cBullet);
907 // Don't forget: further below, after building styles
908 // Call SetBulletFont() !!!
910 //For i120928,position index info
911 else if (style::NumberingType::BITMAP == nType)
913 rNumFormat.SetGrfBulletCP(cGrfBulletCP);
915 else
917 // Replace symbols at aOfsNumsXCH offsets to %1%, %2% as supported by LO
918 OUString sListFormat = sNumString;
919 if (sListFormat.getLength())
921 sal_uInt32 nExtraOffset = 0;
922 sal_uInt8 nLevelB = 0;
923 while (nLevelB < nMaxLevel && aLVL.aOfsNumsXCH[nLevelB])
925 // Replacement symbol is read from source string from position taken from aOfsNumsXCH array
926 sal_uInt8 nOffset = aLVL.aOfsNumsXCH[nLevelB] + nExtraOffset - 1;
927 if (nOffset >= sListFormat.getLength())
929 SAL_WARN("sw.ww8", "List level reference is beyond the border. Ignored.");
930 nLevelB++;
931 continue;
933 sal_uInt8 nReplacement = sListFormat[nOffset] + 1;
935 OUString sReplacement("%" + OUString::number(nReplacement) + "%");
936 sListFormat = sListFormat.replaceAt(nOffset, 1, sReplacement);
938 // We need also update an offset, since we are replacing one symbol by at least two
939 nExtraOffset += sReplacement.getLength() - 1;
940 nLevelB++;
944 rNumFormat.SetListFormat(sListFormat);
946 // Total count of replacement holders is determining amount of required parent numbering to include
947 // TODO: not sure how "%" symbol is escaped. This is not supported yet
948 sal_Int16 nParentNum = comphelper::string::getTokenCount(sListFormat, '%');
949 rNumFormat.SetIncludeUpperLevels(nParentNum);
952 // #i89181#
953 if ( rNumFormat.GetPositionAndSpaceMode() ==
954 SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
956 if (eAdj == SvxAdjust::Right)
958 rNumFormat.SetAbsLSpace(aLVL.nDxaLeft);
959 rNumFormat.SetFirstLineOffset(-aLVL.nDxaLeft);
960 rNumFormat.SetCharTextDistance(-aLVL.nDxaLeft1);
962 else
964 rNumFormat.SetAbsLSpace( aLVL.nDxaLeft );
965 rNumFormat.SetFirstLineOffset(aLVL.nDxaLeft1);
968 else
970 rNumFormat.SetIndentAt( aLVL.nDxaLeft );
971 rNumFormat.SetFirstLineIndent(aLVL.nDxaLeft1);
972 if ( !aLVL.bV6 )
973 rNumFormat.SetListtabPos( nTabPos );
974 else
975 rNumFormat.SetListtabPos( aLVL.nV6Indent );
976 SvxNumberFormat::LabelFollowedBy eNumLabelFollowedBy = SvxNumberFormat::LISTTAB;
977 switch ( ixchFollow )
979 case 0:
981 eNumLabelFollowedBy = SvxNumberFormat::LISTTAB;
983 break;
984 case 1:
986 eNumLabelFollowedBy = SvxNumberFormat::SPACE;
988 break;
989 case 2:
991 eNumLabelFollowedBy = SvxNumberFormat::NOTHING;
993 break;
995 rNumFormat.SetLabelFollowedBy( eNumLabelFollowedBy );
998 return true;
1001 void WW8ListManager::AdjustLVL( sal_uInt8 nLevel, SwNumRule& rNumRule,
1002 WW8aISet const & rListItemSet, WW8aCFormat& rCharFormat, bool& bNewCharFormatCreated,
1003 const OUString& sPrefix )
1005 bNewCharFormatCreated = false;
1006 sal_uInt8 nIdenticalItemSetLevel;
1007 const SfxPoolItem* pItem;
1009 SwNumFormat aNumFormat = rNumRule.Get( nLevel );
1011 SfxItemSet* pThisLevelItemSet = rListItemSet[nLevel].get();
1013 if( pThisLevelItemSet && pThisLevelItemSet->Count())
1015 nIdenticalItemSetLevel = nMaxLevel;
1016 SfxItemIter aIter( *pThisLevelItemSet );
1017 for (sal_uInt8 nLowerLevel = 0; nLowerLevel < nLevel; ++nLowerLevel)
1019 SfxItemSet* pLowerLevelItemSet = rListItemSet[nLowerLevel].get();
1020 if( pLowerLevelItemSet
1021 && (pLowerLevelItemSet->Count() == pThisLevelItemSet->Count()) )
1023 nIdenticalItemSetLevel = nLowerLevel;
1024 const SfxPoolItem* pItemIter = aIter.GetCurItem();
1027 if( // search for appropriate pItem in pLowerLevelItemSet
1028 (SfxItemState::SET != pLowerLevelItemSet->GetItemState(
1029 pItemIter->Which(), false, &pItem ) )
1030 || // use virtual "!=" Operator
1031 (*pItem != *pItemIter) )
1032 // if no Item with equal nWhich was found or Item value was not equal
1033 // store inequality and break!
1035 nIdenticalItemSetLevel = nMaxLevel;
1036 break;
1038 pItemIter = aIter.NextItem();
1039 } while (pItemIter);
1041 if( nIdenticalItemSetLevel != nMaxLevel )
1042 break;
1046 SwCharFormat* pFormat;
1047 if (nMaxLevel == nIdenticalItemSetLevel)
1049 // Define Style
1050 const OUString aName( (!sPrefix.isEmpty() ? sPrefix : rNumRule.GetName())
1051 + "z" + OUString::number( nLevel ) );
1053 // remove const by casting
1054 pFormat = m_rDoc.MakeCharFormat(aName, m_rDoc.GetDfltCharFormat());
1055 bNewCharFormatCreated = true;
1056 // Set Attributes
1057 pFormat->SetFormatAttr( *pThisLevelItemSet );
1059 else
1061 // append Style
1062 pFormat = rCharFormat[ nIdenticalItemSetLevel ];
1065 // store
1066 rCharFormat[ nLevel ] = pFormat;
1068 // Append Style to NumFormat
1070 aNumFormat.SetCharFormat( pFormat );
1073 // if necessary: Append Bullet Font to NumFormat
1075 if( SVX_NUM_CHAR_SPECIAL == aNumFormat.GetNumberingType() )
1077 SwCharFormat* pFormat = aNumFormat.GetCharFormat();
1078 vcl::Font aFont;
1079 if( !pFormat )
1081 aFont = numfunc::GetDefBulletFont();
1083 else
1085 const SvxFontItem& rFontItem = pFormat->GetFont();
1086 aFont.SetFamily( rFontItem.GetFamily() );
1087 aFont.SetFamilyName( rFontItem.GetFamilyName() );
1088 aFont.SetStyleName( rFontItem.GetStyleName() );
1089 aFont.SetPitch( rFontItem.GetPitch() );
1090 aFont.SetCharSet( rFontItem.GetCharSet() );
1092 aNumFormat.SetBulletFont( &aFont );
1095 // Set NumFormat in NumRule
1097 rNumRule.Set(nLevel, aNumFormat);
1100 SwNumRule* WW8ListManager::CreateNextRule(bool bSimple)
1102 // Used to build the Style Name
1103 const OUString sPrefix("WW8Num" + OUString::number(m_nUniqueList++));
1104 // #i86652#
1105 sal_uInt16 nRul =
1106 m_rDoc.MakeNumRule( m_rDoc.GetUniqueNumRuleName(&sPrefix), nullptr, false,
1107 SvxNumberFormat::LABEL_ALIGNMENT );
1108 SwNumRule* pMyNumRule = m_rDoc.GetNumRuleTable()[nRul];
1109 pMyNumRule->SetAutoRule(false);
1110 pMyNumRule->SetContinusNum(bSimple);
1111 return pMyNumRule;
1114 SwNumRule* WW8ListManager::GetNumRule(size_t i)
1116 if (i < maLSTInfos.size())
1117 return maLSTInfos[i]->pNumRule;
1118 else
1119 return nullptr;
1122 // public methods
1124 WW8ListManager::WW8ListManager(SvStream& rSt_, SwWW8ImplReader& rReader_)
1125 : maSprmParser(rReader_.GetFib()), m_rReader(rReader_)
1126 , m_rDoc(m_rReader.GetDoc())
1127 , m_rFib(m_rReader.GetFib()), m_rSt(rSt_)
1128 , m_nUniqueList(1)
1129 , m_nLastLFOPosition(USHRT_MAX)
1132 // LST and LFO only since WW8
1133 if( ( 8 > m_rFib.m_nVersion )
1134 || ( m_rFib.m_fcPlcfLst == m_rFib.m_fcPlfLfo )
1135 || ( m_rFib.m_lcbPlcfLst < 2 )
1136 || ( m_rFib.m_lcbPlfLfo < 2) ) return; // no public lists
1138 // create Arrays
1139 bool bLVLOk = true;
1141 sal_uInt64 nOriginalPos = m_rSt.Tell();
1143 // 1. read PLCF LST and create list templates in Writer
1145 bool bOk = checkSeek(m_rSt, m_rFib.m_fcPlcfLst);
1147 if (!bOk)
1148 return;
1150 sal_uInt32 nRemainingPlcfLst = m_rFib.m_lcbPlcfLst;
1152 sal_uInt16 nListCount(0);
1153 m_rSt.ReadUInt16( nListCount );
1154 nRemainingPlcfLst -= 2;
1155 bOk = nListCount > 0;
1157 if (!bOk)
1158 return;
1160 // 1.1 read all LST
1161 const size_t nMinRecordSize = 10 + 2*nMaxLevel;
1162 const size_t nMaxRecords = m_rSt.remainingSize() / nMinRecordSize;
1163 if (nListCount > nMaxRecords)
1165 SAL_WARN("sw.ww8", "Parsing error: " << nMaxRecords <<
1166 " max possible entries, but " << nListCount << " claimed, truncating");
1167 nListCount = nMaxRecords;
1169 for (sal_uInt16 nList=0; nList < nListCount; ++nList)
1171 if (nRemainingPlcfLst < cbLSTF)
1172 break;
1174 WW8LST aLST = {};
1176 // 1.1.1 read Data
1178 m_rSt.ReadUInt32( aLST.nIdLst );
1179 m_rSt.ReadUInt32( aLST.nTplC );
1180 for (sal_uInt16 & nLevel : aLST.aIdSty)
1181 m_rSt.ReadUInt16( nLevel );
1183 sal_uInt8 aBits1(0);
1184 m_rSt.ReadUChar( aBits1 );
1186 m_rSt.SeekRel( 1 );
1188 if( aBits1 & 0x01 )
1189 aLST.bSimpleList = true;
1190 if( aBits1 & 0x02 )
1191 aLST.bRestartHdn = true;
1193 // 1.1.2 new NumRule inserted in Doc and WW8LSTInfo marked
1196 #i1869#
1197 In word 2000 microsoft got rid of creating new "simple lists" with
1198 only 1 level, all new lists are created with 9 levels. To hack it
1199 so that the list types formerly known as simple lists still have
1200 their own tab page to themselves one of the reserved bits is used
1201 to show that a given list is to be in the simple list tabpage.
1202 This has now nothing to do with the actual number of list level a
1203 list has, only how many will be shown in the user interface.
1205 i.e. create a simple list in 2000 and open it in 97 and 97 will
1206 claim (correctly) that it is an outline list. We can set our
1207 continuous flag in these lists to store this information.
1209 SwNumRule* pMyNumRule = CreateNextRule(
1210 aLST.bSimpleList || (aBits1 & 0x10));
1212 WW8LSTInfo* pLSTInfo = new WW8LSTInfo(pMyNumRule, aLST);
1213 maLSTInfos.emplace_back(pLSTInfo);
1215 nRemainingPlcfLst -= cbLSTF;
1218 // 1.2 read all LVL of all aLST
1220 sal_uInt16 nLSTInfos = static_cast< sal_uInt16 >(maLSTInfos.size());
1221 for (sal_uInt16 nList = 0; nList < nLSTInfos; ++nList)
1223 WW8aISet aItemSet; // Character attributes from GrpprlChpx
1225 WW8LSTInfo* pListInfo = maLSTInfos[nList].get();
1226 if( !pListInfo || !pListInfo->pNumRule ) break;
1227 SwNumRule& rMyNumRule = *pListInfo->pNumRule;
1229 // 1.2.1 read specific LVL(s) for this aLST
1231 sal_uInt16 nLvlCount = static_cast< sal_uInt16 >(pListInfo->bSimpleList ? nMinLevel : nMaxLevel);
1232 pListInfo->maParaSprms.resize(nMaxLevel);
1233 for (sal_uInt16 nLevel = 0; nLevel < nLvlCount; ++nLevel)
1235 SwNumFormat aNumFormat( rMyNumRule.Get( nLevel ) );
1236 // read LVLF
1237 bLVLOk = ReadLVL( aNumFormat, aItemSet[nLevel],
1238 pListInfo->aIdSty[nLevel], true, nLevel,
1239 pListInfo->maParaSprms[nLevel]);
1240 if( !bLVLOk )
1241 break;
1242 // and set in rMyNumRule
1243 rMyNumRule.Set( nLevel, aNumFormat );
1245 if( !bLVLOk )
1246 break;
1248 // 1.2.2 compare ItemPools and CHPx Settings of different Levels
1249 // and create Style(s) if necessary
1251 for (sal_uInt16 nLevel = 0; nLevel < nLvlCount; ++nLevel)
1253 bool bDummy;
1254 AdjustLVL( nLevel, rMyNumRule, aItemSet,
1255 pListInfo->aCharFormat, bDummy );
1259 // 2. read and save PLF LFO
1261 bOk = checkSeek(m_rSt, m_rFib.m_fcPlfLfo);
1263 if (!bOk)
1264 return;
1266 sal_Int32 nLfoCount(0);
1267 m_rSt.ReadInt32( nLfoCount );
1268 bOk = nLfoCount > 0;
1270 if (!bOk)
1271 return;
1273 // 2.1 read all LFO
1275 for (sal_Int32 nLfo = 0; nLfo < nLfoCount; ++nLfo)
1277 bOk = false;
1279 WW8LFO aLFO = {};
1281 m_rSt.ReadUInt32( aLFO.nIdLst );
1282 m_rSt.SeekRel( 8 );
1283 m_rSt.ReadUChar( aLFO.nLfoLvl );
1284 if (!m_rSt.good())
1285 break;
1286 m_rSt.SeekRel( 3 );
1287 // as many Overrides as there are
1288 if ((nMaxLevel < aLFO.nLfoLvl) || m_rSt.GetError())
1289 break;
1291 // get the Parent NumRule of the current List
1292 WW8LSTInfo* pParentListInfo = GetLSTByListId(aLFO.nIdLst);
1293 if (pParentListInfo)
1295 // Save the NumRule in this first step
1296 aLFO.pNumRule = pParentListInfo->pNumRule;
1298 // are there multiple Levels in the List?
1299 aLFO.bSimpleList = pParentListInfo->bSimpleList;
1301 // store in Array
1302 std::unique_ptr<WW8LFOInfo> pLFOInfo(new WW8LFOInfo(aLFO));
1303 if (pParentListInfo)
1305 //Copy the basic paragraph properties for each level from the
1306 //original list into the list format override levels.
1307 int nMaxSize = pParentListInfo->maParaSprms.size();
1308 pLFOInfo->maParaSprms.resize(nMaxSize);
1309 for (int i = 0; i < nMaxSize; ++i)
1310 pLFOInfo->maParaSprms[i] = pParentListInfo->maParaSprms[i];
1312 m_LFOInfos.push_back(std::move(pLFOInfo));
1313 bOk = true;
1316 if( bOk )
1319 // 2.2 read specific LFOLVL for all LFO
1321 size_t nLFOInfos = m_LFOInfos.size();
1322 for (size_t nLfo = 0; nLfo < nLFOInfos; ++nLfo)
1324 WW8LFOInfo& rLFOInfo = *m_LFOInfos[nLfo];
1325 // Do LFOLVL exist?
1326 if( rLFOInfo.bOverride )
1328 WW8LSTInfo* pParentListInfo = GetLSTByListId(rLFOInfo.nIdLst);
1329 if (!pParentListInfo)
1330 break;
1332 // 2.2.1 create new NumRule for this List
1334 SwNumRule* pParentNumRule = rLFOInfo.pNumRule;
1335 OSL_ENSURE(pParentNumRule, "ww: Impossible lists, please report");
1336 if( !pParentNumRule )
1337 break;
1338 // create name-prefix for NumRule-Name
1339 // and (if necessary) for Style-Name
1340 const OUString sPrefix("WW8NumSt" + OUString::number( nLfo + 1 ));
1341 // Now assign pNumRule its actual value!!!
1342 // (it contained the parent NumRule up to this point)
1344 // check if a Style is referencing this LFO
1345 if( USHRT_MAX > m_rReader.StyleUsingLFO( nLfo ) )
1347 sal_uInt16 nRul = m_rDoc.MakeNumRule(
1348 m_rDoc.GetUniqueNumRuleName( &sPrefix ), pParentNumRule);
1349 rLFOInfo.pNumRule = m_rDoc.GetNumRuleTable()[ nRul ];
1350 rLFOInfo.pNumRule->SetAutoRule(false);
1352 else
1354 sal_uInt16 nRul = m_rDoc.MakeNumRule(
1355 m_rDoc.GetUniqueNumRuleName(), pParentNumRule);
1356 rLFOInfo.pNumRule = m_rDoc.GetNumRuleTable()[ nRul ];
1357 rLFOInfo.pNumRule->SetAutoRule(true); // = default
1360 // 2.2.2 read all LFOLVL (and LVL) for the new NumRule
1362 WW8aISet aItemSet; // Character attributes from GrpprlChpx
1363 WW8aCFormat aCharFormat = {}; // Character Style Pointer
1365 //2.2.2.0 skip inter-group of override header ?
1366 //See #i25438# for why I moved this here, compare
1367 //that original bugdoc's binary to what it looks like
1368 //when resaved with word, i.e. there is always a
1369 //4 byte header, there might be more than one if
1370 //that header was 0xFFFFFFFF, e.g. #114412# ?
1371 sal_uInt32 nTest;
1372 m_rSt.ReadUInt32( nTest );
1375 nTest = 0;
1376 m_rSt.ReadUInt32( nTest );
1378 while (nTest == 0xFFFFFFFF);
1379 m_rSt.SeekRel(-4);
1381 for (sal_uInt8 nLevel = 0; nLevel < rLFOInfo.nLfoLvl; ++nLevel)
1383 WW8LFOLVL aLFOLVL;
1384 bLVLOk = false;
1386 // 2.2.2.1 read LFOLVL
1388 m_rSt.ReadInt32( aLFOLVL.nStartAt );
1389 sal_uInt8 aBits1(0);
1390 m_rSt.ReadUChar( aBits1 );
1391 m_rSt.SeekRel( 3 );
1392 if (m_rSt.GetError())
1393 break;
1395 // Note: MS writes the Override-Level-Number into 4 bit.
1396 // We do not! (See comment at "struct WW8LFOInfo")
1397 aLFOLVL.nLevel = aBits1 & 0x0F;
1398 if( (0xFF > aBits1) &&
1399 (nMaxLevel > aLFOLVL.nLevel) )
1401 if (aBits1 & 0x10)
1402 aLFOLVL.bStartAt = true;
1403 else
1404 aLFOLVL.bStartAt = false;
1406 // 2.2.2.2 load dedicated LVL if necessary
1408 SwNumFormat aNumFormat(
1409 rLFOInfo.pNumRule->Get(aLFOLVL.nLevel));
1410 if (aBits1 & 0x20)
1412 aLFOLVL.bFormat = true;
1413 // if bStartup is true, replace Startup-Level
1414 // with the LVLF that is saved in the LVL
1415 bLVLOk = nLevel < rLFOInfo.maParaSprms.size() &&
1416 ReadLVL(aNumFormat, aItemSet[nLevel],
1417 pParentListInfo->aIdSty[nLevel],
1418 aLFOLVL.bStartAt, nLevel,
1419 rLFOInfo.maParaSprms[nLevel]);
1421 if (!bLVLOk)
1422 break;
1424 else if (aLFOLVL.bStartAt)
1426 aNumFormat.SetStart(
1427 writer_cast<sal_uInt16>(aLFOLVL.nStartAt));
1430 // 2.2.2.3 Set NumFormat in NumRule
1432 rLFOInfo.pNumRule->Set(aLFOLVL.nLevel, aNumFormat);
1434 bLVLOk = true;
1436 if (nMaxLevel > aLFOLVL.nLevel)
1437 rLFOInfo.maOverrides[aLFOLVL.nLevel] = aLFOLVL;
1439 if( !bLVLOk )
1440 break;
1442 // 2.2.3 adjust LVL of the new NumRule
1444 bool bNewCharFormatCreated = false;
1445 for (sal_uInt8 nLevel = 0; nLevel < rLFOInfo.nLfoLvl; ++nLevel)
1447 AdjustLVL( nLevel, *rLFOInfo.pNumRule, aItemSet, aCharFormat,
1448 bNewCharFormatCreated, sPrefix );
1453 // and we're done!
1454 m_rSt.Seek( nOriginalPos );
1457 void WW8ListManager::ImplDestroy()
1460 named lists remain in document
1461 unused automatic lists are removed from document (DelNumRule)
1463 for(auto & rpInfo : maLSTInfos)
1465 if (rpInfo->pNumRule && !rpInfo->bUsedInDoc &&
1466 rpInfo->pNumRule->IsAutoRule())
1468 m_rDoc.DelNumRule(rpInfo->pNumRule->GetName());
1470 rpInfo.reset();
1472 for (auto aIter = m_LFOInfos.rbegin(); aIter != m_LFOInfos.rend(); ++aIter)
1474 if ((*aIter)->bOverride
1475 && (*aIter)->pNumRule
1476 && !(*aIter)->bUsedInDoc
1477 && (*aIter)->pNumRule->IsAutoRule())
1479 m_rDoc.DelNumRule( (*aIter)->pNumRule->GetName() );
1484 WW8ListManager::~WW8ListManager()
1486 suppress_fun_call_w_exception(ImplDestroy());
1489 static bool IsEqualFormatting(const SwNumRule &rOne, const SwNumRule &rTwo)
1491 bool bRet =
1493 rOne.GetRuleType() == rTwo.GetRuleType() &&
1494 rOne.IsContinusNum() == rTwo.IsContinusNum() &&
1495 rOne.IsAbsSpaces() == rTwo.IsAbsSpaces() &&
1496 rOne.GetPoolFormatId() == rTwo.GetPoolFormatId() &&
1497 rOne.GetPoolHelpId() == rTwo.GetPoolHelpId() &&
1498 rOne.GetPoolHlpFileId() == rTwo.GetPoolHlpFileId()
1501 if (bRet)
1503 for (sal_uInt8 n = 0; n < MAXLEVEL; ++n )
1505 //The SvxNumberFormat compare, not the SwNumFormat compare
1506 const SvxNumberFormat &rO = rOne.Get(n);
1507 const SvxNumberFormat &rT = rTwo.Get(n);
1508 if (rO != rT)
1510 bRet = false;
1511 break;
1515 return bRet;
1518 SwNumRule* WW8ListManager::GetNumRuleForActivation(sal_uInt16 nLFOPosition,
1519 const sal_uInt8 nLevel, std::vector<sal_uInt8> &rParaSprms, SwTextNode *pNode)
1521 if (m_LFOInfos.size() <= nLFOPosition)
1522 return nullptr;
1524 WW8LFOInfo& rLFOInfo = *m_LFOInfos[nLFOPosition];
1526 bool bFirstUse = !rLFOInfo.bUsedInDoc;
1527 rLFOInfo.bUsedInDoc = true;
1529 if( !rLFOInfo.pNumRule )
1530 return nullptr;
1532 // #i25545#
1533 // #i100132# - a number format does not have to exist on given list level
1534 SwNumFormat aFormat(rLFOInfo.pNumRule->Get(nLevel));
1536 if (m_rReader.IsRightToLeft() && m_nLastLFOPosition != nLFOPosition) {
1537 if ( aFormat.GetNumAdjust() == SvxAdjust::Right)
1538 aFormat.SetNumAdjust(SvxAdjust::Left);
1539 else if ( aFormat.GetNumAdjust() == SvxAdjust::Left)
1540 aFormat.SetNumAdjust(SvxAdjust::Right);
1541 rLFOInfo.pNumRule->Set(nLevel, aFormat);
1543 m_nLastLFOPosition = nLFOPosition;
1545 #i1869#
1546 If this list has had its bits set in word 2000 to pretend that it is a
1547 simple list from the point of view of the user, then it is almost
1548 certainly a simple continuous list, and we will try to keep it like that.
1549 Otherwise when we save again it will be shown as the true outline list
1550 that it is, confusing the user that just wanted what they thought was a
1551 simple list. On the other hand it is possible that some of the other levels
1552 were used by the user, in which case we will not pretend anymore that it
1553 is a simple list. Something that word 2000 does anyway, that 97 didn't, to
1554 my bewilderment.
1556 if (nLevel && rLFOInfo.pNumRule->IsContinusNum())
1557 rLFOInfo.pNumRule->SetContinusNum(false);
1559 if( (!rLFOInfo.bOverride) && (!rLFOInfo.bLSTbUIDSet) )
1561 WW8LSTInfo* pParentListInfo = GetLSTByListId( rLFOInfo.nIdLst );
1562 if( pParentListInfo )
1563 pParentListInfo->bUsedInDoc = true;
1564 rLFOInfo.bLSTbUIDSet = true;
1567 if (rLFOInfo.maParaSprms.size() > nLevel)
1568 rParaSprms = rLFOInfo.maParaSprms[nLevel];
1570 SwNumRule *pRet = rLFOInfo.pNumRule;
1572 bool bRestart(false);
1573 sal_uInt16 nStart(0);
1574 bool bNewstart(false);
1576 Note: If you fiddle with this then you have to make sure that #i18322#
1577 #i13833#, #i20095# and #112466# continue to work
1579 Check if there were overrides for this level
1581 if (rLFOInfo.bOverride && nLevel < rLFOInfo.nLfoLvl)
1583 WW8LSTInfo* pParentListInfo = GetLSTByListId(rLFOInfo.nIdLst);
1584 OSL_ENSURE(pParentListInfo, "ww: Impossible lists, please report");
1585 if (pParentListInfo && pParentListInfo->pNumRule)
1587 const WW8LFOLVL &rOverride = rLFOInfo.maOverrides[nLevel];
1588 bool bNoChangeFromParent =
1589 IsEqualFormatting(*pRet, *(pParentListInfo->pNumRule));
1591 //If so then I think word still uses the parent (maybe)
1592 if (bNoChangeFromParent)
1594 pRet = pParentListInfo->pNumRule;
1596 //did it not affect start at value ?
1597 if (bFirstUse && rOverride.bStartAt)
1599 const SwNumFormat &rFormat =
1600 pParentListInfo->pNumRule->Get(nLevel);
1601 if (
1602 rFormat.GetStart() ==
1603 rLFOInfo.maOverrides[nLevel].nStartAt
1606 bRestart = true;
1608 else
1610 bNewstart = true;
1611 nStart = writer_cast<sal_uInt16>
1612 (rLFOInfo.maOverrides[nLevel].nStartAt);
1616 pParentListInfo->bUsedInDoc = true;
1621 if (pNode)
1623 pNode->SetAttrListLevel(nLevel);
1625 if (bRestart || bNewstart)
1626 pNode->SetListRestart(true);
1627 if (bNewstart)
1628 pNode->SetAttrListRestartValue(nStart);
1630 return pRet;
1633 // SwWW8ImplReader: append a List to a Style or Paragraph
1635 bool SwWW8ImplReader::SetTextFormatCollAndListLevel(const SwPaM& rRg,
1636 SwWW8StyInf& rStyleInfo)
1638 bool bRes = true;
1639 if( rStyleInfo.m_pFormat && rStyleInfo.m_bColl )
1641 bRes = m_rDoc.SetTextFormatColl(rRg, static_cast<SwTextFormatColl*>(rStyleInfo.m_pFormat));
1642 SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode();
1643 OSL_ENSURE( pTextNode, "No Text-Node at PaM-Position" );
1644 if ( !pTextNode )
1646 // make code robust
1647 return bRes;
1650 const SwNumRule * pNumRule = pTextNode->GetNumRule(); // #i27610#
1652 if( !IsInvalidOrToBeMergedTabCell() &&
1653 ! (pNumRule && pNumRule->IsOutlineRule()) ) // #i27610#
1655 pTextNode->ResetAttr( RES_PARATR_NUMRULE );
1658 if (USHRT_MAX > rStyleInfo.m_nLFOIndex && WW8ListManager::nMaxLevel
1659 > rStyleInfo.m_nListLevel)
1661 const bool bApplyListStyle = false;
1662 RegisterNumFormatOnTextNode(rStyleInfo.m_nLFOIndex, rStyleInfo.m_nListLevel,
1663 bApplyListStyle);
1666 return bRes;
1669 void UseListIndent(SwWW8StyInf &rStyle, const SwNumFormat &rFormat)
1671 // #i86652#
1672 if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
1674 const auto nAbsLSpace = rFormat.GetAbsLSpace();
1675 const tools::Long nListFirstLineIndent = GetListFirstLineIndent(rFormat);
1676 SvxFirstLineIndentItem firstLine(rStyle.m_pFormat->GetFormatAttr(RES_MARGIN_FIRSTLINE));
1677 SvxTextLeftMarginItem leftMargin(rStyle.m_pFormat->GetFormatAttr(RES_MARGIN_TEXTLEFT));
1678 leftMargin.SetTextLeft(nAbsLSpace);
1679 firstLine.SetTextFirstLineOffset(writer_cast<short>(nListFirstLineIndent));
1680 rStyle.m_pFormat->SetFormatAttr(firstLine);
1681 rStyle.m_pFormat->SetFormatAttr(leftMargin);
1682 rStyle.m_bListRelevantIndentSet = true;
1686 void SetStyleIndent(SwWW8StyInf &rStyle, const SwNumFormat &rFormat)
1688 if ( rFormat.GetPositionAndSpaceMode() != SvxNumberFormat::LABEL_WIDTH_AND_POSITION ) // #i86652#
1689 return;
1691 SvxFirstLineIndentItem firstLine(rStyle.m_pFormat->GetFormatAttr(RES_MARGIN_FIRSTLINE));
1692 SvxTextLeftMarginItem leftMargin(rStyle.m_pFormat->GetFormatAttr(RES_MARGIN_TEXTLEFT));
1693 if (rStyle.m_bListRelevantIndentSet)
1696 SyncIndentWithList(firstLine, leftMargin, rFormat, false, false); // #i103711#, #i105414#
1698 else
1700 leftMargin.SetTextLeft(0);
1701 firstLine.SetTextFirstLineOffset(0);
1703 rStyle.m_pFormat->SetFormatAttr(firstLine);
1704 rStyle.m_pFormat->SetFormatAttr(leftMargin);
1707 void SwWW8ImplReader::SetStylesList(sal_uInt16 nStyle, sal_uInt16 nCurrentLFO,
1708 sal_uInt8 nCurrentLevel)
1710 if (nStyle >= m_vColl.size())
1711 return;
1713 SwWW8StyInf &rStyleInf = m_vColl[nStyle];
1714 if (!rStyleInf.m_bValid)
1715 return;
1717 OSL_ENSURE(m_pCurrentColl, "Cannot be called outside of style import");
1718 // Phase 1: Numbering attributes when reading a StyleDef
1719 if( !m_pCurrentColl )
1720 return;
1722 if (nCurrentLFO < USHRT_MAX)
1723 rStyleInf.m_nLFOIndex = nCurrentLFO;
1724 if (nCurrentLevel < MAXLEVEL)
1725 rStyleInf.m_nListLevel = nCurrentLevel;
1727 // only save the Parameters for now. The actual List will be appended
1728 // at a later point, when the Listdefinitions is read...
1729 if (rStyleInf.m_nLFOIndex < USHRT_MAX && rStyleInf.m_nListLevel < WW8ListManager::nMaxLevel)
1731 std::vector<sal_uInt8> aParaSprms;
1732 SwNumRule* pNmRule = m_xLstManager->GetNumRuleForActivation(
1733 rStyleInf.m_nLFOIndex, rStyleInf.m_nListLevel, aParaSprms);
1734 if (pNmRule)
1735 UseListIndent(rStyleInf, pNmRule->Get(rStyleInf.m_nListLevel));
1739 void SwWW8ImplReader::RegisterNumFormatOnStyle(sal_uInt16 nStyle)
1742 if (nStyle >= m_vColl.size())
1743 return;
1745 SwWW8StyInf &rStyleInf = m_vColl[nStyle];
1746 if (!(rStyleInf.m_bValid && rStyleInf.m_pFormat))
1747 return;
1749 //Save old pre-list modified indent, which are the word indent values
1750 rStyleInf.m_pWordFirstLine.reset(rStyleInf.m_pFormat->GetFormatAttr(RES_MARGIN_FIRSTLINE).Clone());
1751 rStyleInf.m_pWordLeftMargin.reset(rStyleInf.m_pFormat->GetFormatAttr(RES_MARGIN_TEXTLEFT).Clone());
1752 rStyleInf.m_pWordRightMargin.reset(rStyleInf.m_pFormat->GetFormatAttr(RES_MARGIN_RIGHT).Clone());
1754 // Phase 2: refresh StyleDef after reading all Lists
1755 if (rStyleInf.m_nLFOIndex >= USHRT_MAX || rStyleInf.m_nListLevel >= WW8ListManager::nMaxLevel)
1756 return;
1758 std::vector<sal_uInt8> aParaSprms;
1759 SwNumRule* pNmRule = m_xLstManager->GetNumRuleForActivation(
1760 rStyleInf.m_nLFOIndex, rStyleInf.m_nListLevel, aParaSprms);
1762 if (pNmRule != nullptr)
1764 if (rStyleInf.IsWW8BuiltInHeadingStyle()
1765 && rStyleInf.HasWW8OutlineLevel())
1767 rStyleInf.m_pOutlineNumrule = pNmRule;
1769 else
1771 rStyleInf.m_pFormat->SetFormatAttr(
1772 SwNumRuleItem(pNmRule->GetName()));
1773 rStyleInf.m_bHasStyNumRule = true;
1776 SetStyleIndent(rStyleInf, pNmRule->Get(rStyleInf.m_nListLevel));
1780 void SwWW8ImplReader::RegisterNumFormatOnTextNode(sal_uInt16 nCurrentLFO,
1781 sal_uInt8 nCurrentLevel,
1782 const bool bSetAttr)
1784 // Note: the method appends NumRule to the Text Node if
1785 // bSetAttr (of course the lists have to be read before)
1786 // and only sets the Level. It does not check if there is a NumRule
1787 // attached to the STYLE !!!
1789 if (!m_xLstManager) // are all list declarations read?
1790 return;
1792 SwTextNode* pTextNd = m_pPaM->GetPointNode().GetTextNode();
1793 OSL_ENSURE(pTextNd, "No Text-Node at PaM-Position");
1794 if (!pTextNd)
1795 return;
1797 // WW8ListManager::nMaxLevel indicates body text, cancelling an inherited numbering.
1798 if (nCurrentLFO < USHRT_MAX && nCurrentLevel == WW8ListManager::nMaxLevel)
1800 pTextNd->SetAttr(SwNumRuleItem(OUString()));
1801 return;
1804 // Undefined listLevel is treated as the first level with valid numbering rule.
1805 // TODO:This doesn't allow for inheriting from a style(HOW?), but it matches previous behaviour.
1806 if (nCurrentLFO < USHRT_MAX && nCurrentLevel == MAXLEVEL)
1807 nCurrentLevel = 0;
1809 std::vector<sal_uInt8> aParaSprms;
1810 const SwNumRule* pRule = bSetAttr ?
1811 m_xLstManager->GetNumRuleForActivation( nCurrentLFO, nCurrentLevel,
1812 aParaSprms, pTextNd) : nullptr;
1814 if (pRule == nullptr && bSetAttr)
1815 return;
1817 if (bSetAttr && pTextNd->GetNumRule() != pRule
1818 && (pTextNd->GetNumRule() != m_rDoc.GetOutlineNumRule()
1819 || pRule != m_pChosenWW8OutlineStyle))
1821 // Now this is either not a part of Chapter Numbering,
1822 // or else it is using a different numRule than the one copied to Chapter Numbering.
1823 OUString sName = pRule == m_pChosenWW8OutlineStyle ? m_rDoc.GetOutlineNumRule()->GetName()
1824 : pRule->GetName();
1825 pTextNd->SetAttr(SwNumRuleItem(sName));
1827 pTextNd->SetAttrListLevel(nCurrentLevel);
1829 // <IsCounted()> state of text node has to be adjusted accordingly.
1830 if ( /*nCurrentLevel >= 0 &&*/ nCurrentLevel < MAXLEVEL )
1832 pTextNd->SetCountedInList( true );
1835 // #i99822#
1836 // Direct application of the list level formatting no longer
1837 // needed for list levels of mode LABEL_ALIGNMENT
1838 bool bApplyListLevelIndentDirectlyAtPara(true);
1840 if (pTextNd->GetNumRule() && nCurrentLevel < MAXLEVEL)
1842 const SwNumFormat& rFormat = pTextNd->GetNumRule()->Get(nCurrentLevel);
1843 if (rFormat.GetPositionAndSpaceMode()
1844 == SvxNumberFormat::LABEL_ALIGNMENT)
1846 bApplyListLevelIndentDirectlyAtPara = false;
1851 if (!bApplyListLevelIndentDirectlyAtPara)
1852 return;
1854 auto pListIndent = std::make_unique<SfxItemSet>(m_rDoc.GetAttrPool(), svl::Items<RES_MARGIN_FIRSTLINE, RES_MARGIN_TEXTLEFT>);
1855 const SfxPoolItem *pItem;
1856 pItem = GetFormatAttr(RES_MARGIN_FIRSTLINE);
1857 OSL_ENSURE(pItem, "impossible");
1858 if (pItem)
1859 pListIndent->Put(*pItem);
1860 pItem = GetFormatAttr(RES_MARGIN_TEXTLEFT);
1861 if (pItem)
1862 pListIndent->Put(*pItem);
1865 Take the original paragraph sprms attached to this list level
1866 formatting and apply them to the paragraph. I'm convinced that
1867 this is exactly what word does.
1869 if (short nLen = static_cast< short >(aParaSprms.size()))
1871 std::unique_ptr<SfxItemSet> pOldCurrentItemSet(SetCurrentItemSet(std::move(pListIndent)));
1873 sal_uInt8* pSprms1 = aParaSprms.data();
1874 while (0 < nLen)
1876 sal_uInt16 nL1 = ImportSprm(pSprms1, nLen);
1877 nLen = nLen - nL1;
1878 pSprms1 += nL1;
1881 pListIndent = SetCurrentItemSet(std::move(pOldCurrentItemSet));
1884 if (const SvxFirstLineIndentItem *const pFirstLine = pListIndent->GetItem<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE))
1886 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *pFirstLine);
1887 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_MARGIN_FIRSTLINE);
1889 if (const SvxTextLeftMarginItem *const pLeftMargin = pListIndent->GetItem<SvxTextLeftMarginItem>(RES_MARGIN_TEXTLEFT))
1891 m_xCtrlStck->NewAttr(*m_pPaM->GetPoint(), *pLeftMargin);
1892 m_xCtrlStck->SetAttr(*m_pPaM->GetPoint(), RES_MARGIN_TEXTLEFT);
1896 void SwWW8ImplReader::RegisterNumFormat(sal_uInt16 nCurrentLFO, sal_uInt8 nCurrentLevel)
1898 // Are we reading the StyleDef ?
1899 if (m_pCurrentColl)
1900 SetStylesList( m_nCurrentColl , nCurrentLFO, nCurrentLevel);
1901 else
1902 RegisterNumFormatOnTextNode(nCurrentLFO, nCurrentLevel);
1905 void SwWW8ImplReader::Read_ListLevel(sal_uInt16, const sal_uInt8* pData,
1906 short nLen)
1908 if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox())
1909 return;
1911 if( nLen < 0 )
1913 // the current level is finished, what should we do ?
1914 m_nListLevel = MAXLEVEL;
1915 if (m_xStyles && !m_bVer67)
1916 m_xStyles->mnWwNumLevel = 0;
1918 else
1920 // security check
1921 if( !pData )
1922 return;
1924 // the Streamdata is zero based
1925 m_nListLevel = *pData;
1927 if (m_xStyles && !m_bVer67)
1930 if this is the case, then if the numbering is actually stored in
1931 winword 6 format, and its likely that sprmPIlvl has been abused
1932 to set the ww6 list level information which we will need when we
1933 reach the true ww6 list def. So set it now
1935 m_xStyles->mnWwNumLevel = m_nListLevel;
1938 // Treat an invalid level as body-level
1939 if (WW8ListManager::nMaxLevel < m_nListLevel)
1940 m_nListLevel = WW8ListManager::nMaxLevel;
1942 RegisterNumFormat(m_nLFOPosition, m_nListLevel);
1943 if (USHRT_MAX > m_nLFOPosition)
1945 assert(false && "m_nLFOPosition is usually reset immediately, so we rarely ever get here.");
1946 m_nLFOPosition = USHRT_MAX;
1947 m_nListLevel = MAXLEVEL;
1952 void SwWW8ImplReader::Read_LFOPosition(sal_uInt16, const sal_uInt8* pData,
1953 short nLen)
1955 if (m_xPlcxMan && m_xPlcxMan->GetDoingDrawTextBox())
1956 return;
1958 if( nLen < 0 )
1960 // the current level is finished, what should we do ?
1961 m_nLFOPosition = USHRT_MAX;
1962 m_nListLevel = MAXLEVEL;
1964 else
1966 // security check
1967 if( !pData )
1968 return;
1970 short nData = SVBT16ToUInt16( pData );
1971 if( 0 >= nData )
1973 // disable the numbering/list style apply to the paragraph or the style
1976 If you have a paragraph in word with left and/or hanging indent
1977 and remove its numbering, then the indentation appears to get
1978 reset, but not back to the base style, instead it goes to a blank
1979 setting.
1980 Unless it's a broken ww6 list in 97 in which case more hackery is
1981 required, some more details about broken ww6 list in
1982 ww8par6.cxx#SwWW8ImplReader::Read_LR
1985 if (m_pCurrentColl)
1987 // here a "named" style is being configured
1989 // disable the numbering/list in the style currently configured
1990 m_pCurrentColl->SetFormatAttr(*GetDfltAttr(RES_PARATR_NUMRULE));
1992 // reset/blank the indent
1993 m_pCurrentColl->SetFormatAttr(SvxFirstLineIndentItem(RES_MARGIN_FIRSTLINE));
1994 m_pCurrentColl->SetFormatAttr(SvxTextLeftMarginItem(RES_MARGIN_TEXTLEFT));
1995 m_pCurrentColl->SetFormatAttr(SvxRightMarginItem(RES_MARGIN_RIGHT));
1997 // These sprmPIlfos are supposed to indicate "cancel" numbering.
1998 // Since m_nLFOPosition is "data - 1", then zero becomes USHRT_MAX
1999 // which is no good since that indicates "unspecified, available for inheritance".
2000 // So instead use USHRT_MAX-1 for indicating an explicit "cancel numbering".
2001 RegisterNumFormat(USHRT_MAX-1, MAXLEVEL);
2003 else if (SwTextNode* pTextNode = m_pPaM->GetPointNode().GetTextNode())
2005 // here a paragraph is being directly formatted
2007 // empty the numbering/list style applied to the current paragraph
2008 SwNumRuleItem aEmptyRule;
2009 pTextNode->SetAttr( aEmptyRule );
2011 // create an empty SvxLRSpaceItem
2012 std::shared_ptr<SvxFirstLineIndentItem> pFirstLine(std::make_shared<SvxFirstLineIndentItem>(RES_MARGIN_FIRSTLINE));
2014 // replace it with the one of the current node if it exist
2015 const SfxPoolItem *const pItem = GetFormatAttr(RES_MARGIN_FIRSTLINE);
2016 if (pItem)
2018 pFirstLine.reset(static_cast<SvxFirstLineIndentItem*>(pItem->Clone()));
2021 // reset/blank the left indent (and only the left)
2022 pFirstLine->SetTextFirstLineOffset(0);
2023 SvxTextLeftMarginItem leftMargin(0, RES_MARGIN_TEXTLEFT);
2025 // apply the modified SvxLRSpaceItem to the current paragraph
2026 pTextNode->SetAttr(*pFirstLine);
2027 pTextNode->SetAttr(leftMargin);
2030 m_nLFOPosition = USHRT_MAX;
2032 else // nData in (0..0x7FFF]
2034 m_nLFOPosition = o3tl::narrowing<sal_uInt16>(nData)-1; // m_nLFOPosition in [0..0x7FFF)
2036 If we are a ww8+ style with ww7- style lists then there is a
2037 bizarre broken word bug where when the list is removed from a para
2038 the ww6 list first line indent still affects the first line
2039 indentation. Setting this flag will allow us to recover from this
2040 braindeadness
2042 if (m_pCurrentColl && (m_nLFOPosition == 2047-1) && m_nCurrentColl < m_vColl.size())
2043 m_vColl[m_nCurrentColl].m_bHasBrokenWW6List = true;
2045 // here the stream data is 1-based, we subtract ONE
2046 if (m_nLFOPosition != 2047-1) //Normal ww8+ list behaviour
2048 RegisterNumFormat(m_nLFOPosition, m_nListLevel);
2049 m_nLFOPosition = USHRT_MAX;
2050 m_nListLevel = MAXLEVEL;
2052 else if (m_xPlcxMan && m_xPlcxMan->HasParaSprm(NS_sprm::LN_PAnld).pSprm)
2055 #i8114# Horrific backwards compatible ww7- lists in ww8+
2056 docs
2058 m_nListLevel = std::min<sal_uInt8>(WW8ListManager::nMaxLevel, m_nListLevel);
2059 Read_ANLevelNo(13 /*equiv ww7- sprm no*/, &m_nListLevel, 1);
2065 // Reading Controls
2067 bool SwWW8ImplReader::ImportFormulaControl(WW8FormulaControl &aFormula,
2068 WW8_CP nStart, SwWw8ControlType nWhich )
2070 bool bRet=false;
2072 * Save the reader state and process the sprms for this anchor cp.
2073 * Doing so will set the nPicLocFc to the offset to find the hypertext
2074 * data in the data stream.
2076 WW8_CP nEndCp = nStart+1; //Only interested in the single 0x01 character
2078 WW8ReaderSave aSave(this,nStart);
2080 WW8PLCFManResult aRes;
2081 nStart = m_xPlcxMan->Where();
2082 while(nStart <= nEndCp)
2084 if ( m_xPlcxMan->Get(&aRes)
2085 && aRes.pMemPos && aRes.nSprmId )
2087 //only interested in sprms which would set nPicLocFc
2088 if ( (68 == aRes.nSprmId) || (0x6A03 == aRes.nSprmId) )
2090 Read_PicLoc( aRes.nSprmId, aRes.pMemPos +
2091 m_oSprmParser->DistanceToData(aRes.nSprmId), 4);
2092 break;
2095 m_xPlcxMan->advance();
2096 nStart = m_xPlcxMan->Where();
2098 sal_uLong nOffset = m_nPicLocFc;
2099 aSave.Restore(this);
2101 sal_uInt64 nOldPos = m_pDataStream->Tell();
2102 WW8_PIC aPic;
2103 bool bValid = checkSeek(*m_pDataStream, nOffset) &&
2104 PicRead(m_pDataStream, &aPic, m_bVer67);
2106 if (bValid && aPic.lcb > 0x3A)
2108 aFormula.FormulaRead(nWhich,m_pDataStream);
2109 bRet = true;
2113 There is a problem with aPic, the WW8_PIC is always used even though it
2114 is too big for the WW95 files, it needs to be modified to check the
2115 version C.
2117 m_pDataStream->Seek( nOldPos );
2118 return bRet;
2121 void SwMSConvertControls::InsertFormula(WW8FormulaControl &rFormula)
2123 const uno::Reference< lang::XMultiServiceFactory > & rServiceFactory =
2124 GetServiceFactory();
2126 if(!rServiceFactory.is())
2127 return;
2129 awt::Size aSz;
2130 uno::Reference< form::XFormComponent> xFComp;
2132 if (rFormula.Import(rServiceFactory, xFComp, aSz))
2134 uno::Reference <drawing::XShape> xShapeRef;
2135 if (InsertControl(xFComp, aSz, &xShapeRef, false))
2136 GetShapes()->add(xShapeRef);
2140 void WW8FormulaControl::FormulaRead(SwWw8ControlType nWhich,
2141 SvStream *pDataStream)
2143 sal_uInt8 nField;
2145 // The following is a FFData structure as described in
2146 // Microsoft's DOC specification (chapter 2.9.78)
2147 sal_uInt32 nVersion = 0;
2148 pDataStream->ReadUInt32(nVersion);
2149 // An unsigned integer that MUST be 0xFFFFFFFF
2150 if (nVersion != 0xFFFFFFFF)
2152 SAL_WARN("sw.ww8", "Parsing error: invalid header for FFData");
2153 return; // bail out
2156 // might be better to read the bits as a 16 bit word
2157 // ( like it is in the spec. )
2158 sal_uInt8 bits1 = 0;
2159 pDataStream->ReadUChar( bits1 );
2160 sal_uInt8 bits2 = 0;
2161 pDataStream->ReadUChar( bits2 );
2163 sal_uInt8 iType = ( bits1 & 0x3 );
2165 // we should verify that bits.iType & nWhich concur
2166 OSL_ENSURE( iType == nWhich, "something wrong, expect control type read from stream doesn't match nWhich passed in");
2167 if ( iType != nWhich )
2168 return; // bail out
2170 sal_uInt8 iRes = (bits1 & 0x7C) >> 2;
2172 pDataStream->ReadUInt16( mnMaxLen );
2174 sal_uInt16 hps = 0;
2175 pDataStream->ReadUInt16( hps );
2177 // xstzName
2178 msTitle = read_uInt16_BeltAndBracesString(*pDataStream);
2180 if (nWhich == WW8_CT_EDIT)
2181 { // Field is a textbox
2182 // Default text
2183 // xstzTextDef
2184 msDefault = read_uInt16_BeltAndBracesString(*pDataStream);
2186 else
2188 // CheckBox or ComboBox
2189 sal_uInt16 wDef = 0;
2190 pDataStream->ReadUInt16( wDef );
2191 mnChecked = wDef; // default
2192 if (nWhich == WW8_CT_CHECKBOX)
2194 if ( iRes != 25 )
2195 mnChecked = iRes;
2196 msDefault = ( wDef == 0 ) ? std::u16string_view( u"0" ) : std::u16string_view( u"1" );
2199 // xstzTextFormat
2200 msFormatting = read_uInt16_BeltAndBracesString(*pDataStream);
2201 // xstzHelpText
2202 msHelp = read_uInt16_BeltAndBracesString(*pDataStream);
2203 // xstzStatText
2204 msToolTip = read_uInt16_BeltAndBracesString(*pDataStream);
2206 // xstzEntryMcr
2207 msEntryMcr = read_uInt16_BeltAndBracesString(*pDataStream);
2208 //xstzExitMcr
2209 msExitMcr = read_uInt16_BeltAndBracesString(*pDataStream);
2211 if (nWhich == WW8_CT_DROPDOWN)
2213 bool bAllOk = true;
2214 // SSTB (see Spec. 2.2.4)
2215 sal_uInt16 fExtend = 0;
2216 pDataStream->ReadUInt16( fExtend );
2217 sal_uInt16 nStringsCnt = 0;
2219 // Isn't it that if fExtend isn't 0xFFFF then fExtend actually
2220 // doesn't exist and we really have just read nStringsCnt ( or cData )?
2221 if (fExtend != 0xFFFF)
2222 bAllOk = false;
2223 pDataStream->ReadUInt16( nStringsCnt );
2225 // I guess this should be zero ( and we should ensure that )
2226 sal_uInt16 cbExtra = 0;
2227 pDataStream->ReadUInt16( cbExtra );
2229 OSL_ENSURE(bAllOk, "Unknown formfield dropdown list structure");
2230 if (!bAllOk) //Not as expected, don't risk it at all.
2231 nStringsCnt = 0;
2232 const size_t nMinRecordSize = sizeof(sal_uInt16);
2233 const size_t nMaxRecords = pDataStream->remainingSize() / nMinRecordSize;
2234 if (nStringsCnt > nMaxRecords)
2236 SAL_WARN("sw.ww8", "Parsing error: " << nMaxRecords <<
2237 " max possible entries, but " << nStringsCnt << " claimed, truncating");
2238 nStringsCnt = nMaxRecords;
2240 maListEntries.reserve(nStringsCnt);
2241 for (sal_uInt32 nI = 0; nI < nStringsCnt; ++nI)
2243 OUString sEntry = read_uInt16_PascalString(*pDataStream);
2244 maListEntries.push_back(sEntry);
2247 mfDropdownIndex = iRes;
2249 mbHelp = bits1 & 0x80;
2251 nField = bits2;
2252 mfToolTip = nField & 0x01;
2253 mfNoMark = (nField & 0x02)>>1;
2254 mfType = (nField & 0x38)>>3;
2255 mfUnused = (nField & 0xE0)>>5;
2258 WW8FormulaListBox::WW8FormulaListBox(SwWW8ImplReader &rR)
2259 : WW8FormulaControl(SL::aListBox, rR)
2263 //Miserable hack to get a hardcoded guesstimate of the size of a list dropdown
2264 //box's first entry to set as the lists default size
2265 awt::Size SwWW8ImplReader::MiserableDropDownFormHack(const OUString &rString,
2266 uno::Reference<beans::XPropertySet> const & rPropSet)
2268 awt::Size aRet;
2269 struct CtrlFontMapEntry
2271 sal_uInt16 nWhichId;
2272 const char* pPropNm;
2274 const CtrlFontMapEntry aMapTable[] =
2276 { RES_CHRATR_COLOR, "TextColor" },
2277 { RES_CHRATR_FONT, "FontName" },
2278 { RES_CHRATR_FONTSIZE, "FontHeight" },
2279 { RES_CHRATR_WEIGHT, "FontWeight" },
2280 { RES_CHRATR_UNDERLINE, "FontUnderline" },
2281 { RES_CHRATR_CROSSEDOUT, "FontStrikeout" },
2282 { RES_CHRATR_POSTURE, "FontSlant" },
2283 { 0, nullptr }
2286 vcl::Font aFont;
2287 uno::Reference< beans::XPropertySetInfo > xPropSetInfo =
2288 rPropSet->getPropertySetInfo();
2290 uno::Any aTmp;
2291 for (const CtrlFontMapEntry* pMap = aMapTable; pMap->nWhichId; ++pMap)
2293 bool bSet = true;
2294 const SfxPoolItem* pItem = GetFormatAttr( pMap->nWhichId );
2295 OSL_ENSURE(pItem, "Impossible");
2296 if (!pItem)
2297 continue;
2299 switch ( pMap->nWhichId )
2301 case RES_CHRATR_COLOR:
2303 OUString aNm;
2304 if (xPropSetInfo->hasPropertyByName(aNm = "TextColor"))
2306 aTmp <<= static_cast<sal_Int32>(static_cast<const SvxColorItem*>(pItem)->GetValue());
2307 rPropSet->setPropertyValue(aNm, aTmp);
2310 aFont.SetColor(static_cast<const SvxColorItem*>(pItem)->GetValue());
2311 break;
2312 case RES_CHRATR_FONT:
2314 const SvxFontItem *pFontItem = static_cast<const SvxFontItem *>(pItem);
2315 OUString aNm;
2316 if (xPropSetInfo->hasPropertyByName(aNm = "FontStyleName"))
2318 aTmp <<= pFontItem->GetStyleName();
2319 rPropSet->setPropertyValue( aNm, aTmp );
2321 if (xPropSetInfo->hasPropertyByName(aNm = "FontFamily"))
2323 aTmp <<= static_cast<sal_Int16>(pFontItem->GetFamily());
2324 rPropSet->setPropertyValue( aNm, aTmp );
2326 if (xPropSetInfo->hasPropertyByName(aNm = "FontCharset"))
2328 aTmp <<= static_cast<sal_Int16>(pFontItem->GetCharSet());
2329 rPropSet->setPropertyValue( aNm, aTmp );
2331 if (xPropSetInfo->hasPropertyByName(aNm = "FontPitch"))
2333 aTmp <<= static_cast<sal_Int16>(pFontItem->GetPitch());
2334 rPropSet->setPropertyValue( aNm, aTmp );
2337 aTmp <<= pFontItem->GetFamilyName();
2338 aFont.SetFamilyName( pFontItem->GetFamilyName() );
2339 aFont.SetStyleName( pFontItem->GetStyleName() );
2340 aFont.SetFamily( pFontItem->GetFamily() );
2341 aFont.SetCharSet( pFontItem->GetCharSet() );
2342 aFont.SetPitch( pFontItem->GetPitch() );
2344 break;
2346 case RES_CHRATR_FONTSIZE:
2348 Size aSize( aFont.GetFontSize().Width(),
2349 static_cast<const SvxFontHeightItem*>(pItem)->GetHeight() );
2350 aTmp <<= static_cast<float>(aSize.Height()) / 20.0;
2352 aFont.SetFontSize(o3tl::convert(aSize, o3tl::Length::twip, o3tl::Length::mm100));
2354 break;
2356 case RES_CHRATR_WEIGHT:
2357 aTmp <<= vcl::unohelper::ConvertFontWeight(
2358 static_cast<const SvxWeightItem*>(pItem)->GetWeight() );
2359 aFont.SetWeight( static_cast<const SvxWeightItem*>(pItem)->GetWeight() );
2360 break;
2362 case RES_CHRATR_UNDERLINE:
2363 aTmp <<= static_cast<sal_Int16>(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle());
2364 aFont.SetUnderline(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle());
2365 break;
2367 case RES_CHRATR_CROSSEDOUT:
2368 aTmp <<= static_cast<sal_Int16>( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() );
2369 aFont.SetStrikeout( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() );
2370 break;
2372 case RES_CHRATR_POSTURE:
2373 aTmp <<= static_cast<sal_Int16>( static_cast<const SvxPostureItem*>(pItem)->GetPosture() );
2374 aFont.SetItalic( static_cast<const SvxPostureItem*>(pItem)->GetPosture() );
2375 break;
2377 default:
2378 bSet = false;
2379 break;
2382 if (bSet && xPropSetInfo->hasPropertyByName(OUString::createFromAscii(pMap->pPropNm)))
2383 rPropSet->setPropertyValue(OUString::createFromAscii(pMap->pPropNm), aTmp);
2385 // now calculate the size of the control
2386 OutputDevice* pOut = Application::GetDefaultDevice();
2387 OSL_ENSURE(pOut, "Impossible");
2388 if (pOut)
2390 pOut->Push( vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE );
2391 pOut->SetMapMode( MapMode( MapUnit::Map100thMM ));
2392 pOut->SetFont( aFont );
2393 aRet.Width = pOut->GetTextWidth(rString);
2394 aRet.Width += 500; //plus size of button, total hack territory
2395 aRet.Height = pOut->GetTextHeight();
2396 pOut->Pop();
2398 return aRet;
2401 bool WW8FormulaListBox::Import(const uno::Reference <
2402 lang::XMultiServiceFactory> &rServiceFactory,
2403 uno::Reference <form::XFormComponent> &rFComp,awt::Size &rSz )
2405 uno::Reference<uno::XInterface> xCreate = rServiceFactory->createInstance("com.sun.star.form.component.ComboBox");
2406 if( !xCreate.is() )
2407 return false;
2409 rFComp.set(xCreate, uno::UNO_QUERY);
2410 if( !rFComp.is() )
2411 return false;
2413 uno::Reference<beans::XPropertySet> xPropSet(xCreate, uno::UNO_QUERY);
2415 uno::Any aTmp;
2416 if (!msTitle.isEmpty())
2417 aTmp <<= msTitle;
2418 else
2419 aTmp <<= msName;
2420 xPropSet->setPropertyValue("Name", aTmp );
2422 if (!msToolTip.isEmpty())
2424 aTmp <<= msToolTip;
2425 xPropSet->setPropertyValue("HelpText", aTmp );
2428 xPropSet->setPropertyValue("Dropdown", css::uno::Any(true));
2430 if (!maListEntries.empty())
2432 sal_uInt32 nLen = maListEntries.size();
2433 uno::Sequence< OUString > aListSource(nLen);
2434 auto aListSourceRange = asNonConstRange(aListSource);
2435 for (sal_uInt32 nI = 0; nI < nLen; ++nI)
2436 aListSourceRange[nI] = maListEntries[nI];
2437 aTmp <<= aListSource;
2438 xPropSet->setPropertyValue("StringItemList", aTmp );
2440 if (mfDropdownIndex < nLen)
2442 aTmp <<= aListSource[mfDropdownIndex];
2444 else
2446 aTmp <<= aListSource[0];
2449 xPropSet->setPropertyValue("DefaultText", aTmp );
2451 rSz = mrRdr.MiserableDropDownFormHack(maListEntries[0], xPropSet);
2453 else
2455 static constexpr OUStringLiteral aBlank =
2456 u"\u2002\u2002\u2002\u2002\u2002";
2457 rSz = mrRdr.MiserableDropDownFormHack(aBlank, xPropSet);
2460 return true;
2463 WW8FormulaCheckBox::WW8FormulaCheckBox(SwWW8ImplReader &rR)
2464 : WW8FormulaControl(SL::aCheckBox, rR)
2468 static void lcl_AddToPropertyContainer
2469 (uno::Reference<beans::XPropertySet> const & xPropSet,
2470 const OUString & rPropertyName, const OUString & rValue)
2472 uno::Reference<beans::XPropertySetInfo> xPropSetInfo =
2473 xPropSet->getPropertySetInfo();
2474 if (xPropSetInfo.is() &&
2475 ! xPropSetInfo->hasPropertyByName(rPropertyName))
2477 uno::Reference<beans::XPropertyContainer>
2478 xPropContainer(xPropSet, uno::UNO_QUERY);
2479 uno::Any aAny((OUString()));
2480 xPropContainer->addProperty
2481 (rPropertyName,
2482 static_cast<sal_Int16>(beans::PropertyAttribute::BOUND |
2483 beans::PropertyAttribute::REMOVABLE),
2484 aAny);
2487 uno::Any aAnyValue(rValue);
2488 xPropSet->setPropertyValue(rPropertyName, aAnyValue );
2491 bool WW8FormulaCheckBox::Import(const uno::Reference <
2492 lang::XMultiServiceFactory> &rServiceFactory,
2493 uno::Reference <form::XFormComponent> &rFComp,awt::Size &rSz )
2495 uno::Reference< uno::XInterface > xCreate = rServiceFactory->createInstance("com.sun.star.form.component.CheckBox");
2496 if( !xCreate.is() )
2497 return false;
2499 rFComp.set( xCreate, uno::UNO_QUERY );
2500 if( !rFComp.is() )
2501 return false;
2503 uno::Reference< beans::XPropertySet > xPropSet( xCreate, uno::UNO_QUERY );
2505 rSz.Width = 16 * mhpsCheckBox;
2506 rSz.Height = 16 * mhpsCheckBox;
2508 uno::Any aTmp;
2509 if (!msTitle.isEmpty())
2510 aTmp <<= msTitle;
2511 else
2512 aTmp <<= msName;
2513 xPropSet->setPropertyValue("Name", aTmp );
2515 aTmp <<= static_cast<sal_Int16>(mnChecked);
2516 xPropSet->setPropertyValue("DefaultState", aTmp);
2518 if (!msToolTip.isEmpty())
2519 lcl_AddToPropertyContainer(xPropSet, "HelpText", msToolTip);
2521 if (!msHelp.isEmpty())
2522 lcl_AddToPropertyContainer(xPropSet, "HelpF1Text", msHelp);
2524 return true;
2528 WW8FormulaEditBox::WW8FormulaEditBox(SwWW8ImplReader &rR)
2529 : WW8FormulaControl(SL::aTextField ,rR)
2533 bool SwMSConvertControls::InsertControl(
2534 const uno::Reference< form::XFormComponent > & rFComp,
2535 const awt::Size& rSize, uno::Reference< drawing::XShape > *pShape,
2536 bool bFloatingCtrl)
2538 const uno::Reference< container::XIndexContainer > &rComps = GetFormComps();
2539 uno::Any aTmp( &rFComp, cppu::UnoType<form::XFormComponent>::get());
2540 rComps->insertByIndex( rComps->getCount(), aTmp );
2542 const uno::Reference< lang::XMultiServiceFactory > &rServiceFactory =
2543 GetServiceFactory();
2544 if( !rServiceFactory.is() )
2545 return false;
2547 uno::Reference< uno::XInterface > xCreate = rServiceFactory->createInstance(
2548 "com.sun.star.drawing.ControlShape");
2549 if( !xCreate.is() )
2550 return false;
2552 uno::Reference< drawing::XShape > xShape(xCreate, uno::UNO_QUERY);
2554 OSL_ENSURE(xShape.is(), "Did not get XShape");
2555 xShape->setSize(rSize);
2557 uno::Reference< beans::XPropertySet > xShapePropSet(
2558 xCreate, uno::UNO_QUERY );
2560 //I lay a small bet that this will change to
2561 //sal_Int16 nTemp=TextContentAnchorType::AS_CHARACTER;
2562 text::TextContentAnchorType nTemp;
2563 if (bFloatingCtrl)
2564 nTemp = text::TextContentAnchorType_AT_PARAGRAPH;
2565 else
2566 nTemp = text::TextContentAnchorType_AS_CHARACTER;
2568 xShapePropSet->setPropertyValue("AnchorType", uno::Any(static_cast<sal_Int16>(nTemp)) );
2570 xShapePropSet->setPropertyValue("VertOrient", uno::Any(sal_Int16(text::VertOrientation::TOP)) );
2572 uno::Reference< text::XText > xDummyTextRef;
2573 uno::Reference< text::XTextRange > xTextRg =
2574 new SwXTextRange( *m_pPaM, xDummyTextRef );
2576 aTmp <<= xTextRg;
2577 xShapePropSet->setPropertyValue("TextRange", aTmp );
2579 // Set the Control-Model for the Control-Shape
2580 uno::Reference< drawing::XControlShape > xControlShape( xShape,
2581 uno::UNO_QUERY );
2582 uno::Reference< awt::XControlModel > xControlModel( rFComp,
2583 uno::UNO_QUERY );
2584 xControlShape->setControl( xControlModel );
2586 if (pShape)
2587 *pShape = xShape;
2589 return true;
2592 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */