lcg-framework 0.7.0__py2.py3-none-any.whl

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 (416) hide show
  1. lcg/__init__.py +115 -0
  2. lcg/content.py +2172 -0
  3. lcg/exercises.py +991 -0
  4. lcg/export/__init__.py +18 -0
  5. lcg/export/braille-tables/lcg.utb +3 -0
  6. lcg/export/braille.py +1901 -0
  7. lcg/export/epub.py +420 -0
  8. lcg/export/exercises_html.py +664 -0
  9. lcg/export/export.py +1476 -0
  10. lcg/export/hhp.py +132 -0
  11. lcg/export/html.py +1717 -0
  12. lcg/export/ims.py +124 -0
  13. lcg/export/mathml.py +2351 -0
  14. lcg/export/nemeth.py +1216 -0
  15. lcg/export/pdf.py +3447 -0
  16. lcg/export/styles/presentation-braille-test.py +43 -0
  17. lcg/export/styles/presentation-braille.py +687 -0
  18. lcg/i18n.py +1227 -0
  19. lcg/locales.py +103 -0
  20. lcg/make.py +370 -0
  21. lcg/nodes.py +576 -0
  22. lcg/parse.py +1651 -0
  23. lcg/plot.py +358 -0
  24. lcg/presentation.py +674 -0
  25. lcg/read.py +405 -0
  26. lcg/resources/css/default.css +180 -0
  27. lcg/resources/css/epub.css +39 -0
  28. lcg/resources/css/lcg-exercises.css +157 -0
  29. lcg/resources/css/lcg-widgets.css +484 -0
  30. lcg/resources/css/photoswipe.css +441 -0
  31. lcg/resources/css/pygments.css +62 -0
  32. lcg/resources/flash/jplayer.swf +0 -0
  33. lcg/resources/images/jplayer-seeking.gif +0 -0
  34. lcg/resources/images/lcg-icons.png +0 -0
  35. lcg/resources/media/exercise-responses/cs/correct-01.mp3 +0 -0
  36. lcg/resources/media/exercise-responses/cs/correct-02.mp3 +0 -0
  37. lcg/resources/media/exercise-responses/cs/correct-03.mp3 +0 -0
  38. lcg/resources/media/exercise-responses/cs/correct-04.mp3 +0 -0
  39. lcg/resources/media/exercise-responses/cs/correct-05.mp3 +0 -0
  40. lcg/resources/media/exercise-responses/cs/correct-06.mp3 +0 -0
  41. lcg/resources/media/exercise-responses/cs/correct-07.mp3 +0 -0
  42. lcg/resources/media/exercise-responses/cs/correct-08.mp3 +0 -0
  43. lcg/resources/media/exercise-responses/cs/correct-09.mp3 +0 -0
  44. lcg/resources/media/exercise-responses/cs/correct-10.mp3 +0 -0
  45. lcg/resources/media/exercise-responses/cs/correct-11.mp3 +0 -0
  46. lcg/resources/media/exercise-responses/cs/correct-12.mp3 +0 -0
  47. lcg/resources/media/exercise-responses/cs/correct-13.mp3 +0 -0
  48. lcg/resources/media/exercise-responses/cs/correct-14.mp3 +0 -0
  49. lcg/resources/media/exercise-responses/cs/correct-15.mp3 +0 -0
  50. lcg/resources/media/exercise-responses/cs/correct-16.mp3 +0 -0
  51. lcg/resources/media/exercise-responses/cs/correct-17.mp3 +0 -0
  52. lcg/resources/media/exercise-responses/cs/correct-18.mp3 +0 -0
  53. lcg/resources/media/exercise-responses/cs/correct-19.mp3 +0 -0
  54. lcg/resources/media/exercise-responses/cs/correct-20.mp3 +0 -0
  55. lcg/resources/media/exercise-responses/cs/correct-21.mp3 +0 -0
  56. lcg/resources/media/exercise-responses/cs/correct-23.mp3 +0 -0
  57. lcg/resources/media/exercise-responses/cs/correct-24.mp3 +0 -0
  58. lcg/resources/media/exercise-responses/cs/correct-26.mp3 +0 -0
  59. lcg/resources/media/exercise-responses/cs/correct-27.mp3 +0 -0
  60. lcg/resources/media/exercise-responses/cs/correct-28.mp3 +0 -0
  61. lcg/resources/media/exercise-responses/cs/correct-29.mp3 +0 -0
  62. lcg/resources/media/exercise-responses/cs/correct-31.mp3 +0 -0
  63. lcg/resources/media/exercise-responses/cs/correct-32.mp3 +0 -0
  64. lcg/resources/media/exercise-responses/cs/correct-33.mp3 +0 -0
  65. lcg/resources/media/exercise-responses/cs/correct-34.mp3 +0 -0
  66. lcg/resources/media/exercise-responses/cs/correct-35.mp3 +0 -0
  67. lcg/resources/media/exercise-responses/cs/correct-36.mp3 +0 -0
  68. lcg/resources/media/exercise-responses/cs/correct-37.mp3 +0 -0
  69. lcg/resources/media/exercise-responses/cs/correct-38.mp3 +0 -0
  70. lcg/resources/media/exercise-responses/cs/correct-39.mp3 +0 -0
  71. lcg/resources/media/exercise-responses/cs/correct-40.mp3 +0 -0
  72. lcg/resources/media/exercise-responses/cs/correct-41.mp3 +0 -0
  73. lcg/resources/media/exercise-responses/cs/correct-42.mp3 +0 -0
  74. lcg/resources/media/exercise-responses/cs/correct-43.mp3 +0 -0
  75. lcg/resources/media/exercise-responses/cs/correct-44.mp3 +0 -0
  76. lcg/resources/media/exercise-responses/cs/correct-45.mp3 +0 -0
  77. lcg/resources/media/exercise-responses/cs/correct-46.mp3 +0 -0
  78. lcg/resources/media/exercise-responses/cs/correct-47.mp3 +0 -0
  79. lcg/resources/media/exercise-responses/cs/correct-48.mp3 +0 -0
  80. lcg/resources/media/exercise-responses/cs/correct-49.mp3 +0 -0
  81. lcg/resources/media/exercise-responses/cs/correct-50.mp3 +0 -0
  82. lcg/resources/media/exercise-responses/cs/correct-51.mp3 +0 -0
  83. lcg/resources/media/exercise-responses/cs/correct-52.mp3 +0 -0
  84. lcg/resources/media/exercise-responses/cs/correct-53.mp3 +0 -0
  85. lcg/resources/media/exercise-responses/cs/correct-54.mp3 +0 -0
  86. lcg/resources/media/exercise-responses/cs/correct-55.mp3 +0 -0
  87. lcg/resources/media/exercise-responses/cs/correct-56.mp3 +0 -0
  88. lcg/resources/media/exercise-responses/cs/correct-57.mp3 +0 -0
  89. lcg/resources/media/exercise-responses/cs/correct-58.mp3 +0 -0
  90. lcg/resources/media/exercise-responses/cs/correct-59.mp3 +0 -0
  91. lcg/resources/media/exercise-responses/cs/correct-60.mp3 +0 -0
  92. lcg/resources/media/exercise-responses/cs/correct-61.mp3 +0 -0
  93. lcg/resources/media/exercise-responses/cs/correct-62.mp3 +0 -0
  94. lcg/resources/media/exercise-responses/cs/correct-63.mp3 +0 -0
  95. lcg/resources/media/exercise-responses/cs/correct-64.mp3 +0 -0
  96. lcg/resources/media/exercise-responses/cs/correct-65.mp3 +0 -0
  97. lcg/resources/media/exercise-responses/cs/correct-66.mp3 +0 -0
  98. lcg/resources/media/exercise-responses/cs/correct-67.mp3 +0 -0
  99. lcg/resources/media/exercise-responses/cs/correct-68.mp3 +0 -0
  100. lcg/resources/media/exercise-responses/cs/correct-69.mp3 +0 -0
  101. lcg/resources/media/exercise-responses/cs/excellent-01.mp3 +0 -0
  102. lcg/resources/media/exercise-responses/cs/excellent-02.mp3 +0 -0
  103. lcg/resources/media/exercise-responses/cs/excellent-03.mp3 +0 -0
  104. lcg/resources/media/exercise-responses/cs/excellent-04.mp3 +0 -0
  105. lcg/resources/media/exercise-responses/cs/excellent-05.mp3 +0 -0
  106. lcg/resources/media/exercise-responses/cs/excellent-06.mp3 +0 -0
  107. lcg/resources/media/exercise-responses/cs/excellent-07.mp3 +0 -0
  108. lcg/resources/media/exercise-responses/cs/excellent-08.mp3 +0 -0
  109. lcg/resources/media/exercise-responses/cs/excellent-09.mp3 +0 -0
  110. lcg/resources/media/exercise-responses/cs/excellent-10.mp3 +0 -0
  111. lcg/resources/media/exercise-responses/cs/excellent-11.mp3 +0 -0
  112. lcg/resources/media/exercise-responses/cs/excellent-12.mp3 +0 -0
  113. lcg/resources/media/exercise-responses/cs/excellent-13.mp3 +0 -0
  114. lcg/resources/media/exercise-responses/cs/excellent-14.mp3 +0 -0
  115. lcg/resources/media/exercise-responses/cs/excellent-15.mp3 +0 -0
  116. lcg/resources/media/exercise-responses/cs/excellent-16.mp3 +0 -0
  117. lcg/resources/media/exercise-responses/cs/excellent-17.mp3 +0 -0
  118. lcg/resources/media/exercise-responses/cs/excellent-18.mp3 +0 -0
  119. lcg/resources/media/exercise-responses/cs/excellent-19.mp3 +0 -0
  120. lcg/resources/media/exercise-responses/cs/excellent-20.mp3 +0 -0
  121. lcg/resources/media/exercise-responses/cs/excellent-21.mp3 +0 -0
  122. lcg/resources/media/exercise-responses/cs/excellent-22.mp3 +0 -0
  123. lcg/resources/media/exercise-responses/cs/excellent-23.mp3 +0 -0
  124. lcg/resources/media/exercise-responses/cs/excellent-24.mp3 +0 -0
  125. lcg/resources/media/exercise-responses/cs/excellent-25.mp3 +0 -0
  126. lcg/resources/media/exercise-responses/cs/excellent-26.mp3 +0 -0
  127. lcg/resources/media/exercise-responses/cs/excellent-27.mp3 +0 -0
  128. lcg/resources/media/exercise-responses/cs/excellent-28.mp3 +0 -0
  129. lcg/resources/media/exercise-responses/cs/excellent-29.mp3 +0 -0
  130. lcg/resources/media/exercise-responses/cs/excellent-30.mp3 +0 -0
  131. lcg/resources/media/exercise-responses/cs/excellent-31.mp3 +0 -0
  132. lcg/resources/media/exercise-responses/cs/excellent-32.mp3 +0 -0
  133. lcg/resources/media/exercise-responses/cs/excellent-33.mp3 +0 -0
  134. lcg/resources/media/exercise-responses/cs/excellent-34.mp3 +0 -0
  135. lcg/resources/media/exercise-responses/cs/excellent-35.mp3 +0 -0
  136. lcg/resources/media/exercise-responses/cs/excellent-36.mp3 +0 -0
  137. lcg/resources/media/exercise-responses/cs/excellent-37.mp3 +0 -0
  138. lcg/resources/media/exercise-responses/cs/excellent-38.mp3 +0 -0
  139. lcg/resources/media/exercise-responses/cs/excellent-39.mp3 +0 -0
  140. lcg/resources/media/exercise-responses/cs/excellent-40.mp3 +0 -0
  141. lcg/resources/media/exercise-responses/cs/excellent-41.mp3 +0 -0
  142. lcg/resources/media/exercise-responses/cs/excellent-42.mp3 +0 -0
  143. lcg/resources/media/exercise-responses/cs/excellent-43.mp3 +0 -0
  144. lcg/resources/media/exercise-responses/cs/good-01.mp3 +0 -0
  145. lcg/resources/media/exercise-responses/cs/good-02.mp3 +0 -0
  146. lcg/resources/media/exercise-responses/cs/good-07.mp3 +0 -0
  147. lcg/resources/media/exercise-responses/cs/good-09.mp3 +0 -0
  148. lcg/resources/media/exercise-responses/cs/good-10.mp3 +0 -0
  149. lcg/resources/media/exercise-responses/cs/good-12.mp3 +0 -0
  150. lcg/resources/media/exercise-responses/cs/good-14.mp3 +0 -0
  151. lcg/resources/media/exercise-responses/cs/good-16.mp3 +0 -0
  152. lcg/resources/media/exercise-responses/cs/good-17.mp3 +0 -0
  153. lcg/resources/media/exercise-responses/cs/good-19.mp3 +0 -0
  154. lcg/resources/media/exercise-responses/cs/good-21.mp3 +0 -0
  155. lcg/resources/media/exercise-responses/cs/good-22.mp3 +0 -0
  156. lcg/resources/media/exercise-responses/cs/good-23.mp3 +0 -0
  157. lcg/resources/media/exercise-responses/cs/good-24.mp3 +0 -0
  158. lcg/resources/media/exercise-responses/cs/good-25.mp3 +0 -0
  159. lcg/resources/media/exercise-responses/cs/good-28.mp3 +0 -0
  160. lcg/resources/media/exercise-responses/cs/good-29.mp3 +0 -0
  161. lcg/resources/media/exercise-responses/cs/good-30.mp3 +0 -0
  162. lcg/resources/media/exercise-responses/cs/good-33.mp3 +0 -0
  163. lcg/resources/media/exercise-responses/cs/good-34.mp3 +0 -0
  164. lcg/resources/media/exercise-responses/cs/good-35.mp3 +0 -0
  165. lcg/resources/media/exercise-responses/cs/good-36.mp3 +0 -0
  166. lcg/resources/media/exercise-responses/cs/good-38.mp3 +0 -0
  167. lcg/resources/media/exercise-responses/cs/good-41.mp3 +0 -0
  168. lcg/resources/media/exercise-responses/cs/good-43.mp3 +0 -0
  169. lcg/resources/media/exercise-responses/cs/good-44.mp3 +0 -0
  170. lcg/resources/media/exercise-responses/cs/good-45.mp3 +0 -0
  171. lcg/resources/media/exercise-responses/cs/good-46.mp3 +0 -0
  172. lcg/resources/media/exercise-responses/cs/good-47.mp3 +0 -0
  173. lcg/resources/media/exercise-responses/cs/good-48.mp3 +0 -0
  174. lcg/resources/media/exercise-responses/cs/good-51.mp3 +0 -0
  175. lcg/resources/media/exercise-responses/cs/incorrect-01.mp3 +0 -0
  176. lcg/resources/media/exercise-responses/cs/incorrect-02.mp3 +0 -0
  177. lcg/resources/media/exercise-responses/cs/incorrect-03.mp3 +0 -0
  178. lcg/resources/media/exercise-responses/cs/incorrect-04.mp3 +0 -0
  179. lcg/resources/media/exercise-responses/cs/incorrect-05.mp3 +0 -0
  180. lcg/resources/media/exercise-responses/cs/incorrect-06.mp3 +0 -0
  181. lcg/resources/media/exercise-responses/cs/incorrect-07.mp3 +0 -0
  182. lcg/resources/media/exercise-responses/cs/incorrect-08.mp3 +0 -0
  183. lcg/resources/media/exercise-responses/cs/incorrect-09.mp3 +0 -0
  184. lcg/resources/media/exercise-responses/cs/incorrect-10.mp3 +0 -0
  185. lcg/resources/media/exercise-responses/cs/incorrect-11.mp3 +0 -0
  186. lcg/resources/media/exercise-responses/cs/incorrect-12.mp3 +0 -0
  187. lcg/resources/media/exercise-responses/cs/incorrect-13.mp3 +0 -0
  188. lcg/resources/media/exercise-responses/cs/incorrect-14.mp3 +0 -0
  189. lcg/resources/media/exercise-responses/cs/incorrect-15.mp3 +0 -0
  190. lcg/resources/media/exercise-responses/cs/incorrect-16.mp3 +0 -0
  191. lcg/resources/media/exercise-responses/cs/incorrect-17.mp3 +0 -0
  192. lcg/resources/media/exercise-responses/cs/incorrect-18.mp3 +0 -0
  193. lcg/resources/media/exercise-responses/cs/incorrect-19.mp3 +0 -0
  194. lcg/resources/media/exercise-responses/cs/incorrect-20.mp3 +0 -0
  195. lcg/resources/media/exercise-responses/cs/incorrect-21.mp3 +0 -0
  196. lcg/resources/media/exercise-responses/cs/incorrect-22.mp3 +0 -0
  197. lcg/resources/media/exercise-responses/cs/incorrect-23.mp3 +0 -0
  198. lcg/resources/media/exercise-responses/cs/incorrect-24.mp3 +0 -0
  199. lcg/resources/media/exercise-responses/cs/incorrect-25.mp3 +0 -0
  200. lcg/resources/media/exercise-responses/cs/incorrect-26.mp3 +0 -0
  201. lcg/resources/media/exercise-responses/cs/incorrect-27.mp3 +0 -0
  202. lcg/resources/media/exercise-responses/cs/incorrect-28.mp3 +0 -0
  203. lcg/resources/media/exercise-responses/cs/incorrect-29.mp3 +0 -0
  204. lcg/resources/media/exercise-responses/cs/incorrect-30.mp3 +0 -0
  205. lcg/resources/media/exercise-responses/cs/incorrect-31.mp3 +0 -0
  206. lcg/resources/media/exercise-responses/cs/incorrect-32.mp3 +0 -0
  207. lcg/resources/media/exercise-responses/cs/incorrect-33.mp3 +0 -0
  208. lcg/resources/media/exercise-responses/cs/incorrect-34.mp3 +0 -0
  209. lcg/resources/media/exercise-responses/cs/incorrect-35.mp3 +0 -0
  210. lcg/resources/media/exercise-responses/cs/incorrect-36.mp3 +0 -0
  211. lcg/resources/media/exercise-responses/cs/incorrect-37.mp3 +0 -0
  212. lcg/resources/media/exercise-responses/cs/incorrect-38.mp3 +0 -0
  213. lcg/resources/media/exercise-responses/cs/incorrect-39.mp3 +0 -0
  214. lcg/resources/media/exercise-responses/cs/incorrect-40.mp3 +0 -0
  215. lcg/resources/media/exercise-responses/cs/incorrect-41.mp3 +0 -0
  216. lcg/resources/media/exercise-responses/cs/incorrect-42.mp3 +0 -0
  217. lcg/resources/media/exercise-responses/cs/incorrect-43.mp3 +0 -0
  218. lcg/resources/media/exercise-responses/cs/incorrect-44.mp3 +0 -0
  219. lcg/resources/media/exercise-responses/cs/incorrect-45.mp3 +0 -0
  220. lcg/resources/media/exercise-responses/cs/incorrect-46.mp3 +0 -0
  221. lcg/resources/media/exercise-responses/cs/incorrect-47.mp3 +0 -0
  222. lcg/resources/media/exercise-responses/cs/incorrect-48.mp3 +0 -0
  223. lcg/resources/media/exercise-responses/cs/incorrect-49.mp3 +0 -0
  224. lcg/resources/media/exercise-responses/cs/incorrect-50.mp3 +0 -0
  225. lcg/resources/media/exercise-responses/cs/incorrect-51.mp3 +0 -0
  226. lcg/resources/media/exercise-responses/cs/incorrect-52.mp3 +0 -0
  227. lcg/resources/media/exercise-responses/cs/incorrect-53.mp3 +0 -0
  228. lcg/resources/media/exercise-responses/cs/incorrect-54.mp3 +0 -0
  229. lcg/resources/media/exercise-responses/cs/incorrect-55.mp3 +0 -0
  230. lcg/resources/media/exercise-responses/cs/incorrect-56.mp3 +0 -0
  231. lcg/resources/media/exercise-responses/cs/incorrect-57.mp3 +0 -0
  232. lcg/resources/media/exercise-responses/cs/incorrect-58.mp3 +0 -0
  233. lcg/resources/media/exercise-responses/cs/incorrect-59.mp3 +0 -0
  234. lcg/resources/media/exercise-responses/cs/incorrect-60.mp3 +0 -0
  235. lcg/resources/media/exercise-responses/cs/perfect-01.mp3 +0 -0
  236. lcg/resources/media/exercise-responses/cs/perfect-02.mp3 +0 -0
  237. lcg/resources/media/exercise-responses/cs/perfect-03.mp3 +0 -0
  238. lcg/resources/media/exercise-responses/cs/perfect-04.mp3 +0 -0
  239. lcg/resources/media/exercise-responses/cs/perfect-05.mp3 +0 -0
  240. lcg/resources/media/exercise-responses/cs/perfect-07.mp3 +0 -0
  241. lcg/resources/media/exercise-responses/cs/perfect-08.mp3 +0 -0
  242. lcg/resources/media/exercise-responses/cs/perfect-09.mp3 +0 -0
  243. lcg/resources/media/exercise-responses/cs/perfect-11.mp3 +0 -0
  244. lcg/resources/media/exercise-responses/cs/perfect-13.mp3 +0 -0
  245. lcg/resources/media/exercise-responses/cs/perfect-14.mp3 +0 -0
  246. lcg/resources/media/exercise-responses/cs/perfect-15.mp3 +0 -0
  247. lcg/resources/media/exercise-responses/cs/perfect-16.mp3 +0 -0
  248. lcg/resources/media/exercise-responses/cs/perfect-17.mp3 +0 -0
  249. lcg/resources/media/exercise-responses/cs/perfect-18.mp3 +0 -0
  250. lcg/resources/media/exercise-responses/cs/perfect-19.mp3 +0 -0
  251. lcg/resources/media/exercise-responses/cs/perfect-21.mp3 +0 -0
  252. lcg/resources/media/exercise-responses/cs/perfect-22.mp3 +0 -0
  253. lcg/resources/media/exercise-responses/cs/perfect-23.mp3 +0 -0
  254. lcg/resources/media/exercise-responses/cs/perfect-24.mp3 +0 -0
  255. lcg/resources/media/exercise-responses/cs/perfect-25.mp3 +0 -0
  256. lcg/resources/media/exercise-responses/cs/perfect-26.mp3 +0 -0
  257. lcg/resources/media/exercise-responses/cs/perfect-27.mp3 +0 -0
  258. lcg/resources/media/exercise-responses/cs/perfect-28.mp3 +0 -0
  259. lcg/resources/media/exercise-responses/cs/perfect-29.mp3 +0 -0
  260. lcg/resources/media/exercise-responses/cs/perfect-30.mp3 +0 -0
  261. lcg/resources/media/exercise-responses/cs/perfect-31.mp3 +0 -0
  262. lcg/resources/media/exercise-responses/cs/perfect-32.mp3 +0 -0
  263. lcg/resources/media/exercise-responses/cs/perfect-33.mp3 +0 -0
  264. lcg/resources/media/exercise-responses/cs/perfect-34.mp3 +0 -0
  265. lcg/resources/media/exercise-responses/cs/perfect-35.mp3 +0 -0
  266. lcg/resources/media/exercise-responses/cs/perfect-36.mp3 +0 -0
  267. lcg/resources/media/exercise-responses/cs/perfect-37.mp3 +0 -0
  268. lcg/resources/media/exercise-responses/cs/perfect-38.mp3 +0 -0
  269. lcg/resources/media/exercise-responses/cs/perfect-40.mp3 +0 -0
  270. lcg/resources/media/exercise-responses/cs/poor-01.mp3 +0 -0
  271. lcg/resources/media/exercise-responses/cs/poor-02.mp3 +0 -0
  272. lcg/resources/media/exercise-responses/cs/poor-03.mp3 +0 -0
  273. lcg/resources/media/exercise-responses/cs/poor-04.mp3 +0 -0
  274. lcg/resources/media/exercise-responses/cs/poor-05.mp3 +0 -0
  275. lcg/resources/media/exercise-responses/cs/poor-06.mp3 +0 -0
  276. lcg/resources/media/exercise-responses/cs/poor-08.mp3 +0 -0
  277. lcg/resources/media/exercise-responses/cs/poor-09.mp3 +0 -0
  278. lcg/resources/media/exercise-responses/cs/poor-10.mp3 +0 -0
  279. lcg/resources/media/exercise-responses/cs/poor-13.mp3 +0 -0
  280. lcg/resources/media/exercise-responses/cs/poor-14.mp3 +0 -0
  281. lcg/resources/media/exercise-responses/cs/poor-15.mp3 +0 -0
  282. lcg/resources/media/exercise-responses/cs/poor-16.mp3 +0 -0
  283. lcg/resources/media/exercise-responses/cs/poor-17.mp3 +0 -0
  284. lcg/resources/media/exercise-responses/cs/poor-18.mp3 +0 -0
  285. lcg/resources/media/exercise-responses/cs/poor-19.mp3 +0 -0
  286. lcg/resources/media/exercise-responses/cs/poor-21.mp3 +0 -0
  287. lcg/resources/media/exercise-responses/cs/poor-22.mp3 +0 -0
  288. lcg/resources/media/exercise-responses/cs/poor-24.mp3 +0 -0
  289. lcg/resources/media/exercise-responses/cs/poor-25.mp3 +0 -0
  290. lcg/resources/media/exercise-responses/cs/poor-26.mp3 +0 -0
  291. lcg/resources/media/exercise-responses/cs/poor-27.mp3 +0 -0
  292. lcg/resources/media/exercise-responses/cs/poor-28.mp3 +0 -0
  293. lcg/resources/media/exercise-responses/cs/poor-29.mp3 +0 -0
  294. lcg/resources/media/exercise-responses/cs/poor-30.mp3 +0 -0
  295. lcg/resources/media/exercise-responses/cs/poor-32.mp3 +0 -0
  296. lcg/resources/media/exercise-responses/cs/poor-33.mp3 +0 -0
  297. lcg/resources/media/exercise-responses/cs/poor-34.mp3 +0 -0
  298. lcg/resources/media/exercise-responses/cs/poor-35.mp3 +0 -0
  299. lcg/resources/media/exercise-responses/cs/poor-36.mp3 +0 -0
  300. lcg/resources/media/exercise-responses/cs/poor-37.mp3 +0 -0
  301. lcg/resources/media/exercise-responses/cs/poor-38.mp3 +0 -0
  302. lcg/resources/media/exercise-responses/cs/poor-39.mp3 +0 -0
  303. lcg/resources/media/exercise-responses/cs/poor-40.mp3 +0 -0
  304. lcg/resources/media/exercise-responses/cs/sufficient-01.mp3 +0 -0
  305. lcg/resources/media/exercise-responses/cs/sufficient-02.mp3 +0 -0
  306. lcg/resources/media/exercise-responses/cs/sufficient-03.mp3 +0 -0
  307. lcg/resources/media/exercise-responses/cs/sufficient-04.mp3 +0 -0
  308. lcg/resources/media/exercise-responses/cs/sufficient-05.mp3 +0 -0
  309. lcg/resources/media/exercise-responses/cs/sufficient-06.mp3 +0 -0
  310. lcg/resources/media/exercise-responses/cs/sufficient-07.mp3 +0 -0
  311. lcg/resources/media/exercise-responses/cs/sufficient-08.mp3 +0 -0
  312. lcg/resources/media/exercise-responses/cs/sufficient-09.mp3 +0 -0
  313. lcg/resources/media/exercise-responses/cs/sufficient-10.mp3 +0 -0
  314. lcg/resources/media/exercise-responses/cs/sufficient-11.mp3 +0 -0
  315. lcg/resources/media/exercise-responses/cs/sufficient-13.mp3 +0 -0
  316. lcg/resources/media/exercise-responses/cs/sufficient-14.mp3 +0 -0
  317. lcg/resources/media/exercise-responses/cs/sufficient-15.mp3 +0 -0
  318. lcg/resources/media/exercise-responses/cs/sufficient-16.mp3 +0 -0
  319. lcg/resources/media/exercise-responses/cs/sufficient-17.mp3 +0 -0
  320. lcg/resources/media/exercise-responses/cs/sufficient-18.mp3 +0 -0
  321. lcg/resources/media/exercise-responses/cs/sufficient-19.mp3 +0 -0
  322. lcg/resources/media/exercise-responses/cs/sufficient-21.mp3 +0 -0
  323. lcg/resources/media/exercise-responses/cs/sufficient-22.mp3 +0 -0
  324. lcg/resources/media/exercise-responses/cs/sufficient-23.mp3 +0 -0
  325. lcg/resources/media/exercise-responses/cs/sufficient-25.mp3 +0 -0
  326. lcg/resources/media/exercise-responses/cs/sufficient-26.mp3 +0 -0
  327. lcg/resources/media/exercise-responses/cs/sufficient-27.mp3 +0 -0
  328. lcg/resources/media/exercise-responses/cs/sufficient-28.mp3 +0 -0
  329. lcg/resources/media/exercise-responses/cs/sufficient-29.mp3 +0 -0
  330. lcg/resources/media/exercise-responses/cs/sufficient-32.mp3 +0 -0
  331. lcg/resources/media/exercise-responses/cs/sufficient-33.mp3 +0 -0
  332. lcg/resources/media/exercise-responses/cs/sufficient-34.mp3 +0 -0
  333. lcg/resources/media/exercise-responses/cs/sufficient-35.mp3 +0 -0
  334. lcg/resources/media/exercise-responses/cs/sufficient-36.mp3 +0 -0
  335. lcg/resources/media/exercise-responses/en/correct-01.mp3 +0 -0
  336. lcg/resources/media/exercise-responses/en/correct-02.mp3 +0 -0
  337. lcg/resources/media/exercise-responses/en/correct-03.mp3 +0 -0
  338. lcg/resources/media/exercise-responses/en/correct-04.mp3 +0 -0
  339. lcg/resources/media/exercise-responses/en/correct-05.mp3 +0 -0
  340. lcg/resources/media/exercise-responses/en/correct-06.mp3 +0 -0
  341. lcg/resources/media/exercise-responses/en/correct-07.mp3 +0 -0
  342. lcg/resources/media/exercise-responses/en/correct-08.mp3 +0 -0
  343. lcg/resources/media/exercise-responses/en/correct-09.mp3 +0 -0
  344. lcg/resources/media/exercise-responses/en/excellent-01.mp3 +0 -0
  345. lcg/resources/media/exercise-responses/en/excellent-02.mp3 +0 -0
  346. lcg/resources/media/exercise-responses/en/excellent-03.mp3 +0 -0
  347. lcg/resources/media/exercise-responses/en/good-01.mp3 +0 -0
  348. lcg/resources/media/exercise-responses/en/good-02.mp3 +0 -0
  349. lcg/resources/media/exercise-responses/en/good-03.mp3 +0 -0
  350. lcg/resources/media/exercise-responses/en/good-04.mp3 +0 -0
  351. lcg/resources/media/exercise-responses/en/incorrect-01.mp3 +0 -0
  352. lcg/resources/media/exercise-responses/en/incorrect-02.mp3 +0 -0
  353. lcg/resources/media/exercise-responses/en/incorrect-03.mp3 +0 -0
  354. lcg/resources/media/exercise-responses/en/incorrect-04.mp3 +0 -0
  355. lcg/resources/media/exercise-responses/en/incorrect-05.mp3 +0 -0
  356. lcg/resources/media/exercise-responses/en/incorrect-06.mp3 +0 -0
  357. lcg/resources/media/exercise-responses/en/incorrect-07.mp3 +0 -0
  358. lcg/resources/media/exercise-responses/en/incorrect-08.mp3 +0 -0
  359. lcg/resources/media/exercise-responses/en/incorrect-09.mp3 +0 -0
  360. lcg/resources/media/exercise-responses/en/incorrect-10.mp3 +0 -0
  361. lcg/resources/media/exercise-responses/en/incorrect-11.mp3 +0 -0
  362. lcg/resources/media/exercise-responses/en/perfect-01.mp3 +0 -0
  363. lcg/resources/media/exercise-responses/en/perfect-02.mp3 +0 -0
  364. lcg/resources/media/exercise-responses/en/perfect-03.mp3 +0 -0
  365. lcg/resources/media/exercise-responses/en/perfect-04.mp3 +0 -0
  366. lcg/resources/media/exercise-responses/en/poor-01.mp3 +0 -0
  367. lcg/resources/media/exercise-responses/en/poor-02.mp3 +0 -0
  368. lcg/resources/media/exercise-responses/en/poor-03.mp3 +0 -0
  369. lcg/resources/media/exercise-responses/en/sufficient-01.mp3 +0 -0
  370. lcg/resources/media/exercise-responses/en/sufficient-02.mp3 +0 -0
  371. lcg/resources/media/exercise-responses/en/sufficient-03.mp3 +0 -0
  372. lcg/resources/scripts/effects.js +1123 -0
  373. lcg/resources/scripts/gettext.js +1265 -0
  374. lcg/resources/scripts/jplayer.min.js +3 -0
  375. lcg/resources/scripts/jquery.min.js +4 -0
  376. lcg/resources/scripts/lcg-exercises.js +533 -0
  377. lcg/resources/scripts/lcg.js +1705 -0
  378. lcg/resources/scripts/photoswipe-lightbox.esm.min.js +5 -0
  379. lcg/resources/scripts/photoswipe.esm.min.js +5 -0
  380. lcg/resources/scripts/prototype.js +6081 -0
  381. lcg/resources/scripts/scriptaculous.js +68 -0
  382. lcg/resources.py +438 -0
  383. lcg/test.py +2615 -0
  384. lcg/transform.py +990 -0
  385. lcg/translations/cs/LC_MESSAGES/lcg-exercises.mo +0 -0
  386. lcg/translations/cs/LC_MESSAGES/lcg.mo +0 -0
  387. lcg/translations/cs/LC_MESSAGES/test.mo +0 -0
  388. lcg/translations/de/LC_MESSAGES/lcg-exercises.mo +0 -0
  389. lcg/translations/de/LC_MESSAGES/lcg.mo +0 -0
  390. lcg/translations/es/LC_MESSAGES/lcg-exercises.mo +0 -0
  391. lcg/translations/es/LC_MESSAGES/lcg.mo +0 -0
  392. lcg/translations/fr/LC_MESSAGES/lcg-exercises.mo +0 -0
  393. lcg/translations/fr/LC_MESSAGES/lcg.mo +0 -0
  394. lcg/translations/hu/LC_MESSAGES/lcg-exercises.mo +0 -0
  395. lcg/translations/hu/LC_MESSAGES/lcg.mo +0 -0
  396. lcg/translations/it/LC_MESSAGES/lcg-exercises.mo +0 -0
  397. lcg/translations/it/LC_MESSAGES/lcg.mo +0 -0
  398. lcg/translations/no/LC_MESSAGES/lcg-exercises.mo +0 -0
  399. lcg/translations/no/LC_MESSAGES/lcg.mo +0 -0
  400. lcg/translations/pl/LC_MESSAGES/lcg-exercises.mo +0 -0
  401. lcg/translations/pl/LC_MESSAGES/lcg.mo +0 -0
  402. lcg/translations/po/LC_MESSAGES/lcg-exercises.mo +0 -0
  403. lcg/translations/po/LC_MESSAGES/lcg.mo +0 -0
  404. lcg/translations/ro/LC_MESSAGES/lcg-exercises.mo +0 -0
  405. lcg/translations/ro/LC_MESSAGES/lcg.mo +0 -0
  406. lcg/translations/sk/LC_MESSAGES/lcg-exercises.mo +0 -0
  407. lcg/translations/sk/LC_MESSAGES/lcg.mo +0 -0
  408. lcg/translations/tr/LC_MESSAGES/lcg-exercises.mo +0 -0
  409. lcg/translations/tr/LC_MESSAGES/lcg.mo +0 -0
  410. lcg/units.py +192 -0
  411. lcg/util.py +671 -0
  412. lcg/widgets.py +518 -0
  413. lcg_framework-0.7.0.dist-info/METADATA +108 -0
  414. lcg_framework-0.7.0.dist-info/RECORD +416 -0
  415. lcg_framework-0.7.0.dist-info/WHEEL +5 -0
  416. lcg_framework-0.7.0.dist-info/licenses/COPYING +340 -0
