Skip to content

Commit 2643125

Browse files
authored
Merge pull request #20951 from calixteman/bug2021392
Correctly scroll the search result in the viewport with rotated pdfs (bug 2021392)
2 parents 1756b48 + 2436593 commit 2643125

7 files changed

Lines changed: 67 additions & 45 deletions

File tree

test/integration/find_spec.mjs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,56 @@ describe("find bar", () => {
177177
);
178178
});
179179
});
180+
181+
describe("Check that the search results are correctly visible in rotated PDFs (bug 2021392)", () => {
182+
let pages;
183+
184+
beforeEach(async () => {
185+
pages = await loadAndWait(
186+
"hello_world_rotated.pdf",
187+
".textLayer",
188+
"page-fit"
189+
);
190+
});
191+
192+
afterEach(async () => {
193+
await closePages(pages);
194+
});
195+
196+
it("must scroll each match into the viewport when navigating search results", async () => {
197+
await Promise.all(
198+
pages.map(async ([browserName, page]) => {
199+
await page.click("#viewFindButton");
200+
await page.waitForSelector("#findInput", { visible: true });
201+
await page.type("#findInput", "hello");
202+
await page.waitForSelector("#findInput[data-status='']");
203+
204+
for (let i = 0; i < 5; i++) {
205+
if (i > 0) {
206+
await page.click("#findNextButton");
207+
await page.waitForSelector("#findInput[data-status='']");
208+
}
209+
210+
// Verify we are on the expected match number.
211+
const resultElement =
212+
await page.waitForSelector("#findResultsCount");
213+
const resultText = await resultElement.evaluate(
214+
el => el.textContent
215+
);
216+
expect(resultText)
217+
.withContext(`In ${browserName}, match ${i + 1}`)
218+
.toEqual(`${FSI}${i + 1}${PDI} of ${FSI}5${PDI} matches`);
219+
220+
// The selected highlight must be visible in the viewport.
221+
const selected = await page.waitForSelector(
222+
".textLayer .highlight.selected"
223+
);
224+
expect(await selected.isIntersectingViewport())
225+
.withContext(`In ${browserName}, match ${i + 1}`)
226+
.toBeTrue();
227+
}
228+
})
229+
);
230+
});
231+
});
180232
});

test/pdfs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,3 +891,4 @@
891891
!extractPages_null_in_array.pdf
892892
!issue20930.pdf
893893
!text_rise_eol_bug.pdf
894+
!hello_world_rotated.pdf

test/pdfs/hello_world_rotated.pdf

1.29 KB
Binary file not shown.

web/pdf_find_controller.js

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
/** @typedef {import("./event_utils").EventBus} EventBus */
1818
/** @typedef {import("./pdf_link_service.js").PDFLinkService} PDFLinkService */
1919

20-
import { binarySearchFirstItem, scrollIntoView } from "./ui_utils.js";
2120
import { getCharacterType, getNormalizeWithNFKC } from "./pdf_find_utils.js";
21+
import { binarySearchFirstItem } from "./ui_utils.js";
2222

