package nl.siegmann.epublib.epub; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.stream.XMLStreamException; import nl.siegmann.epublib.Constants; import nl.siegmann.epublib.domain.Book; import nl.siegmann.epublib.domain.Guide; import nl.siegmann.epublib.domain.GuideReference; import nl.siegmann.epublib.domain.Resource; import nl.siegmann.epublib.domain.Spine; import nl.siegmann.epublib.domain.SpineReference; import nl.siegmann.epublib.service.MediatypeService; import org.rr.commons.utils.StringUtil; import org.xmlpull.v1.XmlSerializer; /** * Writes the opf package document as defined by namespace http://www.idpf.org/2007/opf * * @author paul * */ public class PackageDocumentWriter extends PackageDocumentBase { private static final Logger log = Logger.getLogger(PackageDocumentWriter.class.getName()); public static void write(EpubWriter epubWriter, XmlSerializer serializer, Book book) throws IOException { try { serializer.startDocument(Constants.ENCODING, false); 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.version, "2.0"); serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.uniqueIdentifier, BOOK_ID_ID); PackageDocumentMetadataWriter.writeMetaData(book, serializer); writeManifest(book, epubWriter, serializer); writeSpine(book, epubWriter, serializer); writeGuide(book, epubWriter, serializer); serializer.endTag(NAMESPACE_OPF, OPFTags.packageTag); serializer.endDocument(); serializer.flush(); } catch (IOException e) { Logger.getLogger(PackageDocumentWriter.class.getName()).log(Level.WARNING, "PackageDocumentWriter.write failed", e); } } /** * 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, EpubWriter epubWriter, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { serializer.startTag(NAMESPACE_OPF, 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(NAMESPACE_OPF, OPFTags.spine); } private static void writeManifest(Book book, EpubWriter epubWriter, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { serializer.startTag(NAMESPACE_OPF, OPFTags.manifest); serializer.startTag(NAMESPACE_OPF, 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(NAMESPACE_OPF, OPFTags.item); for(Resource resource: getAllResourcesSortById(book)) { writeItem(book, resource, serializer); } serializer.endTag(NAMESPACE_OPF, OPFTags.manifest); } private static List<Resource> getAllResourcesSortById(Book book) { List<Resource> allResources = new ArrayList<>(book.getResources().getAll()); Collections.sort(allResources, new Comparator<Resource>() { @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.isEmpty(resource.getId())) { log.warning("resource id must not be empty (href: " + resource.getHref() + ", mediatype:" + resource.getMediaType() + ")"); return; } if(StringUtil.isEmpty(resource.getHref())) { log.warning("resource href must not be empty (id: " + resource.getId() + ", mediatype:" + resource.getMediaType() + ")"); return; } if(resource.getMediaType() == null) { log.warning("resource mediatype must not be empty (id: " + resource.getId() + ", href:" + resource.getHref() + ")"); return; } serializer.startTag(NAMESPACE_OPF, 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(NAMESPACE_OPF, 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(NAMESPACE_OPF, 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(NAMESPACE_OPF, OPFTags.itemref); } } private static void writeGuide(Book book, EpubWriter epubWriter, XmlSerializer serializer) throws IllegalArgumentException, IllegalStateException, IOException { serializer.startTag(NAMESPACE_OPF, OPFTags.guide); ensureCoverPageGuideReferenceWritten(book.getGuide(), epubWriter, serializer); for (GuideReference reference: book.getGuide().getReferences()) { writeGuideReference(reference, serializer); } serializer.endTag(NAMESPACE_OPF, OPFTags.guide); } private static void ensureCoverPageGuideReferenceWritten(Guide guide, EpubWriter 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(NAMESPACE_OPF, OPFTags.reference); serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.type, reference.getType()); serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.href, reference.getCompleteHref()); if (StringUtil.isNotEmpty(reference.getTitle())) { serializer.attribute(EpubWriter.EMPTY_NAMESPACE_PREFIX, OPFAttributes.title, reference.getTitle()); } serializer.endTag(NAMESPACE_OPF, OPFTags.reference); } }