1 /************************************************************************/
2 /* */
3 /* Save a BufferDocument into an HTML file. */
4 /* Depending on the parameters, this is either an HTML file with */
5 /* a directory for the images, or a MHTML (rfc2112,rfc2557) aggregate. */
6 /* RFC 2557 was never validated. */
7 /* */
8 /************************************************************************/
9
10 # include "docHtmlConfig.h"
11
12 # include <string.h>
13 # include <stdio.h>
14 # include <ctype.h>
15
16 # include <utilWindowsLanguageCode.h>
17 # include <sioGeneral.h>
18
19 # include <docBuf.h>
20 # include <psShading.h>
21 # include "docHtmlWriteImpl.h"
22 # include <docHyperlinkField.h>
23 # include <docBookmarkField.h>
24 # include <docTreeNode.h>
25 # include <docNotes.h>
26 # include <docDocumentNote.h>
27
28 # include <appDebugon.h>
29
docInitHtmlWritingContext(HtmlWritingContext * hwc)30 void docInitHtmlWritingContext( HtmlWritingContext * hwc )
31 {
32 xmlInitXmlWriter( &(hwc->hwcXmlWriter) );
33
34 hwc->hwcLayoutContext= (const LayoutContext *)0;
35 docLayoutInitBlockFrame( &(hwc->hwcBlockFrame) );
36 docInitParagraphFrame( &(hwc->hwcParagraphFrame) );
37
38 hwc->hwcOpenImageStream= (HtmlOpenImageStream)0;
39 hwc->hwcGetImageSrc= (HtmlGetImageSrc)0;
40 hwc->hwcGetCssName= (HtmlGetCssName)0;
41 hwc->hwcPrivate= (void *)0;
42 hwc->hwcDocument= (BufferDocument *)0;
43
44 utilInitIndexMapping( &(hwc->hwcDeferredNotes) );
45
46 hwc->hwcInlineCss= 0;
47 hwc->hwcInlineNotes= 0;
48
49 hwc->hwcInHyperlink= 0;
50 hwc->hwcInBookmark= 0;
51 hwc->hwcInPageref= 0;
52 hwc->hwcBytesInLink= 0;
53
54 docPlainTextAttribute( &(hwc->hwcDefaultAttribute), hwc->hwcDocument );
55 hwc->hwcDefaultAttribute.taFontSizeHalfPoints= 24;
56 hwc->hwcDefaultAttribute.taFontNumber= -1;
57
58 docInitParagraphProperties( &(hwc->hwcParagraphProperties) );
59
60 hwc->hwcImageCount= 0;
61 hwc->hwcNoteRefCount= 0;
62 hwc->hwcNoteDefCount= 0;
63
64 hwc->hwcTableNesting= 0;
65
66 hwc->hwcCurrentAttributeNumber= -1;
67
68 hwc->hwcSupportsBullets= 0;
69 hwc->hwcEmitBackground= 0;
70 hwc->hwcEmitBackground= 0;
71 return;
72 }
73
docCleanHtmlWritingContext(HtmlWritingContext * hwc)74 void docCleanHtmlWritingContext( HtmlWritingContext * hwc )
75 {
76 utilCleanIndexMapping( &(hwc->hwcDeferredNotes) );
77
78 docCleanParagraphProperties( &(hwc->hwcParagraphProperties) );
79
80 docLayoutCleanBlockFrame( &(hwc->hwcBlockFrame) );
81 /*docCleanParagraphFrame( &(hwc->hwcParagraphFrame) );*/
82
83 return;
84 }
85
86 /************************************************************************/
87 /* */
88 /* Save a tag with an argument. */
89 /* */
90 /************************************************************************/
91
docHtmlPutString(const char * s,HtmlWritingContext * hwc)92 void docHtmlPutString( const char * s,
93 HtmlWritingContext * hwc )
94 { xmlPutString( s, &(hwc->hwcXmlWriter) ); }
95
docHtmlNewLine(HtmlWritingContext * hwc)96 void docHtmlNewLine( HtmlWritingContext * hwc )
97 {
98 xmlNewLine( &(hwc->hwcXmlWriter) );
99 }
100
docHtmlWriteStringAttribute(HtmlWritingContext * hwc,const char * name,const char * value)101 void docHtmlWriteStringAttribute( HtmlWritingContext * hwc,
102 const char * name,
103 const char * value )
104 {
105 xmlWriteStringAttribute( &(hwc->hwcXmlWriter), name, value );
106 }
107
docHtmlWriteIntAttribute(HtmlWritingContext * hwc,const char * name,int value)108 void docHtmlWriteIntAttribute( HtmlWritingContext * hwc,
109 const char * name,
110 int value )
111 {
112 xmlWriteIntAttribute( &(hwc->hwcXmlWriter), name, value );
113 }
114
docHtmlEscapeCharacters(HtmlWritingContext * hwc,const char * ss,int len)115 void docHtmlEscapeCharacters( HtmlWritingContext * hwc,
116 const char * ss,
117 int len )
118 {
119 xmlEscapeCharacters( &(hwc->hwcXmlWriter), ss, len );
120 }
121
docHtmlEscapeString(const char * s,HtmlWritingContext * hwc)122 static void docHtmlEscapeString( const char * s,
123 HtmlWritingContext * hwc )
124 {
125 xmlEscapeCharacters( &(hwc->hwcXmlWriter), s, strlen( s ) );
126
127 return;
128 }
129
130 /************************************************************************/
131 /* */
132 /* Change attributes. */
133 /* */
134 /************************************************************************/
135
docHtmlChangeAttributes(HtmlWritingContext * hwc,int taNr)136 void docHtmlChangeAttributes( HtmlWritingContext * hwc,
137 int taNr )
138 {
139 if ( taNr == hwc->hwcCurrentAttributeNumber )
140 { return; }
141
142 if ( hwc->hwcCurrentAttributeNumber >= 0 )
143 {
144 docHtmlPutString( "</span>", hwc );
145 }
146
147 if ( taNr >= 0 )
148 {
149 char scratch[20];
150
151 sprintf( scratch, "t%d", taNr );
152
153 docHtmlPutString( "<span", hwc );
154 docHtmlWriteStringAttribute( hwc, "class", scratch );
155 docHtmlPutString( ">", hwc );
156 }
157
158 hwc->hwcCurrentAttributeNumber= taNr;
159 }
160
docHtmlStartAnchor(HtmlWritingContext * hwc,int isNote,const MemoryBuffer * fileName,const MemoryBuffer * markName,const MemoryBuffer * refName,const char * title,int titleSize)161 int docHtmlStartAnchor( HtmlWritingContext * hwc,
162 int isNote,
163 const MemoryBuffer * fileName,
164 const MemoryBuffer * markName,
165 const MemoryBuffer * refName,
166 const char * title,
167 int titleSize )
168 {
169 XmlWriter * xw= &(hwc->hwcXmlWriter);
170 SimpleOutputStream * sos= xw->xwSos;
171
172 int afterSpace;
173 int needed= 0;
174
175 if ( fileName && utilMemoryBufferIsEmpty( fileName ) )
176 { fileName= (const MemoryBuffer *)0; }
177 if ( markName && utilMemoryBufferIsEmpty( markName ) )
178 { markName= (const MemoryBuffer *)0; }
179 if ( refName && utilMemoryBufferIsEmpty( refName ) )
180 { refName= (const MemoryBuffer *)0; }
181
182 docHtmlPutString( "<a", hwc );
183
184 if ( isNote )
185 {
186 docHtmlWriteStringAttribute( hwc, "style", "text-decoration:none" );
187 }
188 afterSpace= 0;
189
190 if ( fileName || markName )
191 {
192 needed += 1+ 6;
193 if ( fileName )
194 { needed += fileName->mbSize; }
195 if ( markName )
196 { needed += 1+ markName->mbSize; }
197 needed += 1;
198 }
199
200 if ( refName )
201 { needed += 1+ 6+ refName->mbSize+ 1; }
202
203 if ( xw->xwColumn > 5 &&
204 xw->xwColumn+ needed > 76 )
205 { docHtmlNewLine( hwc ); afterSpace= 1; }
206
207 if ( fileName || markName )
208 {
209 if ( ! afterSpace )
210 { docHtmlPutString( " ", hwc ); }
211
212 docHtmlPutString( "href=\"", hwc );
213
214 if ( fileName )
215 { xmlEscapeBuffer( xw, fileName ); }
216
217 if ( markName )
218 {
219 (void)sioOutPutByte( '#', sos );
220 xmlEscapeBuffer( xw, markName );
221 }
222
223 docHtmlPutString( "\"", hwc );
224 afterSpace= 0;
225 }
226
227 if ( refName )
228 {
229 if ( ! afterSpace )
230 { docHtmlPutString( " ", hwc ); }
231
232 docHtmlPutString( "id=\"", hwc );
233 xmlEscapeBuffer( xw, refName );
234
235 docHtmlPutString( "\"", hwc );
236 afterSpace= 0;
237 }
238
239 if ( titleSize > 0 )
240 {
241 if ( ! afterSpace )
242 { docHtmlPutString( " ", hwc ); }
243
244 docHtmlPutString( "title=\"", hwc );
245 xmlEscapeCharacters( xw, title, titleSize );
246
247 docHtmlPutString( "\"", hwc );
248 afterSpace= 0;
249 }
250
251 docHtmlPutString( ">", hwc );
252
253 return 0;
254 }
255
docHtmlStartField(const DocumentField * df,HtmlWritingContext * hwc,const BufferItem * bi,int attNr)256 int docHtmlStartField( const DocumentField * df,
257 HtmlWritingContext * hwc,
258 const BufferItem * bi,
259 int attNr )
260 {
261 int rval= 0;
262
263 const MemoryBuffer * fileName= (const MemoryBuffer *)0;
264 const MemoryBuffer * markName= (const MemoryBuffer *)0;
265
266 const char * refName= (const char *)0;
267 int refSize= 0;
268 const char * title= (const char *)0;
269 int titleSize= 0;
270
271 HyperlinkField hf;
272 const MemoryBuffer * mbBookmark= (const MemoryBuffer *)0;
273
274 docInitHyperlinkField( &hf );
275
276 switch( df->dfKind )
277 {
278 case DOCfkCHFTN:
279 hwc->hwcInHyperlink++;
280 if ( ! hwc->hwcInBookmark && hwc->hwcInHyperlink == 1 )
281 { docHtmlStartNote( df, hwc, bi, attNr ); }
282 break;
283
284 case DOCfkHYPERLINK:
285 hwc->hwcInHyperlink++;
286 if ( ! hwc->hwcInBookmark && hwc->hwcInHyperlink == 1 )
287 {
288 if ( ! docGetHyperlinkField( &hf, df ) )
289 {
290 const int isNote= 0;
291
292 docHtmlChangeAttributes( hwc, -1 );
293
294 docHtmlStartAnchor( hwc, isNote,
295 &(hf.hfFile), &(hf.hfBookmark),
296 (const MemoryBuffer *)0,
297 title, titleSize );
298
299 docHtmlChangeAttributes( hwc, attNr );
300
301 hwc->hwcBytesInLink= 0;
302 }
303 }
304 break;
305
306 case DOCfkBOOKMARK:
307 hwc->hwcInBookmark++;
308 if ( ! docFieldGetBookmark( &mbBookmark, df ) )
309 {
310 if ( df->dfHeadPosition.epParaNr ==
311 df->dfTailPosition.epParaNr &&
312 ! hwc->hwcInHyperlink &&
313 hwc->hwcInBookmark == 1 )
314 {
315 const int isNote= 0;
316
317 docHtmlChangeAttributes( hwc, -1 );
318
319 docHtmlStartAnchor( hwc, isNote,
320 fileName, markName,
321 mbBookmark,
322 title, titleSize );
323
324 docHtmlChangeAttributes( hwc, attNr );
325 }
326 else{
327 docHtmlPutString( "<span id=\"", hwc );
328 docHtmlEscapeCharacters( hwc, refName, refSize );
329 docHtmlPutString( "\"/>", hwc );
330 }
331 }
332 break;
333
334 case DOCfkPAGEREF:
335 hwc->hwcInPageref++;
336 break;
337
338 default:
339 break;
340 }
341
342 docCleanHyperlinkField( &hf );
343
344 return rval;
345 }
346
docHtmlFinishField(const DocumentField * df,HtmlWritingContext * hwc)347 int docHtmlFinishField( const DocumentField * df,
348 HtmlWritingContext * hwc )
349 {
350 switch( df->dfKind )
351 {
352 case DOCfkCHFTN:
353 case DOCfkHYPERLINK:
354 if ( ! hwc->hwcInBookmark && hwc->hwcInHyperlink == 1 )
355 {
356 docHtmlChangeAttributes( hwc, -1 );
357
358 docHtmlPutString( "</a>", hwc );
359 }
360 hwc->hwcInHyperlink--;
361 if ( hwc->hwcInHyperlink < 0 )
362 { hwc->hwcInHyperlink= 0; }
363 break;
364
365 case DOCfkBOOKMARK:
366 if ( df->dfHeadPosition.epParaNr ==
367 df->dfTailPosition.epParaNr &&
368 ! hwc->hwcInHyperlink &&
369 hwc->hwcInBookmark == 1 )
370 {
371 docHtmlChangeAttributes( hwc, -1 );
372 docHtmlPutString( "</a>", hwc );
373 }
374
375 hwc->hwcInBookmark--;
376 if ( hwc->hwcInBookmark < 0 )
377 { hwc->hwcInBookmark= 0; }
378 break;
379
380 case DOCfkPAGEREF:
381 hwc->hwcInPageref--;
382 break;
383
384 default:
385 break;
386 }
387
388 return 0;
389 }
390
docHtmlEmitBackgroundProperty(const ItemShading * is,HtmlWritingContext * hwc)391 void docHtmlEmitBackgroundProperty(
392 const ItemShading * is,
393 HtmlWritingContext * hwc )
394 {
395 const BufferDocument * bd= hwc->hwcDocument;
396
397 int isFilled= 0;
398 RGB8Color rgb8;
399
400 char scratch[39];
401
402 if ( is->isPattern == DOCspSOLID )
403 {
404 if ( docGetSolidRgbShadeOfItem( &isFilled, &rgb8, bd, is ) )
405 { LDEB(1); }
406
407 if ( isFilled )
408 {
409 sprintf( scratch, "\"#%02x%02x%02x\"",
410 rgb8.rgb8Red, rgb8.rgb8Green, rgb8.rgb8Blue );
411
412 docHtmlWriteStringAttribute( hwc, "BGCOLOR", scratch );
413 }
414 }
415
416 return;
417 }
418
419 /************************************************************************/
420
docHtmlSaveMetaElement(HtmlWritingContext * hwc,const char * name,const MemoryBuffer * content)421 static void docHtmlSaveMetaElement( HtmlWritingContext * hwc,
422 const char * name,
423 const MemoryBuffer * content )
424 {
425 if ( ! utilMemoryBufferIsEmpty( content ) )
426 {
427 docHtmlPutString( "<meta name=\"", hwc );
428 docHtmlEscapeString( name, hwc );
429 docHtmlPutString( "\" content=\"", hwc );
430 docHtmlEscapeString( utilMemoryBufferGetString( content ), hwc );
431 docHtmlPutString( "\"/>", hwc );
432 docHtmlNewLine( hwc );
433 }
434
435 return;
436 }
437
438 /************************************************************************/
439
docHtmlStartDocument(HtmlWritingContext * hwc)440 int docHtmlStartDocument( HtmlWritingContext * hwc )
441 {
442 int rval= 0;
443 const DocumentProperties * dp= &(hwc->hwcDocument->bdProperties);
444 const char * lang;
445
446 MemoryBuffer stylesheet;
447
448 utilInitMemoryBuffer( &stylesheet );
449
450 lang= utilLangForLanguageCode( dp->dpDefaultLanguage );
451
452 # if 0
453 docHtmlPutString( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\r\n",
454 hwc );
455 # else
456 docHtmlPutString( "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n", hwc );
457 docHtmlPutString( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" ",
458 hwc );
459 docHtmlPutString( "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\r\n",
460 hwc );
461 # endif
462
463 docHtmlNewLine( hwc );
464 docHtmlPutString( "<html", hwc );
465 docHtmlWriteStringAttribute( hwc,
466 "xmlns", "http://www.w3.org/1999/xhtml" );
467 if ( lang )
468 { docHtmlWriteStringAttribute( hwc, "xml:lang", lang ); }
469 docHtmlPutString( ">", hwc );
470 docHtmlNewLine( hwc );
471
472 docHtmlPutString( "<head>", hwc );
473 docHtmlNewLine( hwc );
474 docHtmlPutString( "<meta ", hwc );
475 docHtmlWriteStringAttribute( hwc, "http-equiv", "Content-Type" );
476 docHtmlWriteStringAttribute( hwc, "content", "text/html; charset=UTF-8" );
477 docHtmlPutString( "/>", hwc );
478 docHtmlNewLine( hwc );
479
480 if ( ! utilMemoryBufferIsEmpty( &(dp->dpTitle) ) )
481 {
482 docHtmlPutString( "<title>", hwc );
483 docHtmlEscapeString( utilMemoryBufferGetString( &(dp->dpTitle) ), hwc );
484 docHtmlPutString( "</title>", hwc );
485 docHtmlNewLine( hwc );
486 }
487
488 if ( ! hwc->hwcInlineCss )
489 {
490 if ( ! hwc->hwcGetCssName )
491 { XDEB(hwc->hwcInlineCss); rval= -1; goto ready; }
492
493 if ( (*hwc->hwcGetCssName)( &stylesheet, hwc ) < 0 )
494 { LDEB(1); return -1; }
495
496 docHtmlPutString( "<link ", hwc );
497 docHtmlWriteStringAttribute( hwc, "rel", "stylesheet" );
498 docHtmlWriteStringAttribute( hwc, "type", "text/css; charset=UTF-8" );
499 docHtmlWriteStringAttribute( hwc, "href",
500 utilMemoryBufferGetString( &stylesheet ) );
501 docHtmlPutString( "/>", hwc );
502 docHtmlNewLine( hwc );
503 }
504
505 docHtmlSaveMetaElement( hwc, "description", &(dp->dpSubject) );
506 docHtmlSaveMetaElement( hwc, "keywords", &(dp->dpKeywords) );
507 docHtmlSaveMetaElement( hwc, "comment", &(dp->dpDoccomm) );
508 docHtmlSaveMetaElement( hwc, "author", &(dp->dpAuthor) );
509
510 if ( ! utilMemoryBufferIsEmpty( &(dp->dpHlinkbase) ) )
511 {
512 docHtmlPutString( "<base href=\"", hwc );
513 docHtmlEscapeString( utilMemoryBufferGetString( &(dp->dpHlinkbase) ), hwc );
514 docHtmlPutString( "\"/>", hwc );
515 docHtmlNewLine( hwc );
516 }
517
518 if ( hwc->hwcInlineCss )
519 {
520 docHtmlPutString( "<style type=\"text/css\">", hwc );
521 docHtmlNewLine( hwc );
522 docHtmlNewLine( hwc );
523
524 if ( docHtmlSaveDocumentStyles( hwc, hwc->hwcXmlWriter.xwSos ) )
525 { LDEB(1); rval= -1; goto ready; }
526
527 docHtmlPutString( "</style>", hwc );
528 docHtmlNewLine( hwc );
529 }
530
531 docHtmlPutString( "</head>", hwc );
532 docHtmlNewLine( hwc );
533
534 docHtmlPutString( "<body", hwc );
535 docHtmlWriteStringAttribute( hwc, "class", "ted" );
536 /*
537 if ( lang )
538 { docHtmlWriteStringAttribute( hwc, "lang", lang ); }
539 */
540 docHtmlPutString( ">", hwc );
541 docHtmlNewLine( hwc );
542
543 ready:
544
545 utilCleanMemoryBuffer( &stylesheet );
546
547 return rval;
548 }
549
docHtmlFinishDocument(HtmlWritingContext * hwc)550 int docHtmlFinishDocument( HtmlWritingContext * hwc )
551 {
552 docHtmlPutString( "</body></html>", hwc );
553 docHtmlNewLine( hwc );
554
555 return 0;
556 }
557
558 /************************************************************************/
559
docHtmlSaveNotes(HtmlWritingContext * hwc)560 int docHtmlSaveNotes( HtmlWritingContext * hwc )
561 {
562 DocumentField * dfNote;
563 DocumentNote * dn;
564
565 docHtmlPutString( "<hr/>", hwc );
566
567 for ( dfNote= docGetFirstNoteOfDocument( &dn, hwc->hwcDocument, -1 );
568 dfNote;
569 dfNote= docGetNextNoteInDocument( &dn, hwc->hwcDocument, dfNote, -1 ) )
570 {
571 DocumentTree * ei;
572
573 ei= &(dn->dnDocumentTree);
574 if ( ! ei->dtRoot )
575 { XDEB(ei->dtRoot); continue; }
576
577 if ( docHtmlSaveSelection( hwc, ei, (const DocumentSelection *)0 ) )
578 { XDEB(ei->dtRoot); return -1; }
579 }
580
581 if ( hwc->hwcNoteDefCount != hwc->hwcNoteRefCount )
582 { LLDEB(hwc->hwcNoteDefCount,hwc->hwcNoteRefCount); }
583
584 return 0;
585 }
586
587