lcg/content.py ADDED
@@ -0,0 +1,2172 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Copyright (C) 2004-2017 OUI Technology Ltd.
4
+ # Copyright (C) 2019-2025 Tomáš Cerha <t.cerha@gmail.com>
5
+ #
6
+ # This program is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program; if not, write to the Free Software
18
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
+
20
+ """Generic content abstraction.
21
+
22
+ The classes defined in this module were designed to represent general content
23
+ elements of a structured document, such as sections, paragraphs, lists, tables
24
+ etc. Each content element is represented by an instance of one of the classes
25
+ defined below. Some types of elements can contain other elements and thus the
26
+ whole document is represented by a tree of content elements. When the
27
+ construction of the whole tree is finished, each element knows its ancestor
28
+ (the container) and all the successors (sub-content) in the tree.
29
+
30
+ The content hierarchy may be built independently, however you need to assign a
31
+ content to a 'lcg.ContentNode' instance to be able to export it.
32
+
33
+ Please note the difference between the hierarchy of content within one node and
34
+ the hierarchy of the nodes themselves.
35
+
36
+ """
37
+
38
+ from __future__ import unicode_literals
39
+ from future import standard_library
40
+ from builtins import range
41
+
42
+ import sys
43
+ import copy
44
+ import re
45
+ import lcg
46
+
47
+
48
+ _ = lcg.TranslatableTextFactory('lcg')
49
+
50
+ standard_library.install_aliases()
51
+ unistr = type(u'') # Python 2/3 transition hack.
52
+ if sys.version_info[0] > 2:
53
+ basestring = str
54
+
55
+
56
+ class Content(object):
57
+ """Generic base class for all types of content.
58
+
59
+ All (more specific) content element classes are derived from this class.
60
+
61
+ Each content element instance may be contained in another content element
62
+ instance (see the 'Container' class) and thus they make a hierarchical
63
+ structure.
64
+
65
+ """
66
+ _ALLOWED_CONTAINER = None
67
+
68
+ def __init__(self, lang=None, resources=()):
69
+ """Initialize the instance.
70
+
71
+ Arguments:
72
+
73
+ lang -- content language as a lowercase ISO 639-1 Alpha-2 language code.
74
+ resources -- sequence of resource instances associated with the
75
+ content. This allows passing resource instances explicitly with
76
+ content rather than relying on their allocation through resource
77
+ provider.
78
+
79
+ """
80
+ assert resources is None or isinstance(resources, (tuple, list)), resources
81
+ self._parent = None
82
+ self._container = None
83
+ self._lang = lang
84
+ self._page_number = ''
85
+ self._resources = tuple(resources)
86
+ super(Content, self).__init__()
87
+
88
+ def set_container(self, container):
89
+ """Set the parent 'Container' to 'container'.
90
+
91
+ This method is normally called automatically by the container
92
+ constructor to inform the contained 'Content' elements about their
93
+ position in content hierarchy. You only need to care about calling
94
+ this method if you implement your own 'Container' class and don't call
95
+ the default constructor for all the contained elements for some reason.
96
+ Otherwise it is always called automatically by LCG behind the scenes.
97
+
98
+ """
99
+ if __debug__:
100
+ cls = self._ALLOWED_CONTAINER or Container
101
+ assert isinstance(container, cls), \
102
+ "Not a '%s' instance: %s" % (cls.__name__, container,)
103
+ self._container = container
104
+
105
+ def set_parent(self, node):
106
+ """Set the parent 'lcg.ContentNode' to 'node'.
107
+
108
+ This method is deprecated. The parent node (the node to which the
109
+ content belongs) should always be obtained from the export context, not
110
+ from the node itself. This method is now called automatically when an
111
+ element is exported to keep the 'parent()' method working, but both
112
+ methods should be avoided in newly written code.
113
+
114
+ """
115
+ assert isinstance(node, lcg.ContentNode), \
116
+ "Not a 'lcg.ContentNode' instance: %s" % node
117
+ # assert self._parent is None or self._parent is node, \
118
+ # "Reparenting not allowed: %s -> %s" % (self._parent, node)
119
+ self._parent = node
120
+
121
+ def set_page_number(self, number):
122
+ """Set page number of the content element.
123
+
124
+ This is to be used in table of contents.
125
+
126
+ Arguments:
127
+
128
+ number -- the page number; basestring
129
+
130
+ """
131
+ assert isinstance(number, basestring), number
132
+ self._page_number = number
133
+
134
+ def parent(self):
135
+ """Deprecated. Use context.node() instead."""
136
+ parent = self._parent or self._container and self._container.parent()
137
+ return parent
138
+
139
+ def lang(self, inherited=True):
140
+ """Return the content language as lowercase ISO 639-1 Alpha-2 language code.
141
+
142
+ Arguments:
143
+
144
+ inherited -- iff True, the language will be determined from the
145
+ parent element in the hierarchy if this element doesn't define
146
+ 'lang' explicitly. When False, the value of 'lang' passed to the
147
+ constructor is returned.
148
+
149
+ """
150
+ lang = self._lang
151
+ if lang is None and inherited and self._container:
152
+ lang = self._container.lang()
153
+ return lang
154
+
155
+ def resources(self):
156
+ """Return the value of 'resources' passed to the constructor as a tuple."""
157
+ return self._resources
158
+
159
+ def container(self):
160
+ """Return the direct container of this element."""
161
+ return self._container
162
+
163
+ def container_path(self):
164
+ """Return a list of containers of the given element
165
+
166
+ The list is sorted from top to bottom, the last element being
167
+ this element itself.
168
+
169
+ """
170
+ path = [self]
171
+ while path[0]._container is not None:
172
+ path.insert(0, path[0]._container)
173
+ return tuple(path)
174
+
175
+ def _neighbor_element(self, direction, stop_classes):
176
+ element = self
177
+ container = self.container()
178
+ while True:
179
+ if container is None:
180
+ return None
181
+ content = container.content()
182
+ index = content.index(element)
183
+ if direction < 0:
184
+ if index > 0:
185
+ neighbor = content[index - 1]
186
+ break
187
+ else:
188
+ if index < len(content) - 1:
189
+ neighbor = content[index + 1]
190
+ break
191
+ if isinstance(container, stop_classes):
192
+ return None
193
+ element = container
194
+ container = container.container()
195
+ while isinstance(neighbor, Container):
196
+ content = neighbor.content()
197
+ if not content:
198
+ return neighbor
199
+ neighbor = content[-1]
200
+ return neighbor
201
+
202
+ def previous_element(self, stop_classes=()):
203
+ """ """
204
+ return self._neighbor_element(-1, stop_classes)
205
+
206
+ def next_element(self, stop_classes=()):
207
+ """ """
208
+ return self._neighbor_element(1, stop_classes)
209
+
210
+ def sections(self):
211
+ """Return the contained sections as a list of 'Section' instances.
212
+
213
+ This method allows introspection of content hierarchy (for example for
214
+ creation of a table of contents).
215
+
216
+ An empty list is returned in the base class. Derived classes which
217
+ contain sections should override this method to return the contained
218
+ sections.
219
+
220
+ """
221
+ return []
222
+
223
+ def page_number(self):
224
+ """Return page number of the content element.
225
+
226
+ This is to be used in table of contents.
227
+
228
+ Arguments:
229
+
230
+ context -- formatting context as a 'Exporter.Context' instance
231
+ created and returned by 'Exporter.context' method
232
+
233
+ """
234
+ return self._page_number
235
+
236
+ def export(self, context):
237
+ """Return the formatted content in an output specific form.
238
+
239
+ The return value can later be processed by 'Exporter' methods.
240
+
241
+ Arguments:
242
+
243
+ context -- formatting context as a 'Exporter.Context' instance
244
+ created and returned by 'Exporter.context' method
245
+
246
+ """
247
+ return context.exporter().export_element(context, self)
248
+
249
+
250
+ class Container(Content):
251
+ """Container of other content elements.
252
+
253
+ Containers allow to build a hierarchy of 'Content' instances inside the
254
+ scope of one node. This is an addition to the hierarchy of the nodes
255
+ themselves.
256
+
257
+ All the contained (wrapped) content elements will be notified about the
258
+ fact, that they are contained within this container and thus belong to the
259
+ hierarchy.
260
+
261
+ """
262
+ _ALLOWED_CONTENT = Content
263
+ _SUBSEQUENCES = False
264
+ _SUBSEQUENCE_LENGTH = None
265
+
266
+ def __init__(self, content, name=(), id=None, role=None, label=None,
267
+ halign=None, valign=None, orientation=None, width=None, height=None,
268
+ padding=None, presentation=None, **kwargs):
269
+ """Initialize the instance.
270
+
271
+ Arguments:
272
+
273
+ content -- the actual content wrapped into this container as a
274
+ sequence of 'Content' instances in the order in which they should
275
+ appear in the output.
276
+ name -- optional string identifier or a sequence of them. These
277
+ identifiers may be then used as output presentation selectors (for
278
+ example in HTML they will compose the value of the 'class'
279
+ attribute).
280
+ id -- unique identifier as a string. The identifier must be unique
281
+ within the whole content hierarchy of one 'lcg.ContentNode'.
282
+ role -- ARIA role name for HTML output as a string or None.
283
+ label -- container label to be used for aria-label in HTML output.
284
+ halign -- horizontal alignment of the container content; one of the
285
+ 'lcg.HorizontalAlignment' constants or 'None' (default alignment).
286
+ valign -- vertical alignment of the container content; one of the
287
+ 'VerticalAlignment' constants or 'None' (default alignment).
288
+ orientation -- orientation of the container content flow (vertical
289
+ vs.horizontalal); one of the 'Orientation' constants or 'None'
290
+ (default orientation).
291
+ width -- explicit output width as 'lcg.Unit' subclass instance.
292
+ Applies to the outer dimension of the whole box including any
293
+ padding and/or box_margin. Currently only supported in PDF export.
294
+ height -- explicit output width as 'lcg.Unit' subclass instance.
295
+ Applies to the outer dimension of the whole box including any
296
+ padding and/or box_margin. Currently only supported in PDF export.
297
+ padding -- space around the container content. If not None, it can
298
+ be either a 'Unit' instance determining equal padding for all sides
299
+ or a sequence of four 'Unit' instances determining padding for each
300
+ side separately in the order (top, right, bottom, left) or a
301
+ sequence of two 'Unit' instances, where the first value determines
302
+ top and bottom padding and the second value determines left and
303
+ right padding. Note, that 'presentation' may also define
304
+ 'box_margin' for a boxed group, but there are major differences
305
+ between box_margin and padding. Box margin only applies to boxed
306
+ groups and is ignored for non boxed groups. Padding applies to
307
+ both. Also, box_margin is (through presentation inheritance)
308
+ inherited to any inner boxed groups which don't explicitly override
309
+ it. Padding only applies to the container for which it is defined.
310
+ Currently only supported in PDF export.
311
+ presentation -- 'Presentation' instance defining various presentation
312
+ properties; if 'None' then no explicit presentation for this container
313
+ is defined.
314
+
315
+ Note that 'halign', 'valign', 'orientation', 'width', 'height' and 'presentation'
316
+ parameters may be ignored by some exporters.
317
+
318
+ """
319
+ super(Container, self).__init__(**kwargs)
320
+ assert isinstance(name, basestring) or lcg.is_sequence_of(name, basestring), name
321
+ assert halign is None or isinstance(halign, basestring), halign
322
+ assert valign is None or isinstance(valign, basestring), valign
323
+ assert orientation is None or isinstance(orientation, basestring), orientation
324
+ assert id is None or isinstance(id, basestring), id
325
+ assert width is None or isinstance(width, lcg.Unit), width
326
+ assert height is None or isinstance(height, lcg.Unit), height
327
+ assert (padding is None or isinstance(padding, lcg.Unit)
328
+ or lcg.is_sequence_of(padding, lcg.Unit) and len(padding) in (2, 4)), padding
329
+ self._id = id
330
+ if isinstance(name, basestring):
331
+ names = (name,)
332
+ else:
333
+ names = tuple(name)
334
+ self._names = names
335
+ self._role = role
336
+ self._label = label
337
+ from lcg import Presentation
338
+ assert presentation is None or isinstance(presentation, Presentation), presentation
339
+ super(Container, self).__init__(**kwargs)
340
+ self._halign = halign
341
+ self._valign = valign
342
+ self._orientation = orientation
343
+ self._width = width
344
+ self._height = height
345
+ self._padding = padding
346
+ self._presentation = presentation
347
+ self._contained_resources = None
348
+ if self._SUBSEQUENCES:
349
+ assert isinstance(content, (list, tuple)), "Not a sequence: %s" % content
350
+ self._content = tuple([tuple(subseq) for subseq in content])
351
+ for subseq in content:
352
+ assert isinstance(subseq, (list, tuple)), "Not a sequence: %s" % subseq
353
+ if self._SUBSEQUENCE_LENGTH is not None:
354
+ assert len(subseq) == self._SUBSEQUENCE_LENGTH
355
+ for c in subseq:
356
+ assert isinstance(c, self._ALLOWED_CONTENT), \
357
+ "Not a '%s' instance: %s" % (self._ALLOWED_CONTENT, c)
358
+ c.set_container(self)
359
+ elif isinstance(content, (list, tuple)):
360
+ assert lcg.is_sequence_of(content, self._ALLOWED_CONTENT), \
361
+ "Not a '%s' instances sequence: %s" % (self._ALLOWED_CONTENT, content)
362
+ self._content = tuple(content)
363
+ for c in content:
364
+ c.set_container(self)
365
+ else:
366
+ assert isinstance(content, self._ALLOWED_CONTENT), \
367
+ "Not a '%s' instance: %s" % (self._ALLOWED_CONTENT, content)
368
+ self._content = (content,)
369
+ content.set_container(self)
370
+
371
+ def content(self):
372
+ """Return the sequence of contained content elements.
373
+ """
374
+ return self._content
375
+
376
+ def id(self):
377
+ """Return the unique identifier of this container as a string."""
378
+ return self._id
379
+
380
+ def sections(self):
381
+ result = []
382
+ for c in self._content:
383
+ if isinstance(c, Section):
384
+ result.append(c)
385
+ elif isinstance(c, Container):
386
+ result.extend(c.sections())
387
+ return result
388
+
389
+ def names(self):
390
+ """Return the tuple of strings passed as 'name' to the constructor."""
391
+ return self._names
392
+
393
+ def role(self):
394
+ """Return the 'role' passed to the constructor."""
395
+ return self._role
396
+
397
+ def label(self):
398
+ """Return the 'label' passed to the constructor."""
399
+ return self._label
400
+
401
+ def halign(self):
402
+ """Return the value of 'halign' as passed to the constructor."""
403
+ return self._halign
404
+
405
+ def valign(self):
406
+ """Return the value of 'valign' as passed to the constructor."""
407
+ return self._valign
408
+
409
+ def orientation(self):
410
+ """Return the value of 'orientation' as passed to the constructor."""
411
+ return self._orientation
412
+
413
+ def width(self):
414
+ """Return the value of 'width' as passed to the constructor."""
415
+ return self._width
416
+
417
+ def height(self):
418
+ """Return the value of 'height' as passed to the constructor."""
419
+ return self._height
420
+
421
+ def padding(self):
422
+ """Return the value of 'padding' as passed to the constructor."""
423
+ return self._padding
424
+
425
+ def presentation(self):
426
+ """Return the value of 'presentation' as passed to the constructor."""
427
+ return self._presentation
428
+
429
+ def resources(self):
430
+ """Return the value of 'resources' as passed to the constructor."""
431
+ if self._contained_resources is None:
432
+ resources = []
433
+ if self._SUBSEQUENCES:
434
+ content = self._content
435
+ else:
436
+ content = (self._content,)
437
+ for subseq in content:
438
+ for c in subseq:
439
+ resources.extend(c.resources())
440
+ self._contained_resources = tuple(resources)
441
+ return self._resources + self._contained_resources
442
+
443
+
444
+ # ======================== Inline content elements ========================
445
+
446
+
447
+ class Strong(Container):
448
+ """Text emphasized by bold font face."""
449
+ pass
450
+
451
+
452
+ class Emphasized(Container):
453
+ """Text emphasized by slanted font face."""
454
+ pass
455
+
456
+
457
+ class Underlined(Container):
458
+ """Underlined text."""
459
+ pass
460
+
461
+
462
+ class Code(Container):
463
+ """Text representing a piece of computer code."""
464
+ pass
465
+
466
+
467
+ class Citation(Container):
468
+ """Citation of a text in another language."""
469
+ pass
470
+
471
+
472
+ class Quotation(Container):
473
+ """Quotation of content from other source."""
474
+
475
+ def __init__(self, content, source=None, uri=None, **kwargs):
476
+ """Arguments:
477
+
478
+ content -- quoted content as lcg.Content instance
479
+ source -- reference to the source of the quotation, such as the
480
+ original author, publication, web page etc. (basestring)
481
+ uri -- link to the source (basestring)
482
+
483
+ """
484
+ assert source is None or isinstance(source, basestring), source
485
+ assert uri is None or isinstance(uri, basestring), uri
486
+ self._source = source
487
+ self._uri = uri
488
+ super(Quotation, self).__init__(content, **kwargs)
489
+
490
+ def source(self):
491
+ """Return the quotation source as passed to the constructor."""
492
+ return self._source
493
+
494
+ def uri(self):
495
+ """Return the quotation source URI as passed to the constructor."""
496
+ return self._uri
497
+
498
+
499
+ class Superscript(Container):
500
+ """Text vertically aligned above the normal line level."""
501
+ pass
502
+
503
+
504
+ class Subscript(Container):
505
+ """Text vertically aligned below the normal line level."""
506
+ pass
507
+
508
+
509
+ class TextContent(Content):
510
+ """A simple piece of text."""
511
+
512
+ def __init__(self, text, **kwargs):
513
+ """Initialize the instance.
514
+
515
+ Arguments:
516
+
517
+ text -- the actual text content of this element as a string or
518
+ unicode
519
+ kwargs -- keyword arguments for parent class constructor.
520
+
521
+ """
522
+ assert isinstance(text, basestring), text
523
+ super(TextContent, self).__init__(**kwargs)
524
+ self._text = text
525
+
526
+ def __unicode__(self):
527
+ text = self._text.strip()
528
+ sample = text and text.splitlines()[0] or ''
529
+ if len(sample) > 20:
530
+ sample = sample[:20]
531
+ if len(sample) < len(text):
532
+ sample += '...'
533
+ cls = self.__class__.__name__
534
+ return '<%s at 0x%x text="%s">' % (cls, id(self), sample)
535
+
536
+ def text(self):
537
+ """Return the value of 'text' as passed to the constructor."""
538
+ return self._text
539
+
540
+ def clone(self, text):
541
+ """Return copy of the instance with text replaced by 'text'.
542
+
543
+ Arguments:
544
+
545
+ text -- text content of the element copy; basestring
546
+
547
+ """
548
+ cloned = copy.copy(self)
549
+ cloned._text = text
550
+ return cloned
551
+
552
+
553
+ class Link(Container):
554
+ """Link to internal or external location."""
555
+
556
+ class ExternalTarget(object):
557
+ """Representation of an external target specified by its URI."""
558
+
559
+ def __init__(self, uri, title, descr=None, lang=None):
560
+ """Arguments:
561
+
562
+ uri -- URI of the link target as a string
563
+ title -- title of the target as a string or unicode
564
+ descr -- ???
565
+ lang -- lowercase ISO 639-1 Alpha-2 language code
566
+
567
+ """
568
+ self._uri = uri
569
+ self._title = title
570
+ self._descr = descr
571
+ self._lang = lang
572
+
573
+ def uri(self):
574
+ return self._uri
575
+
576
+ def title(self):
577
+ return self._title
578
+
579
+ def descr(self):
580
+ return self._descr
581
+
582
+ def lang(self):
583
+ return self._lang
584
+
585
+ def __init__(self, target, label=None, descr=None, type=None, lang=None):
586
+ """Arguments:
587
+
588
+ target -- target of the link, instance of 'Section', 'lcg.ContentNode',
589
+ 'lcg.Resource', 'Link.ExternalTarget' or 'basestring'. If a string is
590
+ used, it is a temporary reference to be resolved in export time.
591
+ It allows creation of links which refer to objects, which can not
592
+ be resolved at the time of Link instance creation (their instances
593
+ may not exist yet when the source text is parsed). The reference
594
+ is then automatically resolved by the method 'target()' into one of
595
+ the other allowed link targets.
596
+ label -- link label text as a string or a 'Content' instance (such as
597
+ 'InlineImage' for image links)
598
+ descr -- breif target description text. If none the description is
599
+ taken from the 'target' instance depending on its type
600
+ type -- ???
601
+ lang -- lowercase ISO 639-1 Alpha-2 language code
602
+
603
+ """
604
+ assert isinstance(target, (Section, lcg.ContentNode, lcg.Resource, self.ExternalTarget,
605
+ basestring)), target
606
+ assert label is None or isinstance(label, (basestring, Content)), label
607
+ assert descr is None or isinstance(descr, basestring), descr
608
+ assert type is None or isinstance(type, basestring), type
609
+ assert lang is None or isinstance(lang, basestring), lang
610
+ if isinstance(label, basestring):
611
+ content = TextContent(label)
612
+ elif label is not None:
613
+ content = label
614
+ else:
615
+ content = ()
616
+ self._target = target
617
+ self._descr = descr
618
+ self._type = type
619
+ super(Link, self).__init__(content, lang=lang)
620
+
621
+ def target(self, context):
622
+ """Return the link target.
623
+
624
+ If a string reference was passed to the constructor argument 'target',
625
+ it is automatically resolved, so the returned instance is always one of
626
+ 'Section', 'lcg.ContentNode', 'lcg.Resource' or 'Link.ExternalTarget'.
627
+
628
+ """
629
+ target = self._target
630
+ if isinstance(target, basestring):
631
+ reference = target
632
+ target = context.resource(reference, warn=False)
633
+ if target is None and '@' not in reference:
634
+ if '#' in reference:
635
+ node_id, section_id = reference.split('#', 1)
636
+ node = context.node()
637
+ if node_id:
638
+ node = node.root().find_node(node_id)
639
+ if node:
640
+ target = node.find_section(context.lang(), section_id)
641
+ else:
642
+ target = context.node().root().find_node(reference)
643
+ if target is None:
644
+ target = self.ExternalTarget(reference, reference)
645
+ return target
646
+
647
+ def descr(self):
648
+ """Return the link description as passed to the constructor argument 'descr'."""
649
+ return self._descr
650
+
651
+ def type(self):
652
+ """Return the value of 'type' as passed to the constructor."""
653
+ return self._type
654
+
655
+ def node_id(self):
656
+ "Return target node id (basestring) or 'None'."
657
+ target = self._target
658
+ if isinstance(target, basestring) and '#' in target:
659
+ return target.split('#', 1)[0]
660
+ return None
661
+
662
+
663
+ class Abbreviation(TextContent):
664
+ """Abbreviation with description."""
665
+
666
+ def __init__(self, text, descr, **kwargs):
667
+ """Arguments:
668
+
669
+ text -- the abbreviation as a string or unicode.
670
+ descr -- the description as a string or unicode.
671
+
672
+ """
673
+ assert isinstance(descr, basestring), descr
674
+ self._descr = descr
675
+ super(Abbreviation, self).__init__(text, **kwargs)
676
+
677
+ def descr(self):
678
+ """Return the abbreviation description given in the constructor."""
679
+ return self._descr
680
+
681
+
682
+ class Anchor(TextContent):
683
+ """Target of a link (an anchor)."""
684
+
685
+ def __init__(self, anchor, text='', **kwargs):
686
+ """Arguments:
687
+
688
+ anchor -- name of the link target anchor as a string.
689
+ text -- text of the target place as a string or unicode.
690
+
691
+ """
692
+ assert isinstance(anchor, basestring), anchor
693
+ self._anchor = anchor
694
+ super(Anchor, self).__init__(text, **kwargs)
695
+
696
+ def anchor(self):
697
+ """Return link target name given in the constructor."""
698
+ return self._anchor
699
+
700
+
701
+ class _InlineObject(Content):
702
+ """Super class for embedded objects, such as images, audio and video."""
703
+
704
+ def __init__(self, title=None, descr=None, name=None, lang=None):
705
+ """Arguments:
706
+
707
+ title -- object title as a string or unicode. If not None, overrides
708
+ 'resource.title()' for this particular use.
709
+ descr -- object description a string or unicode. If not None, overrides
710
+ 'resource.descr()' for this particular use.
711
+ name -- arbitrary name (string) identifying the object. Used as CSS
712
+ class name in HTML to allow individual styling.
713
+ lang -- content language as an ISO 639-1 Alpha-2 language code (lowercase)
714
+
715
+ """
716
+ assert title is None or isinstance(title, basestring), title
717
+ assert descr is None or isinstance(descr, basestring), descr
718
+ assert name is None or isinstance(name, basestring), name
719
+ self._title = title
720
+ self._descr = descr
721
+ self._name = name
722
+ super(_InlineObject, self).__init__(lang=lang)
723
+
724
+ def _resource_instance(self, context, resource, cls):
725
+ if isinstance(resource, basestring):
726
+ filename = resource
727
+ if ((filename.startswith('http:') or filename.startswith('https:') or
728
+ filename.startswith('ftp:'))):
729
+ resource = cls(filename, uri=filename)
730
+ else:
731
+ resource = context.resource(filename)
732
+ if resource is None:
733
+ resource = cls(filename, uri=filename)
734
+ else:
735
+ assert isinstance(resource, cls), resource
736
+ return resource
737
+
738
+ def title(self):
739
+ """Return the value of 'title' as passed to the constructor."""
740
+ return self._title
741
+
742
+ def descr(self):
743
+ """Return the value of 'descr' as passed to the constructor."""
744
+ return self._descr
745
+
746
+ def name(self):
747
+ """Return the value of 'name' as passed to the constructor."""
748
+ return self._name
749
+
750
+
751
+ class InlineImage(_InlineObject):
752
+ """Image embedded inside the document.
753
+
754
+ The image may be either a part of a textual paragraph or standalone
755
+ (outside paragraph). When inside paragraph, it may float left or right
756
+ from the surrounding text or be put directly into its position within the
757
+ text.
758
+
759
+ """
760
+
761
+ LEFT = 'left'
762
+ RIGHT = 'right'
763
+ TOP = 'top'
764
+ BOTTOM = 'bottom'
765
+ MIDDLE = 'middle'
766
+
767
+ def __init__(self, image, align=None, width=None, height=None, standalone=False, **kwargs):
768
+ """Arguments:
769
+
770
+ image -- the displayed image as an 'lcg.Image' resource instance.
771
+ align -- requested alignment of the image within the surrounding
772
+ text; one of the class constants: 'InlineImage.LEFT' and
773
+ 'InlineImage.RIGHT' make the image float aside the text,
774
+ 'InlineImage.TOP', 'InlineImage.BOTTOM' , 'InlineImage.MIDDLE'
775
+ align the image horizontally within the text line. 'None' let's the
776
+ output backend decide the best image position.
777
+ width -- output image width as lcg.Unit subclass instance. If only
778
+ one of width/height is specified (not None), the other dimension is
779
+ computed to maintain the original image proportions. Note that
780
+ various output backends may only support certain units.
781
+ height -- output image height as lcg.Unit subclass instance. See 'width'.
782
+ standalone -- iff true then never put the image inside a paragraph.
783
+
784
+ All other keyword arguments are passed to the parent class constructor.
785
+
786
+ """
787
+ assert isinstance(image, (lcg.Image, basestring)), image
788
+ assert align in (None, self.LEFT, self.RIGHT, self.TOP, self.BOTTOM, self.MIDDLE), align
789
+ assert width is None or isinstance(width, lcg.Unit), width
790
+ assert height is None or isinstance(height, lcg.Unit), height
791
+ assert isinstance(standalone, bool), standalone
792
+ self._image = image
793
+ self._align = align
794
+ self._width = width
795
+ self._height = height
796
+ self._standalone = standalone
797
+ super(InlineImage, self).__init__(**kwargs)
798
+
799
+ def image(self, context):
800
+ """Return the value of 'image' passed to the constructor.
801
+
802
+ If the 'image' was passed as a string reference, it is automatically
803
+ converted into an 'lcg.Image' instance.
804
+
805
+ """
806
+ return self._resource_instance(context, self._image, lcg.Image)
807
+
808
+ def align(self):
809
+ """Return the value of 'align' as passed to the constructor."""
810
+ return self._align
811
+
812
+ def width(self):
813
+ """Return the value of 'width' as passed to the constructor."""
814
+ return self._width
815
+
816
+ def height(self):
817
+ """Return the value of 'height' as passed to the constructor."""
818
+ return self._height
819
+
820
+ def standalone(self):
821
+ """Return the value of 'standalone' as passed to the constructor."""
822
+ return self._standalone
823
+
824
+
825
+ class InlineAudio(_InlineObject):
826
+ """Audio file embedded inside the document.
827
+
828
+ When possible, this element should be represented by an interactive control
829
+ which invokes playback of the embedded audio.
830
+
831
+ """
832
+
833
+ def __init__(self, audio, image=None, **kwargs):
834
+ """Arguments:
835
+
836
+ audio -- the target audio file as 'lcg.Audio' resource instance.
837
+ image -- visual presentation image as an 'lcg.Image' resource instance or None.
838
+
839
+ All other arguments are passed to the parent class constructor.
840
+
841
+ """
842
+ assert isinstance(audio, (lcg.Audio, basestring)), audio
843
+ assert image is None or isinstance(image, (lcg.Image, basestring)), image
844
+ self._audio = audio
845
+ self._image = image
846
+ super(InlineAudio, self).__init__(**kwargs)
847
+
848
+ def audio(self, context):
849
+ """Return the value of 'audio' as passed to the constructor.
850
+
851
+ If the 'audio' was passed as a string reference, it is automatically
852
+ converted into an 'lcg.Audio' instance.
853
+
854
+ """
855
+ return self._resource_instance(context, self._audio, lcg.Audio)
856
+
857
+ def image(self, context):
858
+ """Return the value of 'image' as passed to the constructor.
859
+
860
+ If the 'image' was passed as a string reference, it is automatically
861
+ converted into an 'lcg.Image' instance.
862
+
863
+ """
864
+ return self._resource_instance(context, self._image, lcg.Image)
865
+
866
+
867
+ class InlineVideo(_InlineObject):
868
+ """Video file embedded inside the document.
869
+
870
+ When possible, this element should be represented as an embedded video
871
+ player preloaded with given video and playing it on invocation.
872
+
873
+ """
874
+
875
+ def __init__(self, video, image=None, size=None, **kwargs):
876
+ """Arguments:
877
+
878
+ video -- 'lcg.Video' resource instance.
879
+ image -- video thumbnail image as an 'lcg.Image' resource instance or None.
880
+ size -- size in pixels as a tuple of two integers (WIDTH, HEIGHT)
881
+
882
+ All other keyword arguments are passed to the parent class constructor.
883
+
884
+ """
885
+ assert isinstance(video, (lcg.Video, basestring)), video
886
+ assert image is None or isinstance(image, (lcg.Image, basestring)), image
887
+ assert size is None or isinstance(size, tuple), size
888
+ self._video = video
889
+ self._image = image
890
+ self._size = size
891
+ super(InlineVideo, self).__init__(**kwargs)
892
+
893
+ def video(self, context):
894
+ """Return the value of 'video' as passed to the constructor.
895
+
896
+ If the 'video' was passed as a string reference, it is automatically
897
+ converted into an 'lcg.Video' instance.
898
+
899
+ """
900
+ return self._resource_instance(context, self._video, lcg.Video)
901
+
902
+ def image(self, context):
903
+ """Return the value of 'image' as passed to the constructor.
904
+
905
+ If the 'image' was passed as a string reference, it is automatically
906
+ converted into an 'lcg.Image' instance.
907
+
908
+ """
909
+ return self._resource_instance(context, self._image, lcg.Image)
910
+
911
+ def size(self):
912
+ """Return the value of 'size' as passed to the constructor."""
913
+ return self._size
914
+
915
+
916
+ class InlineExternalVideo(Content):
917
+ """Embedded video from external services such as YouTube or Vimeo
918
+
919
+ When possible, this element should be represented as an embedded video
920
+ player preloaded with given video and playing it on invocation.
921
+
922
+ """
923
+
924
+ def __init__(self, service, video_id, title=None, descr=None, size=None, lang=None):
925
+ """Arguments:
926
+
927
+ service -- string identifier of the video service. The two currently
928
+ supported services are 'youtube' and 'vimeo'.
929
+ video_id -- string identifier of the video within the given service.
930
+ title -- video title as a string or None.
931
+ size -- explicit video size in pixels as a tuple of two integers
932
+ (WIDTH, HEIGHT) or None for the default size.
933
+ lang -- content language as an ISO 639-1 Alpha-2 language code (lowercase)
934
+
935
+ """
936
+ assert service in ('youtube', 'vimeo'), service
937
+ assert isinstance(video_id, basestring), video_id
938
+ assert size is None or isinstance(size, tuple), size
939
+ self._service = service
940
+ self._video_id = video_id
941
+ self._title = title
942
+ self._descr = descr
943
+ self._size = size
944
+ super(InlineExternalVideo, self).__init__(lang=lang)
945
+
946
+ def service(self):
947
+ """Return the string identifier of the video service."""
948
+ return self._service
949
+
950
+ def video_id(self):
951
+ """Return the string identifier of the video within the service."""
952
+ return self._video_id
953
+
954
+ def title(self):
955
+ """Return the value of 'title' as passed to the constructor."""
956
+ return self._title
957
+
958
+ def descr(self):
959
+ """Return the value of 'descr' as passed to the constructor."""
960
+ return self._descr
961
+
962
+ def size(self):
963
+ """Return the video size in pixels as a pair of integers or None."""
964
+ return self._size
965
+
966
+
967
+ class Title(Content):
968
+ """Inline element, which is substituted by the title of the requested item in export time.
969
+
970
+ It may not be possible to find out what is the title of the current node or some other element
971
+ in the time of content construction. So this symbolic element may be used to refer to the item
972
+ an it will be simply replaced by the title text in export time.
973
+
974
+ The constructor argument 'id' may be used to refer to the required item. It may be a section
975
+ id in the current page or an identifier of another node. The default value (None) refers to
976
+ the title of the current node. If the item refered by id can not be found, the id itself is
977
+ used for substitution.
978
+
979
+ """
980
+
981
+ def __init__(self, id=None):
982
+ """Arguments:
983
+
984
+ id -- reference to the item providing the required title; it may be a
985
+ section id (??? what is it ???) in the current page or an
986
+ identifier of another node (??? what is it ???). 'None' refers to
987
+ the title of the current node. If the item refered by 'id' cannot
988
+ be found, the id itself is used for substitution.
989
+
990
+ """
991
+ super(Title, self).__init__()
992
+ self._id = id
993
+
994
+ def id(self):
995
+ """Return the value of 'id' as passed to the constructor."""
996
+ return self._id
997
+
998
+
999
+ class HorizontalSeparator(Content):
1000
+ """Horizontal separator.
1001
+
1002
+ Typically rendered as a horizontal line separating visually the content
1003
+ above and below it.
1004
+
1005
+ """
1006
+
1007
+ def __init__(self, thickness=None, color=None, **kwargs):
1008
+ """Arguments:
1009
+ thickness -- Thickness of the line as 'lcg.Unit' or None for the (media
1010
+ dependent) default thickness. Currently only supported by the PDF backend.
1011
+ color -- line color as 'lcg.Color' or None for the (media dependent)
1012
+ default color. Currently only supported by the PDF backend.
1013
+ """
1014
+ assert thickness is None or isinstance(thickness, lcg.Unit), thickness
1015
+ assert color is None or isinstance(color, lcg.Color), color
1016
+ self._thickness = thickness
1017
+ self._color = color
1018
+ super(HorizontalSeparator, self).__init__(**kwargs)
1019
+
1020
+ def thickness(self):
1021
+ return self._thickness
1022
+
1023
+ def color(self):
1024
+ return self._color
1025
+
1026
+
1027
+ class NewPage(Content):
1028
+ """New page starts here."""
1029
+ pass
1030
+
1031
+
1032
+ class NewLine(Content):
1033
+ """Explicit line break."""
1034
+ pass
1035
+
1036
+
1037
+ class PageNumber(Content):
1038
+ """Current page number.
1039
+
1040
+ The page number is generated as an ordinal arabic number starting from one.
1041
+
1042
+ This content may be used only inside page headers and footers.
1043
+
1044
+ """
1045
+
1046
+ def __init__(self, total=False, separator=None, lang=None):
1047
+ """
1048
+ Arguments:
1049
+
1050
+ total -- iff true, output not only the page number, but also the
1051
+ total number of pages; if 'separator' is 'None', output only the
1052
+ total number of pages
1053
+ separator -- basestring or 'None'; if it is a basestring and 'total'
1054
+ is true, insert it betwenn the page numebr and the total number of
1055
+ pages
1056
+ lang -- content language as an ISO 639-1 Alpha-2 language code (lowercase)
1057
+
1058
+ """
1059
+ super(PageNumber, self).__init__(lang=lang)
1060
+ self._total = total
1061
+ self._separator = separator
1062
+
1063
+ def total(self):
1064
+ """Return the value of 'total' as passed to the constructor."""
1065
+ return self._total
1066
+
1067
+ def separator(self):
1068
+ """Return the value of 'separator' as passed to the constructor."""
1069
+ return self._separator
1070
+
1071
+
1072
+ class PageHeading(Content):
1073
+ """Current page heading.
1074
+
1075
+ This content may be used only inside page headers and footers.
1076
+
1077
+ """
1078
+ pass
1079
+
1080
+
1081
+ class HSpace(Content):
1082
+ """Horizontal space of given size.
1083
+
1084
+ This should be used only in places where explicit space is needed. In many
1085
+ cases better means such as higher level elements, alignment or style sheets
1086
+ can be used.
1087
+
1088
+ """
1089
+
1090
+ def __init__(self, size, lang=None):
1091
+ """
1092
+ @type: L{lcg.Unit}
1093
+ @param: Size of the space.
1094
+ @type: string
1095
+ @param: Content language as an ISO 639-1 Alpha-2 language code (lowercase).
1096
+ """
1097
+ super(HSpace, self).__init__(lang=lang)
1098
+ assert isinstance(size, lcg.Unit), size
1099
+ self._size = size
1100
+
1101
+ def size(self, context):
1102
+ """Return the value of 'size' as passed to the constructor."""
1103
+ return self._size
1104
+
1105
+
1106
+ class VSpace(HSpace):
1107
+ """Vertical space of given size.
1108
+
1109
+ This should be used only in places where explicit space is needed. In many
1110
+ cases better means such as higher level elements, alignment or style sheets
1111
+ can be used.
1112
+
1113
+ """
1114
+
1115
+
1116
+ class HtmlContent(Content):
1117
+ """Special element for dirtect HTML embedding in LCG content hierarchy.
1118
+
1119
+ This class only supports HTML export and an attempt to export an element of
1120
+ this class to any other target format will lead to an error.
1121
+
1122
+ The HTML content may be passed directly as a Python string or as a Python
1123
+ function which will be called to generate the content dynamically in export
1124
+ time. The function will be called with at least two arguments (context,
1125
+ element), where 'element' is the 'HtmlContent' instance itself and
1126
+ 'context' is a 'wiking.Exporter.Context' instance. Any positional
1127
+ arguments passed to the constructor are also passed to the export function.
1128
+
1129
+ Use with caution. Defining specific content classes for more generic
1130
+ content elements is encouraged over using this class. This class should
1131
+ only be used for simple cases where defining a class makes too much
1132
+ unnecessary noise...
1133
+
1134
+ This class demonstrates a content element, which exports itself actively
1135
+ and doesn't rely on the exporter as the other generic elements defined in
1136
+ this module.
1137
+
1138
+ """
1139
+
1140
+ def __init__(self, content, *args, **kwargs):
1141
+ """Initialize the instance.
1142
+
1143
+ Arguments:
1144
+
1145
+ content -- the actual HTML content of this element as a basestring
1146
+ or an export function (see class docstring for details).
1147
+ *args -- additional positional arguments passed to the export
1148
+ function (given in 'content'). No arguments are allowed when
1149
+ 'content' is a basestring.
1150
+ kwargs -- keyword arguments for parent class constructor.
1151
+
1152
+ """
1153
+ assert isinstance(content, basestring) or callable(content), content
1154
+ self._content = content
1155
+ self._export_args = args
1156
+ super(HtmlContent, self).__init__(**kwargs)
1157
+
1158
+ def export(self, context):
1159
+ assert isinstance(context.exporter(), lcg.HtmlExporter), \
1160
+ "Only HTML export is supported for this element."
1161
+ if isinstance(self._content, basestring):
1162
+ g = context.generator()
1163
+ return g.noescape(self._content)
1164
+ else:
1165
+ return self._content(context, self, *self._export_args)
1166
+
1167
+
1168
+ class Heading(Container):
1169
+ """Heading, e.g. heading of a section.
1170
+
1171
+ Purpose of this element is twofold: to provide information about level of
1172
+ the heading and to distinguish the element from other parts of a document
1173
+ for the purpose of applying specific styles to its content.
1174
+
1175
+ """
1176
+
1177
+ def __init__(self, content, level, **kwargs):
1178
+ """
1179
+ Arguments:
1180
+
1181
+ cotnent -- the actual content of this element as lcg.Content element
1182
+ or their sequence
1183
+ level -- level of the heading, positive integer, starting from 1
1184
+ kwargs -- keyword arguments for parent class constructor
1185
+
1186
+ """
1187
+ assert isinstance(level, int) and level > 0, level
1188
+ super(Heading, self).__init__(content, **kwargs)
1189
+ self._level = level
1190
+
1191
+ def level(self):
1192
+ "Return level of the heading, positive integer, starting from 1."
1193
+ return self._level
1194
+
1195
+
1196
+ class PreformattedText(TextContent):
1197
+ """Preformatted text."""
1198
+
1199
+ def __init__(self, text, mime_type=None, **kwargs):
1200
+ """Arguments:
1201
+
1202
+ text -- the text itself as a basestring.
1203
+ mime_type -- MIME type of the text as a basestring. Used for
1204
+ syntax highlighting in HTML export.
1205
+
1206
+ """
1207
+ super(PreformattedText, self).__init__(text, **kwargs)
1208
+ self._mime_type = mime_type
1209
+
1210
+ def mime_type(self):
1211
+ """Return MIME type of the text if it was passed to the constructor or None."""
1212
+ return self._mime_type
1213
+
1214
+
1215
+ # ======================== Block level elements ========================
1216
+
1217
+
1218
+ SectionContainer = Container
1219
+ """Deprecated: Use 'Container' instead."""
1220
+
1221
+
1222
+ class Paragraph(Container):
1223
+ """A paragraph of text, where the text can be any 'Content'."""
1224
+
1225
+ def __init__(self, content, noindent=False, **kwargs):
1226
+ """Arguments:
1227
+
1228
+ content -- sequence of list items as 'Content' instances.
1229
+ noindent -- True if first line indentation should be suppressed.
1230
+ Note, that only PDF export currently supports first paragraph line
1231
+ indentation.
1232
+
1233
+ """
1234
+ assert isinstance(noindent, bool)
1235
+ self._noindent = noindent
1236
+ super(Paragraph, self).__init__(content, **kwargs)
1237
+
1238
+ def noindent(self):
1239
+ """Return 'noindent' as it was given in the constructor."""
1240
+ return self._noindent
1241
+
1242
+
1243
+ class ItemizedList(Container):
1244
+ """An itemized list."""
1245
+
1246
+ NUMERIC = 'numeric'
1247
+ LOWER_ALPHA = 'lower-alpha'
1248
+ UPPER_ALPHA = 'upper-alpha'
1249
+
1250
+ def __init__(self, content, order=None, **kwargs):
1251
+ """Arguments:
1252
+
1253
+ content -- sequence of list items as 'Content' instances.
1254
+ order -- one of class constants determining the list item ordering style or
1255
+ None for an unordered list (bullet list).
1256
+
1257
+ """
1258
+ assert order in (None, self.LOWER_ALPHA, self.UPPER_ALPHA, self.NUMERIC)
1259
+ self._order = order
1260
+ super(ItemizedList, self).__init__(content, **kwargs)
1261
+
1262
+ def order(self):
1263
+ """Return the value of 'order' as passed to the constructor."""
1264
+ return self._order
1265
+
1266
+
1267
+ class DefinitionList(Container):
1268
+ """A list of definitions.
1269
+
1270
+ The constructor accepts a sequence of definitions, where each definition is a pair '(TERM,
1271
+ DESCRIPTION)', where both items are 'Content' instances.
1272
+
1273
+ """
1274
+ _SUBSEQUENCES = True
1275
+ _SUBSEQUENCE_LENGTH = 2
1276
+
1277
+
1278
+ class FieldSet(Container):
1279
+ """A list of label and value pairs.
1280
+
1281
+ The constructor accepts a sequence of '(LABEL, VALUE)' pairs, where both
1282
+ items are 'Content' instances. Logically similar to 'DefinitionList', but
1283
+ more suitable for an enumeration of shorter fields presented side by side
1284
+ (while definition description usually follows on another line below the
1285
+ term).
1286
+
1287
+ """
1288
+ _SUBSEQUENCES = True
1289
+ _SUBSEQUENCE_LENGTH = 2
1290
+
1291
+
1292
+ class TableCell(Container):
1293
+ """Table cell is a container of cell content and may appear within 'TableRow'."""
1294
+ LEFT = lcg.HorizontalAlignment.LEFT
1295
+ RIGHT = lcg.HorizontalAlignment.RIGHT
1296
+ CENTER = lcg.HorizontalAlignment.CENTER
1297
+
1298
+ def __init__(self, content, align=None, **kwargs):
1299
+ """Arguments:
1300
+
1301
+ align -- requested cell content alignment, 'None' or one of the
1302
+ constants 'TableCell.LEFT', 'TableCell.RIGHT', 'TableCell.CENTER'
1303
+ or 'None' for the default alignment.
1304
+
1305
+ """
1306
+ assert align in (None, self.LEFT, self.RIGHT, self.CENTER), align
1307
+ self._align = align
1308
+ super(TableCell, self).__init__(content, **kwargs)
1309
+
1310
+ def align(self):
1311
+ """Return the value of 'align' as passed to the constructor."""
1312
+ return self._align
1313
+
1314
+
1315
+ class TableHeading(TableCell):
1316
+ """Table heading is a container of heading content and may appear within 'TableRow'."""
1317
+ pass
1318
+
1319
+
1320
+ class TableRow(Container):
1321
+ """Table row is a container of cells or headings and may appear within 'Table'."""
1322
+ _ALLOWED_CONTENT = (TableCell, TableHeading)
1323
+
1324
+ def __init__(self, content, line_above=None, line_below=None, iterated=False, **kwargs):
1325
+ """Arguments:
1326
+
1327
+ line_above -- iff true, request line above the row
1328
+ line_below -- iff true, request line below the row
1329
+ iterated -- iff true, this row expands into any number of rows
1330
+ according to the iterator variable used within one or more of its
1331
+ cells
1332
+
1333
+ """
1334
+ self._line_above = line_above
1335
+ self._line_below = line_below
1336
+ self._iterated = iterated
1337
+ super(TableRow, self).__init__(content, **kwargs)
1338
+
1339
+ def line_above(self):
1340
+ return self._line_above
1341
+
1342
+ def line_below(self):
1343
+ return self._line_below
1344
+
1345
+ def set_line_below(self, n):
1346
+ self._line_below = n
1347
+
1348
+ def iterated(self):
1349
+ return self._iterated
1350
+
1351
+
1352
+ class Table(Container):
1353
+ """Table is a container of 'TableRow' instances."""
1354
+ _ALLOWED_CONTENT = (TableRow, HorizontalSeparator,)
1355
+
1356
+ def __init__(self, content, title=None, long=False, column_widths=None, bars=(),
1357
+ compact=False, transformations=('facing', 'transpose',), **kwargs):
1358
+ """Arguments:
1359
+
1360
+ content -- sequence of 'TableRow' and 'HorizontalSeparator' instances
1361
+ title -- 'None' or table caption as a string or unicode
1362
+ long -- boolean indicating whether the table is long. Long table is
1363
+ usually a table that may not fit on a single page. Long tables may
1364
+ be handled in a different way, e.g. by splitting into several parts
1365
+ even when the table could fit on a new separate page or by using
1366
+ different column widths on each page when the column widths are
1367
+ variable.
1368
+ column_widths -- sequence of 'lcg.Unit's defining widths of columns, it
1369
+ must have the same number of elements and in the same order as
1370
+ table columns. If any of the elements is 'None', the width of the
1371
+ corresponding column is to be determined automatically.
1372
+ Alternatively the whole 'column_widths' value may be 'None' to
1373
+ determine widths of all the columns automatically.
1374
+ bars -- sequence of vertical bars positions, integers. The position
1375
+ before the first column is numbered 0, the next position is 1 and
1376
+ the position after the last column is numbered N where N is the
1377
+ number of columns in the table.
1378
+ compact -- If True, suppress default padding in all table cells
1379
+ (including headings). Use containers to add custom padding. Only
1380
+ implemented in PDF output.
1381
+ transformations -- permitted table transformations; currently used
1382
+ only in Braille backend. It's a sequence containing any of the
1383
+ following strings: 'facing' (table may be spread across facing
1384
+ pages), 'transpose' (table may be transposed), 'row-expand' (table
1385
+ may be expanded by rows), 'column-expand' (table may be expanded by
1386
+ columns), 'split' (table may be vertically split into several
1387
+ narrower tables).
1388
+
1389
+ """
1390
+ assert title is None or isinstance(title, basestring)
1391
+ assert isinstance(long, bool), long
1392
+ assert column_widths is None or isinstance(column_widths, (tuple, list)), column_widths
1393
+ assert isinstance(bars, (tuple, list)), bars
1394
+ assert isinstance(compact, bool), compact
1395
+ self._title = title
1396
+ self._long = long
1397
+ self._column_widths = column_widths
1398
+ self._bars = bars
1399
+ self._compact = compact
1400
+ self._transformations = transformations
1401
+ super(Table, self).__init__(content, **kwargs)
1402
+
1403
+ def title(self):
1404
+ """Return the value of 'title' as passed to the constructor."""
1405
+ return self._title
1406
+
1407
+ def long(self):
1408
+ """Return the value of 'long' as passed to the constructor."""
1409
+ return self._long
1410
+
1411
+ def column_widths(self):
1412
+ """Return the value of 'column_widths' as passed to the constructor."""
1413
+ return self._column_widths
1414
+
1415
+ def bars(self):
1416
+ """Return positions of vertical bars.
1417
+
1418
+ See '__init__()' documentation for information about the positions.
1419
+
1420
+ """
1421
+ return self._bars
1422
+
1423
+ def compact(self):
1424
+ """Return the value of 'compact' passed to the constructor."""
1425
+ return self._compact
1426
+
1427
+ def transformations(self):
1428
+ """Return the value of 'transformations' as passed to the constructor."""
1429
+ return self._transformations
1430
+
1431
+
1432
+ class Section(Container):
1433
+ """Section wraps the subordinary contents into an inline section.
1434
+
1435
+ Section is very similar to a 'Container', but there are a few
1436
+ differences:
1437
+
1438
+ * Every section has a title, which appears in the output document as a
1439
+ heading.
1440
+
1441
+ * Sections can be used as link targets. You can define section id
1442
+ explicitly or it is assigned automatically. The automatically assigned
1443
+ ids are based on section order and hierarchy, so they will not change
1444
+ across several LCG invocations as long as the hierarchy remains
1445
+ unchanged.
1446
+
1447
+ * Sections are numbered. Each section knows its number within its
1448
+ container.
1449
+
1450
+ """
1451
+ _ID_PREFIX = 'sec'
1452
+
1453
+ def __init__(self, title, content, heading=None, id=None, anchor=None,
1454
+ descr=None, in_toc=True, **kwargs):
1455
+ """Arguments:
1456
+
1457
+ title -- plain text section title; basestring
1458
+ content -- the actual content wrapped into this section as a
1459
+ sequence of 'Content' instances in the order in which they should
1460
+ appear in the output
1461
+ heading -- content to be used as section heading. By default (when
1462
+ None), the content is created automatically as TextContent(title),
1463
+ but you may pass any lcg.Content instance when some more fancy
1464
+ content is desired.
1465
+ anchor -- deprecated - use 'id' instead.
1466
+ in_toc -- a boolean flag indicating whether this section is supposed
1467
+ to be included in the Table of Contents
1468
+
1469
+ Other keyword arguments are defined by parent classes.
1470
+
1471
+ """
1472
+ if anchor:
1473
+ assert id is None
1474
+ id = anchor
1475
+ assert isinstance(title, basestring), title
1476
+ assert heading is None or isinstance(heading, Content), heading
1477
+ assert isinstance(in_toc, bool), in_toc
1478
+ self._title = title
1479
+ self._in_toc = in_toc
1480
+ self._descr = descr
1481
+ self._heading = heading or TextContent(title)
1482
+ super(Section, self).__init__(content, id=id, **kwargs)
1483
+
1484
+ def section_path(self):
1485
+ """Return the sequence of parent sections in the container hierarchy.
1486
+
1487
+ The returned value is a list of 'Section' instances, which are above
1488
+ this section in the hieararchy. The document's top level section
1489
+ appears as the first and 'self' is always the last element of the list.
1490
+
1491
+ """
1492
+ return [c for c in self.container_path() if isinstance(c, Section)]
1493
+
1494
+ def title(self):
1495
+ """Return the section title as a basestring."""
1496
+ return self._title
1497
+
1498
+ def descr(self):
1499
+ """Return a breif (but more verbose than title) description as a basestring or None."""
1500
+ return self._descr
1501
+
1502
+ def heading(self):
1503
+ """Return section heading as a 'Heading' instance.
1504
+
1505
+ Return the content passed as 'heading' argument wrapped in the
1506
+ 'Heading' element (denoting the actual section heading level depending
1507
+ on section position in the document).
1508
+
1509
+ """
1510
+ return Heading(self._heading, level=len(self.section_path()) + 1)
1511
+
1512
+ def in_toc(self):
1513
+ """Return true if the section is supposed to appear in TOC."""
1514
+ return self._in_toc
1515
+
1516
+ def id(self):
1517
+ """Return unique section identifier as a string.
1518
+
1519
+ If 'id' was passed to the constructor, it is used. Otherwise the
1520
+ identifier is automatically generated. The generated id is unique
1521
+ within the parent node and does not change if the section structure
1522
+ remains unchanged (external references should work throughout multiple
1523
+ exports in this case).
1524
+
1525
+ """
1526
+ def section_number(x):
1527
+ container = x.container()
1528
+ while container and not isinstance(container, Section) and container.container():
1529
+ container = container.container()
1530
+ if container:
1531
+ return list(container.sections()).index(x) + 1
1532
+ else:
1533
+ return 1
1534
+ if self._id is None:
1535
+ path = self.section_path()
1536
+ if len(path) >= 2:
1537
+ self._id = path[-2].id() + '.' + unistr(section_number(self))
1538
+ else:
1539
+ numbers = [unistr(section_number(x)) for x in path]
1540
+ self._id = self._ID_PREFIX + '.'.join(numbers)
1541
+ return self._id
1542
+
1543
+ # Temporary backwards compatibility.
1544
+ anchor = id
1545
+
1546
+
1547
+ class TableOfContents(Content):
1548
+ """A table of contents which lists the content subtree.
1549
+
1550
+ This element works as a sort of macro, which expands to the hierarchical
1551
+ listing of content nodes and their subcontent (sections) on the output.
1552
+
1553
+ The items actually present in the table of contents depend on the content
1554
+ itself (i.e. hidden sections are omitted automatically) and on the arguments
1555
+ passed to the 'TableOfContents' constructor (limiting the hierachy depth,
1556
+ selection of the root item of the hierarchy, etc).
1557
+
1558
+ See also the 'NodeIndex' class if you need to display a hierarchy of nodes.
1559
+
1560
+ """
1561
+ _TOC_ITEM_TYPE = Content
1562
+
1563
+ def __init__(self, item=None, title=None, depth=None, detailed=True, **kwargs):
1564
+ """Arguments:
1565
+ item -- the place where to start in the content hierarchy tree as a
1566
+ 'Container' instance instance or None. If not None, the table of
1567
+ contents will start from that point -- the contained sections (see
1568
+ 'Container.sections()') will become top level items in the table.
1569
+ If None, the container of given 'TableOfContents' element itself is
1570
+ used so the result is a local Table of Contents at given place.
1571
+ See also 'Section' documentation for more information how section
1572
+ hierarchy representation is built.
1573
+ title -- the title of the index as a string or unicode
1574
+ depth -- hierarchy depth limit as an integer or 'None' for unlimited depth
1575
+ detailed -- boolean indicating whether the whole 'Content' hierarchy
1576
+ ('True') or only 'lcg.ContentNode' hierarchy ('False') of the leaf
1577
+ nodes of the node tree will be included in the index. This argument
1578
+ has no effect when 'item' is a 'Container' instance.
1579
+
1580
+ All other arguments have the same meaning as in the parent class constructor.
1581
+
1582
+ """
1583
+ assert item is None or isinstance(item, self._TOC_ITEM_TYPE)
1584
+ assert title is None or isinstance(title, basestring)
1585
+ assert depth is None or isinstance(depth, int)
1586
+ assert isinstance(detailed, bool)
1587
+ self._item = item
1588
+ self._title = title
1589
+ self._depth = depth
1590
+ self._detailed = detailed
1591
+ super(TableOfContents, self).__init__(**kwargs)
1592
+
1593
+ def _root_item(self):
1594
+ """Return the position in the hierarchy where to start the table of contents."""
1595
+ item = self._item
1596
+ if not item:
1597
+ # Create a local table of contens automatically (taking this elements container as a
1598
+ # start point).
1599
+ assert isinstance(self._container, Container), self._container
1600
+ item = self._container
1601
+ return item
1602
+
1603
+ def title(self):
1604
+ """Return the value of 'title' as passed to the constructor."""
1605
+ return self._title
1606
+
1607
+ def items(self, lang):
1608
+ """Return the hierarchy of items present in the table of contents.
1609
+
1610
+ The returned value is a recursive structure. It is a list of pairs
1611
+ (tuples) ITEM, SUBITEMS, where ITEM is always either 'Section' or
1612
+ 'lcg.ContentNode' instance and SUBITEMS is a nested list of the same
1613
+ type if given item has subitems displayed in the table of contents.
1614
+ Otherwise SUBITEMS is an empty list.
1615
+
1616
+ The method handles hidden items, depth limit and other conditions
1617
+ internally, so that the caller doesn't need to care which items belong
1618
+ to the table of contents. All returned items should be displayed.
1619
+
1620
+ """
1621
+ def subitems(item, depth=None):
1622
+ if depth is not None:
1623
+ if depth <= 0:
1624
+ return []
1625
+ depth -= 1
1626
+ if isinstance(item, lcg.ContentNode):
1627
+ items = [node for node in item.children() if not node.hidden()]
1628
+ else:
1629
+ items = []
1630
+ if len(items) == 0 and self._detailed:
1631
+ if isinstance(item, (tuple, list)):
1632
+ items = item
1633
+ else:
1634
+ if isinstance(item, lcg.ContentNode):
1635
+ sections = item.content(lang).sections()
1636
+ else:
1637
+ sections = item.sections()
1638
+ items = [s for s in sections if s.in_toc()]
1639
+ return [(subitem, subitems(subitem, depth)) for subitem in items]
1640
+ return subitems(self._root_item(), depth=self._depth)
1641
+
1642
+
1643
+ class NodeIndex(TableOfContents):
1644
+ """A table of contents which lists the node subtree of the current node.
1645
+
1646
+ This class is just a specific type of 'TableOfContents', which by default
1647
+ starts at the parent node of the content where it is used and which by
1648
+ default displays only nodes, not their inner conent (detailed=False).
1649
+
1650
+ """
1651
+ _TOC_ITEM_TYPE = lcg.ContentNode
1652
+
1653
+ def __init__(self, title=None, node=None, depth=None, detailed=False):
1654
+ super(NodeIndex, self).__init__(node, title=title, depth=depth, detailed=detailed)
1655
+
1656
+ def _root_item(self):
1657
+ return self._item or self.parent()
1658
+
1659
+
1660
+ class RootIndex(NodeIndex):
1661
+ """'NodeIndex' starting from the top level node of the whole tree."""
1662
+
1663
+ def _root_item(self):
1664
+ return self.parent().root()
1665
+
1666
+
1667
+ class NoneContent(Content):
1668
+ """Deprecated.
1669
+
1670
+ Intended for places where 'Content' is required but there is nothing to put
1671
+ in. It is, however, actually useless, since 'Content' may be used for the
1672
+ same purpose.
1673
+
1674
+ """
1675
+ pass
1676
+
1677
+
1678
+ class SetVariable(Content):
1679
+ """Pseudo-content serving for setting node global variables.
1680
+
1681
+ This content doesn't produce any output, it just sets a node global
1682
+ variable value. It is used by structured text parser to set global
1683
+ variables at proper places.
1684
+
1685
+ """
1686
+
1687
+ def __init__(self, name, value, **kwargs):
1688
+ """
1689
+ Arguments:
1690
+
1691
+ name -- name of the variable, string
1692
+ value -- value of the variable, 'Content' instance
1693
+
1694
+ """
1695
+ assert isinstance(name, basestring), name
1696
+ assert isinstance(value, Content), value
1697
+ self._name = name
1698
+ self._value = value
1699
+ super(SetVariable, self).__init__(**kwargs)
1700
+
1701
+ def name(self):
1702
+ """Return name of the variable."""
1703
+ return self._name
1704
+
1705
+ def value(self):
1706
+ """Return value of the variable as a 'Content' instance."""
1707
+ return self._value
1708
+
1709
+
1710
+ class Substitution(Content):
1711
+ """Variable to be substituted by the actual value on export."""
1712
+
1713
+ def __init__(self, name, markup=None, **kwargs):
1714
+ """
1715
+ Arguments:
1716
+
1717
+ name -- name of the variable, string, without $, may contain dots for
1718
+ nested variable lookup, eg. 'x.y'.
1719
+ markup -- original source markup (to be substituted if the value
1720
+ doesn't exist), eg. '$x.y' or '${x.y}'. If None, '$'+name is
1721
+ supposed.
1722
+
1723
+ """
1724
+ assert isinstance(name, basestring), name
1725
+ assert markup is None or isinstance(markup, basestring), markup
1726
+ self._name = name
1727
+ self._markup = markup or '$' + name
1728
+ super(Substitution, self).__init__(**kwargs)
1729
+
1730
+ def name(self):
1731
+ """Return name of the variable."""
1732
+ return self._name
1733
+
1734
+ def markup(self):
1735
+ """Return source text representation of the variable."""
1736
+ return self._markup
1737
+
1738
+
1739
+ class Figure(Container):
1740
+ """A container that can have a caption, typicaly for images"""
1741
+
1742
+ LEFT = 'left'
1743
+ RIGHT = 'right'
1744
+
1745
+ def __init__(self, content, caption=None, align=None, **kwargs):
1746
+ """Arguments:
1747
+
1748
+ caption - caption text as lcg.Content or a series of lcg.Content objects
1749
+
1750
+ align - alignment and text wrapping around this container,
1751
+ possible values are 'left', 'right' or None
1752
+ """
1753
+ super(Figure, self).__init__(content, **kwargs)
1754
+ self._caption = caption
1755
+ self._align = align
1756
+
1757
+ def caption(self):
1758
+ return self._caption
1759
+
1760
+ def align(self):
1761
+ return self._align
1762
+
1763
+
1764
+ class MathML(Content):
1765
+ """Representation of MathML content for inclusion in LCG documents.
1766
+
1767
+ It works by making an instance containing a unicode representation of the
1768
+ MathML object. You can access the MathML content either by using
1769
+ 'content()' method which returns the unicode or by using 'dom_content()'
1770
+ which returns a parsed DOM structure to work with.
1771
+
1772
+ It is expected that the MathML content is a presentation form of MathML 3.
1773
+
1774
+ """
1775
+ class EntityHandler(object):
1776
+ """Entity dictionary to be used in 'tree_content()' method.
1777
+
1778
+ This class provides just trivial implementation which always returns
1779
+ the entity name itself. Subclasses (or different) classes may provide
1780
+ other mechanisms, e.g. mapping the entities to the corresponding
1781
+ Unicode characters.
1782
+
1783
+ """
1784
+
1785
+ def __getitem__(self, key):
1786
+ return key
1787
+
1788
+ def get(self, key, default=None):
1789
+ try:
1790
+ return self[key]
1791
+ except KeyError:
1792
+ return default
1793
+
1794
+ def __init__(self, content):
1795
+ """
1796
+ Arguments:
1797
+
1798
+ content -- the MathML content represented as a MathML XML unicode
1799
+
1800
+ """
1801
+ super(MathML, self).__init__()
1802
+ assert isinstance(content, basestring), content
1803
+ self._content = content
1804
+
1805
+ def content(self):
1806
+ """Return the XML unicode content given in the constructor."""
1807
+ return self._content
1808
+
1809
+ def _str_content(self):
1810
+ content = self._content
1811
+ # Unfortunately even the etree parser doesn't work very well with
1812
+ # "custom" entities -- it discards them in attributes. So we have to
1813
+ # expand the entities manualy.
1814
+ regexp = re.compile('&([a-zA-Z]+);')
1815
+ i = 0
1816
+ while True:
1817
+ match = regexp.search(content[i:], re.M)
1818
+ if not match:
1819
+ break
1820
+ entity = match.group(1)
1821
+ start, end = match.span()
1822
+ if entity in ('amp', 'lt', 'gt', 'quot', 'apos', 'bsol', 'newline',):
1823
+ i += end
1824
+ else:
1825
+ from lcg.export.mathml import entities
1826
+ expansion = entities.get(entity)
1827
+ if expansion is None:
1828
+ i += end
1829
+ else:
1830
+ content = content[:i + start] + expansion + content[i + end:]
1831
+ i += start + len(expansion)
1832
+ content = content.encode('utf-8')
1833
+ return content
1834
+
1835
+ def _dom_content(self, element):
1836
+ from xml.etree import ElementTree
1837
+ top_elements = element.getElementsByTagName('math')
1838
+ assert len(top_elements) == 1
1839
+ top = top_elements[0]
1840
+ tree = ElementTree.Element(top.tagName)
1841
+
1842
+ def export(parent_tree, node):
1843
+ node_type = node.nodeType
1844
+ if node_type == node.ELEMENT_NODE:
1845
+ tree = ElementTree.SubElement(parent_tree, node.tagName)
1846
+ attributes = node.attributes
1847
+ for i in range(attributes.length):
1848
+ a = attributes.item(i)
1849
+ tree.set(a.name, a.value)
1850
+ for n in node.childNodes:
1851
+ export(tree, n)
1852
+ elif node_type == node.TEXT_NODE or node_type == node.ENTITY_NODE:
1853
+ value = node.nodeValue.strip()
1854
+ if value:
1855
+ assert not parent_tree.text, node
1856
+ parent_tree.text = node.nodeValue
1857
+ elif node_type == node.COMMENT_NODE:
1858
+ pass
1859
+ else:
1860
+ raise Exception('Unhandled node type', node, node.nodeType)
1861
+ for n in top.childNodes:
1862
+ export(tree, n)
1863
+ return tree
1864
+
1865
+ def _dom_tree_content(self):
1866
+ # This can't handle entity references (e.g. &PlusMinus;).
1867
+ from xml.dom.minidom import parseString
1868
+ return self._dom_content(parseString(self._str_content()))
1869
+
1870
+ def _tree_content(self, entity_dictionary):
1871
+ from xml.etree import ElementTree
1872
+ import io
1873
+ parser = ElementTree.XMLParser()
1874
+ if sys.version_info[0] == 2:
1875
+ # TODO: This doesn't work in Python 3. The only way of getting it to work
1876
+ # seems to be defining all entities in a DOCTYPE as suggested in:
1877
+ # https://stackoverflow.com/questions/35591478/ +
1878
+ # how-to-parse-html-with-entities-such-as-nbsp-using-builtin-library-elementtree
1879
+ # But is this really needed? Valid MathML entities are already defined.
1880
+ # Do we need to allow invalid entities for some reasons?
1881
+ parser.parser.UseForeignDTD(True)
1882
+ if entity_dictionary is None:
1883
+ entity_dictionary = self.EntityHandler()
1884
+ parser.entity = entity_dictionary
1885
+ etree = ElementTree.ElementTree()
1886
+ content = self._str_content()
1887
+ try:
1888
+ tree = etree.parse(io.BytesIO(content), parser=parser)
1889
+ except ElementTree.ParseError as e:
1890
+ raise lcg.ParseError("Error when parsing MathML element", e, content)
1891
+ regexp = re.compile('{.*}')
1892
+ for e in tree.iter():
1893
+ match = regexp.match(e.tag)
1894
+ if match:
1895
+ e.tag = e.tag[match.end():]
1896
+ return tree
1897
+
1898
+ def _transform_content(self, math):
1899
+ from xml.etree import ElementTree
1900
+ math = copy.copy(math)
1901
+ for node in math.getiterator():
1902
+ children = list(node)
1903
+ for i in range(len(children)):
1904
+ c = children[i]
1905
+ if c.tag == 'semantics':
1906
+ c_children = list(c)
1907
+ if c_children:
1908
+ node.insert(i, c_children[0])
1909
+ node.remove(c)
1910
+ else:
1911
+ # Not valid, but let's handle it some way
1912
+ c.clear()
1913
+ c.tag = 'mrow'
1914
+ for node in math.getiterator('mfenced'):
1915
+ opening = node.attrib.get('open', '(')
1916
+ closing = node.attrib.get('close', ')')
1917
+ separators = node.attrib.get('separators', ',').split()
1918
+ children = list(node)
1919
+ node.clear()
1920
+ node.tag = 'mrow'
1921
+ if opening:
1922
+ ElementTree.SubElement(node, 'mo', dict(fence='true')).text = opening
1923
+ i = 0
1924
+ for c in children:
1925
+ if separators and i > 0:
1926
+ s = separators[-1] if i > len(separators) else separators[i - 1]
1927
+ ElementTree.SubElement(node, 'mo', dict(separator='true')).text = s
1928
+ node.append(c)
1929
+ i += 1
1930
+ if closing:
1931
+ ElementTree.SubElement(node, 'mo', dict(fence='true')).text = closing
1932
+ return math
1933
+
1934
+ def tree_content(self, entity_dictionary=None, transform=False, top_only=False):
1935
+ """Return a parsed 'xml.etree.Element' instance of the math content.
1936
+
1937
+ Arguments:
1938
+
1939
+ entity_dictionary -- an object providing common dictionary access
1940
+ methods, 'get()' and '__getitem__()', mapping entity names to
1941
+ values. There are no requirements on the returned values, they are
1942
+ completely implementation dependent. Typical values may be
1943
+ unchanged entity names or unicodes corresponding to the given
1944
+ entities. If not given, 'MathML.EntityHandler' instance is used.
1945
+ transform -- iff true then expand elements which can be expressed
1946
+ using other elements (e.g. mfenced)
1947
+ top_only -- if true then check that the top element is 'math'
1948
+ element, otherwise look for 'math' element in the tree content
1949
+
1950
+ """
1951
+ tree = self._tree_content(entity_dictionary)
1952
+ if top_only:
1953
+ if tree.tag != 'math':
1954
+ raise Exception("Invalid MathML top element", tree)
1955
+ else:
1956
+ try:
1957
+ tree = tree.getiterator('math').pop()
1958
+ except IndexError:
1959
+ raise Exception("No math element found", tree)
1960
+ if transform:
1961
+ tree = self._transform_content(tree)
1962
+ return tree
1963
+
1964
+
1965
+ class InlineSVG(Content):
1966
+ """Generic SVG content for inclusion in LCG documents.
1967
+
1968
+ SVG may be included in HTML and PDF backends. Requires svglib when PDF
1969
+ output is used.
1970
+
1971
+ """
1972
+ def __init__(self, content, *args, **kwargs):
1973
+ """Initialize the instance.
1974
+
1975
+ Arguments:
1976
+
1977
+ content -- the actual SVG content of this element as a string, bytes
1978
+ or a function of one argument (exporter context) returning the SVG
1979
+ as string or bytes.
1980
+ *args -- additional positional arguments passed to the export
1981
+ function (given in 'content'). No arguments are allowed when
1982
+ 'content' is string or bytes.
1983
+ **kwargs -- keyword arguments for parent class constructor.
1984
+
1985
+ """
1986
+ assert isinstance(content, (str, bytes)) or callable(content), content
1987
+ self._content = content
1988
+ self._export_args = args
1989
+ super(InlineSVG, self).__init__(**kwargs)
1990
+
1991
+ def svg(self, context):
1992
+ """Return the SVG as bytes."""
1993
+ content = self._content
1994
+ if callable(content):
1995
+ content = content(context, *self._export_args)
1996
+ if not isinstance(content, bytes):
1997
+ content = content.encode('utf-8')
1998
+ return content
1999
+
2000
+ # Convenience functions for simple content construction.
2001
+
2002
+ def coerce(content, formatted=False):
2003
+ """Coerce the argument into an LCG content element.
2004
+
2005
+ Arguments:
2006
+
2007
+ content -- can be a sequence, string or a Content instance. A sequence
2008
+ is turned to a 'Container' of the items. Moreover each item is coerced
2009
+ recursively and 'None' items are omitted. A string is parsed for
2010
+ inline markup or simply turned into 'TextContent' instance depending on
2011
+ the 'formatted' argument. A 'Content' instance is returned as is. Any
2012
+ other argument raises AssertionError.
2013
+
2014
+ formatted -- a boolean flag indicating how strings should treated. If
2015
+ False (the default) strings are simply turned into 'TextContent'
2016
+ instances. If True, strings are parsed for inline markup using
2017
+ 'lcg.Parser.parse_inline_markup()'. Applies recursively if a sequence
2018
+ is passed as the 'content' argument.
2019
+
2020
+ """
2021
+ assert isinstance(formatted, bool)
2022
+ if isinstance(content, (list, tuple)):
2023
+ container = Container
2024
+ items = []
2025
+ for item in content:
2026
+ if item is not None:
2027
+ if not isinstance(item, Content):
2028
+ item = coerce(item, formatted=formatted)
2029
+ items.append(item)
2030
+ return container(items)
2031
+ elif isinstance(content, basestring):
2032
+ if formatted:
2033
+ from lcg import Parser
2034
+ return Parser().parse_inline_markup(content)
2035
+ else:
2036
+ return TextContent(content)
2037
+ else:
2038
+ assert isinstance(content, Content), ('Invalid content', content,)
2039
+ return content
2040
+
2041
+
2042
+ def join(items, separator=' '):
2043
+ """Coerce all items and put the coerced separator in between them."""
2044
+ sep = coerce(separator)
2045
+ result = []
2046
+ for item in items:
2047
+ if result:
2048
+ result.append(sep)
2049
+ result.append(coerce(item))
2050
+ return coerce(result)
2051
+
2052
+
2053
+ def link(target, label=None, type=None, descr=None):
2054
+ """Return a 'Link' instance.
2055
+
2056
+ Arguments:
2057
+
2058
+ target -- link target. It can be a direct URI as a string or unicode
2059
+ instance or any referable content element, such as 'Section',
2060
+ 'lcg.ContentNode', 'lcg.Resource' or 'Link.ExternalTarget'.
2061
+ label -- link label is mandatory when the target is a direct URI (string
2062
+ or unicode) and optional for LCG content element targets. In this case
2063
+ it just overrides the default title of the refered object.
2064
+ type -- mime type specification of the refered object.
2065
+ descr -- additional description of the link target. Only applicable when
2066
+ the link target is a direct URI.
2067
+
2068
+ """
2069
+ if isinstance(target, basestring):
2070
+ assert label is not None
2071
+ target = Link.ExternalTarget(target, None, descr=descr)
2072
+ else:
2073
+ assert isinstance(target, (lcg.ContentNode, Section, Link.ExternalTarget, lcg.Resource)), \
2074
+ target
2075
+ assert descr is None
2076
+ return Link(target, label=label, type=type)
2077
+
2078
+
2079
+ def dl(items, formatted=False):
2080
+ """Create a 'DefinitionList' from a sequence of (TERM, DESCRIPTION) pairs.
2081
+
2082
+ Each term and description in the sequence is automatically coerced into a content instance, but
2083
+ only the description is treated as formatted.
2084
+
2085
+ """
2086
+ return DefinitionList([(coerce(term), coerce(descr, formatted=formatted))
2087
+ for term, descr in items])
2088
+
2089
+
2090
+ def ul(items, formatted=False):
2091
+ """Create an 'ItemizedList' by coercing given sequence of items."""
2092
+ return ItemizedList([coerce(item, formatted=formatted) for item in items])
2093
+
2094
+
2095
+ def ol(items, formatted=False, alpha=False):
2096
+ """Create an 'ItemizedList' by coercing given sequence of items."""
2097
+ return ItemizedList([coerce(item, formatted=formatted) for item in items],
2098
+ order=(alpha and ItemizedList.LOWER_ALPHA or ItemizedList.NUMERIC))
2099
+
2100
+
2101
+ def fieldset(pairs, formatted=False):
2102
+ """Create a 'FieldSet' out of given sequence of (LABEL, VALUE) pairs.
2103
+
2104
+ Both label and value are coerced, but only value is treated as formatted.
2105
+
2106
+ """
2107
+ fields = [(coerce(label), coerce(value, formatted=formatted)) for label, value in pairs]
2108
+ return FieldSet(fields)
2109
+
2110
+
2111
+ def sec(title, content, heading=None, name='default-section', **kwargs):
2112
+ """Create a 'Section' by coercing all content."""
2113
+ formatted = kwargs.pop('formatted', False)
2114
+ return Section(title=title, heading=coerce(heading, formatted=formatted) if heading else None,
2115
+ content=[coerce(item, formatted=formatted) for item in content],
2116
+ name=name, **kwargs)
2117
+
2118
+
2119
+ def _container(container, items, formatted=False, **kwargs):
2120
+ return container([coerce(item, formatted=formatted) for item in items], **kwargs)
2121
+
2122
+
2123
+ def p(*items, **kwargs):
2124
+ """Create a 'Paragraph' by coercing all arguments."""
2125
+ return _container(Paragraph, items, **kwargs)
2126
+
2127
+
2128
+ def strong(*items, **kwargs):
2129
+ """Create a 'Strong' instance by coercing all arguments."""
2130
+ return _container(Strong, items, **kwargs)
2131
+
2132
+
2133
+ def em(*items, **kwargs):
2134
+ """Create an 'Emphasized' instance by coercing all arguments."""
2135
+ return _container(Emphasized, items, **kwargs)
2136
+
2137
+
2138
+ def u(*items, **kwargs):
2139
+ """Create an 'Underlined' instance by coercing all arguments."""
2140
+ return _container(Underlined, items, **kwargs)
2141
+
2142
+
2143
+ def code(*items, **kwargs):
2144
+ """Create an 'Code' instance by coercing all arguments."""
2145
+ return _container(Code, items, **kwargs)
2146
+
2147
+
2148
+ def cite(*items, **kwargs):
2149
+ """Create an 'Citation' instance by coercing all arguments."""
2150
+ return _container(Citation, items, **kwargs)
2151
+
2152
+
2153
+ def container(*items, **kwargs):
2154
+ return _container(Container, items, **kwargs)
2155
+
2156
+
2157
+ def br():
2158
+ return NewLine()
2159
+
2160
+
2161
+ def hr():
2162
+ return HorizontalSeparator()
2163
+
2164
+
2165
+ def pre(text, **kwargs):
2166
+ """Create an 'PreformattedText' instance by coercing all arguments."""
2167
+ return PreformattedText(text, **kwargs)
2168
+
2169
+
2170
+ def abbr(text, descr, **kwargs):
2171
+ """Create an 'Abbreviation' instance."""
2172
+ return Abbreviation(text, descr, **kwargs)