|
1 // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // ConArc plug-in converter. |
|
15 // Converts EPOC Rich Text files (i.e. externalised CRichText objects) to HTML files. |
|
16 // 1. Does not support object->object conversions |
|
17 // 2. Therefore does not support embedded objects |
|
18 // 3. No output encoding for UNICODE has beed decided upon |
|
19 // |
|
20 // |
|
21 |
|
22 #include <e32base.h> |
|
23 #include <e32def.h> |
|
24 #include <e32std.h> |
|
25 #include <s32file.h> |
|
26 #include <txtuids.h> |
|
27 #include <txtetext.h> |
|
28 #include <txtrich.h> |
|
29 #include <conarc.h> |
|
30 #include <bautils.h> |
|
31 |
|
32 #include "RT2HTMCV.H" |
|
33 #include "HTMLDEFS.H" |
|
34 #include <ecom/ecom.h> |
|
35 #include <ecom/implementationproxy.h> |
|
36 |
|
37 |
|
38 //------------------------------------------------------------------------------------------------- |
|
39 const TInt KMaxTagLength = 256; |
|
40 const TText KCharacterSpace = 0x20; |
|
41 //------------------------------------------------------------------------------------------------- |
|
42 #ifdef _UNICODE |
|
43 HBufC8* ConvertUnicodeToUTF8LC(const TDesC16& uniText); |
|
44 #endif // _UNICODE |
|
45 |
|
46 |
|
47 _LIT(KTempFileExt, ".dat"); |
|
48 |
|
49 typedef TPckgBuf<CRichText*> TRTPtrBuffer; |
|
50 |
|
51 /** |
|
52 ------------------------------------------------------------------------------------------------- |
|
53 The required ConArc classes: CCtrToHTMLConverter and CCrtToHTMLLibrary |
|
54 ------------------------------------------------------------------------------------------------- |
|
55 Class CCrtToHTMLConverter. |
|
56 The CConverterBase derived class which is responsable for performing the conversion operation. |
|
57 1. Supports File->File conversions and output to a RWriteStream |
|
58 2. For the stream interface a temporary file store is created for reading the rich text object. |
|
59 this is deleted once the conversion is complete |
|
60 3. The conversion is performed on a paragraph by paragraph basis |
|
61 4. iHTMLOut is used to output the resulting HTML. For File->File conversions a new file stream store |
|
62 is opened based on the user provided file name. For the streaming interface iHTMLOut is assigned |
|
63 to a RWriteStream& provided by the user |
|
64 */ |
|
65 CCrtToHTMLConverter::CCrtToHTMLConverter() : iHTMLOut(iFileStream) |
|
66 { |
|
67 } |
|
68 |
|
69 void CCrtToHTMLConverter::ConvertL(const TFileName& aSourceFile, const TFileName& aTargetFile, MConverterUiObserver* aObserver /*= NULL*/) |
|
70 // |
|
71 // Configures and performs a file -> file conversion synchronously |
|
72 // |
|
73 { |
|
74 iObserver = aObserver; |
|
75 PrepForConversionL(aSourceFile, aTargetFile); |
|
76 |
|
77 while(DoConvertL()) |
|
78 { |
|
79 } |
|
80 ASSERT(iParaRemain == 0); |
|
81 } |
|
82 |
|
83 void CCrtToHTMLConverter::ConvertAL(const TFileName& aSourceFile, const TFileName& aTargetFile, MConverterUiObserver* aObserver /*= NULL*/) |
|
84 // |
|
85 // Configures the object to perform a file -> file conversion asynchronously |
|
86 // |
|
87 { |
|
88 // Cast away the const-ness - for now... |
|
89 iObserver = aObserver; |
|
90 PrepForConversionL(aSourceFile, aTargetFile); |
|
91 |
|
92 if(iObserver) |
|
93 { |
|
94 iObserver->MaxSteps(iParaRemain, 0); |
|
95 } |
|
96 } |
|
97 |
|
98 /** |
|
99 To create the RReadStream you package a pointer to the CRichText object and create a RDesReadStream: |
|
100 TPckgBuf<CRichText*> buffer(text); //text is the CRichText* to be converted |
|
101 RDesReadStream readStream(buffer); //readStream can now be passed to ConvertObjectL/ConvertObjectAL |
|
102 */ |
|
103 void CCrtToHTMLConverter::ConvertObjectL(RReadStream& aReadStream, RWriteStream& aWriteStream, MConverterUiObserver* aObserver) |
|
104 { |
|
105 iObserver = aObserver; |
|
106 PrepForConversionL(aReadStream, aWriteStream); |
|
107 |
|
108 while(DoConvertL()); |
|
109 |
|
110 ASSERT(iParaRemain == 0); |
|
111 } |
|
112 |
|
113 /** |
|
114 To create the RReadStream you package a pointer to the CRichText object and create a RDesReadStream: |
|
115 TPckgBuf<CRichText*> buffer(text); //text is the CRichText* to be converted |
|
116 RDesReadStream readStream(buffer); //readStream can now be passed to ConvertObjectL/ConvertObjectAL |
|
117 */ |
|
118 void CCrtToHTMLConverter::ConvertObjectAL(RReadStream& aReadStream, RWriteStream& aWriteStream, MConverterUiObserver* aObserver) |
|
119 { |
|
120 iObserver = aObserver; |
|
121 PrepForConversionL(aReadStream, aWriteStream); |
|
122 |
|
123 if(iObserver) |
|
124 { |
|
125 iObserver->MaxSteps(iParaRemain, 0); |
|
126 } |
|
127 } |
|
128 |
|
129 |
|
130 TUid CCrtToHTMLConverter::Uid() |
|
131 // |
|
132 // Returns the UID associated with the conversion operation |
|
133 // |
|
134 { |
|
135 return KCrt2HTMLConverterUid; |
|
136 } |
|
137 |
|
138 TInt CCrtToHTMLConverter::Capabilities() |
|
139 // |
|
140 // Returns EConvertsFiles. Only File->File conversion is supported. |
|
141 // |
|
142 { |
|
143 return EConvertsFiles; |
|
144 } |
|
145 |
|
146 void CCrtToHTMLConverter::CancelConvert() |
|
147 // |
|
148 // Clean up. Doesn't remove half finished file. |
|
149 // |
|
150 { |
|
151 ResetMembers(); |
|
152 } |
|
153 |
|
154 CCrtToHTMLConverter::~CCrtToHTMLConverter() |
|
155 { |
|
156 ResetMembers(); |
|
157 iFs.Close(); |
|
158 } |
|
159 |
|
160 /** |
|
161 Performs a single step of the conversion. Converts a single paragraph from Rich Text -> HTML |
|
162 THe final aparagrahp of a RichText doc contains a single end of document marker. When we |
|
163 reach this we can delete the output sink object, which will flush and close the output file. |
|
164 */ |
|
165 TBool CCrtToHTMLConverter::DoConvertL() |
|
166 { |
|
167 TBool closePara = ETrue; |
|
168 |
|
169 if(iParaNum == -1) // We're at the start |
|
170 { |
|
171 StartDocumentL(); // Write the HTML header |
|
172 |
|
173 iParaNum = 0; |
|
174 iParaLen = 0; |
|
175 iParaPos = 0; |
|
176 closePara = EFalse; // Coz we don't want a </P> tag a the start... |
|
177 } |
|
178 |
|
179 // Do a single paragraph... |
|
180 if((iParaPos = iInputText->CharPosOfParagraph(iParaLen, iParaNum++)) >= 0) |
|
181 { |
|
182 // Construct a formatting object |
|
183 CParaFormat* format = CParaFormat::NewLC(); |
|
184 |
|
185 TParaFormatMask mask; |
|
186 |
|
187 // Cue, Mr Hack... |
|
188 // Knock 1 off the document length as a panic prevention measure, as the iParaLength of |
|
189 // the last paragraph is _beyond_ the end of the document. Doh! |
|
190 if(iParaNum == iParaCount) |
|
191 iParaLen -= 1; |
|
192 |
|
193 iInputText->GetParaFormatL(format, mask, iParaPos, iParaLen); |
|
194 |
|
195 ASSERT(mask.IsNull()); |
|
196 |
|
197 ProcessParagraphFormatL(format, closePara); // Put paragraph level formatting |
|
198 ProcessParagraphTextL(iParaPos, iParaLen); // Process the paragraph itself |
|
199 --iParaRemain; |
|
200 |
|
201 CleanupStack::PopAndDestroy(); // CParaFormat format |
|
202 if(iParaNum == iParaCount) // We're at the end of the document, so finish up |
|
203 { |
|
204 ASSERT(iParaPos + iParaLen == iInputText->DocumentLength()); |
|
205 EndDocumentL(); |
|
206 // Commit the file |
|
207 iHTMLOut.CommitL(); |
|
208 iHTMLOut.Close(); |
|
209 return EFalse; |
|
210 } |
|
211 } |
|
212 else |
|
213 { |
|
214 Panic(EHTMLBeyondLastPara); |
|
215 } |
|
216 return ETrue; |
|
217 } |
|
218 |
|
219 /** |
|
220 Both File->File and stream converstions use a file store to read the input CRichText object |
|
221 In File->File conversions the user creates the rich text file store prior to calling ConvertL()/ConvertAL() |
|
222 In stream conversions CCrtToHTMLConverter creates a temporary file store |
|
223 */ |
|
224 void CCrtToHTMLConverter::RestoreInputTextL(const TFileName& aSourceFile) |
|
225 { |
|
226 delete iInputText; |
|
227 iInputText = NULL; |
|
228 |
|
229 iParaLayer = CParaFormatLayer::NewL(); |
|
230 iCharLayer = CCharFormatLayer::NewL(); |
|
231 iInputText = CRichText::NewL(iParaLayer, iCharLayer, CEditableText::EFlatStorage); |
|
232 |
|
233 // Get the store and restore from it to the CRichText object |
|
234 iStore = CDirectFileStore::OpenL(iFs, aSourceFile, EFileRead | EFileShareReadersOnly); |
|
235 |
|
236 if(iStore->Type()[0] != KDirectFileStoreLayoutUid) |
|
237 User::Leave(KErrUnknown); |
|
238 |
|
239 iInputText->RestoreL(*iStore, iStore->Root()); |
|
240 |
|
241 iParaRemain = iInputText->ParagraphCount(); |
|
242 iParaCount = iParaRemain; |
|
243 } |
|
244 |
|
245 /** |
|
246 Configures the object prior to performing a conversion. |
|
247 1. Connects to the file server |
|
248 2. Restores the input CRichText object from the given file |
|
249 3. Prepares the output context |
|
250 */ |
|
251 void CCrtToHTMLConverter::PrepForConversionL(const TFileName& aSourceFile, const TFileName& aTargetFile) |
|
252 { |
|
253 ResetMembers(); |
|
254 // ResetMembers() will close the file server session to ensure it's flushed and any temporary files are deleted |
|
255 User::LeaveIfError(iFs.Connect()); |
|
256 |
|
257 iHTMLOut = iFileStream; |
|
258 RestoreInputTextL(aSourceFile); |
|
259 |
|
260 // Set up the output context |
|
261 User::LeaveIfError(((RFileWriteStream&)iHTMLOut).Replace(iFs, aTargetFile, EFileStream|EFileWrite|EFileShareExclusive)); |
|
262 } |
|
263 |
|
264 /** |
|
265 Configures the object prior to performing a conversion. |
|
266 1. Connects to the file server |
|
267 2. Assign the output stream |
|
268 3. Copys the read CRichText object to a file store |
|
269 4. Restores our copy of the input CRichText |
|
270 |
|
271 There's no way to copy a CRichText object without first externalizing it in some way. The normal mechanism for |
|
272 saving and loading CRichText objects is to use Store and Restore. Using Externalize and Internalize is non-trivial |
|
273 with CRichText. The stream interface only gives us a RReadStream& making Restore very difficult. The way around |
|
274 this is for the user to just stream a pointer to the actual CRichText object. This way the caller dosn't need to |
|
275 worry about setting up any stores. CCrtToHTMLConverter does the copy. |
|
276 To create the RReadStream you package a pointer to the CRichText object and create a RDesReadStream: |
|
277 TPckgBuf<CRichText*> buffer(text); //text is the CRichText* to be converted |
|
278 RDesReadStream readStream(buffer); //readStream can now be passed to ConvertObjectL/ConvertObjectAL |
|
279 */ |
|
280 void CCrtToHTMLConverter::PrepForConversionL(RReadStream& aReadStream, RWriteStream& aWriteStream) |
|
281 { |
|
282 ResetMembers(); |
|
283 // ResetMembers() will close the file server session to ensure it's flushed and any temporary files are deleted |
|
284 User::LeaveIfError(iFs.Connect()); |
|
285 |
|
286 iHTMLOut = aWriteStream; |
|
287 |
|
288 // Unpackage the source rich text pointer |
|
289 TRTPtrBuffer readBuffer; |
|
290 aReadStream.ReadL(readBuffer); |
|
291 CRichText* sourceText = readBuffer(); |
|
292 |
|
293 // The temporary file needs to be unique to allow for concurrent conversions |
|
294 // This file is deleted on a call to ResetMembers() |
|
295 TFileName newTempFile; |
|
296 User::LeaveIfError(iFs.PrivatePath(newTempFile)); |
|
297 |
|
298 newTempFile.AppendNum((TInt)sourceText); |
|
299 newTempFile.Append(KTempFileExt); |
|
300 BaflUtils::EnsurePathExistsL(iFs, newTempFile); |
|
301 iInternalFile = newTempFile.AllocL(); |
|
302 |
|
303 CDirectFileStore* fileStore = CDirectFileStore::ReplaceL(iFs, *iInternalFile, EFileWrite|EFileShareAny); |
|
304 CleanupStack::PushL( fileStore ); |
|
305 fileStore->SetTypeL( KDirectFileStoreLayoutUid ); |
|
306 TStreamId streamID = sourceText->StoreL(*fileStore); |
|
307 fileStore->SetRootL(streamID); |
|
308 fileStore->CommitL(); |
|
309 CleanupStack::PopAndDestroy( fileStore ); |
|
310 |
|
311 // Restore our copy of the input rich text object from the newly created file store |
|
312 RestoreInputTextL(*iInternalFile); |
|
313 } |
|
314 |
|
315 /** |
|
316 Ensures that any contained objects are desstroyed and sets all member variable to zero(NULL) |
|
317 */ |
|
318 void CCrtToHTMLConverter::ResetMembers() |
|
319 { |
|
320 iStyleIndex = -1; |
|
321 iParaNum = -1; |
|
322 iParaLen = 0; |
|
323 iParaPos = 0; |
|
324 iParaCount = 0; |
|
325 iParaRemain = 0; |
|
326 iIndent = 0; |
|
327 iOldFmtCount = 0; |
|
328 |
|
329 iHTMLOut.Close(); |
|
330 delete iInputText; |
|
331 iInputText = NULL; |
|
332 delete iParaLayer; |
|
333 iParaLayer = NULL; |
|
334 delete iCharLayer; |
|
335 iCharLayer = NULL; |
|
336 delete iBullet; |
|
337 iBullet = NULL; |
|
338 delete iStore; |
|
339 iStore = NULL; |
|
340 if (iFs.Handle()) |
|
341 { |
|
342 if (iInternalFile && BaflUtils::FileExists(iFs,*iInternalFile)) |
|
343 { |
|
344 iFs.Delete(*iInternalFile); |
|
345 delete iInternalFile; |
|
346 iInternalFile = NULL; |
|
347 } |
|
348 // I'm closing the file server to ensure it flushes. |
|
349 // Otherwise internal files are not always deleted |
|
350 iFs.Close(); |
|
351 } |
|
352 } |
|
353 |
|
354 /** |
|
355 Scans the passed buffer, replacing special characters in the source with relevant HTML tags |
|
356 before sending them to be output |
|
357 */ |
|
358 void CCrtToHTMLConverter::TranslateL(const TDesC& aBuf) |
|
359 { |
|
360 int i = 0; |
|
361 while(i < aBuf.Length()) |
|
362 { |
|
363 TText ch = aBuf[i++]; |
|
364 switch(ch) |
|
365 { |
|
366 // !! am I picking up all possibles here? |
|
367 case CEditableText::EPageBreak: |
|
368 case CEditableText::EPotentialHyphen: |
|
369 case CEditableText::ENonBreakingHyphen: |
|
370 break; // These characters are not emitted. |
|
371 case CEditableText::EPictureCharacter: |
|
372 break; |
|
373 case CEditableText::ELineBreak: |
|
374 WriteTagL(KHtmlLineBreak); |
|
375 break; |
|
376 case CEditableText::ENonBreakingSpace: |
|
377 case CEditableText::ETabCharacter: |
|
378 WriteContentL(TPtrC(&KCharacterSpace, 1)); |
|
379 break; |
|
380 case CEditableText::EParagraphDelimiter: |
|
381 break; |
|
382 case KLessThan: |
|
383 WriteTagL(KHtmlLessThan); |
|
384 break; |
|
385 case KGreaterThan: |
|
386 WriteTagL(KHtmlGreaterThan); |
|
387 break; |
|
388 case KAmpersand: |
|
389 WriteTagL(KHtmlAmpersand); |
|
390 break; |
|
391 default: |
|
392 WriteContentL(TPtrC(&ch, 1)); |
|
393 break; |
|
394 } |
|
395 } |
|
396 } |
|
397 |
|
398 void CCrtToHTMLConverter::WriteTagL(const TDesC8& aTagText) |
|
399 { |
|
400 iHTMLOut.WriteL(aTagText); |
|
401 } |
|
402 |
|
403 void CCrtToHTMLConverter::WriteContentL(const TDesC& aText) |
|
404 { |
|
405 #ifdef _UNICODE |
|
406 HBufC8* pBuf = ConvertUnicodeToUTF8LC(aText); |
|
407 User::LeaveIfNull(pBuf); |
|
408 iHTMLOut.WriteL(*pBuf); |
|
409 CleanupStack::PopAndDestroy(); |
|
410 #else |
|
411 iHTMLOut.WriteL(aText); |
|
412 #endif |
|
413 } |
|
414 |
|
415 void CCrtToHTMLConverter::ProcessParagraphFormatL(const CParaFormat* aFormat, TBool aClosePara) |
|
416 // |
|
417 // Processes any paragraph level formatting (paragraph alignment, list bullets, indentation) |
|
418 // |
|
419 { |
|
420 // Close indents |
|
421 for( ; iIndent ; iIndent--) |
|
422 WriteTagL(KHtmlBlockquoteEnd); |
|
423 |
|
424 if(!iBullet && aClosePara) |
|
425 { |
|
426 if ( iInsertBlankDivClose ) |
|
427 { |
|
428 WriteTagL(KHtmlDivBlankEnd); |
|
429 } |
|
430 else |
|
431 { |
|
432 WriteTagL(KHtmlDivEnd); |
|
433 } |
|
434 } |
|
435 |
|
436 // Process unordered (bulleted) lists |
|
437 if(iBullet) |
|
438 { |
|
439 // Previous paragraph was bulleted |
|
440 if(!aFormat->iBullet) |
|
441 { |
|
442 // End of list |
|
443 delete iBullet; |
|
444 iBullet = NULL; |
|
445 WriteTagL(KHtmlBulletListPointEnd); |
|
446 WriteTagL(KHtmlBulletListEnd); |
|
447 } |
|
448 else |
|
449 { |
|
450 if(*iBullet == *(aFormat->iBullet)) |
|
451 { |
|
452 WriteTagL(KHtmlBulletListPointEnd); |
|
453 WriteTagL(KHtmlBulletListPointStart); |
|
454 } |
|
455 else |
|
456 { |
|
457 // A _new_ list |
|
458 WriteTagL(KHtmlBulletListPointEnd); |
|
459 WriteTagL(KHtmlBulletListEnd); |
|
460 WriteTagL(KHtmlBulletListStart); |
|
461 WriteTagL(KHtmlBulletListPointStart); |
|
462 *iBullet = *(aFormat->iBullet); |
|
463 } |
|
464 } |
|
465 } |
|
466 else |
|
467 { |
|
468 // Previous paragraph was _not_ bulleted |
|
469 if(aFormat->iBullet) |
|
470 { |
|
471 // But this one is: start a new list |
|
472 iBullet = new (ELeave) TBullet(); |
|
473 *iBullet = *(aFormat->iBullet); |
|
474 WriteTagL(KHtmlBulletListStart); |
|
475 WriteTagL(KHtmlBulletListPointStart); |
|
476 } |
|
477 } |
|
478 |
|
479 // Process paragraph alignment |
|
480 switch(aFormat->iHorizontalAlignment) |
|
481 { |
|
482 case CParaFormat::ELeftAlign: // Paragraph aligned flush with left margin |
|
483 WriteTagL(KHtmlDivAlignLeftStart); |
|
484 break; |
|
485 case CParaFormat::ECenterAlign: // Paragraph center aligned |
|
486 WriteTagL(KHtmlDivAlignCentreStart); |
|
487 break; |
|
488 case CParaFormat::ERightAlign: // Paragraph aligned flush with right margin |
|
489 WriteTagL(KHtmlDivAlignRightStart); |
|
490 break; |
|
491 case CParaFormat::EJustifiedAlign: // Justified text |
|
492 WriteTagL(KHtmlDivAlignJustifyStart); |
|
493 break; |
|
494 default: |
|
495 WriteTagL(KHtmlDivAlignNoneStart); |
|
496 break; |
|
497 } |
|
498 |
|
499 // Open indents |
|
500 iIndent = (aFormat->iLeftMarginInTwips) / KTwipsToBlockQuote; |
|
501 for(TInt i = 0; i < iIndent; i++) |
|
502 WriteTagL(KHtmlBlockquoteStart); |
|
503 } |
|
504 |
|
505 void CCrtToHTMLConverter::ProcessParagraphTextL(TInt aPos, TInt aLength) |
|
506 // |
|
507 // Processes a paragraph of text |
|
508 // |
|
509 { |
|
510 TInt pos = aPos; |
|
511 TCharFormat oldFormat, |
|
512 newFormat; |
|
513 |
|
514 TCharFormatMask maskChar; |
|
515 // Set up initial character formatting |
|
516 iInputText->GetSpecificCharFormat(newFormat, maskChar, pos); |
|
517 DiffCharFormats(newFormat, oldFormat, maskChar); |
|
518 OpenCharFormatL (maskChar, newFormat); |
|
519 oldFormat = newFormat; |
|
520 |
|
521 // reset blank paragraph flag |
|
522 iInsertBlankDivClose = EFalse; |
|
523 // Scan the paragraph 1 char at a time... |
|
524 while((pos - aPos) < aLength) |
|
525 { |
|
526 ASSERT(pos < iInputText->DocumentLength()); |
|
527 TPtrC str = iInputText->Read(pos++, 1); |
|
528 |
|
529 if(str[0] == CEditableText::EParagraphDelimiter) |
|
530 { |
|
531 // only insert a blank div if we have a blank paragraph |
|
532 // ie aLength == 1 && it's only contents are CEditableText::EParagraphDelimiter |
|
533 iInsertBlankDivClose = ( aLength == 1 ); |
|
534 continue; |
|
535 } |
|
536 |
|
537 TCharFormatMask testMask; |
|
538 TCharFormat tstFormat; |
|
539 |
|
540 iInputText->GetCharFormat(tstFormat, testMask, pos-1, 2); |
|
541 |
|
542 if(!tstFormat.IsEqual(oldFormat)) |
|
543 { |
|
544 // Something has changed... |
|
545 DiffCharFormats(tstFormat, oldFormat, maskChar); |
|
546 CloseCharFormatL(maskChar, oldFormat); |
|
547 OpenCharFormatL (maskChar, tstFormat); |
|
548 } |
|
549 oldFormat = tstFormat; |
|
550 TranslateL(str); |
|
551 } |
|
552 // End of paragraph, reset formatting to base... |
|
553 TCharFormat closeFormat; |
|
554 DiffCharFormats(oldFormat, closeFormat, maskChar); |
|
555 CloseCharFormatL(maskChar, oldFormat); |
|
556 } |
|
557 |
|
558 void CCrtToHTMLConverter::DiffCharFormats(const TCharFormat& aFormatA, const TCharFormat& aFormatB, TCharFormatMask& aMask) |
|
559 // |
|
560 // Compare two TCharFormat and set flags in the mask which descrbe the differences |
|
561 // (Would be quite nice if TCharFormat knew how to do this itself...) |
|
562 // |
|
563 { |
|
564 aMask.ClearAll(); |
|
565 |
|
566 if(aFormatA.iLanguage!=aFormatB.iLanguage) |
|
567 aMask.SetAttrib(EAttCharLanguage); |
|
568 |
|
569 if(aFormatA.iFontSpec.iHeight != aFormatB.iFontSpec.iHeight) |
|
570 aMask.SetAttrib(EAttFontHeight); |
|
571 |
|
572 if(!(aFormatA.iFontSpec.iTypeface == aFormatB.iFontSpec.iTypeface)) |
|
573 aMask.SetAttrib(EAttFontTypeface); |
|
574 |
|
575 if(aFormatA.iFontSpec.iFontStyle.Posture() != aFormatB.iFontSpec.iFontStyle.Posture()) |
|
576 aMask.SetAttrib(EAttFontPosture); |
|
577 |
|
578 if(aFormatA.iFontSpec.iFontStyle.StrokeWeight() != aFormatB.iFontSpec.iFontStyle.StrokeWeight()) |
|
579 aMask.SetAttrib(EAttFontStrokeWeight); |
|
580 |
|
581 if(aFormatA.iFontSpec.iFontStyle.PrintPosition() != aFormatB.iFontSpec.iFontStyle.PrintPosition()) |
|
582 aMask.SetAttrib(EAttFontPrintPos); |
|
583 |
|
584 if(aFormatA.iFontPresentation.iUnderline != aFormatB.iFontPresentation.iUnderline) |
|
585 aMask.SetAttrib(EAttFontUnderline); |
|
586 |
|
587 if (aFormatA.iFontPresentation.iStrikethrough != aFormatB.iFontPresentation.iStrikethrough) |
|
588 aMask.SetAttrib(EAttFontStrikethrough); |
|
589 |
|
590 if(aFormatA.iFontPresentation.iTextColor != aFormatB.iFontPresentation.iTextColor) |
|
591 aMask.SetAttrib(EAttColor); |
|
592 |
|
593 if(!(aFormatA.iFontSpec.iTypeface == aFormatB.iFontSpec.iTypeface)) |
|
594 aMask.SetAttrib(EAttFontTypeface); |
|
595 } |
|
596 |
|
597 void CCrtToHTMLConverter::OpenCharFormatL(const TCharFormatMask& aMask, const TCharFormat& aFormat) |
|
598 // |
|
599 // Open the formating tags as set in aMask using parameters gleened from aFormat |
|
600 // |
|
601 { |
|
602 if(aMask.IsNull()) |
|
603 return; |
|
604 // Bold text ? |
|
605 if(aMask.AttribIsSet(EAttFontStrokeWeight) && (aFormat.iFontSpec.iFontStyle.StrokeWeight() == EStrokeWeightBold)) |
|
606 WriteTagL(KHtmlBoldStart); |
|
607 // Underlined ? |
|
608 if(aMask.AttribIsSet(EAttFontUnderline) && (aFormat.iFontPresentation.iUnderline == EUnderlineOn)) |
|
609 WriteTagL(KHtmlUnderlineStart); |
|
610 // Italic ? |
|
611 if(aMask.AttribIsSet(EAttFontPosture) && (aFormat.iFontSpec.iFontStyle.Posture() == EPostureItalic)) |
|
612 WriteTagL(KHtmlItalicStart); |
|
613 // Strike through ? |
|
614 if(aMask.AttribIsSet(EAttFontStrikethrough) && aFormat.iFontPresentation.iStrikethrough) |
|
615 WriteTagL(KHtmlStrikeoutStart); |
|
616 // Sub/Super-script ? |
|
617 if(aMask.AttribIsSet(EAttFontPrintPos)) |
|
618 { |
|
619 switch(aFormat.iFontSpec.iFontStyle.PrintPosition()) |
|
620 { |
|
621 case EPrintPosSuperscript: WriteTagL(KHtmlSuperscriptStart); |
|
622 break; |
|
623 case EPrintPosSubscript : WriteTagL(KHtmlSubscriptStart); |
|
624 break; |
|
625 case EPrintPosNormal : break; |
|
626 } |
|
627 } |
|
628 // Font typeface |
|
629 if(aMask.AttribIsSet(EAttFontTypeface) && !aFormat.iFontSpec.iTypeface.IsProportional()) |
|
630 WriteTagL(KHtmlTeletypeStart); |
|
631 |
|
632 |
|
633 // if both font height and colour are set. |
|
634 if(aMask.AttribIsSet(EAttFontHeight) && aMask.AttribIsSet(EAttColor)) |
|
635 { |
|
636 TInt htmlHeight = ((aFormat.iFontSpec.iHeight - KHtmlTwipsToHeightBaseAdjust) / KHtmlTwipsToHeight) + 1; |
|
637 TBuf8<KMaxTagLength > tag; |
|
638 if(htmlHeight > KHtmlMaxFontSize) |
|
639 htmlHeight = KHtmlMaxFontSize; |
|
640 tag.Format(KHtmlFontStartClrNSize, htmlHeight, aFormat.iFontPresentation.iTextColor.Red(), |
|
641 aFormat.iFontPresentation.iTextColor.Green(), |
|
642 aFormat.iFontPresentation.iTextColor.Blue()); |
|
643 WriteTagL(tag); |
|
644 //if(!iOldFmtCount) |
|
645 iOldFmtCount = 1; |
|
646 } |
|
647 else |
|
648 { |
|
649 // Font height |
|
650 if(aMask.AttribIsSet(EAttFontHeight)) |
|
651 { |
|
652 TInt htmlHeight = ((aFormat.iFontSpec.iHeight - KHtmlTwipsToHeightBaseAdjust) / KHtmlTwipsToHeight) + 1; |
|
653 TBuf8<KMaxTagLength > tag; |
|
654 if(htmlHeight > KHtmlMaxFontSize) |
|
655 htmlHeight = KHtmlMaxFontSize; |
|
656 tag.Format(KHtmlFontSizeStart, htmlHeight); |
|
657 WriteTagL(tag); |
|
658 //if(!iOldFmtCount) |
|
659 iOldFmtCount = 1; |
|
660 |
|
661 } |
|
662 // Font colour |
|
663 if(aMask.AttribIsSet(EAttColor)) |
|
664 { |
|
665 TBuf8<KMaxTagLength > tag; |
|
666 tag.Format(KHtmlFontColourStart, aFormat.iFontPresentation.iTextColor.Red(), |
|
667 aFormat.iFontPresentation.iTextColor.Green(), |
|
668 aFormat.iFontPresentation.iTextColor.Blue()); |
|
669 WriteTagL(tag); |
|
670 //if(!iOldFmtCount) |
|
671 iOldFmtCount = 1; |
|
672 } |
|
673 } |
|
674 |
|
675 |
|
676 |
|
677 } |
|
678 |
|
679 void CCrtToHTMLConverter::CloseCharFormatL(const TCharFormatMask& aMask, const TCharFormat& aFormat) |
|
680 // |
|
681 // Open the formating tags as set in aMask using parameters gleened from aFormat |
|
682 // These tags should be checked in the exact _reverse_ order as when they were opened |
|
683 // to ensure that they nest properly. |
|
684 // |
|
685 { |
|
686 if(aMask.IsNull()) |
|
687 return; |
|
688 |
|
689 //Font color or height |
|
690 if(iOldFmtCount && (aMask.AttribIsSet(EAttColor) || aMask.AttribIsSet(EAttFontHeight))) |
|
691 { |
|
692 WriteTagL(KHtmlFontEnd); |
|
693 iOldFmtCount = 0; |
|
694 } |
|
695 |
|
696 // Font typeface |
|
697 if(aMask.AttribIsSet(EAttFontTypeface) && !aFormat.iFontSpec.iTypeface.IsProportional()) |
|
698 WriteTagL(KHtmlTeletypeEnd); |
|
699 // Sub/Super-script ? |
|
700 if(aMask.AttribIsSet(EAttFontPrintPos)) |
|
701 { |
|
702 switch(aFormat.iFontSpec.iFontStyle.PrintPosition()) |
|
703 { |
|
704 case EPrintPosSuperscript: WriteTagL(KHtmlSuperscriptEnd); |
|
705 break; |
|
706 case EPrintPosSubscript : WriteTagL(KHtmlSubscriptEnd); |
|
707 break; |
|
708 case EPrintPosNormal : break; |
|
709 } |
|
710 } |
|
711 // Strike through ? |
|
712 if(aMask.AttribIsSet(EAttFontStrikethrough) && aFormat.iFontPresentation.iStrikethrough) |
|
713 WriteTagL(KHtmlStrikeoutEnd); |
|
714 // Italic ? |
|
715 if(aMask.AttribIsSet(EAttFontPosture) && (aFormat.iFontSpec.iFontStyle.Posture() == EPostureItalic)) |
|
716 WriteTagL(KHtmlItalicEnd); |
|
717 // Underlined ? |
|
718 if(aMask.AttribIsSet(EAttFontUnderline) && (aFormat.iFontPresentation.iUnderline == EUnderlineOn)) |
|
719 WriteTagL(KHtmlUnderlineEnd); |
|
720 // Bold text ? |
|
721 if(aMask.AttribIsSet(EAttFontStrokeWeight) && (aFormat.iFontSpec.iFontStyle.StrokeWeight() == EStrokeWeightBold)) |
|
722 WriteTagL(KHtmlBoldEnd); |
|
723 } |
|
724 |
|
725 //------------------------------------------------------------------------------------------------- |
|
726 // Output functions |
|
727 // |
|
728 void CCrtToHTMLConverter::StartDocumentL() |
|
729 // |
|
730 // Put the <HTML><HEAD>...</HEAD> tags and open the >BODY> tag. |
|
731 // |
|
732 { |
|
733 // Do some validity checks ? // TTD: Change this to 4.0 xxxx - or skip it! |
|
734 WriteTagL(KHTMLDocType32); // <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\"> |
|
735 WriteTagL(KHtmlStartTag); // <HTML> |
|
736 WriteTagL(KHtmlHeadStartTag); // <HEAD> |
|
737 |
|
738 WriteTagL(KHTMLHeadTitleStartTag); // <TITLE> |
|
739 TranslateL(_L("Converted from Rich Text")); // ... |
|
740 WriteTagL(KHTMLHeadTitleEndTag); // </TITLE> |
|
741 #ifdef _UNICODE |
|
742 WriteTagL(KHTMLMetaCharSetUTF8); // <META HTTP-EQUIV ="Content-Type" CONTENT = "text/html; charset=UTF-8"> |
|
743 #endif //_UNICODE |
|
744 WriteTagL(KHTMLHeadMetaGenTag); // <META NAME = "generator" CONTENT = "rt2html converter"> |
|
745 WriteTagL(KHtmlHeadEndTag); // </HEAD> |
|
746 |
|
747 // Get default background colour... |
|
748 CParaFormat* formatPara = CParaFormat::NewLC(); |
|
749 iInputText->GlobalParaFormatLayer()->SenseEffectiveL(formatPara); |
|
750 TRgb backColour = formatPara->iFillColor; |
|
751 CleanupStack::PopAndDestroy(); // CParaFormat formatPara |
|
752 |
|
753 // Get default foreground colour... |
|
754 TCharFormat formatChar; |
|
755 iInputText->GlobalCharFormatLayer()->SenseEffective(formatChar); |
|
756 TRgb foreColour = formatChar.iFontPresentation.iTextColor; |
|
757 |
|
758 TBuf8<KMaxTagLength > tag; |
|
759 tag.Format(KHtmlBodyStartTag, backColour.Red(), |
|
760 backColour.Green(), |
|
761 backColour.Blue(), |
|
762 foreColour.Red(), |
|
763 foreColour.Green(), |
|
764 foreColour.Blue()); |
|
765 |
|
766 WriteTagL(tag); |
|
767 } |
|
768 |
|
769 |
|
770 void CCrtToHTMLConverter::EndDocumentL() |
|
771 // |
|
772 // Close the document (write the </BODY> and </HTML> tags |
|
773 // |
|
774 { |
|
775 if(iOldFmtCount) |
|
776 { |
|
777 WriteTagL(KHtmlFontEnd); |
|
778 } |
|
779 |
|
780 if(iBullet) |
|
781 { |
|
782 // End of list |
|
783 delete iBullet; |
|
784 iBullet = NULL; |
|
785 WriteTagL(KHtmlBulletListPointEnd); |
|
786 WriteTagL(KHtmlBulletListEnd); |
|
787 } |
|
788 else if (iInsertBlankDivClose) |
|
789 { |
|
790 WriteTagL(KHtmlDivBlankEnd);// </DIV> |
|
791 } |
|
792 else |
|
793 { |
|
794 WriteTagL(KHtmlDivEnd);//</DIV> |
|
795 } |
|
796 |
|
797 WriteTagL(KHtmlBodyEndTag); // </BODY> |
|
798 WriteTagL(KHtmlEndTag); // </HTML> |
|
799 } |
|
800 |
|
801 CConverterBase2* CCrtToHTMLConverter::NewL() |
|
802 { |
|
803 CConverterBase2* crtToHtmlConverter=new (ELeave) CCrtToHTMLConverter(); |
|
804 return crtToHtmlConverter; |
|
805 } |
|
806 |
|
807 const TImplementationProxy ImplementationTable[] = |
|
808 { |
|
809 IMPLEMENTATION_PROXY_ENTRY(0x1000071c,CCrtToHTMLConverter::NewL) |
|
810 }; |
|
811 |
|
812 EXPORT_C const TImplementationProxy* ImplementationGroupProxy(TInt& aTableCount) |
|
813 { |
|
814 aTableCount = sizeof(ImplementationTable) / sizeof(TImplementationProxy); |
|
815 return ImplementationTable; |
|
816 } |
|
817 //------------------------------------------------------------------------------------------------- |
|
818 // Conversion helper |
|
819 //------------------------------------------------------------------------------------------------- |
|
820 #ifdef _UNICODE |
|
821 #include <utf.h> |
|
822 // Stole most of this from Phil :-) |
|
823 HBufC8* ConvertUnicodeToUTF8LC(const TDesC16& uniText) |
|
824 { |
|
825 // Final UTF8 destination buffer. |
|
826 TInt len = uniText.Length() * sizeof(TText); |
|
827 HBufC8* utfText = HBufC8::NewL(len); // Probably be enough... |
|
828 CleanupStack::PushL(utfText); |
|
829 |
|
830 // Keep going until there are no unconverted characters left. |
|
831 FOREVER |
|
832 { |
|
833 TPtr8 destination = utfText->Des(); |
|
834 destination.FillZ(); |
|
835 TInt charsLeft = CnvUtfConverter::ConvertFromUnicodeToUtf8(destination, uniText); |
|
836 |
|
837 if(charsLeft < 0) |
|
838 User::Leave(KErrCorrupt); // Conversion error due to input stream. |
|
839 else if(0==charsLeft) |
|
840 { |
|
841 return utfText; |
|
842 } |
|
843 else |
|
844 { |
|
845 // There are characters left to convert due to running out of destination buffer space. |
|
846 len += charsLeft * sizeof(TText); |
|
847 utfText = utfText->ReAlloc(len); |
|
848 } |
|
849 } |
|
850 // return NULL; PFD - removed unreachable code as no warnings are acceptable. |
|
851 } |
|
852 #endif // _UNICODE |
|
853 |
|
854 //------------------------------------------------------------------------------------------------- |
|
855 // Globals |
|
856 //------------------------------------------------------------------------------------------------- |
|
857 |
|
858 GLDEF_C void Panic(TTextToHTMLPanic aPanic) |
|
859 // Panic the process with HTML converter as the category. |
|
860 // |
|
861 { |
|
862 User::Panic(_L("CRT2HTML"),aPanic); |
|
863 } |
|
864 |
|
865 |