MobileRead Forums

MobileRead Forums (https://www.mobileread.com/forums/index.php)
-   Plugins (https://www.mobileread.com/forums/forumdisplay.php?f=268)
-   -   [Plugin] RemoveInLineStyles Plugin (https://www.mobileread.com/forums/showthread.php?t=274274)

KevinH 05-18-2016 01:40 PM

[Plugin] RemoveInLineStyles Plugin
 
2 Attachment(s)
Hi All,

This "edit" plugin will search for and remove all inline styles (style attributes in xhtml tags) and replace them with the equivalent class stored in an external stylesheet.

Updated: May 31, 2021
Current Version: "0.3.1"

Requires: Python 3.4 or Later and Sigil 1.0 or later

See the attached: RemoveInLineStyles_v031.zip

For Sigil Versions before Sigil-1.0
please see the attached older version RemoveInLineStyles_v021.zip

How it works:
It will parse all existing stylesheets using cssutils (part of the official internal Python 3 requirements included with Sigil) to create a list of all stylesheet file names and class names currently being used to prevent name collisions.

Then it will parse each and every xhtml file, examining each tag and if a style attribute is present, it will remove it, look in the current list of newly created rules to see if an exact match exists and if not add a new class name with that new rule. The correct class attribute is then added to replace the removed style attribute. Note that for safety, inline styles used inside svg or mathml elements/regions are not touched.

If inline styles are found, an external link to the newly created external stylesheet will be inserted into the head element just before the closing head tag.

No changes will be made if no inline styles are actually found in the xhtml file and no new css stylesheet will be created unless at least one inline style is found someplace in the ebook.

This code should be safe to use if no external stylesheets already exist or if there is no mixing of existing css rules and inline styles for the exact same element.

Warnings
1. Please note, any styles inside the style tag in the head tag will be ignored.
2. Should not be used on code that mixes css rules with inline styles at the same time as the authors intent with the inline style may be to overrule anything previous because inline styles have higher specificity.


Problems / Bug Reports / Suggestions for Improvements
If you do run into problems with this plugin, please report them here including a small sample of the problem xhtml/css code, along with information on your platform, Sigil version, etc. And I will try to quickly fix any bugs or issues you run into.

Hope this helps!

KevinH

DiapDealer 05-18-2016 01:47 PM

Oh, this will come in extremely handy (speaking for myself, anyway). Thanks!

KevinH 05-19-2016 02:25 PM

Update to version v021 in first post of this thread. RemoveInLineStyle will now simply ignore inline styles used inside svg elements/regions and math elements/regions for increased safety.

Kevin

odamizu 05-21-2016 12:32 AM

Quote:

Originally Posted by KevinH (Post 3319852)
This "edit" plugin will search for and remove all inline styles (style attributes in xhtml tags) and replace them with the equivalent class stored in an external stylesheet.

:thanks:

kovidgoyal 05-21-2016 12:53 AM

@KevinH: Perhaps you should add a note that this plugin can destroy your styles, since there is no way to robustly convert an inline style to a class based style. The CSS selector specificity will always be wrong. For example

Code:

#test {
color: red
}


<span id="test" style="color:green">test</test>

will render green before this plugin runs and red after it runs.

This is because in the CSS cascading spec the specificity of an inline style declaration is always higher than the specificity of any selector based rule.

Doitsu 05-21-2016 01:15 AM

Quote:

Originally Posted by kovidgoyal (Post 3321502)
Perhaps you should add a note that this plugin can destroy your styles, since there is no way to robustly convert an inline style to a class based style. The CSS selector specificity will always be wrong.

You're certainly raising a valid point, however, the ePubs for which this plugin was primarily intended are not very likely to contain id selectors and many web designers don't recommend using id selectors.

kovidgoyal 05-21-2016 02:03 AM

It has nothing to do with id selectors, here's an example without id selectors

Code:

p span.test {
color: red
}

<p>
<span class="test" style="color:green">test</span>
</p>

The point is that there is no way to map the specificity of an inline style to a selector based style robustly. Which means that the CSS cascade before and after the plugin runs is different.

KevinH 05-21-2016 09:22 AM

Good point! In both cases there are existing style sheets and are mixing in inline styles. Even if I add the new general class to a style sheet so it is found second, it won't have higher specificity.

I will add that to the warnings (now added).

I will modify the code to allow the user to abort if inline styles are used with existing style sheets already included. Or I guess I could map styles sheet classes to xhtml lines and if a rule already exists for that element, leave it alone.

Thanks for the heads up!

KevinH 05-21-2016 10:50 AM

Kovid,

As I walk the xhtml, if I can detect if a rule already exists for an element that *also* specifies an inline style, could I not add an "id" attribute and then remove the inline style and add it as a new rule to the new stylesheet but with that id selector. That should match the highest specificity rule but be come after the original definition. That should work for both your examples.

Or are there other cases that would still make this approach unsound.

KevinH

kovidgoyal 05-21-2016 02:11 PM

There is no easy way to solve this (I considered implementing this for the calibre editor a long time ago, and decided not to, because it cannot easily be made robust) -- if you use id selectors, your selector can still be trumped by another id selector that is more specific, for example, if it also has a class or a descendant selector and so on (say #xxx.yyy or p#xxx or p > span#xxx)

See https://css-tricks.com/specifics-on-...le-header-id-0 for a good overview.

What you can do do is go over all rules in all stylesheets, find the highest specificity selector that applies to the element in question, then if:

1) The selector has an id, simply merge the rules from the inline style block into that rule, taking care, in particular of shorthand properties
2) The selector does not have an id -- create a new id based selector by giving the element a random id if it does not already have one

You can refine this process by adding a few more if statements to (2) so that you use class based selectors more often.

And note that even this process is not quite bulletproof there are a few edge cases I have not mentioned -- left as an exercise to the reader.

KevinH 05-21-2016 02:47 PM

I think the only safe thing is to check if any existing class / selector match at all for an element that also has an inline style, we simply leave the inline style untouched.

FDPuthuff 11-15-2020 02:23 PM

Thank you sir.
I'll play with this and see how it can help me. :cool:

slowsmile 11-19-2020 04:54 AM

@KevinH... A way out for you might perhaps be to just rename your new "inline classes" to your own convention e.g. sgc1, sgc2 etc while at the same time moving them to the CSS. I don't know if this is useful to you but you can certainly do this using the Tidy dll(I've used the Tidy dll like this in my HTML2Epub plugin) and just moved the new classes from the top of the html file to the CSS using my own code. I think you have to set the 'css-prefix' Tidy flag for this feature to work. If you created your CSS classes this way there would hopefully be no style name clashes. The only downside perhaps is that the Tidy dll is quite a large file.

