diff --git a/epublib-core/foo.zip b/epublib-core/foo.zip new file mode 100644 index 00000000..53dd93cf Binary files /dev/null and b/epublib-core/foo.zip differ diff --git a/epublib-core/pom.xml b/epublib-core/pom.xml index 1768eb77..daab95d3 100644 --- a/epublib-core/pom.xml +++ b/epublib-core/pom.xml @@ -5,19 +5,19 @@ 4.0.0 - nl.siegmann.epublib + com.nlibros epublib-core epublib-core + 3.1 A java library for reading/writing/manipulating epub files http://www.siegmann.nl/epublib 2009 - - - nl.siegmann.epublib - epublib-parent - 3.1 - ../epublib-parent/pom.xml - + + UTF-8 + 1.6.1 + 1.7 + 1.7 + @@ -54,45 +54,7 @@ - - - - org.apache.maven.plugins - maven-shade-plugin - 1.4 - - - package - - shade - - - true - complete - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - - - - - - - - org.apache.maven.plugins - maven-site-plugin - 3.0-beta-3 - - - + maven diff --git a/epublib-core/src/main/java/nl/siegmann/epublib/domain/Book.java b/epublib-core/src/main/java/nl/siegmann/epublib/domain/Book.java index 152d5b69..dd19f17b 100644 --- a/epublib-core/src/main/java/nl/siegmann/epublib/domain/Book.java +++ b/epublib-core/src/main/java/nl/siegmann/epublib/domain/Book.java @@ -1,5 +1,6 @@ package nl.siegmann.epublib.domain; +import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -7,11 +8,9 @@ import java.util.Map; - - /** * Representation of a Book. - * + *

* All resources of a Book (html, css, xml, fonts, images) are represented as Resources. See getResources() for access to these.
* A Book as 3 indexes into these Resources, as per the epub specification.
*

@@ -28,503 +27,513 @@ * The Content page may be in the Table of Contents, the Guide, but not in the Spine. * Etc. *

- - - - - - - - - - - - - - - - - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Spine - - - - - - - -Table of Contents - - - - - - - -Guide - -Chapter 1 - -Chapter 1 - -Part 2 - -Chapter 2 - -Chapter 1 - -Chapter 2 - -Cover - -Resources - -Preface - - - - - - - - - - - - - - - - - - - - - * @author paul + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * image/svg+xml + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * Spine + *

+ * + *

+ * + *

+ * + *

+ * Table of Contents + *

+ * + *

+ * + *

+ * + *

+ * Guide + *

+ * Chapter 1 + *

+ * Chapter 1 + *

+ * Part 2 + *

+ * Chapter 2 + *

+ * Chapter 1 + *

+ * Chapter 2 + *

+ * Cover + *

+ * Resources + *

+ * Preface + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * + *

