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