![]() |
#31 | |
Connoisseur
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 89
Karma: 185923
Join Date: May 2015
Device: iPad 1/2/Air, K3/PW2/Fire1, Kobo Touch, Samsung Tab, Nook Color/Touch
|
Quote:
|
|
![]() |
![]() |
![]() |
#32 | |
mostly an observer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,519
Karma: 987654
Join Date: Dec 2012
Device: Kindle
|
Quote:
|
|
![]() |
![]() |
Advert | |
|
![]() |
#33 | |
A Hairy Wizard
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 3,336
Karma: 20171571
Join Date: Dec 2012
Location: Charleston, SC today
Device: iPhone 15/11/X/6/iPad 1,2,Air & Air Pro/Surface Pro/Kindle PW & Fire
|
Quote:
|
|
![]() |
![]() |
![]() |
#34 | ||
Connoisseur
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 89
Karma: 185923
Join Date: May 2015
Device: iPad 1/2/Air, K3/PW2/Fire1, Kobo Touch, Samsung Tab, Nook Color/Touch
|
No problem
![]() Quote:
Code:
// Before p.blah, p.foo, p.bar { ... } // After p.blah { ... } p.foo { ... } p.bar { ... } Code:
// Before <p class="blah foo">Words</p> p.blah.foo { ... } // After <p class="blah-foo">Words</p> p.blah-foo { ... } Code:
// Before <p><span>Words</span></p> p span { ... } // After <p><span class="p-span">Words</span></p> span.p-span { ... } Code:
// Before <p class="blah foo">Words</p> p.blah { color:red } p.foo {size:1.2em } // After <p class="blah-foo">Words</p> p.blah-foo { color:red;size:1.2em } ![]() Quote:
I could probably get this going using NodeJS in a few hours, make the script available, maybe even set up an NPM package. Any thoughts? |
||
![]() |
![]() |
![]() |
#35 | ||||||
Curmudgeon
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 629
Karma: 1623086
Join Date: Jan 2012
Device: iPad, iPhone, Nook Simple Touch
|
Quote:
Code:
p.blah span.foo { ... } Code:
p.blah span.foo, span.foo { ... } Code:
p.blah, span.foo { ... } Quote:
Whether combined rules were actually necessary or not... would require lots of software archaeology to determine definitively. ![]() Quote:
Quote:
![]() Quote:
Quote:
Be careful when handling the @ styles, e.g. @media, @font-face, etc. You can probably just ignore @font-face rules entirely, and emit them in the final output. You'll want to process the rules within an @media query just like you would any other rule, being sure to add any replacement rules inside the same @media query that the original came from. I wouldn't try to process the @media rules separately, because my gut says that could cause a nasty mess. ![]() I'm suddenly tempted to try this. Last edited by dgatwood; 08-04-2015 at 10:49 PM. |
||||||
![]() |
![]() |
Advert | |
|
![]() |
#36 | |
Connoisseur
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 89
Karma: 185923
Join Date: May 2015
Device: iPad 1/2/Air, K3/PW2/Fire1, Kobo Touch, Samsung Tab, Nook Color/Touch
|
Quote:
Question: It seems like you could do point #4 first, and then you'd only have to do point #2 once. Right? |
|
![]() |
![]() |
![]() |
#37 | |
Curmudgeon
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 629
Karma: 1623086
Join Date: Jan 2012
Device: iPad, iPhone, Nook Simple Touch
|
Quote:
For fun, I took a crack at #2 and #3 last week. I ran into a bit of a wall where I couldn't do anything useful with the results from within a web browser, and I also ran into some data loss because Safari's CSS objects only include the bits that Safari uses, and leaves out properties that are specific to other browsers, but it might be useful as a starting point. Code:
function translate() { changed = false; do { fixup_multi_class_elements(); fixup_complex_styles(); } while (changed); } function fixup_multi_class_elements() { var changed = false; var CSSClassMap = new Array(); var nodeIterator = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT, { acceptNode: function(node) { return NodeFilter.FILTER_ACCEPT; } }, false); while (currentNode = nodeIterator.nextNode()) { if (currentNode.className.match(/\S\s\S/)) { /* Multiple words separated by a space. */ var bits = currentNode.className.split(/\s/).sort() var newCSSName = bits.join().replace(/\s/, ""); for (var bitnum in bits) { var bit = bits[bitnum]; if (!CSSClassMap[bit]) { CSSClassMap[bit] = new Array(); } CSSClassMap[bit].push(newCSSName); } currentNode.className = newCSSName; changed = true; } } var replacements = new Array(); var stylesheets = document.styleSheets; for (var stylesheet = 0; stylesheet < stylesheets.length; stylesheet++) { var newRules = new Array(); var stylesheet = document.styleSheets[stylesheet]; var pos = 0; for (var i = 0; i < stylesheet.cssRules.length; i++) { var rule = stylesheet.cssRules[i]; if (rule.type == CSSRule.MEDIA_RULE) { // CSS media rule alert("Media rules not implemented yet.\n"); newRules.push(constructStyleRule(null, rule.cssText)) // Eventually, need to iterate the child rules, and // build a new rule. } else if (rule.type == CSSRule.STYLE_RULE) { var newrule = combinedClassRules(rule, CSSClassMap); newRules.push(constructStyleRule(newrule["selector"], newrule["style"])) } else { // Just add the rule. newRules.push(constructStyleRule(null, rule.cssText)); } } while (stylesheet.cssRules.length) { stylesheet.deleteRule(0); } var newStylesheet = ""; var pos = 0; for (var i=0; i < newRules.length; i++) { var newrule = newRules[i]; //console.log("INSERTING RULE\n"); console.log(newrule); // stylesheet.insertRule(newrule["selector"], newrule["style"], i); if (newrule["selector"]) { newStylesheet += "\n"+newrule["selector"]+"\n{\n"+newrule["style"]+"\n}\n"; } else { newStylesheet += "\n"+newrule["style"]+"\n"; } } console.log(newStylesheet); } } function constructStyleRule(selectorString, ruleString) { return { "selector": selectorString, "style": ruleString + "" }; } function combinedClassRules(rule, CSSClassMap) { var selector = rule.selectorText; var parts = selector.split(","); var newSelectors = new Array(); // console.log("PARTS:"); console.log(parts); for (var partnum in parts) { var part = parts[partnum]; // console.log("PART: "+part); if (part.match(/\S/)) { var replacedSelectors = mapSelectors(part, CSSClassMap) if (replacedSelectors.length) newSelectors.push(replacedSelectors); newSelectors.push(part); } } return constructStyleRule(newSelectors.join(","), rule.style.cssText); } function classesAndIDsInSelector(selector, CSSClassMap) { var parts = selector.split(/[#.]/).slice(1); var classes = new Array(); for (partnum in parts) { var part = parts[partnum]; // -?[_a-zA-Z]+[_a-zA-Z0-9-]* var possibleClassOrID = part.replace(/^\s*/, "").replace(/(\s|[#.>+~[]).*$/, ""); if (possibleClassOrID.length) { /* Valid class or ID */ classes.push(possibleClassOrID); } } // console.log("SEL "+selector+" contains "+classes.join(",")); return classes; } function mapSelectors(selector, CSSClassMap) { var candidates = classesAndIDsInSelector(selector, CSSClassMap); var tempArray = new Array(); for (var oldClassNum in candidates) { var oldClass = candidates[oldClassNum]; var newClasses = CSSClassMap[oldClass]; if (newClasses) { for (var newClassNum in newClasses) { var newClass = newClasses[newClassNum]; tempArray.push(replaceClassInSelector(selector, oldClass, newClass)); } } } return tempArray.join(","); } /* From http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex */ function escapeRegExp(str) { return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); } function replaceClassInSelector(selector, oldClass, newClass) { var quoted = escapeRegExp(oldClass); var searchRE = new RegExp("([#.])"+quoted+"(?=$|\s|[#.>+~[])", "g"); return selector.replace(searchRE, "$1"+newClass); } function fixup_complex_styles() { } |
|
![]() |
![]() |
![]() |
#38 | |
Connoisseur
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 89
Karma: 185923
Join Date: May 2015
Device: iPad 1/2/Air, K3/PW2/Fire1, Kobo Touch, Samsung Tab, Nook Color/Touch
|
Quote:
![]() I do understand the wall there, though--I think it's best to stay away from the browser on this one, due to quirks and portability issues. I ended up writing #4 in a script that just processes the files on-disk in an unzipped ePub, basically. With the Cheerio module this is fairly easy:
So something like p > span becomes .p-span, voila. I put it here as a Gist with syntax highlighting, or you can see it here: Spoiler:
I do want to evolve it and probably make it into a proper NPM package with tests and all that, but I figured I'd post my immediate results. ----- Okay, now there's the question of #2 and #3. You basically mention elements that have multiple classes, but I think for a truly universal solution a more complex approach is required. Correct me if I'm wrong, but it's not so much elements with multiple classes as it is elements that multiple selectors apply to, right? Like, what if you have span.blah and you have a <p class="blah"> for whatever reason? The selector wouldn't actually apply in that scenario, but if you were just looking at classes, you would think it did. Or if you have selectors #super and .duper, and element <p class="duper" id="super"> then both rules would apply to that element. It's really all dependent on what kind of CSS is being used by the book creator; if you're just using classes then obviously that's fine, I'm just thinking it all the way through to the conclusion. I suppose if you already got rid of all of the complex selectors, basically anything matching /[+~\[\] ]/, then all you have to worry about is IDs and classes? So you could walk the DOM with that in mind, I suppose. |
|
![]() |
![]() |
![]() |
#39 | ||||
Curmudgeon
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 629
Karma: 1623086
Join Date: Jan 2012
Device: iPad, iPhone, Nook Simple Touch
|
Quote:
Quote:
Quote:
Quote:
|
||||
![]() |
![]() |
![]() |
#40 | |
A Hairy Wizard
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 3,336
Karma: 20171571
Join Date: Dec 2012
Location: Charleston, SC today
Device: iPhone 15/11/X/6/iPad 1,2,Air & Air Pro/Surface Pro/Kindle PW & Fire
|
Quote:
Aren't multiple classes defined in the ePub spec? Shouldn't a compliant reader/app be able to handle <p class="super duper"> in exactly the way you describe...combining the two class's css with the class listed first given higher priority? I rarely use multiple classes, but it seems Sigil and Marvin handle them just fine. Is this just a work around for kindle's shortcomings rather than an ePub issue?? Cheers, |
|
![]() |
![]() |
![]() |
#41 | |||
Connoisseur
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 89
Karma: 185923
Join Date: May 2015
Device: iPad 1/2/Air, K3/PW2/Fire1, Kobo Touch, Samsung Tab, Nook Color/Touch
|
Quote:
I'm building out a Node module on github that I dubbed allscribe, intended to serve as a library for various ebook processing tasks. This KF7 business is the first thing I'm adding to it. The first part (selector simplification into classes) is done, and the tests are passing. I'll be moving onto the class de-duping next, and I can report when I'm done. The idea is that you can do something like this: Code:
var book = openEpub('/Documents/Books/Magnum-Opus'); var clone = book.clone(book.path + '_copy'); clone.simplifyCSS(); Quote:
Quote:
![]() ![]() I guess if I really wanted to get serious about this, I'd build a testing framework that ran things through Kindlegen and then somehow examined the KF7 markup, which could be used to write unit tests for all this. Frankly, I wouldn't mind except that extracting data from an old .MOBI file seems super arcane. There ain't no JS library for that one, son. |
|||
![]() |
![]() |
![]() |
#42 | ||
Curmudgeon
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 629
Karma: 1623086
Join Date: Jan 2012
Device: iPad, iPhone, Nook Simple Touch
|
Quote:
Quote:
![]() |
||
![]() |
![]() |
![]() |
#43 | |
Connoisseur
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 89
Karma: 185923
Join Date: May 2015
Device: iPad 1/2/Air, K3/PW2/Fire1, Kobo Touch, Samsung Tab, Nook Color/Touch
|
Quote:
![]() It actually makes it a little easier, because instead of cloning rules for the class-combination selectors, I can just add the combo selector to any rule that has one of the component selectors. So: Code:
<span class="apple banana">Hello World!</span> span.apple { color: red; } Code:
<span class="apple-banana">Hello World!</span> span.apple, .apple-banana { color: red; } Anyway, I was sick for a couple of days, but I have it working now. At least all of my unit tests are passing. Assuming you have gorilla.epub you can do: Code:
var allscribe = require('allscribe'); var gorilla = allscribe.openEpub('gorilla.epub'); gorilla.process(function(unpacked){ unpacked.simplifyCssAndMarkup(); // handles multi-element and such complex selectors unpacked.mergeMarkupClasses(); // handles multiple classes on an element return 'silverback'; }); Anyway, I'll be incorporating all this into my current workflow, start testing how my book looks on K1, etc. But it's a good start, I think. Too bad it's probably not all that accessible to non-scripters. ![]() (I would ask you to give it a shot, seeing that you are a scripter, but all your stuff is custom anyway ![]() |
|
![]() |
![]() |
![]() |
#44 | |
Connoisseur
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 89
Karma: 185923
Join Date: May 2015
Device: iPad 1/2/Air, K3/PW2/Fire1, Kobo Touch, Samsung Tab, Nook Color/Touch
|
Quote:
Any other ideas? I also tried * element.class, in case it's "not in a body". |
|
![]() |
![]() |
![]() |
#45 | |
Curmudgeon
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 629
Karma: 1623086
Join Date: Jan 2012
Device: iPad, iPhone, Nook Simple Touch
|
Quote:
Control-click on the app bundle and choose "Show Package Contents". Then go to Contents, PlugIns, then control-click on BKAssetEpub.bundle, choose "Show Package Contents", then Contents, Resources, and open the various .css.tmpl files. |
|
![]() |
![]() |
![]() |
|
![]() |
||||
Thread | Thread Starter | Forum | Replies | Last Post |
EPUB to HTLM (single page) | obihal | Conversion | 8 | 05-20-2014 01:45 PM |
Online HTML book -> epub: TOC from single file | dancal | Conversion | 0 | 01-27-2014 01:45 PM |
EPUB Formatting Challenge: Embedding blog posts in the flow of story text | Morganucopia | ePub | 18 | 08-02-2013 04:47 PM |
Several xhtml/html to a single epub file help. | clowe1028 | ePub | 3 | 03-21-2010 03:47 AM |
single HTML to ePub with fixed width font | skyfish | Calibre | 8 | 12-10-2009 01:30 PM |