docbook 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/CLAUDE.md +19 -0
  4. data/CODE_OF_CONDUCT.md +10 -0
  5. data/README.adoc +335 -0
  6. data/Rakefile +12 -0
  7. data/docs/.lycheeignore +33 -0
  8. data/docs/Gemfile +10 -0
  9. data/docs/INDEX.adoc +67 -0
  10. data/docs/_config.yml +186 -0
  11. data/docs/advanced/element-classes.adoc +185 -0
  12. data/docs/advanced/frontend-customization.adoc +193 -0
  13. data/docs/advanced/index.adoc +14 -0
  14. data/docs/advanced/templates.adoc +123 -0
  15. data/docs/features/element-coverage.adoc +373 -0
  16. data/docs/features/html-output/data-model.adoc +285 -0
  17. data/docs/features/html-output/directory-mode.adoc +180 -0
  18. data/docs/features/html-output/index.adoc +90 -0
  19. data/docs/features/html-output/single-file-mode.adoc +125 -0
  20. data/docs/features/index-generation.adoc +197 -0
  21. data/docs/features/index.adoc +63 -0
  22. data/docs/features/numbering.adoc +183 -0
  23. data/docs/features/toc-generation.adoc +150 -0
  24. data/docs/features/xinclude/fragid-schemes.adoc +287 -0
  25. data/docs/features/xinclude/index.adoc +119 -0
  26. data/docs/features/xinclude/text-includes.adoc +123 -0
  27. data/docs/features/xinclude/xml-includes.adoc +167 -0
  28. data/docs/getting-started/index.adoc +50 -0
  29. data/docs/getting-started/installation.adoc +113 -0
  30. data/docs/getting-started/quick-start.adoc +161 -0
  31. data/docs/guides/converting-article.adoc +188 -0
  32. data/docs/guides/converting-book.adoc +192 -0
  33. data/docs/guides/index.adoc +12 -0
  34. data/docs/guides/roundtrip-testing.adoc +129 -0
  35. data/docs/interfaces/cli/format-command.adoc +109 -0
  36. data/docs/interfaces/cli/index.adoc +73 -0
  37. data/docs/interfaces/cli/roundtrip-command.adoc +125 -0
  38. data/docs/interfaces/cli/to-html-command.adoc +178 -0
  39. data/docs/interfaces/cli/validate-command.adoc +104 -0
  40. data/docs/interfaces/index.adoc +101 -0
  41. data/docs/interfaces/ruby-api/html-output.adoc +186 -0
  42. data/docs/interfaces/ruby-api/index.adoc +111 -0
  43. data/docs/interfaces/ruby-api/parsing.adoc +202 -0
  44. data/docs/interfaces/ruby-api/xinclude.adoc +162 -0
  45. data/docs/interfaces/ruby-api/xref-resolution.adoc +156 -0
  46. data/docs/lychee.toml +42 -0
  47. data/docs/reference/cli-options.adoc +155 -0
  48. data/docs/reference/content-block-types.adoc +243 -0
  49. data/docs/reference/glossary.adoc +119 -0
  50. data/docs/reference/index.adoc +12 -0
  51. data/docs/reference/supported-elements.adoc +749 -0
  52. data/docs/understanding/architecture.adoc +145 -0
  53. data/docs/understanding/content-pipeline.adoc +102 -0
  54. data/docs/understanding/data-models.adoc +156 -0
  55. data/docs/understanding/index.adoc +34 -0
  56. data/exe/docbook +7 -0
  57. data/frontend/dist/app.css +1 -0
  58. data/frontend/dist/app.iife.js +24 -0
  59. data/frontend/package-lock.json +1445 -0
  60. data/frontend/package.json +22 -0
  61. data/frontend/src/App.vue +230 -0
  62. data/frontend/src/app.ts +8 -0
  63. data/frontend/src/components/AppSidebar.vue +116 -0
  64. data/frontend/src/components/AppendixSection.vue +39 -0
  65. data/frontend/src/components/BlockRenderer.vue +358 -0
  66. data/frontend/src/components/ChapterSection.vue +32 -0
  67. data/frontend/src/components/ContentRenderer.vue +13 -0
  68. data/frontend/src/components/EbookContainer.vue +147 -0
  69. data/frontend/src/components/EbookTopBar.vue +116 -0
  70. data/frontend/src/components/PartSection.vue +44 -0
  71. data/frontend/src/components/ReferenceEntry.vue +80 -0
  72. data/frontend/src/components/SearchModal.vue +286 -0
  73. data/frontend/src/components/SectionContent.vue +31 -0
  74. data/frontend/src/components/SettingsPanel.vue +236 -0
  75. data/frontend/src/components/TocTreeItem.vue +135 -0
  76. data/frontend/src/composables/useEbookStore.ts +191 -0
  77. data/frontend/src/composables/useSearch.ts +249 -0
  78. data/frontend/src/env.d.ts +7 -0
  79. data/frontend/src/stores/documentStore.ts +221 -0
  80. data/frontend/src/stores/uiStore.ts +98 -0
  81. data/frontend/src/styles.css +253 -0
  82. data/frontend/tsconfig.json +24 -0
  83. data/frontend/tsconfig.node.json +11 -0
  84. data/frontend/vite.config.ts +30 -0
  85. data/lib/docbook/cli.rb +123 -0
  86. data/lib/docbook/document.rb +67 -0
  87. data/lib/docbook/elements/abbrev.rb +16 -0
  88. data/lib/docbook/elements/acknowledgements.rb +22 -0
  89. data/lib/docbook/elements/address.rb +18 -0
  90. data/lib/docbook/elements/alt.rb +16 -0
  91. data/lib/docbook/elements/annotation.rb +18 -0
  92. data/lib/docbook/elements/appendix.rb +34 -0
  93. data/lib/docbook/elements/article.rb +31 -0
  94. data/lib/docbook/elements/att.rb +15 -0
  95. data/lib/docbook/elements/attribution.rb +20 -0
  96. data/lib/docbook/elements/audioobject.rb +18 -0
  97. data/lib/docbook/elements/author.rb +18 -0
  98. data/lib/docbook/elements/bibliography.rb +24 -0
  99. data/lib/docbook/elements/bibliomixed.rb +40 -0
  100. data/lib/docbook/elements/biblioref.rb +20 -0
  101. data/lib/docbook/elements/blockquote.rb +26 -0
  102. data/lib/docbook/elements/book.rb +36 -0
  103. data/lib/docbook/elements/buildtarget.rb +16 -0
  104. data/lib/docbook/elements/callout.rb +22 -0
  105. data/lib/docbook/elements/calloutlist.rb +22 -0
  106. data/lib/docbook/elements/caution.rb +30 -0
  107. data/lib/docbook/elements/chapter.rb +62 -0
  108. data/lib/docbook/elements/citation.rb +16 -0
  109. data/lib/docbook/elements/citerefentry.rb +26 -0
  110. data/lib/docbook/elements/citetitle.rb +20 -0
  111. data/lib/docbook/elements/classname.rb +16 -0
  112. data/lib/docbook/elements/code.rb +16 -0
  113. data/lib/docbook/elements/colophon.rb +22 -0
  114. data/lib/docbook/elements/computeroutput.rb +18 -0
  115. data/lib/docbook/elements/copyright.rb +17 -0
  116. data/lib/docbook/elements/danger.rb +28 -0
  117. data/lib/docbook/elements/date.rb +16 -0
  118. data/lib/docbook/elements/dedication.rb +22 -0
  119. data/lib/docbook/elements/dir.rb +16 -0
  120. data/lib/docbook/elements/emphasis.rb +18 -0
  121. data/lib/docbook/elements/entry.rb +30 -0
  122. data/lib/docbook/elements/entrytbl.rb +22 -0
  123. data/lib/docbook/elements/equation.rb +26 -0
  124. data/lib/docbook/elements/example.rb +30 -0
  125. data/lib/docbook/elements/fieldsynopsis.rb +21 -0
  126. data/lib/docbook/elements/figure.rb +28 -0
  127. data/lib/docbook/elements/filename.rb +16 -0
  128. data/lib/docbook/elements/firstname.rb +16 -0
  129. data/lib/docbook/elements/firstterm.rb +18 -0
  130. data/lib/docbook/elements/footnote.rb +22 -0
  131. data/lib/docbook/elements/footnoteref.rb +21 -0
  132. data/lib/docbook/elements/foreignphrase.rb +18 -0
  133. data/lib/docbook/elements/formalpara.rb +20 -0
  134. data/lib/docbook/elements/function.rb +16 -0
  135. data/lib/docbook/elements/glossary.rb +24 -0
  136. data/lib/docbook/elements/glossdef.rb +18 -0
  137. data/lib/docbook/elements/glossentry.rb +26 -0
  138. data/lib/docbook/elements/glosssee.rb +18 -0
  139. data/lib/docbook/elements/glossseealso.rb +18 -0
  140. data/lib/docbook/elements/glossterm.rb +18 -0
  141. data/lib/docbook/elements/holder.rb +16 -0
  142. data/lib/docbook/elements/honorific.rb +16 -0
  143. data/lib/docbook/elements/imagedata.rb +29 -0
  144. data/lib/docbook/elements/imageobject.rb +22 -0
  145. data/lib/docbook/elements/important.rb +30 -0
  146. data/lib/docbook/elements/index.rb +26 -0
  147. data/lib/docbook/elements/indexdiv.rb +22 -0
  148. data/lib/docbook/elements/indexentry.rb +22 -0
  149. data/lib/docbook/elements/indexterm.rb +34 -0
  150. data/lib/docbook/elements/info.rb +25 -0
  151. data/lib/docbook/elements/informalexample.rb +24 -0
  152. data/lib/docbook/elements/informalfigure.rb +20 -0
  153. data/lib/docbook/elements/inlinemediaobject.rb +22 -0
  154. data/lib/docbook/elements/itemizedlist.rb +21 -0
  155. data/lib/docbook/elements/legalnotice.rb +22 -0
  156. data/lib/docbook/elements/link.rb +26 -0
  157. data/lib/docbook/elements/listitem.rb +32 -0
  158. data/lib/docbook/elements/literal.rb +16 -0
  159. data/lib/docbook/elements/literallayout.rb +22 -0
  160. data/lib/docbook/elements/mediaobject.rb +26 -0
  161. data/lib/docbook/elements/msg.rb +20 -0
  162. data/lib/docbook/elements/msgexplan.rb +22 -0
  163. data/lib/docbook/elements/msgset.rb +22 -0
  164. data/lib/docbook/elements/note.rb +30 -0
  165. data/lib/docbook/elements/orderedlist.rb +23 -0
  166. data/lib/docbook/elements/orgname.rb +16 -0
  167. data/lib/docbook/elements/para.rb +61 -0
  168. data/lib/docbook/elements/paragraph_like.rb +18 -0
  169. data/lib/docbook/elements/parameter.rb +16 -0
  170. data/lib/docbook/elements/part.rb +38 -0
  171. data/lib/docbook/elements/personname.rb +22 -0
  172. data/lib/docbook/elements/phrase.rb +20 -0
  173. data/lib/docbook/elements/preface.rb +58 -0
  174. data/lib/docbook/elements/primary.rb +16 -0
  175. data/lib/docbook/elements/procedure.rb +24 -0
  176. data/lib/docbook/elements/productname.rb +18 -0
  177. data/lib/docbook/elements/productnumber.rb +16 -0
  178. data/lib/docbook/elements/programlisting.rb +28 -0
  179. data/lib/docbook/elements/property.rb +16 -0
  180. data/lib/docbook/elements/pubdate.rb +16 -0
  181. data/lib/docbook/elements/publishername.rb +16 -0
  182. data/lib/docbook/elements/quotation.rb +24 -0
  183. data/lib/docbook/elements/quote.rb +18 -0
  184. data/lib/docbook/elements/refentry.rb +32 -0
  185. data/lib/docbook/elements/refentrytitle.rb +16 -0
  186. data/lib/docbook/elements/reference.rb +26 -0
  187. data/lib/docbook/elements/refmeta.rb +29 -0
  188. data/lib/docbook/elements/refmiscinfo.rb +16 -0
  189. data/lib/docbook/elements/refname.rb +16 -0
  190. data/lib/docbook/elements/refnamediv.rb +22 -0
  191. data/lib/docbook/elements/refpurpose.rb +16 -0
  192. data/lib/docbook/elements/refsect1.rb +22 -0
  193. data/lib/docbook/elements/refsect2.rb +22 -0
  194. data/lib/docbook/elements/refsect3.rb +22 -0
  195. data/lib/docbook/elements/refsection.rb +32 -0
  196. data/lib/docbook/elements/releaseinfo.rb +16 -0
  197. data/lib/docbook/elements/remark.rb +20 -0
  198. data/lib/docbook/elements/replaceable.rb +16 -0
  199. data/lib/docbook/elements/row.rb +15 -0
  200. data/lib/docbook/elements/screen.rb +28 -0
  201. data/lib/docbook/elements/secondary.rb +16 -0
  202. data/lib/docbook/elements/sect1.rb +26 -0
  203. data/lib/docbook/elements/sect2.rb +24 -0
  204. data/lib/docbook/elements/sect3.rb +24 -0
  205. data/lib/docbook/elements/sect4.rb +24 -0
  206. data/lib/docbook/elements/sect5.rb +22 -0
  207. data/lib/docbook/elements/section.rb +66 -0
  208. data/lib/docbook/elements/see.rb +16 -0
  209. data/lib/docbook/elements/seealso.rb +18 -0
  210. data/lib/docbook/elements/set.rb +26 -0
  211. data/lib/docbook/elements/setindex.rb +24 -0
  212. data/lib/docbook/elements/sidebar.rb +22 -0
  213. data/lib/docbook/elements/simplesect.rb +32 -0
  214. data/lib/docbook/elements/step.rb +26 -0
  215. data/lib/docbook/elements/substeps.rb +18 -0
  216. data/lib/docbook/elements/subtitle.rb +16 -0
  217. data/lib/docbook/elements/surname.rb +16 -0
  218. data/lib/docbook/elements/table.rb +24 -0
  219. data/lib/docbook/elements/tag.rb +20 -0
  220. data/lib/docbook/elements/tbody.rb +15 -0
  221. data/lib/docbook/elements/term.rb +37 -0
  222. data/lib/docbook/elements/tertiary.rb +16 -0
  223. data/lib/docbook/elements/textobject.rb +18 -0
  224. data/lib/docbook/elements/tfoot.rb +15 -0
  225. data/lib/docbook/elements/tgroup.rb +21 -0
  226. data/lib/docbook/elements/thead.rb +15 -0
  227. data/lib/docbook/elements/tip.rb +30 -0
  228. data/lib/docbook/elements/title.rb +16 -0
  229. data/lib/docbook/elements/toc.rb +24 -0
  230. data/lib/docbook/elements/tocdiv.rb +22 -0
  231. data/lib/docbook/elements/tocentry.rb +20 -0
  232. data/lib/docbook/elements/topic.rb +26 -0
  233. data/lib/docbook/elements/type.rb +16 -0
  234. data/lib/docbook/elements/uri.rb +16 -0
  235. data/lib/docbook/elements/userinput.rb +18 -0
  236. data/lib/docbook/elements/variable.rb +16 -0
  237. data/lib/docbook/elements/variablelist.rb +19 -0
  238. data/lib/docbook/elements/varlistentry.rb +17 -0
  239. data/lib/docbook/elements/version.rb +16 -0
  240. data/lib/docbook/elements/videoobject.rb +18 -0
  241. data/lib/docbook/elements/warning.rb +30 -0
  242. data/lib/docbook/elements/xref.rb +18 -0
  243. data/lib/docbook/elements/year.rb +16 -0
  244. data/lib/docbook/elements.rb +204 -0
  245. data/lib/docbook/models/document_metadata.rb +30 -0
  246. data/lib/docbook/models/document_root.rb +33 -0
  247. data/lib/docbook/models/index_entry.rb +28 -0
  248. data/lib/docbook/models/reading_position.rb +22 -0
  249. data/lib/docbook/models/section_number.rb +18 -0
  250. data/lib/docbook/models/section_root.rb +29 -0
  251. data/lib/docbook/models/toc_node.rb +24 -0
  252. data/lib/docbook/models.rb +16 -0
  253. data/lib/docbook/output/data.rb +196 -0
  254. data/lib/docbook/output/html.rb +1450 -0
  255. data/lib/docbook/output/index_generator.rb +281 -0
  256. data/lib/docbook/output.rb +8 -0
  257. data/lib/docbook/services/document_builder.rb +258 -0
  258. data/lib/docbook/services/element_to_hash.rb +287 -0
  259. data/lib/docbook/services/index_generator.rb +134 -0
  260. data/lib/docbook/services/numbering_service.rb +186 -0
  261. data/lib/docbook/services/toc_generator.rb +138 -0
  262. data/lib/docbook/services.rb +14 -0
  263. data/lib/docbook/templates/document.html.liquid +20 -0
  264. data/lib/docbook/templates/partials/_head.liquid +39 -0
  265. data/lib/docbook/templates/partials/_scripts.liquid +10 -0
  266. data/lib/docbook/version.rb +5 -0
  267. data/lib/docbook/xinclude_resolver.rb +217 -0
  268. data/lib/docbook/xref_resolver.rb +78 -0
  269. data/lib/docbook.rb +17 -0
  270. data/sig/docbook.rbs +4 -0
  271. metadata +385 -0
