From 89724d321d26186d13b02e63c354fc1425ba19d4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 20 Jun 2012 10:14:55 +0530 Subject: [PATCH] Proper CFI calculation for paged mode --- src/calibre/ebooks/oeb/display/cfi.coffee | 74 +++++++++++---------- src/calibre/ebooks/oeb/display/paged.coffee | 44 +++++++++++- src/calibre/gui2/viewer/position.py | 7 +- 3 files changed, 83 insertions(+), 42 deletions(-) diff --git a/src/calibre/ebooks/oeb/display/cfi.coffee b/src/calibre/ebooks/oeb/display/cfi.coffee index 2af3a218ed..b0fd6aba93 100644 --- a/src/calibre/ebooks/oeb/display/cfi.coffee +++ b/src/calibre/ebooks/oeb/display/cfi.coffee @@ -572,6 +572,44 @@ class CanonicalFragmentIdentifier null # }}} + at_point: (ox, oy) -> + # The CFI at the specified point. Different to at() in that this method + # returns null if there is an error, and also calculates a point from + # the CFI and returns null if the calculated point is far from the + # original point. + + dist = (p1, p2) -> + Math.sqrt(Math.pow(p1[0]-p2[0], 2), Math.pow(p1[1]-p2[1], 2)) + + try + cfi = window.cfi.at(ox, oy) + point = window.cfi.point(cfi) + catch err + cfi = null + + + if cfi + if point.range != null + r = point.range + rect = r.getClientRects()[0] + + x = (point.a*rect.left + (1-point.a)*rect.right) + y = (rect.top + rect.bottom)/2 + [x, y] = viewport_to_document(x, y, r.startContainer.ownerDocument) + else + node = point.node + r = node.getBoundingClientRect() + [x, y] = viewport_to_document(r.left, r.top, node.ownerDocument) + if typeof(point.x) == 'number' and node.offsetWidth + x += (point.x*node.offsetWidth)/100 + if typeof(point.y) == 'number' and node.offsetHeight + y += (point.y*node.offsetHeight)/100 + + if dist(viewport_to_document(ox, oy), [x, y]) > 50 + cfi = null + + return cfi + at_current: () -> # {{{ [winx, winy] = window_scroll_pos() [winw, winh] = [window.innerWidth, window.innerHeight] @@ -585,44 +623,12 @@ class CanonicalFragmentIdentifier minx = max(-winx, -winw) maxx = winw - dist = (p1, p2) -> - Math.sqrt(Math.pow(p1[0]-p2[0], 2), Math.pow(p1[1]-p2[1], 2)) - - get_cfi = (ox, oy) -> - try - cfi = window.cfi.at(ox, oy) - point = window.cfi.point(cfi) - catch err - cfi = null - - if cfi - if point.range != null - r = point.range - rect = r.getClientRects()[0] - - x = (point.a*rect.left + (1-point.a)*rect.right) - y = (rect.top + rect.bottom)/2 - [x, y] = viewport_to_document(x, y, r.startContainer.ownerDocument) - else - node = point.node - r = node.getBoundingClientRect() - [x, y] = viewport_to_document(r.left, r.top, node.ownerDocument) - if typeof(point.x) == 'number' and node.offsetWidth - x += (point.x*node.offsetWidth)/100 - if typeof(point.y) == 'number' and node.offsetHeight - y += (point.y*node.offsetHeight)/100 - - if dist(viewport_to_document(ox, oy), [x, y]) > 50 - cfi = null - - return cfi - - x_loop = (cury) -> + x_loop = (cury) => for direction in [-1, 1] delta = deltax * direction curx = 0 until (direction < 0 and curx < minx) or (direction > 0 and curx > maxx) - cfi = get_cfi(curx, cury) + cfi = this.at_point(curx, cury) if cfi return cfi curx += delta diff --git a/src/calibre/ebooks/oeb/display/paged.coffee b/src/calibre/ebooks/oeb/display/paged.coffee index 39c7dc28e7..f1ff165d9f 100644 --- a/src/calibre/ebooks/oeb/display/paged.coffee +++ b/src/calibre/ebooks/oeb/display/paged.coffee @@ -67,6 +67,8 @@ class PagedDisplay ### constructor: () -> + if not this instanceof arguments.callee + throw new Error('PagedDisplay constructor called as function') this.set_geometry() this.page_width = 0 this.screen_width = 0 @@ -213,11 +215,11 @@ class PagedDisplay # most column in the viewport is the column containing the start of the # element and that the scroll position is at the start of the column. elem = document.getElementById(name) - if !elem + if not elem elems = document.getElementsByName(name) if elems elem = elems[0] - if !elem + if not elem return elem.scrollIntoView() if this.in_paged_mode @@ -249,12 +251,48 @@ class PagedDisplay window.scrollTo(0, y) ) + current_cfi: () -> + # The Conformal Fragment Identifier at the current position, returns + # null if it could not be calculated. Requires the cfi.coffee library. + ans = null + if not window.cfi? + return ans + if this.in_paged_mode + c = this.current_column_location() + for x in [c, c-this.page_width, c+this.page_width] + # Try the current column, the previous column and the next + # column. Each column is tried from top to bottom. + [left, right] = [x, x + this.page_width] + if left < 0 or right > document.body.scrollWidth + continue + deltax = Math.floor(this.page_width/25) + deltay = Math.floor(window.innerHeight/25) + cury = this.margin_top + until cury >= (window.innerHeight - this.margin_bottom) + curx = left + this.current_margin_side + until curx >= (right - this.current_margin_side) + cfi = window.cfi.at_point(curx-window.pageXOffset, cury-window.pageYOffset) + if cfi + log('Viewport cfi:', cfi) + return cfi + curx += deltax + cury += deltay + else + try + ans = window.cfi.at_current() + if not ans + ans = null + catch err + log(err) + if ans + log('Viewport cfi:', ans) + return ans + if window? window.paged_display = new PagedDisplay() # TODO: # css pagebreak rules -# CFI and bookmarks # Go to reference positions # Indexing # Resizing of images diff --git a/src/calibre/gui2/viewer/position.py b/src/calibre/gui2/viewer/position.py index d213443388..3a617e29c8 100644 --- a/src/calibre/gui2/viewer/position.py +++ b/src/calibre/gui2/viewer/position.py @@ -19,13 +19,10 @@ class PagePosition(object): ans = None res = self.document.mainFrame().evaluateJavaScript(''' ans = 'undefined'; - try { - ans = window.cfi.at_current(); + if (window.paged_display) { + ans = window.paged_display.current_cfi(); if (!ans) ans = 'undefined'; - } catch (err) { - window.console.log(err); } - window.console.log("Viewport cfi: " + ans); ans; ''') if res.isValid() and not res.isNull() and res.type() == res.String: