00001
00008
00009
00010 #include <stdlib.h>
00011 #include <stdio.h>
00012 #include <string.h>
00013 #include "zip.h"
00014 #include "NE.h"
00015
00016 #if defined(_WIN32) || defined(_WIN64)
00017
00018 # define strncasecmp _strnicmp
00019 #endif
00020
00021 #define kBufferSize 65536L // a larger value might be marginally more efficient
00022 #define kPathSize 512 // must be large enough for document paths
00023
00024 #define DOCDIR "OPS"
00025 #define ROOTDOC "content.opf"
00026 #define NCXDOC "toc.ncx"
00027 #define COVERDOC "cover.xhtml"
00028 #define ENDNOTESDOC "endnotes.xhtml"
00029
00030 #define Chk(e) do { err = (e); if (err != kNEErrOk) return err; } while (0)
00031
00032
00033
00034 NEErr NEStringCopy(char **str, char const *src, int srcLen)
00035 {
00036 int i;
00037
00038 if (*str)
00039 free(*str);
00040
00041 if (srcLen < 0)
00042 srcLen = strlen(src);
00043
00044 *str = malloc(srcLen + 1);
00045 if (!*str)
00046 return kNEErrMalloc;
00047
00048 for (i = 0; i < srcLen; i++)
00049 (*str)[i] = src[i];
00050 (*str)[i] = '\0';
00051
00052 return kNEErrOk;
00053 }
00054
00055 NEErr NEStringCat(char **str, char const *src, int srcLen)
00056 {
00057 if (!*str)
00058 return NEStringCopy(str, src, srcLen);
00059 else
00060 {
00061 int i;
00062 int strLen = strlen(*str);
00063 if (srcLen < 0)
00064 srcLen = strlen(src);
00065
00066 *str = realloc(*str, strLen + srcLen + 1);
00067 if (!*str)
00068 return kNEErrMalloc;
00069
00070 for (i = 0; i < srcLen; i++)
00071 (*str)[strLen + i] = src[i];
00072 (*str)[strLen + i] = '\0';
00073 return kNEErrOk;
00074 }
00075 }
00076
00077 NEErr NEStringAdd(char **str, char const *src, int srcLen)
00078 {
00079 if (!*str)
00080 return NEStringCopy(str, src, srcLen);
00081 else
00082 {
00083 int i;
00084 int strLen = strlen(*str);
00085 if (srcLen < 0)
00086 srcLen = strlen(src);
00087
00088 *str = realloc(*str, strLen + srcLen + 2);
00089 if (!*str)
00090 return kNEErrMalloc;
00091
00092 (*str)[strLen] = '\n';
00093 for (i = 0; i < srcLen; i++)
00094 (*str)[strLen + 1 + i] = src[i];
00095 (*str)[strLen + 1 + i] = '\0';
00096 return kNEErrOk;
00097 }
00098 }
00099
00100 char const *NEStringNextPart(char const *str)
00101 {
00102 if (!str)
00103 return NULL;
00104 while (str[0] && str[0] != '\n')
00105 str++;
00106 if (str[0])
00107 return str + 1;
00108 else
00109 return NULL;
00110 }
00111
00112 int NEStringPartLength(char const *str)
00113 {
00114 int n;
00115 for (n = 0; str[n] && str[n] != '\n'; n++)
00116 ;
00117 return n;
00118 }
00119
00120 NEBoolean NEStringEq(char const *str, char const *str2, int len2)
00121 {
00122 int i;
00123 if (len2 >= 0)
00124 {
00125 for (i = 0; i < len2; i++)
00126 if (str[i] != str2[i])
00127 return FALSE;
00128 }
00129 else
00130 for (i = 0; str2[i] && str2[i] != '\n'; i++)
00131 if (str[i] != str2[i])
00132 return FALSE;
00133 return str[i] == '\0' || str[i] == '\n';
00134 }
00135
00136 void NEStringFree(char **str)
00137 {
00138 if (*str)
00139 free(*str);
00140 *str = NULL;
00141 }
00142
00143 NEErr NEBegin(NEPtr ne, char const *filename)
00144 {
00145 int zerr;
00146 static char const mimetype[] = "application/epub+zip\n";
00147
00148 if (filename)
00149 {
00150 ne->zf = zipOpen64(filename, APPEND_STATUS_CREATE);
00151 if (!ne->zf)
00152 return kNEErrCannotCreateEPUBFile;
00153 }
00154 else
00155 ne->zf = NULL;
00156
00157
00158 ne->title = NULL;
00159 NEStringCopy(&ne->title, "Untitled", -1);
00160 ne->creator = NULL;
00161 ne->identifier = NULL;
00162 NEStringCopy(&ne->identifier, "no-identifier", -1);
00163 ne->language = NULL;
00164 ne->subject = NULL;
00165 ne->description = NULL;
00166 ne->publisher = NULL;
00167 ne->date = NULL;
00168 ne->source = NULL;
00169 ne->rights = NULL;
00170
00171
00172 ne->parts = NULL;
00173 ne->auxParts = NULL;
00174 ne->cover = NULL;
00175 ne->other = NULL;
00176 ne->coverImage = NULL;
00177
00178
00179 ne->tocEntries = NULL;
00180 ne->ncxCount = 0;
00181 ne->maxTOCDepth = 1;
00182 ne->endnotes = NULL;
00183 ne->endnoteCount = 0;
00184 ne->lastRefLink = NULL;
00185 ne->currentDoc = NULL;
00186
00187
00188 zerr = zipOpenNewFileInZip(ne->zf,
00189 "mimetype", NULL,
00190 NULL, 0, NULL, 0,
00191 NULL,
00192 0, 0);
00193 if (zerr != Z_OK)
00194 return kNEErrZip;
00195 zerr = zipWriteInFileInZip(ne->zf, mimetype, strlen(mimetype));
00196 if (zerr != Z_OK)
00197 return kNEErrZip;
00198 zerr = zipCloseFileInZip(ne->zf);
00199 if (zerr != Z_OK)
00200 return kNEErrZip;
00201
00202 return kNEErrOk;
00203 }
00204
00205 NEErr NEAddMetadata(NEPtr ne,
00206 NEMetadataKey key, char const *data, int dataLen)
00207 {
00208 switch (key)
00209 {
00210 case kNEMetaTitle:
00211 NEStringCopy(&ne->title, data, dataLen);
00212 break;
00213 case kNEMetaCreator:
00214 NEStringAdd(&ne->creator, data, dataLen);
00215 break;
00216 case kNEMetaIdentifier:
00217 NEStringCopy(&ne->identifier, data, dataLen);
00218 break;
00219 case kNEMetaLanguage:
00220 NEStringAdd(&ne->language, data, dataLen);
00221 break;
00222 case kNEMetaSubject:
00223 NEStringAdd(&ne->subject, data, dataLen);
00224 break;
00225 case kNEMetaDescription:
00226 NEStringCopy(&ne->description, data, dataLen);
00227 break;
00228 case kNEMetaPublisher:
00229 NEStringCopy(&ne->publisher, data, dataLen);
00230 break;
00231 case kNEMetaDate:
00232 NEStringCopy(&ne->date, data, dataLen);
00233 break;
00234 case kNEMetaSource:
00235 NEStringCopy(&ne->source, data, dataLen);
00236 break;
00237 case kNEMetaRights:
00238 NEStringCopy(&ne->rights, data, dataLen);
00239 break;
00240 }
00241 return kNEErrOk;
00242 }
00243
00244 NEErr NESetCoverImage(NEPtr ne, char const *filename, int filenameLen)
00245 {
00246 NEErr err;
00247
00248 Chk(NEStringCopy(&ne->coverImage, filename, filenameLen));
00249 Chk(NEAddFile(ne, ne->coverImage, ne->coverImage));
00250 return kNEErrOk;
00251 }
00252
00253 NEErr NEAddPart(NEPtr ne, char const *filename, NEBoolean auxiliary)
00254 {
00255 NEErr err;
00256
00257 if (auxiliary)
00258 Chk(NEStringAdd(&ne->auxParts, filename, -1));
00259 else
00260 Chk(NEStringAdd(&ne->parts, filename, -1));
00261 return kNEErrOk;
00262 }
00263
00264 static char const *SuffixToMimetype(char const *filename, int filenameLen)
00265 {
00266 int i, suffix;
00267 static struct
00268 {
00269 char const *suffix;
00270 char const *mimetype;
00271 } const m[] = {
00272 {"gif", "image/gif"},
00273 {"jpg", "image/jpeg"},
00274 {"png", "image/png"},
00275 {"svg", "image/svg+xml"},
00276 {"xhtml", "text/xhtml+xml"},
00277 {"css", "text/css"},
00278 {"xml", "application/xml"},
00279 {"ncx", "application/x-dtbncx+xml"},
00280 {NULL, NULL}
00281 };
00282
00283 if (filenameLen < 0)
00284 filenameLen = strlen(filename);
00285 for (suffix = filenameLen; suffix > 0 && filename[suffix - 1] != '.'; suffix--)
00286 ;
00287 if (suffix <= 0)
00288 return "text/plain";
00289
00290 for (i = 0; m[i].suffix; i++)
00291 if (!strncasecmp(filename + suffix, m[i].suffix, filenameLen - suffix))
00292 return m[i].mimetype;
00293
00294 return "text/plain";
00295 }
00296
00297 NEErr NEAddOther(NEPtr ne,
00298 char const *filename, int filenameLen,
00299 char const *mimetype)
00300 {
00301 NEStringAdd(&ne->other, filename, filenameLen);
00302 NEStringAdd(&ne->other,
00303 mimetype ? mimetype : SuffixToMimetype(filename, filenameLen),
00304 -1);
00305 return kNEErrOk;
00306 }
00307
00308 void NEEnumOther(NEPtr ne,
00309 char const **filename,
00310 int *filenameLength)
00311 {
00312 if (*filename)
00313 {
00314 *filename = NEStringNextPart(*filename);
00315 *filename = NEStringNextPart(*filename);
00316 }
00317 else
00318 *filename = ne->other;
00319 if (*filename)
00320 *filenameLength = NEStringPartLength(*filename);
00321 }
00322
00323 NEErr NEAddFile(NEPtr ne,
00324 char const *filename,
00325 char const *path)
00326 {
00327 FILE *fp = NULL;
00328 char *buffer = NULL;
00329 long n;
00330 NEErr err;
00331
00332 if (!ne->zf)
00333 return kNEErrOk;
00334
00335 buffer = malloc(kBufferSize);
00336 if (!buffer)
00337 return kNEErrMalloc;
00338 fp = fopen(path, "rb");
00339 if (!fp)
00340 {
00341 free(buffer);
00342 return kNEErrCannotOpenFile;
00343 }
00344
00345 err = NENewFile(ne, filename);
00346 if (err != kNEErrOk)
00347 goto error;
00348
00349 for (;;)
00350 {
00351 n = fread(buffer, 1, kBufferSize, fp);
00352 if (n <= 0)
00353 break;
00354 err = NEWriteToFile(ne, buffer, n);
00355 if (err != kNEErrOk)
00356 {
00357 NECloseFile(ne);
00358 goto error;
00359 }
00360 }
00361
00362 free(buffer);
00363 NECloseFile(ne);
00364 return kNEErrOk;
00365
00366 error:
00367 if (fp)
00368 fclose(fp);
00369 if (buffer)
00370 free(buffer);
00371 return err;
00372 }
00373
00374 NEErr NEAddTOCEntry(NEPtr ne, char const *title, char const *relativeUrl, int level)
00375 {
00376 char str[16];
00377
00378 ne->ncxCount++;
00379 if (level > ne->maxTOCDepth)
00380 ne->maxTOCDepth = level;
00381
00382
00383
00384 NEStringCat(&ne->tocEntries, "<navPoint id=\"", -1);
00385 sprintf(str, "p%d", ne->ncxCount);
00386 NEStringCat(&ne->tocEntries, str, -1);
00387 NEStringCat(&ne->tocEntries, "\" playOrder=\"", -1);
00388 sprintf(str, "%d", ne->ncxCount);
00389 NEStringCat(&ne->tocEntries, str, -1);
00390 NEStringCat(&ne->tocEntries, "\"><navLabel><text>", -1);
00391 NEStringCat(&ne->tocEntries, title, -1);
00392 NEStringCat(&ne->tocEntries, "</text></navLabel><content src=\"", -1);
00393 NEStringCat(&ne->tocEntries, relativeUrl, -1);
00394 NEStringCat(&ne->tocEntries, "\"/></navPoint>\n", -1);
00395
00396 return kNEErrOk;
00397 }
00398
00399 NEErr NENewFile(NEPtr ne,
00400 char const *filename)
00401 {
00402 int zerr;
00403 char filename2[kPathSize];
00404
00405 if (!ne->zf)
00406 return kNEErrOk;
00407
00408 if (filename[0] == '/')
00409 {
00410
00411 strncpy(filename2, filename + 1, kPathSize);
00412 }
00413 else
00414 {
00415
00416 strcpy(filename2, DOCDIR "/");
00417 strncat(filename2, filename, kPathSize);
00418 }
00419
00420 zerr = zipOpenNewFileInZip(ne->zf,
00421 filename2, NULL,
00422 NULL, 0, NULL, 0,
00423 NULL,
00424 Z_DEFLATED, Z_DEFAULT_COMPRESSION);
00425 return zerr == Z_OK ? kNEErrOk : kNEErrZip;
00426 }
00427
00428 NEErr NEWriteToFile(NEPtr ne,
00429 char const *data, int len)
00430 {
00431 if (!ne->zf)
00432 {
00433 fwrite(data, 1, len >= 0 ? len : strlen(data), stdout);
00434 return kNEErrOk;
00435 }
00436
00437 if (len < 0)
00438 len = strlen(data);
00439 return zipWriteInFileInZip(ne->zf, data, len) == Z_OK ? kNEErrOk : kNEErrZip;
00440 }
00441
00442 NEErr NECloseFile(NEPtr ne)
00443 {
00444 if (!ne->zf)
00445 return kNEErrOk;
00446 return zipCloseFileInZip(ne->zf) == Z_OK ? kNEErrOk : kNEErrZip;
00447 }
00448
00449 NEErr NEAddEndnote(NEPtr ne,
00450 char const *endnote, int len,
00451 char const *refDoc, int refDocLen,
00452 char const **refLink)
00453 {
00454 char str[16];
00455 NEBoolean beginsWithP, quoted, squoted;
00456 int i;
00457
00458 sprintf(str, "%d", ++ne->endnoteCount);
00459
00460
00461 NEStringCopy(&ne->lastRefLink, " <a href=\"" ENDNOTESDOC "#en", -1);
00462 NEStringCat(&ne->lastRefLink, str, -1);
00463 NEStringCat(&ne->lastRefLink, "\" id=\"enRef", -1);
00464 NEStringCat(&ne->lastRefLink, str, -1);
00465 NEStringCat(&ne->lastRefLink, "\">[", -1);
00466 NEStringCat(&ne->lastRefLink, str, -1);
00467 NEStringCat(&ne->lastRefLink, "]</a>", -1);
00468 *refLink = ne->lastRefLink;
00469
00470
00471
00472
00473
00474 NEStringCat(&ne->endnotes, "<div class=\"endnote\" id=\"en", -1);
00475 NEStringCat(&ne->endnotes, str, -1);
00476 NEStringCat(&ne->endnotes, "\">\n", -1);
00477 beginsWithP = endnote[0] == '<' && endnote[1] == 'p'
00478 && (endnote[2] < 'a' || endnote[2] > 'z')
00479 && (endnote[2] < 'A' || endnote[2] > 'Z');
00480 if (beginsWithP)
00481 {
00482
00483 for (i = 0, quoted = squoted = FALSE; i < len; i++)
00484 if (!squoted && endnote[i] == '"')
00485 quoted = !quoted;
00486 else if (!quoted && endnote[i] == '\'')
00487 squoted = !squoted;
00488 else if (!quoted && !squoted && endnote[i] == '>')
00489 break;
00490 if (i < len)
00491 i++;
00492
00493 NEStringCat(&ne->endnotes, endnote, i);
00494 }
00495 else
00496 NEStringCat(&ne->endnotes, "<p>", -1);
00497
00498 NEStringCat(&ne->endnotes, "<a href=\"", -1);
00499 NEStringCat(&ne->endnotes, refDoc, refDocLen);
00500 NEStringCat(&ne->endnotes, "#enRef", -1);
00501 NEStringCat(&ne->endnotes, str, -1);
00502 NEStringCat(&ne->endnotes, "\">[", -1);
00503 NEStringCat(&ne->endnotes, str, -1);
00504 NEStringCat(&ne->endnotes, "]</a>", -1);
00505 if (beginsWithP)
00506 {
00507 NEStringCat(&ne->endnotes, " ", -1);
00508 NEStringCat(&ne->endnotes, endnote + i, len - i);
00509 }
00510 else
00511 {
00512 NEStringCat(&ne->endnotes, "</p>\n", -1);
00513 NEStringCat(&ne->endnotes, endnote, len);
00514 }
00515 NEStringCat(&ne->endnotes, "</div>\n", -1);
00516
00517 return kNEErrOk;
00518 }
00519
00520 NEErr NEMakeCover(NEPtr ne)
00521 {
00522 NEErr err;
00523
00524 Chk(NENewFile(ne, COVERDOC));
00525 Chk(NEWriteToFile(ne,
00526 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
00527 "<!DOCTYPE html PUBLIC\n"
00528 " \"-//W3C//DTD XHTML 1.1//EN\"\n"
00529 " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
00530 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
00531 "<head>\n"
00532 "<title>Cover</title>\n"
00533 "<style type=\"text/css\">img {max-width: 100%;}</style>\n"
00534 "</head>\n"
00535 "<body class=\"cover\">\n"
00536 , -1));
00537 if (ne->coverImage)
00538 {
00539 Chk(NEWriteToFile(ne, "<div><img src=\"", -1));
00540 Chk(NEWriteToFile(ne, ne->coverImage, -1));
00541 Chk(NEWriteToFile(ne, "\" alt=\"Cover\"/></div>", -1));
00542 }
00543 else
00544 {
00545 Chk(NEWriteToFile(ne,
00546 "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"\n"
00547 " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
00548 " width=\"100%\" height=\"100%\" viewBox=\"0 0 500 656\">\n"
00549 "<rect x=\"0\" y=\"0\" fill=\"blue\" width=\"500\" height=\"656\"/>\n"
00550 , -1));
00551 if (ne->title)
00552 {
00553 Chk(NEWriteToFile(ne,
00554 "<text font-family=\"Helvetica\" font-size=\"36\" fill=\"white\" x=\"100\" y=\"350\">"
00555 , -1));
00556 Chk(NEWriteToFile(ne, ne->title, -1));
00557 Chk(NEWriteToFile(ne,
00558 "</text>\n"
00559 , -1));
00560 }
00561 if (ne->creator)
00562 {
00563 Chk(NEWriteToFile(ne,
00564 "<text font-family=\"Helvetica\" font-size=\"24\" fill=\"white\" x=\"100\" y=\"250\">"
00565 , -1));
00566 Chk(NEWriteToFile(ne, ne->creator, -1));
00567 Chk(NEWriteToFile(ne,
00568 "</text>\n"
00569 , -1));
00570 }
00571 Chk(NEWriteToFile(ne,
00572 "</svg>\n"
00573 , -1));
00574 }
00575 Chk(NEWriteToFile(ne,
00576 "</body>\n"
00577 "</html>\n"
00578 , -1));
00579 Chk(NECloseFile(ne));
00580 Chk(NEStringCopy(&ne->cover, COVERDOC, -1));
00581 return kNEErrOk;
00582 }
00583
00592 static NEErr WriteMetadata(NEPtr ne,
00593 char const *name, char const *attr, char const *str)
00594 {
00595 char const *sub;
00596
00597 for (sub = str; sub; sub = NEStringNextPart(sub))
00598 {
00599 NEErr err;
00600
00601 Chk(NEWriteToFile(ne, " <dc:", -1));
00602 Chk(NEWriteToFile(ne, name, -1));
00603 if (attr)
00604 {
00605 Chk(NEWriteToFile(ne, " ", -1));
00606 Chk(NEWriteToFile(ne, attr, -1));
00607 }
00608 Chk(NEWriteToFile(ne, ">", -1));
00609 Chk(NEWriteToFile(ne, sub, NEStringPartLength(sub)));
00610 Chk(NEWriteToFile(ne, "</dc:", -1));
00611 Chk(NEWriteToFile(ne, name, -1));
00612 Chk(NEWriteToFile(ne, ">\n", -1));
00613 }
00614 return kNEErrOk;
00615 }
00616
00623 static NEErr WriteMeta(NEPtr ne,
00624 char const *name, char const *content)
00625 {
00626 NEErr err;
00627
00628 Chk(NEWriteToFile(ne, " <meta name=\"", -1));
00629 Chk(NEWriteToFile(ne, name, -1));
00630 Chk(NEWriteToFile(ne, "\" content=\"", -1));
00631 Chk(NEWriteToFile(ne, content, -1));
00632 Chk(NEWriteToFile(ne, "\"/>\n", -1));
00633
00634 return kNEErrOk;
00635 }
00636
00641 static NEErr WriteEndnotes(NEPtr ne)
00642 {
00643 NEErr err;
00644
00645 if (!ne->endnotes)
00646 return kNEErrOk;
00647
00648 Chk(NENewFile(ne, ENDNOTESDOC));
00649 Chk(NEWriteToFile(ne,
00650 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
00651 "<!DOCTYPE html PUBLIC\n"
00652 " \"-//W3C//DTD XHTML 1.1//EN\"\n"
00653 " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
00654 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
00655 "<head>\n"
00656 "<title>End Notes</title>\n"
00657 "<style type=\"text/css\">\n"
00658 "div.endnote {page-break-before: always}\n"
00659 "</style>\n"
00660 "</head>\n"
00661 "<body>\n",
00662 -1));
00663 Chk(NEWriteToFile(ne,
00664 ne->endnotes,
00665 -1));
00666 Chk(NEWriteToFile(ne,
00667 "</body>\n"
00668 "</html>\n",
00669 -1));
00670 Chk(NECloseFile(ne));
00671 Chk(NEAddPart(ne, ENDNOTESDOC, TRUE));
00672
00673 return kNEErrOk;
00674 }
00675
00680 static NEErr WriteOPF(NEPtr ne)
00681 {
00682 int p;
00683 NEErr err;
00684 char const *sub;
00685 int id;
00686 char idStr[16];
00687
00688 Chk(NENewFile(ne, ROOTDOC));
00689 Chk(NEWriteToFile(ne,
00690 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
00691 "<package version=\"2.0\"\n"
00692 " unique-identifier=\"PrimaryID\"\n"
00693 " xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n"
00694 " xmlns=\"http://www.idpf.org/2007/opf\">\n"
00695 " <metadata>\n",
00696 -1));
00697
00698 Chk(NEWriteToFile(ne, " <dc:identifier id=\"PrimaryID\">", -1));
00699 Chk(NEWriteToFile(ne, ne->identifier, -1));
00700 Chk(NEWriteToFile(ne, "</dc:identifier>\n", -1));
00701 Chk(WriteMetadata(ne, "title", NULL, ne->title));
00702 Chk(WriteMetadata(ne, "creator", NULL , ne->creator));
00703 Chk(WriteMetadata(ne, "subject", NULL, ne->subject));
00704 Chk(WriteMetadata(ne, "description", NULL, ne->description));
00705 Chk(WriteMetadata(ne, "publisher", NULL, ne->publisher));
00706 Chk(WriteMetadata(ne, "date", NULL, ne->date));
00707 Chk(WriteMetadata(ne, "source", NULL, ne->source));
00708 Chk(WriteMetadata(ne, "language", NULL, ne->language ? ne->language : "en"));
00709 Chk(WriteMetadata(ne, "rights", NULL, ne->rights));
00710 Chk(WriteMeta(ne, "cover", "CoverID"));
00711
00712 Chk(NEWriteToFile(ne,
00713 " </metadata>\n"
00714 " <manifest>\n",
00715 -1));
00716 Chk(NEWriteToFile(ne,
00717 " <item id=\"ncx\"\n href=\""
00718 NCXDOC
00719 "\"\n media-type=\"application/x-dtbncx+xml\"/>\n",
00720 -1));
00721 for (p = 0, id = 1; p < 3; p++)
00722 for (sub = p == 0 ? ne->cover : p == 1 ? ne->parts : ne->auxParts;
00723 sub;
00724 sub = NEStringNextPart(sub), id++)
00725 {
00726 Chk(NEWriteToFile(ne, " <item id=\"", -1));
00727 sprintf(idStr, "id%d", id);
00728 Chk(NEWriteToFile(ne, idStr, -1));
00729 Chk(NEWriteToFile(ne, "\"\n href=\"", -1));
00730 Chk(NEWriteToFile(ne, sub, NEStringPartLength(sub)));
00731 Chk(NEWriteToFile(ne, "\"\n media-type=\"application/xhtml+xml\"/>\n", -1));
00732 }
00733 for (sub = ne->other; sub; sub = NEStringNextPart(sub), id++)
00734 {
00735 Chk(NEWriteToFile(ne, " <item id=\"", -1));
00736 sprintf(idStr, "id%d", id);
00737 Chk(NEWriteToFile(ne, idStr, -1));
00738 Chk(NEWriteToFile(ne, "\"\n href=\"", -1));
00739 Chk(NEWriteToFile(ne, sub, NEStringPartLength(sub)));
00740 sub = NEStringNextPart(sub);
00741 Chk(NEWriteToFile(ne, "\"\n media-type=\"", -1));
00742 Chk(NEWriteToFile(ne, sub, NEStringPartLength(sub)));
00743 Chk(NEWriteToFile(ne, "\"/>\n", -1));
00744 }
00745 if (ne->coverImage)
00746 {
00747 Chk(NEWriteToFile(ne, " <item id=\"CoverID\"\n href=\"", -1));
00748 Chk(NEWriteToFile(ne, ne->coverImage, -1));
00749 Chk(NEWriteToFile(ne, "\"\n media-type=\"", -1));
00750 Chk(NEWriteToFile(ne, SuffixToMimetype(ne->coverImage, -1), -1));
00751 Chk(NEWriteToFile(ne, "\"/>\n", -1));
00752 }
00753 Chk(NEWriteToFile(ne,
00754 " </manifest>\n"
00755 " <spine toc=\"ncx\">\n",
00756 -1));
00757 for (p = 0, id = 1; p < 3; p++)
00758 for (sub = p == 0 ? ne->cover : p == 1 ? ne->parts : ne->auxParts;
00759 sub;
00760 sub = NEStringNextPart(sub), id++)
00761 {
00762 Chk(NEWriteToFile(ne, " <itemref idref=\"", -1));
00763 sprintf(idStr, "id%d", id);
00764 Chk(NEWriteToFile(ne, idStr, -1));
00765 Chk(NEWriteToFile(ne,
00766 p == 1 ? "\"/>\n" : "\" linear=\"no\"/>\n", -1));
00767 }
00768 Chk(NEWriteToFile(ne,
00769 " </spine>\n",
00770 -1));
00771 if (ne->cover)
00772 {
00773 Chk(NEWriteToFile(ne,
00774 " <guide>\n"
00775 " <reference type=\"cover\" title=\"Cover\" href=\"",
00776 -1));
00777 Chk(NEWriteToFile(ne, ne->cover, -1));
00778 Chk(NEWriteToFile(ne,
00779 "\"/>\n"
00780 " </guide>\n",
00781 -1));
00782 }
00783 Chk(NEWriteToFile(ne,
00784 "</package>\n",
00785 -1));
00786 Chk(NECloseFile(ne));
00787
00788 return kNEErrOk;
00789 }
00790
00795 static NEErr WriteNCX(NEPtr ne)
00796 {
00797 NEErr err;
00798 char str[16];
00799
00800 Chk(NENewFile(ne, NCXDOC));
00801 Chk(NEWriteToFile(ne,
00802 "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
00803 "<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\" "
00804 "\"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n"
00805 "<ncx version=\"2005-1\" xml:lang=\"en-US\"\n"
00806 " xmlns=\"http://www.daisy.org/z3986/2005/ncx/\">\n",
00807 -1));
00808 Chk(NEWriteToFile(ne,
00809 " <head>\n",
00810 -1));
00811 Chk(NEWriteToFile(ne,
00812 " <meta name=\"dtb:uid\" content=\"",
00813 -1));
00814 Chk(NEWriteToFile(ne, ne->identifier, -1));
00815 Chk(NEWriteToFile(ne,
00816 "\"/>\n",
00817 -1));
00818 sprintf(str, "%d", ne->maxTOCDepth);
00819 Chk(NEWriteToFile(ne,
00820 " <meta name=\"dtb:depth\" content=\"",
00821 -1));
00822 Chk(NEWriteToFile(ne, str, -1));
00823 Chk(NEWriteToFile(ne,
00824 "\"/>\n",
00825 -1));
00826 Chk(NEWriteToFile(ne,
00827 " <meta name=\"dtb:totalPageCount\" content=\"0\"/>\n",
00828 -1));
00829 Chk(NEWriteToFile(ne,
00830 " <meta name=\"dtb:maxNumberPage\" content=\"0\"/>\n",
00831 -1));
00832 Chk(NEWriteToFile(ne,
00833 " </head>\n",
00834 -1));
00835 Chk(NEWriteToFile(ne,
00836 " <docTitle><text>",
00837 -1));
00838 Chk(NEWriteToFile(ne, ne->title ? ne->title : "Untitled", -1));
00839 Chk(NEWriteToFile(ne,
00840 "</text></docTitle>\n",
00841 -1));
00842 Chk(NEWriteToFile(ne,
00843 " <navMap>\n",
00844 -1));
00845 if (ne->tocEntries)
00846 Chk(NEWriteToFile(ne, ne->tocEntries, -1));
00847 Chk(NEWriteToFile(ne,
00848 " </navMap>\n",
00849 -1));
00850 Chk(NEWriteToFile(ne,
00851 "</ncx>\n",
00852 -1));
00853 Chk(NECloseFile(ne));
00854
00855 return kNEErrOk;
00856 }
00857
00858 NEErr NEEnd(NEPtr ne)
00859 {
00860 int zerr;
00861 NEErr err;
00862
00863 Chk(WriteEndnotes(ne));
00864 Chk(WriteOPF(ne));
00865 Chk(WriteNCX(ne));
00866
00867
00868 Chk(NENewFile(ne, "/META-INF/container.xml"));
00869 Chk(NEWriteToFile(ne,
00870 "<?xml version=\"1.0\"?>\n"
00871 "<container version=\"1.0\" "
00872 " xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n"
00873 " <rootfiles>\n"
00874 " <rootfile full-path=\"" DOCDIR "/" ROOTDOC "\""
00875 " media-type=\"application/oebps-package+xml\"/>\n"
00876 " </rootfiles>\n"
00877 "</container>\n"
00878 , -1));
00879 Chk(NECloseFile(ne));
00880
00881 zerr = zipClose(ne->zf, NULL);
00882
00883
00884 NEStringFree(&ne->title);
00885 NEStringFree(&ne->creator);
00886 NEStringFree(&ne->identifier);
00887 NEStringFree(&ne->language);
00888 NEStringFree(&ne->subject);
00889 NEStringFree(&ne->description);
00890 NEStringFree(&ne->publisher);
00891 NEStringFree(&ne->date);
00892 NEStringFree(&ne->source);
00893 NEStringFree(&ne->rights);
00894 NEStringFree(&ne->endnotes);
00895 NEStringFree(&ne->lastRefLink);
00896 NEStringFree(&ne->parts);
00897 NEStringFree(&ne->auxParts);
00898 NEStringFree(&ne->cover);
00899 NEStringFree(&ne->coverImage);
00900 NEStringFree(&ne->other);
00901 NEStringFree(&ne->tocEntries);
00902
00903 if (zerr != Z_OK)
00904 return kNEErrZip;
00905 return kNEErrOk;
00906 }