@@ -0,0 +1,186 @@
1
+ ---
2
+ layout: default
3
+ title: HTML Output
4
+ parent: Ruby API
5
+ grand_parent: Interfaces
6
+ nav_order: 2
7
+ ---
8
+
9
+ == HTML Output
10
+
11
+ The `Docbook::Output::Html` class generates HTML from a parsed DocBook document.
12
+
13
+ === Constructor
14
+
15
+ [source,ruby]
16
+ ----
17
+ html_generator = Docbook::Output::Html.new(
18
+ document, # Parsed DocBook element (required)
19
+ xref_resolver: xref_resolver, # XrefResolver instance (optional)
20
+ output_mode: :single_file, # :single_file or :directory
21
+ base_path: "/path/to/xml/dir", # Base path for image resolution
22
+ output_path: "/path/to/output" # Output directory (required for directory mode)
23
+ )
24
+ ----
25
+
26
+ ==== Parameters
27
+
28
+ [cols="2,1,5"]
29
+ |===
30
+ | Parameter | Required | Description
31
+
32
+ | `document` | Yes | A parsed DocBook element from `Document.from_xml`
33
+ | `xref_resolver` | No | A `Docbook::XrefResolver` instance (call `resolve!` first). Used to resolve `xref` elements to target titles.
34
+ | `output_mode` | No | `:single_file` (default) or `:directory`. Controls how images and assets are handled.
35
+ | `base_path` | No | Directory containing the source XML file. Used to locate images referenced by relative paths.
36
+ | `output_path` | No | Output directory for directory mode. Required when `output_mode` is `:directory`.
37
+ |===
38
+
39
+ === Generating HTML
40
+
41
+ Call `to_html` on the generator instance to produce the HTML output:
42
+
43
+ [source,ruby]
44
+ ----
45
+ html = html_generator.to_html
46
+ # => "<!DOCTYPE html><html>..." (String)
47
+ ----
48
+
49
+ === Output Modes
50
+
51
+ ==== Single-File Mode
52
+
53
+ Produces a self-contained HTML string with all assets embedded:
54
+
55
+ [source,ruby]
56
+ ----
57
+ require "docbook"
58
+
59
+ xml_string = File.read("article.xml")
60
+ document = Docbook::Document.from_xml(xml_string)
61
+ xref_resolver = Docbook::XrefResolver.new(document).resolve!
62
+
63
+ html = Docbook::Output::Html.new(
64
+ document,
65
+ xref_resolver: xref_resolver,
66
+ output_mode: :single_file,
67
+ base_path: File.dirname(File.expand_path("article.xml"))
68
+ ).to_html
69
+
70
+ File.write("output.html", html)
71
+ ----
72
+
73
+ In single-file mode:
74
+
75
+ * CSS and JavaScript are embedded inline in the HTML
76
+ * Images are converted to Base64 data URIs (using the `Marcel` gem for MIME type detection)
77
+ * All content data (TOC, sections, index) is embedded in `<script>` tags as JSON
78
+ * The resulting file is completely self-contained
79
+
80
+ ==== Directory Mode
81
+
82
+ Produces an HTML shell plus separate data and asset files:
83
+
84
+ [source,ruby]
85
+ ----
86
+ require "docbook"
87
+ require "fileutils"
88
+
89
+ xml_string = File.read("book.xml")
90
+ resolved = Docbook::XIncludeResolver.resolve_string(xml_string, base_path: "book.xml")
91
+ document = Docbook::Document.from_xml(resolved.to_xml)
92
+ xref_resolver = Docbook::XrefResolver.new(document).resolve!
93
+
94
+ output_dir = "/var/www/html/book"
95
+ FileUtils.mkdir_p(output_dir)
96
+
97
+ html = Docbook::Output::Html.new(
98
+ document,
99
+ xref_resolver: xref_resolver,
100
+ output_mode: :directory,
101
+ base_path: File.dirname(File.expand_path("book.xml")),
102
+ output_path: output_dir
103
+ ).to_html
104
+
105
+ # The generator writes docbook.data.json to output_path automatically
106
+ # The returned HTML string is the index.html content
107
+ File.write(File.join(output_dir, "index.html"), html)
108
+ ----
109
+
110
+ In directory mode, the `to_html` method also writes `docbook.data.json` to the `output_path` directory. The directory structure is:
111
+
112
+ [source]
113
+ ----
114
+ output_dir/
115
+ index.html # HTML returned by to_html
116
+ docbook.data.json # Written automatically by to_html
117
+ ----
118
+
119
+ [NOTE]
120
+ ====
121
+ The CLI's `write_output` method handles copying `app.css` and `app.iife.js` to the `assets/` subdirectory. When using the Ruby API directly, you are responsible for copying the frontend assets if you need them.
122
+ ====
123
+
124
+ === Cross-Reference Resolution
125
+
126
+ The `xref_resolver` parameter is used to resolve `<xref>` elements in the document. When an `xref` element has a `linkend` attribute, the resolver looks up the target element by its `xml:id` and returns the title text:
127
+
128
+ [source,ruby]
129
+ ----
130
+ # Without xref_resolver, xref elements display the raw linkend value
131
+ # With xref_resolver, xref elements display the target's title
132
+ xref_resolver = Docbook::XrefResolver.new(document).resolve!
133
+
134
+ html = Docbook::Output::Html.new(
135
+ document,
136
+ xref_resolver: xref_resolver,
137
+ output_mode: :single_file
138
+ ).to_html
139
+ ----
140
+
141
+ === Image Handling
142
+
143
+ Images are handled differently based on the output mode:
144
+
145
+ * *Single-file mode:* Images are read from disk, Base64-encoded, and embedded as data URIs
146
+ * *Directory mode:* Images reference their original file paths (relative to the output directory)
147
+
148
+ The `base_path` parameter is used to locate image files on disk. The generator searches the base path and all parent directories to find referenced images. It also checks common prefixes like `resources/` and `../resources/`.
149
+
150
+ === Generated Data Structure
151
+
152
+ The `to_html` method builds a structured data model internally before rendering. This model includes:
153
+
154
+ [cols="2,5"]
155
+ |===
156
+ | Component | Description
157
+
158
+ | *Title* | The document title extracted from `<info><title>` or the root element's `<title>`
159
+ | *TOC* | Hierarchical table of contents with section IDs, titles, and types
160
+ | *Numbering* | Computed numbering for parts (Roman), chapters (Arabic), appendices (Alpha), and sections (hierarchical)
161
+ | *Content* | Section content organized by section ID, with blocks for paragraphs, code, images, lists, etc.
162
+ | *Index* | Collected `indexterm` elements grouped alphabetically with see/see-also support
163
+ |===
164
+
165
+ === Supported Content Types
166
+
167
+ The HTML generator processes the following block-level content types from each section:
168
+
169
+ * Paragraphs (`para`, `formalpara`)
170
+ * Code listings (`programlisting`, `screen`, `literallayout`)
171
+ * Images and figures (`mediaobject`, `figure`, `informalfigure`, `inlinemediaobject`)
172
+ * Lists (`orderedlist`, `itemizedlist`, `variablelist`)
173
+ * Admonitions (`note`, `warning`, `caution`, `important`, `tip`, `danger`)
174
+ * Examples (`example`, `informalexample`)
175
+ * Block quotes (`blockquote`)
176
+ * Glossary entries (`glossentry`)
177
+ * Bibliography entries (`bibliomixed`)
178
+ * Reference entries (`refentry`)
179
+ * Index sections (`index`, `setindex`)
180
+ * Nested sections (`section`, `simplesect`)
181
+
182
+ === See Also
183
+
184
+ * *<<parsing,Parsing Documents>>* -- Parse XML before generating HTML
185
+ * *<<xref-resolution,XRef Resolution>>* -- Resolve cross-references for the HTML generator
186
+ * *<<xinclude,XInclude Resolution>>* -- Resolve XIncludes before parsing
@@ -0,0 +1,111 @@
1
+ ---
2
+ layout: default
3
+ title: Ruby API
4
+ parent: Interfaces
5
+ nav_order: 2
6
+ has_children: true
7
+ ---
8
+
9
+ == Ruby API
10
+
11
+ The `docbook` gem provides a programmatic Ruby API for parsing, processing, and converting DocBook XML documents. This is the same API that powers the CLI, exposed for direct use in Ruby applications.
12
+
13
+ === Getting Started
14
+
15
+ Add the gem to your project and require it:
16
+
17
+ [source,ruby]
18
+ ----
19
+ require "docbook"
20
+ ----
21
+
22
+ All public classes are under the `Docbook` module namespace.
23
+
24
+ === Core Classes
25
+
26
+ The Ruby API revolves around four core classes:
27
+
28
+ [cols="3,5"]
29
+ |===
30
+ | Class | Purpose
31
+
32
+ | `Docbook::Document` | Entry point for parsing XML strings into typed Ruby element models. Supports 39 root element types.
33
+ | `Docbook::XIncludeResolver` | Resolves `xi:include` elements in XML documents. Must be called *before* parsing with `Document.from_xml`.
34
+ | `Docbook::XrefResolver` | Builds an `xml:id` lookup map from a parsed document and resolves `xref`/`linkend` references to titles.
35
+ | `Docbook::Output::Html` | Generates HTML from a parsed document in single-file or directory mode.
36
+ |===
37
+
38
+ === Processing Pipeline
39
+
40
+ A typical processing pipeline has four stages:
41
+
42
+ [source,ruby]
43
+ ----
44
+ require "docbook"
45
+
46
+ # Stage 1: Read the XML file
47
+ xml_string = File.read("book.xml")
48
+
49
+ # Stage 2: Resolve XIncludes (optional, but usually needed)
50
+ resolved = Docbook::XIncludeResolver.resolve_string(xml_string, base_path: "book.xml")
51
+
52
+ # Stage 3: Parse into Ruby element models
53
+ document = Docbook::Document.from_xml(resolved.to_xml)
54
+
55
+ # Stage 4: Resolve cross-references
56
+ xref_resolver = Docbook::XrefResolver.new(document).resolve!
57
+
58
+ # Stage 5: Generate HTML
59
+ html = Docbook::Output::Html.new(
60
+ document,
61
+ xref_resolver: xref_resolver,
62
+ output_mode: :single_file,
63
+ base_path: File.dirname(File.expand_path("book.xml"))
64
+ ).to_html
65
+
66
+ File.write("output.html", html)
67
+ ----
68
+
69
+ [TIP]
70
+ ====
71
+ Stages 2 and 4 are optional but recommended. XInclude resolution should happen *before* parsing because the parser needs a single, complete XML document. Cross-reference resolution should happen *after* parsing so the resolver can traverse the element tree.
72
+ ====
73
+
74
+ === Error Handling
75
+
76
+ The gem defines a base error class `Docbook::Error` that is raised for invalid input:
77
+
78
+ [source,ruby]
79
+ ----
80
+ begin
81
+ Docbook::Document.from_xml("<invalid>not docbook</invalid>")
82
+ rescue Docbook::Error => e
83
+ puts "Parse error: #{e.message}"
84
+ # => "Unsupported DocBook root element: invalid"
85
+ end
86
+ ----
87
+
88
+ Common error scenarios:
89
+
90
+ * Empty or invalid XML -- raises `Docbook::Error, "Empty or invalid XML document"`
91
+ * Unsupported root element -- raises `Docbook::Error, "Unsupported DocBook root element: <name>"`
92
+ * Missing file -- raises `Errno::ENOENT` from `File.read`
93
+
94
+ === Element Models
95
+
96
+ All DocBook elements are modeled as `Lutaml::Model::Serializable` subclasses under the `Docbook::Elements` namespace. These models provide:
97
+
98
+ * *Typed attributes* -- Every XML element maps to a Ruby class with typed attributes (strings, collections, nested models)
99
+ * *Mixed content* -- Elements that support mixed content (text interspersed with child elements) provide an `each_mixed_content` method for traversal
100
+ * *XML round-tripping* -- Models can be serialized back to XML via `to_xml`
101
+
102
+ For example, a `<book>` element is represented as `Docbook::Elements::Book` with attributes for `info`, `part`, `chapter`, `appendix`, `preface`, `bibliography`, `glossary`, and `index`.
103
+
104
+ === Detailed Documentation
105
+
106
+ Explore each component in detail:
107
+
108
+ * *<<parsing,Parsing Documents>>* -- How to parse XML and access element models
109
+ * *<<html-output,HTML Output>>* -- Generating HTML in single-file and directory modes
110
+ * *<<xinclude,XInclude Resolution>>* -- Resolving `xi:include` elements
111
+ * *<<xref-resolution,XRef Resolution>>* -- Building ID maps and resolving cross-references
@@ -0,0 +1,202 @@
1
+ ---
2
+ layout: default
3
+ title: Parsing Documents
4
+ parent: Ruby API
5
+ grand_parent: Interfaces
6
+ nav_order: 1
7
+ ---
8
+
9
+ == Parsing Documents
10
+
11
+ The `Docbook::Document` class is the primary entry point for parsing DocBook XML into typed Ruby objects.
12
+
13
+ === Basic Parsing
14
+
15
+ Use `Document.from_xml` to parse an XML string:
16
+
17
+ [source,ruby]
18
+ ----
19
+ require "docbook"
20
+
21
+ xml = <<~XML
22
+ <article xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="sample">
23
+ <info>
24
+ <title>Sample Article</title>
25
+ </info>
26
+ <section xml:id="intro">
27
+ <title>Introduction</title>
28
+ <para>Hello, DocBook world.</para>
29
+ </section>
30
+ </article>
31
+ XML
32
+
33
+ document = Docbook::Document.from_xml(xml)
34
+ # => #<Docbook::Elements::Article:0x0000...>
35
+ ----
36
+
37
+ The method returns an instance of the appropriate element class based on the root element name. In this case, it returns a `Docbook::Elements::Article` because the root element is `<article>`.
38
+
39
+ === Supported Root Elements
40
+
41
+ `Document.from_xml` automatically detects the root element and selects the correct parser. The gem supports 39 root element types:
42
+
43
+ [cols="3,3,3"]
44
+ |===
45
+ | Structural | Sectioning | Reference
46
+
47
+ | `article` | `section` | `refentry`
48
+ | `book` | `sect1` | `reference`
49
+ | `chapter` | `sect2` | `refsection`
50
+ | `appendix` | `sect3` | `refsect1`
51
+ | `preface` | `sect4` | `refsect2`
52
+ | `part` | `sect5` | `refsect3`
53
+ | `set` | `simplesect` |
54
+ | `topic` | `para` |
55
+ |===
56
+
57
+ [cols="3,3,3"]
58
+ |===
59
+ | Front Matter | Back Matter | Other
60
+
61
+ | `dedication` | `bibliography` | `index`
62
+ | `acknowledgements` | `glossary` | `setindex`
63
+ | `colophon` | | `toc`
64
+ |===
65
+
66
+ === Checking Root Element Support
67
+
68
+ Before parsing, you can check whether a specific root element is supported:
69
+
70
+ [source,ruby]
71
+ ----
72
+ # Check if a root element name is supported
73
+ Docbook::Document.supports?("article") # => true
74
+ Docbook::Document.supports?("html") # => false
75
+
76
+ # List all supported root element names
77
+ Docbook::Document.supported_root_elements
78
+ # => ["article", "book", "chapter", "appendix", "preface", "part",
79
+ # "section", "refentry", "bibliography", "glossary", "set",
80
+ # "reference", "topic", "dedication", "acknowledgements",
81
+ # "colophon", "index", "toc", "sect1", "sect2", "sect3",
82
+ # "sect4", "sect5", "refsection", "refsect1", "refsect2",
83
+ # "refsect3", "setindex", "para", "simplesect"]
84
+ ----
85
+
86
+ === Error Handling
87
+
88
+ Parsing errors raise `Docbook::Error`:
89
+
90
+ [source,ruby]
91
+ ----
92
+ # Empty XML
93
+ Docbook::Document.from_xml("")
94
+ # => raises Docbook::Error: "Empty or invalid XML document"
95
+
96
+ # Unsupported root element
97
+ Docbook::Document.from_xml("<html><body>Not DocBook</body></html>")
98
+ # => raises Docbook::Error: "Unsupported DocBook root element: html"
99
+ ----
100
+
101
+ Always wrap parsing in a rescue block for robust applications:
102
+
103
+ [source,ruby]
104
+ ----
105
+ begin
106
+ document = Docbook::Document.from_xml(xml_string)
107
+ rescue Docbook::Error => e
108
+ warn "Failed to parse document: #{e.message}"
109
+ exit 1
110
+ end
111
+ ----
112
+
113
+ === Accessing Parsed Elements
114
+
115
+ Once parsed, the returned object is a fully populated element model. The available attributes depend on the element type:
116
+
117
+ [source,ruby]
118
+ ----
119
+ # Parse a book
120
+ document = Docbook::Document.from_xml(book_xml)
121
+ # document is a Docbook::Elements::Book
122
+
123
+ document.xml_id # => "my-book" (the xml:id attribute)
124
+ document.version # => "5.0"
125
+ document.info # => #<Docbook::Elements::Info:...>
126
+ document.info.title # => #<Docbook::Elements::Title:...>
127
+ document.info.title.content # => "My Book Title"
128
+ document.chapter # => [#<Docbook::Elements::Chapter:...>, ...]
129
+ document.appendix # => [#<Docbook::Elements::Appendix:...>, ...]
130
+ document.part # => [#<Docbook::Elements::Part:...>, ...]
131
+ document.preface # => [#<Docbook::Elements::Preface:...>, ...]
132
+ ----
133
+
134
+ [source,ruby]
135
+ ----
136
+ # Parse an article
137
+ document = Docbook::Document.from_xml(article_xml)
138
+ # document is a Docbook::Elements::Article
139
+
140
+ document.info.title.content # => "Article Title"
141
+ document.section # => [#<Docbook::Elements::Section:...>, ...]
142
+ document.section.first.title # => #<Docbook::Elements::Title:...>
143
+ document.section.first.title.content # => "Introduction"
144
+ ----
145
+
146
+ === Mixed Content Traversal
147
+
148
+ Many DocBook elements support *mixed content* -- a mixture of text strings and child elements. The `each_mixed_content` method yields each child node in document order:
149
+
150
+ [source,ruby]
151
+ ----
152
+ section = document.section.first
153
+ section.each_mixed_content do |node|
154
+ case node
155
+ when String
156
+ # Raw text content between elements
157
+ puts "Text: #{node}"
158
+ when Docbook::Elements::Para
159
+ puts "Paragraph: #{node.content}"
160
+ when Docbook::Elements::MediaObject
161
+ puts "Media object"
162
+ when Docbook::Elements::ProgramListing
163
+ puts "Code listing: #{node.content}"
164
+ when Docbook::Elements::OrderedList
165
+ puts "Ordered list"
166
+ # ... many more element types
167
+ end
168
+ end
169
+ ----
170
+
171
+ This is the primary mechanism for traversing element content in the HTML generation pipeline. The `each_mixed_content` method yields both `String` instances (for text nodes) and typed element objects (for child elements).
172
+
173
+ === File-Based Parsing
174
+
175
+ A complete file-based parsing workflow:
176
+
177
+ [source,ruby]
178
+ ----
179
+ require "docbook"
180
+
181
+ # Read the file
182
+ xml_string = File.read("book.xml")
183
+
184
+ # Optionally resolve XIncludes first
185
+ resolved_xml = Docbook::XIncludeResolver.resolve_string(xml_string, base_path: "book.xml")
186
+
187
+ # Parse
188
+ document = Docbook::Document.from_xml(resolved_xml.to_xml)
189
+
190
+ # Work with the parsed document
191
+ puts "Title: #{document.info&.title&.content}"
192
+ puts "Chapters: #{document.chapter&.size || 0}"
193
+ document.chapter&.each do |chapter|
194
+ puts " - #{chapter.title&.content}"
195
+ end
196
+ ----
197
+
198
+ === See Also
199
+
200
+ * *<<html-output,HTML Output>>* -- Convert parsed documents to HTML
201
+ * *<<xinclude,XInclude Resolution>>* -- Resolve XIncludes before parsing
202
+ * *<<xref-resolution,XRef Resolution>>* -- Build cross-reference maps from parsed documents
@@ -0,0 +1,162 @@
1
+ ---
2
+ layout: default
3
+ title: XInclude Resolution
4
+ parent: Ruby API
5
+ grand_parent: Interfaces
6
+ nav_order: 3
7
+ ---
8
+
9
+ == XInclude Resolution
10
+
11
+ The `Docbook::XIncludeResolver` class resolves http://www.w3.org/2001/XInclude/[XInclude] elements in DocBook XML documents. XInclude resolution must happen *before* parsing with `Document.from_xml`, because the parser needs a single, complete XML document.
12
+
13
+ === Why Resolve XIncludes First
14
+
15
+ DocBook documents are often modular: a book may include chapters from separate files using `<xi:include>` elements. The `Document.from_xml` parser expects a single XML document with no unresolved XIncludes. The `XIncludeResolver` expands these references to produce a merged document.
16
+
17
+ === Class Methods
18
+
19
+ The resolver provides two class-level entry points:
20
+
21
+ ==== `resolve(doc)`
22
+
23
+ Resolves XIncludes in a Nokogiri document object. Processes `xi:include` elements iteratively to handle nested includes.
24
+
25
+ [source,ruby]
26
+ ----
27
+ require "nokogiri"
28
+ require "docbook"
29
+
30
+ doc = Nokogiri::XML(File.read("book.xml"))
31
+ Docbook::XIncludeResolver.resolve(doc)
32
+ # => returns the same Nokogiri document with xi:include elements replaced
33
+ ----
34
+
35
+ ==== `resolve_string(xml_string, base_path:)`
36
+
37
+ Resolves XIncludes in a raw XML string. This is the most convenient method for most use cases. The `base_path` parameter tells the resolver where to look for included files.
38
+
39
+ [source,ruby]
40
+ ----
41
+ require "docbook"
42
+
43
+ xml_string = File.read("book.xml")
44
+ resolved = Docbook::XIncludeResolver.resolve_string(xml_string, base_path: "book.xml")
45
+ resolved.to_xml # => merged XML string with all xi:include expanded
46
+ ----
47
+
48
+ [IMPORTANT]
49
+ ====
50
+ The `base_path` parameter should be the path to the XML file itself (not just the directory). The resolver extracts the directory from this path to resolve relative `href` attributes in `xi:include` elements.
51
+ ====
52
+
53
+ === Resolution Process
54
+
55
+ The resolver processes XIncludes iteratively:
56
+
57
+ 1. Find all `xi:include` elements in the document
58
+ 2. For each include, resolve the `href` to a file path relative to the base directory
59
+ 3. Replace the `xi:include` element with the included content
60
+ 4. Re-scan for any new `xi:include` elements (handles nested includes)
61
+ 5. Repeat until no more `xi:include` elements remain
62
+
63
+ This iterative approach ensures that deeply nested includes (file A includes file B, which includes file C) are fully resolved.
64
+
65
+ === Include Types
66
+
67
+ The resolver handles two types of XIncludes:
68
+
69
+ ==== XML Includes (default)
70
+
71
+ Standard XML includes where the `parse` attribute is omitted or set to `"xml"`. The root element of the included file replaces the `xi:include` element:
72
+
73
+ [source,ruby]
74
+ ----
75
+ # book.xml contains:
76
+ # <xi:include href="chapters/chapter1.xml"/>
77
+ #
78
+ # After resolution, the <chapter> element from chapter1.xml
79
+ # replaces the xi:include element
80
+ ----
81
+
82
+ ==== Text Includes
83
+
84
+ Text includes where `parse="text"`. The file contents are included as plain text:
85
+
86
+ [source,ruby]
87
+ ----
88
+ # Includes a code snippet as literal text
89
+ # <xi:include href="code/example.rb" parse="text"/>
90
+ ----
91
+
92
+ ==== Text Includes with Fragid
93
+
94
+ Text includes also support the `fragid` attribute for selecting a subset of the file content. This is an extension based on the DocBook xslTNG specification:
95
+
96
+ * `fragid="line=START,END"` -- Select lines from START to END (RFC 5147 line scheme)
97
+ * `fragid="char=START,END"` -- Select characters from START to END (RFC 5147 char scheme)
98
+ * `fragid="search=#PATTERN#[;after|;before],#STOP#"` -- Select lines matching a literal pattern
99
+ * `fragid="search=/REGEX/,/STOP/"` -- Select lines matching a regex pattern
100
+
101
+ === Complete Example
102
+
103
+ A full workflow for resolving XIncludes before parsing:
104
+
105
+ [source,ruby]
106
+ ----
107
+ require "docbook"
108
+
109
+ # Read the main document
110
+ xml_string = File.read("book.xml")
111
+
112
+ # Resolve all XIncludes
113
+ resolved = Docbook::XIncludeResolver.resolve_string(
114
+ xml_string,
115
+ base_path: "book.xml"
116
+ )
117
+
118
+ # Now parse the merged document
119
+ document = Docbook::Document.from_xml(resolved.to_xml)
120
+
121
+ puts "Chapters: #{document.chapter&.size || 0}"
122
+ document.chapter&.each do |chapter|
123
+ puts " - #{chapter.title&.content}"
124
+ end
125
+ ----
126
+
127
+ === Handling Nested Includes
128
+
129
+ The resolver handles nested includes automatically. Consider this scenario:
130
+
131
+ * `book.xml` includes `part1.xml`
132
+ * `part1.xml` includes `chapter1.xml` and `chapter2.xml`
133
+
134
+ After resolution, all content is merged into a single document:
135
+
136
+ [source,ruby]
137
+ ----
138
+ resolved = Docbook::XIncludeResolver.resolve_string(
139
+ File.read("book.xml"),
140
+ base_path: "book.xml"
141
+ )
142
+ # The resolved document contains content from book.xml,
143
+ # part1.xml, chapter1.xml, and chapter2.xml
144
+ ----
145
+
146
+ === Error Handling
147
+
148
+ The resolver is designed to be tolerant of errors:
149
+
150
+ * If a referenced file does not exist, the `xi:include` element is left in place (no error raised)
151
+ * If the resolver encounters an unexpected error during processing, it returns the original document unchanged
152
+ * The resolver uses Nokogiri's `url` attribute to determine the base directory for relative path resolution
153
+
154
+ === Namespace Handling
155
+
156
+ The resolver ensures that namespace declarations are properly preserved when merging documents. If an included file declares namespaces that are not present in the main document, the resolver adds the namespace declarations to the root element.
157
+
158
+ === See Also
159
+
160
+ * *<<parsing,Parsing Documents>>* -- Parse resolved XML into element models
161
+ * *<<xref-resolution,XRef Resolution>>* -- Resolve cross-references after parsing
162
+ * *<<html-output,HTML Output>>* -- Generate HTML from parsed documents