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