mirror of
https://github.com/futurepress/epub.js.git
synced 2025-10-04 15:09:16 +02:00
Merge pull request #927 from mikkelvp/dev
Respect pre-paginated page spreads
This commit is contained in:
commit
7b8480fde0
8 changed files with 214 additions and 36 deletions
|
@ -1094,7 +1094,7 @@ class Contents {
|
|||
* @param {number} width
|
||||
* @param {number} height
|
||||
*/
|
||||
fit(width, height){
|
||||
fit(width, height, section){
|
||||
var viewport = this.viewport();
|
||||
var viewportWidth = parseInt(viewport.width);
|
||||
var viewportHeight = parseInt(viewport.height);
|
||||
|
@ -1124,6 +1124,11 @@ class Contents {
|
|||
this.css("background-size", viewportWidth * scale + "px " + viewportHeight * scale + "px");
|
||||
|
||||
this.css("background-color", "transparent");
|
||||
if (section && section.properties.includes("page-spread-left")) {
|
||||
// set margin since scale is weird
|
||||
var marginLeft = width - (viewportWidth * scale);
|
||||
this.css("margin-left", marginLeft + "px");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -189,11 +189,11 @@ class Layout {
|
|||
* @param {Contents} contents
|
||||
* @return {Promise}
|
||||
*/
|
||||
format(contents){
|
||||
format(contents, section){
|
||||
var formating;
|
||||
|
||||
if (this.name === "pre-paginated") {
|
||||
formating = contents.fit(this.columnWidth, this.height);
|
||||
formating = contents.fit(this.columnWidth, this.height, section);
|
||||
} else if (this._flow === "paginated") {
|
||||
formating = contents.columns(this.width, this.height, this.columnWidth, this.gap);
|
||||
} else { // scrolled
|
||||
|
|
136
src/locations.js
136
src/locations.js
|
@ -20,12 +20,15 @@ class Locations {
|
|||
this.epubcfi = new EpubCFI();
|
||||
|
||||
this._locations = [];
|
||||
this._locationsWords = [];
|
||||
this.total = 0;
|
||||
|
||||
this.break = 150;
|
||||
|
||||
this._current = 0;
|
||||
|
||||
this._wordCounter = 0;
|
||||
|
||||
this.currentLocation = '';
|
||||
this._currentCfi ='';
|
||||
this.processingTimeout = undefined;
|
||||
|
@ -172,6 +175,139 @@ class Locations {
|
|||
return locations;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load all of sections in the book to generate locations
|
||||
* @param {string} startCfi start position
|
||||
* @param {int} wordCount how many words to split on
|
||||
* @param {int} count result count
|
||||
* @return {object} locations
|
||||
*/
|
||||
generateFromWords(startCfi, wordCount, count) {
|
||||
var start = startCfi ? new EpubCFI(startCfi) : undefined;
|
||||
this.q.pause();
|
||||
this._locationsWords = [];
|
||||
this._wordCounter = 0;
|
||||
|
||||
this.spine.each(function(section) {
|
||||
if (section.linear) {
|
||||
if (start) {
|
||||
if (section.index >= start.spinePos) {
|
||||
this.q.enqueue(this.processWords.bind(this), section, wordCount, start, count);
|
||||
}
|
||||
} else {
|
||||
this.q.enqueue(this.processWords.bind(this), section, wordCount, start, count);
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
return this.q.run().then(function() {
|
||||
if (this._currentCfi) {
|
||||
this.currentLocation = this._currentCfi;
|
||||
}
|
||||
|
||||
return this._locationsWords;
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
|
||||
processWords(section, wordCount, startCfi, count) {
|
||||
if (count && this._locationsWords.length >= count) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return section.load(this.request)
|
||||
.then(function(contents) {
|
||||
var completed = new defer();
|
||||
var locations = this.parseWords(contents, section, wordCount, startCfi);
|
||||
var remainingCount = count - this._locationsWords.length;
|
||||
this._locationsWords = this._locationsWords.concat(locations.length >= count ? locations.slice(0, remainingCount) : locations);
|
||||
|
||||
section.unload();
|
||||
|
||||
this.processingTimeout = setTimeout(() => completed.resolve(locations), this.pause);
|
||||
return completed.promise;
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
//http://stackoverflow.com/questions/18679576/counting-words-in-string
|
||||
countWords(s) {
|
||||
s = s.replace(/(^\s*)|(\s*$)/gi, "");//exclude start and end white-space
|
||||
s = s.replace(/[ ]{2,}/gi, " ");//2 or more space to 1
|
||||
s = s.replace(/\n /, "\n"); // exclude newline with a start spacing
|
||||
return s.split(" ").length;
|
||||
}
|
||||
|
||||
parseWords(contents, section, wordCount, startCfi) {
|
||||
var cfiBase = section.cfiBase;
|
||||
var locations = [];
|
||||
var doc = contents.ownerDocument;
|
||||
var body = qs(doc, "body");
|
||||
var prev;
|
||||
var _break = wordCount;
|
||||
var foundStartNode = startCfi ? startCfi.spinePos !== section.index : true;
|
||||
var startNode;
|
||||
if (startCfi && section.index === startCfi.spinePos) {
|
||||
startNode = startCfi.findNode(startCfi.range ? startCfi.path.steps.concat(startCfi.start.steps) : startCfi.path.steps, contents.ownerDocument);
|
||||
}
|
||||
var parser = function(node) {
|
||||
if (!foundStartNode) {
|
||||
if (node === startNode) {
|
||||
foundStartNode = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (node.textContent.length < 10) {
|
||||
if (node.textContent.trim().length === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var len = this.countWords(node.textContent);
|
||||
var dist;
|
||||
var pos = 0;
|
||||
|
||||
if (len === 0) {
|
||||
return false; // continue
|
||||
}
|
||||
|
||||
dist = _break - this._wordCounter;
|
||||
|
||||
// Node is smaller than a break,
|
||||
// skip over it
|
||||
if (dist > len) {
|
||||
this._wordCounter += len;
|
||||
pos = len;
|
||||
}
|
||||
|
||||
|
||||
while (pos < len) {
|
||||
dist = _break - this._wordCounter;
|
||||
|
||||
// Gone over
|
||||
if (pos + dist >= len) {
|
||||
// Continue counter for next node
|
||||
this._wordCounter += len - pos;
|
||||
// break
|
||||
pos = len;
|
||||
// At End
|
||||
} else {
|
||||
// Advance pos
|
||||
pos += dist;
|
||||
|
||||
let cfi = new EpubCFI(node, cfiBase);
|
||||
locations.push({ cfi: cfi.toString(), wordCount: this._wordCounter });
|
||||
this._wordCounter = 0;
|
||||
}
|
||||
}
|
||||
prev = node;
|
||||
};
|
||||
|
||||
sprint(body, parser.bind(this));
|
||||
|
||||
return locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a location from an EpubCFI
|
||||
* @param {EpubCFI} cfi
|
||||
|
|
|
@ -228,8 +228,23 @@ class DefaultViewManager {
|
|||
}, epubcfi);
|
||||
}
|
||||
|
||||
createView(section) {
|
||||
return new this.View(section, this.viewSettings);
|
||||
createView(section, forceRight) {
|
||||
return new this.View(section, extend(this.viewSettings, { forceRight }) );
|
||||
}
|
||||
|
||||
handleNextPrePaginated(forceRight, section, action) {
|
||||
let next;
|
||||
|
||||
if (this.layout.name === "pre-paginated" && this.layout.divisor > 1) {
|
||||
if (forceRight || section.index === 0) {
|
||||
// First page (cover) should stand alone for pre-paginated books
|
||||
return;
|
||||
}
|
||||
next = section.next();
|
||||
if (next && !next.properties.includes("page-spread-left")) {
|
||||
return action.call(this, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
display(section, target){
|
||||
|
@ -246,7 +261,7 @@ class DefaultViewManager {
|
|||
var visible = this.views.find(section);
|
||||
|
||||
// View is already shown, just move to correct location in view
|
||||
if(visible && section) {
|
||||
if(visible && section && this.layout.name !== "pre-paginated") {
|
||||
let offset = visible.offset();
|
||||
|
||||
if (this.settings.direction === "ltr") {
|
||||
|
@ -268,7 +283,12 @@ class DefaultViewManager {
|
|||
// Hide all current views
|
||||
this.clear();
|
||||
|
||||
this.add(section)
|
||||
let forceRight = false;
|
||||
if (this.layout.name === "pre-paginated" && this.layout.divisor === 2 && section.properties.includes("page-spread-right")) {
|
||||
forceRight = true;
|
||||
}
|
||||
|
||||
this.add(section, forceRight)
|
||||
.then(function(view){
|
||||
|
||||
// Move to correct place within the section, if needed
|
||||
|
@ -281,15 +301,7 @@ class DefaultViewManager {
|
|||
displaying.reject(err);
|
||||
})
|
||||
.then(function(){
|
||||
var next;
|
||||
if (this.layout.name === "pre-paginated" &&
|
||||
this.layout.divisor > 1 && section.index > 0) {
|
||||
// First page (cover) should stand alone for pre-paginated books
|
||||
next = section.next();
|
||||
if (next) {
|
||||
return this.add(next);
|
||||
}
|
||||
}
|
||||
return this.handleNextPrePaginated(forceRight, section, this.add);
|
||||
}.bind(this))
|
||||
.then(function(){
|
||||
|
||||
|
@ -331,8 +343,8 @@ class DefaultViewManager {
|
|||
this.scrollTo(distX, distY, true);
|
||||
}
|
||||
|
||||
add(section){
|
||||
var view = this.createView(section);
|
||||
add(section, forceRight){
|
||||
var view = this.createView(section, forceRight);
|
||||
|
||||
this.views.append(view);
|
||||
|
||||
|
@ -345,11 +357,10 @@ class DefaultViewManager {
|
|||
});
|
||||
|
||||
return view.display(this.request);
|
||||
|
||||
}
|
||||
|
||||
append(section){
|
||||
var view = this.createView(section);
|
||||
append(section, forceRight){
|
||||
var view = this.createView(section, forceRight);
|
||||
this.views.append(view);
|
||||
|
||||
view.onDisplayed = this.afterDisplayed.bind(this);
|
||||
|
@ -362,8 +373,8 @@ class DefaultViewManager {
|
|||
return view.display(this.request);
|
||||
}
|
||||
|
||||
prepend(section){
|
||||
var view = this.createView(section);
|
||||
prepend(section, forceRight){
|
||||
var view = this.createView(section, forceRight);
|
||||
|
||||
view.on(EVENTS.VIEWS.RESIZED, (bounds) => {
|
||||
this.counter(bounds);
|
||||
|
@ -450,15 +461,14 @@ class DefaultViewManager {
|
|||
if(next) {
|
||||
this.clear();
|
||||
|
||||
return this.append(next)
|
||||
let forceRight = false;
|
||||
if (this.layout.name === "pre-paginated" && this.layout.divisor === 2 && next.properties.includes("page-spread-right")) {
|
||||
forceRight = true;
|
||||
}
|
||||
|
||||
return this.append(next, forceRight)
|
||||
.then(function(){
|
||||
var right;
|
||||
if (this.layout.name === "pre-paginated" && this.layout.divisor > 1) {
|
||||
right = next.next();
|
||||
if (right) {
|
||||
return this.append(right);
|
||||
}
|
||||
}
|
||||
return this.handleNextPrePaginated(forceRight, next, this.append);
|
||||
}.bind(this), (err) => {
|
||||
return err;
|
||||
})
|
||||
|
@ -522,7 +532,12 @@ class DefaultViewManager {
|
|||
if(prev) {
|
||||
this.clear();
|
||||
|
||||
return this.prepend(prev)
|
||||
let forceRight = false;
|
||||
if (this.layout.name === "pre-paginated" && this.layout.divisor === 2 && typeof prev.prev() !== "object") {
|
||||
forceRight = true;
|
||||
}
|
||||
|
||||
return this.prepend(prev, forceRight)
|
||||
.then(function(){
|
||||
var left;
|
||||
if (this.layout.name === "pre-paginated" && this.layout.divisor > 1) {
|
||||
|
@ -806,6 +821,9 @@ class DefaultViewManager {
|
|||
|
||||
this.layout = layout;
|
||||
this.updateLayout();
|
||||
if (this.views && this.views.length > 0 && this.layout.name === "pre-paginated") {
|
||||
this.display(this.views.first().section);
|
||||
}
|
||||
// this.manager.layout(this.layout.format);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ class IframeView {
|
|||
height: 0,
|
||||
layout: undefined,
|
||||
globalLayoutProperties: {},
|
||||
method: undefined
|
||||
method: undefined,
|
||||
forceRight: false
|
||||
}, options || {});
|
||||
|
||||
this.id = "epubjs-view-" + uuid();
|
||||
|
@ -148,7 +149,7 @@ class IframeView {
|
|||
.then(function(){
|
||||
|
||||
// apply the layout function to the contents
|
||||
this.layout.format(this.contents);
|
||||
this.layout.format(this.contents, this.section);
|
||||
|
||||
// find and report the writingMode axis
|
||||
let writingMode = this.contents.writingMode();
|
||||
|
@ -164,6 +165,10 @@ class IframeView {
|
|||
return new Promise((resolve, reject) => {
|
||||
// Expand the iframe to the full size of the content
|
||||
this.expand();
|
||||
//
|
||||
if (this.settings.forceRight) {
|
||||
this.element.style.marginLeft = this.width() + "px";
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ class Packaging {
|
|||
metadata.flow = this.getPropertyText(xml, "rendition:flow");
|
||||
metadata.viewport = this.getPropertyText(xml, "rendition:viewport");
|
||||
metadata.media_active_class = this.getPropertyText(xml, "media:active-class");
|
||||
metadata.spread = this.getPropertyText(xml, "rendition:spread");
|
||||
// metadata.page_prog_dir = packageXml.querySelector("spine").getAttribute("page-progression-direction");
|
||||
|
||||
return metadata;
|
||||
|
|
|
@ -206,6 +206,14 @@ class Rendition {
|
|||
if (!this.settings.layout && (this.book.package.metadata.layout === "pre-paginated" || this.book.displayOptions.fixedLayout === "true")) {
|
||||
this.settings.layout = "pre-paginated";
|
||||
}
|
||||
switch(this.book.package.metadata.spread) {
|
||||
case 'none':
|
||||
this.settings.spread = 'none';
|
||||
break;
|
||||
case 'both':
|
||||
this.settings.spread = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(!this.manager) {
|
||||
this.ViewManager = this.requireManager(this.settings.manager);
|
||||
|
@ -319,6 +327,10 @@ class Rendition {
|
|||
|
||||
section = this.book.spine.get(target);
|
||||
|
||||
if (!section && target.includes(".xhtml")) {
|
||||
section = this.book.spine.get("xhtml/" + target);
|
||||
}
|
||||
|
||||
if(!section){
|
||||
displaying.reject(new Error("No Section Found"));
|
||||
return displayed;
|
||||
|
|
3
types/packaging.d.ts
vendored
3
types/packaging.d.ts
vendored
|
@ -23,7 +23,8 @@ export interface PackagingMetadataObject {
|
|||
layout: string,
|
||||
orientation: string,
|
||||
flow: string,
|
||||
viewport: string
|
||||
viewport: string,
|
||||
spread: string
|
||||
}
|
||||
|
||||
export interface PackagingSpineItem {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue