Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ The release run heads these entries with the version and opens a fresh

## Unreleased

- A fitted or zoomed view scales its text with the page in WebKit, where the
type used to stay at its unscaled size. `HtmlViewportMode::fit_width_by_view`
is usable on iOS. Closes #761.

- A PDF `gs` applies the `/ExtGState` stroke parameters `/LW`, `/LC`, `/LJ`,
`/ML` and `/D`. A producer that sets the line width only there β€” Canva does β€”
used to have every stroke drawn at the initial width of 1.
Expand Down
6 changes: 4 additions & 2 deletions src/odr/internal/html/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,10 @@ void html::write_zoom_style(HtmlWriter &out, const HtmlConfig &config,
out.out() << "body{zoom:" << number(*zoom) << "}";
}

// paper has its own geometry; beats the script's inline zoom
out.out() << "@media print{:root{--odr-zoom:1!important}"
// paper has its own geometry; beats the script's inline zoom and adjustment
out.out() << "@media print{:root{--odr-zoom:1!important;"
"-webkit-text-size-adjust:100%!important;"
"text-size-adjust:100%!important}"
"body{zoom:1!important}}";

out.write_header_style_end();
Expand Down
71 changes: 66 additions & 5 deletions src/odr/internal/html/frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,13 +372,72 @@ constexpr std::string_view viewport_js = R"js(
return pinned !== null ? pinned : fit;
}

// Webkit divides a stated `text-size-adjust` by the css zoom, so restating
// the zoom as the percentage holds the type where its box is. Blink reads the
// same percentage as a plain multiplier, hence the probe below.
var adjustsText = false;
var adjusted = "";

function textAdjust(value) {
adjusted = value;
root.style.setProperty("-webkit-text-size-adjust", value);
root.style.setProperty("text-size-adjust", value);
}

// A run against a stated length, so no rect convention enters. True in webkit
// and in the engines that ignore the property; false where it scales the text
// a second time.
function textAdjustHolds() {
var ruler = document.createElement("div");
ruler.style.cssText =
"position:absolute;top:0;left:0;width:400px;height:0;overflow:hidden";
var run = document.createElement("span");
run.style.cssText = "font:100px/1 monospace;white-space:pre";
run.textContent = "MMMMMMMMMM";
ruler.appendChild(run);
body.appendChild(ruler);

function ratio() {
var length = ruler.getBoundingClientRect().width;
return length ? run.getBoundingClientRect().width / length : 0;
}

var zoom = body.style.zoom;
body.style.zoom = "1";
var unzoomed = ratio();
body.style.zoom = "0.5";
textAdjust("50%");
var zoomed = ratio();
textAdjust("");
body.style.zoom = zoom;
body.removeChild(ruler);

return unzoomed > 0 && Math.abs(zoomed - unzoomed) < unzoomed / 50;
}

function zoomBody(zoom) {
body.style.zoom = zoom;
root.style.setProperty("--odr-zoom", zoom);
if (adjustsText) {
textAdjust(zoom * 100 + "%");
}
}

// The natural width of what the body holds, measured unscaled.
function contentWidth() {
var zoom = body.style.zoom;
var adjust = adjusted;
// `1`, not empty: a stylesheet may carry a zoom of its own to fall back to.
body.style.zoom = "1";
// the percentage compensates a zoom this measurement removes
if (adjust) {
textAdjust("100%");
}
var natural = body.scrollWidth;
body.style.zoom = zoom;
if (adjust) {
textAdjust(adjust);
}
return natural;
}

Expand Down Expand Up @@ -527,9 +586,7 @@ constexpr std::string_view viewport_js = R"js(
}

function apply(target) {
var zoom = applied();
body.style.zoom = zoom;
root.style.setProperty("--odr-zoom", zoom);
zoomBody(applied());

settle(target);
notify();
Expand Down Expand Up @@ -604,11 +661,15 @@ constexpr std::string_view viewport_js = R"js(
return applied();
};

// Before the first measurement, so every one of them reads the same state.
adjustsText = textAdjustHolds();
width = root.clientWidth;
if (measures && pinned === null) {
fit = measureFit();
body.style.zoom = fit;
root.style.setProperty("--odr-zoom", fit);
}
// Restated inline so a css-stated zoom carries the adjustment with it.
if (applied() !== 1) {
zoomBody(applied());
}
remember();

Expand Down
6 changes: 6 additions & 0 deletions test/browser/viewport/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,9 @@ each printing its own verdict.
Keep the tab on screen: the browser throttles `requestAnimationFrame` in a
window that is not. The harness dispatches the scroll and resize events itself,
but not the settling frames that follow them.

Two webkit rules nothing here reproduces, both about type under an applied
`zoom` (#761): it holds the text at its unscaled size unless the zoom is stated
back as `text-size-adjust`, by a factor its cluster heuristics decide, and it
draws no text below 9px. Neither shows on 400 identical lines β€” the factor needs
the shape of a real render. The oracle is a document served to a real webkit.
2 changes: 1 addition & 1 deletion test/data.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ odr_test_data(
odr_test_data(
PATH "reference-output/odr-private"
URL "https://github.com/opendocument-app/OpenDocument.test-private.output.git"
REVISION "2f5964b9b8a5172aac2e4f0d893c6ccf625f8857")
REVISION "bcea37b997a045387b7069412ba668fa44a7e26b")
4 changes: 3 additions & 1 deletion test/src/internal/html/common_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ std::string emit_zoom(const HtmlConfig &config, const ihtml::WidthFit fits,

/// Written whatever the zoom is, so every case states it.
constexpr const char *print =
"@media print{:root{--odr-zoom:1!important}body{zoom:1!important}}";
"@media print{:root{--odr-zoom:1!important;"
"-webkit-text-size-adjust:100%!important;text-size-adjust:100%!important}"
"body{zoom:1!important}}";

std::string styled(const std::string &css) {
return "<style>" + css + print + "</style>";
Expand Down
Loading