├── .gitignore ├── LICENSE ├── README.md ├── book.go ├── container.go ├── epub_test.go ├── ncx.go ├── open.go └── opf.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.epub 2 | 3 | *.swp 4 | .*~ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 kapmahc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # epub 2 | A pure go implementation of epub file format. 3 | 4 | 5 | 6 | ## Documents 7 | - 8 | - 9 | - 10 | -------------------------------------------------------------------------------- /book.go: -------------------------------------------------------------------------------- 1 | package epub 2 | 3 | import ( 4 | "archive/zip" 5 | "encoding/xml" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "path" 10 | ) 11 | 12 | // Book epub book 13 | type Book struct { 14 | Ncx Ncx `json:"ncx"` 15 | Opf Opf `json:"opf"` 16 | Container Container `json:"-"` 17 | Mimetype string `json:"-"` 18 | 19 | fd *zip.ReadCloser 20 | } 21 | 22 | //Open open resource file 23 | func (p *Book) Open(n string) (io.ReadCloser, error) { 24 | return p.open(p.filename(n)) 25 | } 26 | 27 | //Files list resource files 28 | func (p *Book) Files() []string { 29 | var fns []string 30 | for _, f := range p.fd.File { 31 | fns = append(fns, f.Name) 32 | } 33 | return fns 34 | } 35 | 36 | //Close close file reader 37 | func (p *Book) Close() { 38 | p.fd.Close() 39 | } 40 | 41 | //----------------------------------------------------------------------------- 42 | func (p *Book) filename(n string) string { 43 | return path.Join(path.Dir(p.Container.Rootfile.Path), n) 44 | } 45 | 46 | func (p *Book) readXML(n string, v interface{}) error { 47 | fd, err := p.open(n) 48 | if err != nil { 49 | return nil 50 | } 51 | defer fd.Close() 52 | dec := xml.NewDecoder(fd) 53 | return dec.Decode(v) 54 | } 55 | 56 | func (p *Book) readBytes(n string) ([]byte, error) { 57 | fd, err := p.open(n) 58 | if err != nil { 59 | return nil, nil 60 | } 61 | defer fd.Close() 62 | 63 | return ioutil.ReadAll(fd) 64 | 65 | } 66 | 67 | func (p *Book) open(n string) (io.ReadCloser, error) { 68 | for _, f := range p.fd.File { 69 | if f.Name == n { 70 | return f.Open() 71 | } 72 | } 73 | return nil, fmt.Errorf("file %s not exist", n) 74 | } 75 | -------------------------------------------------------------------------------- /container.go: -------------------------------------------------------------------------------- 1 | package epub 2 | 3 | //Container META-INF/container.xml file 4 | type Container struct { 5 | Rootfile Rootfile `xml:"rootfiles>rootfile" json:"rootfile"` 6 | } 7 | 8 | //Rootfile root file 9 | type Rootfile struct { 10 | Path string `xml:"full-path,attr" json:"path"` 11 | Type string `xml:"media-type,attr" json:"type"` 12 | } 13 | -------------------------------------------------------------------------------- /epub_test.go: -------------------------------------------------------------------------------- 1 | package epub_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/kapmahc/epub" 7 | ) 8 | 9 | func TestEpub(t *testing.T) { 10 | bk, err := open(t, "test.epub") 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | defer bk.Close() 15 | } 16 | 17 | func open(t *testing.T, f string) (*epub.Book, error) { 18 | bk, err := epub.Open(f) 19 | if err != nil { 20 | return nil, err 21 | } 22 | defer bk.Close() 23 | 24 | t.Logf("files: %+v", bk.Files()) 25 | t.Logf("book: %+v", bk) 26 | 27 | return bk, nil 28 | } 29 | -------------------------------------------------------------------------------- /ncx.go: -------------------------------------------------------------------------------- 1 | package epub 2 | 3 | //Ncx OPS/toc.ncx 4 | type Ncx struct { 5 | Points []NavPoint `xml:"navMap>navPoint" json:"points"` 6 | } 7 | 8 | //NavPoint nav point 9 | type NavPoint struct { 10 | Text string `xml:"navLabel>text" json:"text"` 11 | Content Content `xml:"content" json:"content"` 12 | Points []NavPoint `xml:"navPoint" json:"points"` 13 | } 14 | 15 | //Content nav-point content 16 | type Content struct { 17 | Src string `xml:"src,attr" json:"src"` 18 | } 19 | -------------------------------------------------------------------------------- /open.go: -------------------------------------------------------------------------------- 1 | package epub 2 | 3 | import "archive/zip" 4 | 5 | //Open open a epub file 6 | func Open(fn string) (*Book, error) { 7 | fd, err := zip.OpenReader(fn) 8 | if err != nil { 9 | return nil, err 10 | } 11 | 12 | bk := Book{fd: fd} 13 | mt, err := bk.readBytes("mimetype") 14 | if err == nil { 15 | bk.Mimetype = string(mt) 16 | err = bk.readXML("META-INF/container.xml", &bk.Container) 17 | } 18 | if err == nil { 19 | err = bk.readXML(bk.Container.Rootfile.Path, &bk.Opf) 20 | } 21 | 22 | for _, mf := range bk.Opf.Manifest { 23 | if mf.ID == bk.Opf.Spine.Toc { 24 | err = bk.readXML(bk.filename(mf.Href), &bk.Ncx) 25 | break 26 | } 27 | } 28 | 29 | if err != nil { 30 | fd.Close() 31 | return nil, err 32 | } 33 | 34 | return &bk, nil 35 | } 36 | -------------------------------------------------------------------------------- /opf.go: -------------------------------------------------------------------------------- 1 | package epub 2 | 3 | //Opf content.opf 4 | type Opf struct { 5 | Metadata Metadata `xml:"metadata" json:"metadata"` 6 | Manifest []Manifest `xml:"manifest>item" json:"manifest"` 7 | Spine Spine `xml:"spine" json:"spine"` 8 | } 9 | 10 | //Metadata metadata 11 | type Metadata struct { 12 | Title []string `xml:"title" json:"title"` 13 | Language []string `xml:"language" json:"language"` 14 | Identifier []Identifier `xml:"identifier" json:"identifier"` 15 | Creator []Author `xml:"creator" json:"creator"` 16 | Subject []string `xml:"subject" json:"subject"` 17 | Description []string `xml:"description" json:"description"` 18 | Publisher []string `xml:"publisher" json:"publisher"` 19 | Contributor []Author `xml:"contributor" json:"contributor"` 20 | Date []Date `xml:"date" json:"date"` 21 | Type []string `xml:"type" json:"type"` 22 | Format []string `xml:"format" json:"format"` 23 | Source []string `xml:"source" json:"source"` 24 | Relation []string `xml:"relation" json:"relation"` 25 | Coverage []string `xml:"coverage" json:"coverage"` 26 | Rights []string `xml:"rights" json:"rights"` 27 | Meta []Metafield `xml:"meta" json:"meta"` 28 | } 29 | 30 | // Identifier identifier 31 | type Identifier struct { 32 | Data string `xml:",chardata" json:"data"` 33 | ID string `xml:"id,attr" json:"id"` 34 | Scheme string `xml:"scheme,attr" json:"scheme"` 35 | } 36 | 37 | // Author author 38 | type Author struct { 39 | Data string `xml:",chardata" json:"author"` 40 | FileAs string `xml:"file-as,attr" json:"file_as"` 41 | Role string `xml:"role,attr" json:"role"` 42 | } 43 | 44 | // Date date 45 | type Date struct { 46 | Data string `xml:",chardata" json:"data"` 47 | Event string `xml:"event,attr" json:"event"` 48 | } 49 | 50 | // Metafield metafield 51 | type Metafield struct { 52 | Name string `xml:"name,attr" json:"name"` 53 | Content string `xml:"content,attr" json:"content"` 54 | } 55 | 56 | //Manifest manifest 57 | type Manifest struct { 58 | ID string `xml:"id,attr" json:"id"` 59 | Href string `xml:"href,attr" json:"href"` 60 | MediaType string `xml:"media-type,attr" json:"type"` 61 | Fallback string `xml:"media-fallback,attr" json:"fallback"` 62 | Properties string `xml:"properties,attr" json:"properties"` 63 | MediaOverlay string `xml:"media-overlay,attr" json:"overlay"` 64 | } 65 | 66 | // Spine spine 67 | type Spine struct { 68 | ID string `xml:"id,attr" json:"id"` 69 | Toc string `xml:"toc,attr" json:"toc"` 70 | PageProgression string `xml:"page-progression-direction,attr" json:"progression"` 71 | Items []SpineItem `xml:"itemref" json:"items"` 72 | } 73 | 74 | // SpineItem spine item 75 | type SpineItem struct { 76 | IDref string `xml:"idref,attr" json:"id_ref"` 77 | Linear string `xml:"linear,attr" json:"linear"` 78 | ID string `xml:"id,attr" json:"id"` 79 | Properties string `xml:"properties,attr" json:"properties"` 80 | } 81 | --------------------------------------------------------------------------------