diff options
| author | Ralph Amissah <ralph.amissah@gmail.com> | 2026-09-13 20:53:26 -0400 |
|---|---|---|
| committer | Ralph Amissah <ralph.amissah@gmail.com> | 2026-09-14 12:18:51 -0400 |
| commit | 6835275408e1629f450d163e0cc5221c977e87d2 (patch) | |
| tree | ac35771a889a4766d8efc89af5f78e91fa99e3d3 /src | |
| parent | regex: confine url within an endnote (delimiter) (diff) | |
epub and html: no errors left over the sample set
epubcheck 5.3.0 over the 35 sample epubs went from 1302 remaining errors
to none. The six causes for those remaining errors fixed here.
most of the count was one of them:
- Tables (1123 errors, 86%) where the writer emitted obsolete
attributes, removed in 2014; table also now in div instead of a
paragraph. also fixed:
- Navigation (115). Every level 4 heading's nav entry ended "#0", the
ocn of an object that has no ocn and so no id.
- Duplicate ids (37). (from three unrelated sources).
- The publication identifier (27 warnings). dc:identifier was a
hardcoded hex string, not a UUID, and the same one in all 35 files,
so two documents in one library collided. It is now UUID v5 over the
document's own uid: a real RFC 4122 identifier, stable across builds
and distinct per document.
- Image manifest entries (15). media-type was "image/" plus the file
extension, giving "image/jpg", which is not a media type; epubcheck
reads it as a foreign resource and wants a fallback. There is now a
mapping, which also covers svg and webp. Item ids came from the file
basename, and "2bits_02_01-100.png" gave an id starting with a digit,
which is not an XML name; ids are now prefixed and sanitised.
- An ocn resolving to no segment (9). A poem block and its first verse
share one ocn; the verses are written into a segment and the block
itself is not.
Also dropped from the package document: an xmlns:xsi that nothing
used, and a prefix declaration for the rendition vocabulary, which is
a reserved prefix and needed no declaring. The package now carries
xml:lang.
test-epub-validity.sh now allows no fatal and no error. Two NAV-011
warnings remain and are described in it. Three reference .ssp files
change, all of them the anchor tag disambiguation above.
(assisted by Claude-Code)
Diffstat (limited to 'src')
| -rw-r--r-- | src/sisudoc/ocda/abstraction/doc_has.d | 14 | ||||
| -rw-r--r-- | src/sisudoc/ocda/meta/metadoc_from_src.d | 28 | ||||
| -rw-r--r-- | src/sisudoc/ocda/meta/metadoc_from_src_functions.d | 1 | ||||
| -rw-r--r-- | src/sisudoc/outputs/io_out/epub3.d | 68 | ||||
| -rw-r--r-- | src/sisudoc/outputs/io_out/sqlite_collection_db.d | 81 | ||||
| -rw-r--r-- | src/sisudoc/outputs/io_out/xmls.d | 91 | ||||
| -rw-r--r-- | src/sisudoc/outputs/io_out/xmls_css.d | 108 |
7 files changed, 318 insertions, 73 deletions
diff --git a/src/sisudoc/ocda/abstraction/doc_has.d b/src/sisudoc/ocda/abstraction/doc_has.d index ecdef25..4ecf018 100644 --- a/src/sisudoc/ocda/abstraction/doc_has.d +++ b/src/sisudoc/ocda/abstraction/doc_has.d @@ -264,8 +264,18 @@ template spineDocHasFromAbstraction() { _tag_assoc[obj.metainfo.identifier]["seg_lv4"] = obj.tags.html_segment_anchor_tag_is; } - _tag_assoc[obj.metainfo.identifier]["seg_lv1to4"] - = obj.tags.epub_segment_anchor_tag_is; + /+ ↓ An object that is not written into any segment still carries an + identifier, and can share it: a poem block and its verses have one + ocn between them, the verses are written and the block itself is not. + The block comes second, so an unguarded assignment replaced a good + segment name with an empty one, and the link came out as + ".xhtml#297". seg_lv4 above is guarded already, which is why this + showed in the epub and not in the html. + +/ + if (obj.tags.epub_segment_anchor_tag_is.length > 0) { + _tag_assoc[obj.metainfo.identifier]["seg_lv1to4"] + = obj.tags.epub_segment_anchor_tag_is; + } } } _out.imagelist = _images.sort.uniq.array; diff --git a/src/sisudoc/ocda/meta/metadoc_from_src.d b/src/sisudoc/ocda/meta/metadoc_from_src.d index fb30a94..c856de3 100644 --- a/src/sisudoc/ocda/meta/metadoc_from_src.d +++ b/src/sisudoc/ocda/meta/metadoc_from_src.d @@ -95,6 +95,7 @@ template docAbstraction() { reset_note_numbers = true; lev_anchor_tag = ""; anchor_tag = ""; + _heading_anchor_tags_seen = _heading_anchor_tags_seen.init; } mixin spineNode; int[string] node_para_int_ = node_metadata_para_int; @@ -618,6 +619,18 @@ template docAbstraction() { = obj_im.obj_inline_markup_and_anchor_tags_and_misc(an_object, an_object_key, conf_make_meta, ((_new_doc) ? Yes._new_doc : No._new_doc)); an_object["substantive"] = substantive_object_and_anchor_tags_struct.obj_txt; anchor_tag = substantive_object_and_anchor_tags_struct.anchor_tag; + /+ ↓ an anchor tag names one heading. Markup can give the + same name to several ("2~x- 1.", "2~x- 2." ...), and + an id has to be unique within a document, so a repeat + takes the ocn as a suffix. The first keeps the name + the author wrote, so a link to it still arrives. + +/ + if (!(anchor_tag.empty)) { + if (anchor_tag in _heading_anchor_tags_seen) { + anchor_tag = anchor_tag ~ "-" ~ obj_cite_digits.object_number.to!string; + } + _heading_anchor_tags_seen[anchor_tag] = true; + } if (_new_doc) { cnt1 = 1; cnt2 = 1; @@ -1005,8 +1018,18 @@ template docAbstraction() { tag_assoc[(obj.metainfo.identifier)]["seg_lv4"] = obj.tags.html_segment_anchor_tag_is; } - tag_assoc[(obj.metainfo.identifier)]["seg_lv1to4"] - = obj.tags.epub_segment_anchor_tag_is; + /+ ↓ An object that is not written into any segment still carries an + identifier, and can share it: a poem block and its verses have one + ocn between them, the verses are written and the block itself is not. + The block comes second, so an unguarded assignment replaced a good + segment name with an empty one, and the link came out as + ".xhtml#297". seg_lv4 above is guarded already, which is why this + showed in the epub and not in the html. + +/ + if (!(obj.tags.epub_segment_anchor_tag_is.empty)) { + tag_assoc[(obj.metainfo.identifier)]["seg_lv1to4"] + = obj.tags.epub_segment_anchor_tag_is; + } } if (obj.metainfo.is_a == "heading") { debug(dom) { writeln(obj.text); } @@ -1582,6 +1605,7 @@ template docAbstraction() { content_non_header = "8"; lev_anchor_tag = ""; anchor_tag = ""; + _heading_anchor_tags_seen = _heading_anchor_tags_seen.init; // identify parts /+ ↓ what the document has, as plain data. ST_DocHas is in metadoc_object_setter so that it can also be built from a loaded diff --git a/src/sisudoc/ocda/meta/metadoc_from_src_functions.d b/src/sisudoc/ocda/meta/metadoc_from_src_functions.d index 77770f4..08ee22c 100644 --- a/src/sisudoc/ocda/meta/metadoc_from_src_functions.d +++ b/src/sisudoc/ocda/meta/metadoc_from_src_functions.d @@ -66,6 +66,7 @@ template docAbstractionFunctions() { string anchor_tag; string anchor_tag_; string[string] tag_in_seg; + bool[string] _heading_anchor_tags_seen; string lev_anchor_tag; string[string][string] tag_assoc; string[] lv0to3_tags; diff --git a/src/sisudoc/outputs/io_out/epub3.d b/src/sisudoc/outputs/io_out/epub3.d index b00165d..420c536 100644 --- a/src/sisudoc/outputs/io_out/epub3.d +++ b/src/sisudoc/outputs/io_out/epub3.d @@ -77,6 +77,45 @@ template outputEPub3() { .replaceAll(rgx.nbsp_char, " "); return _txt; } + /+ ↓ a manifest item id, which has to be an XML name. + An XML name may not begin with a digit, and a file quite happily + may: "2bits_02_01-100.png" gave id="2bits_02_01-100", which is not + a name. A prefix settles the first character, and anything else a + filename allows and a name does not becomes an underscore. +/ + string _manifest_id(string _prefix, string _name) { + string _id; + foreach (c; _name) { + _id ~= ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + || (c >= '0' && c <= '9') || c == '_' || c == '-' || c == '.') + ? c : '_'; + } + return _prefix ~ "_" ~ _id; + } + /+ ↓ the media type of an image, which is not its file extension. + There is no "image/jpg"; a manifest that claims one is declaring a + foreign resource, and EPUB then demands a fallback for it. The + core image types are these, and anything else genuinely is + foreign. +/ + string _image_media_type(string _ext) { + switch (_ext.toLower) { + case "jpg": case "jpeg": return "image/jpeg"; + case "png": return "image/png"; + case "gif": return "image/gif"; + case "svg": case "svgz": return "image/svg+xml"; + case "webp": return "image/webp"; + default: return "image/" ~ _ext.toLower; + } + } + /+ ↓ the publication identifier, as a real RFC 4122 UUID. + Version 5, name based: the name is the document's own uid, so the + identifier is stable across rebuilds and distinct per document, + which is what dc:identifier is for. A random v4 would be neither. + The namespace is v5 of "sisudoc.org" under the DNS namespace. +/ + string _epub_publication_uuid(string _doc_uid) { + import std.uuid : sha1UUID, dnsNamespace; + auto _ns = sha1UUID("sisudoc.org", dnsNamespace); + return sha1UUID(_doc_uid, _ns).toString; + } /+ ↓ the last modified time, as EPUB3 requires it: exactly one dcterms:modified on the package, in UTC to the second, CCYY-MM-DDThh:mm:ssZ +/ @@ -105,11 +144,10 @@ template outputEPub3() { string epub3_oebps_content(D,P)(D doc, P parts) { auto xhtml_format = outputXHTMLs(); auto pth_epub3 = spinePathsEPUB!()(doc.matters.output_path, doc.matters.src.language); - string _uuid = "18275d951861c77f78acd05672c9906924c59f18a2e0ba06dad95959693e9bd8"; // TODO sort uuid in doc.matters! + string _uuid = _epub_publication_uuid(doc.matters.src.doc_uid_out); string content = format(q"┃<?xml version="1.0" encoding="utf-8"?> - <package version="3.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="bookid" prefix="rendition: http://www.idpf.org/vocab/rendition/#"> + <package version="3.0" xmlns="http://www.idpf.org/2007/opf" unique-identifier="bookid" xml:lang="%s"> <metadata - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dc="http://purl.org/dc/elements/1.1/"> <dc:identifier id="bookid">urn:uuid:%s</dc:identifier> @@ -126,6 +164,7 @@ template outputEPub3() { <item id="css" href="%s" media-type="text/css"/> <item id="nav" href="toc_nav.xhtml" media-type="application/xhtml+xml" properties="nav" /> ┃", + doc.matters.src.language, _uuid, xhtml_format.special_characters_plain(doc.matters.conf_make_meta.meta.title_main), (doc.matters.conf_make_meta.meta.title_sub.empty) ? "" : format( @@ -145,14 +184,13 @@ template outputEPub3() { (pth_epub3.fn_oebps_css).chompPrefix("OEBPS/"), ); content ~= parts["manifest_documents"]; - // TODO sort jpg & png foreach (image; doc.matters.srcs.image_list) { - content ~= format(q"┃ <item id="%s" href="%s/%s" media-type="image/%s" /> + content ~= format(q"┃ <item id="%s" href="%s/%s" media-type="%s" /> ┃", - image.baseName.stripExtension, + _manifest_id("img", image.baseName.stripExtension), (pth_epub3.doc_oebps_image).chompPrefix("OEBPS/"), image, - image.extension.chompPrefix("."), + _image_media_type(image.extension.chompPrefix(".")), ); } content ~= " " ~ "</manifest>" ~ "\n "; @@ -251,8 +289,14 @@ template outputEPub3() { _new_title_set = true; } else { _new_title_set = false; + /+ ↓ a fragment only where there is one to reach. + A heading of level 4 or less is the top of its own + file, so the file is the target. A heading below + that sits inside a file and carries id="<ocn>", so + that is the fragment. An object with no ocn has no + id, and "#0" was pointing at nothing. +/ string _hashtag = ""; - if ((obj.metainfo.heading_lev_markup <= 4) && (obj.metainfo.ocn == 0)) { + if ((obj.metainfo.heading_lev_markup > 4) && (obj.metainfo.ocn > 0)) { _hashtag = "#" ~ obj.metainfo.ocn.to!string; } string _href = "<a href=\"" @@ -575,15 +619,15 @@ template outputEPub3() { ) { _segment_files_seen[obj.tags.segment_anchor_tag_epub] = true; oepbs_content_parts["manifest_documents"] ~= - format(q"┃<item id="%s.xhtml" href="%s.xhtml" media-type="application/xhtml+xml" /> + format(q"┃<item id="%s" href="%s.xhtml" media-type="application/xhtml+xml" /> ┃", - obj.tags.segment_anchor_tag_epub, + _manifest_id("seg", obj.tags.segment_anchor_tag_epub), obj.tags.segment_anchor_tag_epub, ); oepbs_content_parts["spine"] ~= - format(q"┃<itemref idref="%s.xhtml" linear="yes" /> + format(q"┃<itemref idref="%s" linear="yes" /> ┃", - obj.tags.segment_anchor_tag_epub, + _manifest_id("seg", obj.tags.segment_anchor_tag_epub), ); } } diff --git a/src/sisudoc/outputs/io_out/sqlite_collection_db.d b/src/sisudoc/outputs/io_out/sqlite_collection_db.d index d5b2dcf..f1556c4 100644 --- a/src/sisudoc/outputs/io_out/sqlite_collection_db.d +++ b/src/sisudoc/outputs/io_out/sqlite_collection_db.d @@ -789,35 +789,58 @@ template SQLiteFormatAndLoadObject() { string[] _table_cols; string _table; string _tablenote; + bool _in_head = false; + bool _in_body = false; foreach(row_idx, row; _table_rows) { _table_cols = row.split(rgx.table_delimiter_col); - _table ~= "<tr>"; - foreach(col_idx, cell; _table_cols) { - if ((_table_cols.length == 1) - && (_table_rows.length <= row_idx+2)) { // check row_idx+2 (rather than == ++row_idx) - _tablenote ~= cell; - } else { - string _col_is = (row_idx == 0 && obj.table.heading) ? "th" : "td"; - string _align = ("style=\"text-align:" - ~ ((obj.table.column_aligns[col_idx] == "l") - ? "left\"" : "right\"")); - _table ~= "<" - ~ _col_is - ~ " width=\"" - ~ obj.table.column_widths[col_idx].to!string - ~ "%\" " - ~ _align - ~ ">"; - _table ~= cell; - _table ~= "</" - ~ _col_is - ~ ">"; - } - } - _table ~= "</tr>"; + if ((_table_cols.length == 1) + && (_table_rows.length <= row_idx+2)) { // check row_idx+2 (rather than == ++row_idx) + _tablenote ~= _table_cols[0]; + continue; + } + /+ ↓ a heading row goes in <thead>, the rest in <tbody> +/ + if (row_idx == 0 && obj.table.heading) { + _table ~= "<thead>"; + _in_head = true; + } else if (!_in_body) { + if (_in_head) { _table ~= "</thead>"; _in_head = false; } + _table ~= "<tbody>"; + _in_body = true; + } + _table ~= "<tr>"; + foreach(col_idx, cell; _table_cols) { + bool _is_th = (row_idx == 0 && obj.table.heading); + string _col_is = _is_th ? "th" : "td"; + string _class = (col_idx < obj.table.column_aligns.length + && obj.table.column_aligns[col_idx] == "l") ? "left" : "right"; + _table ~= "<" + ~ _col_is + ~ (_is_th ? " scope=\"col\"" : "") + ~ " class=\"" + ~ _class + ~ "\">"; + _table ~= cell; + _table ~= "</" + ~ _col_is + ~ ">"; } + _table ~= "</tr>"; + } + if (_in_head) { _table ~= "</thead>"; } + else if (_in_body) { _table ~= "</tbody>"; } + /+ ↓ column widths as a <colgroup>, not a width attribute on every cell +/ + string _colgroup; + if (obj.table.column_widths.length > 0) { + string _cols; + foreach(w; obj.table.column_widths) { + _cols ~= (w > 0) + ? "<col style=\"width:" ~ w.to!string ~ "%\" />" + : "<col />"; + } + _colgroup = "<colgroup>" ~ _cols ~ "</colgroup>"; + } Tuple!(string, string) t = tuple( - _table, + (_colgroup ~ _table), _tablenote, ); return t; @@ -826,15 +849,15 @@ template SQLiteFormatAndLoadObject() { Tuple!(string, string) t = _tablarize(obj, _txt); _txt = t[0]; string _note = t[1]; - string o = format(q"┃<p class="%s"> - <table summary="normal text css" width="95%%" border="0" cellpadding="2" align="center"> + string o = format(q"┃<div class="%s"> + <table class="table"> %s </table> %s - </p>┃", + </div>┃", obj.metainfo.is_a, _txt, - _note + (_note.strip.empty) ? "" : ("<p class=\"tablenote\">" ~ _note.strip ~ "</p>") ); return o; } diff --git a/src/sisudoc/outputs/io_out/xmls.d b/src/sisudoc/outputs/io_out/xmls.d index 097ce44..c1e733e 100644 --- a/src/sisudoc/outputs/io_out/xmls.d +++ b/src/sisudoc/outputs/io_out/xmls.d @@ -205,6 +205,17 @@ template outputXHTMLs() { .replaceAll(rgx.inline_cite, ("<cite>$1</cite>")); return _txt; } + /+ ↓ the heading element for a markup level. + Markup goes to level 7; HTML stops at h6, and an <h7> is simply + not an element, so the deepest three markup levels share h6 and + are told apart by the "heading" class and their ocn. Level 0 is + the document title, which is h1 in a segment of its own. + +/ + int _heading_element_level(O)(O obj, string _xml_type) { + int _lev = obj.metainfo.heading_lev_markup.to!int; + if (_xml_type == "epub" && _lev == 0) { return 1; } + return (_lev > 6) ? 6 : _lev; + } string _xhtml_anchor_tags(O)(O obj) { string tags=""; if (obj.tags.anchor_tags.length > 0) { @@ -495,13 +506,13 @@ string tail(M)(M doc_matters) { string o; o = format(q"┃<hr /> <div class="rights"> - <p class="small" id="copyright"> + <p class="small" id="doc_footer_copyright"> <b>Copyright:</b> %s </p> </div> %s <div class="rights"> - <p class="small" id="rights"> + <p class="small" id="doc_footer_rights"> %s </p> </div> @@ -579,7 +590,9 @@ string tail(M)(M doc_matters) { if (_xml_type == "seg" || _xml_type == "epub") { seg_lvs = (_xml_type == "epub") ? "seg_lv1to4" : "seg_lv4"; foreach (m; _txt.match(rgx.inline_link_hash)) { - if (m.captures["hash"] in doc_matters.has.tag_associations) { + if ((m.captures["hash"] in doc_matters.has.tag_associations) + && (seg_lvs in doc_matters.has.tag_associations[(m.captures["hash"])]) + ) { if ( m.captures["hash"] == doc_matters.has.tag_associations[(m.captures["hash"])][seg_lvs] @@ -928,15 +941,13 @@ string tail(M)(M doc_matters) { (doc_matters.opt.action.ocn_off) ? "" : ((obj.metainfo.object_number.empty) ? "" : obj.metainfo.identifier), - ((_xml_type == "epub" && obj.metainfo.heading_lev_markup == 0) ? 1 - : obj.metainfo.heading_lev_markup), + _heading_element_level(obj, _xml_type), obj.metainfo.is_a, obj.metainfo.identifier, tags, heading_lev_anchor_tag, _txt, - ((_xml_type == "epub" && obj.metainfo.heading_lev_markup == 0) ? 1 - : obj.metainfo.heading_lev_markup), + _heading_element_level(obj, _xml_type), ); } else { o ~= format(q"┃%s @@ -946,14 +957,12 @@ string tail(M)(M doc_matters) { </h%s> </div>┃", _horizontal_rule, - ((_xml_type == "epub" && obj.metainfo.heading_lev_markup == 0) ? 1 - : obj.metainfo.heading_lev_markup), + _heading_element_level(obj, _xml_type), obj.metainfo.is_a, tags, heading_lev_anchor_tag, _txt, - ((_xml_type == "epub" && obj.metainfo.heading_lev_markup == 0) ? 1 - : obj.metainfo.heading_lev_markup), + _heading_element_level(obj, _xml_type), ); } return o; @@ -1316,6 +1325,17 @@ string tail(M)(M doc_matters) { ); return u; } + /+ ↓ column widths as a <colgroup>, not a width attribute on every cell +/ + string _table_colgroup(O)(const O obj) { + if (obj.table.column_widths.length == 0) { return ""; } + string _cols; + foreach(w; obj.table.column_widths) { + _cols ~= (w > 0) + ? "<col style=\"width:" ~ w.to!string ~ "%\" />" + : "<col />"; + } + return "<colgroup>" ~ _cols ~ "</colgroup>"; + } Tuple!(string, string) tablarize(O)( string _txt, const O obj, @@ -1324,27 +1344,42 @@ string tail(M)(M doc_matters) { string[] _table_cols; string _table; string _tablenote; + bool _in_head = false; + bool _in_body = false; foreach(row_idx, row; _table_rows) { _table_cols = row.split(rgx.table_delimiter_col); + if ((_table_cols.length == 1) + && (_table_rows.length <= row_idx+2)) { + _tablenote ~= _table_cols[0]; + continue; + } + /+ ↓ a heading row goes in <thead>, the rest in <tbody> +/ + if (row_idx == 0 && obj.table.heading) { + _table ~= "<thead>"; + _in_head = true; + } else if (!_in_body) { + if (_in_head) { _table ~= "</thead>"; _in_head = false; } + _table ~= "<tbody>"; + _in_body = true; + } _table ~= "<tr>"; foreach(col_idx, cell; _table_cols) { - if ((_table_cols.length == 1) - && (_table_rows.length <= row_idx+2)) { - _tablenote ~= cell; - } else { - string _col_is = (row_idx == 0 && obj.table.heading) ? "th" : "td"; - string _align = ("style=\"text-align:" - ~ ((obj.table.column_aligns[col_idx] == "l") - ? "left\"" : "right\"")); - _table ~= "<" ~ _col_is ~ " width=\"" ~ obj.table.column_widths[col_idx].to!string ~ "%\" " ~ _align ~ ">"; - _table ~= cell; - _table ~= "</" ~ _col_is ~ ">"; - } + bool _is_th = (row_idx == 0 && obj.table.heading); + string _col_is = _is_th ? "th" : "td"; + string _class = (col_idx < obj.table.column_aligns.length + && obj.table.column_aligns[col_idx] == "l") ? "left" : "right"; + _table ~= "<" ~ _col_is + ~ (_is_th ? " scope=\"col\"" : "") + ~ " class=\"" ~ _class ~ "\">"; + _table ~= cell; + _table ~= "</" ~ _col_is ~ ">"; } _table ~= "</tr>"; } + if (_in_head) { _table ~= "</thead>"; } + else if (_in_body) { _table ~= "</tbody>"; } Tuple!(string, string) t = tuple( - _table, + (_table_colgroup(obj) ~ _table), _tablenote, ); return t; @@ -1366,12 +1401,12 @@ string tail(M)(M doc_matters) { string o; o = format(q"┃ <div class="substance"> <label class="ocn"><a href="#%s" class="lnkocn">%s</a></label> - <p class="%s" id="%s">%s - <table summary="normal text css" width="95%%" border="0" cellpadding="2" align="center"> + <div class="%s" id="%s">%s + <table class="table"> %s </table> %s - </p> + </div> </div>┃", obj.metainfo.object_number, (doc_matters.opt.action.ocn_off) ? "" : ((obj.metainfo.object_number.empty) ? "" : obj.metainfo.identifier), @@ -1379,7 +1414,7 @@ string tail(M)(M doc_matters) { obj.metainfo.object_number, tags, _txt, - _note + (_note.strip.empty) ? "" : ("<p class=\"tablenote\">" ~ _note.strip ~ "</p>") ); return o; } diff --git a/src/sisudoc/outputs/io_out/xmls_css.d b/src/sisudoc/outputs/io_out/xmls_css.d index ed9f994..6cab68e 100644 --- a/src/sisudoc/outputs/io_out/xmls_css.d +++ b/src/sisudoc/outputs/io_out/xmls_css.d @@ -880,6 +880,24 @@ table { margin-right : 2em; background-color : inherit; } +div.table { + margin : 1em 0; + overflow-x : auto; +} +table.table { + display : table; + width : 95%%; + margin : 0 auto; + border-collapse : collapse; + background-color : inherit; +} +table.table th, table.table td { + padding : 2px; + vertical-align : top; +} +p.tablenote { + margin : 0.5em 0 0 0; +} tr { } th,td { vertical-align : top; @@ -1668,6 +1686,24 @@ table { margin-right : 2em; background-color : inherit; } +div.table { + margin : 1em 0; + overflow-x : auto; +} +table.table { + display : table; + width : 95%%; + margin : 0 auto; + border-collapse : collapse; + background-color : inherit; +} +table.table th, table.table td { + padding : 2px; + vertical-align : top; +} +p.tablenote { + margin : 0.5em 0 0 0; +} tr { } th,td { vertical-align : top; @@ -2467,6 +2503,24 @@ table { margin-right : 2em; background-color : inherit; } +div.table { + margin : 1em 0; + overflow-x : auto; +} +table.table { + display : table; + width : 95%%; + margin : 0 auto; + border-collapse : collapse; + background-color : inherit; +} +table.table th, table.table td { + padding : 2px; + vertical-align : top; +} +p.tablenote { + margin : 0.5em 0 0 0; +} tr { } th,td { vertical-align : top; @@ -3181,6 +3235,24 @@ table { margin-right : 2em; background-color : inherit; } +div.table { + margin : 1em 0; + overflow-x : auto; +} +table.table { + display : table; + width : 95%%; + margin : 0 auto; + border-collapse : collapse; + background-color : inherit; +} +table.table th, table.table td { + padding : 2px; + vertical-align : top; +} +p.tablenote { + margin : 0.5em 0 0 0; +} tr { } th,td { vertical-align : top; @@ -3906,6 +3978,24 @@ table { margin-right : 2em; background-color : inherit; } +div.table { + margin : 1em 0; + overflow-x : auto; +} +table.table { + display : table; + width : 95%%; + margin : 0 auto; + border-collapse : collapse; + background-color : inherit; +} +table.table th, table.table td { + padding : 2px; + vertical-align : top; +} +p.tablenote { + margin : 0.5em 0 0 0; +} tr { } th,td { vertical-align : top; @@ -4460,6 +4550,24 @@ table { margin-right : 2em; background-color : inherit; } +div.table { + margin : 1em 0; + overflow-x : auto; +} +table.table { + display : table; + width : 95%%; + margin : 0 auto; + border-collapse : collapse; + background-color : inherit; +} +table.table th, table.table td { + padding : 2px; + vertical-align : top; +} +p.tablenote { + margin : 0.5em 0 0 0; +} tr { } th,td { vertical-align : top; |
