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 <boost/scoped_array.hpp>
21 : #include <tools/stream.hxx>
22 : #include <hintids.hxx>
23 : #include <rtl/tencinfo.h>
24 : #include <sfx2/printer.hxx>
25 : #include <editeng/fontitem.hxx>
26 : #include <editeng/langitem.hxx>
27 : #include <editeng/formatbreakitem.hxx>
28 : #include <editeng/scripttypeitem.hxx>
29 : #include <shellio.hxx>
30 : #include <doc.hxx>
31 : #include <IDocumentDeviceAccess.hxx>
32 : #include <IDocumentStylePoolAccess.hxx>
33 : #include <swtypes.hxx>
34 : #include <ndtxt.hxx>
35 : #include <pam.hxx>
36 : #include <frmatr.hxx>
37 : #include <fltini.hxx>
38 : #include <pagedesc.hxx>
39 : #include <breakit.hxx>
40 : #include <swerror.h>
41 : #include <statstr.hrc>
42 : #include <mdiexp.hxx>
43 : #include <poolfmt.hxx>
44 :
45 : #include <vcl/metric.hxx>
46 :
47 : #define ASC_BUFFLEN 4096
48 :
49 : class SwASCIIParser
50 : {
51 : SwDoc* pDoc;
52 : SwPaM* pPam;
53 : SvStream& rInput;
54 : sal_Char* pArr;
55 : const SwAsciiOptions& rOpt;
56 : SfxItemSet* pItemSet;
57 : long nFileSize;
58 : SvtScriptType nScript;
59 : bool bNewDoc;
60 :
61 : sal_uLong ReadChars();
62 : void InsertText( const OUString& rStr );
63 :
64 : public:
65 : SwASCIIParser( SwDoc* pD, const SwPaM& rCrsr, SvStream& rIn,
66 : bool bReadNewDoc, const SwAsciiOptions& rOpts );
67 : ~SwASCIIParser();
68 :
69 : sal_uLong CallParser();
70 : };
71 :
72 : // Call for the general reader interface
73 4 : sal_uLong AsciiReader::Read( SwDoc &rDoc, const OUString&, SwPaM &rPam, const OUString & )
74 : {
75 4 : if( !pStrm )
76 : {
77 : OSL_ENSURE( false, "ASCII read without a stream" );
78 0 : return ERR_SWG_READ_ERROR;
79 : }
80 :
81 : SwASCIIParser* pParser = new SwASCIIParser( &rDoc, rPam, *pStrm,
82 4 : !bInsertMode, aOpt.GetASCIIOpts() );
83 4 : sal_uLong nRet = pParser->CallParser();
84 :
85 4 : delete pParser;
86 : // after Read reset the options
87 4 : aOpt.ResetASCIIOpts();
88 4 : return nRet;
89 : }
90 :
91 4 : SwASCIIParser::SwASCIIParser(SwDoc* pD, const SwPaM& rCrsr, SvStream& rIn,
92 : bool bReadNewDoc, const SwAsciiOptions& rOpts)
93 : : pDoc(pD), rInput(rIn), rOpt(rOpts), nFileSize(0), nScript(SvtScriptType::NONE)
94 4 : , bNewDoc(bReadNewDoc)
95 : {
96 4 : pPam = new SwPaM( *rCrsr.GetPoint() );
97 4 : pArr = new sal_Char [ ASC_BUFFLEN + 2 ];
98 :
99 4 : pItemSet = new SfxItemSet( pDoc->GetAttrPool(),
100 : RES_CHRATR_FONT, RES_CHRATR_LANGUAGE,
101 : RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_LANGUAGE,
102 : RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_LANGUAGE,
103 4 : 0 );
104 :
105 : // set defaults from the options
106 4 : if( rOpt.GetLanguage() )
107 : {
108 3 : SvxLanguageItem aLang( (LanguageType)rOpt.GetLanguage(),
109 3 : RES_CHRATR_LANGUAGE );
110 3 : pItemSet->Put( aLang );
111 3 : pItemSet->Put( aLang, RES_CHRATR_CJK_LANGUAGE );
112 3 : pItemSet->Put( aLang, RES_CHRATR_CTL_LANGUAGE );
113 : }
114 4 : if( !rOpt.GetFontName().isEmpty() )
115 : {
116 3 : vcl::Font aTextFont( rOpt.GetFontName(), Size( 0, 10 ) );
117 3 : if( pDoc->getIDocumentDeviceAccess().getPrinter( false ) )
118 0 : aTextFont = pDoc->getIDocumentDeviceAccess().getPrinter( false )->GetFontMetric( aTextFont );
119 3 : SvxFontItem aFont( aTextFont.GetFamily(), aTextFont.GetName(),
120 9 : OUString(), aTextFont.GetPitch(), aTextFont.GetCharSet(), RES_CHRATR_FONT );
121 3 : pItemSet->Put( aFont );
122 3 : pItemSet->Put( aFont, RES_CHRATR_CJK_FONT );
123 6 : pItemSet->Put( aFont, RES_CHRATR_CTL_FONT );
124 : }
125 4 : }
126 :
127 4 : SwASCIIParser::~SwASCIIParser()
128 : {
129 4 : delete pPam;
130 4 : delete [] pArr;
131 4 : delete pItemSet;
132 4 : }
133 :
134 : // Calling the parser
135 4 : sal_uLong SwASCIIParser::CallParser()
136 : {
137 4 : rInput.Seek(STREAM_SEEK_TO_END);
138 4 : rInput.ResetError();
139 :
140 4 : nFileSize = rInput.Tell();
141 4 : rInput.Seek(STREAM_SEEK_TO_BEGIN);
142 4 : rInput.ResetError();
143 :
144 4 : ::StartProgress( STR_STATSTR_W4WREAD, 0, nFileSize, pDoc->GetDocShell() );
145 :
146 4 : SwPaM* pInsPam = 0;
147 4 : sal_Int32 nSttContent = 0;
148 4 : if (!bNewDoc)
149 : {
150 0 : const SwNodeIndex& rTmp = pPam->GetPoint()->nNode;
151 0 : pInsPam = new SwPaM( rTmp, rTmp, 0, -1 );
152 0 : nSttContent = pPam->GetPoint()->nContent.GetIndex();
153 : }
154 :
155 4 : SwTextFormatColl *pColl = 0;
156 :
157 4 : if (bNewDoc)
158 : {
159 4 : pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_HTML_PRE, false);
160 4 : if (!pColl)
161 0 : pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD,false);
162 4 : if (pColl)
163 4 : pDoc->SetTextFormatColl(*pPam, pColl);
164 : }
165 :
166 4 : sal_uLong nError = ReadChars();
167 :
168 4 : if( pItemSet )
169 : {
170 : // set only the attribute, for scanned scripts.
171 4 : if( !( SvtScriptType::LATIN & nScript ))
172 : {
173 0 : pItemSet->ClearItem( RES_CHRATR_FONT );
174 0 : pItemSet->ClearItem( RES_CHRATR_LANGUAGE );
175 : }
176 4 : if( !( SvtScriptType::ASIAN & nScript ))
177 : {
178 2 : pItemSet->ClearItem( RES_CHRATR_CJK_FONT );
179 2 : pItemSet->ClearItem( RES_CHRATR_CJK_LANGUAGE );
180 : }
181 4 : if( !( SvtScriptType::COMPLEX & nScript ))
182 : {
183 2 : pItemSet->ClearItem( RES_CHRATR_CTL_FONT );
184 2 : pItemSet->ClearItem( RES_CHRATR_CTL_LANGUAGE );
185 : }
186 4 : if( pItemSet->Count() )
187 : {
188 3 : if( bNewDoc )
189 : {
190 3 : if (pColl)
191 : {
192 : // Using the pool defaults for the font causes significant
193 : // trouble for the HTML filter, because it is not able
194 : // to export the pool defaults (or to be more precise:
195 : // the HTML filter is not able to detect whether a pool
196 : // default has changed or not. Even a comparison with the
197 : // HTMLi template does not work, because the defaults are
198 : // not copied when a new doc is created. The result of
199 : // comparing pool defaults therefore would be that the
200 : // defaults are exported always if the have changed for
201 : // text documents in general. That's not sensible, as well
202 : // as it is not sensible to export them always.
203 : sal_uInt16 aWhichIds[4] =
204 : {
205 : RES_CHRATR_FONT, RES_CHRATR_CJK_FONT,
206 : RES_CHRATR_CTL_FONT, 0
207 3 : };
208 3 : sal_uInt16 *pWhichIds = aWhichIds;
209 15 : while (*pWhichIds)
210 : {
211 : const SfxPoolItem *pItem;
212 9 : if (SfxItemState::SET == pItemSet->GetItemState(*pWhichIds,
213 9 : false, &pItem))
214 : {
215 5 : pColl->SetFormatAttr( *pItem );
216 5 : pItemSet->ClearItem( *pWhichIds );
217 : }
218 9 : ++pWhichIds;
219 : }
220 : }
221 3 : if (pItemSet->Count())
222 3 : pDoc->SetDefault(*pItemSet);
223 : }
224 0 : else if( pInsPam )
225 : {
226 : // then set over the insert range the defined attributes
227 0 : *pInsPam->GetMark() = *pPam->GetPoint();
228 0 : ++pInsPam->GetPoint()->nNode;
229 0 : pInsPam->GetPoint()->nContent.Assign(
230 0 : pInsPam->GetContentNode(), nSttContent );
231 :
232 : // !!!!!
233 : OSL_ENSURE( false, "Have to change - hard attr. to para. style" );
234 0 : pDoc->getIDocumentContentOperations().InsertItemSet( *pInsPam, *pItemSet );
235 : }
236 : }
237 4 : delete pItemSet, pItemSet = 0;
238 : }
239 :
240 4 : delete pInsPam;
241 :
242 4 : ::EndProgress( pDoc->GetDocShell() );
243 4 : return nError;
244 : }
245 :
246 4 : sal_uLong SwASCIIParser::ReadChars()
247 : {
248 4 : sal_Unicode *pStt = 0, *pEnd = 0, *pLastStt = 0;
249 4 : long nReadCnt = 0, nLineLen = 0;
250 4 : sal_Unicode cLastCR = 0;
251 4 : bool bSwapUnicode = false;
252 :
253 4 : const SwAsciiOptions *pUseMe=&rOpt;
254 4 : SwAsciiOptions aEmpty;
255 24 : if (nFileSize >= 2 &&
256 21 : aEmpty.GetFontName() == rOpt.GetFontName() &&
257 2 : aEmpty.GetCharSet() == rOpt.GetCharSet() &&
258 10 : aEmpty.GetLanguage() == rOpt.GetLanguage() &&
259 1 : aEmpty.GetParaFlags() == rOpt.GetParaFlags())
260 : {
261 : sal_uLong nLen, nOrig;
262 1 : nOrig = nLen = rInput.Read(pArr, ASC_BUFFLEN);
263 : rtl_TextEncoding eCharSet;
264 1 : bool bRet = SwIoSystem::IsDetectableText(pArr, nLen, &eCharSet, &bSwapUnicode);
265 : OSL_ENSURE(bRet, "Autodetect of text import without nag dialog must have failed");
266 1 : if (bRet && eCharSet != RTL_TEXTENCODING_DONTKNOW)
267 : {
268 0 : aEmpty.SetCharSet(eCharSet);
269 0 : rInput.SeekRel(-(long(nLen)));
270 : }
271 : else
272 1 : rInput.SeekRel(-(long(nOrig)));
273 1 : pUseMe=&aEmpty;
274 : }
275 :
276 4 : rtl_TextToUnicodeConverter hConverter=0;
277 4 : rtl_TextToUnicodeContext hContext=0;
278 4 : rtl_TextEncoding currentCharSet = pUseMe->GetCharSet();
279 4 : if (RTL_TEXTENCODING_UCS2 != currentCharSet)
280 : {
281 4 : if( currentCharSet == RTL_TEXTENCODING_DONTKNOW )
282 0 : currentCharSet = RTL_TEXTENCODING_ASCII_US;
283 4 : hConverter = rtl_createTextToUnicodeConverter( currentCharSet );
284 : OSL_ENSURE( hConverter, "no string convert available" );
285 4 : if (!hConverter)
286 0 : return ERROR_SW_READ_BASE;
287 4 : bSwapUnicode = false;
288 4 : hContext = rtl_createTextToUnicodeContext( hConverter );
289 : }
290 0 : else if (pUseMe != &aEmpty) //Already successfully figured out type
291 : {
292 0 : rInput.StartReadingUnicodeText( currentCharSet );
293 0 : bSwapUnicode = rInput.IsEndianSwap();
294 : }
295 :
296 8 : boost::scoped_array<sal_Unicode> aWork;
297 4 : sal_uLong nArrOffset = 0;
298 :
299 : do {
300 192268 : if( pStt >= pEnd )
301 : {
302 54 : if( pLastStt != pStt )
303 50 : InsertText( OUString( pLastStt ));
304 :
305 : // Read a new block
306 : sal_uLong lGCount;
307 108 : if( SVSTREAM_OK != rInput.GetError() || 0 == (lGCount =
308 54 : rInput.Read( pArr + nArrOffset,
309 108 : ASC_BUFFLEN - nArrOffset )))
310 4 : break; // break from the while loop
311 :
312 : /*
313 : If there was some unconverted bytes on the last cycle then they
314 : were put at the beginning of the array, so total bytes available
315 : to convert this cycle includes them. If we found 0 following bytes
316 : then we ignore the previous partial character.
317 : */
318 50 : lGCount+=nArrOffset;
319 :
320 50 : if( hConverter )
321 : {
322 : sal_uInt32 nInfo;
323 50 : sal_Size nNewLen = lGCount, nCntBytes;
324 50 : aWork.reset(new sal_Unicode[nNewLen + 1]); // add 1 for '\0'
325 50 : sal_Unicode* pBuf = aWork.get();
326 50 : pBuf[nNewLen] = 0; // ensure '\0'
327 :
328 : nNewLen = rtl_convertTextToUnicode( hConverter, hContext,
329 : pArr, lGCount, pBuf, nNewLen,
330 : (
331 : RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
332 : RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
333 : RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT |
334 : RTL_TEXTTOUNICODE_FLAGS_GLOBAL_SIGNATURE
335 : ),
336 : &nInfo,
337 50 : &nCntBytes );
338 50 : if( 0 != ( nArrOffset = lGCount - nCntBytes ) )
339 0 : memmove( pArr, pArr + nCntBytes, nArrOffset );
340 :
341 50 : pStt = pLastStt = aWork.get();
342 50 : pEnd = pStt + nNewLen;
343 : }
344 : else
345 : {
346 0 : pStt = pLastStt = reinterpret_cast<sal_Unicode*>(pArr);
347 0 : pEnd = reinterpret_cast<sal_Unicode*>(pArr + lGCount);
348 :
349 0 : if( bSwapUnicode )
350 : {
351 0 : sal_Char* pF = pArr, *pN = pArr + 1;
352 0 : for( sal_uLong n = 0; n < lGCount; n += 2, pF += 2, pN += 2 )
353 : {
354 0 : sal_Char c = *pF;
355 0 : *pF = *pN;
356 0 : *pN = c;
357 : }
358 : }
359 : }
360 :
361 50 : *pEnd = 0;
362 50 : nReadCnt += lGCount;
363 :
364 50 : ::SetProgressState( nReadCnt, pDoc->GetDocShell() );
365 :
366 50 : if( cLastCR )
367 : {
368 0 : if( 0x0a == *pStt && 0x0d == cLastCR )
369 0 : pLastStt = ++pStt;
370 0 : cLastCR = 0;
371 0 : nLineLen = 0;
372 : // We skip the last one at the end
373 0 : if( !rInput.IsEof() || !(pEnd == pStt ||
374 0 : ( !*pEnd && pEnd == pStt+1 ) ) )
375 0 : pDoc->getIDocumentContentOperations().SplitNode( *pPam->GetPoint(), false );
376 : }
377 : }
378 :
379 192264 : bool bIns = true, bSplitNode = false;
380 192264 : switch( *pStt )
381 : {
382 :
383 734 : case 0x0a: if( LINEEND_LF == pUseMe->GetParaFlags() )
384 : {
385 734 : bIns = false;
386 734 : *pStt = 0;
387 734 : ++pStt;
388 :
389 : // We skip the last one at the end
390 734 : if( !rInput.IsEof() || pEnd != pStt )
391 731 : bSplitNode = true;
392 : }
393 734 : break;
394 :
395 304 : case 0x0d: if( LINEEND_LF != pUseMe->GetParaFlags() )
396 : {
397 0 : bIns = false;
398 0 : *pStt = 0;
399 0 : ++pStt;
400 :
401 0 : bool bChkSplit = false;
402 0 : if( LINEEND_CRLF == pUseMe->GetParaFlags() )
403 : {
404 0 : if( pStt == pEnd )
405 0 : cLastCR = 0x0d;
406 0 : else if( 0x0a == *pStt )
407 : {
408 0 : ++pStt;
409 0 : bChkSplit = true;
410 : }
411 : }
412 : else
413 0 : bChkSplit = true;
414 :
415 : // We skip the last one at the end
416 0 : if( bChkSplit && ( !rInput.IsEof() || pEnd != pStt ))
417 0 : bSplitNode = true;
418 : }
419 304 : break;
420 :
421 : case 0x0c:
422 : {
423 : // Insert a hard page break
424 307 : *pStt++ = 0;
425 307 : if( nLineLen )
426 : {
427 306 : InsertText( OUString( pLastStt ));
428 : }
429 307 : pDoc->getIDocumentContentOperations().SplitNode( *pPam->GetPoint(), false );
430 307 : pDoc->getIDocumentContentOperations().InsertPoolItem(
431 307 : *pPam, SvxFormatBreakItem( SVX_BREAK_PAGE_BEFORE, RES_BREAK ) );
432 307 : pLastStt = pStt;
433 307 : nLineLen = 0;
434 307 : bIns = false;
435 : }
436 307 : break;
437 :
438 : case 0x1a:
439 79 : if( nReadCnt == nFileSize && pStt+1 == pEnd )
440 0 : *pStt = 0;
441 : else
442 79 : *pStt = '#'; // Replacement visualisation
443 79 : break;
444 :
445 136 : case '\t': break;
446 :
447 : default:
448 190704 : if( ' ' > *pStt )
449 : // Found control char, replace with '#'
450 70754 : *pStt = '#';
451 190704 : break;
452 : }
453 :
454 192264 : if( bIns )
455 : {
456 191623 : if( ( nLineLen >= MAX_ASCII_PARA - 100 ) &&
457 800 : ( ( *pStt == ' ' ) || ( nLineLen >= MAX_ASCII_PARA - 1 ) ) )
458 : {
459 4 : sal_Unicode c = *pStt;
460 4 : *pStt = 0;
461 4 : InsertText( OUString( pLastStt ));
462 4 : pDoc->getIDocumentContentOperations().SplitNode( *pPam->GetPoint(), false );
463 4 : pLastStt = pStt;
464 4 : nLineLen = 0;
465 4 : *pStt = c;
466 : }
467 191223 : ++pStt;
468 191223 : ++nLineLen;
469 : }
470 1041 : else if( bSplitNode )
471 : {
472 : // We found a CR/LF, thus save the text
473 731 : InsertText( OUString( pLastStt ));
474 731 : pDoc->getIDocumentContentOperations().SplitNode( *pPam->GetPoint(), false );
475 731 : pLastStt = pStt;
476 731 : nLineLen = 0;
477 : }
478 : } while(true);
479 :
480 4 : if( hConverter )
481 : {
482 4 : rtl_destroyTextToUnicodeContext( hConverter, hContext );
483 4 : rtl_destroyTextToUnicodeConverter( hConverter );
484 : }
485 8 : return 0;
486 : }
487 :
488 1091 : void SwASCIIParser::InsertText( const OUString& rStr )
489 : {
490 1091 : pDoc->getIDocumentContentOperations().InsertString( *pPam, rStr );
491 1091 : pDoc->UpdateRsid( *pPam, rStr.getLength() );
492 1091 : pDoc->UpdateParRsid( pPam->GetPoint()->nNode.GetNode().GetTextNode() );
493 :
494 4364 : if( pItemSet && g_pBreakIt && nScript != ( SvtScriptType::LATIN |
495 3273 : SvtScriptType::ASIAN |
496 2182 : SvtScriptType::COMPLEX ) )
497 67 : nScript |= g_pBreakIt->GetAllScriptsOfText( rStr );
498 1268 : }
499 :
500 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|