Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strange behaviors of block elements in paged-footnotes #156

Closed
atusy opened this issue Dec 14, 2019 · 4 comments
Closed

Strange behaviors of block elements in paged-footnotes #156

atusy opened this issue Dec 14, 2019 · 4 comments

Comments

@atusy
Copy link
Contributor

@atusy atusy commented Dec 14, 2019

For example, a footnote with bullet list in html is rendered in the body, and leaves the empty footnote at the bottom of the page.
A footnote with bullet list in markdown is collapsed.

They work fine when paged-footnotes: false.

Source Rmd

---
output: pagedown::html_paged
paged-footnotes: true
---

A sentence.[^html]

Another sentence.[^md]



[^html]: <ul><li>a</li><li>b</li></ul>

[^md]:
    - a
    - b

Output

image

@RLesur

This comment has been minimized.

Copy link
Collaborator

@RLesur RLesur commented Dec 14, 2019

paged-footnotes: true only supports inline footnotes.
The reason comes the W3C CSS Generated Content for Paged Media Module Working Draft. As you can see here, these specifications are only defined for inline elements. There is no support for block elements like lists.

In pagedown, paged-footnotes: true involves this Pandoc filter https://github.com/rstudio/pagedown/blob/master/inst/resources/lua/footnotes.lua which turns block elements into inline elements. The main function used in this filter is pandoc.utils.blocks_to_inlines(). The second part of paged-footnotes: true is this Paged.js handler:

// Footnotes support
Paged.registerHandlers(class extends Paged.Handler {
constructor(chunker, polisher, caller) {
super(chunker, polisher, caller);
this.splittedParagraphRefs = [];
}
beforeParsed(content) {
// remove footnotes in toc, lof, lot
// see https://github.com/rstudio/pagedown/issues/54
let removeThese = content.querySelectorAll('.toc .footnote, .lof .footnote, .lot .footnote');
for (const el of removeThese) {
el.remove();
}
let footnotes = content.querySelectorAll('.footnote');
for (let footnote of footnotes) {
let parentElement = footnote.parentElement;
let footnoteCall = document.createElement('a');
let footnoteNumber = footnote.dataset.pagedownFootnoteNumber;
footnoteCall.className = 'footnote-ref'; // same class as Pandoc
footnoteCall.setAttribute('id', 'fnref' + footnoteNumber); // same notation as Pandoc
footnoteCall.setAttribute('href', '#' + footnote.id);
footnoteCall.innerHTML = '<sup>' + footnoteNumber +'</sup>';
parentElement.insertBefore(footnoteCall, footnote);
// Here comes a hack. Fortunately, it works with Chrome and FF.
let handler = document.createElement('p');
handler.className = 'footnoteHandler';
parentElement.insertBefore(handler, footnote);
handler.appendChild(footnote);
handler.style.display = 'inline-block';
handler.style.width = '100%';
handler.style.float = 'right';
handler.style.pageBreakInside = 'avoid';
}
}
afterPageLayout(pageFragment, page, breakToken) {
function hasItemParent(node) {
if (node.parentElement === null) {
return false;
} else {
if (node.parentElement.tagName === 'LI') {
return true;
} else {
return hasItemParent(node.parentElement);
}
}
}
// If a li item is broken, we store the reference of the p child element
// see https://github.com/rstudio/pagedown/issues/23#issue-376548000
if (breakToken !== undefined) {
if (breakToken.node.nodeName === "#text" && hasItemParent(breakToken.node)) {
this.splittedParagraphRefs.push(breakToken.node.parentElement.dataset.ref);
}
}
}
afterRendered(pages) {
for (let page of pages) {
const footnotes = page.element.querySelectorAll('.footnote');
if (footnotes.length === 0) {
continue;
}
const pageContent = page.element.querySelector('.pagedjs_page_content');
let hr = document.createElement('hr');
let footnoteArea = document.createElement('div');
pageContent.style.display = 'flex';
pageContent.style.flexDirection = 'column';
hr.className = 'footnote-break';
hr.style.marginTop = 'auto';
hr.style.marginBottom = 0;
hr.style.marginLeft = 0;
hr.style.marginRight = 'auto';
pageContent.appendChild(hr);
footnoteArea.className = 'footnote-area';
pageContent.appendChild(footnoteArea);
for (let footnote of footnotes) {
let handler = footnote.parentElement;
footnoteArea.appendChild(footnote);
handler.parentNode.removeChild(handler);
footnote.innerHTML = '<sup>' + footnote.dataset.pagedownFootnoteNumber + '</sup>' + footnote.innerHTML;
footnote.style.fontSize = 'x-small';
footnote.style.marginTop = 0;
footnote.style.marginBottom = 0;
footnote.style.paddingTop = 0;
footnote.style.paddingBottom = 0;
footnote.style.display = 'block';
}
}
for (let ref of this.splittedParagraphRefs) {
let paragraphFirstPage = document.querySelector('[data-split-to="' + ref + '"]');
// We test whether the paragraph is empty
// see https://github.com/rstudio/pagedown/issues/23#issue-376548000
if (paragraphFirstPage.innerText === "") {
paragraphFirstPage.parentElement.style.display = "none";
let paragraphSecondPage = document.querySelector('[data-split-from="' + ref + '"]');
paragraphSecondPage.parentElement.style.setProperty('list-style', 'inherit', 'important');
}
}
}
});