Sarmat89 05-13-2021 06:25 AM

The plugin is not quite compatible with the newer Sigil: it places the resulting file in the root directory, but inserts a "../Styles/" path reference to the HTML files.

KevinH 05-13-2021 10:25 AM

Thanks! I forgot to update it. I will do that now.

KevinH 05-13-2021 12:06 PM

Okay, Please give RemoveInLineStyles_v030.zip a try. It is attached to the first post in this thread.

It requires at least Sigil-1.0 to work but should properly handle any epub layout.

Sarmat89 05-28-2021 06:45 PM

The new version doesn't seem to work:

Code:

Traceback (most recent call last):
  File "C:\Programs\Sigil\plugin_launchers\python\launcher.py", line 142, in launch
    self.exitcode = target_script.run(container)
  File "C:\Users\xxx\AppData\Local\sigil-ebook\sigil\plugins\RemoveInLineStyles\plugin.py", line 164, in run
    bk.addbookpath(css_new_filename, css_new_bookpath, cssdata, mime='text/css')
  File "C:\Programs\Sigil\plugin_launchers\python\bookcontainer.py", line 457, in addbookpath
    return self._w.addbookpath(uniqueid, bookpath, data, mime)
  File "C:\Programs\Sigil\plugin_launchers\python\wrapper.py", line 721, in addbookpath
    with open(filepath, 'wb') as fp:
PermissionError: [Errno 13] Permission denied: 'C:\\sgc_styles.css'


KevinH 05-28-2021 07:18 PM

