Line data Source code
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 <msfilter.hxx>
21 : #include "writerwordglue.hxx"
22 : #include <doc.hxx>
23 : #include "writerhelper.hxx"
24 :
25 : #include <algorithm> //std::find_if
26 : #include <functional> //std::unary_function
27 :
28 : #include <unicode/ubidi.h> //ubidi_getLogicalRun
29 : #include <tools/tenccvt.hxx> //GetExtendedTextEncoding
30 : #include <com/sun/star/i18n/ScriptType.hpp> //ScriptType
31 :
32 : #include <unotools/fontcvt.hxx> //GetSubsFontName
33 : #include <editeng/paperinf.hxx> //lA0Width...
34 : #include <editeng/lrspitem.hxx> //SvxLRSpaceItem
35 : #include <editeng/ulspitem.hxx> //SvxULSpaceItem
36 : #include <editeng/boxitem.hxx> //SvxBoxItem
37 : #include <editeng/fontitem.hxx> //SvxFontItem
38 : #include <frmfmt.hxx> //SwFrmFmt
39 : #include <fmtclds.hxx> //SwFmtCol
40 : #include <hfspacingitem.hxx> //SwHeaderAndFooterEatSpacingItem
41 : #include <fmtfsize.hxx> //SwFmtFrmSize
42 : #include <swrect.hxx> //SwRect
43 : #include <fmthdft.hxx> //SwFmtHeader/SwFmtFooter
44 : #include <frmatr.hxx> //GetLRSpace...
45 : #include <ndtxt.hxx> //SwTxtNode
46 : #include <breakit.hxx> //pBreakIt
47 : #include <i18npool/mslangid.hxx>
48 :
49 : #define ASSIGN_CONST_ASC(s) AssignAscii(s)
50 :
51 : namespace myImplHelpers
52 : {
53 22 : SwTwips CalcHdFtDist(const SwFrmFmt& rFmt, sal_uInt16 nSpacing)
54 : {
55 : /*
56 : The normal case for reexporting word docs is to have dynamic spacing,
57 : as this is word's only setting, and the reason for the existance of the
58 : dynamic spacing features. If we have dynamic spacing active then we can
59 : add its spacing to the value height of the h/f and get the wanted total
60 : size for word.
61 :
62 : Otherwise we have to get the real layout rendered
63 : height, which is totally nonoptimum, but the best we can do.
64 : */
65 22 : long nDist=0;
66 22 : const SwFmtFrmSize& rSz = rFmt.GetFrmSize();
67 :
68 : const SwHeaderAndFooterEatSpacingItem &rSpacingCtrl =
69 : sw::util::ItemGet<SwHeaderAndFooterEatSpacingItem>
70 22 : (rFmt, RES_HEADER_FOOTER_EAT_SPACING);
71 22 : if (rSpacingCtrl.GetValue())
72 22 : nDist += rSz.GetHeight();
73 : else
74 : {
75 0 : SwRect aRect(rFmt.FindLayoutRect(false));
76 0 : if (aRect.Height())
77 0 : nDist += aRect.Height();
78 : else
79 : {
80 0 : const SwFmtFrmSize& rSize = rFmt.GetFrmSize();
81 0 : if (ATT_VAR_SIZE != rSize.GetHeightSizeType())
82 0 : nDist += rSize.GetHeight();
83 : else
84 : {
85 0 : nDist += 274; // default for 12pt text
86 0 : nDist += nSpacing;
87 : }
88 : }
89 : }
90 22 : return nDist;
91 : }
92 :
93 11 : SwTwips CalcHdDist(const SwFrmFmt& rFmt)
94 : {
95 11 : return CalcHdFtDist(rFmt, rFmt.GetULSpace().GetUpper());
96 : }
97 :
98 11 : SwTwips CalcFtDist(const SwFrmFmt& rFmt)
99 : {
100 11 : return CalcHdFtDist(rFmt, rFmt.GetULSpace().GetLower());
101 : }
102 :
103 : /*
104 : SwTxtFmtColl and SwCharFmt are quite distinct types and how they are
105 : gotten is also distinct, but the algorithm to match word's eqivalents into
106 : them is the same, so we put the different stuff into two seperate helper
107 : implementations and a core template that uses the helpers that uses the
108 : same algorithm to do the work. We'll make the helpers specializations of a
109 : non existing template so I can let the compiler figure out the right one
110 : to use from a simple argument to the algorithm class
111 : */
112 : template <class C> class MapperImpl;
113 : template<> class MapperImpl<SwTxtFmtColl>
114 : {
115 : private:
116 : SwDoc &mrDoc;
117 : public:
118 39 : MapperImpl(SwDoc &rDoc) : mrDoc(rDoc) {}
119 : SwTxtFmtColl* GetBuiltInStyle(ww::sti eSti);
120 : SwTxtFmtColl* GetStyle(const String &rName);
121 : SwTxtFmtColl* MakeStyle(const String &rName);
122 : };
123 :
124 265 : SwTxtFmtColl* MapperImpl<SwTxtFmtColl>::GetBuiltInStyle(ww::sti eSti)
125 : {
126 265 : const RES_POOL_COLLFMT_TYPE RES_NONE = RES_POOLCOLL_DOC_END;
127 : static const RES_POOL_COLLFMT_TYPE aArr[]=
128 : {
129 : RES_POOLCOLL_STANDARD, RES_POOLCOLL_HEADLINE1,
130 : RES_POOLCOLL_HEADLINE2, RES_POOLCOLL_HEADLINE3,
131 : RES_POOLCOLL_HEADLINE4, RES_POOLCOLL_HEADLINE5,
132 : RES_POOLCOLL_HEADLINE6, RES_POOLCOLL_HEADLINE7,
133 : RES_POOLCOLL_HEADLINE8, RES_POOLCOLL_HEADLINE9,
134 : RES_POOLCOLL_TOX_IDX1, RES_POOLCOLL_TOX_IDX2,
135 : RES_POOLCOLL_TOX_IDX3, RES_NONE, RES_NONE, RES_NONE, RES_NONE,
136 : RES_NONE, RES_NONE, RES_POOLCOLL_TOX_CNTNT1,
137 : RES_POOLCOLL_TOX_CNTNT2, RES_POOLCOLL_TOX_CNTNT3,
138 : RES_POOLCOLL_TOX_CNTNT4, RES_POOLCOLL_TOX_CNTNT5,
139 : RES_POOLCOLL_TOX_CNTNT6, RES_POOLCOLL_TOX_CNTNT7,
140 : RES_POOLCOLL_TOX_CNTNT8, RES_POOLCOLL_TOX_CNTNT9, RES_NONE,
141 : RES_POOLCOLL_FOOTNOTE, RES_NONE, RES_POOLCOLL_HEADER,
142 : RES_POOLCOLL_FOOTER, RES_POOLCOLL_TOX_IDXH, RES_NONE, RES_NONE,
143 : RES_POOLCOLL_JAKETADRESS, RES_POOLCOLL_SENDADRESS, RES_NONE,
144 : RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_POOLCOLL_ENDNOTE,
145 : RES_NONE, RES_NONE, RES_NONE, RES_POOLCOLL_LISTS_BEGIN,
146 : RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE,
147 : RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE, RES_NONE,
148 : RES_NONE, RES_NONE, RES_POOLCOLL_HEADLINE_BASE, RES_NONE,
149 : RES_POOLCOLL_SIGNATURE, RES_NONE, RES_POOLCOLL_TEXT,
150 : RES_POOLCOLL_TEXT_MOVE, RES_NONE, RES_NONE, RES_NONE, RES_NONE,
151 : RES_NONE, RES_NONE, RES_POOLCOLL_DOC_SUBTITEL
152 : };
153 :
154 : OSL_ENSURE(SAL_N_ELEMENTS(aArr) == 75, "Style Array has false size");
155 :
156 265 : SwTxtFmtColl* pRet = 0;
157 : //If this is a built-in word style that has a built-in writer
158 : //equivalent, then map it to one of our built in styles regardless
159 : //of its name
160 265 : if (sal::static_int_cast< size_t >(eSti) < SAL_N_ELEMENTS(aArr) && aArr[eSti] != RES_NONE)
161 173 : pRet = mrDoc.GetTxtCollFromPool( static_cast< sal_uInt16 >(aArr[eSti]), false);
162 265 : return pRet;
163 : }
164 :
165 156 : SwTxtFmtColl* MapperImpl<SwTxtFmtColl>::GetStyle(const String &rName)
166 : {
167 156 : return sw::util::GetParaStyle(mrDoc, rName);
168 : }
169 :
170 63 : SwTxtFmtColl* MapperImpl<SwTxtFmtColl>::MakeStyle(const String &rName)
171 : {
172 : return mrDoc.MakeTxtFmtColl(rName,
173 63 : const_cast<SwTxtFmtColl *>(mrDoc.GetDfltTxtFmtColl()));
174 : }
175 :
176 : template<> class MapperImpl<SwCharFmt>
177 : {
178 : private:
179 : SwDoc &mrDoc;
180 : public:
181 39 : MapperImpl(SwDoc &rDoc) : mrDoc(rDoc) {}
182 : SwCharFmt* GetBuiltInStyle(ww::sti eSti);
183 : SwCharFmt* GetStyle(const String &rName);
184 : SwCharFmt* MakeStyle(const String &rName);
185 : };
186 :
187 94 : SwCharFmt* MapperImpl<SwCharFmt>::GetBuiltInStyle(ww::sti eSti)
188 : {
189 94 : RES_POOL_CHRFMT_TYPE eLookup = RES_POOLCHR_NORMAL_END;
190 94 : switch (eSti)
191 : {
192 : case ww::stiFtnRef:
193 1 : eLookup = RES_POOLCHR_FOOTNOTE;
194 1 : break;
195 : case ww::stiLnn:
196 0 : eLookup = RES_POOLCHR_LINENUM;
197 0 : break;
198 : case ww::stiPgn:
199 9 : eLookup = RES_POOLCHR_PAGENO;
200 9 : break;
201 : case ww::stiEdnRef:
202 1 : eLookup = RES_POOLCHR_ENDNOTE;
203 1 : break;
204 : case ww::stiHyperlink:
205 9 : eLookup = RES_POOLCHR_INET_NORMAL;
206 9 : break;
207 : case ww::stiHyperlinkFollowed:
208 1 : eLookup = RES_POOLCHR_INET_VISIT;
209 1 : break;
210 : case ww::stiStrong:
211 1 : eLookup = RES_POOLCHR_HTML_STRONG;
212 1 : break;
213 : case ww::stiEmphasis:
214 1 : eLookup = RES_POOLCHR_HTML_EMPHASIS;
215 1 : break;
216 : default:
217 71 : eLookup = RES_POOLCHR_NORMAL_END;
218 71 : break;
219 : }
220 94 : SwCharFmt *pRet = 0;
221 94 : if (eLookup != RES_POOLCHR_NORMAL_END)
222 23 : pRet = mrDoc.GetCharFmtFromPool( static_cast< sal_uInt16 >(eLookup) );
223 94 : return pRet;
224 : }
225 :
226 137 : SwCharFmt* MapperImpl<SwCharFmt>::GetStyle(const String &rName)
227 : {
228 137 : return sw::util::GetCharStyle(mrDoc, rName);
229 : }
230 :
231 64 : SwCharFmt* MapperImpl<SwCharFmt>::MakeStyle(const String &rName)
232 : {
233 64 : return mrDoc.MakeCharFmt(rName, mrDoc.GetDfltCharFmt());
234 : }
235 :
236 78 : template<class C> class StyleMapperImpl
237 : {
238 : private:
239 : MapperImpl<C> maHelper;
240 : std::set<const C*> maUsedStyles;
241 : C* MakeNonCollidingStyle(const String& rName);
242 : public:
243 : typedef std::pair<C*, bool> StyleResult;
244 78 : StyleMapperImpl(SwDoc &rDoc) : maHelper(rDoc) {}
245 : StyleResult GetStyle(const String& rName, ww::sti eSti);
246 : };
247 :
248 : template<class C>
249 : typename StyleMapperImpl<C>::StyleResult
250 359 : StyleMapperImpl<C>::GetStyle(const String& rName, ww::sti eSti)
251 : {
252 359 : C *pRet = maHelper.GetBuiltInStyle(eSti);
253 :
254 : //If we've used it once, don't reuse it
255 359 : if (pRet && (maUsedStyles.end() != maUsedStyles.find(pRet)))
256 2 : pRet = 0;
257 :
258 359 : if (!pRet)
259 : {
260 165 : pRet = maHelper.GetStyle(rName);
261 : //If we've used it once, don't reuse it
262 165 : if (pRet && (maUsedStyles.end() != maUsedStyles.find(pRet)))
263 1 : pRet = 0;
264 : }
265 :
266 359 : bool bStyExist = pRet ? true : false;
267 :
268 359 : if (!pRet)
269 : {
270 127 : String aName(rName);
271 127 : xub_StrLen nPos = aName.Search(',');
272 : // No commas allow in SW style names
273 127 : if (STRING_NOTFOUND != nPos)
274 0 : aName.Erase(nPos);
275 127 : pRet = MakeNonCollidingStyle(aName);
276 : }
277 :
278 359 : if (pRet)
279 359 : maUsedStyles.insert(pRet);
280 :
281 359 : return StyleResult(pRet, bStyExist);
282 : }
283 :
284 : template<class C>
285 127 : C* StyleMapperImpl<C>::MakeNonCollidingStyle(const String& rName)
286 : {
287 127 : String aName(rName);
288 127 : C* pColl = 0;
289 :
290 127 : if (0 != (pColl = maHelper.GetStyle(aName)))
291 : {
292 : //If the style collides first stick WW- in front of it, unless
293 : //it already has it and then successively add a larger and
294 : //larger number after it, its got to work at some stage!
295 1 : if (!aName.EqualsIgnoreCaseAscii("WW-", 0, 3))
296 1 : aName.InsertAscii("WW-" , 0);
297 :
298 1 : sal_Int32 nI = 1;
299 2 : while (
300 : 0 != (pColl = maHelper.GetStyle(aName)) &&
301 : (nI < SAL_MAX_INT32)
302 : )
303 : {
304 0 : aName += String::CreateFromInt32(nI++);
305 : }
306 : }
307 :
308 127 : return pColl ? 0 : maHelper.MakeStyle(aName);
309 : }
310 :
311 1031 : String FindBestMSSubstituteFont(const String &rFont)
312 : {
313 1031 : String sRet;
314 1031 : if (sw::util::IsStarSymbol(rFont))
315 3 : sRet.ASSIGN_CONST_ASC("Arial Unicode MS");
316 : else
317 1028 : sRet = GetSubsFontName(rFont, SUBSFONT_ONLYONE | SUBSFONT_MS);
318 1031 : return sRet;
319 : }
320 :
321 : //Utility to remove entries before a given starting position
322 : class IfBeforeStart
323 : : public std::unary_function<const sw::util::CharRunEntry&, bool>
324 : {
325 : private:
326 : xub_StrLen mnStart;
327 : public:
328 129 : IfBeforeStart(xub_StrLen nStart) : mnStart(nStart) {}
329 129 : bool operator()(const sw::util::CharRunEntry &rEntry) const
330 : {
331 129 : return rEntry.mnEndPos < mnStart;
332 : }
333 : };
334 : }
335 :
336 : namespace sw
337 : {
338 : namespace util
339 : {
340 :
341 4 : bool IsPlausableSingleWordSection(const SwFrmFmt &rTitleFmt,
342 : const SwFrmFmt &rFollowFmt)
343 : {
344 4 : bool bPlausableTitlePage = true;
345 :
346 4 : const SwFmtCol& rFirstCols = rTitleFmt.GetCol();
347 4 : const SwFmtCol& rFollowCols = rFollowFmt.GetCol();
348 4 : const SwColumns& rFirstColumns = rFirstCols.GetColumns();
349 4 : const SwColumns& rFollowColumns = rFollowCols.GetColumns();
350 4 : const SvxLRSpaceItem &rOneLR = rTitleFmt.GetLRSpace();
351 4 : const SvxLRSpaceItem &rTwoLR= rFollowFmt.GetLRSpace();
352 4 : const SwFmtFrmSize& rFirstFrmSize = rTitleFmt.GetFrmSize();
353 4 : const SwFmtFrmSize& rFollowFrmSize = rFollowFmt.GetFrmSize();
354 :
355 4 : if (rFirstColumns.size() != rFollowColumns.size())
356 : {
357 : //e.g. #i4320#
358 0 : bPlausableTitlePage = false;
359 : }
360 4 : else if (rOneLR != rTwoLR)
361 0 : bPlausableTitlePage = false;
362 4 : else if (rFirstFrmSize != rFollowFrmSize)
363 0 : bPlausableTitlePage = false;
364 : else
365 : {
366 4 : HdFtDistanceGlue aOne(rTitleFmt.GetAttrSet());
367 4 : HdFtDistanceGlue aTwo(rFollowFmt.GetAttrSet());
368 : //e.g. #i14509#
369 4 : if (!aOne.EqualTopBottom(aTwo))
370 0 : bPlausableTitlePage = false;
371 : }
372 4 : return bPlausableTitlePage;
373 : }
374 :
375 101 : HdFtDistanceGlue::HdFtDistanceGlue(const SfxItemSet &rPage)
376 : {
377 101 : if (const SvxBoxItem *pBox = HasItem<SvxBoxItem>(rPage, RES_BOX))
378 : {
379 101 : dyaHdrTop = pBox->CalcLineSpace(BOX_LINE_TOP);
380 101 : dyaHdrBottom = pBox->CalcLineSpace(BOX_LINE_BOTTOM);
381 : }
382 : else
383 : {
384 0 : dyaHdrTop = dyaHdrBottom = 0;
385 0 : dyaHdrBottom = 0;
386 : }
387 : const SvxULSpaceItem &rUL =
388 101 : ItemGet<SvxULSpaceItem>(rPage, RES_UL_SPACE);
389 101 : dyaHdrTop = dyaHdrTop + rUL.GetUpper();
390 101 : dyaHdrBottom = dyaHdrBottom + rUL.GetLower();
391 :
392 101 : dyaTop = dyaHdrTop;
393 101 : dyaBottom = dyaHdrBottom;
394 :
395 : using sw::types::msword_cast;
396 :
397 101 : const SwFmtHeader *pHd = HasItem<SwFmtHeader>(rPage, RES_HEADER);
398 101 : if (pHd && pHd->IsActive() && pHd->GetHeaderFmt())
399 : {
400 11 : mbHasHeader = true;
401 11 : dyaTop = dyaTop + static_cast< sal_uInt16 >( (myImplHelpers::CalcHdDist(*(pHd->GetHeaderFmt()))) );
402 : }
403 : else
404 90 : mbHasHeader = false;
405 :
406 101 : const SwFmtFooter *pFt = HasItem<SwFmtFooter>(rPage, RES_FOOTER);
407 101 : if (pFt && pFt->IsActive() && pFt->GetFooterFmt())
408 : {
409 11 : mbHasFooter = true;
410 11 : dyaBottom = dyaBottom + static_cast< sal_uInt16 >( (myImplHelpers::CalcFtDist(*(pFt->GetFooterFmt()))) );
411 : }
412 : else
413 90 : mbHasFooter = false;
414 101 : }
415 :
416 4 : bool HdFtDistanceGlue::EqualTopBottom(const HdFtDistanceGlue &rOther)
417 : const
418 : {
419 4 : return (dyaTop == rOther.dyaTop && dyaBottom == rOther.dyaBottom);
420 : }
421 :
422 39 : ParaStyleMapper::ParaStyleMapper(SwDoc &rDoc)
423 39 : : mpImpl(new myImplHelpers::StyleMapperImpl<SwTxtFmtColl>(rDoc))
424 : {
425 39 : }
426 :
427 39 : ParaStyleMapper::~ParaStyleMapper()
428 : {
429 39 : delete mpImpl;
430 39 : }
431 :
432 265 : ParaStyleMapper::StyleResult ParaStyleMapper::GetStyle(
433 : const String& rName, ww::sti eSti)
434 : {
435 265 : return mpImpl->GetStyle(rName, eSti);
436 : }
437 :
438 39 : CharStyleMapper::CharStyleMapper(SwDoc &rDoc)
439 39 : : mpImpl(new myImplHelpers::StyleMapperImpl<SwCharFmt>(rDoc))
440 : {
441 39 : }
442 :
443 39 : CharStyleMapper::~CharStyleMapper()
444 : {
445 39 : delete mpImpl;
446 39 : }
447 :
448 94 : CharStyleMapper::StyleResult CharStyleMapper::GetStyle(
449 : const String& rName, ww::sti eSti)
450 : {
451 94 : return mpImpl->GetStyle(rName, eSti);
452 : }
453 :
454 1031 : FontMapExport::FontMapExport(const String &rFamilyName)
455 : {
456 1031 : xub_StrLen nIndex = 0;
457 1031 : msPrimary = GetNextFontToken(rFamilyName, nIndex);
458 1031 : msSecondary = myImplHelpers::FindBestMSSubstituteFont(msPrimary);
459 1031 : if (!msSecondary.Len() && nIndex != STRING_NOTFOUND)
460 0 : msSecondary = GetNextFontToken(rFamilyName, nIndex);
461 1031 : }
462 :
463 25791 : bool ItemSort::operator()(sal_uInt16 nA, sal_uInt16 nB) const
464 : {
465 : /*
466 : #i24291#
467 : All we want to do is ensure for now is that if a charfmt exist
468 : in the character properties that it rises to the top and is
469 : exported first. In the future we might find more ordering
470 : depandancies for export, in which case this is the place to do
471 : it
472 : */
473 25791 : if (nA == nB)
474 2 : return false;
475 25789 : if (nA == RES_TXTATR_CHARFMT)
476 94 : return true;
477 25695 : if (nB == RES_TXTATR_CHARFMT)
478 93 : return false;
479 25602 : return nA < nB;
480 : }
481 :
482 197 : CharRuns GetPseudoCharRuns(const SwTxtNode& rTxtNd,
483 : xub_StrLen nTxtStart, bool bSplitOnCharSet)
484 : {
485 197 : const String &rTxt = rTxtNd.GetTxt();
486 :
487 197 : bool bParaIsRTL = false;
488 : OSL_ENSURE(rTxtNd.GetDoc(), "No document for node?, suspicious");
489 197 : if (rTxtNd.GetDoc())
490 : {
491 394 : if (FRMDIR_HORI_RIGHT_TOP ==
492 394 : rTxtNd.GetDoc()->GetTextDirection(SwPosition(rTxtNd)))
493 : {
494 0 : bParaIsRTL = true;
495 : }
496 : }
497 :
498 : using namespace ::com::sun::star::i18n;
499 :
500 197 : sal_uInt16 nScript = i18n::ScriptType::LATIN;
501 197 : if (rTxt.Len() && pBreakIt && pBreakIt->GetBreakIter().is())
502 129 : nScript = pBreakIt->GetBreakIter()->getScriptType(rTxt, 0);
503 :
504 : rtl_TextEncoding eChrSet = ItemGet<SvxFontItem>(rTxtNd,
505 197 : GetWhichOfScript(RES_CHRATR_FONT, nScript)).GetCharSet();
506 197 : eChrSet = GetExtendedTextEncoding(eChrSet);
507 :
508 197 : CharRuns aRunChanges;
509 :
510 197 : if (!rTxt.Len())
511 : {
512 : aRunChanges.push_back(CharRunEntry(0, nScript, eChrSet,
513 68 : bParaIsRTL));
514 : return aRunChanges;
515 : }
516 :
517 : typedef std::pair<int32_t, bool> DirEntry;
518 : typedef std::vector<DirEntry> DirChanges;
519 : typedef DirChanges::const_iterator cDirIter;
520 :
521 : typedef std::pair<xub_StrLen, sal_Int16> CharSetEntry;
522 : typedef std::vector<CharSetEntry> CharSetChanges;
523 : typedef CharSetChanges::const_iterator cCharSetIter;
524 :
525 : typedef std::pair<xub_StrLen, sal_uInt16> ScriptEntry;
526 : typedef std::vector<ScriptEntry> ScriptChanges;
527 : typedef ScriptChanges::const_iterator cScriptIter;
528 :
529 129 : DirChanges aDirChanges;
530 129 : CharSetChanges aCharSets;
531 129 : ScriptChanges aScripts;
532 :
533 129 : UBiDiDirection eDefaultDir = bParaIsRTL ? UBIDI_RTL : UBIDI_LTR;
534 129 : UErrorCode nError = U_ZERO_ERROR;
535 129 : UBiDi* pBidi = ubidi_openSized(rTxt.Len(), 0, &nError);
536 258 : ubidi_setPara(pBidi, reinterpret_cast<const UChar *>(rTxt.GetBuffer()), rTxt.Len(),
537 387 : static_cast< UBiDiLevel >(eDefaultDir), 0, &nError);
538 :
539 129 : sal_Int32 nCount = ubidi_countRuns(pBidi, &nError);
540 129 : aDirChanges.reserve(nCount);
541 :
542 129 : int32_t nStart = 0;
543 : int32_t nEnd;
544 : UBiDiLevel nCurrDir;
545 :
546 258 : for (sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx)
547 : {
548 129 : ubidi_getLogicalRun(pBidi, nStart, &nEnd, &nCurrDir);
549 : /*
550 : UBiDiLevel is the type of the level values in this BiDi
551 : implementation.
552 :
553 : It holds an embedding level and indicates the visual direction
554 : by its bit 0 (even/odd value).
555 :
556 : The value for UBIDI_DEFAULT_LTR is even and the one for
557 : UBIDI_DEFAULT_RTL is odd
558 : */
559 129 : aDirChanges.push_back(DirEntry(nEnd, nCurrDir & 0x1));
560 129 : nStart = nEnd;
561 : }
562 129 : ubidi_close(pBidi);
563 :
564 129 : if (bSplitOnCharSet)
565 : {
566 : //Split unicode text into plausable 8bit ranges for export to
567 : //older non unicode aware format
568 0 : xub_StrLen nLen = rTxt.Len();
569 0 : xub_StrLen nPos = 0;
570 0 : while (nPos != nLen)
571 : {
572 : rtl_TextEncoding ScriptType =
573 0 : getBestMSEncodingByChar(rTxt.GetChar(nPos++));
574 0 : while (
575 : (nPos != nLen) &&
576 0 : (ScriptType == getBestMSEncodingByChar(rTxt.GetChar(nPos)))
577 : )
578 : {
579 0 : ++nPos;
580 : }
581 :
582 0 : aCharSets.push_back(CharSetEntry(nPos, ScriptType));
583 : }
584 : }
585 :
586 : using sw::types::writer_cast;
587 :
588 129 : if (pBreakIt && pBreakIt->GetBreakIter().is())
589 : {
590 129 : xub_StrLen nLen = rTxt.Len();
591 129 : xub_StrLen nPos = 0;
592 387 : while (nPos < nLen)
593 : {
594 258 : sal_Int32 nEnd2 = pBreakIt->GetBreakIter()->endOfScript(rTxt, nPos,
595 129 : nScript);
596 129 : if (nEnd2 < 0)
597 0 : break;
598 129 : nPos = static_cast< xub_StrLen >(nEnd2);
599 129 : aScripts.push_back(ScriptEntry(nPos, nScript));
600 129 : nScript = pBreakIt->GetBreakIter()->getScriptType(rTxt, nPos);
601 : }
602 : }
603 :
604 129 : cDirIter aBiDiEnd = aDirChanges.end();
605 129 : cCharSetIter aCharSetEnd = aCharSets.end();
606 129 : cScriptIter aScriptEnd = aScripts.end();
607 :
608 129 : cDirIter aBiDiIter = aDirChanges.begin();
609 129 : cCharSetIter aCharSetIter = aCharSets.begin();
610 129 : cScriptIter aScriptIter = aScripts.begin();
611 :
612 129 : bool bCharIsRTL = bParaIsRTL;
613 :
614 645 : while (
615 258 : aBiDiIter != aBiDiEnd ||
616 129 : aCharSetIter != aCharSetEnd ||
617 129 : aScriptIter != aScriptEnd
618 : )
619 : {
620 129 : xub_StrLen nMinPos = rTxt.Len();
621 :
622 129 : if (aBiDiIter != aBiDiEnd)
623 : {
624 129 : if (aBiDiIter->first < nMinPos)
625 0 : nMinPos = static_cast< xub_StrLen >(aBiDiIter->first);
626 129 : bCharIsRTL = aBiDiIter->second;
627 : }
628 :
629 129 : if (aCharSetIter != aCharSetEnd)
630 : {
631 0 : if (aCharSetIter->first < nMinPos)
632 0 : nMinPos = aCharSetIter->first;
633 0 : eChrSet = aCharSetIter->second;
634 : }
635 :
636 129 : if (aScriptIter != aScriptEnd)
637 : {
638 129 : if (aScriptIter->first < nMinPos)
639 0 : nMinPos = aScriptIter->first;
640 129 : nScript = aScriptIter->second;
641 : }
642 :
643 : aRunChanges.push_back(
644 129 : CharRunEntry(nMinPos, nScript, eChrSet, bCharIsRTL));
645 :
646 129 : if (aBiDiIter != aBiDiEnd)
647 : {
648 129 : if (aBiDiIter->first == nMinPos)
649 129 : ++aBiDiIter;
650 : }
651 :
652 129 : if (aCharSetIter != aCharSetEnd)
653 : {
654 0 : if (aCharSetIter->first == nMinPos)
655 0 : ++aCharSetIter;
656 : }
657 :
658 129 : if (aScriptIter != aScriptEnd)
659 : {
660 129 : if (aScriptIter->first == nMinPos)
661 129 : ++aScriptIter;
662 : }
663 : }
664 :
665 : aRunChanges.erase(std::remove_if(aRunChanges.begin(),
666 129 : aRunChanges.end(), myImplHelpers::IfBeforeStart(nTxtStart)), aRunChanges.end());
667 :
668 129 : return aRunChanges;
669 : }
670 : }
671 :
672 : namespace ms
673 : {
674 1228 : sal_uInt8 rtl_TextEncodingToWinCharset(rtl_TextEncoding eTextEncoding)
675 : {
676 : sal_uInt8 nRet =
677 1228 : rtl_getBestWindowsCharsetFromTextEncoding(eTextEncoding);
678 1228 : switch (eTextEncoding)
679 : {
680 : case RTL_TEXTENCODING_DONTKNOW:
681 : case RTL_TEXTENCODING_UCS2:
682 : case RTL_TEXTENCODING_UTF7:
683 : case RTL_TEXTENCODING_UTF8:
684 : case RTL_TEXTENCODING_JAVA_UTF8:
685 : OSL_ENSURE(nRet != 0x80, "This method may be redundant");
686 950 : nRet = 0x80;
687 950 : break;
688 : default:
689 278 : break;
690 : }
691 1228 : return nRet;
692 : }
693 :
694 13 : long DateTime2DTTM( const DateTime& rDT )
695 : {
696 : /*
697 : mint short :6 0000003F minutes (0-59)
698 : hr short :5 000007C0 hours (0-23)
699 : dom short :5 0000F800 days of month (1-31)
700 : mon short :4 000F0000 months (1-12)
701 : yr short :9 1FF00000 years (1900-2411)-1900
702 : wdy short :3 E0000000 weekday(Sunday=0
703 : Monday=1
704 : ( wdy can be ignored ) Tuesday=2
705 : Wednesday=3
706 : Thursday=4
707 : Friday=5
708 : Saturday=6)
709 : */
710 :
711 13 : if ( rDT.GetDate() == 0L )
712 4 : return 0L;
713 9 : long nDT = ( rDT.GetDayOfWeek() + 1 ) % 7;
714 9 : nDT <<= 9;
715 9 : nDT += ( rDT.GetYear() - 1900 ) & 0x1ff;
716 9 : nDT <<= 4;
717 9 : nDT += rDT.GetMonth() & 0xf;
718 9 : nDT <<= 5;
719 9 : nDT += rDT.GetDay() & 0x1f;
720 9 : nDT <<= 5;
721 9 : nDT += rDT.GetHour() & 0x1f;
722 9 : nDT <<= 6;
723 9 : nDT += rDT.GetMin() & 0x3f;
724 9 : return nDT;
725 : }
726 :
727 0 : sal_uLong MSDateTimeFormatToSwFormat(String& rParams,
728 : SvNumberFormatter *pFormatter, sal_uInt16 &rLang, bool bHijri,
729 : sal_uInt16 nDocLang)
730 : {
731 : // tell the Formatter about the new entry
732 0 : sal_Int32 nCheckPos = 0;
733 0 : short nType = NUMBERFORMAT_DEFINED;
734 0 : sal_uInt32 nKey = 0;
735 :
736 0 : SwapQuotesInField(rParams);
737 :
738 : // Force to Japanese when finding one of 'geaE'
739 0 : rtl::OUString sJChars( "geE" );
740 0 : bool bForceJapanese = ( STRING_NOTFOUND != rParams.SearchChar( sJChars.getStr() ) );
741 0 : if ( bForceJapanese )
742 : {
743 : rParams.SearchAndReplaceAll( rtl::OUString( "ee" ),
744 0 : rtl::OUString( "yyyy" ) );
745 : rParams.SearchAndReplaceAll( rtl::OUString( "EE" ),
746 0 : rtl::OUString( "YYYY" ) );
747 : }
748 0 : if (LANGUAGE_FRENCH != nDocLang)
749 : {
750 : // Handle the 'a' case here
751 0 : xub_StrLen nLastPos = 0;
752 0 : do
753 : {
754 0 : xub_StrLen nPos = rParams.Search( 'a', nLastPos + 1 );
755 0 : bForceJapanese |= ( nPos != STRING_NOTFOUND && IsNotAM( rParams, nPos ) );
756 0 : nLastPos = nPos;
757 : } while ( STRING_NOTFOUND != nLastPos );
758 : }
759 :
760 : // Force to NatNum when finding one of 'oOA'
761 0 : String sOldParams( rParams );
762 : rParams.SearchAndReplaceAll( rtl::OUString( "o" ),
763 0 : rtl::OUString( "m" ) );
764 : rParams.SearchAndReplaceAll( rtl::OUString( "O" ),
765 0 : rtl::OUString( "M" ) );
766 0 : bool bForceNatNum = !sOldParams.Equals( rParams );
767 0 : if (LANGUAGE_FRENCH != nDocLang)
768 : {
769 : // Handle the 'A' case here
770 0 : xub_StrLen nLastPos = 0;
771 0 : do
772 : {
773 0 : xub_StrLen nPos = rParams.Search( 'A', nLastPos + 1 );
774 0 : bool bIsCharA = ( nPos != STRING_NOTFOUND && IsNotAM( rParams, nPos ) );
775 0 : bForceNatNum |= bIsCharA;
776 0 : if ( bIsCharA )
777 0 : rParams.SetChar( nPos, 'D' );
778 0 : nLastPos = nPos;
779 : } while ( STRING_NOTFOUND != nLastPos );
780 : }
781 :
782 0 : xub_StrLen nLen = rParams.Len();
783 0 : xub_StrLen nI = 0;
784 0 : while (nI < nLen)
785 : {
786 0 : if (rParams.GetChar(nI) == '\\')
787 0 : nI++;
788 0 : else if (rParams.GetChar(nI) == '\"')
789 : {
790 0 : ++nI;
791 : //While not at the end and not at an unescaped end quote
792 0 : while ((nI < nLen) && (!(rParams.GetChar(nI) == '\"') && (rParams.GetChar(nI-1) != '\\')))
793 0 : ++nI;
794 : }
795 : else //normal unquoted section
796 : {
797 0 : sal_Unicode nChar = rParams.GetChar(nI);
798 :
799 : // Change the localized word string to english
800 0 : switch ( nDocLang )
801 : {
802 : case LANGUAGE_FRENCH:
803 0 : if ( ( nChar == 'a' || nChar == 'A' ) && IsNotAM(rParams, nI) )
804 0 : rParams.SetChar(nI, 'Y');
805 0 : break;
806 : default:
807 : ;
808 : }
809 0 : if (nChar == '/')
810 : {
811 : // MM We have to escape '/' in case it's used as a char
812 0 : rParams.Replace(nI, 1, rtl::OUString("\\/"));
813 0 : nI++;
814 0 : nLen++;
815 : }
816 :
817 : // Deal with language differences in date format expression.
818 : // Should be made with i18n framework.
819 : // The list of the mappings and of those "special" locales is to be found at:
820 : // http://l10n.openoffice.org/i18n_framework/LocaleData.html
821 0 : if ( !bForceJapanese && !bForceNatNum )
822 : {
823 : // Convert to the localized equivalent for OOo
824 0 : switch ( rLang )
825 : {
826 : case LANGUAGE_FINNISH:
827 : {
828 0 : if (nChar == 'y' || nChar == 'Y')
829 0 : rParams.SetChar (nI, 'V');
830 0 : else if (nChar == 'm' || nChar == 'M')
831 0 : rParams.SetChar (nI, 'K');
832 0 : else if (nChar == 'd' || nChar == 'D')
833 0 : rParams.SetChar (nI, 'P');
834 0 : else if (nChar == 'h' || nChar == 'H')
835 0 : rParams.SetChar (nI, 'T');
836 : }
837 0 : break;
838 : case LANGUAGE_DANISH:
839 : case LANGUAGE_NORWEGIAN:
840 : case LANGUAGE_NORWEGIAN_BOKMAL:
841 : case LANGUAGE_NORWEGIAN_NYNORSK:
842 : case LANGUAGE_SWEDISH:
843 : case LANGUAGE_SWEDISH_FINLAND:
844 : {
845 0 : if (nChar == 'h' || nChar == 'H')
846 0 : rParams.SetChar (nI, 'T');
847 : }
848 0 : break;
849 : case LANGUAGE_PORTUGUESE:
850 : case LANGUAGE_PORTUGUESE_BRAZILIAN:
851 : case LANGUAGE_SPANISH_MODERN:
852 : case LANGUAGE_SPANISH_DATED:
853 : case LANGUAGE_SPANISH_MEXICAN:
854 : case LANGUAGE_SPANISH_GUATEMALA:
855 : case LANGUAGE_SPANISH_COSTARICA:
856 : case LANGUAGE_SPANISH_PANAMA:
857 : case LANGUAGE_SPANISH_DOMINICAN_REPUBLIC:
858 : case LANGUAGE_SPANISH_VENEZUELA:
859 : case LANGUAGE_SPANISH_COLOMBIA:
860 : case LANGUAGE_SPANISH_PERU:
861 : case LANGUAGE_SPANISH_ARGENTINA:
862 : case LANGUAGE_SPANISH_ECUADOR:
863 : case LANGUAGE_SPANISH_CHILE:
864 : case LANGUAGE_SPANISH_URUGUAY:
865 : case LANGUAGE_SPANISH_PARAGUAY:
866 : case LANGUAGE_SPANISH_BOLIVIA:
867 : case LANGUAGE_SPANISH_EL_SALVADOR:
868 : case LANGUAGE_SPANISH_HONDURAS:
869 : case LANGUAGE_SPANISH_NICARAGUA:
870 : case LANGUAGE_SPANISH_PUERTO_RICO:
871 : {
872 0 : if (nChar == 'a' || nChar == 'A')
873 0 : rParams.SetChar (nI, 'O');
874 0 : else if (nChar == 'y' || nChar == 'Y')
875 0 : rParams.SetChar (nI, 'A');
876 : }
877 0 : break;
878 : case LANGUAGE_DUTCH:
879 : case LANGUAGE_DUTCH_BELGIAN:
880 : {
881 0 : if (nChar == 'y' || nChar == 'Y')
882 0 : rParams.SetChar (nI, 'J');
883 0 : else if (nChar == 'u' || nChar == 'U')
884 0 : rParams.SetChar (nI, 'H');
885 : }
886 0 : break;
887 : case LANGUAGE_ITALIAN:
888 : case LANGUAGE_ITALIAN_SWISS:
889 : {
890 0 : if (nChar == 'a' || nChar == 'A')
891 0 : rParams.SetChar (nI, 'O');
892 0 : else if (nChar == 'g' || nChar == 'G')
893 0 : rParams.SetChar (nI, 'X');
894 0 : else if (nChar == 'y' || nChar == 'Y')
895 0 : rParams.SetChar(nI, 'A');
896 0 : else if (nChar == 'd' || nChar == 'D')
897 0 : rParams.SetChar (nI, 'G');
898 : }
899 0 : break;
900 : case LANGUAGE_GERMAN:
901 : case LANGUAGE_GERMAN_SWISS:
902 : case LANGUAGE_GERMAN_AUSTRIAN:
903 : case LANGUAGE_GERMAN_LUXEMBOURG:
904 : case LANGUAGE_GERMAN_LIECHTENSTEIN:
905 : {
906 0 : if (nChar == 'y' || nChar == 'Y')
907 0 : rParams.SetChar (nI, 'J');
908 0 : else if (nChar == 'd' || nChar == 'D')
909 0 : rParams.SetChar (nI, 'T');
910 : }
911 0 : break;
912 : case LANGUAGE_FRENCH:
913 : case LANGUAGE_FRENCH_BELGIAN:
914 : case LANGUAGE_FRENCH_CANADIAN:
915 : case LANGUAGE_FRENCH_SWISS:
916 : case LANGUAGE_FRENCH_LUXEMBOURG:
917 : case LANGUAGE_FRENCH_MONACO:
918 : {
919 0 : if (nChar == 'y' || nChar == 'Y' || nChar == 'a')
920 0 : rParams.SetChar (nI, 'A');
921 0 : else if (nChar == 'd' || nChar == 'D' || nChar == 'j')
922 0 : rParams.SetChar (nI, 'J');
923 : }
924 0 : break;
925 : default:
926 : {
927 : ; // Nothing
928 : }
929 : }
930 : }
931 : }
932 0 : ++nI;
933 : }
934 :
935 0 : if (bForceNatNum)
936 0 : bForceJapanese = true;
937 :
938 0 : if (bForceJapanese)
939 0 : rLang = LANGUAGE_JAPANESE;
940 :
941 0 : if (bForceNatNum)
942 0 : rParams.Insert(rtl::OUString("[NatNum1][$-411]"),0);
943 :
944 0 : if (bHijri)
945 0 : rParams.Insert(rtl::OUString("[~hijri]"), 0);
946 :
947 0 : OUString sTemp(rParams);
948 0 : pFormatter->PutEntry(sTemp, nCheckPos, nType, nKey, rLang);
949 0 : rParams = sTemp;
950 :
951 0 : return nKey;
952 : }
953 :
954 0 : bool IsNotAM(String& rParams, xub_StrLen nPos)
955 : {
956 : return (
957 0 : (nPos == rParams.Len() - 1) ||
958 : (
959 0 : (rParams.GetChar(nPos+1) != 'M') &&
960 0 : (rParams.GetChar(nPos+1) != 'm')
961 : )
962 0 : );
963 : }
964 :
965 0 : void SwapQuotesInField(String &rFmt)
966 : {
967 : //Swap unescaped " and ' with ' and "
968 0 : xub_StrLen nLen = rFmt.Len();
969 0 : for (xub_StrLen nI = 0; nI < nLen; ++nI)
970 : {
971 0 : if ((rFmt.GetChar(nI) == '\"') && (!nI || rFmt.GetChar(nI-1) != '\\'))
972 0 : rFmt.SetChar(nI, '\'');
973 0 : else if ((rFmt.GetChar(nI) == '\'') && (!nI || rFmt.GetChar(nI-1) != '\\'))
974 0 : rFmt.SetChar(nI, '\"');
975 : }
976 0 : }
977 :
978 : }
979 : }
980 :
981 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|