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