wp-epub-gen 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.cjs CHANGED
@@ -363,20 +363,24 @@ function loadAndProcessHtml(data) {
363
363
  }
364
364
  try {
365
365
  let $ = cheerio__namespace.load(trimmedData, {
366
- xml: {
367
- lowerCaseTags: true,
368
- recognizeSelfClosing: true
369
- }
366
+ xmlMode: true,
367
+ // @ts-ignore
368
+ decodeEntities: false,
369
+ lowerCaseTags: true,
370
+ recognizeSelfClosing: true,
371
+ lowerCaseAttributeNames: true
370
372
  });
371
373
  const body = $("body");
372
374
  if (body.length) {
373
375
  const html = body.html();
374
376
  if (html) {
375
377
  $ = cheerio__namespace.load(html, {
376
- xml: {
377
- lowerCaseTags: true,
378
- recognizeSelfClosing: true
379
- }
378
+ xmlMode: true,
379
+ // @ts-ignore
380
+ decodeEntities: false,
381
+ lowerCaseTags: true,
382
+ recognizeSelfClosing: true,
383
+ lowerCaseAttributeNames: true
380
384
  });
381
385
  }
382
386
  }
@@ -436,7 +440,7 @@ function processImages($, chapter, epubConfigs) {
436
440
  }
437
441
  const trimmedUrl = url2.trim();
438
442
  try {
439
- if (!trimmedUrl.match(/^(https?:\/\/|data:|\.\/|\/)/)) {
443
+ if (!trimmedUrl.match(/^(https?:\/\/|file:\/\/|data:|\.\/|\/)/)) {
440
444
  logger.warn(`Image URL "${trimmedUrl}" appears to be invalid, but processing anyway`);
441
445
  }
442
446
  } catch (error) {
@@ -493,54 +497,20 @@ function processImages($, chapter, epubConfigs) {
493
497
  }
494
498
  });
495
499
  }
496
- function extractAndCleanHtmlContent($, originalData) {
500
+ function extractAndCleanHtmlContent($) {
497
501
  let data;
498
502
  if ($("body").length) {
499
503
  data = $("body").html() || "";
500
504
  } else {
501
505
  data = $.root().html() || "";
502
506
  }
503
- if (!originalData) {
504
- return data.replace(
505
- /<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)><\/\1>/gi,
506
- "<$1$2/>"
507
- ).replace(
508
- new RegExp("<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)(?<!\\/)>", "gi"),
509
- "<$1$2/>"
510
- );
511
- }
512
- const entityMap = /* @__PURE__ */ new Map();
513
- const entityRegex = /&[a-zA-Z][a-zA-Z0-9]*;|&#[0-9]+;|&#x[0-9a-fA-F]+;/g;
514
- const matches = Array.from(originalData.matchAll(entityRegex));
515
- let processedOriginal = originalData;
516
- const timestamp = Date.now();
517
- const randomId = Math.random().toString(36).substring(2, 8);
518
- const placeholderPrefix = `__ENTITY_${timestamp}_${randomId}_`;
519
- for (let i = matches.length - 1; i >= 0; i--) {
520
- const match = matches[i];
521
- const placeholder = `${placeholderPrefix}${i}__`;
522
- entityMap.set(placeholder, match[0]);
523
- processedOriginal = processedOriginal.substring(0, match.index) + placeholder + processedOriginal.substring(match.index + match[0].length);
524
- }
525
- const $temp = cheerio__namespace.load(processedOriginal, {
526
- xmlMode: false
527
- });
528
- let tempData;
529
- if ($temp("body").length) {
530
- tempData = $temp("body").html() || "";
531
- } else {
532
- tempData = $temp.root().html() || "";
533
- }
534
- for (const [placeholder, entity] of entityMap) {
535
- tempData = tempData.replace(new RegExp(placeholder, "g"), entity);
536
- }
537
- return tempData.replace(
507
+ return data.replace(
538
508
  /<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)><\/\1>/gi,
539
509
  "<$1$2/>"
540
510
  ).replace(
541
511
  new RegExp("<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)(?<!\\/)>", "gi"),
542
512
  "<$1$2/>"
543
- );
513
+ ).replace(/<\/img\s*>/gi, "");
544
514
  }