2323
const FindState = {
2424
FOUND: 0,
@@ -28,7 +28,6 @@ const FindState = {
2828
};
2929

3030
const FIND_TIMEOUT = 250; // ms
31-
const MATCH_SCROLL_OFFSET_TOP = -50; // px
3231

3332
const CHARACTERS_TO_NORMALIZE = {
3433
"\u2010": "-", // Hyphen
@@ -553,7 +552,6 @@ class PDFFindController {
553552
/**
554553
* @typedef {Object} PDFFindControllerScrollMatchIntoViewParams
555554
* @property {HTMLElement} element
556-
* @property {number} selectedLeft
557555
* @property {number} pageIndex
558556
* @property {number} matchIndex
559557
*/
@@ -562,12 +560,7 @@ class PDFFindController {
562560
* Scroll the current match into view.
563561
* @param {PDFFindControllerScrollMatchIntoViewParams}
564562
*/
565-
scrollMatchIntoView({
566-
element = null,
567-
selectedLeft = 0,
568-
pageIndex = -1,
569-
matchIndex = -1,
570-
}) {
563+
scrollMatchIntoView({ element = null, pageIndex = -1, matchIndex = -1 }) {
571564
if (!this._scrollMatches || !element) {
572565
return;
573566
} else if (matchIndex === -1 || matchIndex !== this._selected.matchIdx) {
@@ -576,11 +569,7 @@ class PDFFindController {
576569
return;
577570
}
578571
this._scrollMatches = false; // Ensure that scrolling only happens once.
579-
const spot = {
580-
top: MATCH_SCROLL_OFFSET_TOP,
581-
left: selectedLeft,
582-
};
583-
scrollIntoView(element, spot, /* scrollMatches = */ true);
572+
element.scrollIntoView({ block: "start", inline: "center" });
584573
}
585574

586575
#reset() {

web/text_highlighter.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,9 @@ class TextHighlighter {
195195
div.append(span);
196196

197197
if (className.includes("selected")) {
198-
const { left } = span.getClientRects()[0];
199-
const parentLeft = div.getBoundingClientRect().left;
200-
return left - parentLeft;
198+
return span;
201199
}
202-
return 0;
200+
return null;
203201
}
204202

205203
div.append(node);
@@ -233,7 +231,7 @@ class TextHighlighter {
233231
const end = match.end;
234232
const isSelected = isSelectedPage && i === selectedMatchIdx;
235233
const highlightSuffix = isSelected ? " selected" : "";
236-
let selectedLeft = 0;
234+
let selectedSpan = null;
237235

238236
// Match inside new div.
239237
if (!prevEnd || begin.divIdx !== prevEnd.divIdx) {
@@ -248,14 +246,14 @@ class TextHighlighter {
248246
}
249247

250248
if (begin.divIdx === end.divIdx) {
251-
selectedLeft = appendTextToDiv(
249+
selectedSpan = appendTextToDiv(
252250
begin.divIdx,
253251
begin.offset,
254252
end.offset,
255253
"highlight" + highlightSuffix
256254
);
257255
} else {
258-
selectedLeft = appendTextToDiv(
256+
selectedSpan = appendTextToDiv(
259257
begin.divIdx,
260258
begin.offset,
261259
infinity.offset,
@@ -271,8 +269,7 @@ class TextHighlighter {
271269
if (isSelected) {
272270
// Attempt to scroll the selected match into view.
273271
findController.scrollMatchIntoView({
274-
element: textDivs[begin.divIdx],
275-
selectedLeft,
272+
element: selectedSpan,
276273
pageIndex: pageIdx,
277274
matchIndex: selectedMatchIdx,
278275
});

web/text_layer_builder.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
&.selected {
111111
background-color: var(--highlight-selected-bg-color);
112112
backdrop-filter: var(--highlight-selected-backdrop-filter);
113+
scroll-margin-top: 50px;
113114
}
114115
}
115116

web/ui_utils.js

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
* limitations under the License.
1414
*/
1515

16-
import { MathClamp } from "pdfjs-lib";
17-
1816
const DEFAULT_SCALE_VALUE = "auto";
1917
const DEFAULT_SCALE = 1.0;
2018
const DEFAULT_SCALE_DELTA = 1.1;
@@ -78,11 +76,8 @@ const AutoPrintRegExp = /\bprint\s*\(/;
7876
* specifying the offset from the top left edge.
7977
* @param {number} [spot.left]
8078
* @param {number} [spot.top]
81-
* @param {boolean} [scrollMatches] - When scrolling search results into view,
82-
* ignore elements that either: Contains marked content identifiers,
83-
* or have the CSS-rule `overflow: hidden;` set. The default value is `false`.
8479
*/
85-
function scrollIntoView(element, spot, scrollMatches = false) {
80+
function scrollIntoView(element, spot) {
8681
// Assuming offsetParent is available (it's not available when viewer is in
8782
// hidden iframe or object). We have to scroll: if the offsetParent is not set
8883
// producing the error. See also animationStarted.
@@ -94,11 +89,8 @@ function scrollIntoView(element, spot, scrollMatches = false) {
9489
let offsetY = element.offsetTop + element.clientTop;
9590
let offsetX = element.offsetLeft + element.clientLeft;
9691
while (
97-
(parent.clientHeight === parent.scrollHeight &&
98-
parent.clientWidth === parent.scrollWidth) ||
99-
(scrollMatches &&
100-
(parent.classList.contains("markedContent") ||
101-
getComputedStyle(parent).overflow === "hidden"))
92+
parent.clientHeight === parent.scrollHeight &&
93+
parent.clientWidth === parent.scrollWidth
10294
) {
10395
offsetY += parent.offsetTop;
10496
offsetX += parent.offsetLeft;
@@ -113,17 +105,7 @@ function scrollIntoView(element, spot, scrollMatches = false) {
113105
offsetY += spot.top;
114106
}
115107
if (spot.left !== undefined) {
116-
if (scrollMatches) {
117-
const elementWidth = element.getBoundingClientRect().width;
118-
const padding = MathClamp(
119-
(parent.clientWidth - elementWidth) / 2,
120-
20,
121-
400
122-
);
123-
offsetX += spot.left - padding;
124-
} else {
125-
offsetX += spot.left;
126-
}
108+
offsetX += spot.left;
127109
parent.scrollLeft = offsetX;
128110
}
129111
}

0 commit comments

Comments
 (0)