1 /************************************************************************
2 **
3 **  Copyright (C) 2016-2020 Kevin B. Hendricks, Stratford, Ontario, Canada
4 **  Copyright (C) 2009-2011 Strahinja Markovic  <strahinja.markovic@gmail.com>
5 **
6 **  This file is part of Sigil.
7 **
8 **  Sigil is free software: you can redistribute it and/or modify
9 **  it under the terms of the GNU General Public License as published by
10 **  the Free Software Foundation, either version 3 of the License, or
11 **  (at your option) any later version.
12 **
13 **  Sigil is distributed in the hope that it will be useful,
14 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 **  GNU General Public License for more details.
17 **
18 **  You should have received a copy of the GNU General Public License
19 **  along with Sigil.  If not, see <http://www.gnu.org/licenses/>.
20 **
21 *************************************************************************/
22 
23 #include <QtCore/QBuffer>
24 #include <QtCore/QDir>
25 #include <QtCore/QFileInfo>
26 #include <QtCore/QObject>
27 
28 #include "BookManipulation/CleanSource.h"
29 #include "Exporters/NCXWriter.h"
30 #include "ResourceObjects/NCXResource.h"
31 #include "Misc/SettingsStore.h"
32 #include "Misc/Utility.h"
33 #include "sigil_constants.h"
34 
35 static const QString TEMPLATE_TEXT =
36     "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
37     "<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\"\n"
38     "   \"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">\n"
39     "<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\">\n"
40     "  <head>\n"
41     "    <meta name=\"dtb:uid\" content=\"ID_UNKNOWN\" />\n"
42     "    <meta name=\"dtb:depth\" content=\"0\" />\n"
43     "    <meta name=\"dtb:totalPageCount\" content=\"0\" />\n"
44     "    <meta name=\"dtb:maxPageNumber\" content=\"0\" />\n"
45     "  </head>\n"
46     "<docTitle>\n"
47     "  <text>Unknown</text>\n"
48     "</docTitle>\n"
49     "<navMap>\n"
50     "<navPoint id=\"navPoint-1\" playOrder=\"1\">\n"
51     "  <navLabel>\n"
52     "    <text>%1</text>\n"
53     "  </navLabel>\n"
54     "  <content src=\"%2\" />\n"
55     "</navPoint>\n"
56     "</navMap>\n"
57     "</ncx>";
58 
59 
60 // under xhtml5 / epub3 no doctype is allowed for the ncx
61 static const QString TEMPLATE3_TEXT =
62     "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
63     "<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\">\n"
64     "  <head>\n"
65     "    <meta name=\"dtb:uid\" content=\"ID_UNKNOWN\" />\n"
66     "    <meta name=\"dtb:depth\" content=\"0\" />\n"
67     "    <meta name=\"dtb:totalPageCount\" content=\"0\" />\n"
68     "    <meta name=\"dtb:maxPageNumber\" content=\"0\" />\n"
69     "  </head>\n"
70     "<docTitle>\n"
71     "   <text>Unknown</text>\n"
72     "</docTitle>\n"
73     "<navMap>\n"
74     "<navPoint id=\"navPoint-1\" playOrder=\"1\">\n"
75     "  <navLabel>\n"
76     "    <text>%1</text>\n"
77     "  </navLabel>\n"
78     "  <content src=\"%2\" />\n"
79     "</navPoint>\n"
80     "</navMap>\n"
81     "</ncx>";
82 
83 
NCXResource(const QString & mainfolder,const QString & fullfilepath,const QString & version,QObject * parent)84 NCXResource::NCXResource(const QString &mainfolder,
85                          const QString &fullfilepath,
86                          const QString & version,
87                          QObject *parent)
88     : XMLResource(mainfolder, fullfilepath, parent)
89 {
90     FillWithDefaultText(version, "OEBPS/Text");
91 }
92 
93 // a rename of the ncx should only need updating in the opf
94 // which should happen automagically via signals and slots here
RenameTo(const QString & new_filename)95 bool NCXResource::RenameTo(const QString &new_filename)
96 {
97     bool successful = Resource::RenameTo(new_filename);
98     return successful;
99 }
100 
101 
102 // a move of the ncx should need updating in the ncx and opf
103 // which should happen automagically via signals and slots here
MoveTo(const QString & newbookpath)104 bool NCXResource::MoveTo(const QString &newbookpath)
105 {
106     bool successful = Resource::MoveTo(newbookpath);
107     return successful;
108 }
109 
110 
Type() const111 Resource::ResourceType NCXResource::Type() const
112 {
113     return Resource::NCXResourceType;
114 }
115 
116 
SetMainID(const QString & main_id)117 void NCXResource::SetMainID(const QString &main_id)
118 {
119     SetText(GetText().replace("ID_UNKNOWN", main_id));
120 }
121 
122 
GenerateNCXFromBookContents(const Book * book)123 bool NCXResource::GenerateNCXFromBookContents(const Book *book)
124 {
125     bool is_changed = false;
126     QByteArray raw_ncx;
127     QBuffer buffer(&raw_ncx);
128     buffer.open(QIODevice::WriteOnly);
129     NCXWriter ncx(book, buffer);
130     ncx.WriteXMLFromHeadings();
131     buffer.close();
132     QString new_text = CleanSource::ProcessXML(QString::fromUtf8(raw_ncx.constData(), raw_ncx.size()),"application/x-dtbncx+xml");
133     QString existing_text = GetText();
134 
135     // Only update the resource if have changed. Note that this is_changed trick will not
136     // work after first loading an EPUB, because the metadata elements have their attributes
137     // in swapped in a different order from the xhtml processing.
138     if (new_text != existing_text) {
139         SetText(new_text);
140         is_changed = true;
141     }
142 
143     return is_changed;
144 }
145 
146 
GenerateNCXFromTOCContents(const Book * book,TOCModel * toc_model)147 void NCXResource::GenerateNCXFromTOCContents(const Book *book, TOCModel *toc_model)
148 {
149     GenerateNCXFromTOCEntries(book, toc_model->GetRootTOCEntry());
150 }
151 
GenerateNCXFromTOCEntries(const Book * book,TOCModel::TOCEntry toc_root_entry)152 void NCXResource::GenerateNCXFromTOCEntries(const Book *book, TOCModel::TOCEntry toc_root_entry)
153 {
154     QByteArray raw_ncx;
155     QBuffer buffer(&raw_ncx);
156     buffer.open(QIODevice::WriteOnly);
157     NCXWriter ncx(book, buffer, toc_root_entry);
158     ncx.WriteXML();
159     buffer.close();
160     SetText(CleanSource::ProcessXML(QString::fromUtf8(raw_ncx.constData(), raw_ncx.size()), "application/x-dtbncx+xml"));
161 }
162 
163 
FillWithDefaultText(const QString & version,const QString & default_text_folder)164 void NCXResource::FillWithDefaultText(const QString &version, const QString &default_text_folder)
165 {
166     QString first_section_bookpath = FIRST_SECTION_NAME;
167     if (!default_text_folder.isEmpty()) first_section_bookpath = default_text_folder + "/" + FIRST_SECTION_NAME;
168     FillWithDefaultTextToBookPath(version, first_section_bookpath);
169     SaveToDisk();
170 }
171 
172 
FillWithDefaultTextToBookPath(const QString & version,const QString & start_bookpath)173 void NCXResource::FillWithDefaultTextToBookPath(const QString &version, const QString &start_bookpath)
174 {
175     QString epubversion = version;
176     if (epubversion.isEmpty()) {
177         SettingsStore ss;
178         epubversion = ss.defaultVersion();
179     }
180     QString ncxbookpath = GetRelativePath();
181     QString texthref = Utility::URLEncodePath(Utility::buildRelativePath(ncxbookpath, start_bookpath));
182     if (epubversion.startsWith('2')) {
183         SetText(TEMPLATE_TEXT.arg(tr("Start")).arg(texthref));
184     } else {
185         SetText(TEMPLATE3_TEXT.arg(tr("Start")).arg(texthref));
186     }
187     // Make sure the file exists on disk.
188     // Among many reasons, this also solves the problem
189     // with the Book Browser not displaying an icon for this resource.
190     SaveToDisk();
191 }
192 
193