545
515
  function processChildrenChapters(chapter, index2, epubConfigs) {
546
516
  if (Array.isArray(chapter.children)) {
@@ -574,7 +544,7 @@ function parseContent(content, index2, epubConfigs) {
574
544
  }
575
545
  processHtmlElements($, allowedAttributes, allowedXhtml11Tags, epubConfigs, index2);
576
546
  processImages($, chapter, epubConfigs);
577
- chapter.data = extractAndCleanHtmlContent($, content.data);
547
+ chapter.data = extractAndCleanHtmlContent($);
578
548
  }
579
549
  processChildrenChapters(chapter, index2, epubConfigs);
580
550
  return chapter;
@@ -870,52 +840,131 @@ const epub3_toc_xhtml_ejs = `<?xml version="1.0" encoding="UTF-8"?>
870
840
  </body>
871
841
  </html>
872
842
  `;
873
- const template_css = `.epub-author {
874
- color: #555;
843
+ const template_css = `/* =========================
844
+ EPUB 3 Base Stylesheet
845
+ ========================= */
846
+
847
+ /* 全局基础设置 */
848
+ html {
849
+ font-size: 100%;
875
850
  }
876
851
 
877
- .epub-link {
878
- margin-bottom: 30px;
852
+ body {
853
+ margin: 0;
854
+ padding: 0;
855
+ line-height: 1.6;
856
+ font-family: serif;
857
+ color: #000;
858
+ background-color: transparent;
879
859
  }
880
860
 
881
- .epub-link a {
882
- color: #666;
883
- font-size: 90%;
861
+ /* 段落 */
862
+ p {
863
+ margin: 0;
864
+ padding: 0;
865
+ /*text-indent: 2em;*/
866
+ orphans: 2;
867
+ widows: 2;
884
868
  }
885
869
 
886
- .toc-author {
887
- font-size: 90%;
888
- color: #555;
870
+ /* 标题 */
871
+ h1,
872
+ h2,
873
+ h3,
874
+ h4,
875
+ h5,
876
+ h6 {
877
+ font-weight: bold;
878
+ line-height: 1.3;
879
+ margin: 1.2em 0 0.6em 0;
880
+ text-indent: 0;
881
+ page-break-after: avoid;
889
882
  }
890
883
 
891
- .toc-link {
892
- color: #999;
893
- font-size: 85%;
894
- display: block;
884
+ h1 {
885
+ font-size: 1.6em;
895
886
  }
896
887
 
897
- hr {
898
- border: 0;
899
- border-bottom: 1px solid #dedede;
900
- margin: 60px 10%;
888
+ h2 {
889
+ font-size: 1.4em;
901
890
  }
902
891
 
903
- .TOC > ol {
904
- margin: 0;
905
- padding: 0;
892
+ h3 {
893
+ font-size: 1.2em;
894
+ }
895
+
896
+ /* 章节标题常用居中 */
897
+ .chapter-title {
898
+ text-align: center;
899
+ margin-top: 2em;
900
+ margin-bottom: 1.5em;
901
+ }
902
+
903
+ /* 首段不缩进(常用于章节开头) */
904
+ .no-indent {
905
+ text-indent: 0;
906
906
  }
907
907
 
908
- .TOC > ol ol {
908
+ /* 强调 */
909
+ em {
910
+ font-style: italic;
911
+ }
912
+
913
+ strong {
914
+ font-weight: bold;
915
+ }
916
+
917
+ /* 引用 */
918
+ blockquote {
919
+ margin: 1em 1.5em;
909
920
  padding: 0;
910
- margin-left: 2em;
911
921
  }
912
922
 
913
- .TOC li {
914
- font-size: 16px;
915
- list-style: none;
916
- margin: 0 auto;
923
+ /* 分隔符(如 ***) */
924
+ hr {
925
+ border: none;
926
+ text-align: center;
927
+ margin: 1.5em 0;
928
+ }
929
+
930
+ /* 图片 */
931
+ img {
932
+ max-width: 100%;
933
+ height: auto;
934
+ }
935
+
936
+ /* 图片居中 */
937
+ .figure {
938
+ text-align: center;
939
+ margin: 1em 0;
940
+ }
941
+
942
+ /* 列表 */
943
+ ul,
944
+ ol {
945
+ margin: 1em 0 1em 1.5em;
917
946
  padding: 0;
918
947
  }
948
+
949
+ li {
950
+ margin: 0.3em 0;
951
+ }
952
+
953
+ /* 表格(谨慎使用) */
954
+ table {
955
+ border-collapse: collapse;
956
+ margin: 1em 0;
957
+ }
958
+
959
+ td,
960
+ th {
961
+ padding: 0.3em 0.5em;
962
+ }
963
+
964
+ /* 分页控制(EPUB 安全用法) */
965
+ .page-break {
966
+ page-break-before: always;
967
+ }
919
968
  `;
920
969
  const toc_ncx_ejs = `<?xml version="1.0" encoding="UTF-8"?>
921
970
  <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
@@ -965,12 +1014,20 @@ const toc_ncx_ejs = `<?xml version="1.0" encoding="UTF-8"?>
965
1014
  `;
966
1015
  const generateTempFile = async (epubData) => {
967
1016
  const { log } = epubData;
968
- const oebps_dir = path.join(epubData.dir, "OEBPS");
969
- await fs$1.ensureDir(oebps_dir);
970
- epubData.css = epubData.css || template_css;
971
- await writeFile(path.join(oebps_dir, "style.css"), epubData.css, "utf-8");
1017
+ const oebpsDir = path.join(epubData.dir, "OEBPS");
1018
+ await fs$1.ensureDir(oebpsDir);
1019
+ const customCss = epubData.css || "";
1020
+ if (!epubData.noDefaultCss) {
1021
+ epubData.css = template_css;
1022
+ if (customCss) {
1023
+ epubData.css += "\n\n" + customCss;
1024
+ }
1025
+ } else {
1026
+ epubData.css = customCss;
1027
+ }
1028
+ await writeFile(path.join(oebpsDir, "style.css"), epubData.css, "utf-8");
972
1029
  if (epubData.fonts?.length) {
973
- const fonts_dir = path.join(oebps_dir, "fonts");
1030
+ const fonts_dir = path.join(oebpsDir, "fonts");
974
1031
  await fs$1.ensureDir(fonts_dir);
975
1032
  epubData.fonts = epubData.fonts.map((font) => {
976
1033
  const filename = path.basename(font);
@@ -1056,14 +1113,14 @@ const generateTempFile = async (epubData) => {
1056
1113
  htmlTocTemplate = epubData.version === 2 ? epub2_toc_xhtml_ejs : epub3_toc_xhtml_ejs;
1057
1114
  }
1058
1115
  const toc_depth = 1;
1059
- fs$1.writeFileSync(path.join(oebps_dir, "content.opf"), ejs.render(opfTemplate, epubData), "utf-8");
1116
+ fs$1.writeFileSync(path.join(oebpsDir, "content.opf"), ejs.render(opfTemplate, epubData), "utf-8");
1060
1117
  fs$1.writeFileSync(
1061
- path.join(oebps_dir, "toc.ncx"),
1118
+ path.join(oebpsDir, "toc.ncx"),
1062
1119
  ejs.render(ncxTocTemplate, { ...epubData, toc_depth }),
1063
1120
  "utf-8"
1064
1121
  );
1065
1122
  fs$1.writeFileSync(
1066
- path.join(oebps_dir, "toc.xhtml"),
1123
+ path.join(oebpsDir, "toc.xhtml"),
1067
1124
  simpleMinifier(ejs.render(htmlTocTemplate, epubData)),
1068
1125
  "utf-8"
1069
1126
  );
@@ -1220,12 +1277,12 @@ function parseOptions(options) {
1220
1277
  if (data.version === 2) {
1221
1278
  data.docHeader = `<?xml version="1.0" encoding="UTF-8"?>
1222
1279
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1223
- <html xmlns="http://www.w3.org/1999/xhtml" lang="#{self.options.lang}">
1280
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="${data.lang}" lang="${data.lang}">
1224
1281
  `;
1225
1282
  } else {
1226
1283
  data.docHeader = `<?xml version="1.0" encoding="UTF-8"?>
1227
1284
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1228
- <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="#{self.options.lang}">
1285
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="${data.lang}" lang="${data.lang}">
1229
1286
  `;
1230
1287
  }
1231
1288
  if (Array.isArray(data.author) && data.author.length === 0) {
@@ -1245,6 +1302,7 @@ async function epubGen(options, configs) {
1245
1302
  if (configs?.logger) {
1246
1303
  logger.setLogger(configs.logger);
1247
1304
  }
1305
+ logger.info("EpubGen started 101...");
1248
1306
  options = { ...options };
1249
1307
  const o = check(options);
1250
1308
  const verbose = options.verbose !== false;
package/build/index.js CHANGED
@@ -342,20 +342,24 @@ function loadAndProcessHtml(data) {
342
342
  }
343
343
  try {
344
344
  let $ = cheerio.load(trimmedData, {
345
- xml: {
346
- lowerCaseTags: true,
347
- recognizeSelfClosing: true
348
- }
345
+ xmlMode: true,
346
+ // @ts-ignore
347
+ decodeEntities: false,
348
+ lowerCaseTags: true,
349
+ recognizeSelfClosing: true,
350
+ lowerCaseAttributeNames: true
349
351
  });
350
352
  const body = $("body");
351
353
  if (body.length) {
352
354
  const html = body.html();
353
355
  if (html) {
354
356
  $ = cheerio.load(html, {
355
- xml: {
356
- lowerCaseTags: true,
357
- recognizeSelfClosing: true
358
- }
357
+ xmlMode: true,
358
+ // @ts-ignore
359
+ decodeEntities: false,
360
+ lowerCaseTags: true,
361
+ recognizeSelfClosing: true,
362
+ lowerCaseAttributeNames: true
359
363
  });
360
364
  }
361
365
  }
@@ -415,7 +419,7 @@ function processImages($, chapter, epubConfigs) {
415
419
  }
416
420
  const trimmedUrl = url.trim();
417
421
  try {
418
- if (!trimmedUrl.match(/^(https?:\/\/|data:|\.\/|\/)/)) {
422
+ if (!trimmedUrl.match(/^(https?:\/\/|file:\/\/|data:|\.\/|\/)/)) {
419
423
  logger.warn(`Image URL "${trimmedUrl}" appears to be invalid, but processing anyway`);
420
424
  }
421
425
  } catch (error) {
@@ -472,54 +476,20 @@ function processImages($, chapter, epubConfigs) {
472
476
  }
473
477
  });
474
478
  }
475
- function extractAndCleanHtmlContent($, originalData) {
479
+ function extractAndCleanHtmlContent($) {
476
480
  let data;
477
481
  if ($("body").length) {
478
482
  data = $("body").html() || "";
479
483
  } else {
480
484
  data = $.root().html() || "";
481
485
  }
482
- if (!originalData) {
483
- return data.replace(
484
- /<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)><\/\1>/gi,
485
- "<$1$2/>"
486
- ).replace(
487
- new RegExp("<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)(?<!\\/)>", "gi"),
488
- "<$1$2/>"
489
- );
490
- }
491
- const entityMap = /* @__PURE__ */ new Map();
492
- const entityRegex = /&[a-zA-Z][a-zA-Z0-9]*;|&#[0-9]+;|&#x[0-9a-fA-F]+;/g;
493
- const matches = Array.from(originalData.matchAll(entityRegex));
494
- let processedOriginal = originalData;
495
- const timestamp = Date.now();
496
- const randomId = Math.random().toString(36).substring(2, 8);
497
- const placeholderPrefix = `__ENTITY_${timestamp}_${randomId}_`;
498
- for (let i = matches.length - 1; i >= 0; i--) {
499
- const match = matches[i];
500
- const placeholder = `${placeholderPrefix}${i}__`;
501
- entityMap.set(placeholder, match[0]);
502
- processedOriginal = processedOriginal.substring(0, match.index) + placeholder + processedOriginal.substring(match.index + match[0].length);
503
- }
504
- const $temp = cheerio.load(processedOriginal, {
505
- xmlMode: false
506
- });
507
- let tempData;
508
- if ($temp("body").length) {
509
- tempData = $temp("body").html() || "";
510
- } else {
511
- tempData = $temp.root().html() || "";
512
- }
513
- for (const [placeholder, entity] of entityMap) {
514
- tempData = tempData.replace(new RegExp(placeholder, "g"), entity);
515
- }
516
- return tempData.replace(
486
+ return data.replace(
517
487
  /<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)><\/\1>/gi,
518
488
  "<$1$2/>"
519
489
  ).replace(
520
490
  new RegExp("<(br|hr|img|input|meta|area|base|col|embed|link|source|track|wbr)([^>]*?)(?<!\\/)>", "gi"),
521
491
  "<$1$2/>"
522
- );
492
+ ).replace(/<\/img\s*>/gi, "");
523
493
  }
524
494
  function processChildrenChapters(chapter, index2, epubConfigs) {
525
495
  if (Array.isArray(chapter.children)) {
@@ -553,7 +523,7 @@ function parseContent(content, index2, epubConfigs) {
553
523
  }
554
524
  processHtmlElements($, allowedAttributes, allowedXhtml11Tags, epubConfigs, index2);
555
525
  processImages($, chapter, epubConfigs);
556
- chapter.data = extractAndCleanHtmlContent($, content.data);
526
+ chapter.data = extractAndCleanHtmlContent($);
557
527
  }
558
528
  processChildrenChapters(chapter, index2, epubConfigs);
559
529
  return chapter;
@@ -849,52 +819,131 @@ const epub3_toc_xhtml_ejs = `<?xml version="1.0" encoding="UTF-8"?>
849
819
  </body>
850
820
  </html>
851
821
  `;
852
- const template_css = `.epub-author {
853
- color: #555;
822
+ const template_css = `/* =========================
823
+ EPUB 3 Base Stylesheet
824
+ ========================= */
825
+
826
+ /* 全局基础设置 */
827
+ html {
828
+ font-size: 100%;
854
829
  }
855
830
 
856
- .epub-link {
857
- margin-bottom: 30px;
831
+ body {
832
+ margin: 0;
833
+ padding: 0;
834
+ line-height: 1.6;
835
+ font-family: serif;
836
+ color: #000;
837
+ background-color: transparent;
858
838
  }
859
839
 
860
- .epub-link a {
861
- color: #666;
862
- font-size: 90%;
840
+ /* 段落 */
841
+ p {
842
+ margin: 0;
843
+ padding: 0;
844
+ /*text-indent: 2em;*/
845
+ orphans: 2;
846
+ widows: 2;
863
847
  }
864
848
 
865
- .toc-author {
866
- font-size: 90%;
867
- color: #555;
849
+ /* 标题 */
850
+ h1,
851
+ h2,
852
+ h3,
853
+ h4,
854
+ h5,
855
+ h6 {
856
+ font-weight: bold;
857
+ line-height: 1.3;
858
+ margin: 1.2em 0 0.6em 0;
859
+ text-indent: 0;
860
+ page-break-after: avoid;
868
861
  }
869
862
 
870
- .toc-link {
871
- color: #999;
872
- font-size: 85%;
873
- display: block;
863
+ h1 {
864
+ font-size: 1.6em;
874
865
  }
875
866
 
876
- hr {
877
- border: 0;
878
- border-bottom: 1px solid #dedede;
879
- margin: 60px 10%;
867
+ h2 {
868
+ font-size: 1.4em;
880
869
  }
881
870
 
882
- .TOC > ol {
883
- margin: 0;
884
- padding: 0;
871
+ h3 {
872
+ font-size: 1.2em;
873
+ }
874
+
875
+ /* 章节标题常用居中 */
876
+ .chapter-title {
877
+ text-align: center;
878
+ margin-top: 2em;
879
+ margin-bottom: 1.5em;
880
+ }
881
+
882
+ /* 首段不缩进(常用于章节开头) */
883
+ .no-indent {
884
+ text-indent: 0;
885
885
  }
886
886
 
887
- .TOC > ol ol {
887
+ /* 强调 */
888
+ em {
889
+ font-style: italic;
890
+ }
891
+
892
+ strong {
893
+ font-weight: bold;
894
+ }
895
+
896
+ /* 引用 */
897
+ blockquote {
898
+ margin: 1em 1.5em;
888
899
  padding: 0;
889
- margin-left: 2em;
890
900
  }
891
901
 
892
- .TOC li {
893
- font-size: 16px;
894
- list-style: none;
895
- margin: 0 auto;
902
+ /* 分隔符(如 ***) */
903
+ hr {
904
+ border: none;
905
+ text-align: center;
906
+ margin: 1.5em 0;
907
+ }
908
+
909
+ /* 图片 */
910
+ img {
911
+ max-width: 100%;
912
+ height: auto;
913
+ }
914
+
915
+ /* 图片居中 */
916
+ .figure {
917
+ text-align: center;
918
+ margin: 1em 0;
919
+ }
920
+
921
+ /* 列表 */
922
+ ul,
923
+ ol {
924
+ margin: 1em 0 1em 1.5em;
896
925
  padding: 0;
897
926
  }
927
+
928
+ li {
929
+ margin: 0.3em 0;
930
+ }
931
+
932
+ /* 表格(谨慎使用) */
933
+ table {
934
+ border-collapse: collapse;
935
+ margin: 1em 0;
936
+ }
937
+
938
+ td,
939
+ th {
940
+ padding: 0.3em 0.5em;
941
+ }
942
+
943
+ /* 分页控制(EPUB 安全用法) */
944
+ .page-break {
945
+ page-break-before: always;
946
+ }
898
947
  `;
899
948
  const toc_ncx_ejs = `<?xml version="1.0" encoding="UTF-8"?>
900
949
  <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
@@ -944,12 +993,20 @@ const toc_ncx_ejs = `<?xml version="1.0" encoding="UTF-8"?>
944
993
  `;
945
994
  const generateTempFile = async (epubData) => {
946
995
  const { log } = epubData;
947
- const oebps_dir = path.join(epubData.dir, "OEBPS");
948
- await fs$1.ensureDir(oebps_dir);
949
- epubData.css = epubData.css || template_css;
950
- await writeFile(path.join(oebps_dir, "style.css"), epubData.css, "utf-8");
996
+ const oebpsDir = path.join(epubData.dir, "OEBPS");
997
+ await fs$1.ensureDir(oebpsDir);
998
+ const customCss = epubData.css || "";
999
+ if (!epubData.noDefaultCss) {
1000
+ epubData.css = template_css;
1001
+ if (customCss) {
1002
+ epubData.css += "\n\n" + customCss;
1003
+ }
1004
+ } else {
1005
+ epubData.css = customCss;
1006
+ }
1007
+ await writeFile(path.join(oebpsDir, "style.css"), epubData.css, "utf-8");
951
1008
  if (epubData.fonts?.length) {
952
- const fonts_dir = path.join(oebps_dir, "fonts");
1009
+ const fonts_dir = path.join(oebpsDir, "fonts");
953
1010
  await fs$1.ensureDir(fonts_dir);
954
1011
  epubData.fonts = epubData.fonts.map((font) => {
955
1012
  const filename = path.basename(font);
@@ -1035,14 +1092,14 @@ const generateTempFile = async (epubData) => {
1035
1092
  htmlTocTemplate = epubData.version === 2 ? epub2_toc_xhtml_ejs : epub3_toc_xhtml_ejs;
1036
1093
  }
1037
1094
  const toc_depth = 1;
1038
- fs$1.writeFileSync(path.join(oebps_dir, "content.opf"), ejs.render(opfTemplate, epubData), "utf-8");
1095
+ fs$1.writeFileSync(path.join(oebpsDir, "content.opf"), ejs.render(opfTemplate, epubData), "utf-8");
1039
1096
  fs$1.writeFileSync(
1040
- path.join(oebps_dir, "toc.ncx"),
1097
+ path.join(oebpsDir, "toc.ncx"),
1041
1098
  ejs.render(ncxTocTemplate, { ...epubData, toc_depth }),
1042
1099
  "utf-8"
1043
1100
  );
1044
1101
  fs$1.writeFileSync(
1045
- path.join(oebps_dir, "toc.xhtml"),
1102
+ path.join(oebpsDir, "toc.xhtml"),
1046
1103
  simpleMinifier(ejs.render(htmlTocTemplate, epubData)),
1047
1104
  "utf-8"
1048
1105
  );
@@ -1199,12 +1256,12 @@ function parseOptions(options) {
1199
1256
  if (data.version === 2) {
1200
1257
  data.docHeader = `<?xml version="1.0" encoding="UTF-8"?>
1201
1258
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1202
- <html xmlns="http://www.w3.org/1999/xhtml" lang="#{self.options.lang}">
1259
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="${data.lang}" lang="${data.lang}">
1203
1260
  `;
1204
1261
  } else {
1205
1262
  data.docHeader = `<?xml version="1.0" encoding="UTF-8"?>
1206
1263
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
1207
- <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="#{self.options.lang}">
1264
+ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xml:lang="${data.lang}" lang="${data.lang}">
1208
1265
  `;
1209
1266
  }
1210
1267
  if (Array.isArray(data.author) && data.author.length === 0) {
@@ -1224,6 +1281,7 @@ async function epubGen(options, configs) {
1224
1281
  if (configs?.logger) {
1225
1282
  logger.setLogger(configs.logger);
1226
1283
  }
1284
+ logger.info("EpubGen started 101...");
1227
1285
  options = { ...options };
1228
1286
  const o = check(options);
1229
1287
  const verbose = options.verbose !== false;
@@ -7,5 +7,5 @@ export declare const epub2_content_opf_ejs = "<?xml version=\"1.0\" encoding=\"U
7
7
  export declare const epub2_toc_xhtml_ejs = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n<html xml:lang=\"<%- lang %>\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n <title><%= title %></title>\n <meta charset=\"UTF-8\"/>\n <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"/>\n</head>\n<body>\n<h1 class=\"h1\"><%= tocTitle %></h1>\n<% content.forEach(function(content, index){ %>\n <% if(!content.excludeFromToc){ %>\n <p class=\"table-of-content\">\n <a href=\"<%= content.href %>\"><%= (1 + index) + \". \" + (content.title || \"Chapter \" + (1 + index)) %>\n <% if(content.author.length){ %>\n - <small class=\"toc-author\"><%= content.author.join(\",\") %></small>\n <% } %>\n <% if(content.url){ %><span class=\"toc-link\"><%= content.url %></span>\n <% }else{ %><span class=\"toc-link\"></span>\n <% } %>\n </a>\n </p>\n <% } %>\n<% }) %>\n</body>\n</html>\n";
8
8
  export declare const epub3_content_opf_ejs = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<package xmlns=\"http://www.idpf.org/2007/opf\"\n version=\"3.0\"\n unique-identifier=\"BookId\"\n xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n xmlns:dcterms=\"http://purl.org/dc/terms/\"\n xml:lang=\"en\"\n xmlns:media=\"http://www.idpf.org/epub/vocab/overlays/#\"\n prefix=\"ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/\">\n\n <metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:opf=\"http://www.idpf.org/2007/opf\">\n\n <dc:identifier id=\"BookId\"><%= id %></dc:identifier>\n <meta refines=\"#BookId\" property=\"identifier-type\" scheme=\"onix:codelist5\">22</meta>\n <meta property=\"dcterms:identifier\" id=\"meta-identifier\">BookId</meta>\n <dc:title><%= title %></dc:title>\n <meta property=\"dcterms:title\" id=\"meta-title\"><%= title %></meta>\n <dc:language><%= lang || \"en\" %></dc:language>\n <meta property=\"dcterms:language\" id=\"meta-language\"><%= lang || \"en\" %></meta>\n <meta property=\"dcterms:modified\"><%= (new Date()).toISOString().split(\".\")[0] + \"Z\" %></meta>\n <dc:creator id=\"creator\"><%= author.length ? author.join(\",\") : author %></dc:creator>\n <meta refines=\"#creator\" property=\"file-as\"><%= author.length ? author.join(\",\") : author %></meta>\n <meta property=\"dcterms:publisher\"><%= publisher || \"anonymous\" %></meta>\n <dc:publisher><%= publisher || \"anonymous\" %></dc:publisher>\n <% var date = new Date(); var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); var stringDate = \"\" + year + \"-\" + month + \"-\" + day; %>\n <meta property=\"dcterms:date\"><%= stringDate %></meta>\n <dc:date><%= stringDate %></dc:date>\n <meta property=\"dcterms:rights\">All rights reserved</meta>\n <dc:rights>Copyright &#x00A9; <%= (new Date()).getFullYear() %> by <%= publisher || \"anonymous\" %></dc:rights>\n <% if(locals.cover) { %>\n <meta name=\"cover\" content=\"image_cover\" />\n <% } %>\n <meta name=\"generator\" content=\"epub-gen\" />\n <meta property=\"ibooks:specified-fonts\">true</meta>\n </metadata>\n\n <manifest>\n <item id=\"ncx\" href=\"toc.ncx\" media-type=\"application/x-dtbncx+xml\" />\n <item id=\"toc\" href=\"toc.xhtml\" media-type=\"application/xhtml+xml\" properties=\"nav\" />\n <item id=\"css\" href=\"style.css\" media-type=\"text/css\" />\n\n <% if(locals.cover) { %>\n <item id=\"image_cover\" href=\"cover.<%= _coverExtension %>\" media-type=\"<%= _coverMediaType %>\" />\n <% } %>\n\n <% images.forEach(function(image, index){ %>\n <item id=\"image_<%= index %>\" href=\"images/<%= image.id %>.<%= image.extension %>\" media-type=\"<%= image.mediaType %>\" />\n <% }) %>\n\n <% function renderContentItem(content) { %>\n <% content.forEach(function(content){ %>\n <item id=\"content_<%= content.id %>\" href=\"<%= content.href %>\" media-type=\"application/xhtml+xml\" />\n <% if (Array.isArray(content.children)) { %>\n <% renderContentItem(content.children) %>\n <% } %>\n <% }) %>\n <% } %>\n <% renderContentItem(content) %>\n\n <% fonts.forEach(function(font, index){ %>\n <item id=\"font_<%= index %>\" href=\"fonts/<%= font %>\" media-type=\"application/x-font-ttf\" />\n <% }) %>\n </manifest>\n\n <spine toc=\"ncx\">\n <% var nodes_1 = content.filter(item => !item.excludeFromToc && item.beforeToc) %>\n <% var nodes_2 = content.filter(item => !item.excludeFromToc && !item.beforeToc) %>\n <% function renderToc(nodes) { %>\n <% nodes.forEach(function(content){ %>\n <itemref idref=\"content_<%= content.id %>\" />\n <% if (Array.isArray(content.children)) { %>\n <% renderToc(content.children) %>\n <% } %>\n <% }) %>\n <% } %>\n <% renderToc(nodes_1) %>\n <itemref idref=\"toc\" />\n <% renderToc(nodes_2) %>\n </spine>\n <guide>\n <reference type=\"text\" title=\"Table of Content\" href=\"toc.xhtml\" />\n </guide>\n</package>\n";
9
9
  export declare const epub3_toc_xhtml_ejs = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\" xml:lang=\"<%- lang %>\"\n lang=\"<%- lang %>\">\n<head>\n <title><%= title %></title>\n <meta charset=\"UTF-8\" />\n <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />\n</head>\n<body>\n<h1 class=\"h1\"><%= tocTitle %></h1>\n<nav id=\"toc\" class=\"TOC\" epub:type=\"toc\">\n <% var nodes_1 = content.filter(item => !item.excludeFromToc && item.beforeToc) %>\n <% var nodes_2 = content.filter(item => !item.excludeFromToc && !item.beforeToc) %>\n <% function renderToc(nodes, indent = 0) { %>\n <ol>\n <% nodes.forEach(function(content, index){ %>\n <li class=\"table-of-content\">\n <a href=\"<%= content.href %>\"><%= (content.title || \"Chapter \" + (1 + index)) %>\n <% if(content.author.length){ %> - <small\n class=\"toc-author\"><%= content.author.join(\",\") %></small>\n <% } %>\n <% if(content.url){ %><span class=\"toc-link\"><%= content.url %></span>\n <% } %>\n </a>\n <% if (Array.isArray(content.children) && content.children.length > 0) { %>\n <% renderToc(content.children, indent + 1) %>\n <% } %>\n </li>\n <% }) %>\n </ol>\n <% } %>\n <% renderToc(nodes_1) %>\n <% renderToc(nodes_2) %>\n</nav>\n\n</body>\n</html>\n";
10
- export declare const template_css = ".epub-author {\n color: #555;\n}\n\n.epub-link {\n margin-bottom: 30px;\n}\n\n.epub-link a {\n color: #666;\n font-size: 90%;\n}\n\n.toc-author {\n font-size: 90%;\n color: #555;\n}\n\n.toc-link {\n color: #999;\n font-size: 85%;\n display: block;\n}\n\nhr {\n border: 0;\n border-bottom: 1px solid #dedede;\n margin: 60px 10%;\n}\n\n.TOC > ol {\n margin: 0;\n padding: 0;\n}\n\n.TOC > ol ol {\n padding: 0;\n margin-left: 2em;\n}\n\n.TOC li {\n font-size: 16px;\n list-style: none;\n margin: 0 auto;\n padding: 0;\n}\n";
10
+ export declare const template_css = "/* =========================\n EPUB 3 Base Stylesheet\n ========================= */\n\n/* \u5168\u5C40\u57FA\u7840\u8BBE\u7F6E */\nhtml {\n font-size: 100%;\n}\n\nbody {\n margin: 0;\n padding: 0;\n line-height: 1.6;\n font-family: serif;\n color: #000;\n background-color: transparent;\n}\n\n/* \u6BB5\u843D */\np {\n margin: 0;\n padding: 0;\n /*text-indent: 2em;*/\n orphans: 2;\n widows: 2;\n}\n\n/* \u6807\u9898 */\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-weight: bold;\n line-height: 1.3;\n margin: 1.2em 0 0.6em 0;\n text-indent: 0;\n page-break-after: avoid;\n}\n\nh1 {\n font-size: 1.6em;\n}\n\nh2 {\n font-size: 1.4em;\n}\n\nh3 {\n font-size: 1.2em;\n}\n\n/* \u7AE0\u8282\u6807\u9898\u5E38\u7528\u5C45\u4E2D */\n.chapter-title {\n text-align: center;\n margin-top: 2em;\n margin-bottom: 1.5em;\n}\n\n/* \u9996\u6BB5\u4E0D\u7F29\u8FDB\uFF08\u5E38\u7528\u4E8E\u7AE0\u8282\u5F00\u5934\uFF09 */\n.no-indent {\n text-indent: 0;\n}\n\n/* \u5F3A\u8C03 */\nem {\n font-style: italic;\n}\n\nstrong {\n font-weight: bold;\n}\n\n/* \u5F15\u7528 */\nblockquote {\n margin: 1em 1.5em;\n padding: 0;\n}\n\n/* \u5206\u9694\u7B26\uFF08\u5982 ***\uFF09 */\nhr {\n border: none;\n text-align: center;\n margin: 1.5em 0;\n}\n\n/* \u56FE\u7247 */\nimg {\n max-width: 100%;\n height: auto;\n}\n\n/* \u56FE\u7247\u5C45\u4E2D */\n.figure {\n text-align: center;\n margin: 1em 0;\n}\n\n/* \u5217\u8868 */\nul,\nol {\n margin: 1em 0 1em 1.5em;\n padding: 0;\n}\n\nli {\n margin: 0.3em 0;\n}\n\n/* \u8868\u683C\uFF08\u8C28\u614E\u4F7F\u7528\uFF09 */\ntable {\n border-collapse: collapse;\n margin: 1em 0;\n}\n\ntd,\nth {\n padding: 0.3em 0.5em;\n}\n\n/* \u5206\u9875\u63A7\u5236\uFF08EPUB \u5B89\u5168\u7528\u6CD5\uFF09 */\n.page-break {\n page-break-before: always;\n}\n";
11
11
  export declare const toc_ncx_ejs = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\">\n <head>\n <meta name=\"dtb:uid\" content=\"<%= id %>\"/>\n <meta name=\"dtb:generator\" content=\"epub-gen\"/>\n <meta name=\"dtb:depth\" content=\"<%= (toc_depth || 1)%>\"/>\n <meta name=\"dtb:totalPageCount\" content=\"0\"/>\n <meta name=\"dtb:maxPageNumber\" content=\"0\"/>\n </head>\n <docTitle>\n <text><%= title %></text>\n </docTitle>\n <docAuthor>\n <text><%= author %></text>\n </docAuthor>\n <navMap>\n <% var _index = 1; %>\n <% var nodes_1 = content.filter(c => !c.excludeFromToc && c.beforeToc) %>\n <% var nodes_2 = content.filter(c => !c.excludeFromToc && !c.beforeToc) %>\n <% function renderToc(nodes) { %>\n <% nodes.forEach(function(content, index){ %>\n <navPoint id=\"content_<%= content.id %>\" playOrder=\"<%= _index++ %>\" class=\"chapter\">\n <navLabel>\n <text><%= (tocAutoNumber ? ((1 + index) + \". \") : \"\") + (content.title || \"Chapter \" + (1 + index)) %></text>\n </navLabel>\n <content src=\"<%= content.href %>\"/>\n <% if (Array.isArray(content.children)) { %>\n <% renderToc(content.children) %>\n <% } %>\n </navPoint>\n <% }) %>\n <% } %>\n\n <% renderToc(nodes_1) %>\n\n <navPoint id=\"toc\" playOrder=\"<%= _index++ %>\" class=\"chapter\">\n <navLabel>\n <text><%= tocTitle %></text>\n </navLabel>\n <content src=\"toc.xhtml\"/>\n </navPoint>\n\n <% renderToc(nodes_2) %>\n </navMap>\n</ncx>\n";
package/build/types.d.ts CHANGED
@@ -17,6 +17,7 @@ export interface IEpubGenOptions {
17
17
  output: string;
18
18
  version?: 2 | 3;
19
19
  css?: string;
20
+ noDefaultCss?: boolean;
20
21
  fonts?: string[];
21
22
  lang?: 'en';
22
23
  tocTitle?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wp-epub-gen",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Epub generator.",
5
5
  "type": "module",
6
6
  "main": "build/index.cjs",
@@ -1,46 +1,125 @@
1
- .epub-author {
2
- color: #555;
1
+ /* =========================
2
+ EPUB 3 Base Stylesheet
3
+ ========================= */
4
+
5
+ /* 全局基础设置 */
6
+ html {
7
+ font-size: 100%;
3
8
  }
4
9
 
5
- .epub-link {
6
- margin-bottom: 30px;
10
+ body {
11
+ margin: 0;
12
+ padding: 0;
13
+ line-height: 1.6;
14
+ font-family: serif;
15
+ color: #000;
16
+ background-color: transparent;
7
17
  }
8
18
 
9
- .epub-link a {
10
- color: #666;
11
- font-size: 90%;
19
+ /* 段落 */
20
+ p {
21
+ margin: 0;
22
+ padding: 0;
23
+ /*text-indent: 2em;*/
24
+ orphans: 2;
25
+ widows: 2;
12
26
  }
13
27
 
14
- .toc-author {
15
- font-size: 90%;
16
- color: #555;
28
+ /* 标题 */
29
+ h1,
30
+ h2,
31
+ h3,
32
+ h4,
33
+ h5,
34
+ h6 {
35
+ font-weight: bold;
36
+ line-height: 1.3;
37
+ margin: 1.2em 0 0.6em 0;
38
+ text-indent: 0;
39
+ page-break-after: avoid;
17
40
  }
18
41
 
19
- .toc-link {
20
- color: #999;
21
- font-size: 85%;
22
- display: block;
42
+ h1 {
43
+ font-size: 1.6em;
23
44
  }
24
45
 
25
- hr {
26
- border: 0;
27
- border-bottom: 1px solid #dedede;
28
- margin: 60px 10%;
46
+ h2 {
47
+ font-size: 1.4em;
29
48
  }
30
49
 
31
- .TOC > ol {
32
- margin: 0;
33
- padding: 0;
50
+ h3 {
51
+ font-size: 1.2em;
52
+ }
53
+
54
+ /* 章节标题常用居中 */
55
+ .chapter-title {
56
+ text-align: center;
57
+ margin-top: 2em;
58
+ margin-bottom: 1.5em;
59
+ }
60
+
61
+ /* 首段不缩进(常用于章节开头) */
62
+ .no-indent {
63
+ text-indent: 0;
64
+ }
65
+
66
+ /* 强调 */
67
+ em {
68
+ font-style: italic;
69
+ }
70
+
71
+ strong {
72
+ font-weight: bold;
34
73
  }
35
74
 
36
- .TOC > ol ol {
75
+ /* 引用 */
76
+ blockquote {
77
+ margin: 1em 1.5em;
37
78
  padding: 0;
38
- margin-left: 2em;
39
79
  }
40
80
 
41
- .TOC li {
42
- font-size: 16px;
43
- list-style: none;
44
- margin: 0 auto;
81
+ /* 分隔符(如 ***) */
82
+ hr {
83
+ border: none;
84
+ text-align: center;
85
+ margin: 1.5em 0;
86
+ }
87
+
88
+ /* 图片 */
89
+ img {
90
+ max-width: 100%;
91
+ height: auto;
92
+ }
93
+
94
+ /* 图片居中 */
95
+ .figure {
96
+ text-align: center;
97
+ margin: 1em 0;
98
+ }
99
+
100
+ /* 列表 */
101
+ ul,
102
+ ol {
103
+ margin: 1em 0 1em 1.5em;
45
104
  padding: 0;
46
105
  }
106
+
107
+ li {
108
+ margin: 0.3em 0;
109
+ }
110
+
111
+ /* 表格(谨慎使用) */
112
+ table {
113
+ border-collapse: collapse;
114
+ margin: 1em 0;
115
+ }
116
+
117
+ td,
118
+ th {
119
+ padding: 0.3em 0.5em;
120
+ }
121
+
122
+ /* 分页控制(EPUB 安全用法) */
123
+ .page-break {
124
+ page-break-before: always;
125
+ }