@trebco/treb 37.0.0 → 37.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. package/build/package.json +119 -0
  2. package/build/treb-base-types/src/api_types.d.ts +11 -0
  3. package/build/treb-base-types/src/api_types.js +22 -0
  4. package/build/treb-base-types/src/api_types.js.map +1 -0
  5. package/build/treb-base-types/src/area-utils.d.ts +9 -0
  6. package/build/treb-base-types/src/area-utils.js +50 -0
  7. package/build/treb-base-types/src/area-utils.js.map +1 -0
  8. package/build/treb-base-types/src/area.d.ts +182 -0
  9. package/build/treb-base-types/src/area.js +715 -0
  10. package/build/treb-base-types/src/area.js.map +1 -0
  11. package/build/treb-base-types/src/basic_types.d.ts +20 -0
  12. package/build/treb-base-types/src/basic_types.js +22 -0
  13. package/build/treb-base-types/src/basic_types.js.map +1 -0
  14. package/build/treb-base-types/src/cell.d.ts +167 -0
  15. package/build/treb-base-types/src/cell.js +432 -0
  16. package/build/treb-base-types/src/cell.js.map +1 -0
  17. package/build/treb-base-types/src/cells.d.ts +251 -0
  18. package/build/treb-base-types/src/cells.js +1136 -0
  19. package/build/treb-base-types/src/cells.js.map +1 -0
  20. package/build/treb-base-types/src/color.d.ts +35 -0
  21. package/build/treb-base-types/src/color.js +162 -0
  22. package/build/treb-base-types/src/color.js.map +1 -0
  23. package/build/treb-base-types/src/dom-utilities.d.ts +70 -0
  24. package/build/treb-base-types/src/dom-utilities.js +144 -0
  25. package/build/treb-base-types/src/dom-utilities.js.map +1 -0
  26. package/build/treb-base-types/src/evaluate-options.d.ts +35 -0
  27. package/build/treb-base-types/src/evaluate-options.js +22 -0
  28. package/build/treb-base-types/src/evaluate-options.js.map +1 -0
  29. package/build/treb-base-types/src/font-stack.d.ts +37 -0
  30. package/build/treb-base-types/src/font-stack.js +93 -0
  31. package/build/treb-base-types/src/font-stack.js.map +1 -0
  32. package/build/treb-base-types/src/gradient.d.ts +18 -0
  33. package/build/treb-base-types/src/gradient.js +86 -0
  34. package/build/treb-base-types/src/gradient.js.map +1 -0
  35. package/build/treb-base-types/src/import.d.ts +48 -0
  36. package/build/treb-base-types/src/import.js +22 -0
  37. package/build/treb-base-types/src/import.js.map +1 -0
  38. package/build/treb-base-types/src/index-standalone.d.ts +6 -0
  39. package/build/treb-base-types/src/index-standalone.js +27 -0
  40. package/build/treb-base-types/src/index-standalone.js.map +1 -0
  41. package/build/treb-base-types/src/index.d.ts +22 -0
  42. package/build/treb-base-types/src/index.js +45 -0
  43. package/build/treb-base-types/src/index.js.map +1 -0
  44. package/build/treb-base-types/src/layout.d.ts +22 -0
  45. package/build/treb-base-types/src/layout.js +22 -0
  46. package/build/treb-base-types/src/layout.js.map +1 -0
  47. package/build/treb-base-types/src/localization.d.ts +37 -0
  48. package/build/treb-base-types/src/localization.js +157 -0
  49. package/build/treb-base-types/src/localization.js.map +1 -0
  50. package/build/treb-base-types/src/rectangle.d.ts +51 -0
  51. package/build/treb-base-types/src/rectangle.js +123 -0
  52. package/build/treb-base-types/src/rectangle.js.map +1 -0
  53. package/build/treb-base-types/src/render_text.d.ts +34 -0
  54. package/build/treb-base-types/src/render_text.js +22 -0
  55. package/build/treb-base-types/src/render_text.js.map +1 -0
  56. package/build/treb-base-types/src/style.d.ts +214 -0
  57. package/build/treb-base-types/src/style.js +373 -0
  58. package/build/treb-base-types/src/style.js.map +1 -0
  59. package/build/treb-base-types/src/table.d.ts +58 -0
  60. package/build/treb-base-types/src/table.js +27 -0
  61. package/build/treb-base-types/src/table.js.map +1 -0
  62. package/build/treb-base-types/src/text_part.d.ts +26 -0
  63. package/build/treb-base-types/src/text_part.js +47 -0
  64. package/build/treb-base-types/src/text_part.js.map +1 -0
  65. package/build/treb-base-types/src/theme.d.ts +120 -0
  66. package/build/treb-base-types/src/theme.js +460 -0
  67. package/build/treb-base-types/src/theme.js.map +1 -0
  68. package/build/treb-base-types/src/union.d.ts +73 -0
  69. package/build/treb-base-types/src/union.js +61 -0
  70. package/build/treb-base-types/src/union.js.map +1 -0
  71. package/build/treb-base-types/src/value-type.d.ts +86 -0
  72. package/build/treb-base-types/src/value-type.js +168 -0
  73. package/build/treb-base-types/src/value-type.js.map +1 -0
  74. package/build/treb-base-types/src/worker-proxy.d.ts +95 -0
  75. package/build/treb-base-types/src/worker-proxy.js +221 -0
  76. package/build/treb-base-types/src/worker-proxy.js.map +1 -0
  77. package/build/treb-calculator/src/calculator.d.ts +249 -0
  78. package/build/treb-calculator/src/calculator.js +2755 -0
  79. package/build/treb-calculator/src/calculator.js.map +1 -0
  80. package/build/treb-calculator/src/complex-math.d.ts +75 -0
  81. package/build/treb-calculator/src/complex-math.js +559 -0
  82. package/build/treb-calculator/src/complex-math.js.map +1 -0
  83. package/build/treb-calculator/src/dag/array-vertex.d.ts +71 -0
  84. package/build/treb-calculator/src/dag/array-vertex.js +156 -0
  85. package/build/treb-calculator/src/dag/array-vertex.js.map +1 -0
  86. package/build/treb-calculator/src/dag/calculation_leaf_vertex.d.ts +48 -0
  87. package/build/treb-calculator/src/dag/calculation_leaf_vertex.js +84 -0
  88. package/build/treb-calculator/src/dag/calculation_leaf_vertex.js.map +1 -0
  89. package/build/treb-calculator/src/dag/graph.d.ts +134 -0
  90. package/build/treb-calculator/src/dag/graph.js +842 -0
  91. package/build/treb-calculator/src/dag/graph.js.map +1 -0
  92. package/build/treb-calculator/src/dag/spreadsheet_vertex.d.ts +58 -0
  93. package/build/treb-calculator/src/dag/spreadsheet_vertex.js +232 -0
  94. package/build/treb-calculator/src/dag/spreadsheet_vertex.js.map +1 -0
  95. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.d.ts +20 -0
  96. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js +25 -0
  97. package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js.map +1 -0
  98. package/build/treb-calculator/src/dag/state_leaf_vertex.d.ts +43 -0
  99. package/build/treb-calculator/src/dag/state_leaf_vertex.js +81 -0
  100. package/build/treb-calculator/src/dag/state_leaf_vertex.js.map +1 -0
  101. package/build/treb-calculator/src/dag/vertex.d.ts +71 -0
  102. package/build/treb-calculator/src/dag/vertex.js +274 -0
  103. package/build/treb-calculator/src/dag/vertex.js.map +1 -0
  104. package/build/treb-calculator/src/descriptors.d.ts +189 -0
  105. package/build/treb-calculator/src/descriptors.js +22 -0
  106. package/build/treb-calculator/src/descriptors.js.map +1 -0
  107. package/build/treb-calculator/src/expression-calculator.d.ts +127 -0
  108. package/build/treb-calculator/src/expression-calculator.js +1033 -0
  109. package/build/treb-calculator/src/expression-calculator.js.map +1 -0
  110. package/build/treb-calculator/src/function-error.d.ts +35 -0
  111. package/build/treb-calculator/src/function-error.js +85 -0
  112. package/build/treb-calculator/src/function-error.js.map +1 -0
  113. package/build/treb-calculator/src/function-library.d.ts +22 -0
  114. package/build/treb-calculator/src/function-library.js +96 -0
  115. package/build/treb-calculator/src/function-library.js.map +1 -0
  116. package/build/treb-calculator/src/functions/base-functions.d.ts +7 -0
  117. package/build/treb-calculator/src/functions/base-functions.js +2611 -0
  118. package/build/treb-calculator/src/functions/base-functions.js.map +1 -0
  119. package/build/treb-calculator/src/functions/beta.d.ts +17 -0
  120. package/build/treb-calculator/src/functions/beta.js +201 -0
  121. package/build/treb-calculator/src/functions/beta.js.map +1 -0
  122. package/build/treb-calculator/src/functions/checkbox.d.ts +3 -0
  123. package/build/treb-calculator/src/functions/checkbox.js +128 -0
  124. package/build/treb-calculator/src/functions/checkbox.js.map +1 -0
  125. package/build/treb-calculator/src/functions/complex-functions.d.ts +2 -0
  126. package/build/treb-calculator/src/functions/complex-functions.js +217 -0
  127. package/build/treb-calculator/src/functions/complex-functions.js.map +1 -0
  128. package/build/treb-calculator/src/functions/date-utils.d.ts +3 -0
  129. package/build/treb-calculator/src/functions/date-utils.js +59 -0
  130. package/build/treb-calculator/src/functions/date-utils.js.map +1 -0
  131. package/build/treb-calculator/src/functions/finance-functions.d.ts +2 -0
  132. package/build/treb-calculator/src/functions/finance-functions.js +547 -0
  133. package/build/treb-calculator/src/functions/finance-functions.js.map +1 -0
  134. package/build/treb-calculator/src/functions/fp.d.ts +2 -0
  135. package/build/treb-calculator/src/functions/fp.js +463 -0
  136. package/build/treb-calculator/src/functions/fp.js.map +1 -0
  137. package/build/treb-calculator/src/functions/function-utilities.d.ts +2 -0
  138. package/build/treb-calculator/src/functions/function-utilities.js +36 -0
  139. package/build/treb-calculator/src/functions/function-utilities.js.map +1 -0
  140. package/build/treb-calculator/src/functions/gamma.d.ts +20 -0
  141. package/build/treb-calculator/src/functions/gamma.js +142 -0
  142. package/build/treb-calculator/src/functions/gamma.js.map +1 -0
  143. package/build/treb-calculator/src/functions/information-functions.d.ts +2 -0
  144. package/build/treb-calculator/src/functions/information-functions.js +71 -0
  145. package/build/treb-calculator/src/functions/information-functions.js.map +1 -0
  146. package/build/treb-calculator/src/functions/lambda-functions.d.ts +2 -0
  147. package/build/treb-calculator/src/functions/lambda-functions.js +85 -0
  148. package/build/treb-calculator/src/functions/lambda-functions.js.map +1 -0
  149. package/build/treb-calculator/src/functions/matrix-functions.d.ts +2 -0
  150. package/build/treb-calculator/src/functions/matrix-functions.js +144 -0
  151. package/build/treb-calculator/src/functions/matrix-functions.js.map +1 -0
  152. package/build/treb-calculator/src/functions/normal.d.ts +2 -0
  153. package/build/treb-calculator/src/functions/normal.js +32 -0
  154. package/build/treb-calculator/src/functions/normal.js.map +1 -0
  155. package/build/treb-calculator/src/functions/regex-functions.d.ts +2 -0
  156. package/build/treb-calculator/src/functions/regex-functions.js +188 -0
  157. package/build/treb-calculator/src/functions/regex-functions.js.map +1 -0
  158. package/build/treb-calculator/src/functions/sparkline.d.ts +37 -0
  159. package/build/treb-calculator/src/functions/sparkline.js +264 -0
  160. package/build/treb-calculator/src/functions/sparkline.js.map +1 -0
  161. package/build/treb-calculator/src/functions/statistics-functions.d.ts +6 -0
  162. package/build/treb-calculator/src/functions/statistics-functions.js +989 -0
  163. package/build/treb-calculator/src/functions/statistics-functions.js.map +1 -0
  164. package/build/treb-calculator/src/functions/students-t.d.ts +3 -0
  165. package/build/treb-calculator/src/functions/students-t.js +64 -0
  166. package/build/treb-calculator/src/functions/students-t.js.map +1 -0
  167. package/build/treb-calculator/src/functions/text-functions.d.ts +3 -0
  168. package/build/treb-calculator/src/functions/text-functions.js +320 -0
  169. package/build/treb-calculator/src/functions/text-functions.js.map +1 -0
  170. package/build/treb-calculator/src/index.d.ts +2 -0
  171. package/build/treb-calculator/src/index.js +22 -0
  172. package/build/treb-calculator/src/index.js.map +1 -0
  173. package/build/treb-calculator/src/notifier-types.d.ts +26 -0
  174. package/build/treb-calculator/src/notifier-types.js +22 -0
  175. package/build/treb-calculator/src/notifier-types.js.map +1 -0
  176. package/build/treb-calculator/src/primitives.d.ts +15 -0
  177. package/build/treb-calculator/src/primitives.js +398 -0
  178. package/build/treb-calculator/src/primitives.js.map +1 -0
  179. package/build/treb-calculator/src/utilities.d.ts +68 -0
  180. package/build/treb-calculator/src/utilities.js +324 -0
  181. package/build/treb-calculator/src/utilities.js.map +1 -0
  182. package/build/treb-charts/src/chart-functions.d.ts +8 -0
  183. package/build/treb-charts/src/chart-functions.js +209 -0
  184. package/build/treb-charts/src/chart-functions.js.map +1 -0
  185. package/build/treb-charts/src/chart-types.d.ts +233 -0
  186. package/build/treb-charts/src/chart-types.js +57 -0
  187. package/build/treb-charts/src/chart-types.js.map +1 -0
  188. package/build/treb-charts/src/chart-utils.d.ts +106 -0
  189. package/build/treb-charts/src/chart-utils.js +1060 -0
  190. package/build/treb-charts/src/chart-utils.js.map +1 -0
  191. package/build/treb-charts/src/chart.d.ts +23 -0
  192. package/build/treb-charts/src/chart.js +94 -0
  193. package/build/treb-charts/src/chart.js.map +1 -0
  194. package/build/treb-charts/src/default-chart-renderer.d.ts +16 -0
  195. package/build/treb-charts/src/default-chart-renderer.js +533 -0
  196. package/build/treb-charts/src/default-chart-renderer.js.map +1 -0
  197. package/build/treb-charts/src/index.d.ts +5 -0
  198. package/build/treb-charts/src/index.js +24 -0
  199. package/build/treb-charts/src/index.js.map +1 -0
  200. package/build/treb-charts/src/main.d.ts +1 -0
  201. package/build/treb-charts/src/main.js +34 -0
  202. package/build/treb-charts/src/main.js.map +1 -0
  203. package/build/treb-charts/src/quicksort.d.ts +1 -0
  204. package/build/treb-charts/src/quicksort.js +49 -0
  205. package/build/treb-charts/src/quicksort.js.map +1 -0
  206. package/build/treb-charts/src/rectangle.d.ts +18 -0
  207. package/build/treb-charts/src/rectangle.js +41 -0
  208. package/build/treb-charts/src/rectangle.js.map +1 -0
  209. package/build/treb-charts/src/renderer-type.d.ts +24 -0
  210. package/build/treb-charts/src/renderer-type.js +22 -0
  211. package/build/treb-charts/src/renderer-type.js.map +1 -0
  212. package/build/treb-charts/src/renderer.d.ts +127 -0
  213. package/build/treb-charts/src/renderer.js +1518 -0
  214. package/build/treb-charts/src/renderer.js.map +1 -0
  215. package/build/treb-charts/src/util.d.ts +18 -0
  216. package/build/treb-charts/src/util.js +71 -0
  217. package/build/treb-charts/src/util.js.map +1 -0
  218. package/build/treb-data-model/src/annotation.d.ts +167 -0
  219. package/build/treb-data-model/src/annotation.js +120 -0
  220. package/build/treb-data-model/src/annotation.js.map +1 -0
  221. package/build/treb-data-model/src/conditional_format.d.ts +155 -0
  222. package/build/treb-data-model/src/conditional_format.js +62 -0
  223. package/build/treb-data-model/src/conditional_format.js.map +1 -0
  224. package/build/treb-data-model/src/data-validation.d.ts +28 -0
  225. package/build/treb-data-model/src/data-validation.js +22 -0
  226. package/build/treb-data-model/src/data-validation.js.map +1 -0
  227. package/build/treb-data-model/src/data_model.d.ts +173 -0
  228. package/build/treb-data-model/src/data_model.js +637 -0
  229. package/build/treb-data-model/src/data_model.js.map +1 -0
  230. package/build/treb-data-model/src/index.d.ts +13 -0
  231. package/build/treb-data-model/src/index.js +28 -0
  232. package/build/treb-data-model/src/index.js.map +1 -0
  233. package/build/treb-data-model/src/language-model.d.ts +22 -0
  234. package/build/treb-data-model/src/language-model.js +22 -0
  235. package/build/treb-data-model/src/language-model.js.map +1 -0
  236. package/build/treb-data-model/src/named.d.ts +124 -0
  237. package/build/treb-data-model/src/named.js +372 -0
  238. package/build/treb-data-model/src/named.js.map +1 -0
  239. package/build/treb-data-model/src/serialize_options.d.ts +49 -0
  240. package/build/treb-data-model/src/serialize_options.js +22 -0
  241. package/build/treb-data-model/src/serialize_options.js.map +1 -0
  242. package/build/treb-data-model/src/sheet.d.ts +499 -0
  243. package/build/treb-data-model/src/sheet.js +2904 -0
  244. package/build/treb-data-model/src/sheet.js.map +1 -0
  245. package/build/treb-data-model/src/sheet_collection.d.ts +58 -0
  246. package/build/treb-data-model/src/sheet_collection.js +112 -0
  247. package/build/treb-data-model/src/sheet_collection.js.map +1 -0
  248. package/build/treb-data-model/src/sheet_selection.d.ts +42 -0
  249. package/build/treb-data-model/src/sheet_selection.js +39 -0
  250. package/build/treb-data-model/src/sheet_selection.js.map +1 -0
  251. package/build/treb-data-model/src/sheet_types.d.ts +104 -0
  252. package/build/treb-data-model/src/sheet_types.js +22 -0
  253. package/build/treb-data-model/src/sheet_types.js.map +1 -0
  254. package/build/treb-data-model/src/types.d.ts +59 -0
  255. package/build/treb-data-model/src/types.js +22 -0
  256. package/build/treb-data-model/src/types.js.map +1 -0
  257. package/build/treb-embed/src/custom-element/spreadsheet-constructor.d.ts +75 -0
  258. package/build/treb-embed/src/custom-element/spreadsheet-constructor.js +1144 -0
  259. package/build/treb-embed/src/custom-element/spreadsheet-constructor.js.map +1 -0
  260. package/build/treb-embed/src/custom-element/treb-global.d.ts +36 -0
  261. package/build/treb-embed/src/custom-element/treb-global.js +64 -0
  262. package/build/treb-embed/src/custom-element/treb-global.js.map +1 -0
  263. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.d.ts +1 -0
  264. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js +61 -0
  265. package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js.map +1 -0
  266. package/build/treb-embed/src/embedded-spreadsheet.d.ts +1358 -0
  267. package/build/treb-embed/src/embedded-spreadsheet.js +5205 -0
  268. package/build/treb-embed/src/embedded-spreadsheet.js.map +1 -0
  269. package/build/treb-embed/src/index.d.ts +12 -0
  270. package/build/treb-embed/src/index.js +34 -0
  271. package/build/treb-embed/src/index.js.map +1 -0
  272. package/build/treb-embed/src/options.d.ts +266 -0
  273. package/build/treb-embed/src/options.js +56 -0
  274. package/build/treb-embed/src/options.js.map +1 -0
  275. package/build/treb-embed/src/plugin.d.ts +9 -0
  276. package/build/treb-embed/src/plugin.js +22 -0
  277. package/build/treb-embed/src/plugin.js.map +1 -0
  278. package/build/treb-embed/src/progress-dialog.d.ts +49 -0
  279. package/build/treb-embed/src/progress-dialog.js +178 -0
  280. package/build/treb-embed/src/progress-dialog.js.map +1 -0
  281. package/build/treb-embed/src/selection-state.d.ts +15 -0
  282. package/build/treb-embed/src/selection-state.js +22 -0
  283. package/build/treb-embed/src/selection-state.js.map +1 -0
  284. package/build/treb-embed/src/spinner.d.ts +8 -0
  285. package/build/treb-embed/src/spinner.js +40 -0
  286. package/build/treb-embed/src/spinner.js.map +1 -0
  287. package/build/treb-embed/src/toolbar-message.d.ts +72 -0
  288. package/build/treb-embed/src/toolbar-message.js +22 -0
  289. package/build/treb-embed/src/toolbar-message.js.map +1 -0
  290. package/build/treb-embed/src/types.d.ts +185 -0
  291. package/build/treb-embed/src/types.js +45 -0
  292. package/build/treb-embed/src/types.js.map +1 -0
  293. package/build/treb-embed/tsconfig.tsbuildinfo +1 -0
  294. package/build/treb-export/src/address-type.d.ts +34 -0
  295. package/build/treb-export/src/address-type.js +53 -0
  296. package/build/treb-export/src/address-type.js.map +1 -0
  297. package/build/treb-export/src/base-template.d.ts +1 -0
  298. package/build/treb-export/src/base-template.js +22 -0
  299. package/build/treb-export/src/base-template.js.map +1 -0
  300. package/build/treb-export/src/column-width.d.ts +2 -0
  301. package/build/treb-export/src/column-width.js +80 -0
  302. package/build/treb-export/src/column-width.js.map +1 -0
  303. package/build/treb-export/src/drawing/bubble-chart-template.d.ts +514 -0
  304. package/build/treb-export/src/drawing/bubble-chart-template.js +544 -0
  305. package/build/treb-export/src/drawing/bubble-chart-template.js.map +1 -0
  306. package/build/treb-export/src/drawing/chart-template-components2.d.ts +365 -0
  307. package/build/treb-export/src/drawing/chart-template-components2.js +386 -0
  308. package/build/treb-export/src/drawing/chart-template-components2.js.map +1 -0
  309. package/build/treb-export/src/drawing/chart.d.ts +26 -0
  310. package/build/treb-export/src/drawing/chart.js +247 -0
  311. package/build/treb-export/src/drawing/chart.js.map +1 -0
  312. package/build/treb-export/src/drawing/column-chart-template2.d.ts +490 -0
  313. package/build/treb-export/src/drawing/column-chart-template2.js +518 -0
  314. package/build/treb-export/src/drawing/column-chart-template2.js.map +1 -0
  315. package/build/treb-export/src/drawing/donut-chart-template2.d.ts +272 -0
  316. package/build/treb-export/src/drawing/donut-chart-template2.js +293 -0
  317. package/build/treb-export/src/drawing/donut-chart-template2.js.map +1 -0
  318. package/build/treb-export/src/drawing/drawing.d.ts +49 -0
  319. package/build/treb-export/src/drawing/drawing.js +193 -0
  320. package/build/treb-export/src/drawing/drawing.js.map +1 -0
  321. package/build/treb-export/src/drawing/embedded-image.d.ts +12 -0
  322. package/build/treb-export/src/drawing/embedded-image.js +54 -0
  323. package/build/treb-export/src/drawing/embedded-image.js.map +1 -0
  324. package/build/treb-export/src/drawing/scatter-chart-template2.d.ts +520 -0
  325. package/build/treb-export/src/drawing/scatter-chart-template2.js +551 -0
  326. package/build/treb-export/src/drawing/scatter-chart-template2.js.map +1 -0
  327. package/build/treb-export/src/export.d.ts +72 -0
  328. package/build/treb-export/src/export.js +2039 -0
  329. package/build/treb-export/src/export.js.map +1 -0
  330. package/build/treb-export/src/import-export-messages.d.ts +31 -0
  331. package/build/treb-export/src/import-export-messages.js +22 -0
  332. package/build/treb-export/src/import-export-messages.js.map +1 -0
  333. package/build/treb-export/src/import.d.ts +33 -0
  334. package/build/treb-export/src/import.js +1258 -0
  335. package/build/treb-export/src/import.js.map +1 -0
  336. package/build/treb-export/src/index.worker.d.ts +1 -0
  337. package/build/treb-export/src/index.worker.js +93 -0
  338. package/build/treb-export/src/index.worker.js.map +1 -0
  339. package/build/treb-export/src/metadata.d.ts +51 -0
  340. package/build/treb-export/src/metadata.js +153 -0
  341. package/build/treb-export/src/metadata.js.map +1 -0
  342. package/build/treb-export/src/ooxml.d.ts +7 -0
  343. package/build/treb-export/src/ooxml.js +41 -0
  344. package/build/treb-export/src/ooxml.js.map +1 -0
  345. package/build/treb-export/src/relationship.d.ts +8 -0
  346. package/build/treb-export/src/relationship.js +27 -0
  347. package/build/treb-export/src/relationship.js.map +1 -0
  348. package/build/treb-export/src/shared-strings.d.ts +11 -0
  349. package/build/treb-export/src/shared-strings.js +105 -0
  350. package/build/treb-export/src/shared-strings.js.map +1 -0
  351. package/build/treb-export/src/template-2.d.ts +1 -0
  352. package/build/treb-export/src/template-2.js +22 -0
  353. package/build/treb-export/src/template-2.js.map +1 -0
  354. package/build/treb-export/src/unescape_xml.d.ts +1 -0
  355. package/build/treb-export/src/unescape_xml.js +61 -0
  356. package/build/treb-export/src/unescape_xml.js.map +1 -0
  357. package/build/treb-export/src/workbook-sheet.d.ts +75 -0
  358. package/build/treb-export/src/workbook-sheet.js +128 -0
  359. package/build/treb-export/src/workbook-sheet.js.map +1 -0
  360. package/build/treb-export/src/workbook-style.d.ts +110 -0
  361. package/build/treb-export/src/workbook-style.js +1134 -0
  362. package/build/treb-export/src/workbook-style.js.map +1 -0
  363. package/build/treb-export/src/workbook-theme.d.ts +13 -0
  364. package/build/treb-export/src/workbook-theme.js +85 -0
  365. package/build/treb-export/src/workbook-theme.js.map +1 -0
  366. package/build/treb-export/src/workbook.d.ts +123 -0
  367. package/build/treb-export/src/workbook.js +644 -0
  368. package/build/treb-export/src/workbook.js.map +1 -0
  369. package/build/treb-export/src/xml-test.d.ts +9 -0
  370. package/build/treb-export/src/xml-test.js +52 -0
  371. package/build/treb-export/src/xml-test.js.map +1 -0
  372. package/build/treb-export/src/xml-utils.d.ts +76 -0
  373. package/build/treb-export/src/xml-utils.js +223 -0
  374. package/build/treb-export/src/xml-utils.js.map +1 -0
  375. package/build/treb-export/src/zip-wrapper.d.ts +22 -0
  376. package/build/treb-export/src/zip-wrapper.js +93 -0
  377. package/build/treb-export/src/zip-wrapper.js.map +1 -0
  378. package/build/treb-format/src/format.d.ts +130 -0
  379. package/build/treb-format/src/format.js +805 -0
  380. package/build/treb-format/src/format.js.map +1 -0
  381. package/build/treb-format/src/format_cache.d.ts +55 -0
  382. package/build/treb-format/src/format_cache.js +166 -0
  383. package/build/treb-format/src/format_cache.js.map +1 -0
  384. package/build/treb-format/src/format_parser.d.ts +70 -0
  385. package/build/treb-format/src/format_parser.js +618 -0
  386. package/build/treb-format/src/format_parser.js.map +1 -0
  387. package/build/treb-format/src/index.d.ts +4 -0
  388. package/build/treb-format/src/index.js +25 -0
  389. package/build/treb-format/src/index.js.map +1 -0
  390. package/build/treb-format/src/number_format_section.d.ts +58 -0
  391. package/build/treb-format/src/number_format_section.js +78 -0
  392. package/build/treb-format/src/number_format_section.js.map +1 -0
  393. package/build/treb-format/src/value_parser.d.ts +48 -0
  394. package/build/treb-format/src/value_parser.js +244 -0
  395. package/build/treb-format/src/value_parser.js.map +1 -0
  396. package/build/treb-grid/src/editors/autocomplete.d.ts +39 -0
  397. package/build/treb-grid/src/editors/autocomplete.js +316 -0
  398. package/build/treb-grid/src/editors/autocomplete.js.map +1 -0
  399. package/build/treb-grid/src/editors/autocomplete_matcher.d.ts +74 -0
  400. package/build/treb-grid/src/editors/autocomplete_matcher.js +212 -0
  401. package/build/treb-grid/src/editors/autocomplete_matcher.js.map +1 -0
  402. package/build/treb-grid/src/editors/editor.d.ts +214 -0
  403. package/build/treb-grid/src/editors/editor.js +879 -0
  404. package/build/treb-grid/src/editors/editor.js.map +1 -0
  405. package/build/treb-grid/src/editors/external_editor.d.ts +11 -0
  406. package/build/treb-grid/src/editors/external_editor.js +118 -0
  407. package/build/treb-grid/src/editors/external_editor.js.map +1 -0
  408. package/build/treb-grid/src/editors/formula_bar.d.ts +85 -0
  409. package/build/treb-grid/src/editors/formula_bar.js +444 -0
  410. package/build/treb-grid/src/editors/formula_bar.js.map +1 -0
  411. package/build/treb-grid/src/editors/overlay_editor.d.ts +85 -0
  412. package/build/treb-grid/src/editors/overlay_editor.js +353 -0
  413. package/build/treb-grid/src/editors/overlay_editor.js.map +1 -0
  414. package/build/treb-grid/src/index.d.ts +12 -0
  415. package/build/treb-grid/src/index.js +28 -0
  416. package/build/treb-grid/src/index.js.map +1 -0
  417. package/build/treb-grid/src/layout/base_layout.d.ts +346 -0
  418. package/build/treb-grid/src/layout/base_layout.js +2050 -0
  419. package/build/treb-grid/src/layout/base_layout.js.map +1 -0
  420. package/build/treb-grid/src/layout/grid_layout.d.ts +19 -0
  421. package/build/treb-grid/src/layout/grid_layout.js +235 -0
  422. package/build/treb-grid/src/layout/grid_layout.js.map +1 -0
  423. package/build/treb-grid/src/layout/mock-layout.d.ts +10 -0
  424. package/build/treb-grid/src/layout/mock-layout.js +37 -0
  425. package/build/treb-grid/src/layout/mock-layout.js.map +1 -0
  426. package/build/treb-grid/src/render/selection-renderer.d.ts +97 -0
  427. package/build/treb-grid/src/render/selection-renderer.js +315 -0
  428. package/build/treb-grid/src/render/selection-renderer.js.map +1 -0
  429. package/build/treb-grid/src/render/svg_header_overlay.d.ts +20 -0
  430. package/build/treb-grid/src/render/svg_header_overlay.js +76 -0
  431. package/build/treb-grid/src/render/svg_header_overlay.js.map +1 -0
  432. package/build/treb-grid/src/render/svg_selection_block.d.ts +27 -0
  433. package/build/treb-grid/src/render/svg_selection_block.js +106 -0
  434. package/build/treb-grid/src/render/svg_selection_block.js.map +1 -0
  435. package/build/treb-grid/src/render/tile_renderer.d.ts +121 -0
  436. package/build/treb-grid/src/render/tile_renderer.js +1609 -0
  437. package/build/treb-grid/src/render/tile_renderer.js.map +1 -0
  438. package/build/treb-grid/src/types/border_constants.d.ts +9 -0
  439. package/build/treb-grid/src/types/border_constants.js +34 -0
  440. package/build/treb-grid/src/types/border_constants.js.map +1 -0
  441. package/build/treb-grid/src/types/clipboard_data.d.ts +11 -0
  442. package/build/treb-grid/src/types/clipboard_data.js +22 -0
  443. package/build/treb-grid/src/types/clipboard_data.js.map +1 -0
  444. package/build/treb-grid/src/types/clipboard_data2.d.ts +46 -0
  445. package/build/treb-grid/src/types/clipboard_data2.js +22 -0
  446. package/build/treb-grid/src/types/clipboard_data2.js.map +1 -0
  447. package/build/treb-grid/src/types/drag_mask.d.ts +10 -0
  448. package/build/treb-grid/src/types/drag_mask.js +78 -0
  449. package/build/treb-grid/src/types/drag_mask.js.map +1 -0
  450. package/build/treb-grid/src/types/external_editor_config.d.ts +33 -0
  451. package/build/treb-grid/src/types/external_editor_config.js +22 -0
  452. package/build/treb-grid/src/types/external_editor_config.js.map +1 -0
  453. package/build/treb-grid/src/types/grid.d.ts +806 -0
  454. package/build/treb-grid/src/types/grid.js +6410 -0
  455. package/build/treb-grid/src/types/grid.js.map +1 -0
  456. package/build/treb-grid/src/types/grid_base.d.ts +442 -0
  457. package/build/treb-grid/src/types/grid_base.js +3523 -0
  458. package/build/treb-grid/src/types/grid_base.js.map +1 -0
  459. package/build/treb-grid/src/types/grid_command.d.ts +408 -0
  460. package/build/treb-grid/src/types/grid_command.js +75 -0
  461. package/build/treb-grid/src/types/grid_command.js.map +1 -0
  462. package/build/treb-grid/src/types/grid_events.d.ts +93 -0
  463. package/build/treb-grid/src/types/grid_events.js +36 -0
  464. package/build/treb-grid/src/types/grid_events.js.map +1 -0
  465. package/build/treb-grid/src/types/grid_options.d.ts +50 -0
  466. package/build/treb-grid/src/types/grid_options.js +34 -0
  467. package/build/treb-grid/src/types/grid_options.js.map +1 -0
  468. package/build/treb-grid/src/types/scale-control.d.ts +21 -0
  469. package/build/treb-grid/src/types/scale-control.js +148 -0
  470. package/build/treb-grid/src/types/scale-control.js.map +1 -0
  471. package/build/treb-grid/src/types/set_range_options.d.ts +24 -0
  472. package/build/treb-grid/src/types/set_range_options.js +22 -0
  473. package/build/treb-grid/src/types/set_range_options.js.map +1 -0
  474. package/build/treb-grid/src/types/tab_bar.d.ts +84 -0
  475. package/build/treb-grid/src/types/tab_bar.js +426 -0
  476. package/build/treb-grid/src/types/tab_bar.js.map +1 -0
  477. package/build/treb-grid/src/types/tile.d.ts +29 -0
  478. package/build/treb-grid/src/types/tile.js +22 -0
  479. package/build/treb-grid/src/types/tile.js.map +1 -0
  480. package/build/treb-grid/src/types/update_flags.d.ts +48 -0
  481. package/build/treb-grid/src/types/update_flags.js +22 -0
  482. package/build/treb-grid/src/types/update_flags.js.map +1 -0
  483. package/build/treb-grid/src/util/fontmetrics.d.ts +21 -0
  484. package/build/treb-grid/src/util/fontmetrics.js +82 -0
  485. package/build/treb-grid/src/util/fontmetrics.js.map +1 -0
  486. package/build/treb-grid/src/util/ua.d.ts +33 -0
  487. package/build/treb-grid/src/util/ua.js +86 -0
  488. package/build/treb-grid/src/util/ua.js.map +1 -0
  489. package/build/treb-parser/src/csv-parser.d.ts +13 -0
  490. package/build/treb-parser/src/csv-parser.js +107 -0
  491. package/build/treb-parser/src/csv-parser.js.map +1 -0
  492. package/build/treb-parser/src/index.d.ts +4 -0
  493. package/build/treb-parser/src/index.js +25 -0
  494. package/build/treb-parser/src/index.js.map +1 -0
  495. package/build/treb-parser/src/md-parser.d.ts +97 -0
  496. package/build/treb-parser/src/md-parser.js +403 -0
  497. package/build/treb-parser/src/md-parser.js.map +1 -0
  498. package/build/treb-parser/src/parser-types.d.ts +345 -0
  499. package/build/treb-parser/src/parser-types.js +53 -0
  500. package/build/treb-parser/src/parser-types.js.map +1 -0
  501. package/build/treb-parser/src/parser.d.ts +422 -0
  502. package/build/treb-parser/src/parser.js +2418 -0
  503. package/build/treb-parser/src/parser.js.map +1 -0
  504. package/build/treb-utils/src/event_source.d.ts +34 -0
  505. package/build/treb-utils/src/event_source.js +110 -0
  506. package/build/treb-utils/src/event_source.js.map +1 -0
  507. package/build/treb-utils/src/ievent_source.d.ts +9 -0
  508. package/build/treb-utils/src/ievent_source.js +22 -0
  509. package/build/treb-utils/src/ievent_source.js.map +1 -0
  510. package/build/treb-utils/src/index.d.ts +6 -0
  511. package/build/treb-utils/src/index.js +30 -0
  512. package/build/treb-utils/src/index.js.map +1 -0
  513. package/build/treb-utils/src/measurement.d.ts +42 -0
  514. package/build/treb-utils/src/measurement.js +145 -0
  515. package/build/treb-utils/src/measurement.js.map +1 -0
  516. package/build/treb-utils/src/scale.d.ts +16 -0
  517. package/build/treb-utils/src/scale.js +106 -0
  518. package/build/treb-utils/src/scale.js.map +1 -0
  519. package/build/treb-utils/src/serialize_html.d.ts +5 -0
  520. package/build/treb-utils/src/serialize_html.js +128 -0
  521. package/build/treb-utils/src/serialize_html.js.map +1 -0
  522. package/build/treb-utils/src/validate_uri.d.ts +20 -0
  523. package/build/treb-utils/src/validate_uri.js +55 -0
  524. package/build/treb-utils/src/validate_uri.js.map +1 -0
  525. package/dist/{chunk-XD5PEZBZ.mjs → chunk-A2NJA5VB.mjs} +1 -1
  526. package/dist/treb-export-worker.mjs +2 -2
  527. package/dist/treb-spreadsheet.mjs +4 -4
  528. package/dist/treb.d.ts +1 -1
  529. package/package.json +1 -1
