diff --git a/README.md b/README.md index dd5c934..4c0d0bf 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,15 @@ If EPUBCheck is installed locally, it will be run alongside the Go tests. To ins 1. Download and extract EPUBCheck in the root directory of this project, e.g. ``` - wget https://github.com/IDPF/epubcheck/releases/download/v4.2.5/epubcheck-4.2.5.zip - unzip epubcheck-4.2.5.zip + wget https://github.com/IDPF/epubcheck/releases/download/v5.1.0/epubcheck-5.1.0.zip + unzip epubcheck-5.1.0.zip + ``` + + You can use this command to download and extract the latest versions of EPUBCheck (recommended). + + ``` + curl -s https://api.github.com/repos/w3c/epubcheck/releases/latest | awk -F': "' '/browser_download_url/ && /epubcheck/ {gsub(/"$/, "", $2); print $2}' | xargs curl -Lo epubcheck.zip + unzip epubcheck.zip ``` If you do not wish to install EPUBCheck locally, you can manually validate the EPUB: diff --git a/epub.go b/epub.go index 35d4a43..95a3daa 100644 --- a/epub.go +++ b/epub.go @@ -31,6 +31,8 @@ Basic usage: package epub import ( + "bytes" + "encoding/xml" "fmt" "io/fs" "log" @@ -156,9 +158,10 @@ type epubCover struct { } type epubSection struct { - filename string - xhtml *xhtml - children []*epubSection + filename string + xhtml *xhtml + children []*epubSection + properties string } // NewEpub returns a new Epub. @@ -377,9 +380,10 @@ func (e *Epub) addSection(parentFilename string, body string, sectionTitle strin } s := &epubSection{ - filename: internalFilename, - xhtml: x, - children: nil, + filename: internalFilename, + xhtml: x, + children: nil, + properties: propertiesFromBody(body), } // section have parentIndex -1 and subsection have parrentindex != -1 @@ -397,6 +401,40 @@ func (e *Epub) addSection(parentFilename string, body string, sectionTitle strin return internalFilename, nil } +// supports mathml, svg, scripted +// does not support remote-sources, switch (deprecated) +func propertiesFromBody(body string) string { + prop := map[string]bool{} + + decoder := xml.NewDecoder(bytes.NewBufferString(body)) + for { + t, _ := decoder.Token() + if t == nil { + break + } + switch se := t.(type) { + case xml.StartElement: + switch strings.ToUpper(se.Name.Local) { + case "SVG": + prop["svg"] = true + case "MATH": + if se.Name.Space == "http://www.w3.org/1998/Math/MathML" { + prop["mathml"] = true + } + case "SCRIPT", "FORM": + prop["scripted"] = true + } + default: + } + } + + ret := []string{} + for k := range prop { + ret = append(ret, k) + } + return strings.Join(ret, " ") +} + // Author returns the author of the EPUB. func (e *Epub) Author() string { return e.author diff --git a/epub_test.go b/epub_test.go index f2ab888..0f25e85 100644 --- a/epub_test.go +++ b/epub_test.go @@ -1480,3 +1480,56 @@ func TestAddSubSectionWithCustomFilename(t *testing.T) { cleanup(testEpubFilename, tempDir) } + +func TestSectionProperties(t *testing.T) { + e, err := NewEpub(testEpubTitle) + if err != nil { + t.Error(err) + } + + _, err = e.AddSection("

Section 1

", "Section 1", "section0001.xhtml", "") + if err != nil { + t.Errorf("Error adding section: %s", err) + } + + _, err = e.AddSection("

Section 2

", "Section 2", "section0002.xhtml", "") + if err != nil { + t.Errorf("Error adding section: %s", err) + } + + _, err = e.AddSection("

Section 3

", "Section 3", "section0003.xhtml", "") + if err != nil { + t.Errorf("Error adding section: %s", err) + } + + _, err = e.AddSection(`

Section 4

`, "Section 4", "section0004.xhtml", "") + if err != nil { + t.Errorf("Error adding section: %s", err) + } + + _, err = e.AddSection("

Section 5

", "Section 5", "section0005.xhtml", "") + if err != nil { + t.Errorf("Error adding section: %s", err) + } + + tempDir := writeAndExtractEpub(t, e, testEpubFilename) + + output, err := validateEpub(t, testEpubFilename) + if err != nil { + t.Errorf("EPUB validation failed") + } + + // Always print the output so we can see warnings as well + if output != nil { + fmt.Println(string(output)) + } + if doCleanup { + cleanup(testEpubFilename, tempDir) + } else { + // Always remove the files in tempDir; they can still be extracted from the test epub as needed + err := filesystem.RemoveAll(tempDir) + if err != nil { + log.Print("Error removing temp directory: %w", err) + } + } +} diff --git a/toc.go b/toc.go index 8371478..9e0acb5 100644 --- a/toc.go +++ b/toc.go @@ -4,6 +4,7 @@ import ( "encoding/xml" "fmt" "log" + "path" "path/filepath" "regexp" "strconv" @@ -174,7 +175,7 @@ func (t *toc) addSubSection(parent string, index int, title string, relativePath t.ncxXML.NavMap = append(t.ncxXML.NavMap, np) } else { - parentRelativePath := filepath.Join(xhtmlFolderName, parent) + parentRelativePath := path.Join(xhtmlFolderName, parent) l := &tocNavItem{ A: tocNavLink{ diff --git a/write.go b/write.go index eb9b0a0..637c406 100644 --- a/write.go +++ b/write.go @@ -485,7 +485,7 @@ func writeSections(rootEpubDir string, e *Epub, sections []*epubSection, parentf if section.filename != e.cover.xhtmlFilename { e.pkg.addToSpine(section.filename) } - e.pkg.addToManifest(section.filename, relativePath, mediaTypeXhtml, "") + e.pkg.addToManifest(section.filename, relativePath, mediaTypeXhtml, section.properties) if parentfilename[section.filename] == "-1" && section.filename != e.cover.xhtmlFilename { j := filenamelist[section.filename] e.toc.addSubSection("-1", j, section.xhtml.Title(), relativePath)