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