@@ -0,0 +1,1609 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2026 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+ import { TextPartFlag, Style, ValueType, Area, Rectangle, ResolveThemeColor, IsDefinedColor } from 'treb-base-types';
22
+ import { MDParser } from 'treb-parser';
23
+ import { BaseLayout, TileRange } from '../layout/base_layout';
24
+ import { Get as GetFontMetrics } from '../util/fontmetrics';
25
+ const DEFAULT_INDENT = ' '; // two spaces in the current font
26
+ export class TileRenderer {
27
+ theme;
28
+ layout;
29
+ model;
30
+ view;
31
+ options;
32
+ // removing last_font because we are doing more complex
33
+ // font manipulation for MD text
34
+ // protected last_font?: string;
35
+ cell_edge_buffer = 4;
36
+ /**
37
+ * a record of cell overflows, also used for merges if they cross tile
38
+ * boundaries. on render, we check if an overflow(ed) cell is dirty; if
39
+ * so, this forces update of dependent cells.
40
+ */
41
+ overflow_areas = [];
42
+ buffer_canvas;
43
+ buffer_context;
44
+ buffer_canvas_size = { width: 256, height: 256 };
45
+ constructor(theme, layout, model, view, options) {
46
+ this.theme = theme;
47
+ this.layout = layout;
48
+ this.model = model;
49
+ this.view = view;
50
+ this.options = options;
51
+ this.buffer_canvas = layout.buffer_canvas;
52
+ this.buffer_canvas.width = this.buffer_canvas_size.width;
53
+ this.buffer_canvas.height = this.buffer_canvas_size.height;
54
+ // we need this attached to the document so it inherits fonts properly.
55
+ // in fact layout should manage it and then hand it to us (or we can grab it)
56
+ // this.buffer_canvas.classList.add('treb-buffer-canvas');
57
+ // document.body.appendChild(this.buffer_canvas);
58
+ const context = this.buffer_canvas.getContext('2d', { alpha: false });
59
+ if (context) {
60
+ const scale = this.layout.dpr;
61
+ this.buffer_context = context;
62
+ this.buffer_context.setTransform(scale, 0, 0, scale, 0, 0);
63
+ this.buffer_context.textAlign = 'left';
64
+ this.buffer_context.textBaseline = // BASELINE; //
65
+ 'alphabetic';
66
+ }
67
+ // this.UpdateTheme();
68
+ /*
69
+ if (this.theme.grid_cell?.font_size?.value){
70
+ if (this.theme.grid_cell.font_size.unit === 'px') {
71
+ FontMetricsCache.base_size_px = this.theme.grid_cell.font_size.value;
72
+ }
73
+ else if (this.theme.grid_cell.font_size.unit === 'pt') {
74
+ FontMetricsCache.base_size_px = this.theme.grid_cell.font_size.value * 4 / 3;
75
+ }
76
+ }
77
+ */
78
+ }
79
+ /**
80
+ * we manage overflow blocks to simplify (more or less) rendering,
81
+ * but they break in the event of insert/delete row/column. we need
82
+ * to adjust, or perhaps flush, when we insert/delete columns.
83
+ *
84
+ */
85
+ FlushOverflows() {
86
+ // flush all, mark dirty and drop areas.
87
+ const cells = this.view.active_sheet.cells;
88
+ for (const cell of cells.Iterate()) {
89
+ if (cell.renderer_data?.overflowed) {
90
+ cell.renderer_data = undefined;
91
+ cell.render_clean[this.view.view_index] = false;
92
+ }
93
+ }
94
+ /*
95
+ cells.IterateAll(cell => {
96
+ if (cell.renderer_data?.overflowed) {
97
+ cell.renderer_data = undefined;
98
+ cell.render_clean[this.view.view_index] = false;
99
+ }
100
+ });
101
+ */
102
+ for (const overflow_area of this.overflow_areas) {
103
+ overflow_area.tile.dirty = true;
104
+ }
105
+ this.overflow_areas = [];
106
+ }
107
+ /**
108
+ * new method for measuring text, intended to take into account
109
+ * indentation and wrapping, scale, and all the other stuff.
110
+ */
111
+ MeasureText(cell, current_width, override_scale) {
112
+ // we need a canvas. I guess we can just randomly use one?
113
+ const tile = this.layout.grid_tiles[0][0];
114
+ // we need fonts, and check if we need variants
115
+ const scale = override_scale ?? this.layout.scale;
116
+ const style = cell.style || {};
117
+ const base_font = Style.CompositeFont(this.theme.grid_cell_font_size, style, scale, this.theme);
118
+ const metrics = GetFontMetrics(base_font.font, base_font.variants);
119
+ const fonts = {
120
+ base: base_font.font,
121
+ strong: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, bold: true }, scale, this.theme).font,
122
+ emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, italic: true }, scale, this.theme).font,
123
+ strong_emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, bold: true, italic: true }, scale, this.theme).font,
124
+ };
125
+ if (base_font.variants) {
126
+ tile.style.fontVariant = base_font.variants;
127
+ }
128
+ else {
129
+ tile.style.fontVariant = '';
130
+ }
131
+ const context = tile.getContext('2d', { alpha: false });
132
+ if (!context) {
133
+ throw new Error('context failed');
134
+ }
135
+ const prepped = this.PrepText(context, fonts, cell, current_width);
136
+ const width = prepped.width;
137
+ const height = metrics.height * prepped.strings.length;
138
+ return { width, height };
139
+ }
140
+ /* *
141
+ * use one of the tile contexts to measure text. we are using the tile
142
+ * context because it's attached to the DOM, and style is applied. we need
143
+ * that for the root font size, in case font size in the style is relative
144
+ * (which it should be).
145
+ *
146
+ * we could use the buffer context, if that were attached to the DOM, but
147
+ * at the moment it is not so this is a shortcut. since we're not actually
148
+ * painting, it's not too bad, but we still fetch the context every time.
149
+ * hopefully it's cached.
150
+ *
151
+ * FIXME: if you're doing it this way, maybe pass in an array of strings/
152
+ * fonts, to avoid getting the context every time?
153
+ *
154
+ * @param text
155
+ * @param font
156
+ * /
157
+ public MeasureText(text: string, font?: string): TextMetrics {
158
+
159
+ const context = this.layout.grid_tiles[0][0].getContext('2d', { alpha: false });
160
+
161
+ if (!context) {
162
+ throw new Error('invalid context');
163
+ }
164
+
165
+ if (font) {
166
+ context.font = font;
167
+ }
168
+
169
+ return context.measureText(text);
170
+ }
171
+ */
172
+ /**
173
+ * when drawing to the buffered canvas, (1) ensure it's large enough,
174
+ * and (2) set transform as necessary (we may be overflowing to the left).
175
+ */
176
+ EnsureBuffer(width = 0, height = 0, offset = 0) {
177
+ // console.info('eb', width, height, offset);
178
+ const scale = this.layout.dpr;
179
+ width = width * scale;
180
+ height = height * scale;
181
+ offset = offset * scale;
182
+ if (width > this.buffer_canvas_size.width
183
+ || height > this.buffer_canvas_size.height) {
184
+ this.buffer_canvas_size.width = Math.max(Math.ceil(width / 256) * 256, this.buffer_canvas_size.width);
185
+ this.buffer_canvas_size.height = Math.max(Math.ceil(height / 256) * 256, this.buffer_canvas_size.height);
186
+ // console.info('size ->', this.buffer_canvas_size);
187
+ this.buffer_canvas.width = this.buffer_canvas_size.width;
188
+ this.buffer_canvas.height = this.buffer_canvas_size.height;
189
+ const context = this.buffer_canvas.getContext('2d', { alpha: false });
190
+ if (context) {
191
+ this.buffer_context = context;
192
+ this.buffer_context.textAlign = 'left';
193
+ this.buffer_context.textBaseline = // BASELINE;
194
+ 'alphabetic';
195
+ }
196
+ }
197
+ this.buffer_context.setTransform(scale, 0, 0, scale, offset, 0);
198
+ }
199
+ /**
200
+ * check all overflow areas. if any elements are dirty, mark all elements
201
+ * as dirty (FIXME: and remove the list?)
202
+ */
203
+ OverflowDirty(full_tile = false) {
204
+ const mutated = [];
205
+ for (const overflow of this.overflow_areas) {
206
+ const row = overflow.area.start.row;
207
+ let dirty = full_tile; // false;
208
+ if (!dirty) {
209
+ for (let column = overflow.area.start.column; !dirty && column <= overflow.area.end.column; column++) {
210
+ const cell = this.view.active_sheet.cells.GetCell({ row, column }, false);
211
+ dirty = !!(cell && !cell.render_clean[this.view.view_index]);
212
+ }
213
+ }
214
+ if (dirty) {
215
+ for (let column = overflow.area.start.column; column <= overflow.area.end.column; column++) {
216
+ const cell = this.view.active_sheet.cells.GetCell({ row, column }, false);
217
+ if (cell) {
218
+ cell.render_clean[this.view.view_index] = false;
219
+ if (cell.renderer_data && cell.renderer_data.overflowed) {
220
+ cell.renderer_data = undefined;
221
+ }
222
+ }
223
+ }
224
+ overflow.tile.dirty = true;
225
+ }
226
+ else
227
+ mutated.push(overflow);
228
+ }
229
+ this.overflow_areas = mutated;
230
+ }
231
+ /**
232
+ *
233
+ */
234
+ RenderCorner( /* selection: GridSelection */) {
235
+ const corner = this.layout.corner_canvas;
236
+ const context = corner.getContext('2d', { alpha: false });
237
+ if (!context) {
238
+ throw new Error('invalid context');
239
+ }
240
+ const font_info = Style.CompositeFont(this.theme.grid_cell_font_size, this.theme.headers || {}, this.layout.scale, this.theme);
241
+ const m2 = GetFontMetrics(font_info.font, font_info.variants);
242
+ const scale = this.layout.dpr;
243
+ const header_size = this.layout.header_offset;
244
+ let x = header_size.x;
245
+ for (let i = 0; i < this.view.active_sheet.freeze.columns; i++) {
246
+ x += this.layout.ColumnWidth(i);
247
+ }
248
+ let y = header_size.y;
249
+ for (let i = 0; i < this.view.active_sheet.freeze.rows; i++) {
250
+ y += this.layout.RowHeight(i);
251
+ }
252
+ context.setTransform(scale, 0, 0, scale, 0, 0);
253
+ context.fillStyle = this.theme.headers?.fill ? ResolveThemeColor(this.theme, this.theme.headers.fill) : '';
254
+ context.fillRect(0, 0, x, header_size.y);
255
+ context.fillRect(0, 0, header_size.x, y);
256
+ // we have to split this into two parts because of the new
257
+ // header grid color. do the header part first...
258
+ context.strokeStyle = this.theme.headers_grid_color || '';
259
+ context.beginPath();
260
+ context.moveTo(header_size.x - 0.5, 0);
261
+ context.lineTo(header_size.x - 0.5, header_size.y);
262
+ context.moveTo(0, header_size.y - 0.5);
263
+ context.lineTo(header_size.x, header_size.y - 0.5);
264
+ context.stroke();
265
+ // actually we can bail out first
266
+ if (!this.view.active_sheet.freeze.columns && !this.view.active_sheet.freeze.rows)
267
+ return;
268
+ // then do the other part with the regular grid color
269
+ context.strokeStyle = this.theme.grid_color || '';
270
+ context.beginPath();
271
+ if (y !== header_size.y) {
272
+ context.moveTo(header_size.x - 0.5, header_size.y);
273
+ context.lineTo(header_size.x - 0.5, y);
274
+ }
275
+ if (x !== header_size.x) {
276
+ context.moveTo(header_size.x, header_size.y - 0.5);
277
+ context.lineTo(x, header_size.y - 0.5);
278
+ }
279
+ context.stroke();
280
+ // here we go back to the header grid color for the breaks
281
+ context.strokeStyle = this.theme.headers_grid_color || '';
282
+ // NOTE: if headers are hidden (which is done by setting width/height to
283
+ // 0 or 1 pixel) we don't want to render them here.
284
+ context.textAlign = 'center';
285
+ context.textBaseline = 'middle'; // FUXME
286
+ context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
287
+ context.fillStyle = ResolveThemeColor(this.theme, this.theme.headers?.text);
288
+ if (this.view.active_sheet.freeze.rows && this.layout.header_offset.x > 1) {
289
+ context.setTransform(scale, 0, 0, scale, 0, 0);
290
+ context.translate(0, header_size.y);
291
+ context.beginPath();
292
+ context.moveTo(0, 0 - 0.5);
293
+ context.lineTo(header_size.x, 0 - 0.5);
294
+ context.stroke();
295
+ this.RenderRowLabels(context, 0, this.view.active_sheet.freeze.rows - 1, // m2.block);
296
+ m2.height);
297
+ }
298
+ if (this.view.active_sheet.freeze.columns && this.layout.header_offset.y > 1) {
299
+ context.setTransform(scale, 0, 0, scale, 0, 0);
300
+ context.translate(header_size.x, 0);
301
+ // what is this doing? it's not consistent with the column paint routine...
302
+ // A: it's different. it's drawing the line at the left of the header,
303
+ // which otherwise wouldn't render [really?]. we already have the line
304
+ // at the bottom rendered above.
305
+ context.beginPath();
306
+ context.moveTo(0 - 0.5, 0);
307
+ context.lineTo(0 - 0.5, header_size.y);
308
+ context.stroke();
309
+ this.RenderColumnLabels(context, 0, this.view.active_sheet.freeze.columns - 1);
310
+ }
311
+ /////
312
+ }
313
+ /**
314
+ * unifying because headers and corner both render labels.
315
+ *
316
+ * @param context
317
+ * @param column
318
+ * @param end
319
+ */
320
+ RenderColumnLabels(context, column, end) {
321
+ const header_y = this.layout.header_offset.y;
322
+ if (header_y <= 1) {
323
+ return;
324
+ }
325
+ context.fillStyle = ResolveThemeColor(this.theme, this.theme.headers?.text, 0);
326
+ context.beginPath();
327
+ for (; column <= end; column++) {
328
+ const width = this.layout.ColumnWidth(column);
329
+ const text = Area.ColumnToLabel(column);
330
+ const metrics = context.measureText(text);
331
+ if (width > metrics.width) {
332
+ context.fillText(text, width / 2, header_y / 2 + 1);
333
+ }
334
+ context.moveTo(width - 0.5, 0);
335
+ context.lineTo(width - 0.5, header_y);
336
+ context.translate(width, 0);
337
+ }
338
+ context.stroke();
339
+ }
340
+ RenderRowLabels(context, row, end, block) {
341
+ const header_x = this.layout.header_offset.x;
342
+ if (header_x <= 1) {
343
+ return;
344
+ }
345
+ context.fillStyle = ResolveThemeColor(this.theme, this.theme.headers?.text, 0);
346
+ context.beginPath();
347
+ for (; row <= end; row++) {
348
+ const height = this.layout.RowHeight(row);
349
+ if (height >= block * 1.2) {
350
+ context.fillText(`${row + 1}`, header_x / 2, height / 2 + 1);
351
+ }
352
+ context.moveTo(0, height - 0.5);
353
+ context.lineTo(header_x, height - 0.5);
354
+ context.translate(0, height);
355
+ }
356
+ context.stroke();
357
+ }
358
+ /**
359
+ */
360
+ RenderHeaders(tiles /*, selection: GridSelection*/, force = false) {
361
+ const scale = this.layout.dpr;
362
+ const header_size = this.layout.header_offset;
363
+ const font_info = Style.CompositeFont(this.theme.grid_cell_font_size, this.theme.headers || {}, this.layout.scale, this.theme);
364
+ const m2 = GetFontMetrics(font_info.font, font_info.variants);
365
+ for (let column = tiles.start.column; column <= tiles.end.column; column++) {
366
+ const tile = this.layout.column_header_tiles[column];
367
+ if (tile.dirty || force) {
368
+ const context = tile.getContext('2d', { alpha: false });
369
+ if (!context)
370
+ continue;
371
+ context.setTransform(scale, 0, 0, scale, 0, 0);
372
+ context.textAlign = 'center';
373
+ context.textBaseline = 'middle'; // FIXME
374
+ context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
375
+ if (font_info.variants) {
376
+ tile.style.fontVariant = font_info.variants;
377
+ }
378
+ else {
379
+ tile.style.fontVariant = '';
380
+ }
381
+ context.fillStyle = this.theme.headers?.fill ? ResolveThemeColor(this.theme, this.theme.headers.fill) : '';
382
+ context.fillRect(0, 0, tile.logical_size.width, this.layout.header_offset.y);
383
+ // context.strokeStyle = this.theme.grid_color || '';
384
+ context.strokeStyle = this.theme.headers_grid_color || '';
385
+ // this draws a line at the bottom of the header
386
+ // (using regular grid color)
387
+ // looks better using the header grid color
388
+ context.beginPath();
389
+ context.moveTo(0, header_size.y - 0.5);
390
+ context.lineTo(tile.logical_size.width, header_size.y - 0.5);
391
+ context.stroke();
392
+ // then we switch to the header color for the edges
393
+ context.strokeStyle = this.theme.headers_grid_color || '';
394
+ this.RenderColumnLabels(context, tile.first_cell.column, tile.last_cell.column);
395
+ tile.dirty = false;
396
+ }
397
+ }
398
+ for (let row = tiles.start.row; row <= tiles.end.row; row++) {
399
+ const tile = this.layout.row_header_tiles[row];
400
+ if (tile.dirty || force) {
401
+ const context = tile.getContext('2d', { alpha: false });
402
+ if (!context)
403
+ continue;
404
+ context.fillStyle = this.theme.headers?.fill ? ResolveThemeColor(this.theme, this.theme.headers.fill) : '';
405
+ context.setTransform(scale, 0, 0, scale, 0, 0);
406
+ context.textAlign = 'center';
407
+ context.textBaseline = 'middle'; // FIXME
408
+ // context.font = Style.Font(this.theme.headers||{}, this.layout.scale);
409
+ context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
410
+ if (font_info.variants) {
411
+ tile.style.fontVariant = font_info.variants;
412
+ }
413
+ else {
414
+ tile.style.fontVariant = '';
415
+ }
416
+ context.fillRect(0, 0, this.layout.header_offset.x, tile.logical_size.height);
417
+ // context.strokeStyle = this.theme.grid_color || '';
418
+ context.strokeStyle = this.theme.headers_grid_color || '';
419
+ context.beginPath();
420
+ context.moveTo(header_size.x - 0.5, 0);
421
+ context.lineTo(header_size.x - 0.5, tile.logical_size.height);
422
+ context.stroke();
423
+ context.strokeStyle = this.theme.headers_grid_color || '';
424
+ this.RenderRowLabels(context, tile.first_cell.row, tile.last_cell.row, // m2.block);
425
+ m2.height);
426
+ tile.dirty = false;
427
+ }
428
+ }
429
+ if (this.view.active_sheet.freeze.rows || this.view.active_sheet.freeze.columns) {
430
+ this.RenderCorner();
431
+ }
432
+ }
433
+ /**
434
+ *
435
+ * @param tile starting tile
436
+ * @param scale scale
437
+ * @param dx tile offset, in tiles
438
+ * @param dy tile offset, in tiles
439
+ * @param left (original) translation, in scaled pixels
440
+ * @param top (original) translation, in scaled pixels
441
+ * @param result buffer info
442
+ */
443
+ CopyToAdjacent(tile, scale, dx, dy, left, top, result) {
444
+ const adjacent = this.layout.AdjacentTile(tile, dy, dx);
445
+ if (!adjacent)
446
+ return; // FIXME: warn?
447
+ let x = left;
448
+ let y = top;
449
+ if (dx > 0) {
450
+ x = left - (tile.pixel_end.x - tile.pixel_start.x) * scale;
451
+ }
452
+ else if (dx < 0) {
453
+ x = left + (adjacent.pixel_end.x - adjacent.pixel_start.x) * scale;
454
+ }
455
+ if (dy > 0) {
456
+ y = top - (tile.pixel_end.y - tile.pixel_start.y) * scale;
457
+ }
458
+ const context = adjacent.getContext('2d', { alpha: false });
459
+ if (context) {
460
+ context.setTransform(scale, 0, 0, scale, x, y);
461
+ context.drawImage(this.buffer_canvas, 0, 0, (result.width || 0) * scale, (result.height || 0) * scale, result.left || 0, 0, result.width || 0, result.height || 0);
462
+ }
463
+ }
464
+ /** render a tile */
465
+ Render(tile) {
466
+ // can we assume this is always set? this feels sloppy, and
467
+ // it feels like something we'd like to turn off... can we
468
+ // fetch the canvas more than once? not sure what the impact of
469
+ // that would be.
470
+ // tile.style.fontVariantNumeric = 'lining-nums tabular-nums';
471
+ const context = tile.getContext('2d', { alpha: false });
472
+ if (!context) {
473
+ return;
474
+ } // should throw
475
+ context.textBaseline = // BASELINE;
476
+ 'alphabetic';
477
+ const scale = this.layout.dpr;
478
+ context.setTransform(scale, 0, 0, scale, 0, 0);
479
+ let left = 0;
480
+ let top = 0;
481
+ for (let column = tile.first_cell.column; column <= tile.last_cell.column; column++) {
482
+ const width = this.layout.ColumnWidth(column);
483
+ if (!width)
484
+ continue;
485
+ top = 0;
486
+ for (let row = tile.first_cell.row; row <= tile.last_cell.row; row++) {
487
+ const height = this.layout.RowHeight(row);
488
+ if (height) {
489
+ context.setTransform(scale, 0, 0, scale, left, top);
490
+ const cell = this.view.active_sheet.CellData({ row, column });
491
+ if (tile.needs_full_repaint || !cell.render_clean[this.view.view_index]) {
492
+ const result = this.RenderCell(tile, cell, context, { row, column }, width, height, (tile.pixel_start.x + left), (tile.pixel_start.y + top));
493
+ // render_list.push({row, column, cell});
494
+ if (result.tile_overflow_right) {
495
+ this.CopyToAdjacent(tile, scale, 1, 0, left, top, result);
496
+ }
497
+ if (result.tile_overflow_left) {
498
+ this.CopyToAdjacent(tile, scale, -1, 0, left, top, result);
499
+ }
500
+ if (result.tile_overflow_bottom) {
501
+ this.CopyToAdjacent(tile, scale, 0, 1, left, top, result);
502
+ }
503
+ }
504
+ }
505
+ top += (height * scale);
506
+ }
507
+ left += (width * scale);
508
+ }
509
+ if (!this.view.active_sheet.freeze.rows && !this.view.active_sheet.freeze.columns)
510
+ return; // render_list;
511
+ // paint to headers
512
+ let copy_height = 0;
513
+ let copy_width = 0;
514
+ if (tile.first_cell.row <= this.view.active_sheet.freeze.rows - 1) {
515
+ for (let i = tile.first_cell.row; i < this.view.active_sheet.freeze.rows && i <= tile.last_cell.row; i++) {
516
+ copy_height += this.layout.RowHeight(i);
517
+ }
518
+ }
519
+ if (tile.first_cell.column <= this.view.active_sheet.freeze.columns - 1) {
520
+ for (let i = tile.first_cell.column; i < this.view.active_sheet.freeze.columns && i <= tile.last_cell.column; i++) {
521
+ copy_width += this.layout.ColumnWidth(i);
522
+ }
523
+ }
524
+ if (copy_height) {
525
+ // get tile header
526
+ const header = this.layout.frozen_row_tiles[tile.tile_position.column];
527
+ if (!header)
528
+ throw new Error('can\'t find matching header tile');
529
+ const header_context = header.getContext('2d', { alpha: true });
530
+ if (!header_context)
531
+ throw new Error('header context failed');
532
+ // FIXME: offset for !first tile
533
+ header_context.setTransform(scale, 0, 0, scale, 0, 0); // this.model.sheet.header_offset.y * scale);
534
+ header_context.drawImage(tile, 0, 0, tile.logical_size.width * scale, copy_height * scale, 0, 0, tile.logical_size.width, copy_height);
535
+ }
536
+ if (copy_width) {
537
+ // get tile header
538
+ const header = this.layout.frozen_column_tiles[tile.tile_position.row];
539
+ if (!header)
540
+ throw new Error('can\'t find matching header tile');
541
+ const header_context = header.getContext('2d', { alpha: true });
542
+ if (!header_context)
543
+ throw new Error('header context failed');
544
+ // FIXME: offset for !first tile
545
+ header_context.setTransform(scale, 0, 0, scale, 0, 0);
546
+ header_context.drawImage(tile, 0, 0, copy_width * scale, tile.logical_size.height * scale, 0, 0, copy_width, tile.logical_size.height);
547
+ }
548
+ if (copy_width && copy_height) {
549
+ const corner_context = this.layout.corner_canvas.getContext('2d', { alpha: 'false' });
550
+ if (!corner_context)
551
+ throw new Error('corner context failed');
552
+ // FIXME: offset for !first tile
553
+ corner_context.setTransform(scale, 0, 0, scale, this.layout.header_offset.x * scale, this.layout.header_offset.y * scale);
554
+ corner_context.drawImage(tile, 0, 0, copy_width * scale, copy_height * scale, 0, 0, copy_width, copy_height);
555
+ }
556
+ return; // render_list;
557
+ }
558
+ /**
559
+ * split and measure text. can be cached. there are actually two completely
560
+ * separate operations here, which we're consolidating for convenience (and
561
+ * because they never overlap).
562
+ *
563
+ * UPDATED returning a 2d array, where the first dimension represents lines
564
+ * and the second dimension represents components
565
+ */
566
+ PrepText(context, fonts, cell, cell_width /*, override_text?: string*/) {
567
+ const strings = [];
568
+ const style = cell.style || {};
569
+ let pad_entry;
570
+ let composite_width = 0;
571
+ // -------------------------------------------------------------------------
572
+ // moved translated boolean formatting here. I don't like this. it
573
+ // should be in sheet (at least that's where the rest of formatting
574
+ // is), but sheet doesn't have a reference to data model (and because
575
+ // sheets are owned by data model, I don't want to add a circular ref).
576
+ if (cell.rendered_type === ValueType.boolean) {
577
+ const value = cell.calculated_type ? cell.calculated : cell.value;
578
+ cell.formatted = value ? (this.model.language_model?.boolean_true || 'TRUE') : (this.model.language_model?.boolean_false || 'FALSE');
579
+ }
580
+ // -------------------------------------------------------------------------
581
+ let override_formatting;
582
+ let formatted = cell.editing ? '' : cell.formatted; // <-- empty on editing, to remove overflows
583
+ // remove existing indent, if any
584
+ if (Array.isArray(formatted)) {
585
+ formatted = formatted.filter(test => test.flag !== TextPartFlag.indent);
586
+ }
587
+ // precalculate indent as string so we can use layout
588
+ let indent = '';
589
+ let align;
590
+ if (style.indent) {
591
+ for (let i = 0; i < style.indent; i++) {
592
+ indent += DEFAULT_INDENT;
593
+ }
594
+ align = style.horizontal_align;
595
+ // default might be left or right based on type
596
+ if (!align) {
597
+ align = (cell.type === ValueType.number ||
598
+ cell.calculated_type === ValueType.number ||
599
+ cell.type === ValueType.complex ||
600
+ cell.calculated_type === ValueType.complex) ? 'right' : 'left';
601
+ }
602
+ }
603
+ if (Array.isArray(formatted)) {
604
+ // type 1 is a multi-part formatted string; used for number formats.
605
+ // we support invisible characters and padded (expanded) characters
606
+ // FIXME: is there any case where this would include md? ...
607
+ // (potentially yes? what happens if you have a string in a number-formatted cell?)
608
+ // this is a single line, with number formatting
609
+ if (indent) {
610
+ if (align === 'right') {
611
+ formatted.push({ text: indent, flag: TextPartFlag.indent });
612
+ }
613
+ else if (align === 'left' || typeof align === 'undefined') {
614
+ formatted.unshift({ text: indent, flag: TextPartFlag.indent });
615
+ }
616
+ }
617
+ for (const part of formatted) {
618
+ if (part.flag === TextPartFlag.formatting) {
619
+ override_formatting = part.text;
620
+ continue;
621
+ }
622
+ const mt_width = context.measureText(part.text).width;
623
+ const render_part = {
624
+ width: mt_width,
625
+ text: part.text,
626
+ hidden: part.flag === TextPartFlag.hidden,
627
+ indent: part.flag === TextPartFlag.indent,
628
+ };
629
+ strings.push(render_part);
630
+ if (part.flag === TextPartFlag.padded) {
631
+ pad_entry = render_part;
632
+ }
633
+ else {
634
+ composite_width += mt_width;
635
+ }
636
+ }
637
+ if (pad_entry) {
638
+ const text = pad_entry.text;
639
+ const text_width = pad_entry.width;
640
+ const balance = cell_width - composite_width - (2 * this.cell_edge_buffer);
641
+ pad_entry.width = Math.max(0, balance);
642
+ if (balance > 0) {
643
+ const count = Math.floor(balance / text_width);
644
+ for (let i = 1; i < count; i++) {
645
+ pad_entry.text += text;
646
+ }
647
+ composite_width = cell_width - (2 * this.cell_edge_buffer);
648
+ }
649
+ else {
650
+ pad_entry.text = '';
651
+ }
652
+ }
653
+ return { strings: [strings], format: override_formatting, width: composite_width };
654
+ }
655
+ else if (formatted) {
656
+ // type 2 is a single string, but may be split into newlines either
657
+ // explicitly or implicitly via wrap
658
+ // ALSO we don't show leading apostrophes, as those indicate a string
659
+ if (cell.type === ValueType.string && formatted[0] === '\'') {
660
+ formatted = formatted.slice(1);
661
+ }
662
+ let md;
663
+ if (this.options.markdown) {
664
+ md = MDParser.instance.Parse(formatted);
665
+ }
666
+ else {
667
+ md = MDParser.instance.Dummy(formatted);
668
+ context.font = fonts.base; // never changes
669
+ }
670
+ // if we are not wrapping, we don't have to do any trimming. if we
671
+ // are wrapping, leave whitespace attached to the front; possibly trim
672
+ // whitespace in between tokens (this should be attached to tokens, but
673
+ // possibly not...)
674
+ let max_width = 0;
675
+ // for wrapping
676
+ let bound = cell_width - (2 * this.cell_edge_buffer);
677
+ const strings = [];
678
+ if (style.wrap) {
679
+ const indent_width = (indent && style.horizontal_align !== 'center') ? context.measureText(indent).width : 0;
680
+ bound -= indent_width;
681
+ for (const line of md) {
682
+ // we should probably normalize whitespace -- because formatting
683
+ // may put some whitespace before tokens, other whitespace after
684
+ // tokens, and so on. it's confusing.
685
+ for (let i = 1; i < line.length; i++) {
686
+ const test = line[i].text.match(/^(\s+)/);
687
+ if (test) {
688
+ line[i - 1].text += test[1];
689
+ line[i].text = line[i].text.replace(/^\s+/, '');
690
+ }
691
+ }
692
+ const words = [];
693
+ for (const element of line) {
694
+ if (this.options.markdown) {
695
+ if (element.strong && element.emphasis) {
696
+ context.font = fonts.strong_emphasis;
697
+ }
698
+ else if (element.strong) {
699
+ context.font = fonts.strong;
700
+ }
701
+ else if (element.emphasis) {
702
+ context.font = fonts.emphasis;
703
+ }
704
+ else {
705
+ context.font = fonts.base;
706
+ }
707
+ }
708
+ const split = element.text.match(/\S+\s*/g); // preserve extra whitespace on the same line...
709
+ if (split && split.length) {
710
+ for (const word of split) {
711
+ // FIXME: maybe overoptimizing, but this is measuring the same
712
+ // text twice; could reduce...
713
+ const trimmed = context.measureText(word.trim()).width;
714
+ const width = context.measureText(word).width; // including trailing whitespace
715
+ words.push({ part: element, text: word, trimmed, width });
716
+ }
717
+ }
718
+ }
719
+ // now we can construct wrapped lines. we don't split words, so
720
+ // we always have at least one word on a line.
721
+ while (words.length) {
722
+ // add first word. line length is _trimmed_ length.
723
+ let last = words.shift(); // NOT undefined
724
+ const line2 = [last];
725
+ let line_width = last.trimmed;
726
+ // add more words? check bounds first
727
+ while (line_width < bound && words.length) {
728
+ // we're holding the trim width on the last word, but to
729
+ // test we need the untrimmed width
730
+ const word = words[0];
731
+ const test = line_width - last.trimmed + last.width + word.trimmed;
732
+ if (test >= bound) {
733
+ break; // line finished
734
+ }
735
+ // add this word to the line, remove it from the stack
736
+ last = word;
737
+ line2.push(word);
738
+ line_width = test;
739
+ words.shift();
740
+ max_width = Math.max(max_width, line_width);
741
+ }
742
+ // trim the last word, then insert a row (we're relying on the
743
+ // fact that this points at the last entry in the array)
744
+ last.text = last.text.trim();
745
+ last.width = last.trimmed;
746
+ max_width = Math.max(max_width, last.width);
747
+ const line_string = line2.map((metric) => {
748
+ return {
749
+ ...metric.part,
750
+ hidden: false,
751
+ width: metric.width,
752
+ text: metric.text,
753
+ };
754
+ });
755
+ if (style.indent) {
756
+ if (align === 'right') {
757
+ line_string.push({ text: indent, hidden: false, width: indent_width, indent: true });
758
+ }
759
+ else if (align === 'left') {
760
+ line_string.unshift({ text: indent, hidden: false, width: indent_width, indent: true });
761
+ }
762
+ }
763
+ strings.push(line_string);
764
+ }
765
+ }
766
+ }
767
+ else {
768
+ // simple case
769
+ for (const line of md) {
770
+ const parts = [];
771
+ /*
772
+ if (style.indent) {
773
+ if (align === 'right') {
774
+ line.push({ text: indent });
775
+ }
776
+ else if (align === 'left') {
777
+ line.unshift({ text: indent });
778
+ }
779
+ }
780
+ */
781
+ let line_width = 0;
782
+ let indent_width = 0;
783
+ if (style.indent) {
784
+ context.font = fonts.base;
785
+ indent_width = context.measureText(indent).width;
786
+ }
787
+ if (style.indent && align === 'left') {
788
+ line_width += indent_width;
789
+ parts.push({
790
+ text: indent,
791
+ hidden: false,
792
+ width: indent_width,
793
+ indent: true,
794
+ });
795
+ }
796
+ for (const element of line) {
797
+ if (this.options.markdown) {
798
+ if (element.strong && element.emphasis) {
799
+ context.font = fonts.strong_emphasis;
800
+ }
801
+ else if (element.strong) {
802
+ context.font = fonts.strong;
803
+ }
804
+ else if (element.emphasis) {
805
+ context.font = fonts.emphasis;
806
+ }
807
+ else {
808
+ context.font = fonts.base;
809
+ }
810
+ }
811
+ const width = context.measureText(element.text).width;
812
+ line_width += width;
813
+ parts.push({
814
+ ...element,
815
+ hidden: false,
816
+ width,
817
+ });
818
+ }
819
+ if (style.indent && align === 'right') {
820
+ line_width += indent_width;
821
+ parts.push({
822
+ text: indent,
823
+ hidden: false,
824
+ width: indent_width,
825
+ indent: true,
826
+ });
827
+ }
828
+ max_width = Math.max(max_width, line_width);
829
+ strings.push(parts);
830
+ }
831
+ }
832
+ return { strings, width: max_width };
833
+ }
834
+ return {
835
+ strings: [[{ text: '', hidden: false, width: 0 }]],
836
+ width: 0,
837
+ };
838
+ }
839
+ ResolveColors(style) {
840
+ const resolved = { ...style };
841
+ resolved.text = { text: ResolveThemeColor(this.theme, style.text, 1) };
842
+ // TODO: other colors
843
+ return resolved;
844
+ }
845
+ RenderCellBorders(address, context, style, left = 0, top = 0, width = 0, height = 0) {
846
+ // cell borders is one of those things that seems simple, even trivial,
847
+ // until you actually try to do it. then it turns out to be ridiculously
848
+ // complicated.
849
+ // one complicating factor that we are adding is that we don't necessarily
850
+ // paint in order, because we may update single cells at a time. so we need
851
+ // to account for shared borders in two directions.
852
+ // general rules:
853
+ //
854
+ // (1) borders take priority over fills
855
+ //
856
+ // (2) bottom cell, then right cell, take priority over this cell (except
857
+ // with regards to rule 1, so our border takes precendence over bottom
858
+ // cell fill, but not bottom cell border).
859
+ //
860
+ // some other things to note:
861
+ //
862
+ // - double borders (we only handle double-bottom, atm) flow _into_ the
863
+ // neighboring cell, instead of just using the shared border. in this
864
+ // case the shared edge should be colored wrt to the cell that owns the
865
+ // double border, either that cell's fill or default.
866
+ //
867
+ // - if we have a fill, we are painting the shared border; but in this case
868
+ // you also have to consider the top-left corner, which could be a border
869
+ // owned by a cell offset by (-1, -1) and because of rule 1, above, that
870
+ // pixel needs to stay border.
871
+ //
872
+ // - that theoretically applies to other corners as well, but somehow that
873
+ // hasn't come up? (...)
874
+ //
875
+ // - instead of clipping all the corners, when necessary, why not just paint
876
+ // the diagonals? might save time
877
+ // I think there are some opportunities for caching here (TODO)
878
+ // ---
879
+ // (moved to sheet, using numpad naming)
880
+ const numpad = this.view.active_sheet.SurroundingStyle(address, this.theme.table);
881
+ // --- start with fills ----------------------------------------------------
882
+ // paint top background
883
+ let color = ResolveThemeColor(this.theme, numpad[8].fill);
884
+ if (color) {
885
+ context.fillStyle = color;
886
+ context.fillRect(left + 0, top - 1, width, 1);
887
+ }
888
+ // paint left background
889
+ color = ResolveThemeColor(this.theme, numpad[4].fill);
890
+ if (color) {
891
+ context.fillStyle = color;
892
+ context.fillRect(left - 1, top, 1, height);
893
+ }
894
+ // paint our background. note this one goes up, left
895
+ color = ResolveThemeColor(this.theme, style.fill);
896
+ if (color) {
897
+ context.fillStyle = color;
898
+ context.fillRect(left - 1, top - 1, width + 1, height + 1);
899
+ }
900
+ // fill of cell to the right
901
+ color = ResolveThemeColor(this.theme, numpad[6].fill);
902
+ if (color) {
903
+ context.fillStyle = color;
904
+ context.fillRect(left + width - 1, top - 1, 1, height + 1);
905
+ }
906
+ // fill of cell underneath
907
+ color = ResolveThemeColor(this.theme, numpad[2].fill);
908
+ if (color) {
909
+ context.fillStyle = color;
910
+ context.fillRect(left - 1, top + height - 1, width + 1, 1);
911
+ }
912
+ // --- corner borders ------------------------------------------------------
913
+ if (numpad[6].border_top && !numpad[6].border_left) {
914
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[6].border_top_fill, 1);
915
+ context.fillRect(left + width - 1, top - 2 + numpad[6].border_top, 1, 1);
916
+ }
917
+ if (numpad[9].border_left) {
918
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[9].border_left_fill, 1);
919
+ context.fillRect(left + width - 1, top - 1, 1, 1);
920
+ }
921
+ if (numpad[9].border_bottom) {
922
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[9].border_bottom_fill, 1);
923
+ context.fillRect(left + width - 1, top - 2 + numpad[9].border_bottom, 1, 1);
924
+ }
925
+ if (numpad[4].border_top && !numpad[4].border_right) {
926
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[4].border_right_fill, 1);
927
+ context.fillRect(left - 1, top - 2 + numpad[4].border_top, 1, 1);
928
+ }
929
+ if (numpad[7].border_right) {
930
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[7].border_right_fill, 1);
931
+ context.fillRect(left - 1, top - 1, 1, 1);
932
+ }
933
+ if (numpad[7].border_bottom) {
934
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[7].border_bottom_fill, 1);
935
+ context.fillRect(left - 1, top - 2 + numpad[7].border_bottom, 1, 1);
936
+ }
937
+ if (numpad[6].border_bottom && !numpad[6].border_left) {
938
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[6].border_bottom_fill, 1);
939
+ context.fillRect(left + width - 1, top + height - numpad[6].border_bottom, 1, 1);
940
+ }
941
+ if (numpad[3].border_left) {
942
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[3].border_left_fill, 1);
943
+ context.fillRect(left + width - 1, top + height - 1, 1, 1);
944
+ }
945
+ if (numpad[3].border_top) {
946
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[3].border_top_fill, 1);
947
+ context.fillRect(left + width - 1, top + height - numpad[3].border_top, 1, 1);
948
+ }
949
+ if (numpad[4].border_bottom && !numpad[4].border_right) {
950
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[4].border_bottom_fill, 1);
951
+ context.fillRect(left - 1, top + height - numpad[4].border_bottom, 1, 1);
952
+ }
953
+ if (numpad[1].border_right) {
954
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[1].border_right_fill, 1);
955
+ context.fillRect(left - 1, top + height - 1, 1, 1);
956
+ }
957
+ if (numpad[1].border_top) {
958
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[1].border_top_fill, 1);
959
+ context.fillRect(left - 1, top + height - numpad[1].border_top, 1, 1);
960
+ }
961
+ // --- neighbor borders ----------------------------------------------------
962
+ // paint top border
963
+ if (numpad[8].border_bottom) {
964
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[8].border_bottom_fill, 1);
965
+ if (numpad[8].border_bottom === 2) {
966
+ context.fillRect(left - 1, top - 2, width + 1, 1);
967
+ context.fillRect(left - 1, top - 0, width + 1, 1);
968
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[8].fill)
969
+ || ResolveThemeColor(this.theme, this.theme.grid_cell?.fill, 0) || '#fff';
970
+ context.fillRect(left - 1, top - 1, width + 1, 1);
971
+ }
972
+ else {
973
+ context.fillRect(left - 1, top - 1, width + 1, 1);
974
+ }
975
+ }
976
+ // paint left border
977
+ if (numpad[4].border_right) {
978
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[4].border_right_fill, 1);
979
+ context.fillRect(left - 1, top - 1, 1, height + 1);
980
+ }
981
+ // paint right border?
982
+ if (numpad[6].border_left) {
983
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[6].border_left_fill, 1);
984
+ context.fillRect(left + width - 1, top - 1, 1, height + 1);
985
+ }
986
+ // bottom? (...)
987
+ if (numpad[2].border_top) {
988
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[2].border_top_fill, 1);
989
+ if (numpad[2].border_top === 2) {
990
+ context.fillRect(left - 1, top + height - 2, width + 1, 1);
991
+ context.fillRect(left - 1, top + height - 0, width + 1, 1);
992
+ context.fillStyle = ResolveThemeColor(this.theme, numpad[2].fill)
993
+ || ResolveThemeColor(this.theme, this.theme.grid_cell?.fill, 0) || '#fff';
994
+ context.fillRect(left - 1, top + height - 1, width + 1, 1);
995
+ }
996
+ else {
997
+ context.fillRect(left - 1, top + height - 1, width + 1, 1);
998
+ }
999
+ }
1000
+ // -- our borders ----------------------------------------------------------
1001
+ if (style.border_top) {
1002
+ context.fillStyle = ResolveThemeColor(this.theme, style.border_top_fill, 1);
1003
+ if (style.border_top === 2) {
1004
+ context.fillRect(left - 1, top - 2, width + 1, 1);
1005
+ context.fillRect(left - 1, top + 0, width + 1, 1);
1006
+ context.fillStyle = ResolveThemeColor(this.theme, style.fill)
1007
+ || ResolveThemeColor(this.theme, this.theme.grid_cell?.fill, 0) || '#fff';
1008
+ context.fillRect(left - 1, top - 1, width + 1, 1);
1009
+ }
1010
+ else {
1011
+ context.fillRect(left - 1, top - 1, width + 1, 1);
1012
+ }
1013
+ }
1014
+ if (style.border_left) {
1015
+ context.fillStyle = ResolveThemeColor(this.theme, style.border_left_fill, 1);
1016
+ context.fillRect(left - 1, top - 1, 1, height + 1);
1017
+ }
1018
+ if (style.border_right) {
1019
+ context.fillStyle = ResolveThemeColor(this.theme, style.border_right_fill, 1);
1020
+ context.fillRect(left + width - 1, top - 1, 1, height + 1);
1021
+ }
1022
+ if (style.border_bottom) {
1023
+ context.fillStyle = ResolveThemeColor(this.theme, style.border_bottom_fill, 1);
1024
+ if (style.border_bottom === 2) {
1025
+ context.fillRect(left - 1, top + height - 2, width + 1, 1);
1026
+ context.fillRect(left - 1, top + height + 0, width + 1, 1);
1027
+ context.fillStyle = ResolveThemeColor(this.theme, style.fill)
1028
+ || ResolveThemeColor(this.theme, this.theme.grid_cell?.fill, 0) || '#fff';
1029
+ context.fillRect(left - 1, top + height - 1, width + 1, 1);
1030
+ }
1031
+ else {
1032
+ context.fillRect(left - 1, top + height - 1, width + 1, 1);
1033
+ }
1034
+ }
1035
+ }
1036
+ /**
1037
+ * paint background image, offset and tiled.
1038
+ */
1039
+ PaintBackgroundImage(context, image, left, top, width, height, render_left = 0, render_top = 0, offset = 0) {
1040
+ // there's no explicit broken flag, but we can infer from size
1041
+ if (!image.width || !image.height) {
1042
+ return;
1043
+ }
1044
+ const scale = (this.layout.scale || 1) * this.layout.dpr;
1045
+ const source_left = (left / scale) % image.width;
1046
+ const source_top = (top / scale) % image.height;
1047
+ const source_width = width / scale;
1048
+ const source_height = height / scale;
1049
+ const roll_x = (source_left + source_width) > image.width;
1050
+ const roll_y = (source_top + source_height) > image.height;
1051
+ if (roll_x) {
1052
+ context.drawImage(image, source_left - image.width, source_top, source_width, source_height, render_left, render_top, width - offset, height - offset);
1053
+ }
1054
+ if (roll_y) {
1055
+ context.drawImage(image, source_left, source_top - image.height, source_width, source_height, render_left, render_top, width - offset, height - offset);
1056
+ }
1057
+ if (roll_x && roll_y) {
1058
+ context.drawImage(image, source_left - image.width, source_top - image.height, source_width, source_height, render_left, render_top, width - offset, height - offset);
1059
+ }
1060
+ context.drawImage(image, source_left, source_top, source_width, source_height, render_left, render_top, width - offset, height - offset);
1061
+ }
1062
+ RenderCellBackground(note, address, context, style, width, height, cell_left = 0, cell_top = 0) {
1063
+ // so here we draw the background and the bottom and right grid edges.
1064
+ // fill is enclosed here, the border method has logic for border colors,
1065
+ // because it turns out to be complicated.
1066
+ context.fillStyle = this.theme.grid_color;
1067
+ context.fillRect(0, 0, width, height);
1068
+ if (this.view.active_sheet.image) {
1069
+ this.PaintBackgroundImage(context, this.view.active_sheet.image, cell_left, cell_top, width, height, 0, 0, 1);
1070
+ }
1071
+ else {
1072
+ const fill = ResolveThemeColor(this.theme, style.fill);
1073
+ if (fill) {
1074
+ context.fillStyle = fill;
1075
+ context.fillRect(0, 0, width - 1, height - 1);
1076
+ }
1077
+ else {
1078
+ context.fillStyle = ResolveThemeColor(this.theme, this.theme.grid_cell?.fill, 0) || '#fff';
1079
+ context.fillRect(0, 0, width - 1, height - 1);
1080
+ }
1081
+ }
1082
+ // the next call actually paints background, if we have a background color
1083
+ this.RenderCellBorders(address, context, style, 0, 0, width, height);
1084
+ // so we need to draw the note icon after that
1085
+ // why is this here? (it's rendered as background, I guess)
1086
+ if (note) {
1087
+ const offset_x = 2;
1088
+ const offset_y = 1;
1089
+ const length = 8;
1090
+ // FIXME: why is the default in here, and not in theme defaults?
1091
+ // actually it is in theme defaults, probably was here first.
1092
+ context.fillStyle = this.theme.note_marker_color;
1093
+ context.beginPath();
1094
+ context.moveTo(width - offset_x, offset_y);
1095
+ context.lineTo(width - offset_x - length, offset_y);
1096
+ context.lineTo(width - offset_x, offset_y + length);
1097
+ context.lineTo(width - offset_x, offset_y);
1098
+ context.fill();
1099
+ }
1100
+ }
1101
+ /**
1102
+ * refactoring render to allow rendering to buffered canvas, in the
1103
+ * case of tile overflow. this is problematic because as the code stands
1104
+ * now, it paints before determining if there's an overflow. so we need
1105
+ * to move some paint calls around.
1106
+ */
1107
+ RenderCell(tile, cell, context, address, width, height, cell_left = 0, cell_top = 0) {
1108
+ const result = {};
1109
+ // preserve the flag, then unset so we don't have to track around
1110
+ const dirty = !cell.render_clean[this.view.view_index];
1111
+ cell.render_clean[this.view.view_index] = true;
1112
+ // special case for overflows (this has been set by someone to the left)
1113
+ if (tile.needs_full_repaint &&
1114
+ cell.renderer_data?.overflowed) {
1115
+ return {};
1116
+ }
1117
+ let style = cell.style ? { ...cell.style } : {};
1118
+ if (cell.table) {
1119
+ style = this.view.active_sheet.CellStyleData(address, cell.table.theme || this.theme.table) || {};
1120
+ }
1121
+ if (cell.merge_area) {
1122
+ if ((address.row === cell.merge_area.start.row) &&
1123
+ (address.column === cell.merge_area.start.column)) {
1124
+ for (let column = cell.merge_area.start.column + 1; column <= cell.merge_area.end.column; column++) {
1125
+ width += this.layout.ColumnWidth(column);
1126
+ }
1127
+ for (let row = cell.merge_area.start.row + 1; row <= cell.merge_area.end.row; row++) {
1128
+ height += this.layout.RowHeight(row);
1129
+ }
1130
+ // get last cell for borders
1131
+ if (cell.merge_area.count > 1) {
1132
+ const end_cell_style = this.view.active_sheet.CellStyleData(cell.merge_area.end);
1133
+ if (end_cell_style) {
1134
+ style.border_bottom = end_cell_style.border_bottom;
1135
+ style.border_right = end_cell_style.border_right;
1136
+ style.border_bottom_fill = end_cell_style.border_bottom_fill;
1137
+ style.border_right_fill = end_cell_style.border_right_fill;
1138
+ }
1139
+ }
1140
+ // check if we are going to overflow into another tile right or down
1141
+ if (cell.merge_area.end.column > tile.last_cell.column) {
1142
+ result.tile_overflow_right = true;
1143
+ }
1144
+ if (cell.merge_area.end.row > tile.last_cell.row) {
1145
+ result.tile_overflow_bottom = true;
1146
+ }
1147
+ // there's an issue with merges that cross tiles and resizing; they
1148
+ // don't get painted properly. we can reuse the overflow record list
1149
+ // to fix this.
1150
+ // NOTE: this refers to _tile_ overflows, not cell overflows. we
1151
+ // should change the name to make this clearer.
1152
+ if (result.tile_overflow_bottom || result.tile_overflow_right) {
1153
+ this.overflow_areas.push({
1154
+ tile,
1155
+ head: { ...address },
1156
+ area: new Area(cell.merge_area.start, cell.merge_area.end),
1157
+ });
1158
+ }
1159
+ }
1160
+ else {
1161
+ return {};
1162
+ }
1163
+ }
1164
+ // want to do some surgery here, need to consider any side-effects.
1165
+ // specifically, to support hyperlinks, I want to (1) do the text
1166
+ // calculation before calling the cell's render_function (so we can figure
1167
+ // out layout); and (2) let the render function indicate that it does not
1168
+ // want to exit, i.e. it's only a prerender for calc purposes.
1169
+ // although that layout calc won't be good enough to account for things
1170
+ // like overflow... also here we are just splitting the string, not
1171
+ // generating text boxes (think about justification, wrap)
1172
+ // doing this a little differently... render function can pass but can
1173
+ // also ask us to preserve layout (text rectangles)
1174
+ // let preserve_layout_info = false;
1175
+ // let renderer_title: string|undefined;
1176
+ // let override_text: string|undefined;
1177
+ // ...updating...
1178
+ const preserve_layout_info = !!cell.hyperlink;
1179
+ if (cell.render_function) {
1180
+ this.RenderCellBackground(!!cell.note, address, context, style, width, height);
1181
+ context.strokeStyle = context.fillStyle = ResolveThemeColor(this.theme, style.text, 1);
1182
+ // there's an issue with theme colors, the function may not be able
1183
+ // to translate so we need to update the style (using a copy) to
1184
+ // resolve colors
1185
+ const apply_style = this.ResolveColors(style);
1186
+ const render_result = cell.render_function.call(undefined, {
1187
+ width, height, context, cell, style: apply_style, scale: this.layout.scale || 1,
1188
+ });
1189
+ if (render_result.handled) {
1190
+ return result;
1191
+ }
1192
+ /*
1193
+ if (render_result.metrics) {
1194
+ preserve_layout_info = true;
1195
+ }
1196
+
1197
+ if (render_result.title) {
1198
+ renderer_title = render_result.title;
1199
+ }
1200
+
1201
+ if (typeof render_result.override_text !== 'undefined') {
1202
+ override_text = render_result.override_text;
1203
+ }
1204
+ */
1205
+ }
1206
+ // if there's no context, we just need to render the background
1207
+ // and border; but it still might be overflowed (via merge)
1208
+ /*
1209
+
1210
+ this is breaking rendering. not sure if it is because of buffering
1211
+ (that doesn't work, below) or because of overflow, but in any event
1212
+ it doesn't work. we should fix, or at least jump over any font stuff
1213
+ below.
1214
+
1215
+ I suspect it was written at an earlier iteration of the overall render
1216
+ routine, and then got out of sync.
1217
+
1218
+ TODO/FIXME
1219
+
1220
+ if (!cell.formatted) {
1221
+ this.RenderCellBackground(
1222
+ !!cell.note,
1223
+ address,
1224
+ (result.tile_overflow_bottom || result.tile_overflow_right) ?
1225
+ this.buffer_context : context, style, width, height);
1226
+ return result;
1227
+ }
1228
+
1229
+ */
1230
+ // NOTE: this is OK to do in the original context, even if we're
1231
+ // (eventually) painting to the buffer context. just remember to set
1232
+ // font in the buffer context.
1233
+ const base_font = Style.CompositeFont(this.theme.grid_cell_font_size, style, this.layout.scale, this.theme);
1234
+ /*
1235
+ if (cell.value) {
1236
+ console.info(base_font);
1237
+ }
1238
+ */
1239
+ const fonts = {
1240
+ base: base_font.font,
1241
+ strong: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, bold: true }, this.layout.scale, this.theme).font,
1242
+ emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, italic: true }, this.layout.scale, this.theme).font,
1243
+ strong_emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, bold: true, italic: true }, this.layout.scale, this.theme).font,
1244
+ };
1245
+ if (base_font.variants) {
1246
+ tile.style.fontVariant = base_font.variants;
1247
+ }
1248
+ else {
1249
+ tile.style.fontVariant = '';
1250
+ }
1251
+ context.font = fonts.base;
1252
+ //
1253
+ // NOTE: we appear to be updating render data on cell size changes
1254
+ // (width/height) to account for line breaks, but that should only
1255
+ // be necessary if the text is wrapped -- correct? maybe we can skip
1256
+ //
1257
+ // (FIXME/TODO)
1258
+ //
1259
+ if (dirty || !cell.renderer_data || cell.renderer_data.width !== width || cell.renderer_data.height !== height) {
1260
+ const text_data = this.PrepText(context, fonts, cell, width);
1261
+ cell.renderer_data = {
1262
+ text_data,
1263
+ width,
1264
+ height,
1265
+ };
1266
+ }
1267
+ const text_data = cell.renderer_data.text_data;
1268
+ // overflow is always a huge headache. here are the basic rules:
1269
+ // (1) only strings can overflow. numbers get ### treatment.
1270
+ // (2) wrapped and merged cells cannot overflow.
1271
+ // (3) overflow is horizontal only.
1272
+ // (4) overflow can extend indefinitely.
1273
+ // Q: what about DQ?
1274
+ const overflow = text_data.width > (width - 2 * this.cell_edge_buffer);
1275
+ let paint_right = width;
1276
+ let paint_left = 0;
1277
+ let clip = false;
1278
+ const is_number = (cell.type === ValueType.number ||
1279
+ cell.calculated_type === ValueType.number ||
1280
+ cell.type === ValueType.complex ||
1281
+ cell.calculated_type === ValueType.complex ||
1282
+ cell.type === ValueType.dimensioned_quantity ||
1283
+ cell.calculated_type === ValueType.dimensioned_quantity);
1284
+ let horizontal_align = style.horizontal_align;
1285
+ if (!horizontal_align) {
1286
+ horizontal_align = is_number ? 'right' : 'left'; // Style.HorizontalAlign.Right : Style.HorizontalAlign.Left;
1287
+ }
1288
+ // NOTE: text rendering options (align, baseline) are set globally
1289
+ // when the tile is created, so we don't need to set them repeatedly here.
1290
+ // we cache some data for drawing backgrounds under overflows, if necessary,
1291
+ // so we can do draw calls after we figure out if we need to buffer or not
1292
+ // UPDATE: we have a case where there's a super-long string trying to
1293
+ // render/overflow, and it's breaking everything. we need to address some
1294
+ // caps/limits. WIP.
1295
+ const overflow_backgrounds = [];
1296
+ if (overflow) {
1297
+ const can_overflow = (cell.type !== ValueType.number &&
1298
+ cell.calculated_type !== ValueType.number &&
1299
+ !style.wrap &&
1300
+ !cell.merge_area);
1301
+ if (can_overflow) {
1302
+ // check how far we want to overflow left and right (pixels)
1303
+ // FIXME: should be (buffer * 2), no?
1304
+ const delta = text_data.width - width + this.cell_edge_buffer;
1305
+ let overflow_pixels_left = 0;
1306
+ let overflow_pixels_right = 0;
1307
+ if (horizontal_align === 'center' /* Style.HorizontalAlign.Center */) {
1308
+ overflow_pixels_left = overflow_pixels_right = delta / 2;
1309
+ }
1310
+ else if (horizontal_align === 'right' /* Style.HorizontalAlign.Right */) {
1311
+ overflow_pixels_left = delta;
1312
+ }
1313
+ else {
1314
+ overflow_pixels_right = delta;
1315
+ }
1316
+ // calculate overflow into adjacent columns
1317
+ let overflow_right_column = address.column;
1318
+ let overflow_left_column = address.column;
1319
+ // cap at max. use actual max, not sheet max (which reflects the
1320
+ // extent of spreadsheet data, but not visible cells).
1321
+ while (overflow_pixels_right > 0 && overflow_right_column < this.layout.last_column) {
1322
+ overflow_right_column++;
1323
+ const target_address = { row: address.row, column: overflow_right_column };
1324
+ const target_cell = this.view.active_sheet.CellData(target_address);
1325
+ const target_width = this.layout.ColumnWidth(overflow_right_column);
1326
+ overflow_pixels_right -= target_width;
1327
+ if (target_cell && !target_cell.type && !target_cell.calculated_type) {
1328
+ overflow_backgrounds.push({
1329
+ address: target_address,
1330
+ cell: target_cell,
1331
+ grid: new Rectangle(paint_right, 0, target_width, height),
1332
+ background: new Rectangle(paint_right - 1, 0, target_width, height - 1),
1333
+ border: new Rectangle(paint_right, 0, target_width, height),
1334
+ });
1335
+ paint_right += target_width;
1336
+ // set render data for cells we are going to overflow into;
1337
+ // that will keep them from getting painted. we only need to
1338
+ // do that on the right side.
1339
+ target_cell.render_clean[this.view.view_index] = true;
1340
+ target_cell.renderer_data = {
1341
+ overflowed: true,
1342
+ };
1343
+ }
1344
+ else {
1345
+ // we actually don't have to clip to the right, assuming
1346
+ // we're going to paint the cells anyway... right?
1347
+ // A: not necessarily, because we might not be painting the cell _now_.
1348
+ clip = true; // need to clip
1349
+ break;
1350
+ }
1351
+ }
1352
+ if (overflow_right_column > tile.last_cell.column) {
1353
+ result.tile_overflow_right = true;
1354
+ }
1355
+ while (overflow_pixels_left > 0 && overflow_left_column >= 1) {
1356
+ overflow_left_column--;
1357
+ const target_address = { row: address.row, column: overflow_left_column };
1358
+ const target_cell = this.view.active_sheet.CellData(target_address);
1359
+ const target_width = this.layout.ColumnWidth(overflow_left_column);
1360
+ overflow_pixels_left -= target_width;
1361
+ if (target_cell && !target_cell.type && !target_cell.calculated_type) {
1362
+ paint_left -= target_width;
1363
+ overflow_backgrounds.push({
1364
+ address: target_address,
1365
+ cell: target_cell,
1366
+ grid: new Rectangle(paint_left, 0, target_width, height),
1367
+ background: new Rectangle(paint_left, 0, target_width, height - 1),
1368
+ border: new Rectangle(paint_left, 0, target_width, height),
1369
+ });
1370
+ }
1371
+ else {
1372
+ clip = true; // need to clip
1373
+ break;
1374
+ }
1375
+ }
1376
+ if (overflow_left_column < tile.first_cell.column) {
1377
+ result.tile_overflow_left = true;
1378
+ }
1379
+ // push overflow onto the list
1380
+ this.overflow_areas.push({
1381
+ head: { ...address }, tile, area: new Area({ row: address.row, column: overflow_left_column }, { row: address.row, column: overflow_right_column })
1382
+ });
1383
+ }
1384
+ else {
1385
+ // don't clip numbers, we are going to ### them
1386
+ clip = !is_number; // (cell.type !== ValueType.number && cell.calculated_type !== ValueType.number);
1387
+ }
1388
+ }
1389
+ let buffering = false;
1390
+ // now we can render into either the primary context or the buffer
1391
+ // context. note we don't have to clip for buffered contexts, as we're
1392
+ // going to copy.
1393
+ const original_context = context;
1394
+ if (result.tile_overflow_bottom || result.tile_overflow_left || result.tile_overflow_right) {
1395
+ buffering = true;
1396
+ // console.info("buffering", result, {paint_left, paint_right, text_data});
1397
+ result.width = paint_right - paint_left;
1398
+ result.height = height;
1399
+ result.left = paint_left;
1400
+ this.EnsureBuffer(result.width + 1, height + 1, -paint_left);
1401
+ context = this.buffer_context;
1402
+ context.font = fonts.base;
1403
+ }
1404
+ this.RenderCellBackground(!!cell.note, address, context, style, width, height, cell_left, cell_top);
1405
+ // Q: why are we doing this inline instead of using the background method?
1406
+ for (const element of overflow_backgrounds) {
1407
+ if (element.cell.style?.fill &&
1408
+ IsDefinedColor(element.cell.style.fill) &&
1409
+ // (element.cell.style.fill.text || element.cell.style.fill.theme || element.cell.style.fill.theme === 0) &&
1410
+ !this.options.grid_over_background) {
1411
+ context.fillStyle = ResolveThemeColor(this.theme, element.cell.style.fill, 0);
1412
+ context.fillRect(element.grid.left, element.grid.top, element.grid.width, element.grid.height);
1413
+ }
1414
+ else {
1415
+ context.fillStyle = this.theme.grid_color || '';
1416
+ context.fillRect(element.grid.left, element.grid.top, element.grid.width, element.grid.height);
1417
+ if (this.view.active_sheet.image) {
1418
+ this.PaintBackgroundImage(context, this.view.active_sheet.image, cell_left + element.background.left, cell_top + element.background.top, element.background.width, element.background.height, element.background.left, element.background.top, 0);
1419
+ }
1420
+ else {
1421
+ context.fillStyle = this.theme.grid_cell?.fill ? ResolveThemeColor(this.theme, this.theme.grid_cell.fill, 0) : '';
1422
+ context.fillRect(element.background.left, element.background.top, element.background.width, element.background.height);
1423
+ }
1424
+ }
1425
+ if (element.cell.style) {
1426
+ this.RenderCellBorders(element.address, context, element.cell.style, element.border.left, element.border.top, element.border.width, element.border.height);
1427
+ }
1428
+ }
1429
+ if (style.databar && height > 7 && width > 9) { // FIXME: buffers
1430
+ const db = style.databar;
1431
+ if (db) {
1432
+ const margin = { x: 4, y: 3 }; // FIXME: parameterize, move out of this function/block
1433
+ context.fillStyle = ResolveThemeColor(this.theme, db.fill);
1434
+ const bar_top = margin.y;
1435
+ const bar_height = height - (margin.y * 2 + 1);
1436
+ let bar_left = margin.x;
1437
+ let bar_width = 0;
1438
+ if (db.zero > 0) {
1439
+ bar_left = bar_left + db.zero * (width - (margin.x * 2 + 1));
1440
+ if (db.value > db.zero) {
1441
+ bar_width = (width - (margin.x * 2 + 1)) * (db.value - db.zero);
1442
+ }
1443
+ else {
1444
+ if (db.negative) {
1445
+ context.fillStyle = ResolveThemeColor(this.theme, db.negative);
1446
+ }
1447
+ bar_width = (width - (margin.x * 2 + 1)) * (db.zero - db.value);
1448
+ bar_left -= bar_width;
1449
+ }
1450
+ }
1451
+ else {
1452
+ bar_width = (width - (margin.x * 2 + 1)) * db.value;
1453
+ }
1454
+ context.fillRect(bar_left, bar_top, bar_width, bar_height);
1455
+ }
1456
+ }
1457
+ // NOTE: we are getting fontmetrics based on the base font (so ignoring italic
1458
+ // and bold variants). this should be OK because we use it for height, mostly.
1459
+ // not sure about invisible text (FIXME)
1460
+ // const m2 = FontMetricsCache2.Get(fonts.base, // this.theme.grid_cell?.font_size?.value);
1461
+ // this.theme.grid_cell_font_size);
1462
+ const m2 = GetFontMetrics(fonts.base, base_font.variants);
1463
+ /*
1464
+ if (cell.value) {
1465
+ console.info(fonts.base, {m2});
1466
+ }
1467
+ */
1468
+ // set stroke for underline
1469
+ // FIXME: color here should default to style, not ''. it's working only
1470
+ // because our default style happens to be the default color. that applies
1471
+ // to text color, background color and border color.
1472
+ context.lineWidth = 1; // 1.5; // FIXME: scale? font scale?
1473
+ context.strokeStyle = context.fillStyle =
1474
+ text_data.format ? text_data.format : ResolveThemeColor(this.theme, style.text, 1);
1475
+ context.beginPath();
1476
+ let left = this.cell_edge_buffer;
1477
+ const line_height = 1; // 1.25;
1478
+ //const line_count = text_data.single ? 1 : text_data.strings.length;
1479
+ const line_count = text_data.strings.length;
1480
+ // const text_height = (line_count * m2.block * line_height);
1481
+ const text_height = (line_count * m2.height * line_height);
1482
+ // we stopped clipping initially because it was expensive -- but then
1483
+ // we were doing it on every cell. it's hard to imagine that clipping
1484
+ // is more expensive than buffering (painting to a second canvas and
1485
+ // copying). let's test clipping just in the case of unpainted overflow.
1486
+ // don't clip if buffering, it's not necessary
1487
+ clip = (clip || (text_height >= height)) && !buffering;
1488
+ if (clip) {
1489
+ context.save();
1490
+ context.beginPath();
1491
+ context.moveTo(paint_left + 1.5, 0);
1492
+ context.lineTo(paint_left + 1.5, height);
1493
+ context.lineTo(paint_right - 1.5, height);
1494
+ context.lineTo(paint_right - 1.5, 0);
1495
+ context.clip();
1496
+ }
1497
+ // path for underline (and strike). if there's no underline (or strike),
1498
+ // it won't do anything.
1499
+ context.beginPath();
1500
+ let original_baseline = Math.round((height - m2.descent - 2) + // baseline for first line of text
1501
+ (-line_height * m2.height * (line_count - 1))); // adjust for multiple lines
1502
+ switch (style.vertical_align) {
1503
+ case 'top':
1504
+ original_baseline = Math.round(2 + m2.ascent);
1505
+ break;
1506
+ case 'middle':
1507
+ original_baseline = Math.round((height - text_height) / 2 + m2.ascent) - 1.5;
1508
+ break;
1509
+ }
1510
+ if (style.databar?.hide_values) {
1511
+ // ...
1512
+ }
1513
+ else if ((cell.type === ValueType.number ||
1514
+ cell.calculated_type === ValueType.number ||
1515
+ cell.type === ValueType.complex ||
1516
+ cell.calculated_type === ValueType.complex) && overflow) {
1517
+ // number overflow is easy
1518
+ const count = Math.floor((width - 2 * this.cell_edge_buffer) / m2.hash);
1519
+ let text = '';
1520
+ for (let i = 0; i < count; i++) {
1521
+ text += '#';
1522
+ }
1523
+ const text_width = context.measureText(text).width;
1524
+ if (horizontal_align === 'center' /* Style.HorizontalAlign.Center */) {
1525
+ left = Math.round((width - text_width) / 2);
1526
+ }
1527
+ else if (horizontal_align === 'right' /* Style.HorizontalAlign.Right */) {
1528
+ left = width - this.cell_edge_buffer - text_width;
1529
+ }
1530
+ context.fillText(text, left, original_baseline);
1531
+ }
1532
+ else {
1533
+ // unifying the old "single" and "!single" branches. now the data is
1534
+ // an array of rows, each of which is an array of elements. elements
1535
+ // may have different formatting.
1536
+ let baseline = original_baseline;
1537
+ // let index = 0;
1538
+ for (const line of text_data.strings) {
1539
+ // FIXME: cache line width
1540
+ let line_width = 0;
1541
+ for (const part of line) {
1542
+ line_width += part.width;
1543
+ }
1544
+ if (horizontal_align === 'center') {
1545
+ left = Math.round((width - line_width) / 2);
1546
+ }
1547
+ else if (horizontal_align === 'right') {
1548
+ left = width - this.cell_edge_buffer - line_width;
1549
+ }
1550
+ // still tinkering with these. surely we need to apply scale? FIXME
1551
+ const underline_y = baseline + 2.5;
1552
+ const strike_y = Math.floor(baseline - m2.ascent * 1 / 3) + .5;
1553
+ let x = left;
1554
+ for (const part of line) {
1555
+ if (part.strong && part.emphasis) {
1556
+ context.font = fonts.strong_emphasis;
1557
+ }
1558
+ else if (part.strong) {
1559
+ context.font = fonts.strong;
1560
+ }
1561
+ else if (part.emphasis) {
1562
+ context.font = fonts.emphasis;
1563
+ }
1564
+ else {
1565
+ context.font = fonts.base;
1566
+ }
1567
+ if (!part.hidden) {
1568
+ if (part.text) {
1569
+ // console.info({text: part.text, x, baseline, clip, buffering, text_data});
1570
+ context.fillText(part.text, x, baseline);
1571
+ }
1572
+ if (style.underline && !part.indent) {
1573
+ context.moveTo(x, underline_y);
1574
+ context.lineTo(x + part.width, underline_y);
1575
+ }
1576
+ if ((style.strike || part.strike) && !part.indent) {
1577
+ context.moveTo(x, strike_y);
1578
+ context.lineTo(x + part.width, strike_y);
1579
+ }
1580
+ // we're putting this inside the test block so
1581
+ // that you can't click on hidden text. not sure
1582
+ // if it works, though.
1583
+ if (preserve_layout_info) {
1584
+ part.left = x;
1585
+ part.top = baseline - // m2.block;
1586
+ m2.ascent;
1587
+ part.height = // m2.block;
1588
+ m2.height;
1589
+ }
1590
+ }
1591
+ x += part.width;
1592
+ }
1593
+ // height tends to be an integer, or maybe at worst 1/2 integer,
1594
+ // so stepping is probably ok (we used to index and multiply).
1595
+ baseline += line_height * m2.height;
1596
+ }
1597
+ }
1598
+ context.stroke();
1599
+ if (clip) {
1600
+ context.restore();
1601
+ }
1602
+ else if (buffering) {
1603
+ const scale = this.layout.dpr;
1604
+ original_context.drawImage(this.buffer_canvas, 0, 0, (result.width || 0) * scale, height * scale, paint_left, 0, result.width || 0, height);
1605
+ }
1606
+ return result;
1607
+ }
1608
+ }
1609
+ //# sourceMappingURL=tile_renderer.js.map