1
0
Fork 0
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:
Fred Chasen 2020-05-14 23:01:21 -07:00 committed by GitHub
commit 7b8480fde0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 214 additions and 36 deletions

View file

@ -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");
}
}
/**

View file

@ -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

View file

@ -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

View file

@ -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);
}

View file

@ -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();
});

View file

@ -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;

View file

@ -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;

View file

@ -23,7 +23,8 @@ export interface PackagingMetadataObject {
layout: string,
orientation: string,
flow: string,
viewport: string
viewport: string,
spread: string
}
export interface PackagingSpineItem {