-
-
Notifications
You must be signed in to change notification settings - Fork 54
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
Bug: vivliostyle doesn't call POST_LAYOUT_BLOCK
for div elements
#1429
Comments
Workaround: put your note box inside in svg element: <svg width="100%" height="100%" data-defer="until-fit">
<foreignobject width="100%" height="100%" data-fit-height="">
<!-- your note html here -->
</foreignobject>
</svg> and a little fix in the deferring code: let deferred = [];
let defer_types = [
"until-fit",
]
Plugin.registerHook(
"POST_LAYOUT_BLOCK",
(nodeContext, checkpoints, column) => {
let inner;
if(nodeContext.viewNode.tagName == "svg" && (inner = nodeContext.viewNode.querySelector("foreignobject[data-fit-height]"))){
nodeContext.viewNode.setAttribute("height", inner.firstElementChild.scrollHeight);
}
let remaining_space = (column.afterEdge - column.beforeEdge) - column.element.firstChild.scrollHeight;
if(!nodeContext.viewNode.hasAttribute("data-viv-box-break")){
for(let i = 0; i < deferred.length; i++){
if(!nodeContext.viewNode.parentElement.matches(`[data-adapt-eloff="${deferred[i].parentEloff}"]`)) continue; // If it's not the same parent: don't try to insert
if((deferred[i].defer_type = "until-fit" &&
remaining_space > deferred[i].savedHeight) ||
!nodeContext.sourceNode.nextSibling // If it doesn't fit but this element is the last in its parent: don't defer anymore
){
// insert the deferred element
nodeContext.viewNode.before(deferred[i]);
remaining_space -= deferred[i].savedHeight;
deferred.splice(i, 1);
i--;
}
}
}
let defer_type = 0;
if(!nodeContext.viewNode.hasAttribute("data-defer")) return;
else{
defer_type = nodeContext.viewNode.getAttribute("data-defer");
}
if(defer_type == "until-fit"){
if((column.element.firstChild.scrollHeight < (column.afterEdge - column.beforeEdge)) || !nodeContext.sourceNode.nextSibling) return; // Don't defer if the element fits or if it doesn't have any other siblings
// else
let clone = nodeContext.viewNode.cloneNode(true);
clone.savedHeight = nodeContext.viewNode.scrollHeight;
clone.deferType = defer_type;
clone.parentEloff = nodeContext.viewNode.parentElement.getAttribute("data-adapt-eloff");
clone.removeAttribute("data-defer");
deferred.push(clone);
nodeContext.viewNode.style.display = "none";
}
},
); |
Another issue: the hook is not called in order, and I need to make sure the deferred box is after the element in the source HTML |
Since the hook isn't called for |
If someone else needs to defer nonbreaakable blocks like me, here is the last version that works without bugs untill now: function adaptSVG(el, inner){
el.setAttribute("height", inner.firstElementChild.scrollHeight);
}
Plugin.registerHook(
"POST_LAYOUT_BLOCK",
(nodeContext, checkpoints, column) => {
let inner;
if(nodeContext.viewNode.tagName == "svg" && (inner = nodeContext.viewNode.querySelector("foreignobject[data-fit-height]"))){
adaptSVG(nodeContext.viewNode, inner);
}
let remaining_space = (column.pageFloatLayoutContext.parent.container.height) - column.element.firstChild.scrollHeight;
let position = nodeContext.sourceNode;
if(nodeContext.viewNode.matches("div[data-defer-position]")){
for(let i = 0; i < deferred.length; i++){
if(!nodeContext.viewNode.parentElement.matches(`[data-adapt-eloff="${deferred[i].parentEloff}"]`) ||
!(parseInt(nodeContext.viewNode.getAttribute("data-adapt-eloff")) > parseInt(deferred[i].eloff)))
continue; // If it's not the same parent or an element before the deferred: don't try to insert
if((deferred[i].defer_type = "until-fit" &&
remaining_space > deferred[i].savedHeight) ||
!nodeContext.sourceNode.matches(":has(~[data-defer-position])") // If it doesn't fit but this position is the last in its parent: don't defer anymore
){
// insert the deferred element
position.after(deferred[i]);
position = deferred[i];
remaining_space -= deferred[i].savedHeight;
deferred.splice(i, 1);
i--;
}
}
}
let defer_type;
if(!nodeContext.viewNode.hasAttribute("data-defer")) return;
else{
defer_type = nodeContext.viewNode.getAttribute("data-defer");
}
if(defer_type == "until-fit"){
if((column.element.firstChild.scrollHeight < (column.afterEdge - column.beforeEdge)) || !nodeContext.sourceNode.matches(":has(~[data-defer-position])")) return; // Don't defer if the element fits or if it doesn't have any other defer-position siblings
// else
let clone = nodeContext.viewNode.cloneNode(true);
clone.savedHeight = nodeContext.viewNode.scrollHeight;
clone.deferType = defer_type;
clone.eloff = nodeContext.viewNode.getAttribute("data-adapt-eloff");
clone.parentEloff = nodeContext.viewNode.parentElement.getAttribute("data-adapt-eloff");
clone.removeAttribute("data-defer");
deferred.push(clone);
nodeContext.viewNode.style.display = "none";
}
},
); You will then use the following code to wrap the deferred blocks and insert deferring positions: // create html form string
function CS(html, selector = ":first-child"){
const temp = document.createElement('template');
temp.innerHTML = html;
return temp.content.querySelector(selector);
}
function Q(el, selector) {return el.querySelector(selector)}
function QA(el, selector) {return el.querySelectorAll(selector)}
function FillWithDeferPositions(el){
for(let i = 0; i < el.childNodes.length; i++){
el.childNodes[i].after(CS("<div data-defer-position>"));
i++;
}
}
function HandleDeferredBoxes(parent){
QA(parent, ".defer").forEach((el)=>{
let frame = CS(`<svg width="100%" height="100%" data-defer="until-fit"><foreignObject width="100%" height="100%" data-fit-height=""></foreignObject></svg>`);
el.after(frame);
Q(frame, "foreignObject").appendChild(el);
if(!Q(frame.parentElement, "[data-defer-position]")){
FillWithDeferPositions(frame.parentElement);
}
})
} You can customize this more by, for example, not inserting deferring positions after headers. const NoDeferPositions = "h1,h2,h3,h4,h5,h6";
function FillWithDeferPositions(el){
for(let i = 0; i < el.childNodes.length; i++){
if(!el.childNodes[i].matches(NoDeferPositions)){
el.childNodes[i].after(CS("<div data-defer-position>"));
i++;
}
}
} |
I'm trying to implement and automatic deferring plugin, which defers the layout of some images or boxes until it fits in the column to avoid breaking it and also avoid wasted white space. I did this:
Now images are deferred sucessfully.
Before:
After:
For a note box: the
POST_LAYOUT_BLOCK
hook is not called, and the note box is not deferred:The text was updated successfully, but these errors were encountered: