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 44 : 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 44 : long nDist=0;
66 44 : const SwFmtFrmSize& rSz = rFmt.GetFrmSize();
67 :
68 : const SwHeaderAndFooterEatSpacingItem &rSpacingCtrl =
69 : sw::util::ItemGet<SwHeaderAndFooterEatSpacingItem>
70 44 : (rFmt, RES_HEADER_FOOTER_EAT_SPACING);
71 44 : if (rSpacingCtrl.GetValue())
72 44 : 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 44 : return nDist;
91 : }
92 :
93 22 : SwTwips CalcHdDist(const SwFrmFmt& rFmt)
94 : {
95 22 : return CalcHdFtDist(rFmt, rFmt.GetULSpace().GetUpper());
96 : }
97 :
98 22 : SwTwips CalcFtDist(const SwFrmFmt& rFmt)
99 : {
100 22 : 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 78 : 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 530 : SwTxtFmtColl* MapperImpl<SwTxtFmtColl>::GetBuiltInStyle(ww::sti eSti)
125 : {
126 530 : 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 530 : 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 530 : if (sal::static_int_cast< size_t >(eSti) < SAL_N_ELEMENTS(aArr) && aArr[eSti] != RES_NONE)
161 346 : pRet = mrDoc.GetTxtCollFromPool( static_cast< sal_uInt16 >(aArr[eSti]), false);
162 530 : return pRet;
163 : }
164 :
165 312 : SwTxtFmtColl* MapperImpl<SwTxtFmtColl>::GetStyle(const String &rName)
166 : {
167 312 : return sw::util::GetParaStyle(mrDoc, rName);
168 : }
169 :
170 126 : SwTxtFmtColl* MapperImpl<SwTxtFmtColl>::MakeStyle(const String &rName)
171 : {
172 : return mrDoc.MakeTxtFmtColl(rName,
173 126 : const_cast<SwTxtFmtColl *>(mrDoc.GetDfltTxtFmtColl()));
174 : }
175 :
176 : template<> class MapperImpl<SwCharFmt>
177 : {
178 : private:
179 : SwDoc &mrDoc;
180 : public:
181 78 : 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 188 : SwCharFmt* MapperImpl<SwCharFmt>::GetBuiltInStyle(ww::sti eSti)
188 : {
189 188 : RES_POOL_CHRFMT_TYPE eLookup = RES_POOLCHR_NORMAL_END;
190 188 : switch (eSti)
191 : {
192 : case ww::stiFtnRef:
193 2 : eLookup = RES_POOLCHR_FOOTNOTE;
194 2 : break;
195 : case ww::stiLnn:
196 0 : eLookup = RES_POOLCHR_LINENUM;
197 0 : break;
198 : case ww::stiPgn:
199 18 : eLookup = RES_POOLCHR_PAGENO;
200 18 : break;
201 : case ww::stiEdnRef:
202 2 : eLookup = RES_POOLCHR_ENDNOTE;
203 2 : break;
204 : case ww::stiHyperlink:
205 18 : eLookup = RES_POOLCHR_INET_NORMAL;
206 18 : break;
207 : case ww::stiHyperlinkFollowed:
208 2 : eLookup = RES_POOLCHR_INET_VISIT;
209 2 : break;
210 : case ww::stiStrong:
211 2 : eLookup = RES_POOLCHR_HTML_STRONG;
212 2 : break;
213 : case ww::stiEmphasis:
214 2 : eLookup = RES_POOLCHR_HTML_EMPHASIS;
215 2 : break;
216 : default:
217 142 : eLookup = RES_POOLCHR_NORMAL_END;
218 142 : break;
219 : }
220 188 : SwCharFmt *pRet = 0;
221 188 : if (eLookup != RES_POOLCHR_NORMAL_END)
222 46 : pRet = mrDoc.GetCharFmtFromPool( static_cast< sal_uInt16 >(eLookup) );
223 188 : return pRet;
224 : }
225 :
226 274 : SwCharFmt* MapperImpl<SwCharFmt>::GetStyle(const String &rName)
227 : {
228 274 : return sw::util::GetCharStyle(mrDoc, rName);
229 : }
230 :
231 128 : SwCharFmt* MapperImpl<SwCharFmt>::MakeStyle(const String &rName)
232 : {
233 128 : return mrDoc.MakeCharFmt(rName, mrDoc.GetDfltCharFmt());
234 : }
235 :
236 156 : 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 156 : 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 718 : StyleMapperImpl<C>::GetStyle(const String& rName, ww::sti eSti)
251 : {
252 718 : C *pRet = maHelper.GetBuiltInStyle(eSti);
253 :
254 : //If we've used it once, don't reuse it
255 718 : if (pRet && (maUsedStyles.end() != maUsedStyles.find(pRet)))
256 4 : pRet = 0;
257 :
258 718 : if (!pRet)
259 : {
260 330 : pRet = maHelper.GetStyle(rName);
261 : //If we've used it once, don't reuse it
262 330 : if (pRet && (maUsedStyles.end() != maUsedStyles.find(pRet)))
263 2 : pRet = 0;
264 : }
265 :
266 718 : bool bStyExist = pRet ? true : false;
267 :
268 718 : if (!pRet)
269 : {
270 254 : String aName(rName);
271 254 : xub_StrLen nPos = aName.Search(',');
272 : // No commas allow in SW style names
273 254 : if (STRING_NOTFOUND != nPos)
274 0 : aName.Erase(nPos);
275 254 : pRet = MakeNonCollidingStyle(aName);
276 : }
277 :
278 718 : if (pRet)
279 718 : maUsedStyles.insert(pRet);
280 :
281 718 : return StyleResult(pRet, bStyExist);
282 : }
283 :
284 : template<class C>
285 254 : C* StyleMapperImpl<C>::MakeNonCollidingStyle(const String& rName)
286 : {
287 254 : String aName(rName);
288 254 : C* pColl = 0;
289 :
290 254 : 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 2 : if (!aName.EqualsIgnoreCaseAscii("WW-", 0, 3))
296 2 : aName.InsertAscii("WW-" , 0);
297 :
298 2 : sal_Int32 nI = 1;
299 4 : while (
300 : 0 != (pColl = maHelper.GetStyle(aName)) &&
301 : (nI < SAL_MAX_INT32)
302 : )
303 : {
304 0 : aName += String::CreateFromInt32(nI++);
305 : }
306 : }
307 :
308 254 : return pColl ? 0 : maHelper.MakeStyle(aName);
309 : }
310 :
311 2032 : String FindBestMSSubstituteFont(const String &rFont)
312 : {
313 2032 : String sRet;
314 2032 : if (sw::util::IsStarSymbol(rFont))
315 6 : sRet.ASSIGN_CONST_ASC("Arial Unicode MS");
316 : else
317 2026 : sRet = GetSubsFontName(rFont, SUBSFONT_ONLYONE | SUBSFONT_MS);
318 2032 : 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 256 : IfBeforeStart(xub_StrLen nStart) : mnStart(nStart) {}
329 256 : bool operator()(const sw::util::CharRunEntry &rEntry) const
330 : {
331 256 : return rEntry.mnEndPos < mnStart;
332 : }
333 : };
334 : }
335 :
336 : namespace sw
337 : {
338 : namespace util
339 : {
340 :
341 8 : bool IsPlausableSingleWordSection(const SwFrmFmt &rTitleFmt,
342 : const SwFrmFmt &rFollowFmt)
343 : {
344 8 : bool bPlausableTitlePage = true;
345 :
346 8 : const SwFmtCol& rFirstCols = rTitleFmt.GetCol();
347 8 : const SwFmtCol& rFollowCols = rFollowFmt.GetCol();
348 8 : const SwColumns& rFirstColumns = rFirstCols.GetColumns();
349 8 : const SwColumns& rFollowColumns = rFollowCols.GetColumns();
350 8 : const SvxLRSpaceItem &rOneLR = rTitleFmt.GetLRSpace();
351 8 : const SvxLRSpaceItem &rTwoLR= rFollowFmt.GetLRSpace();
352 8 : const SwFmtFrmSize& rFirstFrmSize = rTitleFmt.GetFrmSize();
353 8 : const SwFmtFrmSize& rFollowFrmSize = rFollowFmt.GetFrmSize();
354 :
355 8 : if (rFirstColumns.size() != rFollowColumns.size())
356 : {
357 : //e.g. #i4320#
358 0 : bPlausableTitlePage = false;
359 : }
360 8 : else if (rOneLR != rTwoLR)
361 0 : bPlausableTitlePage = false;
362 8 : else if (rFirstFrmSize != rFollowFrmSize)
363 0 : bPlausableTitlePage = false;
364 : else
365 : {
366 8 : HdFtDistanceGlue aOne(rTitleFmt.GetAttrSet());
367 8 : HdFtDistanceGlue aTwo(rFollowFmt.GetAttrSet());
368 : //e.g. #i14509#
369 8 : if (!aOne.EqualTopBottom(aTwo))
370 0 : bPlausableTitlePage = false;
371 : }
372 8 : return bPlausableTitlePage;
373 : }
374 :
375 198 : HdFtDistanceGlue::HdFtDistanceGlue(const SfxItemSet &rPage)
376 : {
377 198 : if (const SvxBoxItem *pBox = HasItem<SvxBoxItem>(rPage, RES_BOX))
378 : {
379 198 : dyaHdrTop = pBox->CalcLineSpace(BOX_LINE_TOP);
380 198 : 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 198 : ItemGet<SvxULSpaceItem>(rPage, RES_UL_SPACE);
389 198 : dyaHdrTop = dyaHdrTop + rUL.GetUpper();
390 198 : dyaHdrBottom = dyaHdrBottom + rUL.GetLower();
391 :
392 198 : dyaTop = dyaHdrTop;
393 198 : dyaBottom = dyaHdrBottom;
394 :
395 : using sw::types::msword_cast;
396 :
397 198 : const SwFmtHeader *pHd = HasItem<SwFmtHeader>(rPage, RES_HEADER);
398 198 : if (pHd && pHd->IsActive() && pHd->GetHeaderFmt())
399 : {
400 22 : mbHasHeader = true;
401 22 : dyaTop = dyaTop + static_cast< sal_uInt16 >( (myImplHelpers::CalcHdDist(*(pHd->GetHeaderFmt()))) );
402 : }
403 : else
404 176 : mbHasHeader = false;
405 :
406 198 : const SwFmtFooter *pFt = HasItem<SwFmtFooter>(rPage, RES_FOOTER);
407 198 : if (pFt && pFt->IsActive() && pFt->GetFooterFmt())
408 : {
409 22 : mbHasFooter = true;
410 22 : dyaBottom = dyaBottom + static_cast< sal_uInt16 >( (myImplHelpers::CalcFtDist(*(pFt->GetFooterFmt()))) );
411 : }
412 : else
413 176 : mbHasFooter = false;
414 198 : }
415 :
416 8 : bool HdFtDistanceGlue::EqualTopBottom(const HdFtDistanceGlue &rOther)
417 : const
418 : {
419 8 : return (dyaTop == rOther.dyaTop && dyaBottom == rOther.dyaBottom);
420 : }
421 :
422 78 : ParaStyleMapper::ParaStyleMapper(SwDoc &rDoc)
423 78 : : mpImpl(new myImplHelpers::StyleMapperImpl<SwTxtFmtColl>(rDoc))
424 : {
425 78 : }
426 :
427 78 : ParaStyleMapper::~ParaStyleMapper()
428 : {
429 78 : delete mpImpl;
430 78 : }
431 :
432 530 : ParaStyleMapper::StyleResult ParaStyleMapper::GetStyle(
433 : const String& rName, ww::sti eSti)
434 : {
435 530 : return mpImpl->GetStyle(rName, eSti);
436 : }
437 :
438 78 : CharStyleMapper::CharStyleMapper(SwDoc &rDoc)
439 78 : : mpImpl(new myImplHelpers::StyleMapperImpl<SwCharFmt>(rDoc))
440 : {
441 78 : }
442 :
443 78 : CharStyleMapper::~CharStyleMapper()
444 : {
445 78 : delete mpImpl;
446 78 : }
447 :
448 188 : CharStyleMapper::StyleResult CharStyleMapper::GetStyle(
449 : const String& rName, ww::sti eSti)
450 : {
451 188 : return mpImpl->GetStyle(rName, eSti);
452 : }
453 :
454 2032 : FontMapExport::FontMapExport(const String &rFamilyName)
455 : {
456 2032 : xub_StrLen nIndex = 0;
457 2032 : msPrimary = GetNextFontToken(rFamilyName, nIndex);
458 2032 : msSecondary = myImplHelpers::FindBestMSSubstituteFont(msPrimary);
459 2032 : if (!msSecondary.Len() && nIndex != STRING_NOTFOUND)
460 0 : msSecondary = GetNextFontToken(rFamilyName, nIndex);
461 2032 : }
462 :
463 51010 : 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 51010 : if (nA == nB)
474 4 : return false;
475 51006 : if (nA == RES_TXTATR_CHARFMT)
476 184 : return true;
477 50822 : if (nB == RES_TXTATR_CHARFMT)
478 182 : return false;
479 50640 : return nA < nB;
480 : }
481 :
482 390 : CharRuns GetPseudoCharRuns(const SwTxtNode& rTxtNd,
483 : xub_StrLen nTxtStart, bool bSplitOnCharSet)
484 : {
485 390 : const String &rTxt = rTxtNd.GetTxt();
486 :
487 390 : bool bParaIsRTL = false;
488 : OSL_ENSURE(rTxtNd.GetDoc(), "No document for node?, suspicious");
489 390 : if (rTxtNd.GetDoc())
490 : {
491 780 : if (FRMDIR_HORI_RIGHT_TOP ==
492 780 : rTxtNd.GetDoc()->GetTextDirection(SwPosition(rTxtNd)))
493 : {
494 0 : bParaIsRTL = true;
495 : }
496 : }
497 :
498 : using namespace ::com::sun::star::i18n;
499 :
500 390 : sal_uInt16 nScript = i18n::ScriptType::LATIN;
501 390 : if (rTxt.Len() && pBreakIt && pBreakIt->GetBreakIter().is())
502 256 : nScript = pBreakIt->GetBreakIter()->getScriptType(rTxt, 0);
503 :
504 : rtl_TextEncoding eChrSet = ItemGet<SvxFontItem>(rTxtNd,
505 390 : GetWhichOfScript(RES_CHRATR_FONT, nScript)).GetCharSet();
506 390 : eChrSet = GetExtendedTextEncoding(eChrSet);
507 :
508 390 : CharRuns aRunChanges;
509 :
510 390 : if (!rTxt.Len())
511 : {
512 : aRunChanges.push_back(CharRunEntry(0, nScript, eChrSet,
513 134 : 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 256 : DirChanges aDirChanges;
530 256 : CharSetChanges aCharSets;
531 256 : ScriptChanges aScripts;
532 :
533 256 : UBiDiDirection eDefaultDir = bParaIsRTL ? UBIDI_RTL : UBIDI_LTR;
534 256 : UErrorCode nError = U_ZERO_ERROR;
535 256 : UBiDi* pBidi = ubidi_openSized(rTxt.Len(), 0, &nError);
536 512 : ubidi_setPara(pBidi, reinterpret_cast<const UChar *>(rTxt.GetBuffer()), rTxt.Len(),
537 768 : static_cast< UBiDiLevel >(eDefaultDir), 0, &nError);
538 :
539 256 : sal_Int32 nCount = ubidi_countRuns(pBidi, &nError);
540 256 : aDirChanges.reserve(nCount);
541 :
542 256 : int32_t nStart = 0;
543 : int32_t nEnd;
544 : UBiDiLevel nCurrDir;
545 :
546 512 : for (sal_Int32 nIdx = 0; nIdx < nCount; ++nIdx)
547 : {
548 256 : 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 256 : aDirChanges.push_back(DirEntry(nEnd, nCurrDir & 0x1));
560 256 : nStart = nEnd;
561 : }
562 256 : ubidi_close(pBidi);
563 :
564 256 : 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 256 : if (pBreakIt && pBreakIt->GetBreakIter().is())
589 : {
590 256 : xub_StrLen nLen = rTxt.Len();
591 256 : xub_StrLen nPos = 0;
592 768 : while (nPos < nLen)
593 : {
594 512 : sal_Int32 nEnd2 = pBreakIt->GetBreakIter()->endOfScript(rTxt, nPos,
595 256 : nScript);
596 256 : if (nEnd2 < 0)
597 0 : break;
598 256 : nPos = static_cast< xub_StrLen >(nEnd2);
599 256 : aScripts.push_back(ScriptEntry(nPos, nScript));
600 256 : nScript = pBreakIt->GetBreakIter()->getScriptType(rTxt, nPos);
601 : }
602 : }
603 :
604 256 : cDirIter aBiDiEnd = aDirChanges.end();
605 256 : cCharSetIter aCharSetEnd = aCharSets.end();
606 256 : cScriptIter aScriptEnd = aScripts.end();
607 :
608 256 : cDirIter aBiDiIter = aDirChanges.begin();
609 256 : cCharSetIter aCharSetIter = aCharSets.begin();
610 256 : cScriptIter aScriptIter = aScripts.begin();
611 :
612 256 : bool bCharIsRTL = bParaIsRTL;
613 :
614 1280 : while (
615 512 : aBiDiIter != aBiDiEnd ||
616 256 : aCharSetIter != aCharSetEnd ||
617 256 : aScriptIter != aScriptEnd
618 : )
619 : {
620 256 : xub_StrLen nMinPos = rTxt.Len();
621 :
622 256 : if (aBiDiIter != aBiDiEnd)
623 : {
624 256 : if (aBiDiIter->first < nMinPos)
625 0 : nMinPos = static_cast< xub_StrLen >(aBiDiIter->first);
626 256 : bCharIsRTL = aBiDiIter->second;
627 : }
628 :
629 256 : if (aCharSetIter != aCharSetEnd)
630 : {
631 0 : if (aCharSetIter->first < nMinPos)
632 0 : nMinPos = aCharSetIter->first;
633 0 : eChrSet = aCharSetIter->second;
634 : }
635 :
636 256 : if (aScriptIter != aScriptEnd)
637 : {
638 256 : if (aScriptIter->first < nMinPos)
639 0 : nMinPos = aScriptIter->first;
640 256 : nScript = aScriptIter->second;
641 : }
642 :
643 : aRunChanges.push_back(
644 256 : CharRunEntry(nMinPos, nScript, eChrSet, bCharIsRTL));
645 :
646 256 : if (aBiDiIter != aBiDiEnd)
647 : {
648 256 : if (aBiDiIter->first == nMinPos)
649 256 : ++aBiDiIter;
650 : }
651 :
652 256 : if (aCharSetIter != aCharSetEnd)
653 : {
654 0 : if (aCharSetIter->first == nMinPos)
655 0 : ++aCharSetIter;
656 : }
657 :
658 256 : if (aScriptIter != aScriptEnd)
659 : {
660 256 : if (aScriptIter->first == nMinPos)
661 256 : ++aScriptIter;
662 : }
663 : }
664 :
665 : aRunChanges.erase(std::remove_if(aRunChanges.begin(),
666 256 : aRunChanges.end(), myImplHelpers::IfBeforeStart(nTxtStart)), aRunChanges.end());
667 :
668 256 : return aRunChanges;
669 : }
670 : }
671 :
672 : namespace ms
673 : {
674 2424 : sal_uInt8 rtl_TextEncodingToWinCharset(rtl_TextEncoding eTextEncoding)
675 : {
676 : sal_uInt8 nRet =
677 2424 : rtl_getBestWindowsCharsetFromTextEncoding(eTextEncoding);
678 2424 : 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 1876 : nRet = 0x80;
687 1876 : break;
688 : default:
689 548 : break;
690 : }
691 2424 : return nRet;
692 : }
693 :
694 26 : 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 26 : if ( rDT.GetDate() == 0L )
712 8 : return 0L;
713 18 : long nDT = ( rDT.GetDayOfWeek() + 1 ) % 7;
714 18 : nDT <<= 9;
715 18 : nDT += ( rDT.GetYear() - 1900 ) & 0x1ff;
716 18 : nDT <<= 4;
717 18 : nDT += rDT.GetMonth() & 0xf;
718 18 : nDT <<= 5;
719 18 : nDT += rDT.GetDay() & 0x1f;
720 18 : nDT <<= 5;
721 18 : nDT += rDT.GetHour() & 0x1f;
722 18 : nDT <<= 6;
723 18 : nDT += rDT.GetMin() & 0x3f;
724 18 : 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: */
|