In the example, the markdown footnote is collapsed because the list (which is a block element) is turned into an inline span (this is the job of the Pandoc filter).

For the HTML footnote, the reason is different. Pandoc parses this markup as raw HTML as you can see in the JSON serialization of the AST:

{"blocks":[{"t":"Para","c":[{"t":"Str","c":"A"},{"t":"Space"},{"t":"Str","c":"sentence."},{"t":"Span","c":[["fn1",["footnote"],[["data-pagedown-footnote-number","1"],["style","white-space: pre-line;"]]],[{"t":"RawInline","c":["html","<ul>"]},{"t":"Str","c":"\n"},{"t":"RawInline","c":["html","<li>"]},{"t":"Str","c":"\na\n"},{"t":"RawInline","c":["html","</li>"]},{"t":"Str","c":"\n"},{"t":"RawInline","c":["html","<li>"]},{"t":"Str","c":"\nb\n"},{"t":"RawInline","c":["html","</li>"]},{"t":"Str","c":"\n"},{"t":"RawInline","c":["html","</ul>"]}]]}]},{"t":"Para","c":[{"t":"Str","c":"Another"},{"t":"Space"},{"t":"Str","c":"sentence."},{"t":"Span","c":[["fn2",["footnote"],[["data-pagedown-footnote-number","2"],["style","white-space: pre-line;"]]],[{"t":"Str","c":"ab"}]]}]}],"pandoc-api-version":[1,17,5,4],"meta":{"newpage_html_class":{"t":"MetaString","c":"page-break-after"},"paged-footnotes":{"t":"MetaBool","c":true},"output":{"t":"MetaMap","c":{"pagedown::html_paged":{"t":"MetaMap","c":{"keep_md":{"t":"MetaBool","c":true}}}}}}}

Pandoc's HTML writer takes this raw HTML asis. So, the generated markup is:

<p>A sentence.<span id="fn1" class="footnote" style="white-space: pre-line;" data-pagedown-footnote-number="1"><ul>
<li>
a
</li>
<li>
b
</li>
</ul></span></p>

Having a block element like <ul></ul> in a <p> element is illegal: this markup is invalid.
Browsers are quite flexible: they add a missing closing </p> tag just before the opening <ul> tag and by consequence a closing </span> tag too. So, the footnote is "reduced" to this void span element:

<span id="fn1" class="footnote" style="white-space: pre-line;" data-pagedown-footnote-number="1"></span>

I think I will add in the documentation that paged-footnotes: true only works for inline footnotes.

@atusy

This comment has been minimized.

Copy link
Contributor Author

@atusy atusy commented Dec 15, 2019

Thanks a lot for the explanation, and for the plan to update the documentation!!
It makes sense.

@atusy atusy changed the title Strange behaviors of multiline paragraphs in paged-foornotes Strange behaviors of block elements in paged-foornotes Dec 15, 2019
@RLesur RLesur changed the title Strange behaviors of block elements in paged-foornotes Strange behaviors of block elements in paged-footnotes Dec 16, 2019
@RLesur RLesur closed this in 3f765d9 Mar 17, 2020
@RLesur

This comment has been minimized.

Copy link
Collaborator

@RLesur RLesur commented Mar 17, 2020

@atusy Thanks a lot for the report! I've updated the documentation in 3f765d9.

@atusy

This comment has been minimized.

Copy link
Contributor Author

@atusy atusy commented Mar 17, 2020

Great! Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.