1 /************************************************************************/
2 /*									*/
3 /*  Save a BufferDocument into an EPUB file.				*/
4 /*									*/
5 /*  Documents can be validated against:					*/
6 /*  -  http://threepress.org/document/epub-validate.			*/
7 /*									*/
8 /************************************************************************/
9 
10 #   include	"docHtmlConfig.h"
11 
12 #   include	<string.h>
13 #   include	<stdio.h>
14 #   include	<ctype.h>
15 
16 #   include	<sioGeneral.h>
17 #   include	<sioZip.h>
18 #   include	<utilWindowsLanguageCode.h>
19 #   include	<utilMemoryBufferPrintf.h>
20 
21 #   include	<docBuf.h>
22 #   include	<docCalculateToc.h>
23 #   include	"docHtmlWriteImpl.h"
24 #   include	<docEvalField.h>
25 #   include	<docTreeType.h>
26 
27 #   include	<appDebugon.h>
28 
29 /************************************************************************/
30 
31 typedef struct EpubWriter
32     {
33     CalculateToc	ewCalculateToc;
34     ZipOutput		ewZipOutput;
35     } EpubWriter;
36 
37 static int docEpubGetImageSrcX(		MemoryBuffer *		target,
38 					int			relative,
39 					int			n,
40 					const InsertedObject *	io,
41 					const char *		ext );
42 
43 static SimpleOutputStream * docEpubOpenImageStream(
44 					HtmlWritingContext *	hwc,
45 					int			n,
46 					const InsertedObject *	io,
47 					const char *		mimeType,
48 					const char *		ext );
49 
50 static int docEpubGetImageSrc(		MemoryBuffer *		target,
51 					HtmlWritingContext *	hwc,
52 					int			n,
53 					const InsertedObject *	io,
54 					const char *		ext );
55 
56 static int docEpubGetCssName(		MemoryBuffer *		target,
57 					HtmlWritingContext *	hwc );
58 
docInitEpubWriter(EpubWriter * ew)59 static void docInitEpubWriter(	EpubWriter *	ew )
60     {
61     docInitCalculateToc( &(ew->ewCalculateToc) );
62     sioZipInitOutput( &(ew->ewZipOutput) );
63     }
64 
docCleanEpubWriter(EpubWriter * ew)65 static void docCleanEpubWriter(	EpubWriter *	ew )
66     {
67     sioZipCleanOutput( &(ew->ewZipOutput) );
68     docCleanCalculateToc( &(ew->ewCalculateToc) );
69     }
70 
71 /************************************************************************/
72 
73 # define DIR_CONTENT	"document"
74 # define DIR_MEDIA	"media"
75 # define NAME_CSS	"document.css"
76 # define NAME_DOC	"document.html"
77 
78 static const char	DocEpubIdNcx[]= "ncx";
79 static const char	DocEpubIdCss[]= "css";
80 static const char	DocEpubIdDoc[]= "doc";
81 
82 static const char	DocEpubNameMimetype[]= "mimetype";
83 static const char	DocEpubNameContainer[]= "META-INF/container.xml";
84 static const char	DocEpubNameOpf[]= "content.opf";
85 static const char	DocEpubNameNcx[]= "toc.ncx";
86 static const char	DocEpubNameContent[]= DIR_CONTENT;
87 static const char	DocEpubNameDocAbs[]= DIR_CONTENT "/" NAME_DOC;
88 static const char	DocEpubNameDocRel[]= NAME_DOC;
89 static const char	DocEpubNameMediaAbs[]= DIR_CONTENT "/" DIR_MEDIA;
90 static const char	DocEpubNameMediaRel[]= DIR_MEDIA;
91 
92 static const char	DocEpubNameCssAbs[]= DIR_CONTENT "/"
93 					     DIR_MEDIA "/" NAME_CSS;
94 static const char	DocEpubNameCssRel[]= DIR_MEDIA "/" NAME_CSS;
95 
96 static const char	DocEpubMediaNcx[]= "application/x-dtbncx+xml";
97 static const char	DocEpubMediaCss[]= "text/css";
98 static const char	DocEpubMediaDoc[]= "application/xhtml+xml";
99 static const char	DocEpubMediaEpub[]= "application/epub+zip";
100 
101 static const char	DocEpubNameContentLen= sizeof(DocEpubNameContent)- 1;
102 static const int	DocEpubNameMediaLenAbs= sizeof(DocEpubNameMediaAbs)- 1;
103 static const int	DocEpubNameMediaLenRel= sizeof(DocEpubNameMediaRel) -1;
104 static const int	DocEpubNameCssLenAbs= sizeof(DocEpubNameCssAbs) -1;
105 static const int	DocEpubNameCssLenRel= sizeof(DocEpubNameCssRel) -1;
106 
107 /************************************************************************/
108 
109 static const char	DocEpubXmlDeclaration[]=
110 			    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
111 
112 /************************************************************************/
113 
114 static const char	DocEpubIdentifierName[]= "dcidentifier";
115 
116 /************************************************************************/
117 
118 static const char *	DocEpubOpfMetaHead[]=
119 {
120   "  <metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n",
121   "    xmlns:dcterms=\"http://purl.org/dc/terms/\"\n",
122   "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n",
123   "    xmlns:opf=\"http://www.idpf.org/2007/opf\">\n",
124 };
125 
126 static const char	DocEpubOpfMetaTail[]= "  </metadata>\n";
127 
128 /************************************************************************/
129 /*									*/
130 /*  Insert a boilerplate container.xml.					*/
131 /*									*/
132 /************************************************************************/
133 
134 static const char *	DocEpubContainerXml[]=
135 {
136   DocEpubXmlDeclaration,
137   "<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n",
138   "  <rootfiles>\n",
139   "    <rootfile full-path=\"", DocEpubNameOpf, "\" media-type=\"application/oebps-package+xml\"/>\n",
140   "  </rootfiles>\n",
141   "</container>\n",
142 };
143 
docEpubEmitContainerXml(ZipOutput * zo)144 static int docEpubEmitContainerXml(		ZipOutput *	zo )
145     {
146     int				rval= 0;
147 
148     const char * const		nameMember= DocEpubNameContainer;
149     SimpleOutputStream *	sosMember= (SimpleOutputStream *)0;
150     const int			compressed= 1;
151 
152     int				line;
153 
154     sosMember= sioOutZipOpen( zo, nameMember, compressed );
155     if  ( ! sosMember )
156 	{ SXDEB(nameMember,sosMember); rval= -1; goto ready;	}
157 
158     for ( line= 0; line < sizeof(DocEpubContainerXml)/sizeof(char *); line++ )
159 	{ sioOutPutString( DocEpubContainerXml[line], sosMember ); }
160 
161   ready:
162 
163     if  ( sosMember && sioOutClose( sosMember ) )
164 	{ SDEB(nameMember); rval= -1;	}
165 
166     return rval;
167     }
168 
169 /************************************************************************/
170 /*									*/
171 /*  Emit a meta information element for the opf package file.		*/
172 /*									*/
173 /************************************************************************/
174 
docEpubEmitOpfMetaElement(XmlWriter * xw,const char * id,const char * name,const MemoryBuffer * value)175 static void docEpubEmitOpfMetaElement(	XmlWriter *		xw,
176 					const char *		id,
177 					const char *		name,
178 					const MemoryBuffer *	value )
179     {
180     sioOutPutString( "    <", xw->xwSos );
181     sioOutPutString( name, xw->xwSos );
182     if  ( id )
183 	{ xmlWriteStringAttribute( xw, "id", id );	}
184     sioOutPutString( ">", xw->xwSos );
185 
186     xmlEscapeCharacters( xw, (const char *)value->mbBytes, value->mbSize );
187 
188     sioOutPutString( "</", xw->xwSos );
189     sioOutPutString( name, xw->xwSos );
190     sioOutPutString( ">", xw->xwSos );
191     xmlNewLine( xw );
192     }
193 
docEpubEmitOpfMetaElementIfSet(XmlWriter * xw,const char * id,const char * name,const MemoryBuffer * value)194 static void docEpubEmitOpfMetaElementIfSet(
195 					XmlWriter *		xw,
196 					const char *		id,
197 					const char *		name,
198 					const MemoryBuffer *	value )
199     {
200     if  ( ! utilMemoryBufferIsEmpty( value ) )
201 	{ docEpubEmitOpfMetaElement( xw, id, name, value );	}
202 
203     return;
204     }
205 
206 /************************************************************************/
207 /*									*/
208 /*  Emit the matadata in an OPF package file. I.E:			*/
209 /*									*/
210 /*  Translate document information to Dublin Core Metadata.		*/
211 /*  See http://dublincore.org/documents/usageguide/elements.shtml	*/
212 /*									*/
213 /*  Dublin Core		RTF						*/
214 /*  ------ ----		---						*/
215 /*  Title		title						*/
216 /*  Creator		author						*/
217 /*  Subject		subject+ keywords				*/
218 /*  Description		doccom						*/
219 /*  Publisher		company (Do not repeat the autor value)		*/
220 /*  Contributor		-- (Do not repeat the autor value)		*/
221 /*  Date		revtim or creatim				*/
222 /*  Type		-- Not available				*/
223 /*  Format		-- Irrelevant					*/
224 /*  Identifier		-- Must be made up.				*/
225 /*  Source		-- Not available				*/
226 /*  Language		deflang						*/
227 /*  Relation		-- Not available				*/
228 /*  Coverage		-- Not available				*/
229 /*  Rights		-- Not available				*/
230 /*									*/
231 /************************************************************************/
232 
docEpubEmitMetaData(XmlWriter * xw,const MemoryBuffer * title,const MemoryBuffer * identifier,const BufferDocument * bd)233 static int docEpubEmitMetaData(		XmlWriter *		xw,
234 					const MemoryBuffer *	title,
235 					const MemoryBuffer *	identifier,
236 					const BufferDocument *	bd )
237     {
238     int				rval= 0;
239     const DocumentProperties *	dp= &(bd->bdProperties);
240     const char *		lang= "en_US";
241 
242     MemoryBuffer		mbLang;
243 
244     int				l;
245 
246     utilInitMemoryBuffer( &mbLang );
247     if  ( utilMemoryBufferSetString( &mbLang, lang ) )
248 	{ LDEB(1); rval= -1; goto ready;	}
249 
250     for ( l= 0; l < sizeof(DocEpubOpfMetaHead)/sizeof(char *); l++ )
251 	{ sioOutPutString( DocEpubOpfMetaHead[l], xw->xwSos ); }
252 
253     docEpubEmitOpfMetaElement( xw, (char *)0, "dc:title", title );
254 
255     lang= utilLangForLanguageCode( dp->dpDefaultLanguage );
256     if  ( lang )
257 	{
258 	if  ( utilMemoryBufferSetString( &mbLang, lang ) )
259 	    { LDEB(1); rval= -1; goto ready;	}
260 	}
261 
262     docEpubEmitOpfMetaElement( xw, (char *)0, "dc:language", &mbLang );
263     docEpubEmitOpfMetaElement( xw, DocEpubIdentifierName,
264 					"dc:identifier", identifier );
265 
266     docEpubEmitOpfMetaElementIfSet( xw, (char *)0,
267 					"dc:creator", &(dp->dpAuthor) );
268 
269     docEpubEmitOpfMetaElementIfSet( xw, (char *)0,
270 					"dc:subject", &(dp->dpSubject) );
271     docEpubEmitOpfMetaElementIfSet( xw, (char *)0,
272 					"dc:subject", &(dp->dpKeywords) );
273 
274     docEpubEmitOpfMetaElementIfSet( xw, (char *)0,
275 					"dc:description", &(dp->dpDoccomm) );
276     docEpubEmitOpfMetaElementIfSet( xw, (char *)0,
277 					"dc:publisher", &(dp->dpCompany) );
278 
279     sioOutPutString( DocEpubOpfMetaTail, xw->xwSos );
280 
281   ready:
282 
283     utilCleanMemoryBuffer( &mbLang );
284 
285     return rval;
286     }
287 
288 /************************************************************************/
289 
docEpubEmitManifestItem(XmlWriter * xw,const char * id,const char * href,const char * mediaType)290 static int docEpubEmitManifestItem(	XmlWriter *		xw,
291 					const char *		id,
292 					const char *		href,
293 					const char *		mediaType )
294     {
295     sioOutPutString( "    <item", xw->xwSos );
296 
297     xmlWriteStringAttribute( xw, "id", id );
298     xmlWriteStringAttribute( xw, "href", href );
299     xmlWriteStringAttribute( xw, "media-type", mediaType );
300 
301     sioOutPutString( "/>\n", xw->xwSos );
302 
303     return 0;
304     }
305 
docEpubEmitSpineItem(XmlWriter * xw,const char * id)306 static int docEpubEmitSpineItem(	XmlWriter *		xw,
307 					const char *		id )
308     {
309     sioOutPutString( "    <itemref", xw->xwSos );
310 
311     xmlWriteStringAttribute( xw, "idref", id );
312 
313     sioOutPutString( "/>\n", xw->xwSos );
314 
315     return 0;
316     }
317 
318 /************************************************************************/
319 
docEpubAddImageToOpf(int n,void * vio,void * vxw)320 static int docEpubAddImageToOpf(	int			n,
321 					void *			vio,
322 					void *			vxw )
323     {
324     int				rval= 0;
325 
326     const InsertedObject *	io= (InsertedObject *)vio;
327     const PictureProperties *	pip= &(io->ioPictureProperties);
328     XmlWriter *			xw= (XmlWriter *)vxw;
329 
330     const char *		mimeType;
331     const char *		ext;
332     const MemoryBuffer *	mb= (const MemoryBuffer *)0;
333     int				type;
334 
335     MemoryBuffer		href;
336     char			id[20+1];
337     const int			relative= 0;
338 
339     utilInitMemoryBuffer( &href );
340 
341     if  ( docHtmlObjectSaveHow( &type, &mimeType, &ext, &mb, io ) )
342 	{ goto ready;	}
343 
344     if  ( docEpubGetImageSrcX( &href, relative, n, io, ext )
345 									< 0 )
346 	{ LDEB(n); rval= -1; goto ready;	}
347 
348     if  ( 1 || pip->pipBliptag == 0 )
349 	{ sprintf( id, "i%d", n );			}
350     else{ sprintf( id, "b%08lx", pip->pipBliptag );	}
351 
352     if  ( docEpubEmitManifestItem( xw, id, utilMemoryBufferGetString( &href ), mimeType ) )
353 	{ SDEB(id); rval= -1; goto ready;	}
354 
355   ready:
356 
357     utilCleanMemoryBuffer( &href );
358 
359     return rval;
360     }
361 
docEpubAddImagesToOpf(const BufferDocument * bd,XmlWriter * xw)362 static int docEpubAddImagesToOpf(	const BufferDocument *	bd,
363 					XmlWriter *		xw )
364     {
365     utilPagedListForAll( &(bd->bdObjectList.iolPagedList),
366 					docEpubAddImageToOpf, (void *)xw );
367 
368     return 0;
369     }
370 
371 /************************************************************************/
372 
373 static const char	DocEpubOpfManifestHead[]= "<manifest>\n";
374 static const char	DocEpubOpfManifestTail[]= "</manifest>\n";
375 
376 static const char *	DocEpubSpineHead[]=
377 {
378   "<spine toc=\"", DocEpubIdNcx, "\">\n",
379 };
380 static const char	DocEpubSpineTail[]= "</spine>\n";
381 
382 
383 static const char *	DocEpubOpfHead[]=
384 {
385   DocEpubXmlDeclaration,
386   "<package xmlns=\"http://www.idpf.org/2007/opf\" version=\"2.0\" unique-identifier=\"", DocEpubIdentifierName, "\">\n",
387 };
388 static const char	DocEpubOpfTail[]= "</package>\n";
389 
docEpubStartOpf(ZipOutput * zo,XmlWriter * xw,const MemoryBuffer * title,const MemoryBuffer * identifier,const BufferDocument * bd)390 static int docEpubStartOpf(	ZipOutput *		zo,
391 				XmlWriter *		xw,
392 				const MemoryBuffer *	title,
393 				const MemoryBuffer *	identifier,
394 				const BufferDocument *	bd )
395     {
396     const char * const		nameMember= DocEpubNameOpf;
397     const int			compressed= 1;
398 
399     int				l;
400 
401     xw->xwSos= sioOutZipOpen( zo, nameMember, compressed );
402     if  ( ! xw->xwSos )
403 	{ SXDEB(nameMember,xw->xwSos); return -1;	}
404 
405     for ( l= 0; l < sizeof(DocEpubOpfHead)/sizeof(char *); l++ )
406 	{ sioOutPutString( DocEpubOpfHead[l], xw->xwSos ); }
407 
408     if  ( docEpubEmitMetaData( xw, title, identifier, bd ) )
409 	{ SDEB(nameMember); return -1;	}
410 
411     return 0;
412     }
413 
docEpubEmitSimpleOpf(ZipOutput * zo,const MemoryBuffer * title,const MemoryBuffer * identifier,const BufferDocument * bd)414 static int docEpubEmitSimpleOpf(	ZipOutput *		zo,
415 					const MemoryBuffer *	title,
416 					const MemoryBuffer *	identifier,
417 					const BufferDocument *	bd )
418     {
419     int				rval= 0;
420     int				l;
421 
422     XmlWriter			xw;
423 
424     xmlInitXmlWriter( &xw );
425 
426     if  ( docEpubStartOpf( zo, &xw, title, identifier, bd ) )
427 	{ LDEB(1); rval= -1; goto ready;	}
428 
429     sioOutPutString( DocEpubOpfManifestHead, xw.xwSos );
430 
431     docEpubEmitManifestItem( &xw,
432 			    DocEpubIdNcx, DocEpubNameNcx, DocEpubMediaNcx );
433     docEpubEmitManifestItem( &xw,
434 			    DocEpubIdCss, DocEpubNameCssAbs, DocEpubMediaCss );
435     docEpubEmitManifestItem( &xw,
436 			    DocEpubIdDoc, DocEpubNameDocAbs, DocEpubMediaDoc );
437 
438     docEpubAddImagesToOpf( bd, &xw );
439 
440     sioOutPutString( DocEpubOpfManifestTail, xw.xwSos );
441 
442     for ( l= 0; l < sizeof(DocEpubSpineHead)/sizeof(char *); l++ )
443 	{ sioOutPutString( DocEpubSpineHead[l], xw.xwSos ); }
444 
445     docEpubEmitSpineItem( &xw, DocEpubIdDoc );
446 
447     sioOutPutString( DocEpubSpineTail, xw.xwSos );
448 
449     sioOutPutString( DocEpubOpfTail, xw.xwSos );
450 
451   ready:
452 
453     if  ( xw.xwSos && sioOutClose( xw.xwSos ) )
454 	{ LDEB(1); rval= -1;	}
455 
456     return rval;
457     }
458 
459 /************************************************************************/
460 
docEpubEmitMimeType(ZipOutput * zo)461 static int docEpubEmitMimeType(		ZipOutput *	zo )
462     {
463     int				rval= 0;
464 
465     SimpleOutputStream *	sosMember= (SimpleOutputStream *)0;
466     const char *		nameMember= DocEpubNameMimetype;
467     const int			compressed= 0;
468 
469     sosMember= sioOutZipOpen( zo, nameMember, compressed );
470     if  ( ! sosMember )
471 	{ SXDEB(nameMember,sosMember); rval= -1; goto ready;	}
472 
473     sioOutPutString( DocEpubMediaEpub, sosMember );
474     sioOutPutString( "\n", sosMember );
475 
476   ready:
477 
478     if  ( sosMember && sioOutClose( sosMember ) )
479 	{ SDEB(nameMember); rval= -1;	}
480 
481     return rval;
482     }
483 
484 /************************************************************************/
485 
docEpubEmitNcxMetaElement(XmlWriter * xw,const char * name,const char * value)486 static void docEpubEmitNcxMetaElement(	XmlWriter *		xw,
487 					const char *		name,
488 					const char *		value )
489     {
490     sioOutPutString( "    <meta", xw->xwSos );
491 
492     xmlWriteStringAttribute( xw, "name", name );
493     xmlWriteStringAttribute( xw, "content", value );
494 
495     sioOutPutString( "/>\n", xw->xwSos );
496     }
497 
docEpubEmitNcxHead(XmlWriter * xw,const MemoryBuffer * title,const MemoryBuffer * identifier,int depth)498 static int docEpubEmitNcxHead(		XmlWriter *		xw,
499 					const MemoryBuffer *	title,
500 					const MemoryBuffer *	identifier,
501 					int			depth )
502     {
503     char		scratch[10];
504 
505     sprintf( scratch, "%d", depth );
506 
507     sioOutPutString( "    <head>\n", xw->xwSos );
508 
509     docEpubEmitNcxMetaElement( xw, "dtb:uid",
510 				    utilMemoryBufferGetString( identifier ) );
511     docEpubEmitNcxMetaElement( xw, "dtb:depth", scratch );
512     docEpubEmitNcxMetaElement( xw, "dtb:totalPageCount", "0" );
513     docEpubEmitNcxMetaElement( xw, "dtb:maxPageNumber", "0" );
514 
515     sioOutPutString( "    </head>\n", xw->xwSos );
516 
517     sioOutPutString( "    <docTitle><text>", xw->xwSos );
518     xmlEscapeCharacters( xw, (const char *)title->mbBytes, title->mbSize );
519     sioOutPutString( "</text></docTitle>\n", xw->xwSos );
520 
521     return 0;
522     }
523 
524 /************************************************************************/
525 
docEpubCloseNavPoint(XmlWriter * xw,int level)526 static int docEpubCloseNavPoint(	XmlWriter *		xw,
527 					int			level )
528     {
529     sioOutPrintf( xw->xwSos, "%*s</navPoint>\n", 4+ 4* level, "" );
530     return 0;
531     }
532 
docEpubStartNavPoint(XmlWriter * xw,int level,const char * id,int playOrder,const MemoryBuffer * label,const char * src)533 static int docEpubStartNavPoint(	XmlWriter *		xw,
534 					int			level,
535 					const char *		id,
536 					int			playOrder,
537 					const MemoryBuffer *	label,
538 					const char *		src )
539     {
540     sioOutPrintf( xw->xwSos, "%*s<navPoint", 4+ 4* level, "" );
541     xmlWriteStringAttribute( xw, "id", id );
542     xmlWriteIntAttribute( xw, "playOrder", playOrder );
543     sioOutPutString( ">\n", xw->xwSos );
544 
545     sioOutPrintf( xw->xwSos, "%*s<navLabel><text>", 8+ 4* level, "" );
546     xmlEscapeCharacters( xw, (const char *)label->mbBytes, label->mbSize );
547     sioOutPutString( "</text></navLabel>\n", xw->xwSos );
548 
549     sioOutPrintf( xw->xwSos, "%*s<content", 8+ 4* level, "" );
550     xmlWriteStringAttribute( xw, "src", src );
551     sioOutPutString( "/>\n", xw->xwSos );
552 
553     return 0;
554     }
555 
556 /************************************************************************/
557 
558 static const char *	DocEpubNcxHead[]=
559 {
560   DocEpubXmlDeclaration,
561   "<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\"\n",
562   "  \"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n",
563   "<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\">\n",
564 };
565 static const char	DocEpubNcxTail[]= "</ncx>\n";
566 
docEpubStartNcx(ZipOutput * zo,XmlWriter * xw,const MemoryBuffer * title,const MemoryBuffer * identifier,int depth,const BufferDocument * bd)567 static int docEpubStartNcx(	ZipOutput *		zo,
568 				XmlWriter *		xw,
569 				const MemoryBuffer *	title,
570 				const MemoryBuffer *	identifier,
571 				int			depth,
572 				const BufferDocument *	bd )
573     {
574     const char * const		nameMember= DocEpubNameNcx;
575     const int			compressed= 1;
576 
577     int				l;
578 
579     xw->xwSos= sioOutZipOpen( zo, nameMember, compressed );
580     if  ( ! xw->xwSos )
581 	{ SXDEB(nameMember,xw->xwSos); return -1;	}
582 
583     for ( l= 0; l < sizeof(DocEpubNcxHead)/sizeof(char *); l++ )
584 	{ sioOutPutString( DocEpubNcxHead[l], xw->xwSos ); }
585 
586     if  ( docEpubEmitNcxHead( xw, title, identifier, depth ) )
587 	{ SDEB(nameMember); return -1;	}
588 
589     return 0;
590     }
591 
592 /************************************************************************/
593 /*									*/
594 /*  Emit a Contents NCX that only holds the main document.		*/
595 /*									*/
596 /************************************************************************/
597 
docEpubEmitSimpleNcx(ZipOutput * zo,const MemoryBuffer * title,const MemoryBuffer * identifier,const BufferDocument * bd)598 static int docEpubEmitSimpleNcx(	ZipOutput *		zo,
599 					const MemoryBuffer *	title,
600 					const MemoryBuffer *	identifier,
601 					const BufferDocument *	bd )
602     {
603     int				rval= 0;
604     const int			depth= 1;
605 
606     int				playOrder= 1;
607 
608     XmlWriter			xw;
609 
610     xmlInitXmlWriter( &xw );
611 
612     if  ( docEpubStartNcx( zo, &xw, title, identifier, depth, bd ) )
613 	{ LDEB(1); rval= -1; goto ready;	}
614 
615     sioOutPutString( "    <navMap>\n", xw.xwSos );
616 
617     docEpubStartNavPoint( &xw, 0, DocEpubIdDoc, playOrder++,
618 						    title, DocEpubNameDocAbs );
619     docEpubCloseNavPoint( &xw, 0 );
620 
621     sioOutPutString( "    </navMap>\n", xw.xwSos );
622 
623     sioOutPutString( DocEpubNcxTail, xw.xwSos );
624 
625   ready:
626 
627     if  ( xw.xwSos && sioOutClose( xw.xwSos ) )
628 	{ LDEB(1); rval= -1;	}
629 
630     return rval;
631     }
632 
633 /************************************************************************/
634 /*									*/
635 /*  Emit a Contents NCX that follows the structure of a table of	*/
636 /*  contents.								*/
637 /*									*/
638 /*  Note the toilsome mapping between the level numbers in the TOC and	*/
639 /*  those in the NCX. This is to cope with situations where the TOC	*/
640 /*  skips levels.							*/
641 /*									*/
642 /************************************************************************/
643 
docEpubEmitCompositeNcx(EpubWriter * ew,const MemoryBuffer * title,const MemoryBuffer * identifier,const BufferDocument * bd)644 static int docEpubEmitCompositeNcx(	EpubWriter *		ew,
645 					const MemoryBuffer *	title,
646 					const MemoryBuffer *	identifier,
647 					const BufferDocument *	bd )
648     {
649     int				rval= 0;
650 
651     int				playOrder= 1;
652     const TocEntry *		te;
653     int				l;
654 
655     char			id[20+1];
656     char			src[150+1];
657 
658     MemoryBuffer		mbLabel;
659     XmlWriter			xw;
660 
661     int				ncxLevel;
662     int				m[PPoutline_COUNT];
663 
664     utilInitMemoryBuffer( &mbLabel );
665     xmlInitXmlWriter( &xw );
666 
667     if  ( docEpubStartNcx( &(ew->ewZipOutput), &xw,
668 			title, identifier, ew->ewCalculateToc.ctDepth, bd ) )
669 	{ LDEB(1); rval= -1; goto ready;	}
670 
671     sioOutPutString( "    <navMap>\n", xw.xwSos );
672 
673     for ( ncxLevel= 0; ncxLevel < PPoutline_COUNT; ncxLevel++ )
674 	{ m[ncxLevel]= 0;	}
675     ncxLevel= 0;
676 
677     te= ew->ewCalculateToc.ctEntries;
678     for ( l= 0; l < ew->ewCalculateToc.ctEntryCount; te++, l++ )
679 	{
680 #	if 0
681 	sioOutPrintf( xw.xwSos, "%*s<--  m[%d]=%d teLevel= %d -->\n",
682 			    4+ 4* ncxLevel, "",
683 			    ncxLevel, m[ncxLevel], te->teLevel );
684 #	endif
685 
686 	while( m[ncxLevel] > te->teLevel )
687 	    { docEpubCloseNavPoint( &xw, --ncxLevel );	}
688 
689 	sprintf( src, "%s#%.*s", DocEpubNameDocAbs,
690 					    te->teMarkName->mbSize,
691 					    te->teMarkName->mbBytes );
692 
693 	utilEmptyMemoryBuffer( &mbLabel );
694 	if  ( docCalculateRefFieldValue( &mbLabel, te->teMarkName, bd ) )
695 	    { LDEB(1); rval= -1; goto ready;	}
696 
697 	sprintf( id, "np-%d", playOrder );
698 	docEpubStartNavPoint( &xw, ncxLevel, id, playOrder++, &mbLabel, src );
699 	m[++ncxLevel]= te->teLevel+ 1;
700 
701 #	if 0
702 	sioOutPrintf( xw.xwSos, "%*s<--  m[%d]:=%d teLevel= %d -->\n",
703 			    4+ 4* ncxLevel, "",
704 			    ncxLevel, m[ncxLevel], te->teLevel );
705 #	endif
706 	}
707 
708     while( ncxLevel > 0 )
709 	{ docEpubCloseNavPoint( &xw, --ncxLevel );	}
710 
711     sioOutPutString( "    </navMap>\n", xw.xwSos );
712 
713     sioOutPutString( DocEpubNcxTail, xw.xwSos );
714 
715   ready:
716 
717     utilCleanMemoryBuffer( &mbLabel );
718 
719     if  ( xw.xwSos && sioOutClose( xw.xwSos ) )
720 	{ LDEB(1); rval= -1;	}
721 
722     return rval;
723     }
724 
725 /************************************************************************/
726 /*									*/
727 /*  Return the name of the CSS file.					*/
728 /*									*/
729 /************************************************************************/
730 
docEpubGetCssNameX(MemoryBuffer * target,int relative)731 static int docEpubGetCssNameX(		MemoryBuffer *		target,
732 					int			relative )
733     {
734     if  ( relative )
735 	{
736 	if  ( utilMemoryBufferSetString( target,DocEpubNameCssRel ) )
737 	    { LDEB(1); return -1;	}
738 
739 	return 0;
740 	}
741     else{
742 	if  ( utilMemoryBufferSetString( target,DocEpubNameCssAbs ) )
743 	    { LDEB(1); return -1;	}
744 
745 	return 0;
746 	}
747     }
748 
docEpubGetCssName(MemoryBuffer * target,HtmlWritingContext * hwc)749 static int docEpubGetCssName(		MemoryBuffer *		target,
750 					HtmlWritingContext *	hwc )
751     {
752     const int		relative= 1;
753 
754     return docEpubGetCssNameX( target, relative );
755     }
756 
757 /************************************************************************/
758 /*									*/
759 /*  Emit the stylesheet							*/
760 /*									*/
761 /************************************************************************/
762 
docEpubWriteCss(HtmlWritingContext * hwc)763 static int docEpubWriteCss(		HtmlWritingContext *	hwc )
764     {
765     int				rval= 0;
766 
767     EpubWriter *		ew= (EpubWriter *)hwc->hwcPrivate;
768     const int			relative= 0;
769 
770     MemoryBuffer		nameMember;
771     SimpleOutputStream *	sosCss= (SimpleOutputStream *)0;
772     const int			compressed= 1;
773 
774     utilInitMemoryBuffer( &nameMember );
775 
776     if  ( docEpubGetCssNameX( &nameMember, relative ) < 0 )
777 	{ LDEB(1); rval= -1; goto ready;	}
778 
779     sosCss= sioOutZipOpen( &(ew->ewZipOutput),
780 			utilMemoryBufferGetString( &nameMember ), compressed );
781     if  ( ! sosCss )
782 	{ XDEB(sosCss); rval= -1; goto ready;	}
783 
784     if  ( docHtmlSaveDocumentStyles( hwc, sosCss ) )
785 	{ LDEB(1); rval= -1; goto ready; }
786 
787   ready:
788 
789     utilCleanMemoryBuffer( &nameMember );
790 
791     if  ( sosCss )
792 	{ sioOutClose( sosCss );	}
793 
794     return rval;
795     }
796 
797 /************************************************************************/
798 /*									*/
799 /*  Emit an actual document.						*/
800 /*									*/
801 /************************************************************************/
802 
docEpubEmitDocument(HtmlWritingContext * hwc,BufferDocument * bd,const char * relativeName,const DocumentSelection * ds)803 static int docEpubEmitDocument(	HtmlWritingContext *		hwc,
804 				BufferDocument *		bd,
805 				const char *			relativeName,
806 				const DocumentSelection *	ds )
807     {
808     int				rval= 0;
809     EpubWriter *		ew= (EpubWriter *)hwc->hwcPrivate;
810 
811     char			absoluteName[200+1];
812     SimpleOutputStream *	sosDoc= (SimpleOutputStream *)0;
813     const int			compressed= 1;
814 
815     if  ( DocEpubNameContentLen+ 1+ strlen(relativeName)+ 1 >=
816 							sizeof(absoluteName) )
817 	{ SDEB(relativeName); rval= -1; goto ready;	}
818 
819     sprintf( absoluteName, "%s/%s", DocEpubNameContent, relativeName );
820 
821     sosDoc= sioOutZipOpen( &(ew->ewZipOutput), absoluteName, compressed );
822     if  ( ! sosDoc )
823 	{ SXDEB(absoluteName,sosDoc); rval= -1; goto ready;	}
824 
825     hwc->hwcXmlWriter.xwSos= sosDoc;
826     hwc->hwcXmlWriter.xwColumn= 0;
827 
828     if  ( docHtmlStartDocument( hwc ) )
829 	{ LDEB(1); rval= -1; goto ready;	}
830 
831     if  ( docHtmlSaveSelection( hwc, &(bd->bdBody), ds ) )
832 	{ XDEB(ds); rval= -1; goto ready;	}
833 
834     if  ( ! hwc->hwcInlineNotes )
835 	{
836 	if  ( hwc->hwcNoteRefCount > 0	&&
837 	      docHtmlSaveNotes( hwc )	)
838 	    { LDEB(hwc->hwcNoteRefCount); rval= -1; goto ready;	}
839 	}
840 
841     if  ( docHtmlFinishDocument( hwc ) )
842 	{ LDEB(1); rval= -1; goto ready;	}
843 
844   ready:
845 
846     hwc->hwcXmlWriter.xwSos= (SimpleOutputStream *)0; /* Only for this doc! */
847 
848     if  ( sosDoc )
849 	{ sioOutClose( sosDoc );	}
850 
851     return rval;
852     }
853 
854 /************************************************************************/
855 /*									*/
856 /*  Find the table of contents of the document.				*/
857 /*									*/
858 /************************************************************************/
859 
docHtmlGetTocForEpub(TocField * tf,BufferDocument * bd)860 static DocumentField * docHtmlGetTocForEpub(	TocField *		tf,
861 						BufferDocument *	bd )
862     {
863     const DocumentFieldList *	dfl= &(bd->bdFieldList);
864     const int			fieldCount= dfl->dflPagedList.plItemCount;
865     int				fieldNr;
866 
867     DocumentField *		rval= (DocumentField *)0;
868 
869     for ( fieldNr= 0; fieldNr < fieldCount; fieldNr++ )
870 	{
871 	DocumentField *		df= docGetFieldByNumber( dfl, fieldNr );
872 
873 	if  ( ! df						||
874 	      df->dfKind != DOCfkTOC				||
875 	      df->dfSelectionScope.ssTreeType != DOCinBODY	)
876 	    { continue;	}
877 
878 	docFieldGetToc( tf, df );
879 	if  ( tf->tfType == TOCtypeTOC )
880 	    { rval= df; break;	}
881 	}
882 
883     return rval;
884     }
885 
886 /************************************************************************/
887 /*									*/
888 /*  Save a document to EPUB.						*/
889 /*									*/
890 /************************************************************************/
891 
docEpubSaveDocument(SimpleOutputStream * sos,BufferDocument * bd,const LayoutContext * lc)892 int docEpubSaveDocument(	SimpleOutputStream *	sos,
893 				BufferDocument *	bd,
894 				const LayoutContext *	lc )
895     {
896     HtmlWritingContext		hwc;
897     int				rval= 0;
898     EpubWriter			ew;
899 
900     const DocumentProperties *	dp= &(bd->bdProperties);
901     DocumentField *		dfToc;
902 
903     MemoryBuffer		identifier;
904     MemoryBuffer		title;
905 
906     const char * const		identifier_X= "Identifier";
907     const char * 		title_X= "UnknownTitle";
908 
909     utilInitMemoryBuffer( &identifier );
910     utilInitMemoryBuffer( &title );
911 
912     docInitHtmlWritingContext( &hwc );
913     docInitEpubWriter( &ew );
914 
915     if  ( utilMemoryBufferSetString( &identifier, identifier_X ) )
916 	{ LDEB(1); rval= -1; goto ready;	}
917     if  ( utilMemoryBufferSetString( &identifier, title_X ) )
918 	{ LDEB(1); rval= -1; goto ready;	}
919 
920     hwc.hwcSupportsBullets= 0;
921     hwc.hwcEmitBackground= 0;
922 
923     ew.ewZipOutput.zoSosZip= sos;
924 
925     ew.ewCalculateToc.ctBdDoc= bd;
926 
927     hwc.hwcXmlWriter.xwSos= (SimpleOutputStream *)0; /* sos is not for HTML */
928     hwc.hwcXmlWriter.xwCrlf= 0;
929 
930     hwc.hwcLayoutContext= lc;
931     hwc.hwcOpenImageStream= docEpubOpenImageStream;
932     hwc.hwcGetImageSrc= docEpubGetImageSrc;
933     hwc.hwcGetCssName= docEpubGetCssName;
934     hwc.hwcPrivate= (void *)&ew;
935     hwc.hwcDocument= bd;
936     hwc.hwcInlineCss= 0;
937     hwc.hwcInlineNotes= 1;
938 
939     if  ( ! utilMemoryBufferIsEmpty( &(dp->dpTitle) ) )
940 	{
941 	if  ( utilCopyMemoryBuffer( &title, &(dp->dpTitle) ) )
942 	    { LDEB(1); rval= -1; goto ready;	}
943 	}
944 
945     if  ( docEpubEmitMimeType( &(ew.ewZipOutput) ) )
946 	{ LDEB(1); rval= -1; goto ready;	}
947     if  ( docEpubEmitContainerXml( &(ew.ewZipOutput) ) )
948 	{ LDEB(1); rval= -1; goto ready;	}
949 
950     dfToc= docHtmlGetTocForEpub( &(ew.ewCalculateToc.ctTocField), bd );
951     if  ( dfToc )
952 	{
953 	if  ( docCollectTocInput( &(ew.ewCalculateToc) ) )
954 	    { LDEB(1); rval= -1; goto ready;	}
955 
956 	if  ( docEpubEmitSimpleOpf( &(ew.ewZipOutput),
957 						&title, &identifier, bd ) )
958 	    { LDEB(1); rval= -1; goto ready;	}
959 	if  ( docEpubEmitCompositeNcx( &ew, &title, &identifier, bd ) )
960 	    { LDEB(1); rval= -1; goto ready;	}
961 	if  ( docEpubEmitDocument( &hwc, bd, DocEpubNameDocRel,
962 					    (const DocumentSelection *)0 ) )
963 	    { LDEB(1); rval= -1; goto ready;	}
964 	}
965     else{
966 	if  ( docEpubEmitSimpleOpf( &(ew.ewZipOutput),
967 						&title, &identifier, bd ) )
968 	    { LDEB(1); rval= -1; goto ready;	}
969 	if  ( docEpubEmitSimpleNcx( &(ew.ewZipOutput),
970 						&title, &identifier, bd ) )
971 	    { LDEB(1); rval= -1; goto ready;	}
972 	if  ( docEpubEmitDocument( &hwc, bd, DocEpubNameDocRel,
973 					    (const DocumentSelection *)0 ) )
974 	    { LDEB(1); rval= -1; goto ready;	}
975 	}
976 
977     if  ( ! hwc.hwcInlineCss && docEpubWriteCss( &hwc ) )
978 	{ LDEB(1); rval= -1; return -1;	}
979 
980     if  ( docHtmlSaveImages( &hwc ) )
981 	{ LDEB(hwc.hwcImageCount); rval= -1; goto ready;	}
982 
983     if  ( sioZipFlushOutput( &(ew.ewZipOutput) ) )
984 	{ LDEB(1); rval= -1; goto ready;	}
985 
986   ready:
987 
988     utilCleanMemoryBuffer( &identifier );
989     utilCleanMemoryBuffer( &title );
990 
991     docCleanEpubWriter( &ew );
992     docCleanHtmlWritingContext( &hwc );
993 
994     return rval;
995     }
996 
997 /************************************************************************/
998 /*									*/
999 /*  Get the 'src' value for an embedded image.				*/
1000 /*									*/
1001 /************************************************************************/
1002 
docEpubGetImageSrcX(MemoryBuffer * target,int relative,int n,const InsertedObject * io,const char * ext)1003 static int docEpubGetImageSrcX(		MemoryBuffer *		target,
1004 					int			relative,
1005 					int			n,
1006 					const InsertedObject *	io,
1007 					const char *		ext )
1008     {
1009     const PictureProperties *	pip= &(io->ioPictureProperties);
1010 
1011     const char *		dir;
1012 
1013     if  ( relative )
1014 	{
1015 	dir= DocEpubNameMediaRel;
1016 	/*dirlen= DocEpubNameMediaLenRel;*/
1017 	}
1018     else{
1019 	dir= DocEpubNameMediaAbs;
1020 	/*dirlen= DocEpubNameMediaLenAbs;*/
1021 	}
1022 
1023     if  ( 1 || pip->pipBliptag == 0 )
1024 	{ utilMemoryBufferPrintf( target, "%s/i%d.%s", dir, n, ext );			}
1025     else{ utilMemoryBufferPrintf( target, "%s/b%08lx.%s", dir, pip->pipBliptag, ext );	}
1026 
1027     return 0;
1028     }
1029 
docEpubGetImageSrc(MemoryBuffer * target,HtmlWritingContext * hwc,int n,const InsertedObject * io,const char * ext)1030 static int docEpubGetImageSrc(		MemoryBuffer *		target,
1031 					HtmlWritingContext *	hwc,
1032 					int			n,
1033 					const InsertedObject *	io,
1034 					const char *		ext )
1035     {
1036     const int		relative= 1;
1037 
1038     return docEpubGetImageSrcX( target, relative, n, io, ext );
1039     }
1040 
1041 /************************************************************************/
1042 /*									*/
1043 /*  Open the file to write an image.					*/
1044 /*									*/
1045 /************************************************************************/
1046 
docEpubOpenImageStream(HtmlWritingContext * hwc,int n,const InsertedObject * io,const char * mimeType,const char * ext)1047 static SimpleOutputStream * docEpubOpenImageStream(
1048 					HtmlWritingContext *	hwc,
1049 					int			n,
1050 					const InsertedObject *	io,
1051 					const char *		mimeType,
1052 					const char *		ext )
1053     {
1054     EpubWriter *		ew= (EpubWriter *)hwc->hwcPrivate;
1055     SimpleOutputStream *	sosImage= (SimpleOutputStream *)0;
1056     const int			relative= 0;
1057     const int			compressed= 1;
1058 
1059     MemoryBuffer		src;
1060 
1061     utilInitMemoryBuffer( &src );
1062 
1063     if  ( docEpubGetImageSrcX( &src, relative, n, io, ext ) < 0 )
1064 	{ LDEB(1); goto ready;	}
1065 
1066     sosImage= sioOutZipOpen( &(ew->ewZipOutput),
1067 				utilMemoryBufferGetString( &src ), compressed );
1068     if  ( ! sosImage )
1069 	{ XDEB(sosImage); goto ready; }
1070 
1071   ready:
1072 
1073     utilCleanMemoryBuffer( &src );
1074 
1075     return sosImage;
1076     }
1077