It should fail given the path is outside the epub: 'C:\\sgc_styles.css'

Are you trying to create the css file outside the epub? Or is this some strange Windows path issue. It worked just fine on macOS when I tried it.

Hmmm...

Something is very strange.

KevinH 05-28-2021 07:34 PM

Please try adding an existing css file to your epub just temporarily.

What is its resulting book path (mouse over the added css file in BookBrowser to see its full book path).

Try then running the plugin. The plugin should put the file right beside the existing css file you added temporarily? Did it or does the plugin still generate an error?

If so move the temporarily added css file into a "Styles" folder and try the plugin once again.

What errors does it generate?

If you have an epub that you can generate the error with, would you please post it someplace private (unless it is public domain) and pm me (KevinH) here onMobileread with a private link so that I can try to reproduce what settings are causing the plugin to write something to your root directory instead of to your epub plugin output folder.

KevinH 05-28-2021 07:49 PM

By any chance is your epub an epub2 with no OEBPS folder and no existing stylesheets?

Sarmat89 05-28-2021 11:15 PM

It is added to the root folder. It is a flat EPUB3 file with existing stylesheets.

The generated stylesheet was successfully added after the existing stylesheets were moved into Styles subfolder.

KevinH 05-28-2021 11:23 PM

If it has existing stylesheets, are they all stored in the root of the epub? Or are some css stored in a different place?

The addbookpath routine literally prepends the path to the plugin working directory to the file path built from its book path. This simply can not be at the root level of the C drive. As the plugin's work directory is created in temp with a unique path.

I will create an epub3 that is flat with no organizing or grouping folders but with a existing css file at the root level of the epub to see if I can recreate what you are seeing.

Is there anything else unique about your test epub?

KevinH 05-28-2021 11:33 PM

Okay, I think I can see how no folder at all could cause an issue for my plugin code but not the exact one you are seeing. But perhaps it is related. So give me a day or two and I will post a revised v031 version for you to try. Hopefully it will prevent the issue you are seeing as well.

Sarmat89 05-30-2021 07:29 PM

That can happen if you append an absolute path starting with \ or /.

KevinH 05-30-2021 08:19 PM

But I prepend a outdir to it. Would you feel comfortable hand editing the plugin.py in a text editor and trying to see if this fixes the issue. If so, here is what I would like you to try if possible:

Near line 70 in plugin.py you will see the following:

Code:

    css_new_filename = unique_filename('sgc_styles.css', namelist)
    style_folders = bk.group_to_folders("Styles","")
    css_new_bookpath = style_folders[0] + "/" + css_new_filename

Code:

    css_new_filename = unique_filename('sgc_styles.css', namelist)
    style_folders = bk.group_to_folders("Styles",[""])
    css_new_bookpath = css_new_filename
    if style_folders[0] != "":
        css_new_bookpath = style_folders[0] + "/" + css_new_filename

Notice the added "[" "]" brackets in the line that gets the folder list (it should always return a list).

And notice the test for non-empty default style_folder to prevent starting with "/" in the book path.

The spacing for the indent is 4 spaces in case it gets messed up by the post.

If you could try that and let me know if it works. If it does I will make an official new release with that change in place.

If you do not feel comfortable editing python code, just let me know and I will try to build something for you to test. I have just been so tied up with the new Sigil release. I apologize for the delay.

KevinH 05-30-2021 08:30 PM

I made the change just in case. Please give this one a try before I make an official new release.

I have my fingers crossed!

[Attachment removed - see the first post in this thread for the plugin zip archive]

Sarmat89 05-31-2021 11:48 AM

It seems to work correctly.

KevinH 05-31-2021 12:30 PM

Quote:

Originally Posted by Sarmat89 (Post 4126242)
It seems to work correctly.

Thanks for testing it.

I will make v031 the official release once I get back to my desktop machine.

KevinH

ps. It is now uploaded to the first post in this thread.


All times are GMT -4. The time now is 08:24 PM.

Powered by: vBulletin
Copyright ©2000 - 3.8.5, Jelsoft Enterprises Ltd.
MobileRead.com is a privately owned, operated and funded community.