Skip to content

Commit

Permalink
#364 Treat footnotes inside footnotes gracefully.
Browse files Browse the repository at this point in the history
By processing them as non-footnote content.
  • Loading branch information
danfickle committed Aug 25, 2021
1 parent 22222f0 commit f329721
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
Expand Down Expand Up @@ -67,6 +68,8 @@
import com.openhtmltopdf.render.FlowingColumnContainerBox;
import com.openhtmltopdf.render.InlineBox;
import com.openhtmltopdf.util.OpenUtil;
import com.openhtmltopdf.util.XRLog;
import com.openhtmltopdf.util.LogMessageId;

/**
* This class is responsible for creating the box tree from the DOM. This is
Expand Down Expand Up @@ -1286,8 +1289,7 @@ private static void createElementChild(
Node working,
List<Styleable> children,
ChildBoxInfo info,
CreateChildrenContext context,
boolean allowFootnotes) {
CreateChildrenContext context) {

Styleable child = null;
SharedContext sharedContext = c.getSharedContext();
Expand Down Expand Up @@ -1321,7 +1323,7 @@ private static void createElementChild(
return;
}

if (style.isFootnote() && allowFootnotes) {
if (style.isFootnote() && !c.isInFloatBottom()) {
c.setFootnoteIndex(c.getFootnoteIndex() + 1);

// This is the official footnote call content that can generate zero or more boxes
Expand Down Expand Up @@ -1462,11 +1464,14 @@ private static BlockBox createFootnoteBody(
footnoteBody.setContainingLayer(layer);

c.pushLayer(layer);
c.setIsInFloatBottom(true);

CreateChildrenContext context = new CreateChildrenContext(false, false, style.getParent(), false);
createElementChild(c, (Element) element.getParentNode(), footnoteBody, element, footnoteChildren, footnoteChildInfo, context, false);
createElementChild(c, (Element) element.getParentNode(), footnoteBody, element, footnoteChildren, footnoteChildInfo, context);
resolveChildren(c, footnoteBody, footnoteChildren, footnoteChildInfo);

c.setFootnoteAllowed(true);
c.setIsInFloatBottom(false);
c.popLayer();

// System.out.println();
Expand Down Expand Up @@ -1518,7 +1523,13 @@ private static void createChildren(
insertGeneratedContent(c, parent, parentStyle, "before", children, info);

if (parentStyle.isFootnote()) {
insertGeneratedContent(c, parent, parentStyle, "footnote-marker", children, info);
if (c.isFootnoteAllowed()) {
insertGeneratedContent(c, parent, parentStyle, "footnote-marker", children, info);
// Ban further footnote content until we bubble back up to createFootnoteBody.
c.setFootnoteAllowed(false);
} else {
XRLog.log(Level.WARNING, LogMessageId.LogMessageId0Param.GENERAL_NO_FOOTNOTES_INSIDE_FOOTNOTES);
}
}

Node working = parent.getFirstChild();
Expand All @@ -1532,7 +1543,7 @@ private static void createChildren(

if (nodeType == Node.ELEMENT_NODE) {
createElementChild(
c, parent, blockParent, working, children, info, context, true);
c, parent, blockParent, working, children, info, context);
} else if (nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE) {
context.needStartText = false;
context.needEndText = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ public enum BlockBoxingState {

private int _footnoteIndex;
private FootnoteManager _footnoteManager;
private boolean _isFootnoteAllowed = true;

@Override
public TextRenderer getTextRenderer() {
Expand Down Expand Up @@ -541,6 +542,21 @@ public void setBreakAtLineContext(BreakAtLineContext breakAtLineContext) {
_breakAtLineContext = breakAtLineContext;
}

/**
* Whether further footnote content is allowed. Used to prohibit
* footnotes inside footnotes.
*/
public boolean isFootnoteAllowed() {
return _isFootnoteAllowed;
}

/**
* See {@link #isFootnoteAllowed()}.
*/
public void setFootnoteAllowed(boolean allowed) {
this._isFootnoteAllowed = allowed;
}

/**
* See {@link #isInFloatBottom()}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ enum LogMessageId0Param implements LogMessageId {

MATCH_TRYING_TO_SET_MORE_THAN_ONE_PSEUDO_ELEMENT(XRLog.MATCH, "Trying to set more than one pseudo-element"),

GENERAL_NO_FOOTNOTES_INSIDE_FOOTNOTES(XRLog.GENERAL, "Footnotes inside footnotes are not supported"),
GENERAL_IMPORT_FONT_FACE_RULES_HAS_NOT_BEEN_CALLED(XRLog.GENERAL, "importFontFaceRules has not been called for this pdf transcoder"),
GENERAL_PDF_ACCESSIBILITY_NO_ALT_ATTRIBUTE_PROVIDED_FOR_IMAGE(XRLog.GENERAL, "No alt attribute provided for image/replaced in PDF/UA document."),
GENERAL_PDF_SPECIFIED_FONTS_DONT_CONTAIN_A_SPACE_CHARACTER(XRLog.GENERAL, "Specified fonts don't contain a space character!"),
Expand All @@ -56,6 +57,7 @@ enum LogMessageId0Param implements LogMessageId {
EXCEPTION_COULD_NOT_READ_PDF_AS_SRC_FOR_IMG(XRLog.EXCEPTION, "Could not read pdf passed as src for img element!"),
EXCEPTION_COULD_NOT_PARSE_DEFAULT_STYLESHEET(XRLog.EXCEPTION, "Could not parse default stylesheet"),
EXCEPTION_SELECTOR_BAD_SIBLING_AXIS(XRLog.EXCEPTION, "Bad sibling axis");


private final String where;
private final String messageFormat;
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<html>
<head>
<style>
@page {
size: 300px 300px;
}
html {
orphans: 0;
widows: 0;
}
body {
font-size: 20px;
}
.footnote {
color: green;
float: footnote;
}
::footnote-call {
counter-increment: footnote;
content: " [fn " counter(footnote) "] ";
}
::footnote-marker {
content: "fn. " counter(footnote);
}
</style>
</head>
<body>
This is some in-flow content.

<div class="footnote">
This is a footnote with a footnote inside.
<div class="footnote">
This is the inner footnote.
</div>
And another:
<div class="footnote">
Another fn inside fn.
<div class="footnote">
Grandchild footnote in footnote.
</div>
</div>
</div>

Even more in-flow content.

<div class="footnote">
Second footnote.
</div>

End of document.

</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,13 @@ public void testIssue364NonPaginatedTable() throws IOException {
assertTrue(vt.runTest("issue-364-non-paginated-table"));
}

/**
* Tests that footnotes inside footnotes are treated as non-footnote content
* and do not cause infinite loop, stack overflow, OOM, etc.
*/
@Test
public void testIssue364FootnoteInsideFootnote() throws IOException {
assertTrue(vt.runTest("issue-364-footnote-inside-footnote"));
}

}

0 comments on commit f329721

Please sign in to comment.