+ * * + * @author paul */ public class Book implements Serializable { - - private static final long serialVersionUID = 2068355170895770100L; - - private Resources resources = new Resources(); - private Metadata metadata = new Metadata(); - private Spine spine = new Spine(); - private TableOfContents tableOfContents = new TableOfContents(); - private Guide guide = new Guide(); - private Resource opfResource; - private Resource ncxResource; - private Resource coverImage; - - /** - * Adds the resource to the table of contents of the book as a child section of the given parentSection - * - * @param parentSection - * @param sectionTitle - * @param resource - * @return The table of contents - */ - public TOCReference addSection(TOCReference parentSection, String sectionTitle, - Resource resource) { - getResources().add(resource); - if (spine.findFirstResourceById(resource.getId()) < 0) { - spine.addSpineReference(new SpineReference(resource)); - } - return parentSection.addChildSection(new TOCReference(sectionTitle, resource)); - } - - public void generateSpineFromTableOfContents() { - Spine spine = new Spine(tableOfContents); - - // in case the tocResource was already found and assigned - spine.setTocResource(this.spine.getTocResource()); - - this.spine = spine; - } - - /** - * Adds a resource to the book's set of resources, table of contents and if there is no resource with the id in the spine also adds it to the spine. - * - * @param title - * @param resource - * @return The table of contents - */ - public TOCReference addSection(String title, Resource resource) { - getResources().add(resource); - TOCReference tocReference = tableOfContents.addTOCReference(new TOCReference(title, resource)); - if (spine.findFirstResourceById(resource.getId()) < 0) { - spine.addSpineReference(new SpineReference(resource)); - } - return tocReference; - } - - - /** - * The Book's metadata (titles, authors, etc) - * - * @return The Book's metadata (titles, authors, etc) - */ - public Metadata getMetadata() { - return metadata; - } - public void setMetadata(Metadata metadata) { - this.metadata = metadata; - } - - - public void setResources(Resources resources) { - this.resources = resources; - } - - - public Resource addResource(Resource resource) { - return resources.add(resource); - } - - /** - * The collection of all images, chapters, sections, xhtml files, stylesheets, etc that make up the book. - * - * @return The collection of all images, chapters, sections, xhtml files, stylesheets, etc that make up the book. - */ - public Resources getResources() { - return resources; - } - - - /** - * The sections of the book that should be shown if a user reads the book from start to finish. - * - * @return The Spine - */ - public Spine getSpine() { - return spine; - } - - - public void setSpine(Spine spine) { - this.spine = spine; - } - - - /** - * The Table of Contents of the book. - * - * @return The Table of Contents of the book. - */ - public TableOfContents getTableOfContents() { - return tableOfContents; - } - - - public void setTableOfContents(TableOfContents tableOfContents) { - this.tableOfContents = tableOfContents; - } - - /** - * The book's cover page as a Resource. - * An XHTML document containing a link to the cover image. - * - * @return The book's cover page as a Resource - */ - public Resource getCoverPage() { - Resource coverPage = guide.getCoverPage(); - if (coverPage == null) { - coverPage = spine.getResource(0); - } - return coverPage; - } - - - public void setCoverPage(Resource coverPage) { - if (coverPage == null) { - return; - } - if (! resources.containsByHref(coverPage.getHref())) { - resources.add(coverPage); - } - guide.setCoverPage(coverPage); - } - - /** - * Gets the first non-blank title from the book's metadata. - * - * @return the first non-blank title from the book's metadata. - */ - public String getTitle() { - return getMetadata().getFirstTitle(); - } - - - /** - * The book's cover image. - * - * @return The book's cover image. - */ - public Resource getCoverImage() { - return coverImage; - } - - public void setCoverImage(Resource coverImage) { - if (coverImage == null) { - return; - } - if (! resources.containsByHref(coverImage.getHref())) { - resources.add(coverImage); - } - this.coverImage = coverImage; - } - - /** - * The guide; contains references to special sections of the book like colophon, glossary, etc. - * - * @return The guide; contains references to special sections of the book like colophon, glossary, etc. - */ - public Guide getGuide() { - return guide; - } - - /** - * All Resources of the Book that can be reached via the Spine, the TableOfContents or the Guide. - *

- * Consists of a list of "reachable" resources: - *

    - *
  • The coverpage
  • - *
  • The resources of the Spine that are not already in the result
  • - *
  • The resources of the Table of Contents that are not already in the result
  • - *
  • The resources of the Guide that are not already in the result
  • - *
- * To get all html files that make up the epub file use {@link #getResources()} - * @return All Resources of the Book that can be reached via the Spine, the TableOfContents or the Guide. - */ - public List getContents() { - Map result = new LinkedHashMap(); - addToContentsResult(getCoverPage(), result); - - for (SpineReference spineReference: getSpine().getSpineReferences()) { - addToContentsResult(spineReference.getResource(), result); - } - - for (Resource resource: getTableOfContents().getAllUniqueResources()) { - addToContentsResult(resource, result); - } - - for (GuideReference guideReference: getGuide().getReferences()) { - addToContentsResult(guideReference.getResource(), result); - } - - return new ArrayList(result.values()); - } - - private static void addToContentsResult(Resource resource, Map allReachableResources){ - if (resource != null && (! allReachableResources.containsKey(resource.getHref()))) { - allReachableResources.put(resource.getHref(), resource); - } - } - - public Resource getOpfResource() { - return opfResource; - } - - public void setOpfResource(Resource opfResource) { - this.opfResource = opfResource; - } - - public void setNcxResource(Resource ncxResource) { - this.ncxResource = ncxResource; - } - - public Resource getNcxResource() { - return ncxResource; - } + + private static final long serialVersionUID = 2068355170895770100L; + + private Resources resources = new Resources(); + private Metadata metadata = new Metadata(); + private Spine spine = new Spine(); + private TableOfContents tableOfContents = new TableOfContents(); + private Guide guide = new Guide(); + private Resource opfResource; + private Resource ncxResource; + private Resource coverImage; + + /** + * Adds the resource to the table of contents of the book as a child section of the given parentSection + * + * @param parentSection + * @param sectionTitle + * @param resource + * @return The table of contents + */ + public TOCReference addSection(TOCReference parentSection, String sectionTitle, + Resource resource) { + getResources().add(resource); + if (spine.findFirstResourceById(resource.getId()) < 0) { + spine.addSpineReference(new SpineReference(resource)); + } + return parentSection.addChildSection(new TOCReference(sectionTitle, resource)); + } + + public void generateSpineFromTableOfContents() { + Spine spine = new Spine(tableOfContents); + + // in case the tocResource was already found and assigned + spine.setTocResource(this.spine.getTocResource()); + + this.spine = spine; + } + + /** + * Adds a resource to the book's set of resources, table of contents and if there is no resource with the id in the spine also adds it to the spine. + * + * @param title + * @param resource + * @return The table of contents + */ + public TOCReference addSection(String title, Resource resource) { + getResources().add(resource); + TOCReference tocReference = tableOfContents.addTOCReference(new TOCReference(title, resource)); + if (spine.findFirstResourceById(resource.getId()) < 0) { + spine.addSpineReference(new SpineReference(resource)); + } + return tocReference; + } + + + /** + * The Book's metadata (titles, authors, etc) + * + * @return The Book's metadata (titles, authors, etc) + */ + public Metadata getMetadata() { + return metadata; + } + + public void setMetadata(Metadata metadata) { + this.metadata = metadata; + } + + + public void setResources(Resources resources) { + this.resources = resources; + } + + + public Resource addResource(Resource resource) { + return resources.add(resource); + } + + /** + * The collection of all images, chapters, sections, xhtml files, stylesheets, etc that make up the book. + * + * @return The collection of all images, chapters, sections, xhtml files, stylesheets, etc that make up the book. + */ + public Resources getResources() { + return resources; + } + + + /** + * The sections of the book that should be shown if a user reads the book from start to finish. + * + * @return The Spine + */ + public Spine getSpine() { + return spine; + } + + + public void setSpine(Spine spine) { + this.spine = spine; + } + + + /** + * The Table of Contents of the book. + * + * @return The Table of Contents of the book. + */ + public TableOfContents getTableOfContents() { + return tableOfContents; + } + + + public void setTableOfContents(TableOfContents tableOfContents) { + this.tableOfContents = tableOfContents; + } + + /** + * The book's cover page as a Resource. + * An XHTML document containing a link to the cover image. + * + * @return The book's cover page as a Resource + */ + public Resource getCoverPage() { + Resource coverPage = guide.getCoverPage(); + if (coverPage == null) { + coverPage = spine.getResource(0); + } + return coverPage; + } + + + public void setCoverPage(Resource coverPage) { + if (coverPage == null) { + return; + } + if (!resources.containsByHref(coverPage.getHref())) { + resources.add(coverPage); + } + guide.setCoverPage(coverPage); + } + + /** + * Gets the first non-blank title from the book's metadata. + * + * @return the first non-blank title from the book's metadata. + */ + public String getTitle() { + return getMetadata().getFirstTitle(); + } + + + /** + * The book's cover image. + * + * @return The book's cover image. + */ + public Resource getCoverImage() { + return coverImage; + } + + public void setCoverImage(Resource coverImage) { + if (coverImage == null) { + return; + } + if (!resources.containsByHref(coverImage.getHref())) { + resources.add(coverImage); + } + this.coverImage = coverImage; + } + + /** + * The guide; contains references to special sections of the book like colophon, glossary, etc. + * + * @return The guide; contains references to special sections of the book like colophon, glossary, etc. + */ + public Guide getGuide() { + return guide; + } + + /** + * All Resources of the Book that can be reached via the Spine, the TableOfContents or the Guide. + *

+ * Consists of a list of "reachable" resources: + *

    + *
  • The coverpage
  • + *
  • The resources of the Spine that are not already in the result
  • + *
  • The resources of the Table of Contents that are not already in the result
  • + *
  • The resources of the Guide that are not already in the result
  • + *
+ * To get all html files that make up the epub file use {@link #getResources()} + * + * @return All Resources of the Book that can be reached via the Spine, the TableOfContents or the Guide. + */ + public List getContents() { + Map result = new LinkedHashMap(); + addToContentsResult(getCoverPage(), result); + + for (SpineReference spineReference : getSpine().getSpineReferences()) { + addToContentsResult(spineReference.getResource(), result); + } + + for (Resource resource : getTableOfContents().getAllUniqueResources()) { + addToContentsResult(resource, result); + } + + for (GuideReference guideReference : getGuide().getReferences()) { + addToContentsResult(guideReference.getResource(), result); + } + + return new ArrayList(result.values()); + } + + private static void addToContentsResult(Resource resource, Map allReachableResources) { + if (resource != null && (!allReachableResources.containsKey(resource.getHref()))) { + allReachableResources.put(resource.getHref(), resource); + } + } + + public Resource getOpfResource() { + return opfResource; + } + + public void setOpfResource(Resource opfResource) { + this.opfResource = opfResource; + } + + public void setNcxResource(Resource ncxResource) { + this.ncxResource = ncxResource; + } + + public Resource getNcxResource() { + return ncxResource; + } + + public String getEpubVersion() throws IOException { + Resource opfResource = getOpfResource(); + String opfContent = new String(opfResource.getData()); + int begin = opfContent.indexOf(" authors = new ArrayList(); - private List contributors = new ArrayList(); - private List dates = new ArrayList(); - private String language = DEFAULT_LANGUAGE; - private Map otherProperties = new HashMap(); - private List rights = new ArrayList(); - private List titles = new ArrayList(); - private List identifiers = new ArrayList(); - private List subjects = new ArrayList(); - private String format = MediatypeService.EPUB.getName(); - private List types = new ArrayList(); - private List descriptions = new ArrayList(); - private List publishers = new ArrayList(); - private Map metaAttributes = new HashMap(); - - public Metadata() { - identifiers.add(new Identifier()); - autoGeneratedId = true; - } - - public boolean isAutoGeneratedId() { - return autoGeneratedId; - } + /** + * + */ + private static final long serialVersionUID = -2437262888962149444L; - /** - * Metadata properties not hard-coded like the author, title, etc. - * - * @return Metadata properties not hard-coded like the author, title, etc. - */ - public Map getOtherProperties() { - return otherProperties; - } - public void setOtherProperties(Map otherProperties) { - this.otherProperties = otherProperties; - } - - public Date addDate(Date date) { - this.dates.add(date); - return date; - } - - public List getDates() { - return dates; - } - public void setDates(List dates) { - this.dates = dates; - } + public static final String DEFAULT_LANGUAGE = "en"; - public Author addAuthor(Author author) { - authors.add(author); - return author; - } - - public List getAuthors() { - return authors; - } - public void setAuthors(List authors) { - this.authors = authors; - } - - public Author addContributor(Author contributor) { - contributors.add(contributor); - return contributor; - } - - public List getContributors() { - return contributors; - } - public void setContributors(List contributors) { - this.contributors = contributors; - } - - public String getLanguage() { - return language; - } - public void setLanguage(String language) { - this.language = language; - } - public List getSubjects() { - return subjects; - } - public void setSubjects(List subjects) { - this.subjects = subjects; - } - public void setRights(List rights) { - this.rights = rights; - } - public List getRights() { - return rights; - } - - - /** - * Gets the first non-blank title of the book. - * Will return "" if no title found. - * - * @return the first non-blank title of the book. - */ - public String getFirstTitle() { - if (titles == null || titles.isEmpty()) { - return ""; - } - for (String title: titles) { - if (StringUtil.isNotBlank(title)) { - return title; - } - } - return ""; - } - - - public String addTitle(String title) { - this.titles.add(title); + private boolean autoGeneratedId = true; + private List authors = new ArrayList(); + private List contributors = new ArrayList(); + private List dates = new ArrayList(); + private String language = DEFAULT_LANGUAGE; + private Map otherProperties = new HashMap(); + private List rights = new ArrayList(); + private List titles = new ArrayList(); + private List identifiers = new ArrayList(); + private List subjects = new ArrayList(); + private String format = MediatypeService.EPUB.getName(); + private List types = new ArrayList(); + private List descriptions = new ArrayList(); + private List publishers = new ArrayList(); + private Map metaAttributes = new HashMap(); + + public Metadata() { + identifiers.add(new Identifier()); + autoGeneratedId = true; + } + + public boolean isAutoGeneratedId() { + return autoGeneratedId; + } + + /** + * Metadata properties not hard-coded like the author, title, etc. + * + * @return Metadata properties not hard-coded like the author, title, etc. + */ + public Map getOtherProperties() { + return otherProperties; + } + + public void setOtherProperties(Map otherProperties) { + this.otherProperties = otherProperties; + } + + public Date addDate(Date date) { + this.dates.add(date); + return date; + } + + public List getDates() { + return dates; + } + + public void setDates(List dates) { + this.dates = dates; + } + + public Author addAuthor(Author author) { + authors.add(author); + return author; + } + + public List getAuthors() { + return authors; + } + + public void setAuthors(List authors) { + this.authors = authors; + } + + public Author addContributor(Author contributor) { + contributors.add(contributor); + return contributor; + } + + public List getContributors() { + return contributors; + } + + public void setContributors(List contributors) { + this.contributors = contributors; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String addSubject(String subject) { + subjects.add(subject); + return subject; + } + + public List getSubjects() { + return subjects; + } + + public void setSubjects(List subjects) { + this.subjects = subjects; + } + + public void setRights(List rights) { + this.rights = rights; + } + + public List getRights() { + return rights; + } + + /** + * Gets the first non-blank title of the book. Will return "" if no title found. + * + * @return the first non-blank title of the book. + */ + public String getFirstTitle() { + if (titles == null || titles.isEmpty()) { + return ""; + } + for (String title : titles) { + if (StringUtil.isNotBlank(title)) { return title; + } } - public void setTitles(List titles) { - this.titles = titles; - } - public List getTitles() { - return titles; - } - - public String addPublisher(String publisher) { - this.publishers.add(publisher); - return publisher; - } - public void setPublishers(List publishers) { - this.publishers = publishers; - } - public List getPublishers() { - return publishers; - } - - public String addDescription(String description) { - this.descriptions.add(description); - return description; - } - public void setDescriptions(List descriptions) { - this.descriptions = descriptions; - } - public List getDescriptions() { - return descriptions; - } - - public Identifier addIdentifier(Identifier identifier) { - if (autoGeneratedId && (! (identifiers.isEmpty()))) { - identifiers.set(0, identifier); - } else { - identifiers.add(identifier); - } - autoGeneratedId = false; - return identifier; - } - public void setIdentifiers(List identifiers) { - this.identifiers = identifiers; - autoGeneratedId = false; - } - - public List getIdentifiers() { - return identifiers; - } - public void setFormat(String format) { - this.format = format; - } - public String getFormat() { - return format; - } + return ""; + } - public String addType(String type) { - this.types.add(type); - return type; - } - - public List getTypes() { - return types; - } - public void setTypes(List types) { - this.types = types; - } + public String addTitle(String title) { + this.titles.add(title); + return title; + } - public String getMetaAttribute(String name) { - return metaAttributes.get(name); - } + public void setTitles(List titles) { + this.titles = titles; + } - public void setMetaAttributes(Map metaAttributes) { - this.metaAttributes = metaAttributes; - } + public List getTitles() { + return titles; + } + + public String addPublisher(String publisher) { + this.publishers.add(publisher); + return publisher; + } + + public void setPublishers(List publishers) { + this.publishers = publishers; + } + + public List getPublishers() { + return publishers; + } + + public String addDescription(String description) { + this.descriptions.add(description); + return description; + } + + public void setDescriptions(List descriptions) { + this.descriptions = descriptions; + } + + public List getDescriptions() { + return descriptions; + } + + public Identifier addIdentifier(Identifier identifier) { + if (autoGeneratedId && (!(identifiers.isEmpty()))) { + identifiers.set(0, identifier); + } else { + identifiers.add(identifier); + } + autoGeneratedId = false; + return identifier; + } + + public void setIdentifiers(List identifiers) { + this.identifiers = identifiers; + autoGeneratedId = false; + } + + public List getIdentifiers() { + return identifiers; + } + + public void setFormat(String format) { + this.format = format; + } + + public String getFormat() { + return format; + } + + public String addType(String type) { + this.types.add(type); + return type; + } + + public List getTypes() { + return types; + } + + public void setTypes(List types) { + this.types = types; + } + + public String getMetaAttribute(String name) { + return metaAttributes.get(name); + } + + public void setMetaAttributes(Map metaAttributes) { + this.metaAttributes = metaAttributes; + } } diff --git a/epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriter.java b/epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriter.java index f08d5587..102babcb 100644 --- a/epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriter.java +++ b/epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriter.java @@ -106,7 +106,7 @@ private void writeResource(Resource resource, ZipOutputStream resultStream) } - private void writePackageDocument(Book book, ZipOutputStream resultStream) throws IOException { + protected void writePackageDocument(Book book, ZipOutputStream resultStream) throws IOException { resultStream.putNextEntry(new ZipEntry("OEBPS/content.opf")); XmlSerializer xmlSerializer = EpubProcessorSupport.createXmlSerializer(resultStream); PackageDocumentWriter.write(this, xmlSerializer, book); diff --git a/epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriter3.java b/epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriter3.java new file mode 100644 index 00000000..9275ddba --- /dev/null +++ b/epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriter3.java @@ -0,0 +1,40 @@ +package nl.siegmann.epublib.epub; + +import nl.siegmann.epublib.domain.Book; +import nl.siegmann.epublib.domain.Resource; +import nl.siegmann.epublib.service.MediatypeService; +import nl.siegmann.epublib.util.IOUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xmlpull.v1.XmlSerializer; + +import java.io.*; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * Generates an epub file. Not thread-safe, single use object. + * + * @author paul + * + */ +public class EpubWriter3 extends EpubWriter { + + public EpubWriter3() { + super(); + } + + public EpubWriter3(BookProcessor bookProcessor) { + super(bookProcessor); + } + + @Override + protected void writePackageDocument(Book book, ZipOutputStream resultStream) throws IOException { + resultStream.putNextEntry(new ZipEntry("OEBPS/content.opf")); + XmlSerializer xmlSerializer = EpubProcessorSupport.createXmlSerializer(resultStream); + PackageDocumentWriter3.write(this, xmlSerializer, book); + xmlSerializer.flush(); + } + +} \ No newline at end of file diff --git a/epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriterFactory.java b/epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriterFactory.java new file mode 100644 index 00000000..53e804a2 --- /dev/null +++ b/epublib-core/src/main/java/nl/siegmann/epublib/epub/EpubWriterFactory.java @@ -0,0 +1,22 @@ +package nl.siegmann.epublib.epub; + +import nl.siegmann.epublib.domain.Book; + +import java.io.IOException; + +/** + * Created by Juan Francisco Rodríguez + **/ +public class EpubWriterFactory { + + public static EpubWriter createWriter(Book book, BookProcessor bookProcessor) throws IOException { + if(book.getEpubVersion().equals("3.0")) { + return new EpubWriter3(bookProcessor); + } + if(book.getEpubVersion().equals("2.0")) { + return new EpubWriter(bookProcessor); + } + return new EpubWriter(bookProcessor); + } + +} diff --git a/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentBase.java b/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentBase.java index 564fc289..feb40515 100644 --- a/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentBase.java +++ b/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentBase.java @@ -9,11 +9,14 @@ */ public class PackageDocumentBase { public static final String BOOK_ID_ID = "BookId"; + public static final String EPUB2 = "2.0"; + public static final String EPUB3 = "3.0"; public static final String NAMESPACE_OPF = "http://www.idpf.org/2007/opf"; public static final String NAMESPACE_DUBLIN_CORE = "http://purl.org/dc/elements/1.1/"; public static final String PREFIX_DUBLIN_CORE = "dc"; public static final String PREFIX_OPF = "opf"; public static final String dateFormat = "yyyy-MM-dd"; + public static final String RENDITION_PREFIX = "rendition: http://www.idpf.org/vocab/rendition/# ibooks:http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/"; protected interface DCTags { String title = "title"; @@ -68,6 +71,8 @@ protected interface OPFAttributes { String version = "version"; String scheme = "scheme"; String property = "property"; + String prefix = "prefix"; + String xmlns = "xmlns"; } protected interface OPFValues { diff --git a/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentMetadataWriter3.java b/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentMetadataWriter3.java new file mode 100644 index 00000000..6ff32e59 --- /dev/null +++ b/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentMetadataWriter3.java @@ -0,0 +1,150 @@ +package nl.siegmann.epublib.epub; + +import nl.siegmann.epublib.Constants; +import nl.siegmann.epublib.domain.Author; +import nl.siegmann.epublib.domain.Book; +import nl.siegmann.epublib.domain.Date; +import nl.siegmann.epublib.domain.Identifier; +import nl.siegmann.epublib.util.StringUtil; +import org.xmlpull.v1.XmlSerializer; + +import javax.xml.namespace.QName; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public class PackageDocumentMetadataWriter3 extends PackageDocumentBase { + + + /** + * Writes the book's metadata. + * + * @param book + * @param serializer + * @throws IOException + * @throws IllegalStateException + * @throws IllegalArgumentException + */ + public static void writeMetaData(Book book, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { + serializer.setPrefix(PREFIX_DUBLIN_CORE, NAMESPACE_DUBLIN_CORE); + serializer.setPrefix(PREFIX_OPF, NAMESPACE_OPF); + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.metadata); + + writeIdentifiers(book.getMetadata().getIdentifiers(), serializer); + writeSimpleMetdataElements(DCTags.title, book.getMetadata().getTitles(), serializer); + writeSimpleMetdataElements(DCTags.subject, book.getMetadata().getSubjects(), serializer); + writeSimpleMetdataElements(DCTags.description, book.getMetadata().getDescriptions(), serializer); + writeSimpleMetdataElements(DCTags.publisher, book.getMetadata().getPublishers(), serializer); + writeSimpleMetdataElements(DCTags.type, book.getMetadata().getTypes(), serializer); + writeSimpleMetdataElements(DCTags.rights, book.getMetadata().getRights(), serializer); + + // write authors + for(Author author: book.getMetadata().getAuthors()) { + serializer.startTag(NAMESPACE_DUBLIN_CORE, DCTags.creator); + serializer.attribute(NAMESPACE_OPF, OPFAttributes.role, author.getRelator().getCode()); + serializer.attribute(NAMESPACE_OPF, OPFAttributes.file_as, author.getLastname() + ", " + author.getFirstname()); + serializer.text(author.getFirstname() + " " + author.getLastname()); + serializer.endTag(NAMESPACE_DUBLIN_CORE, DCTags.creator); + } + + // write contributors + for(Author author: book.getMetadata().getContributors()) { + serializer.startTag(NAMESPACE_DUBLIN_CORE, DCTags.contributor); + serializer.attribute(NAMESPACE_OPF, OPFAttributes.role, author.getRelator().getCode()); + serializer.attribute(NAMESPACE_OPF, OPFAttributes.file_as, author.getLastname() + ", " + author.getFirstname()); + serializer.text(author.getFirstname() + " " + author.getLastname()); + serializer.endTag(NAMESPACE_DUBLIN_CORE, DCTags.contributor); + } + + // write dates + for (Date date: book.getMetadata().getDates()) { + serializer.startTag(NAMESPACE_DUBLIN_CORE, DCTags.date); + if (date.getEvent() != null) { + serializer.attribute(NAMESPACE_OPF, OPFAttributes.event, date.getEvent().toString()); + } + serializer.text(date.getValue()); + serializer.endTag(NAMESPACE_DUBLIN_CORE, DCTags.date); + } + + // write language + if(StringUtil.isNotBlank(book.getMetadata().getLanguage())) { + serializer.startTag(NAMESPACE_DUBLIN_CORE, "language"); + serializer.text(book.getMetadata().getLanguage()); + serializer.endTag(NAMESPACE_DUBLIN_CORE, "language"); + } + + // write other properties + if(book.getMetadata().getOtherProperties() != null) { + for(Map.Entry mapEntry: book.getMetadata().getOtherProperties().entrySet()) { + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.meta); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.property, mapEntry.getKey().getLocalPart()); + serializer.text(mapEntry.getValue()); + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.meta); + } + } + + // write coverimage + if(book.getCoverImage() != null) { // write the cover image + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.meta); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.name, OPFValues.meta_cover); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.content, book.getCoverImage().getId()); + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.meta); + } + + // write generator + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.meta); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.name, OPFValues.generator); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.content, Constants.EPUBLIB_GENERATOR_NAME); + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.meta); + + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.metadata); + } + + private static void writeSimpleMetdataElements(String tagName, List values, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { + for(String value: values) { + if (StringUtil.isBlank(value)) { + continue; + } + serializer.startTag(NAMESPACE_DUBLIN_CORE, tagName); + serializer.text(value); + serializer.endTag(NAMESPACE_DUBLIN_CORE, tagName); + } + } + + + /** + * Writes out the complete list of Identifiers to the package document. + * The first identifier for which the bookId is true is made the bookId identifier. + * If no identifier has bookId == true then the first bookId identifier is written as the primary. + * + * @param identifiers + * @param serializer + * @throws IOException + * @throws IllegalStateException + * @throws IllegalArgumentException + * @ + */ + private static void writeIdentifiers(List identifiers, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { + Identifier bookIdIdentifier = Identifier.getBookIdIdentifier(identifiers); + if(bookIdIdentifier == null) { + return; + } + + serializer.startTag(NAMESPACE_DUBLIN_CORE, DCTags.identifier); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, DCAttributes.id, BOOK_ID_ID); + serializer.attribute(NAMESPACE_OPF, OPFAttributes.scheme, bookIdIdentifier.getScheme()); + serializer.text(bookIdIdentifier.getValue()); + serializer.endTag(NAMESPACE_DUBLIN_CORE, DCTags.identifier); + + for(Identifier identifier: identifiers.subList(1, identifiers.size())) { + if(identifier == bookIdIdentifier) { + continue; + } + serializer.startTag(NAMESPACE_DUBLIN_CORE, DCTags.identifier); + serializer.attribute(NAMESPACE_OPF, "scheme", identifier.getScheme()); + serializer.text(identifier.getValue()); + serializer.endTag(NAMESPACE_DUBLIN_CORE, DCTags.identifier); + } + } + +} diff --git a/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentWriter.java b/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentWriter.java index bcd62607..445ef693 100644 --- a/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentWriter.java +++ b/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentWriter.java @@ -39,6 +39,7 @@ public static void write(EpubWriter epubWriter, XmlSerializer serializer, Book b serializer.setPrefix(PREFIX_OPF, NAMESPACE_OPF); serializer.setPrefix(PREFIX_DUBLIN_CORE, NAMESPACE_DUBLIN_CORE); serializer.startTag(NAMESPACE_OPF, OPFTags.packageTag); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.xmlns, NAMESPACE_OPF); serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.version, "2.0"); serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.uniqueIdentifier, BOOK_ID_ID); diff --git a/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentWriter3.java b/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentWriter3.java new file mode 100644 index 00000000..23638d73 --- /dev/null +++ b/epublib-core/src/main/java/nl/siegmann/epublib/epub/PackageDocumentWriter3.java @@ -0,0 +1,196 @@ +package nl.siegmann.epublib.epub; + +import nl.siegmann.epublib.Constants; +import nl.siegmann.epublib.domain.*; +import nl.siegmann.epublib.service.MediatypeService; +import nl.siegmann.epublib.util.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xmlpull.v1.XmlSerializer; + +import javax.xml.stream.XMLStreamException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + + +/** + * Writes the opf package document as defined by namespace http://www.idpf.org/2007/opf + * + * @author paul + * + */ +public class PackageDocumentWriter3 extends PackageDocumentBase { + + private static final Logger log = LoggerFactory.getLogger(PackageDocumentWriter3.class); + + public static void write(EpubWriter3 epubWriter, XmlSerializer serializer, Book book) throws IOException { + try { + serializer.startDocument(Constants.CHARACTER_ENCODING, false); + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.packageTag); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.xmlns, NAMESPACE_OPF); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.version, EPUB3); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.uniqueIdentifier, BOOK_ID_ID); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.prefix, RENDITION_PREFIX); + + PackageDocumentMetadataWriter3.writeMetaData(book, serializer); + + writeManifest(book, epubWriter, serializer); + writeSpine(book, epubWriter, serializer); + writeGuide(book, epubWriter, serializer); + + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.packageTag); + serializer.endDocument(); + serializer.flush(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + /** + * Writes the package's spine. + * + * @param book + * @param epubWriter + * @param serializer + * @throws IOException + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws XMLStreamException + */ + private static void writeSpine(Book book, EpubWriter3 epubWriter, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.spine); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.toc, book.getSpine().getTocResource().getId()); + + if(book.getCoverPage() != null // there is a cover page + && book.getSpine().findFirstResourceById(book.getCoverPage().getId()) < 0) { // cover page is not already in the spine + // write the cover html file + serializer.startTag(NAMESPACE_OPF, OPFTags.itemref); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.idref, book.getCoverPage().getId()); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.linear, "no"); + serializer.endTag(NAMESPACE_OPF, OPFTags.itemref); + } + writeSpineItems(book.getSpine(), serializer); + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.spine); + } + + + private static void writeManifest(Book book, EpubWriter3 epubWriter, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.manifest); + + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.item); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id, epubWriter.getNcxId()); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href, epubWriter.getNcxHref()); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.media_type, epubWriter.getNcxMediaType()); + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.item); + +// writeCoverResources(book, serializer); + + for(Resource resource: getAllResourcesSortById(book)) { + writeItem(book, resource, serializer); + } + + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.manifest); + } + + private static List getAllResourcesSortById(Book book) { + List allResources = new ArrayList(book.getResources().getAll()); + Collections.sort(allResources, new Comparator() { + + @Override + public int compare(Resource resource1, Resource resource2) { + return resource1.getId().compareToIgnoreCase(resource2.getId()); + } + }); + return allResources; + } + + /** + * Writes a resources as an item element + * @param resource + * @param serializer + * @throws IOException + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws XMLStreamException + */ + private static void writeItem(Book book, Resource resource, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { + if(resource == null || + (resource.getMediaType() == MediatypeService.NCX + && book.getSpine().getTocResource() != null)) { + return; + } + if(StringUtil.isBlank(resource.getId())) { + log.error("resource id must not be empty (href: " + resource.getHref() + ", mediatype:" + resource.getMediaType() + ")"); + return; + } + if(StringUtil.isBlank(resource.getHref())) { + log.error("resource href must not be empty (id: " + resource.getId() + ", mediatype:" + resource.getMediaType() + ")"); + return; + } + if(resource.getMediaType() == null) { + log.error("resource mediatype must not be empty (id: " + resource.getId() + ", href:" + resource.getHref() + ")"); + return; + } + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.item); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.id, resource.getId()); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href, resource.getHref()); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.media_type, resource.getMediaType().getName()); + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.item); + } + + /** + * List all spine references + * @throws IOException + * @throws IllegalStateException + * @throws IllegalArgumentException + */ + private static void writeSpineItems(Spine spine, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { + for(SpineReference spineReference: spine.getSpineReferences()) { + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.itemref); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.idref, spineReference.getResourceId()); + if (! spineReference.isLinear()) { + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.linear, OPFValues.no); + } + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.itemref); + } + } + + private static void writeGuide(Book book, EpubWriter3 epubWriter, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.guide); + ensureCoverPageGuideReferenceWritten(book.getGuide(), epubWriter, serializer); + for (GuideReference reference: book.getGuide().getReferences()) { + writeGuideReference(reference, serializer); + } + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.guide); + } + + private static void ensureCoverPageGuideReferenceWritten(Guide guide, + EpubWriter3 epubWriter, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { + if (! (guide.getGuideReferencesByType(GuideReference.COVER).isEmpty())) { + return; + } + Resource coverPage = guide.getCoverPage(); + if (coverPage != null) { + writeGuideReference(new GuideReference(guide.getCoverPage(), GuideReference.COVER, GuideReference.COVER), serializer); + } + } + + + private static void writeGuideReference(GuideReference reference, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { + if (reference == null) { + return; + } + serializer.startTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.reference); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.type, reference.getType()); + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href, reference.getCompleteHref()); + if (StringUtil.isNotBlank(reference.getTitle())) { + serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.title, reference.getTitle()); + } + serializer.endTag(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFTags.reference); + } +} \ No newline at end of file diff --git a/epublib-parent/target/epublib-parent-3.1.pom b/epublib-parent/target/epublib-parent-3.1.pom deleted file mode 100644 index 8dc6e8a3..00000000 --- a/epublib-parent/target/epublib-parent-3.1.pom +++ /dev/null @@ -1,135 +0,0 @@ - - - - - 4.0.0 - - nl.siegmann.epublib - epublib-parent - epublib-parent - pom - 3.1 - A java library for reading/writing/manipulating epub files - http://www.siegmann.nl/epublib - 2009 - - - UTF-8 - 1.6.1 - - - - ../epublib-core - ../epublib-tools - - - - - LGPL - http://www.gnu.org/licenses/lgpl.html - repo - - - - - - paul - Paul Siegmann - paul.siegmann+epublib@gmail.com - http://www.siegmann.nl/ - +1 - - - - - github - http://github.com/psiegman/epublib/issues - - - - - github.repo - file:///D:/private/project/git-maven-repo/mvn-repo/releases - - - - - http://github.com/psiegman/epublib - scm:git:https://psiegman@github.com/psiegman/epublib.git - scm:git:https://psiegman@github.com/psiegman/epublib.git - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - - - - org.apache.maven.plugins - maven-source-plugin - 2.1.2 - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - - - - - - - org.apache.maven.plugins - maven-site-plugin - 3.0-beta-3 - - - - - - maven - http://repo1.maven.org/maven2/ - - - jboss - https://repository.jboss.org/nexus/ - - -