aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2018-01-28 12:08:00 +0000
committerMichael Williamson <mike@zwobble.org>2018-01-28 12:08:00 +0000
commitf9c7505139d9287e1a5747ebca754797520ff93f (patch)
treee8b73540e11de6988af96a80cf9457759acf531b /src/main/java/org
parentdef58df60701d5575d0fa8afddb6cd27c875fb30 (diff)
Adjust style for easier transformation
Diffstat (limited to 'src/main/java/org')
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/conversion/DocumentToHtml.java400
-rw-r--r--src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java54
2 files changed, 236 insertions, 218 deletions
diff --git a/src/main/java/org/zwobble/mammoth/internal/conversion/DocumentToHtml.java b/src/main/java/org/zwobble/mammoth/internal/conversion/DocumentToHtml.java
index 3f3576f..8117f58 100644
--- a/src/main/java/org/zwobble/mammoth/internal/conversion/DocumentToHtml.java
+++ b/src/main/java/org/zwobble/mammoth/internal/conversion/DocumentToHtml.java
@@ -67,7 +67,7 @@ public class DocumentToHtml {
private final boolean isHeader;
Context() {
- this(false);
+ this.isHeader = false;
}
Context(boolean isHeader) {
@@ -145,225 +145,227 @@ public class DocumentToHtml {
return convertToHtml(element.getChildren(), context);
}
- private List<HtmlNode> convertToHtml(DocumentElement element, Context context) {
- return element.accept(new DocumentElementVisitor<List<HtmlNode>, Context>() {
- @Override
- public List<HtmlNode> visit(Paragraph paragraph, Context context) {
- Supplier<List<HtmlNode>> children = () -> {
- List<HtmlNode> content = convertChildrenToHtml(paragraph, context);
- return preserveEmptyParagraphs ? cons(Html.FORCE_WRITE, content) : content;
- };
- HtmlPath mapping = styleMap.getParagraphHtmlPath(paragraph)
- .orElseGet(() -> {
- if (paragraph.getStyle().isPresent()) {
- warnings.add("Unrecognised paragraph style: " + paragraph.getStyle().get().describe());
- }
- return HtmlPath.element("p");
- });
- return mapping.wrap(children).get();
- }
+ private class ElementConverterVisitor implements DocumentElementVisitor<List<HtmlNode>, Context> {
+ @Override
+ public List<HtmlNode> visit(Paragraph paragraph, Context context) {
+ Supplier<List<HtmlNode>> children = () -> {
+ List<HtmlNode> content = convertChildrenToHtml(paragraph, context);
+ return preserveEmptyParagraphs ? cons(Html.FORCE_WRITE, content) : content;
+ };
+ HtmlPath mapping = styleMap.getParagraphHtmlPath(paragraph)
+ .orElseGet(() -> {
+ if (paragraph.getStyle().isPresent()) {
+ warnings.add("Unrecognised paragraph style: " + paragraph.getStyle().get().describe());
+ }
+ return HtmlPath.element("p");
+ });
+ return mapping.wrap(children).get();
+ }
- @Override
- public List<HtmlNode> visit(Run run, Context context) {
- Supplier<List<HtmlNode>> nodes = () -> convertChildrenToHtml(run, context);
- if (run.isSmallCaps()) {
- nodes = styleMap.getSmallCaps().orElse(HtmlPath.EMPTY).wrap(nodes);
- }
- if (run.isStrikethrough()) {
- nodes = styleMap.getStrikethrough().orElse(HtmlPath.collapsibleElement("s")).wrap(nodes);
- }
- if (run.isUnderline()) {
- nodes = styleMap.getUnderline().orElse(HtmlPath.EMPTY).wrap(nodes);
- }
- if (run.getVerticalAlignment() == VerticalAlignment.SUBSCRIPT) {
- nodes = HtmlPath.collapsibleElement("sub").wrap(nodes);
- }
- if (run.getVerticalAlignment() == VerticalAlignment.SUPERSCRIPT) {
- nodes = HtmlPath.collapsibleElement("sup").wrap(nodes);
- }
- if (run.isItalic()) {
- nodes = styleMap.getItalic().orElse(HtmlPath.collapsibleElement("em")).wrap(nodes);
- }
- if (run.isBold()) {
- nodes = styleMap.getBold().orElse(HtmlPath.collapsibleElement("strong")).wrap(nodes);
- }
- HtmlPath mapping = styleMap.getRunHtmlPath(run)
- .orElseGet(() -> {
- if (run.getStyle().isPresent()) {
- warnings.add("Unrecognised run style: " + run.getStyle().get().describe());
- }
- return HtmlPath.EMPTY;
- });
- return mapping.wrap(nodes).get();
+ @Override
+ public List<HtmlNode> visit(Run run, Context context) {
+ Supplier<List<HtmlNode>> nodes = () -> convertChildrenToHtml(run, context);
+ if (run.isSmallCaps()) {
+ nodes = styleMap.getSmallCaps().orElse(HtmlPath.EMPTY).wrap(nodes);
}
-
- @Override
- public List<HtmlNode> visit(Text text, Context context) {
- if (text.getValue().isEmpty()) {
- return list();
- } else {
- return list(Html.text(text.getValue()));
- }
+ if (run.isStrikethrough()) {
+ nodes = styleMap.getStrikethrough().orElse(HtmlPath.collapsibleElement("s")).wrap(nodes);
}
-
- @Override
- public List<HtmlNode> visit(Tab tab, Context context) {
- return list(Html.text("\t"));
+ if (run.isUnderline()) {
+ nodes = styleMap.getUnderline().orElse(HtmlPath.EMPTY).wrap(nodes);
}
-
- @Override
- public List<HtmlNode> visit(Break breakElement, Context context) {
- HtmlPath mapping = styleMap.getBreakHtmlPath(breakElement)
- .orElseGet(() -> {
- if (breakElement.getType() == Break.Type.LINE) {
- return HtmlPath.element("br");
- } else {
- return HtmlPath.EMPTY;
- }
- });
- return mapping.wrap(() -> list()).get();
+ if (run.getVerticalAlignment() == VerticalAlignment.SUBSCRIPT) {
+ nodes = HtmlPath.collapsibleElement("sub").wrap(nodes);
}
-
- @Override
- public List<HtmlNode> visit(Table table, Context context) {
- HtmlPath mapping = styleMap.getTableHtmlPath(table)
- .orElse(HtmlPath.element("table"));
- return mapping.wrap(() -> generateTableChildren(table, context)).get();
+ if (run.getVerticalAlignment() == VerticalAlignment.SUPERSCRIPT) {
+ nodes = HtmlPath.collapsibleElement("sup").wrap(nodes);
}
-
- private List<HtmlNode> generateTableChildren(Table table, Context context) {
- int bodyIndex = findIndex(table.getChildren(), child -> !isHeader(child))
- .orElse(table.getChildren().size());
- if (bodyIndex == 0) {
- return convertToHtml(table.getChildren(), context.isHeader(false));
- } else {
- List<HtmlNode> headRows = convertToHtml(
- table.getChildren().subList(0, bodyIndex),
- context.isHeader(true)
- );
- List<HtmlNode> bodyRows = convertToHtml(
- table.getChildren().subList(bodyIndex, table.getChildren().size()),
- context.isHeader(false)
- );
- return list(
- Html.element("thead", headRows),
- Html.element("tbody", bodyRows)
- );
- }
+ if (run.isItalic()) {
+ nodes = styleMap.getItalic().orElse(HtmlPath.collapsibleElement("em")).wrap(nodes);
}
-
- private boolean isHeader(DocumentElement child) {
- return tryCast(TableRow.class, child)
- .map(TableRow::isHeader)
- .orElse(false);
+ if (run.isBold()) {
+ nodes = styleMap.getBold().orElse(HtmlPath.collapsibleElement("strong")).wrap(nodes);
}
+ HtmlPath mapping = styleMap.getRunHtmlPath(run)
+ .orElseGet(() -> {
+ if (run.getStyle().isPresent()) {
+ warnings.add("Unrecognised run style: " + run.getStyle().get().describe());
+ }
+ return HtmlPath.EMPTY;
+ });
+ return mapping.wrap(nodes).get();
+ }
- @Override
- public List<HtmlNode> visit(TableRow tableRow, Context context) {
- return list(Html.element("tr", convertChildrenToHtml(tableRow, context)));
+ @Override
+ public List<HtmlNode> visit(Text text, Context context) {
+ if (text.getValue().isEmpty()) {
+ return list();
+ } else {
+ return list(Html.text(text.getValue()));
}
+ }
- @Override
- public List<HtmlNode> visit(TableCell tableCell, Context context) {
- String tagName = context.isHeader ? "th" : "td";
- Map<String, String> attributes = new HashMap<>();
- if (tableCell.getColspan() != 1) {
- attributes.put("colspan", Integer.toString(tableCell.getColspan()));
- }
- if (tableCell.getRowspan() != 1) {
- attributes.put("rowspan", Integer.toString(tableCell.getRowspan()));
- }
- return list(Html.element(tagName, attributes,
- Lists.cons(Html.FORCE_WRITE, convertChildrenToHtml(tableCell, context))));
- }
+ @Override
+ public List<HtmlNode> visit(Tab tab, Context context) {
+ return list(Html.text("\t"));
+ }
- @Override
- public List<HtmlNode> visit(Hyperlink hyperlink, Context context) {
- Map<String, String> attributes = mutableMap("href", generateHref(hyperlink));
- hyperlink.getTargetFrame().ifPresent(targetFrame ->
- attributes.put("target", targetFrame)
- );
+ @Override
+ public List<HtmlNode> visit(Break breakElement, Context context) {
+ HtmlPath mapping = styleMap.getBreakHtmlPath(breakElement)
+ .orElseGet(() -> {
+ if (breakElement.getType() == Break.Type.LINE) {
+ return HtmlPath.element("br");
+ } else {
+ return HtmlPath.EMPTY;
+ }
+ });
+ return mapping.wrap(() -> list()).get();
+ }
- return list(Html.collapsibleElement("a", attributes, convertChildrenToHtml(hyperlink, context)));
- }
+ @Override
+ public List<HtmlNode> visit(Table table, Context context) {
+ HtmlPath mapping = styleMap.getTableHtmlPath(table)
+ .orElse(HtmlPath.element("table"));
+ return mapping.wrap(() -> generateTableChildren(table, context)).get();
+ }
- private String generateHref(Hyperlink hyperlink) {
- if (hyperlink.getHref().isPresent()) {
- return hyperlink.getHref().get();
- } else if (hyperlink.getAnchor().isPresent()) {
- return "#" + generateId(hyperlink.getAnchor().get());
- } else {
- return "";
- }
+ private List<HtmlNode> generateTableChildren(Table table, Context context) {
+ int bodyIndex = findIndex(table.getChildren(), child -> !isHeader(child))
+ .orElse(table.getChildren().size());
+ if (bodyIndex == 0) {
+ return convertToHtml(table.getChildren(), context.isHeader(false));
+ } else {
+ List<HtmlNode> headRows = convertToHtml(
+ table.getChildren().subList(0, bodyIndex),
+ context.isHeader(true)
+ );
+ List<HtmlNode> bodyRows = convertToHtml(
+ table.getChildren().subList(bodyIndex, table.getChildren().size()),
+ context.isHeader(false)
+ );
+ return list(
+ Html.element("thead", headRows),
+ Html.element("tbody", bodyRows)
+ );
}
+ }
- @Override
- public List<HtmlNode> visit(Bookmark bookmark, Context context) {
- return list(Html.element("a", map("id", generateId(bookmark.getName())), list(Html.FORCE_WRITE)));
- }
+ private boolean isHeader(DocumentElement child) {
+ return tryCast(TableRow.class, child)
+ .map(TableRow::isHeader)
+ .orElse(false);
+ }
- @Override
- public List<HtmlNode> visit(NoteReference noteReference, Context context) {
- noteReferences.add(noteReference);
- String noteAnchor = generateNoteHtmlId(noteReference.getNoteType(), noteReference.getNoteId());
- String noteReferenceAnchor = generateNoteRefHtmlId(noteReference.getNoteType(), noteReference.getNoteId());
- return list(Html.element("sup", list(
- Html.element("a", map("href", "#" + noteAnchor, "id", noteReferenceAnchor), list(
- Html.text("[" + noteReferences.size() + "]"))))));
- }
+ @Override
+ public List<HtmlNode> visit(TableRow tableRow, Context context) {
+ return list(Html.element("tr", convertChildrenToHtml(tableRow, context)));
+ }
- @Override
- public List<HtmlNode> visit(CommentReference commentReference, Context context) {
- return styleMap.getCommentReference().orElse(HtmlPath.IGNORE).wrap(() -> {
- String commentId = commentReference.getCommentId();
- Comment comment = lookup(comments, commentId)
- .orElseThrow(() -> new RuntimeException("Referenced comment could not be found, id: " + commentId));
- String label = "[" + comment.getAuthorInitials().orElse("") + (referencedComments.size() + 1) + "]";
- referencedComments.add(new ReferencedComment(label, comment));
-
- // TODO: Remove duplication with note references
- return list(Html.element(
- "a",
- map(
- "href", "#" + generateReferentHtmlId("comment", commentId),
- "id", generateReferenceHtmlId("comment", commentId)),
- list(Html.text(label))));
- }).get();
+ @Override
+ public List<HtmlNode> visit(TableCell tableCell, Context context) {
+ String tagName = context.isHeader ? "th" : "td";
+ Map<String, String> attributes = new HashMap<>();
+ if (tableCell.getColspan() != 1) {
+ attributes.put("colspan", Integer.toString(tableCell.getColspan()));
+ }
+ if (tableCell.getRowspan() != 1) {
+ attributes.put("rowspan", Integer.toString(tableCell.getRowspan()));
}
+ return list(Html.element(tagName, attributes,
+ Lists.cons(Html.FORCE_WRITE, convertChildrenToHtml(tableCell, context))));
+ }
+
+ @Override
+ public List<HtmlNode> visit(Hyperlink hyperlink, Context context) {
+ Map<String, String> attributes = mutableMap("href", generateHref(hyperlink));
+ hyperlink.getTargetFrame().ifPresent(targetFrame ->
+ attributes.put("target", targetFrame)
+ );
- @Override
- public List<HtmlNode> visit(Image image, Context context) {
- // TODO: custom image handlers
- // TODO: handle empty content type
- return image.getContentType()
- .map(contentType -> {
- try {
- Map<String, String> attributes = new HashMap<>(imageConverter.convert(new org.zwobble.mammoth.images.Image() {
- @Override
- public Optional<String> getAltText() {
- return image.getAltText();
- }
-
- @Override
- public String getContentType() {
- return contentType;
- }
-
- @Override
- public InputStream getInputStream() throws IOException {
- return image.open();
- }
- }));
- image.getAltText().ifPresent(altText -> attributes.put("alt", altText));
- return list(Html.element("img", attributes));
- } catch (IOException exception) {
- warnings.add(exception.getMessage());
- return Lists.<HtmlNode>list();
- }
- })
- .orElse(list());
+ return list(Html.collapsibleElement("a", attributes, convertChildrenToHtml(hyperlink, context)));
+ }
+
+ private String generateHref(Hyperlink hyperlink) {
+ if (hyperlink.getHref().isPresent()) {
+ return hyperlink.getHref().get();
+ } else if (hyperlink.getAnchor().isPresent()) {
+ return "#" + generateId(hyperlink.getAnchor().get());
+ } else {
+ return "";
}
- }, context);
+ }
+
+ @Override
+ public List<HtmlNode> visit(Bookmark bookmark, Context context) {
+ return list(Html.element("a", map("id", generateId(bookmark.getName())), list(Html.FORCE_WRITE)));
+ }
+
+ @Override
+ public List<HtmlNode> visit(NoteReference noteReference, Context context) {
+ noteReferences.add(noteReference);
+ String noteAnchor = generateNoteHtmlId(noteReference.getNoteType(), noteReference.getNoteId());
+ String noteReferenceAnchor = generateNoteRefHtmlId(noteReference.getNoteType(), noteReference.getNoteId());
+ return list(Html.element("sup", list(
+ Html.element("a", map("href", "#" + noteAnchor, "id", noteReferenceAnchor), list(
+ Html.text("[" + noteReferences.size() + "]"))))));
+ }
+
+ @Override
+ public List<HtmlNode> visit(CommentReference commentReference, Context context) {
+ return styleMap.getCommentReference().orElse(HtmlPath.IGNORE).wrap(() -> {
+ String commentId = commentReference.getCommentId();
+ Comment comment = lookup(comments, commentId)
+ .orElseThrow(() -> new RuntimeException("Referenced comment could not be found, id: " + commentId));
+ String label = "[" + comment.getAuthorInitials().orElse("") + (referencedComments.size() + 1) + "]";
+ referencedComments.add(new ReferencedComment(label, comment));
+
+ // TODO: Remove duplication with note references
+ return list(Html.element(
+ "a",
+ map(
+ "href", "#" + generateReferentHtmlId("comment", commentId),
+ "id", generateReferenceHtmlId("comment", commentId)),
+ list(Html.text(label))));
+ }).get();
+ }
+
+ @Override
+ public List<HtmlNode> visit(Image image, Context context) {
+ // TODO: custom image handlers
+ // TODO: handle empty content type
+ return image.getContentType()
+ .map(contentType -> {
+ try {
+ Map<String, String> attributes = new HashMap<>(imageConverter.convert(new org.zwobble.mammoth.images.Image() {
+ @Override
+ public Optional<String> getAltText() {
+ return image.getAltText();
+ }
+
+ @Override
+ public String getContentType() {
+ return contentType;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ return image.open();
+ }
+ }));
+ image.getAltText().ifPresent(altText -> attributes.put("alt", altText));
+ return list(Html.element("img", attributes));
+ } catch (IOException exception) {
+ warnings.add(exception.getMessage());
+ return Lists.<HtmlNode>list();
+ }
+ })
+ .orElse(list());
+ }
+ }
+
+ private List<HtmlNode> convertToHtml(DocumentElement element, Context context) {
+ return element.accept(new ElementConverterVisitor(), context);
}
private String generateNoteHtmlId(NoteType noteType, String noteId) {
diff --git a/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java b/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java
index a616aac..f8060a9 100644
--- a/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java
+++ b/src/main/java/org/zwobble/mammoth/internal/docx/DocumentReader.java
@@ -21,11 +21,6 @@ import static org.zwobble.mammoth.internal.util.Lists.*;
import static org.zwobble.mammoth.internal.util.Strings.trimLeft;
public class DocumentReader {
- @FunctionalInterface
- private interface PartWithBodyReader {
- <T> T readPart(String name, BiFunction<XmlElement, BodyXmlReader, T> read, Optional<T> defaultValue);
- }
-
public static InternalResult<Document> readDocument(Optional<Path> path, Archive zipFile) {
PartPaths partPaths = findPartPaths(zipFile);
@@ -33,20 +28,7 @@ public class DocumentReader {
Numbering numbering = readNumbering(zipFile, partPaths);
ContentTypes contentTypes = readContentTypes(zipFile);
FileReader fileReader = new PathRelativeFileReader(path);
- PartWithBodyReader partReader = new PartWithBodyReader() {
- @Override
- public <T> T readPart(String name, BiFunction<XmlElement, BodyXmlReader, T> readPart, Optional<T> defaultValue) {
- Relationships relationships = readRelationships(zipFile, findRelationshipsPathFor(name));
- BodyXmlReader bodyReader = new BodyXmlReader(styles, numbering, relationships, contentTypes, zipFile, fileReader);
- if (defaultValue.isPresent()) {
- return tryParseOfficeXml(zipFile, name)
- .map(root -> readPart.apply(root, bodyReader))
- .orElse(defaultValue.get());
- } else {
- return readPart.apply(parseOfficeXml(zipFile, name), bodyReader);
- }
- }
- };
+ PartWithBodyReader partReader = new PartWithBodyReader(zipFile, contentTypes, fileReader, numbering, styles);
return InternalResult.flatMap(
readNotes(partReader, partPaths),
readComments(partReader, partPaths),
@@ -58,6 +40,40 @@ public class DocumentReader {
);
}
+ private static class PartWithBodyReader {
+ private final Archive zipFile;
+ private final ContentTypes contentTypes;
+ private final FileReader fileReader;
+ private final Numbering numbering;
+ private final Styles styles;
+
+ public PartWithBodyReader(
+ Archive zipFile,
+ ContentTypes contentTypes,
+ FileReader fileReader,
+ Numbering numbering,
+ Styles styles
+ ) {
+ this.zipFile = zipFile;
+ this.contentTypes = contentTypes;
+ this.fileReader = fileReader;
+ this.numbering = numbering;
+ this.styles = styles;
+ }
+
+ <T> T readPart(String name, BiFunction<XmlElement, BodyXmlReader, T> readPart, Optional<T> defaultValue) {
+ Relationships relationships = readRelationships(zipFile, findRelationshipsPathFor(name));
+ BodyXmlReader bodyReader = new BodyXmlReader(styles, numbering, relationships, contentTypes, zipFile, fileReader);
+ if (defaultValue.isPresent()) {
+ return tryParseOfficeXml(zipFile, name)
+ .map(root -> readPart.apply(root, bodyReader))
+ .orElse(defaultValue.get());
+ } else {
+ return readPart.apply(parseOfficeXml(zipFile, name), bodyReader);
+ }
+ }
+ }
+
public static class PartPaths {
private final String mainDocument;
private final String comments;