|
|
#16 | |
|
Grand Sorcerer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 29,712
Karma: 212474403
Join Date: Jan 2010
Device: Nexus 7, Kindle Fire HD
|
Quote:
https://www.mobileread.com/forums/sh...7&postcount=20 I may be putting too much emphasis on the silent opf rebuild, since it's been that way for a long, long time. Any save is going to correct a missing closing tag in the opf afterall. It just seemed odd to me that a problematic epub could be opened, validated, and closed (with the pre-existing error still intact) with no warning messages whatsoever Last edited by DiapDealer; 09-04-2026 at 09:06 AM. |
|
|
|
|
|
|
#17 |
|
Sigil Developer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 9,984
Karma: 7518950
Join Date: Nov 2009
Device: many
|
It should not happen as SaveAllToDisk is used in prep for any plugin and Save by Sigil. I can not see how using bk.readotherfile in a plugin gets something different. Even if it is just missing the xml header line if SaveAllToDisk actually is working properly. So I am with you on this. Something is strange. I will explore this with some debug output in ResourceObjects/TextResource.cpp to make sure its SaveToDisk routine is properly being hit (for the opf's bookpath). The OPFResource inherits from XMLResource which in turn inherits from TextResource, so that is the routine that should be triggered unless we override it in OPFResource itself.
Last edited by KevinH; 09-04-2026 at 09:50 AM. |
|
|
|
|
|
#18 |
|
Sigil Developer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 9,984
Karma: 7518950
Join Date: Nov 2009
Device: many
|
Actually, OPFResource overrides SaveToDisk so I will take a look at it.
|
|
|
|
|
|
#19 |
|
Grand Sorcerer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 29,712
Karma: 212474403
Join Date: Jan 2010
Device: Nexus 7, Kindle Fire HD
|
For what it's worth: The latest version of the epubveri is now returning the same two errors that epubcheck did in the original post. The missing closing spine tag is not being reported by epubcheck or epubveri. So that leads me to believe that plugins are getting the same opf (corrected to add the closing spine tag) that Sigil is seeing. The corrected closing tag has not been saved to the epub yet, but both validators are ignoring it. Which leads me to believe that the opf has indeed been saved to disk (to Sigil's scratch folder) before being handed off to the plugin.
|
|
|
|
|
|
#20 |
|
Sigil Developer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 9,984
Karma: 7518950
Join Date: Nov 2009
Device: many
|
Whew! That is good to hear!
Staring at tiny code in my phone was killing me!
|
|
|
|
|
|
#21 |
|
Grand Sorcerer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 29,712
Karma: 212474403
Join Date: Jan 2010
Device: Nexus 7, Kindle Fire HD
|
I'm still a little concerned about how an epub with a bad opf can potentially be opened, validated with no errors, and then closed (without saving) with an opf error still intact. With no warnings.
But as I said, I think that particular situation has been a possibility for a long time. But the epub would usually be saved at some point, or something else would be flushed to disk to change the modified status of the epub. And if the Epub3-itizer plugin gets its fix, we should be in a good place. Last edited by DiapDealer; 09-04-2026 at 06:03 PM. |
|
|
|
|
|
#22 | |
|
Zealot
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 131
Karma: 211110
Join Date: Jul 2026
Location: Planet Earth
Device: Kobo Forma
|
Quote:
Code:
FATAL(RSC-016): content.opf(15,3): The element type "spine" must be terminated by the matching end-tag "</spine>". Code:
if id == self.opfbookpath:
if id in self.modified:
return self.build_opf()
So epubveri was not cleverer; it was looking at the file rather than at the model. Which of the two epubcheck was given in DNSB's run I cannot tell from here — run over the container it reports this fault, as above, so if it stayed quiet it was reading something else. Now the part I would like your opinion on, because this thread has found a hole in my reasoning rather than in my code. Reading the file instead of the model was a deliberate choice, and I made it for one reason: line numbers. Version 0.1.0 substituted get_opf() unconditionally, and build_opf re-serialises from Sigil's model — it sorts the manifest by id and rewrites each item — so the plugin was reporting line numbers into a document nobody could see. On one book a manifest entry sat at line 91 in the file, 95 in the rebuild and 96 in what Code View displayed. Taking the file from the ebook root put the numbers back on the text in front of the user. DNSB's book breaks that reasoning. Here Code View shows a </spine> that is not on disk, so the file and the display disagree, which is the one thing my choice was supposed to guarantee. The line numbers are then right about the file and wrong about the screen, by one line, from the spine onwards. So, two questions for the three of you, since you know Sigil far better than I do: 1. Which artefact should a validator inside Sigil report on? The container as it stands on disk is what ships if the user never saves — and it is what caught this — but it is not what they are looking at. The model is what they see and what a save will write, but it will never show them a fault Sigil has already quietly repaired. I currently do the first, and after this thread I am no longer sure that is right for an editor plugin as opposed to a command line. 2. Is there any way for a plugin to know that Sigil repaired something while loading? If there is, the honest thing is to say so in the results — "this was repaired in memory, save to write it" — rather than to report a fault the user cannot find in Code View, or to hide one that is really there. Comparing the file against build_opf() is not it: the re-serialisation differs from the original even when nothing is wrong. Either way, thank you for chasing this one down to the line in opf_newparser.py. That is a better outcome than the validator noise that started it. |
|
|
|
|
|
|
#23 |
|
Sigil Developer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 9,984
Karma: 7518950
Join Date: Nov 2009
Device: many
|
To clarify, Validators inherit from the output container so they should be using only the copy_book_contents and that in turn uses readotherfile but since a validator plugin and not the edit plugin, the opf should never be marked as modified by the plugin. So you should get the opf file itself not, the rebuild. Rebuild is used by edit plugins that can make changes to its copies and therefore the only valid opf if modified is the rebuilt one.
So in a validator plugin if the save all to disk is indeed working as expected for the opf it should result in any Sigil internal user made modifications or fixes to the opf being flushed to disk first before any plugin is launched and it should result in readotherfile returning a file that exactly matches what Sigil is showing and using internally. If that is not happening, it is a bug that I will track down and fix upon my return next week. As for changes to notify for opf fixes, they are not needed by design as Sigil requires a 100% machine readable opf at all times and so auto repairs common xml issues with each read, to prevent Sigil from losing data. That is not changing. So somewhere the built-in flush to disk the happens before any plugin is not happening as expected as it seems to forget to flush auto-repairs for some reason that I will look into. Hope this clarifies. |
|
|
|
|
|
#24 | |
|
Zealot
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 131
Karma: 211110
Join Date: Jul 2026
Location: Planet Earth
Device: Kobo Forma
|
Quote:
1. How the plugin sees the file at all — it has no other option Worth stating plainly, because it means no plugin can be doing anything clever here. wrapper.py:213 initialises self.modified to an empty OrderedDict, and every later entry comes from the running plugin's own calls. Sigil does not seed it. So the readotherfile special case can only return build_opf() for an OPF that this plugin has already changed, and a validation plugin — which changes nothing — always receives the file from ebook_root. That is not a design decision of mine; there is no other outcome available. 2. The flush looks right at every step I could follow I went looking for a place where the OPF might be skipped and could not find one:
So on that reading every plugin launch should rewrite the OPF, pretty-printed through BS4 — which would also have closed the spine tag on its way past. A cheap test of that, using what is already in this thread: DNSB attached the converted OPF in post 8. If it is neither pretty-printed in Sigil's style nor closed, then no OPFResource::SaveToDisk ever wrote it, and the question is why the call did not reach there rather than what it did when it arrived. That is a two-minute check and it would split the search in half. The one place I would instrument, and here I am guessing: SaveAllResourcesToDisk uses QtConcurrent::blockingMap, so SaveOneResourceToDisk runs off the GUI thread, and TextResource::SetText carries your own comment about delaying the QTextDocument update when it is not called from the main thread. GetText() on that path reads m_TextDocument->toText(). Printing what GetText() returns for the OPF at that moment would say whether the text being processed is the one you expect. 3. The other door, which is the part I would fix regardless Separately from the flush: launcher.py:157-158 calls container._w.write_opf() at the end of an edit plugin, and wrapper.py:413-422 writes build_opf() straight to disk as bytes — no parse, no mend, no check. Since writeotherfile refuses the OPF as a protected file (wrapper.py:929), that write-back is the only way a plugin's OPF text reaches the book, and nothing on it asks whether the text is well-formed. You have fixed Epub3-itizer, so this particular book will not happen again. But the next plugin that builds an OPF by hand walks through the same door, and the fault lands silently in the user's book rather than in the plugin author's lap. A single ElementTree parse there — refuse the write and report it — would have turned DNSB's afternoon into an error message at the moment the bad text was produced. Happy to open a PR if that is welcome, or to leave it entirely alone if you would rather decide the shape yourself. 4. A detector, if it is useful Fifteen lines, no dependencies, works on any book. Drop it in as a validation plugin and it answers "did the flush happen" in one click: Code:
import os
from xml.etree import ElementTree as ET
def run(bk):
bookpath = bk.get_opfbookpath()
raw = bk.readotherfile(bookpath)
if isinstance(raw, bytes):
raw = raw.decode("utf-8", "replace")
try:
ET.fromstring(raw)
except ET.ParseError as failure:
bk.add_result("error", bookpath, "-1",
"the OPF a plugin receives is not well-formed: %s"
% failure)
return 0
bk.add_result("info", bookpath, "-1",
"the OPF a plugin receives is well-formed (%d bytes)"
% len(raw))
return 0
One thing that made all of this cheap, in case it is useful to anyone else here: launcher.py can be driven directly, with no GUI — hand it a book directory, an output directory, "validation" and a plugin path, run it under Sigil's bundled Python, and you get the real Wrapper and the real result XML. I had been testing my plugin against a container of my own making because I had written down that Sigil could not be scripted. The window cannot; the launcher can, and every Sigil behaviour my plugin got wrong so far had been learned by installing a zip and clicking. Not chasing this — it is your bug to fix in your own time, and none of it is urgent from my side. It just seemed better to hand over what I had found than to sit on it. |
|
|
|
|
|
|
#25 |
|
Sigil Developer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 9,984
Karma: 7518950
Join Date: Nov 2009
Device: many
|
I am well familiar with the code as I designed it close to 10 years ago. And I have been programming for over 50 years and do understand how to debug. Note: ePub3-itizer is an *output* plugin not an edit plugin so it is the Sigil Importer for epub which allowed it in with a bad form, which it was designed to do to handle poor epubs. And PluginRunner does do an OPF Well-formed check before accepting the results of an edit plugin and does so for every xml and xhtml file so that is not the problem.
Either the flush to disk is not working in some way (no rtti so not clear if more explicit qobject cast is sometimes needed or that somehow a broken opf was loaded by Sigil's ImportEpub and no further call to load the opf for any Sigil tool was ever made meaning no auto repair was done. Either way we will take care of it in a few days when I am back and have access to more than my phone. Thank you but your help is not actually helping in this case. The problem is most likely in the Sigil ImportEpub code that auto repairs before any initial read but purposely does not update the opf immediately after unpacking it to prevent comments in the opf from being initially lost. Normally this does not hurt anything as whenever the opf must be read by Sigil it is auto-repaired first then saved upon a flush. So this is an odd case as no further reading of the opf seems to have been done. We will handle it from here. Last edited by KevinH; Yesterday at 03:06 PM. |
|
|
|
|
|
#26 | |
|
Zealot
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 131
Karma: 211110
Join Date: Jul 2026
Location: Planet Earth
Device: Kobo Forma
|
Quote:
I will leave it with you and watch the thread. Happy to test anything on my side once you have had a look. |
|
|
|
|
![]() |
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Coolreader Oddity? | GracieAllen | Android Devices | 0 | 02-09-2022 11:03 AM |
| Overdrive oddity | stormcloude | General Discussions | 1 | 11-14-2013 07:02 AM |
| Conversion oddity | JimLL | Conversion | 2 | 04-15-2012 04:31 PM |
| An oddity ... ? | carpetmojo | Reading Recommendations | 10 | 04-07-2012 08:10 PM |
| Oddity | Gunnerp245 | Calibre | 8 | 11-14-2010 11:25 AM |