diff --git a/share/pdf2htmlEX.js.in b/share/pdf2htmlEX.js.in index e7cab41..c7e8836 100644 --- a/share/pdf2htmlEX.js.in +++ b/share/pdf2htmlEX.js.in @@ -20,6 +20,9 @@ var pdf2htmlEX = (function(){ }; var DEFAULT_PAGES_TO_PRELOAD = 3; + // Smooth zoom is enabled when pages shown are less then SMOOTH_ZOOM_THRESHOLD. Otherwise page content is hidden and redrawn after a delay (function schedule_render). + var SMOOTH_ZOOM_THRESHOLD = 4; // 0: disable smooth zoom optimizations (less CPU usage but flickering on zoom) + var pdf2htmlEX = new Object(); var EPS = 1e-6; @@ -42,6 +45,7 @@ var pdf2htmlEX = (function(){ if(page == undefined) return; this.loaded = false; + this.shown = false; this.$p = $(page); this.$container = $(container); @@ -61,7 +65,7 @@ var pdf2htmlEX = (function(){ * set_r : last set * cur_r : currently using */ - this.default_r = this.set_r = this.cur_r = this.$p.height() / this.$b.height(); + this.default_r = this.set_r = this.cur_r = this.h / this.$b.height(); this.data = $($('.'+CSS_CLASS_NAMES['page_data'], this.$p)[0]).data('data'); @@ -75,33 +79,39 @@ var pdf2htmlEX = (function(){ /* hide & show are for contents, the page frame is still there */ hide : function(){ this.$b.removeClass('opened'); + this.shown = false; }, show : function(){ - if(Math.abs(this.set_r - this.cur_r) > EPS) { - this.cur_r = this.set_r; - this.$b.css('transform', 'scale('+this.cur_r.toFixed(3)+')'); + if (this.loaded) { + if(Math.abs(this.set_r - this.cur_r) > EPS) { + this.cur_r = this.set_r; + this.$b.css('transform', 'scale('+this.cur_r.toFixed(3)+')'); + } + if (! this.shown) { + this.$b.addClass('opened'); + this.shown = true; + } } - this.$b.addClass('opened'); }, - rescale : function(ratio, is_relative) { + rescale : function(ratio, keep_shown) { if(ratio == 0) { this.set_r = this.default_r; - } else if (is_relative) { - this.set_r *= ratio; } else { this.set_r = ratio; } - /* wait for redraw */ - this.hide(); + if (keep_shown) + this.show(); // Refresh content + else + this.hide(); // Wait for redraw - this.$p.height(this.$b.height() * this.set_r); - this.$p.width(this.$b.width() * this.set_r); + this.$d.height(this.h * this.set_r); + this.$d.width(this.w * this.set_r); }, /* return if any part of this page is shown in the container */ is_visible : function() { var off = this.position(); - return !((off[1] > this.height()) || (off[1] + this.$container.height() < 0)); + return !((off[1] > this.h) || (off[1] + this.$container.height() < 0)); }, /* return if this page or any neighbor of it is visible */ is_nearly_visible : function() { @@ -109,7 +119,7 @@ var pdf2htmlEX = (function(){ /* I should use the height of the previous page or the next page here * but since they are not easily available, just use '*2', which should be a good estimate in most cases */ - return !((off[1] > this.height() * 2) || (off[1] + this.$container.height() * 2 < 0)); + return !((off[1] > this.h * 2) || (off[1] + this.$container.height() * 2 < 0)); }, /* return the coordinate of the top-left corner of container * in our coordinate system @@ -118,9 +128,6 @@ var pdf2htmlEX = (function(){ var off = this.$p.offset(); var off_c = this.$container.offset(); return [off_c.left-off.left, off_c.top-off.top]; - }, - height : function() { - return this.$p.height(); } }); @@ -141,6 +148,7 @@ var pdf2htmlEX = (function(){ /* Constants */ render_timeout : 100, scale_step : 0.9, + scale : 1, init_before_loading_content : function() { /*hide all pages before loading, will reveal only visible ones later */ @@ -154,7 +162,7 @@ var pdf2htmlEX = (function(){ this.$loading_indicator = $('.'+this.loading_indicator_cls); // Open the outline if nonempty - if(this.$outline.children().length > 0) { + if(this.$outline.children().length > 0) { this.$sidebar.addClass('opened'); } @@ -164,14 +172,14 @@ var pdf2htmlEX = (function(){ var _ = this; this.$container.scroll(function(){ _.schedule_render(); }); - //this.zoom_fixer(); + this.zoom_fixer(); // handle links this.$container.add(this.$outline).on('click', '.'+CSS_CLASS_NAMES['link'], this, this.link_handler); this.render(); }, - + find_pages : function() { var new_pages = new Array(); var $pl= $('.'+CSS_CLASS_NAMES['page_frame'], this.$container); @@ -210,10 +218,11 @@ var pdf2htmlEX = (function(){ url: url, dataType: 'text' }).done(function(data){ - _.pages[idx].$p.parent().replaceWith(data); + _.pages[idx].$d.replaceWith(data); var $new_pf = _.$container.find('#' + CSS_CLASS_NAMES['page_frame'] + page_no_hex); _.pages[idx] = new Page($new_pf, _.$container); + _.pages[idx].hide(); _.pages[idx].rescale(_.scale); _.schedule_render(); @@ -327,15 +336,85 @@ var pdf2htmlEX = (function(){ }); }, - rescale : function (ratio, is_relative) { - var pl = this.pages; - for(var i in pl) { - pl[i].rescale(ratio, is_relative); + rescale : function (ratio, is_relative, offsetX, offsetY) { + if (! offsetX) + offsetX = 0; + if (! offsetY) + offsetY = 0; + + // Save offset of the active page + var active_page = this.get_active_page(); + var prev_offset = active_page.$p.offset(); + var old_scale = this.scale; + + var prerendering_enabled = false; + if (SMOOTH_ZOOM_THRESHOLD > 0) { + // Immediate rendering optimizations enabled to improve reactiveness while zooming + // Find out which pages are visible + var min_visible, max_visible; + min_visible = max_visible = active_page.n; + while (min_visible > 0 && this.pages[min_visible].is_visible()) { min_visible-- } + while (max_visible < this.pages.length && this.pages[max_visible].is_visible()) { max_visible++ } + + // If less then the threshold, enable prerendering on selected pages + if (max_visible - min_visible - 2 < SMOOTH_ZOOM_THRESHOLD) + prerendering_enabled = true; } + // Set new scale + if (is_relative) + this.scale *= ratio; + else + this.scale = ratio; + + // Rescale pages + var pl = this.pages; + for(var i in pl) { + if (prerendering_enabled && i > min_visible && i < max_visible) + pl[i].rescale(this.scale, true); // Force immediate refresh + else + pl[i].rescale(this.scale); // Delayed refresh + } + + // Correct container scroll to keep view aligned while zooming + var correction_top = active_page.$p.offset().top - prev_offset.top; + this.$container.scrollTop( this.$container.scrollTop() + correction_top + offsetY ); + + // Take the center of the view as a reference + var prev_center_x = this.$container.width() / 2 - prev_offset.left; + // Calculate the difference respect the center of the view after the zooming + var correction_left = prev_center_x * (this.scale/old_scale - 1) + active_page.$p.offset().left - prev_offset.left; + // Scroll the container accordingly to keep alignment to the initial reference + this.$container.scrollLeft( this.$container.scrollLeft() + correction_left + offsetX ); + + // Delayed rendering for pages not already shown this.schedule_render(); }, + fit_width : function () { + var active_page = this.get_active_page(); + + this.rescale(this.$container.width() / active_page.w, false); + this.scroll_to(active_page.n, [0,0]); + }, + + fit_height : function () { + var active_page = this.get_active_page(); + + this.rescale(this.$container.height() / active_page.h, false); + this.scroll_to(active_page.n, [0,0]); + }, + + get_active_page : function () { + // get page that are on the center of the view //TODO better on top?! + var y_center = $(this.$container).offset().top + this.$container.height() / 2; + for (var i=2; i y_center) + return this.pages[i-1]; + } + return this.pages[i-1]; // Last page + }, + get_containing_page : function(obj) { /* get the page obj containing obj */ var p = obj.closest('.'+CSS_CLASS_NAMES['page_frame'])[0]; @@ -354,7 +433,7 @@ var pdf2htmlEX = (function(){ { cur_pos = cur_page.position(); //get the coordinates in default user system - cur_pos = transform(cur_page.ictm, [cur_pos[0], cur_page.height()-cur_pos[1]]); + cur_pos = transform(cur_page.ictm, [cur_pos[0], cur_page.h-cur_pos[1]]); } var detail_str = t.attr('data-dest-detail'); @@ -383,7 +462,7 @@ var pdf2htmlEX = (function(){ break; case 'FitH': case 'FitBH': - pos = [0, (detail[2] == null) ? cur_pos[1] : detail[2]] + pos = [0, (detail[2] == null) ? cur_pos[1] : detail[2]]; ok = true; break; case 'FitV': @@ -406,10 +485,10 @@ var pdf2htmlEX = (function(){ var transform_and_scroll = function() { pos = transform(target_page.ctm, pos); if(upside_down) { - pos[1] = target_page.height() - pos[1]; + pos[1] = target_page.h - pos[1]; } _.scroll_to(detail[0], pos); - } + }; if (target_page.loaded) { transform_and_scroll(); diff --git a/src/HTMLRenderer/general.cc b/src/HTMLRenderer/general.cc index 09d60e3..8a4c1a0 100644 --- a/src/HTMLRenderer/general.cc +++ b/src/HTMLRenderer/general.cc @@ -192,6 +192,8 @@ void HTMLRenderer::startPage(int pageNum, GfxState *state, XRef * xref) << "\" data-page-no=\"" << pageNum << "\">" << "
"; /*