Skip to content

fixed-layout method for obtaining current render details #62

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Protected
Copy link
Contributor

Something like this is necessary for adding overlays or decorations to a viewer implementations that are aware of the proportions of the spread devoted to each page when pages have different sizes and shapes. The load event is insufficient because it's not spread-aware. I'm going with this for now, but I'm aware you may prefer a different syntax, possibly another event.

@johnfactotum
Copy link
Owner

Well, in Foliate I'm using document.defaultView.frameElement and calling getBoundingClientRect() as well as manually parsing the transform property from getComputedStyle() on the frame element. Not much of an interface but does work now for the purpose of overlaying stuff. Perhaps something like this could be a separate module that can be re-used in both renderers.

@Protected
Copy link
Contributor Author

I'm not sure how you propose to isolate something like this into another module; don't the details of the rendering depend entirely on the renderer?

@johnfactotum
Copy link
Owner

I mean like a small utility that can be used for transforming rects in iframes into rects on the parent page. Anything that uses iframes can then use such a module. So either the user of the renderers would use it directly, or the renderers can use to implement a method for mapping rects. Well, it'd mainly be useful for overlaying selection widgets. But one could also use it for getting the page dimensions for fixed-layout books.

An API for getting the "page" dimensions is useful for reflowable books, too. So that's another thing to consider. But probably, whether it's reflowable or fixed layout, page dimensions would probably fall in the scope of an overlay that is positioned and partly managed by the renderer, like how the overlayer works currently in the paginator. That's going to be a lot better than positioning overlaid elements separately in terms of animating things.

As for the event, I think the current expectation is that one'd use the relocate event to react to rerenders.

@Protected
Copy link
Contributor Author

I hacked the sort of thing you're talking about on paginator using a fake overlayer, which has the benefit of not having to change anything inside foliate:

let loadedView, measureRectangle;

view.renderer.addEventListener("create-overlayer", (e) => {
    loadedView = e.detail.doc;

    const measureRectangle = document.createElement("div");
    measureRectangle.style.position = "absolute";
    e.detail.attach({element: measureRectangle, redraw: () => {}});
    measureRectangle = measureRectangle;
});

externalElement.addEventListener("click", (mouseEvent) => {
    const contentOffset = measureRectangle.getClientRects()[0];
    const inlineTarget = loadedView.elementFromPoint(mouseEvent.x - contentOffset.x, mouseEvent.y - contentOffset.y);

    // ...
});

Once you include a better system maybe I can use that for both cases!

@johnfactotum
Copy link
Owner

johnfactotum commented May 30, 2025

As I said, you can do it now by getting the iframe element yourself from the document object, which you can get from the load event or the relocate event (from the range).

Now, it's true that the iframe is at least in theory something that is internal to the renderer implementation, but I think it's a bit of a gray area... It's not exactly a secret and if you're not changing the iframe, but merely measuring it, it's probably fine. But this is why I feel that a method provided by the renderer will probably be best.

@Protected
Copy link
Contributor Author

I agree with a method provided by the renderer, I just don't see that there's enough to abstract into a separate module.

@Protected
Copy link
Contributor Author

Protected commented May 30, 2025

I'm going to need more explicit guidance here. I haven't had any issues getting the document inside the iframe, but that's not enough. What I need in the example above is to reliably get the computed offset between foliate-view (or the renderer's) anchor point and the actual document's position, which is not a null vector in the reflowable renderer under typical circumstances. Otherwise it's impossible to externally "know" where things are since we only have the position of foliate-view and the renderer, but page document elements are positioned relative to their own document.

In order to obtain this without changes to foliate, I need access to the DOM somewhere around the #content element, or in other words, outside the iframe, but inside the renderer's shadow root.

this.#view.element would be perfect but this is never exposed directly. Only overlayers are exposed, which makes them usable for this purpose, since they are in the shadow tree but are not isolated inside another shadow tree or iframe.

You could add a get viewOffset() to obtain this directly from this.#view.element instead, like you have get viewSize().-

@johnfactotum
Copy link
Owner

You don't need to know about anything outside the iframe. You just need to get the client rect of the iframe itself, with which you can offset client rects coming from within the iframe.

The exact same calculation needs to be performed, for example, when using Epub.js (futurepress/epub.js#809), or anywhere, in general, where you need to position anything on a parent page relative to something in an iframe. So it's actually something of a DOM utility function that is generally useful, not limited even to ebook rendering.

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

Successfully merging this pull request may close these issues.

2 participants