Adding the api

This commit is contained in:
Samuel Holtzkampf 2015-12-03 15:23:58 +02:00
parent 77562f2f77
commit 8ad9c996f9
22 changed files with 2976 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/target
test.epub
nb-configuration.xml
nbactions.xml

55
pom.xml Normal file
View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>coza.opencollab</groupId>
<artifactId>epub-creator</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlcleaner</groupId>
<artifactId>htmlcleaner</artifactId>
<version>2.8</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<distributionManagement>
<repository>
<id>oc-nexus-releases</id>
<name>OpenCollab Nexus Release Repo</name>
<url>
http://nexus.opencollab.co.za/nexus/content/repositories/releases
</url>
</repository>
<snapshotRepository>
<id>oc-nexus-snapshot</id>
<name>OpenCollab Nexus Snapshot Repo</name>
<url>
http://nexus.opencollab.co.za/nexus/content/repositories/snapshots
</url>
</snapshotRepository>
</distributionManagement>
</project>

View file

@ -0,0 +1,100 @@
/* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator;
/**
* Constants used as default values in the EpubCreator and EpubBook. This is set
* according to the EPUB 3 standards.
*
* @author OpenCollab
*/
public class EpubConstants {
private EpubConstants() {
}
/**
* Used to wrap plain text in a valid XHTML document
*/
public static final String HTML_WRAPPER = "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>{0}</title></head><body>{1}</body></html>";
/**
* Template of the OPF file
*/
public static final String OPF_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<package xmlns=\"http://www.idpf.org/2007/opf\" version=\"3.0\" unique-identifier=\"uid\">\n"
+ " <metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n"
+ " <dc:identifier id=\"uid\"></dc:identifier>\n"
+ " <dc:title></dc:title>\n"
+ " <dc:language></dc:language>\n"
+ " <meta property=\"dcterms:modified\"></meta>"
+ " </metadata>\n"
+ " <manifest>\n"
+ " </manifest>\n"
+ " <spine>\n"
+ " </spine>\n"
+ "</package>";
/**
* Template of a valid table of contents navigation(TOC) XHTML document
*/
public static final String TOC_XML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">\n"
+ " <head>\n"
+ " <meta charset=\"utf-8\" />\n"
+ " <title>{0}</title> \n"
+ " </head>\n"
+ " <body>\n"
+ " <nav epub:type=\"toc\" id=\"toc\">\n"
+ " <ol></ol>\n"
+ " </nav>\n"
+ " <nav epub:type=\"landmarks\" hidden=\"\">\n"
+ " <ol></ol>\n"
+ " </nav>\n"
+ " </body>\n"
+ "</html>";
/**
* Template of a valid EPUB3 container XML
*/
public static final String CONTAINER_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<container xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\" version=\"1.0\">\n"
+ " <rootfiles>\n"
+ " <rootfile full-path=\"{0}/book.opf\" media-type=\"application/oebps-package+xml\"/>\n"
+ " </rootfiles>\n"
+ "</container>";
/**
* Default folder used to save content in
*/
public static final String CONTENT_FOLDER = "content";
/**
* The default href of the toc file
*/
public static final String TOC_FILE_NAME = "toc.xhtml";
/**
* The default href of the opf file
*/
public static final String OPF_FILE_NAME = "book.opf";
}

View file

@ -0,0 +1,41 @@
/* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator.api;
import coza.opencollab.epub.creator.model.EpubBook;
/**
* Service to create the Package Document that carries bibliographic and
* structural meta data about an EPUB Publication
*
* @author OpenCollab
*/
public interface OpfCreator {
/**
* Creates the OPF file text from the EpubBook data
*
* @param book the ePub book to generate the OPF for
* @return the generated OPF markup
*/
public String createOpfString(EpubBook book);
}

View file

@ -0,0 +1,41 @@
/* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator.api;
import coza.opencollab.epub.creator.model.Content;
import coza.opencollab.epub.creator.model.EpubBook;
/**
* Service to create the EPUB navigation document
*
* @author OpenCollab
*/
public interface TocCreator {
/**
* Creates the EPUB TOC navigation document Content object
*
* @param book the EpubBook to create the TOC for
* @return the TOC Content object
*/
public Content createTocFromBook(EpubBook book);
}

View file

@ -0,0 +1,202 @@
/* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator.impl;
import coza.opencollab.epub.creator.EpubConstants;
import coza.opencollab.epub.creator.api.OpfCreator;
import coza.opencollab.epub.creator.model.Content;
import coza.opencollab.epub.creator.model.EpubBook;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.ContentNode;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.PrettyXmlSerializer;
import org.htmlcleaner.Serializer;
import org.htmlcleaner.TagNode;
/**
* Default implementation of the OpfCreator. This follows EPUB3 standards to
* create the OPF file content.
*
* @author OpenCollab
*/
public class OpfCreatorDefault implements OpfCreator {
/**
* The template XML used to create the OPF file. This is settable if a
* different template needs to be used.
*/
private String opfXML = EpubConstants.OPF_XML;
/**
* HtmlCleaner used to clean the XHTML document
*/
private final HtmlCleaner cleaner;
/**
* XmlSerializer used to format to XML String output
*/
private final Serializer htmlSetdown;
public OpfCreatorDefault() {
cleaner = new HtmlCleaner();
CleanerProperties htmlProperties = cleaner.getProperties();
htmlProperties.setOmitHtmlEnvelope(true);
htmlProperties.setAdvancedXmlEscape(false);
htmlProperties.setUseEmptyElementTags(true);
htmlSetdown = new PrettyXmlSerializer(htmlProperties);
}
/**
* {@inheritDoc}
*/
@Override
public String createOpfString(EpubBook book) {
TagNode tagNode = cleaner.clean(opfXML);
addMetaDataTags(tagNode, book);
addManifestTags(tagNode, book);
addSpineTags(tagNode, book);
return htmlSetdown.getAsString(tagNode);
}
/**
* Add the required meta data
*
* @param tagNode the HTML tagNode of the OPF template
* @param book the EpubBook
*/
private void addMetaDataTags(TagNode tagNode, EpubBook book) {
TagNode metaNode = tagNode.findElementByName("metadata", true);
addNodeData(metaNode, "dc:identifier", book.getId());
addNodeData(metaNode, "dc:title", book.getTitle());
addNodeData(metaNode, "dc:language", book.getLanguage());
addNodeData(metaNode, "meta", new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'").format(new Date()));
if (book.getAuthor() != null) {
TagNode creatorNode = new TagNode("dc:creator");
creatorNode.addChild(new ContentNode(book.getAuthor()));
metaNode.addChild(creatorNode);
}
}
/**
* Adds a item tag to the manifest for each Content object.
*
* The manifest contains all Content that will be added to the EPUB as files
*
* @param tagNode the HTML tagNode of the OPF template
* @param book the EpubBook
*/
private void addManifestTags(TagNode tagNode, EpubBook book) {
TagNode manifestNode = tagNode.findElementByName("manifest", true);
for (Content content : book.getContents()) {
manifestNode.addChild(buildItemNode(content));
}
}
/**
* Builds an item tag from the Content object
*
* @param content
* @return
*/
private TagNode buildItemNode(Content content) {
TagNode itemNode = new TagNode("item");
itemNode.addAttribute("href", content.getHref());
itemNode.addAttribute("id", content.getId());
itemNode.addAttribute("media-type", content.getMediaType());
if (content.getProperties() != null) {
itemNode.addAttribute("properties", content.getProperties());
}
if (content.hasFallBack()) {
itemNode.addAttribute("fallback", content.getFallBack().getId());
}
return itemNode;
}
/**
* Adds item ref tags for all Content objects that must be added to the
* spine.
*
* The spine contains all the resources that will be shown when reading the
* book from start to end
*
* @param tagNode the HTML tagNode of the OPF template
* @param book the EpubBook
*/
private void addSpineTags(TagNode tagNode, EpubBook book) {
TagNode spineNode = tagNode.findElementByName("spine", true);
for (Content content : book.getContents()) {
if (content.isSpine()) {
spineNode.addChild(buildItemrefNode(content));
}
}
}
/**
* Builds an item ref tag from the Content object
*
* @param content
* @return
*/
private TagNode buildItemrefNode(Content content) {
TagNode itemNode = new TagNode("itemref");
itemNode.addAttribute("idref", content.getId());
if (!content.isLinear()) {
itemNode.addAttribute("linear", "no");
}
return itemNode;
}
/**
* Adds a ContentNode (value) with to a child element of the TagNode
*
* <elementName>{value}<elementName>
*
* @param tagNode
* @param elementName
* @param value
*/
private void addNodeData(TagNode tagNode, String elementName, String value) {
TagNode editNode = tagNode.findElementByName(elementName, true);
editNode.addChild(new ContentNode(value));
}
/**
* The base XML used for the OPF file.
*
* @return the OPF XML text
*/
public String getOpfXML() {
return opfXML;
}
/**
* The base XML used for the OPF file. This is optional as there is a EPUB3
* standard default but it can be overridden.
*
* @param opfXML the OPF XML to set
*/
public void setOpfXML(String opfXML) {
this.opfXML = opfXML;
}
}

View file

@ -0,0 +1,220 @@
/* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator.impl;
import coza.opencollab.epub.creator.EpubConstants;
import coza.opencollab.epub.creator.api.TocCreator;
import coza.opencollab.epub.creator.model.Content;
import coza.opencollab.epub.creator.model.EpubBook;
import coza.opencollab.epub.creator.model.Landmark;
import coza.opencollab.epub.creator.model.TocLink;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.ContentNode;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.PrettyXmlSerializer;
import org.htmlcleaner.Serializer;
import org.htmlcleaner.TagNode;
/**
* Default implementation of the TocCreator. This follows EPUB3 standards to
* create the Navigation Document file content.
*
* @author OpenCollab
*/
public class TocCreatorDefault implements TocCreator {
/**
* HtmlCleaner used to alter the XHTML document
*/
private final HtmlCleaner cleaner;
private String href = EpubConstants.TOC_FILE_NAME;
private String tocHtml = EpubConstants.TOC_XML;
/**
* XmlSerializer used to format to XML String output
*/
private final Serializer htmlSetdown;
public TocCreatorDefault() {
cleaner = new HtmlCleaner();
CleanerProperties htmlProperties = cleaner.getProperties();
htmlProperties.setOmitHtmlEnvelope(false);
htmlProperties.setAdvancedXmlEscape(false);
htmlProperties.setUseEmptyElementTags(true);
htmlSetdown = new PrettyXmlSerializer(htmlProperties);
}
/**
* {@inheritDoc}
*/
@Override
public Content createTocFromBook(EpubBook book) {
List<TocLink> links = book.getTocLinks();
if (book.isAutoToc()) {
links = generateAutoLinks(book);
}
List<Landmark> landmarks = book.getLandmarks();
String tocString = createTocHtml(links, landmarks, getTocHtml());
Content toc = new Content("application/xhtml+xml", getHref(), tocString.getBytes());
toc.setProperties("nav");
toc.setId("toc");
toc.setLinear(false);
return toc;
}
/**
* Builds the TOC HTML content from the EpubBook TocLinks
*
* @param book the EpubBook
* @return the TOC HTML String
*/
private String createTocHtml(List<TocLink> links, List<Landmark> landmarks, String tocHtml) {
TagNode tagNode = cleaner.clean(tocHtml);
if (!CollectionUtils.isEmpty(links)) {
addTocLinks(tagNode, links);
}
if (!CollectionUtils.isEmpty(landmarks)) {
addLandmarks(tagNode, landmarks);
}
return htmlSetdown.getAsString(tagNode);
}
/**
* Recursive method adding links and sub links to the TOC Navigation
* Document
*
* @param tagNode
* @param links
*/
private void addTocLinks(TagNode tagNode, List<TocLink> links) {
TagNode navNode = tagNode.findElementByAttValue("epub:type", "toc", true, false);
TagNode parentNode = navNode.findElementByName("ol", true);
for (TocLink toc : links) {
TagNode linkNode = buildLinkNode(toc);
if (!CollectionUtils.isEmpty(toc.getTocChildLinks())) {
TagNode olNode = new TagNode("ol");
addTocLinks(olNode, toc.getTocChildLinks());
linkNode.addChild(olNode);
}
parentNode.addChild(linkNode);
}
}
/**
* Adds landmarks to the Navigation Document
*
* @param tagNode
* @param links
*/
private void addLandmarks(TagNode tagNode, List<Landmark> landmarks) {
TagNode navNode = tagNode.findElementByAttValue("epub:type", "landmarks", true, false);
TagNode parentNode = navNode.findElementByName("ol", true);
for (Landmark landmark : landmarks) {
TagNode landmarkNode = buildLandMarkNode(landmark);
parentNode.addChild(landmarkNode);
}
}
/**
* Builds an link tag for the TOC
*
* @param content
* @return
*/
private TagNode buildLinkNode(TocLink link) {
TagNode linkNode = new TagNode("li");
TagNode aNode = new TagNode("a");
aNode.addAttribute("href", link.getHref());
aNode.addChild(new ContentNode(link.getTitle()));
if (link.getAltTitle() != null) {
aNode.addAttribute("title", link.getHref());
}
linkNode.addChild(aNode);
return linkNode;
}
/**
* Builds an link tag for the TOC landmarks
*
* @param content
* @return
*/
private TagNode buildLandMarkNode(Landmark landmark) {
TagNode linkNode = new TagNode("li");
TagNode aNode = new TagNode("a");
aNode.addAttribute("href", landmark.getHref());
aNode.addAttribute("epub:type", landmark.getType());
aNode.addChild(new ContentNode(landmark.getTitle()));
linkNode.addChild(aNode);
return linkNode;
}
/**
* Generates a list of TocLinks for all Content that should be included in
* the Navigation Document. This will only be used when auto TOC is set
*
* @param book
* @return
*/
private List<TocLink> generateAutoLinks(EpubBook book) {
List<TocLink> links = new ArrayList();
for (Content content : book.getContents()) {
if (content.isToc()) {
links.add(new TocLink(content.getHref(), content.getId(), null));
}
}
return links;
}
/**
* @return the HREF
*/
public String getHref() {
return href;
}
/**
* @param href the HREF to set
*/
public void setHref(String href) {
this.href = href;
}
/**
* @return the tocHtml
*/
public String getTocHtml() {
return tocHtml;
}
/**
* @param tocHtml the tocHtml to set
*/
public void setTocHtml(String tocHtml) {
this.tocHtml = tocHtml;
}
}

View file

@ -0,0 +1,336 @@
/* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator.model;
import coza.opencollab.epub.creator.util.MediaTypeUtil;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
/**
* Representation of content in the book
*
* @author OpenCollab
*/
public class Content {
/**
* The MediaType get set as the media-type attribute value in the OPF
* manifest items
*/
private String mediaType;
/**
* The HREF get set as the HREF attribute value in the OPF manifest items
*/
private String href;
/**
* The id get set as the id attribute value in the OPF manifest items and
* the id ref in the spine. This must be unique
*/
private String id;
/**
* The properties get set as the properties attribute value in the OPF
* manifest items. This is used for the TOC and cover-image content objects
*/
private String properties;
/**
* The file content
*/
private byte[] content;
/**
* Specifies if it is a linear item in the spine
*/
private boolean linear = true;
/**
* Specifies if it must be added to the spine
*/
private boolean spine = true;
/**
* Specifies if it must be added to the TOC (Navigation Document)
*/
private boolean toc = false;
/**
* The fallback content
*/
private Content fallBack;
/**
* Creates new instance of Content
*
* @param mediaType the mime type
* @param href the link to the content item
* @param content the file byte array
*/
public Content(String mediaType, String href, byte[] content) {
if (mediaType == null) {
this.mediaType = MediaTypeUtil.getMediaTypeFromFilename(href);
} else {
this.mediaType = mediaType;
}
this.href = href;
this.content = content;
}
/**
* Creates new instance of Content
*
* @param href the link to the content item
* @param content the file byte array
*/
public Content(String href, byte[] content) {
this(MediaTypeUtil.getMediaTypeFromFilename(href), href, content);
}
/**
* Creates a new instance of Content
*
* @param mediaType the mime type
* @param href the link to the content item
* @param id used as the id attribute value in the OPF manifest items and
* the id ref in the spine
* @param properties the properties attribute value in the OPF
* @param content the file byte array
*/
public Content(String mediaType, String href, String id, String properties, byte[] content) {
this(mediaType, href, content);
this.id = id;
this.properties = properties;
this.content = content;
}
/**
* Creates a new instance of Content
*
* @param href the link to the content item
* @param id used as the id attribute value in the OPF manifest items and
* the id ref in the spine
* @param properties the properties attribute value in the OPF
* @param content the file byte array
*/
public Content(String href, String id, String properties, byte[] content) {
this(MediaTypeUtil.getMediaTypeFromFilename(href), href, id, properties, content);
}
/**
* Creates new instance of Content
*
* @param mediaType the mime type
* @param href the link to the content item
* @param content the file byte array
* @throws java.io.IOException if content could not be converted to byte
* array
*/
public Content(String mediaType, String href, InputStream content) throws IOException {
this(mediaType, href, IOUtils.toByteArray(content));
}
/**
* Creates new instance of Content
*
* @param href the link to the content item
* @param content the file byte array
* @throws java.io.IOException if content could not be converted to byte
* array
*/
public Content(String href, InputStream content) throws IOException {
this(MediaTypeUtil.getMediaTypeFromFilename(href), href, IOUtils.toByteArray(content));
}
/**
* Creates a new instance of Content
*
* @param mediaType the mime type
* @param href the link to the content item
* @param id used as the id attribute value in the OPF manifest items and
* the id ref in the spine
* @param properties the properties attribute value in the OPF
* @param content the file byte array
* @throws java.io.IOException if content could not be converted to byte
* array
*/
public Content(String mediaType, String href, String id, String properties, InputStream content) throws IOException {
this(mediaType, href, id, properties, IOUtils.toByteArray(content));
}
/**
* Creates a new instance of Content
*
* @param href the link to the content item
* @param id used as the id attribute value in the OPF manifest items and
* the id ref in the spine
* @param properties the properties attribute value in the OPF
* @param content the file byte array
* @throws java.io.IOException if content could not be converted to byte
* array
*/
public Content(String href, String id, String properties, InputStream content) throws IOException {
this(MediaTypeUtil.getMediaTypeFromFilename(href), href, id, properties, IOUtils.toByteArray(content));
}
/**
* Indicates whether a fallback content has been set
*
* @return the fallback flag
*/
public boolean hasFallBack() {
return fallBack != null;
}
/**
* @return the mediaType
*/
public String getMediaType() {
return mediaType;
}
/**
* @param mediaType the mediaType to set
*/
public void setMediaType(String mediaType) {
this.mediaType = mediaType;
}
/**
* @return the HREF
*/
public String getHref() {
return href;
}
/**
* @param href the HREF to set
*/
public void setHref(String href) {
this.href = href;
}
/**
* @return the id
*/
public String getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(String id) {
this.id = id;
}
/**
* @return the properties
*/
public String getProperties() {
return properties;
}
/**
* @param properties the properties to set
*/
public void setProperties(String properties) {
this.properties = properties;
}
/**
* @return the content
*/
public byte[] getContent() {
return content;
}
/**
* @param content the content to set
*/
public void setContent(byte[] content) {
this.content = content;
}
/**
* @return the linear
*/
public boolean isLinear() {
return linear;
}
/**
* @param linear the linear to set
*/
public void setLinear(boolean linear) {
this.linear = linear;
}
/**
* @return the spine
*/
public boolean isSpine() {
return spine;
}
/**
* @param spine the spine to set
*/
public void setSpine(boolean spine) {
this.spine = spine;
}
/**
* @return the TOC
*/
public boolean isToc() {
return toc;
}
/**
* @param toc the TOC to set
*/
public void setToc(boolean toc) {
this.toc = toc;
}
/**
* @return the fallBack
*/
public Content getFallBack() {
return fallBack;
}
/**
* The spine parameter will be set to false as we do not want to add the
* fallback content to the spine. To override default behavior call
* getFallBack to set spine property.
*
* @param fallBack the fallBack to set
*/
public void setFallBack(Content fallBack) {
fallBack.setSpine(false);
this.fallBack = fallBack;
}
}

View file

@ -0,0 +1,429 @@
/* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator.model;
import coza.opencollab.epub.creator.EpubConstants;
import coza.opencollab.epub.creator.util.EpubWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Representation of an EPUB book
*
* @author OpenCollab
*/
public class EpubBook {
/**
* A list of the the content files, this includes the cover, HTML pages, CSS
* JavaScript or any other resource
*/
private List<Content> contents;
/**
* Indicates whether the TOC must be created automatically. If this is false
* the tocLinks must be set
*/
private boolean autoToc = true;
/**
* The 2 letter language code set in the dc:language meta data
*/
private String language;
/**
* The id used as the meta data dc:identifier
*/
private String id;
/**
* The title of the book
*/
private String title;
/**
* The author, this is set as the meta data dc:creator value
*/
private String author;
/**
* Unique content id that is incremental set on content with no id
*/
private int contentId = 1;
/**
* List of the links that must be added to the TOC, they can be nested
*/
private List<TocLink> tocLinks;
/**
* List of the landmarks to be added to the TOC
*/
private List<Landmark> landmarks;
/**
* Instance of the EpubWriter to write the book to file or stream
*/
private final EpubWriter epubCreator;
/**
* Unique set of href's used to make sure we do not add duplicates
*/
private final Set uniqueHrefs;
/**
* The href can not be repeated in the OPF, thus is we want to add the same
* content in more than one place we have to create duplicate Content
* objects but with different href's
*/
private int hrefUniquePostfix = 1;
/**
* Constructs EPUBBook
*/
public EpubBook() {
this.epubCreator = new EpubWriter();
this.contents = new ArrayList<>();
this.tocLinks = new ArrayList<>();
this.uniqueHrefs = new HashSet();
}
/**
* Constructs EPUBBook
*
* @param language the 2 letter language code set in the dc:language meta
* data
* @param id the id used as the meta data dc:identifier
* @param title the title of the book
* @param author the author, this is set as the meta data dc:creator value
*/
public EpubBook(String language, String id, String title, String author) {
this();
this.language = language;
this.id = id;
this.title = title;
this.author = author;
}
/**
* Checks if href unique, adds postfix if not
*
* @param content
*/
private void checkHref(Content content) {
content.setHref(removeLeadingFileSeparator(content.getHref()));
if (uniqueHrefs.contains(content.getHref())) {
content.setHref(incrementHref(content.getHref()));
}
uniqueHrefs.add(content.getHref());
}
/**
* Checks if href unique, adds postfix if not
*
* @param href
*/
private String checkHref(String href) {
href = removeLeadingFileSeparator(href);
if (uniqueHrefs.contains(href)) {
return incrementHref(href);
}
return href;
}
/**
* Increment href. Checks for file extensions
*
* @param href
* @return
*/
private String incrementHref(String href) {
if (href.contains(".")) {
return href.replace(".", "_" + hrefUniquePostfix++ + ".");
}
return href + "_" + hrefUniquePostfix++;
}
/**
* Remove the leading file separator
*
* @param href
* @return
*/
private String removeLeadingFileSeparator(String href) {
if (href.startsWith("/") || href.startsWith("\\")) {
return href.substring(1);
}
return href;
}
/**
* Adds content to the content list and checks the id. Only adds unique
* href's
*
* @param content the EpubBook content - TOC, pages, files
* @return boolean indicating if the content has been added
*/
public boolean addContent(Content content) {
checkContentId(content);
checkHref(content);
contents.add(content);
return true;
}
/**
* Inserts content at a specific index. Only adds unique href's
*
* @param content the EpubBook content - TOC, pages, files
* @param index index where the content will be inserted
* @return boolean indicating if the content has been added
*/
public boolean insertContent(Content content, int index) {
checkContentId(content);
checkHref(content);
contents.add(index, content);
return true;
}
/**
* Wraps a String in the HTML wrapper and adds create a content object that
* is added to the content list. Returns null if the href is not unique
*
* @param title the title of the page
* @param href used as unique link
* @param content text content to be added
* @return the Content object generated from the text
*/
public Content addTextContent(String title, String href, String content) {
href = checkHref(href);
String contentString = MessageFormat.format(EpubConstants.HTML_WRAPPER, title, content);
Content textContent = new Content("application/xhtml+xml", href, contentString.getBytes());
addContent(textContent);
return textContent;
}
/**
* Creates and adds file Content to the book
*
* @param contents the byte array content
* @param mediaType the mime type
* @param href used as unique link
* @param toc flag whether it must be added to the TOC
* @param spine flag whether it must be added to the spine
* @return a reference to the newly created Content object
*/
public Content addContent(byte[] contents, String mediaType, String href, boolean toc, boolean spine) {
Content content = new Content(mediaType, href, contents);
content.setToc(toc);
content.setSpine(spine);
addContent(content);
return content;
}
/**
* Creates and adds an InputStream to the book
*
* @param contents the InputStream to the content
* @param mediaType the mime type
* @param href used as unique link
* @param toc flag whether it must be added to the TOC
* @param spine flag whether it must be added to the spine
* @return a reference to the newly created Content object
* @throws java.io.IOException if the InputStream can not be converted to
* byte[]
*/
public Content addContent(InputStream contents, String mediaType, String href, boolean toc, boolean spine) throws IOException {
Content content = new Content(mediaType, href, contents);
content.setToc(toc);
content.setSpine(spine);
addContent(content);
return content;
}
/**
* Checks if Content object has an id and adds an unique if not
*
* @param content
*/
private void checkContentId(Content content) {
if (content.getId() == null) {
content.setId(id.replaceAll("[^a-zA-Z0-9\\-]", "_") + "_" + (contentId++));
}
}
/**
* Adds an file as the cover image
*
* @param coverImage the cover image
* @param mediaType the mime type of the cover image
* @param href used to name the cover image
*/
public void addCoverImage(byte[] coverImage, String mediaType, String href) {
Content cover = new Content(mediaType, href, coverImage);
cover.setProperties("cover-image");
cover.setSpine(false);
addContent(cover);
}
/**
* Creates the EPUB zip container and writes it to the OutputStream
*
* @param out the OutputStream
* @throws Exception if the content can not be zipped and written
*/
public void writeToStream(OutputStream out) throws Exception {
epubCreator.writeEpubToStream(this, out);
}
/**
* Creates the EPUB zip container and writes it to the File
*
* @param fileName to store as
* @throws Exception if the content can not be zipped and stored
*/
public void writeToFile(String fileName) throws Exception {
epubCreator.writeEpubToFile(this, fileName);
}
/**
* @return the contents
*/
public List<Content> getContents() {
return contents;
}
/**
* @param contents the content to set
*/
public void setContents(List<Content> contents) {
this.contents = contents;
}
/**
* @return the autoToc
*/
public boolean isAutoToc() {
return autoToc;
}
/**
* @param autoToc the autoToc to set
*/
public void setAutoToc(boolean autoToc) {
this.autoToc = autoToc;
}
/**
* @return the language
*/
public String getLanguage() {
return language;
}
/**
* @param language the language to set
*/
public void setLanguage(String language) {
this.language = language;
}
/**
* @return the id
*/
public String getId() {
return id;
}
/**
* @param id the id to set
*/
public void setId(String id) {
this.id = id;
}
/**
* @return the title
*/
public String getTitle() {
return title;
}
/**
* @param title the title to set
*/
public void setTitle(String title) {
this.title = title;
}
/**
* @return the tocLinks
*/
public List<TocLink> getTocLinks() {
return tocLinks;
}
/**
* @param tocLinks the tocLinks to set
*/
public void setTocLinks(List<TocLink> tocLinks) {
this.tocLinks = tocLinks;
}
/**
* @return the landmarks
*/
public List<Landmark> getLandmarks() {
return landmarks;
}
/**
* @param landmarks the landmarks to set
*/
public void setLandmarks(List<Landmark> landmarks) {
this.landmarks = landmarks;
}
/**
* @return the author
*/
public String getAuthor() {
return author;
}
/**
* @param author the author to set
*/
public void setAuthor(String author) {
this.author = author;
}
/**
* @return the epubCreator
*/
public EpubWriter getEpubCreator() {
return epubCreator;
}
/**
* @return the uniqueHrefs
*/
public Set getUniqueHrefs() {
return uniqueHrefs;
}
}

View file

@ -0,0 +1,86 @@
/* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator.model;
/**
* Represents a landmark object in the Navigation document landmarks section
*
* @author OpenCollab
*/
public class Landmark {
/**
* The href of the referenced landmark
*/
private String href;
/**
* The title to display
*/
private String title;
/**
* The type (cover,title-page,frontmatter,bodymatter,backmatter,toc, loi,lot
* (list of tables),preface,bibliography,index,glossary,acknowledgments
*/
private String type;
/**
* @return the href
*/
public String getHref() {
return href;
}
/**
* @param href the href to set
*/
public void setHref(String href) {
this.href = href;
}
/**
* @return the title
*/
public String getTitle() {
return title;
}
/**
* @param title the title to set
*/
public void setTitle(String title) {
this.title = title;
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(String type) {
this.type = type;
}
}

View file

@ -0,0 +1,116 @@
/* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator.model;
import java.util.List;
/**
* Represents a link in the table of contents, this object can have nested child
* links
*
* @author OpenCollab
*/
public class TocLink {
/**
* The href
*/
private String href;
/**
* The displayed text
*/
private String title;
/**
* Can be set for accessibility if the pronunciation of a link heading may
* be ambiguous due to embedded images, math content, or other content
* without intrinsic text. Will be included as an 'title' attribute
*/
private String altTitle;
/**
* Any nested links
*/
private List<TocLink> tocChildLinks;
public TocLink(String href, String title, String altTitle) {
this.href = href;
this.title = title;
this.altTitle = altTitle;
}
/**
* @return the href
*/
public String getHref() {
return href;
}
/**
* @param href the href to set
*/
public void setHref(String href) {
this.href = href;
}
/**
* @return the title
*/
public String getTitle() {
return title;
}
/**
* @param title the title to set
*/
public void setTitle(String title) {
this.title = title;
}
/**
* @return the altTitle
*/
public String getAltTitle() {
return altTitle;
}
/**
* @param altTitle the altTitle to set
*/
public void setAltTitle(String altTitle) {
this.altTitle = altTitle;
}
/**
* @return the tocChildLinks
*/
public List<TocLink> getTocChildLinks() {
return tocChildLinks;
}
/**
* @param tocChildLinks the tocChildLinks to set
*/
public void setTocChildLinks(List<TocLink> tocChildLinks) {
this.tocChildLinks = tocChildLinks;
}
}

View file

@ -0,0 +1,217 @@
/* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator.util;
import coza.opencollab.epub.creator.EpubConstants;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.MessageFormat;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import coza.opencollab.epub.creator.api.OpfCreator;
import coza.opencollab.epub.creator.api.TocCreator;
import coza.opencollab.epub.creator.impl.OpfCreatorDefault;
import coza.opencollab.epub.creator.impl.TocCreatorDefault;
import coza.opencollab.epub.creator.model.Content;
import coza.opencollab.epub.creator.model.EpubBook;
import java.util.List;
/**
* The EpubWriter creates the EPUB zip bundle.
*
* @author OpenCollab
*/
public class EpubWriter {
private String containerXML = EpubConstants.CONTAINER_XML;
private String contentFolder = EpubConstants.CONTENT_FOLDER;
private String opfFileName = EpubConstants.OPF_FILE_NAME;
private OpfCreator opfCreator = new OpfCreatorDefault();
private TocCreator tocCreator = new TocCreatorDefault();
/**
* Writes the EPUB book zip container and contents to a file
*
* @param book the EpubBook
* @param fileName name of the file to be written
* @throws IOException if file could not be written
*/
public void writeEpubToFile(EpubBook book, String fileName) throws IOException {
try (FileOutputStream file = new FileOutputStream(new File(fileName))) {
writeEpubToStream(book, file);
}
}
/**
* Writes the EPUB book zip container and contents to an OutputStream
*
* @param book the EpubBook
* @param out the OutputStream to write to
* @throws IOException if file could not be written
*/
public void writeEpubToStream(EpubBook book, OutputStream out) throws IOException {
try (ZipOutputStream resultStream = new ZipOutputStream(out)) {
List<Content> contents = book.getContents();
addMimeType(resultStream);
contents.add(0, getTocCreator().createTocFromBook(book));
addStringToZip(resultStream, "META-INF/container.xml", MessageFormat.format(containerXML, contentFolder));
addStringToZip(resultStream, contentFolder + "/" + getOpfFileName(), getOpfCreator().createOpfString(book));
addContent(resultStream, contents);
}
}
/**
* Adds the zip/EPUB mime type to the EPUB zip file
*
* @param resultStream
* @throws IOException
*/
private void addMimeType(ZipOutputStream resultStream) throws IOException {
ZipEntry mimetypeZipEntry = new ZipEntry("mimetype");
mimetypeZipEntry.setMethod(ZipEntry.STORED);
byte[] mimetypeBytes = "application/epub+zip".getBytes("UTF-8");
mimetypeZipEntry.setSize(mimetypeBytes.length);
mimetypeZipEntry.setCrc(calculateCrc(mimetypeBytes));
resultStream.putNextEntry(mimetypeZipEntry);
resultStream.write(mimetypeBytes);
}
/**
* Calculates the CRC32 of data for the zip entry
*
* @param data
* @return
*/
private long calculateCrc(byte[] data) {
CRC32 crc = new CRC32();
crc.update(data);
return crc.getValue();
}
/**
* Adds string content as an zip entry with the specified file name
*
* @param resultStream
* @param fileName
* @param content
* @throws IOException
*/
private void addStringToZip(ZipOutputStream resultStream, String fileName, String content) throws IOException {
resultStream.putNextEntry(new ZipEntry(fileName));
Writer out = new OutputStreamWriter(resultStream, "UTF-8");
out.write(content);
out.flush();
}
/**
* Adds the content objects zip entries
*
* @param resultStream
* @param contents
* @throws IOException
*/
private void addContent(ZipOutputStream resultStream, List<Content> contents) throws IOException {
for (Content content : contents) {
resultStream.putNextEntry(new ZipEntry(contentFolder + "/" + content.getHref()));
resultStream.write(content.getContent());
}
}
/**
* @return the CONTAINER_XML
*/
public String getContainerXML() {
return containerXML;
}
/**
* @param containerXML the CONTAINER_XML to set
*/
public void setContainerXML(String containerXML) {
this.containerXML = containerXML;
}
/**
* @return the CONTENT_FOLDER
*/
public String getContentFolder() {
return contentFolder;
}
/**
* @param contentFolder the CONTENT_FOLDER to set
*/
public void setContentFolder(String contentFolder) {
this.contentFolder = contentFolder;
}
/**
* @return the OPF_FILE_NAME
*/
public String getOpfFileName() {
return opfFileName;
}
/**
* @param opfFileName the OPF_FILE_NAME to set
*/
public void setOpfFileName(String opfFileName) {
this.opfFileName = opfFileName;
}
/**
* @return the opfCreator
*/
public OpfCreator getOpfCreator() {
return opfCreator;
}
/**
* @param opfCreator the opfCreator to set
*/
public void setOpfCreator(OpfCreator opfCreator) {
this.opfCreator = opfCreator;
}
/**
* @return the tocCreator
*/
public TocCreator getTocCreator() {
return tocCreator;
}
/**
* @param tocCreator the tocCreator to set
*/
public void setTocCreator(TocCreator tocCreator) {
this.tocCreator = tocCreator;
}
}

View file

@ -0,0 +1,101 @@
/*
* The MIT License
*
* Copyright 2014 OpenCollab.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package coza.opencollab.epub.creator.util;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Used to map file extensions to Mime types from a properties file
*
* @author OpenCollab
*/
public class MediaTypeUtil {
private static Map<String, String> mediaTypeMap = setMediaFromProperties();
/**
* Created a map of media types for specific file extensions, read from the
* epub-mediatypes.properties file
*
* @return a map of all the media types in the properties file
*/
private static Map<String, String> setMediaFromProperties() {
Properties properties = new Properties();
try {
properties.load(MediaTypeUtil.class.getClassLoader().getResourceAsStream("epub-mediatypes.properties"));
} catch (IOException ex) {
Logger.getLogger(MediaTypeUtil.class.getName()).log(Level.SEVERE,
"Could not read the 'epub-mediatypes.properties' file to populate the media types. "
+ "Please use MediaTypeUtil.setMediaTypeMap(Map<String, String> aMediaTypeMap) to set the map.", ex);
return new HashMap<>();
}
Map<String, String> mappings = new HashMap<>();
Enumeration elements = properties.propertyNames();
while (elements.hasMoreElements()) {
String keyProps = (String) elements.nextElement();
String valProps = properties.getProperty(keyProps);
mappings.put(keyProps, valProps);
}
return mappings;
}
/**
* Returns the correct media type for a specific file extension
*
* @param ext the file extension
* @return the mime type
*/
public static String getMediaTypeFromExt(String ext) {
return mediaTypeMap.get(ext.toLowerCase());
}
/**
* Returns the correct media type for a specific file name
*
* @param fileName the full filename
* @return the mime type
*/
public static String getMediaTypeFromFilename(String fileName) {
String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
return getMediaTypeFromExt(ext);
}
/**
* Allows you to override the media type map
*
* @param aMediaTypeMap the mediaTypeMap to set
*/
public static void setMediaTypeMap(Map<String, String> aMediaTypeMap) {
mediaTypeMap = aMediaTypeMap;
}
}

View file

@ -0,0 +1,51 @@
# The MIT License
#
# Copyright 2014 OpenCollab.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# Mapping of file extensions to the correct media type, this file will be used
# in MediaTypeUtil
xhtml=application/xhtml+xml
html=application/xhtml+xml
htm=application/xhtml+xml
jpg=image/jpeg
jpeg=image/jpeg
gif=image/gif
png=image/png
svg=image/svg+xml
ncx=application/x-dtbncx+xml
otf=application/vnd.ms-opentype
woff=application/font-woff
smil=application/smil+xml
pls=application/pls+xml
mp3=audio/mpeg
mp4=audio/mp4
css=text/css
js=text/javascript
epub=application/epub+zip
tff=application/x-truetype-font
ogg=audio/ogg
xpgt=application/adobe-page-template+xml
pdf=application/pdf
docx=application/vnd.openxmlformats-officedocument.wordprocessingml.document
doc=application/msword
xls=application/vnd.ms-excel
xlsx=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

View file

@ -0,0 +1,41 @@
package coza.opencollab.epub.creator;
import coza.opencollab.epub.creator.model.EpubBook;
import java.io.File;
import java.io.FileOutputStream;
import junit.framework.Assert;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
/**
*
* @author OpenCollab
*/
public class EpubCreatorTest {
@Test
public void testEpubCreate() {
try (FileOutputStream file = new FileOutputStream(new File("test.epub"))) {
EpubBook book = new EpubBook("en", "Samuel .-__Id1", "Samuel Test Book", "Samuel Holtzkampf");
book.addContent(this.getClass().getResourceAsStream("/epub30-overview.xhtml"),
"application/xhtml+xml", "xhtml/epub30-overview.xhtml", true, true).setId("Overview");
book.addContent(this.getClass().getResourceAsStream("/idpflogo_web_125.jpg"),
"image/jpeg", "img/idpflogo_web_125.jpg", false, false);
book.addContent(this.getClass().getResourceAsStream("/epub-spec.css"),
"text/css", "css/epub-spec.css", false, false);
book.addTextContent("TestHtml", "xhtml/samuelTest2.xhtml", "Samuel test one two four!!!!!\nTesting two").setToc(true);
book.addTextContent("TestHtml", "xhtml/samuelTest.xhtml", "Samuel test one two three\nTesting two").setToc(true);
book.addCoverImage(IOUtils.toByteArray(this.getClass().getResourceAsStream("/P1010832.jpg")),
"image/jpeg", "images/P1010832.jpg");
book.writeToStream(file);
// TODO : real tests to see if document correct, this is just to test that creation is succesfull
Assert.assertEquals("test", "test");
} catch (Exception ex) {
System.out.println(ex);
Assert.assertEquals("test", "test1");
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View file

@ -0,0 +1,581 @@
@charset "utf-8";
body {
margin-top: 2em;
margin-left: 6em;
margin-right: 15em;
margin-bottom: 2em;
font-family: arial, helvetica, sans-serif;
color: black;
background: white;
}
/***************************************************************************/
/* dl **************************************************************/
/***************************************************************************/
dl dt {
font-weight: bold;
}
dt span.term {
color: #005A9C;
font-weight: normal;
}
dd > dl {
margin-top: 1px;
padding-top: 0px;
margin-bottom: 1px;
padding-bottom: 0px;
}
/***************************************************************************/
/* ul **************************************************************/
/***************************************************************************/
ul.conformance-list li {
list-style-type: none;
}
/***************************************************************************/
/* glosslist *********************************************************/
/***************************************************************************/
*.glosslist dl {
margin-left: 2em
}
*.glosslist dl dt {
color: #005A9C
}
*.glossentry {
font-size: .9em;
}
/***************************************************************************/
/* misc block ********************************************************/
/***************************************************************************/
p.informative, p.normative {
font-weight: bold;
font-size: 100%;
color: #8B0000;
padding: 5px 0px 10px;
}
.biblioentry:target {
border: 1px dashed rgb(200,200,225);
}
/***************************************************************************/
/* misc inline *******************************************************/
/***************************************************************************/
code, span.property, a.codelink {
color: #660099;
font-family: monospace;
font-weight: bold
}
*.RFC2119 {
font-style: italic;
font-size: 80%;
}
*.todo, *.TODO {
background-color: yellow
}
/* vocab */
*.subproplabel, *.subpropref {
font-style: italic;
font-size: 90%;
}
/***************************************************************************/
/* elem-synopsis ****************************************************/
/***************************************************************************/
*.elem-synopsis {
border: 1px solid rgb(200,200,225);
border-top: 1px solid rgb(210,210,245);
border-left: 1px solid rgb(210,210,245);
background: rgb(240,240,255);
padding-left: 1em;
padding-right: 1em;
margin-bottom: 2em;
-moz-box-shadow: 5px 5px 5px #888;
-webkit-box-shadow: 5px 5px 5px #888;
}
/* special case for the code.option fields in elem-synopsis */
code.option {
color: black;
font-family: arial, helvetica, sans-serif;
font-weight: normal
}
/* for the special case when an attlist occurs outside of an elem-synopsis*/
div.section > div.elem-synopsis-attlist {
border: 2px solid rgb(200,200,225);
background: rgb(230,230,255);
padding: 1em
}
/***************************************************************************/
/* toc ***************************************************************/
/***************************************************************************/
div.toc dl dt {
font-weight: normal
}
*.toc p, div.list-of-examples p {
font: small-caps 105% sans-serif;
color: #005A9C;
}
*.toc {
margin-top: 3em
}
nav#toc li {
list-style-type:none;
}
nav#toc > ol > li > span > a {
color: #0000ff
}
nav#toc > ol > li > ol > li > a {
color: #0033ff
}
nav#toc > ol > li > ol > li > ol > li > a {
font-size: .9em;
color: #0066ff;
}
nav#toc > ol > li > ol > li > ol > li > ol > li > a {
font-size: .9em;
color: #0099ff
}
nav#toc > ol > li > ol > li > ol > li > ol > li > ol > li > a {
font-size: .9em;
color: #0099ff
}
nav#toc > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > a {
font-size: .9em;
color: #0099ff
}
nav#toc > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > ol > li > a {
font-size: .9em;
color: #0099ff
}
/***************************************************************************/
/* examples **********************************************************/
/***************************************************************************/
div.informalexample {
margin: 1.5em auto 2em auto;
}
div.informalexample > p {
margin: 0px 20px;
font-style: italic;
font-size: .8em
}
div.informalexample > p code {
color: black;
}
pre {
font-family: "Courier New", Courier, Monospace;
color: #000;
background-color: #eee;
margin: 0;
border: 1px solid #ddd;
padding: 9px;
overflow: auto;
white-space: pre-wrap;
white-space: -moz-pre-wrap !important;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
pre.synopsis, p.syntax {
background-color:rgb(253,253,253);
border: 1px solid rgb(220,220,220);
border-left: 1px solid rgb(230,230,230);
border-top: 1px solid rgb(230,230,230);
margin-bottom: 1em;
-moz-box-shadow: 5px 5px 5px #888;
-webkit-box-shadow: 5px 5px 5px #888;
-box-shadow: 5px 5px 5px #888;
}
p.syntax {
font-family: courier, fixed, monospace;
padding: 9px;
}
/***************************************************************************/
/* note, caution *****************************************************/
/***************************************************************************/
.caution .note {
font-style : italic;
font-size : .9em;
}
div.note, div.caution {
margin: 2em 1em;
border: solid gray 1px;
background-color: rgb(255,255,224);
padding: .5em
}
div.caution {
background-color: rgb(255,235,205)
}
div.note > *, div.caution > * {
/* margin: .25em 0em;
padding: 0em; */
}
div.note > h2, div.note > h3, div.note > h4, div.note > h5,
div.caution > h2, div.caution > h3, div.caution > h4, div.caution > h5 {
font-size: 1em;
font-style: italic;
font-variant: small-caps;
float: left;
padding: .18em .6em .18em .5em;
margin: -1em -0.5em -0.5em auto;
width: auto;
border: solid 1px;
background: white;
}
/***************************************************************************/
/* headings ************************************************************/
/***************************************************************************/
h1, h2, h3, h4, h5, h6 {
text-align: left;
color: #005A9C;
}
h1 {
font: 170% sans-serif;
padding-bottom: 1em;
}
h2 {
font: 140% sans-serif;
padding-top: 2em;
border-bottom: 1px solid #005A9C
}
h3 {
font: 120% sans-serif;
padding-top: 1.5em;
}
h4 {
font: bold 110% sans-serif;
padding-top: 1.2em;
}
h5 {
font: italic 105% sans-serif
}
h6 {
font: italic 100% sans-serif
}
p.bridgehead {
font: 110% sans-serif;
padding-top: 0.7em;
color: #005A9C;
}
/***************************************************************************/
/* links *******************************************************************/
/***************************************************************************/
a.glossterm {
color: black;
text-decoration: none;
border-bottom : 1px dotted rgb(210,210,210);
}
a.biblioref {
color: #0000FF;
text-decoration: none;
}
a.glossterm:hover, a.biblioref:hover {
text-decoration: underline;
background-color: white;
color: #0000FF;
}
/***************************************************************************/
/* "link here" anchor *****************************************************/
/***************************************************************************/
a.hidden-reveal {
color: #005A9C;
}
a.hidden-reveal:link {
text-decoration: none;
color: #005A9C;
}
a.hidden-reveal:visited {
text-decoration: none;
color: #005A9C;
}
a.hidden-reveal:hover {
text-decoration: underline;
background-color: #005A9C;
color: #FFFFFF;
}
a.hidden-reveal:active {
text-decoration: none;
color: #005A9C;
}
/***************************************************************************/
/* frontmatter ********************************************************/
/***************************************************************************/
*.releaseinfo {
font-size: 140%;
color: #005A9C;
margin-right: 0.4em
}
*.pubdate {
font-size: 140%;
color: #005A9C
}
*.legalnotice {
font-size: 90%
}
dl.printhistory {
margin-top: 2em;
margin-bottom: 2em
}
dl.printhistory dt {
font: small-caps 105% sans-serif;
color: #005A9C;
margin-top: 0.4em;
margin-bottom: 0.4em
}
div.authorgroup p.editor {
margin-left: 2em;
font-size: 90%
}
/***************************************************************************/
/* table *************************************************************/
/***************************************************************************/
table {
border: 1px solid #005A9C;
border-spacing: 0px;
padding: 1em;
margin-top: 1.5em;
margin-bottom: 2em;
-moz-box-shadow: 2px 2px 2px rgb(240,240,240);
-webkit-box-shadow: 2px 2px 2px rgb(190,190,190);
-box-shadow: 2px 2px 2px rgb(190,190,190);
}
th {
border: 1px solid #005A9C;
font-weight: bold;
padding: 0.5em;
font-weight: bold;
color: #005A9C;
font-size: 90%
}
td {
border: 1px solid #005A9C;
padding: 0.5em;
text-align: left
}
td *, td > * {
text-align: left;
}
table, tbody {
padding: 0;
}
caption {
text-align: left;
font-size: 90%
}
/* the xsl renders db:simplelist as single-column tables */
table.simplelist, table.simplelist th, table.simplelist td, table.simplelist tr {
border-style: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
-box-shadow: none;
}
/* ensure confomity of width for property tables */
div.informaltable > table {
border-spacing: 0px;
border: 1px solid rgb(210,210,225);
font-size: 1em;
width: 100%
}
div.informaltable > table td, div.informaltable > table th {
border: none;
}
div.informaltable > table td.rdfa-property {
padding: 3px;
text-indent: 15px;
color: rgb(0,90,156);
background-color: rgb(240,240,255);
border-bottom: 1px solid rgb(210,210,225);
}
td.rdfa-property > code {
color: rgb(0,50,116);
font-weight: bold;
font-size: 1.1em
}
div.informaltable > table td.rdfa-property-header {
width: 150px;
text-align: center;
padding: 3px;
border-right: 1px solid rgb(210,210,225);
color: rgb(0,90,156);
background-color: rgb(240,240,255);
}
td.rdfa-property-desc {
padding: 3px
}
td.rdfa-cardinality > p {
padding: 0em;
margin: 0em
}
/* the core media types table */
table#tbl-core-media-types > thead > tr > th,
table#tbl-epubReadingSystem-properties > thead > tr > th,
table#tbl-epubReadingSystem-features > thead > tr > th {
background-color: rgb(240,240,255);
text-align: left;
white-space:nowrap;
}
table#tbl-core-media-types > tbody > tr > th {
text-align: left;
font-weight: normal;
font-style: italic;
padding: 3px;
}
table#tbl-core-media-types td, table#tbl-core-media-types th, table#tbl-core-media-types tr {
border-color: #005A9C;
margin: 0em;
}
table#tbl-core-media-types td code, table#tbl-core-media-types td a {
white-space:nowrap;
}
table.productionset, table.productionset td {
border : none;
background-color : #EEE;
}
td[headers="tbl-cmt-appl"] {
font-size: .9em;
}
/* ns prefix table in contentdocs */
table#tbl-nspfx {
border:none;
border-spacing: 0px;
margin-left: 1em;
}
span.italic {
font-style: italic;
}
span.link-marker {
float: left;
}
/* display prop examples as block */
code.prop-example {
display: block;
}
/* participant listings */
ul.personlist {
list-style-type: none;
padding-left: 1em;
margin-bottom: 2em;
}
*.personlist span.affiliation {
color: rgb(75,75,75);
}
*.personlist span.wg-role {
font-style: italic;
}
/*
span.surname {
text-transform: uppercase;
}
*/
table.productionset tbody tr {
border: none;
}
table.productionset tbody tr td {
border: none;
border-bottom: 1px solid #CCC;
}
table.productionset tbody tr td a {
text-decoration: none;
border-bottom: 1px dotted #0000ff;
}
table.productionset tbody tr td a:hover {
text-decoration: underline;
}
table.productionset tbody tr td a:visited,
table.productionset tbody tr td a:active,
table.productionset tbody tr td a:hover {
color: #0000ff;
}
p.diff, p.errata {
font-size: 0.9em;
font-style: italic
}
code {
font-style: normal;
}
div.informalexample > p code {
font-size: 1.2em;
}
/* vocab experimental term labels */
strong.experimental {
color: rgb(255,0,0)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB