apexify.js 5.2.2 → 5.2.6

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 (237) hide show
  1. package/CHANGELOG.md +173 -4
  2. package/README.md +66 -0
  3. package/dist/cjs/Canvas/ApexPainter.d.ts +191 -4
  4. package/dist/cjs/Canvas/ApexPainter.d.ts.map +1 -1
  5. package/dist/cjs/Canvas/ApexPainter.js +201 -96
  6. package/dist/cjs/Canvas/ApexPainter.js.map +1 -1
  7. package/dist/cjs/Canvas/extended/CanvasCreator.d.ts.map +1 -1
  8. package/dist/cjs/Canvas/extended/CanvasCreator.js +3 -18
  9. package/dist/cjs/Canvas/extended/CanvasCreator.js.map +1 -1
  10. package/dist/cjs/Canvas/extended/GIFCreator.d.ts +15 -3
  11. package/dist/cjs/Canvas/extended/GIFCreator.d.ts.map +1 -1
  12. package/dist/cjs/Canvas/extended/GIFCreator.js +107 -9
  13. package/dist/cjs/Canvas/extended/GIFCreator.js.map +1 -1
  14. package/dist/cjs/Canvas/extended/HitDetectionCreator.d.ts +148 -0
  15. package/dist/cjs/Canvas/extended/HitDetectionCreator.d.ts.map +1 -0
  16. package/dist/cjs/Canvas/extended/HitDetectionCreator.js +294 -0
  17. package/dist/cjs/Canvas/extended/HitDetectionCreator.js.map +1 -0
  18. package/dist/cjs/Canvas/extended/ImageCreator.d.ts +3 -2
  19. package/dist/cjs/Canvas/extended/ImageCreator.d.ts.map +1 -1
  20. package/dist/cjs/Canvas/extended/ImageCreator.js +43 -4
  21. package/dist/cjs/Canvas/extended/ImageCreator.js.map +1 -1
  22. package/dist/cjs/Canvas/extended/Path2DCreator.d.ts +166 -0
  23. package/dist/cjs/Canvas/extended/Path2DCreator.d.ts.map +1 -0
  24. package/dist/cjs/Canvas/extended/Path2DCreator.js +246 -0
  25. package/dist/cjs/Canvas/extended/Path2DCreator.js.map +1 -0
  26. package/dist/cjs/Canvas/extended/PixelDataCreator.d.ts +69 -0
  27. package/dist/cjs/Canvas/extended/PixelDataCreator.d.ts.map +1 -0
  28. package/dist/cjs/Canvas/extended/PixelDataCreator.js +232 -0
  29. package/dist/cjs/Canvas/extended/PixelDataCreator.js.map +1 -0
  30. package/dist/cjs/Canvas/extended/TextCreator.d.ts.map +1 -1
  31. package/dist/cjs/Canvas/extended/TextCreator.js +0 -7
  32. package/dist/cjs/Canvas/extended/TextCreator.js.map +1 -1
  33. package/dist/cjs/Canvas/extended/TextMetricsCreator.d.ts +18 -0
  34. package/dist/cjs/Canvas/extended/TextMetricsCreator.d.ts.map +1 -0
  35. package/dist/cjs/Canvas/extended/TextMetricsCreator.js +194 -0
  36. package/dist/cjs/Canvas/extended/TextMetricsCreator.js.map +1 -0
  37. package/dist/cjs/Canvas/extended/VideoCreator.d.ts.map +1 -1
  38. package/dist/cjs/Canvas/extended/VideoCreator.js +1 -42
  39. package/dist/cjs/Canvas/extended/VideoCreator.js.map +1 -1
  40. package/dist/cjs/Canvas/utils/Background/bg.d.ts.map +1 -1
  41. package/dist/cjs/Canvas/utils/Background/bg.js +2 -15
  42. package/dist/cjs/Canvas/utils/Background/bg.js.map +1 -1
  43. package/dist/cjs/Canvas/utils/Charts/barchart.d.ts.map +1 -1
  44. package/dist/cjs/Canvas/utils/Charts/barchart.js +32 -277
  45. package/dist/cjs/Canvas/utils/Charts/barchart.js.map +1 -1
  46. package/dist/cjs/Canvas/utils/Charts/comparisonchart.d.ts.map +1 -1
  47. package/dist/cjs/Canvas/utils/Charts/comparisonchart.js +0 -41
  48. package/dist/cjs/Canvas/utils/Charts/comparisonchart.js.map +1 -1
  49. package/dist/cjs/Canvas/utils/Charts/horizontalbarchart.d.ts.map +1 -1
  50. package/dist/cjs/Canvas/utils/Charts/horizontalbarchart.js +42 -241
  51. package/dist/cjs/Canvas/utils/Charts/horizontalbarchart.js.map +1 -1
  52. package/dist/cjs/Canvas/utils/Charts/linechart.d.ts.map +1 -1
  53. package/dist/cjs/Canvas/utils/Charts/linechart.js +16 -148
  54. package/dist/cjs/Canvas/utils/Charts/linechart.js.map +1 -1
  55. package/dist/cjs/Canvas/utils/Charts/piechart.d.ts.map +1 -1
  56. package/dist/cjs/Canvas/utils/Charts/piechart.js +21 -117
  57. package/dist/cjs/Canvas/utils/Charts/piechart.js.map +1 -1
  58. package/dist/cjs/Canvas/utils/Custom/advancedLines.d.ts.map +1 -1
  59. package/dist/cjs/Canvas/utils/Custom/advancedLines.js +3 -16
  60. package/dist/cjs/Canvas/utils/Custom/advancedLines.js.map +1 -1
  61. package/dist/cjs/Canvas/utils/Custom/customLines.d.ts.map +1 -1
  62. package/dist/cjs/Canvas/utils/Custom/customLines.js +0 -7
  63. package/dist/cjs/Canvas/utils/Custom/customLines.js.map +1 -1
  64. package/dist/cjs/Canvas/utils/General/batchOperations.d.ts.map +1 -1
  65. package/dist/cjs/Canvas/utils/General/batchOperations.js +0 -4
  66. package/dist/cjs/Canvas/utils/General/batchOperations.js.map +1 -1
  67. package/dist/cjs/Canvas/utils/General/conversion.d.ts.map +1 -1
  68. package/dist/cjs/Canvas/utils/General/conversion.js.map +1 -1
  69. package/dist/cjs/Canvas/utils/General/general functions.d.ts.map +1 -1
  70. package/dist/cjs/Canvas/utils/General/general functions.js +0 -19
  71. package/dist/cjs/Canvas/utils/General/general functions.js.map +1 -1
  72. package/dist/cjs/Canvas/utils/General/imageCompression.d.ts.map +1 -1
  73. package/dist/cjs/Canvas/utils/General/imageCompression.js +0 -18
  74. package/dist/cjs/Canvas/utils/General/imageCompression.js.map +1 -1
  75. package/dist/cjs/Canvas/utils/General/imageStitching.d.ts.map +1 -1
  76. package/dist/cjs/Canvas/utils/General/imageStitching.js +0 -14
  77. package/dist/cjs/Canvas/utils/General/imageStitching.js.map +1 -1
  78. package/dist/cjs/Canvas/utils/Image/imageEffects.d.ts.map +1 -1
  79. package/dist/cjs/Canvas/utils/Image/imageEffects.js +8 -16
  80. package/dist/cjs/Canvas/utils/Image/imageEffects.js.map +1 -1
  81. package/dist/cjs/Canvas/utils/Image/imageFilters.js +12 -30
  82. package/dist/cjs/Canvas/utils/Image/imageFilters.js.map +1 -1
  83. package/dist/cjs/Canvas/utils/Image/imageMasking.d.ts.map +1 -1
  84. package/dist/cjs/Canvas/utils/Image/imageMasking.js +0 -22
  85. package/dist/cjs/Canvas/utils/Image/imageMasking.js.map +1 -1
  86. package/dist/cjs/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
  87. package/dist/cjs/Canvas/utils/Image/imageProperties.js +2 -37
  88. package/dist/cjs/Canvas/utils/Image/imageProperties.js.map +1 -1
  89. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -1
  90. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js +6 -23
  91. package/dist/cjs/Canvas/utils/Image/professionalImageFilters.js.map +1 -1
  92. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -1
  93. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js +7 -16
  94. package/dist/cjs/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -1
  95. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -1
  96. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js +1 -16
  97. package/dist/cjs/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -1
  98. package/dist/cjs/Canvas/utils/Shapes/shapes.d.ts.map +1 -1
  99. package/dist/cjs/Canvas/utils/Shapes/shapes.js +120 -39
  100. package/dist/cjs/Canvas/utils/Shapes/shapes.js.map +1 -1
  101. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -1
  102. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js +5 -58
  103. package/dist/cjs/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -1
  104. package/dist/cjs/Canvas/utils/Texts/textPathRenderer.d.ts.map +1 -1
  105. package/dist/cjs/Canvas/utils/Texts/textPathRenderer.js +1 -4
  106. package/dist/cjs/Canvas/utils/Texts/textPathRenderer.js.map +1 -1
  107. package/dist/cjs/Canvas/utils/Texts/textProperties.d.ts.map +1 -1
  108. package/dist/cjs/Canvas/utils/Texts/textProperties.js +0 -18
  109. package/dist/cjs/Canvas/utils/Texts/textProperties.js.map +1 -1
  110. package/dist/cjs/Canvas/utils/Video/videoHelpers.d.ts.map +1 -1
  111. package/dist/cjs/Canvas/utils/Video/videoHelpers.js +9 -110
  112. package/dist/cjs/Canvas/utils/Video/videoHelpers.js.map +1 -1
  113. package/dist/cjs/Canvas/utils/types.d.ts +136 -1
  114. package/dist/cjs/Canvas/utils/types.d.ts.map +1 -1
  115. package/dist/cjs/Canvas/utils/types.js.map +1 -1
  116. package/dist/cjs/Canvas/utils/utils.d.ts +4 -2
  117. package/dist/cjs/Canvas/utils/utils.d.ts.map +1 -1
  118. package/dist/cjs/Canvas/utils/utils.js.map +1 -1
  119. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  120. package/dist/esm/Canvas/ApexPainter.d.ts +191 -4
  121. package/dist/esm/Canvas/ApexPainter.d.ts.map +1 -1
  122. package/dist/esm/Canvas/ApexPainter.js +201 -96
  123. package/dist/esm/Canvas/ApexPainter.js.map +1 -1
  124. package/dist/esm/Canvas/extended/CanvasCreator.d.ts.map +1 -1
  125. package/dist/esm/Canvas/extended/CanvasCreator.js +3 -18
  126. package/dist/esm/Canvas/extended/CanvasCreator.js.map +1 -1
  127. package/dist/esm/Canvas/extended/GIFCreator.d.ts +15 -3
  128. package/dist/esm/Canvas/extended/GIFCreator.d.ts.map +1 -1
  129. package/dist/esm/Canvas/extended/GIFCreator.js +107 -9
  130. package/dist/esm/Canvas/extended/GIFCreator.js.map +1 -1
  131. package/dist/esm/Canvas/extended/HitDetectionCreator.d.ts +148 -0
  132. package/dist/esm/Canvas/extended/HitDetectionCreator.d.ts.map +1 -0
  133. package/dist/esm/Canvas/extended/HitDetectionCreator.js +294 -0
  134. package/dist/esm/Canvas/extended/HitDetectionCreator.js.map +1 -0
  135. package/dist/esm/Canvas/extended/ImageCreator.d.ts +3 -2
  136. package/dist/esm/Canvas/extended/ImageCreator.d.ts.map +1 -1
  137. package/dist/esm/Canvas/extended/ImageCreator.js +43 -4
  138. package/dist/esm/Canvas/extended/ImageCreator.js.map +1 -1
  139. package/dist/esm/Canvas/extended/Path2DCreator.d.ts +166 -0
  140. package/dist/esm/Canvas/extended/Path2DCreator.d.ts.map +1 -0
  141. package/dist/esm/Canvas/extended/Path2DCreator.js +246 -0
  142. package/dist/esm/Canvas/extended/Path2DCreator.js.map +1 -0
  143. package/dist/esm/Canvas/extended/PixelDataCreator.d.ts +69 -0
  144. package/dist/esm/Canvas/extended/PixelDataCreator.d.ts.map +1 -0
  145. package/dist/esm/Canvas/extended/PixelDataCreator.js +232 -0
  146. package/dist/esm/Canvas/extended/PixelDataCreator.js.map +1 -0
  147. package/dist/esm/Canvas/extended/TextCreator.d.ts.map +1 -1
  148. package/dist/esm/Canvas/extended/TextCreator.js +0 -7
  149. package/dist/esm/Canvas/extended/TextCreator.js.map +1 -1
  150. package/dist/esm/Canvas/extended/TextMetricsCreator.d.ts +18 -0
  151. package/dist/esm/Canvas/extended/TextMetricsCreator.d.ts.map +1 -0
  152. package/dist/esm/Canvas/extended/TextMetricsCreator.js +194 -0
  153. package/dist/esm/Canvas/extended/TextMetricsCreator.js.map +1 -0
  154. package/dist/esm/Canvas/extended/VideoCreator.d.ts.map +1 -1
  155. package/dist/esm/Canvas/extended/VideoCreator.js +1 -42
  156. package/dist/esm/Canvas/extended/VideoCreator.js.map +1 -1
  157. package/dist/esm/Canvas/utils/Background/bg.d.ts.map +1 -1
  158. package/dist/esm/Canvas/utils/Background/bg.js +2 -15
  159. package/dist/esm/Canvas/utils/Background/bg.js.map +1 -1
  160. package/dist/esm/Canvas/utils/Charts/barchart.d.ts.map +1 -1
  161. package/dist/esm/Canvas/utils/Charts/barchart.js +32 -277
  162. package/dist/esm/Canvas/utils/Charts/barchart.js.map +1 -1
  163. package/dist/esm/Canvas/utils/Charts/comparisonchart.d.ts.map +1 -1
  164. package/dist/esm/Canvas/utils/Charts/comparisonchart.js +0 -41
  165. package/dist/esm/Canvas/utils/Charts/comparisonchart.js.map +1 -1
  166. package/dist/esm/Canvas/utils/Charts/horizontalbarchart.d.ts.map +1 -1
  167. package/dist/esm/Canvas/utils/Charts/horizontalbarchart.js +42 -241
  168. package/dist/esm/Canvas/utils/Charts/horizontalbarchart.js.map +1 -1
  169. package/dist/esm/Canvas/utils/Charts/linechart.d.ts.map +1 -1
  170. package/dist/esm/Canvas/utils/Charts/linechart.js +16 -148
  171. package/dist/esm/Canvas/utils/Charts/linechart.js.map +1 -1
  172. package/dist/esm/Canvas/utils/Charts/piechart.d.ts.map +1 -1
  173. package/dist/esm/Canvas/utils/Charts/piechart.js +21 -117
  174. package/dist/esm/Canvas/utils/Charts/piechart.js.map +1 -1
  175. package/dist/esm/Canvas/utils/Custom/advancedLines.d.ts.map +1 -1
  176. package/dist/esm/Canvas/utils/Custom/advancedLines.js +3 -16
  177. package/dist/esm/Canvas/utils/Custom/advancedLines.js.map +1 -1
  178. package/dist/esm/Canvas/utils/Custom/customLines.d.ts.map +1 -1
  179. package/dist/esm/Canvas/utils/Custom/customLines.js +0 -7
  180. package/dist/esm/Canvas/utils/Custom/customLines.js.map +1 -1
  181. package/dist/esm/Canvas/utils/General/batchOperations.d.ts.map +1 -1
  182. package/dist/esm/Canvas/utils/General/batchOperations.js +0 -4
  183. package/dist/esm/Canvas/utils/General/batchOperations.js.map +1 -1
  184. package/dist/esm/Canvas/utils/General/conversion.d.ts.map +1 -1
  185. package/dist/esm/Canvas/utils/General/conversion.js.map +1 -1
  186. package/dist/esm/Canvas/utils/General/general functions.d.ts.map +1 -1
  187. package/dist/esm/Canvas/utils/General/general functions.js +0 -19
  188. package/dist/esm/Canvas/utils/General/general functions.js.map +1 -1
  189. package/dist/esm/Canvas/utils/General/imageCompression.d.ts.map +1 -1
  190. package/dist/esm/Canvas/utils/General/imageCompression.js +0 -18
  191. package/dist/esm/Canvas/utils/General/imageCompression.js.map +1 -1
  192. package/dist/esm/Canvas/utils/General/imageStitching.d.ts.map +1 -1
  193. package/dist/esm/Canvas/utils/General/imageStitching.js +0 -14
  194. package/dist/esm/Canvas/utils/General/imageStitching.js.map +1 -1
  195. package/dist/esm/Canvas/utils/Image/imageEffects.d.ts.map +1 -1
  196. package/dist/esm/Canvas/utils/Image/imageEffects.js +8 -16
  197. package/dist/esm/Canvas/utils/Image/imageEffects.js.map +1 -1
  198. package/dist/esm/Canvas/utils/Image/imageFilters.js +12 -30
  199. package/dist/esm/Canvas/utils/Image/imageFilters.js.map +1 -1
  200. package/dist/esm/Canvas/utils/Image/imageMasking.d.ts.map +1 -1
  201. package/dist/esm/Canvas/utils/Image/imageMasking.js +0 -22
  202. package/dist/esm/Canvas/utils/Image/imageMasking.js.map +1 -1
  203. package/dist/esm/Canvas/utils/Image/imageProperties.d.ts.map +1 -1
  204. package/dist/esm/Canvas/utils/Image/imageProperties.js +2 -37
  205. package/dist/esm/Canvas/utils/Image/imageProperties.js.map +1 -1
  206. package/dist/esm/Canvas/utils/Image/professionalImageFilters.d.ts.map +1 -1
  207. package/dist/esm/Canvas/utils/Image/professionalImageFilters.js +6 -23
  208. package/dist/esm/Canvas/utils/Image/professionalImageFilters.js.map +1 -1
  209. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.d.ts.map +1 -1
  210. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js +7 -16
  211. package/dist/esm/Canvas/utils/Image/simpleProfessionalFilters.js.map +1 -1
  212. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.d.ts.map +1 -1
  213. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js +1 -16
  214. package/dist/esm/Canvas/utils/Patterns/enhancedPatternRenderer.js.map +1 -1
  215. package/dist/esm/Canvas/utils/Shapes/shapes.d.ts.map +1 -1
  216. package/dist/esm/Canvas/utils/Shapes/shapes.js +120 -39
  217. package/dist/esm/Canvas/utils/Shapes/shapes.js.map +1 -1
  218. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.d.ts.map +1 -1
  219. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js +5 -58
  220. package/dist/esm/Canvas/utils/Texts/enhancedTextRenderer.js.map +1 -1
  221. package/dist/esm/Canvas/utils/Texts/textPathRenderer.d.ts.map +1 -1
  222. package/dist/esm/Canvas/utils/Texts/textPathRenderer.js +1 -4
  223. package/dist/esm/Canvas/utils/Texts/textPathRenderer.js.map +1 -1
  224. package/dist/esm/Canvas/utils/Texts/textProperties.d.ts.map +1 -1
  225. package/dist/esm/Canvas/utils/Texts/textProperties.js +0 -18
  226. package/dist/esm/Canvas/utils/Texts/textProperties.js.map +1 -1
  227. package/dist/esm/Canvas/utils/Video/videoHelpers.d.ts.map +1 -1
  228. package/dist/esm/Canvas/utils/Video/videoHelpers.js +9 -110
  229. package/dist/esm/Canvas/utils/Video/videoHelpers.js.map +1 -1
  230. package/dist/esm/Canvas/utils/types.d.ts +136 -1
  231. package/dist/esm/Canvas/utils/types.d.ts.map +1 -1
  232. package/dist/esm/Canvas/utils/types.js.map +1 -1
  233. package/dist/esm/Canvas/utils/utils.d.ts +4 -2
  234. package/dist/esm/Canvas/utils/utils.d.ts.map +1 -1
  235. package/dist/esm/Canvas/utils/utils.js.map +1 -1
  236. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  237. package/package.json +1 -3
@@ -8,7 +8,6 @@ const imageProperties_1 = require("../Image/imageProperties");
8
8
  */
9
9
  async function renderEnhancedText(ctx, text, x, y, style, fontSize, color, textGradient) {
10
10
  ctx.save();
11
- // Preserve text alignment settings
12
11
  const savedTextAlign = ctx.textAlign;
13
12
  const savedTextBaseline = ctx.textBaseline;
14
13
  const effectiveFontSize = fontSize || style?.fontSize || 16;
@@ -18,30 +17,24 @@ async function renderEnhancedText(ctx, text, x, y, style, fontSize, color, textG
18
17
  fontString += 'bold ';
19
18
  if (style?.italic)
20
19
  fontString += 'italic ';
21
- // Use font family without quotes for better rendering (quotes can cause rendering issues)
22
20
  fontString += `${effectiveFontSize}px ${fontFamily}`;
23
21
  ctx.font = fontString;
24
- // Reset letter and word spacing to prevent stretched text
25
22
  ctx.letterSpacing = '0px';
26
23
  ctx.wordSpacing = '0px';
27
- // Restore text alignment to ensure correct positioning
28
24
  ctx.textAlign = savedTextAlign;
29
25
  ctx.textBaseline = savedTextBaseline;
30
- // Register custom font if provided
31
26
  if (style?.fontPath && style?.fontName) {
32
27
  try {
33
28
  const { GlobalFonts } = await import('@napi-rs/canvas');
34
29
  const path = await import('path');
35
30
  const fullPath = path.join(process.cwd(), style.fontPath);
36
31
  GlobalFonts.registerFromPath(fullPath, style.fontName);
37
- // Replace font family in font string (handle both quoted and unquoted formats)
38
32
  ctx.font = fontString.replace(fontFamily, style.fontName);
39
33
  }
40
34
  catch (error) {
41
35
  console.warn(`Failed to register font: ${style.fontPath}`, error);
42
36
  }
43
37
  }
44
- // Apply shadow
45
38
  if (style?.shadow) {
46
39
  ctx.shadowColor = style.shadow.color || 'rgba(0,0,0,0.5)';
47
40
  ctx.shadowOffsetX = style.shadow.offsetX || 2;
@@ -51,7 +44,6 @@ async function renderEnhancedText(ctx, text, x, y, style, fontSize, color, textG
51
44
  ctx.globalAlpha = style.shadow.opacity;
52
45
  }
53
46
  }
54
- // Set fill style (gradient or color)
55
47
  if (textGradient) {
56
48
  const metrics = ctx.measureText(text);
57
49
  ctx.fillStyle = (0, imageProperties_1.createGradientFill)(ctx, textGradient, {
@@ -61,9 +53,7 @@ async function renderEnhancedText(ctx, text, x, y, style, fontSize, color, textG
61
53
  else if (color) {
62
54
  ctx.fillStyle = color;
63
55
  }
64
- // Draw text
65
56
  ctx.fillText(text, x, y);
66
- // Apply stroke
67
57
  if (style?.stroke) {
68
58
  ctx.strokeStyle = style.stroke.color || '#000000';
69
59
  ctx.lineWidth = style.stroke.width || 1;
@@ -75,7 +65,6 @@ async function renderEnhancedText(ctx, text, x, y, style, fontSize, color, textG
75
65
  }
76
66
  ctx.strokeText(text, x, y);
77
67
  }
78
- // Reset shadow and alpha
79
68
  ctx.shadowColor = 'transparent';
80
69
  ctx.shadowOffsetX = 0;
81
70
  ctx.shadowOffsetY = 0;
@@ -120,18 +109,15 @@ function drawXAxisTicks(ctx, originX, originY, axisEndX, minValue, maxValue, ste
120
109
  ctx.textBaseline = 'top';
121
110
  const chartWidth = axisEndX - originX;
122
111
  if (customValues && customValues.length > 0) {
123
- // Position labels based on their actual values, not pixel spacing
124
112
  const actualMin = Math.min(...customValues);
125
113
  const actualMax = Math.max(...customValues);
126
- const range = actualMax - actualMin || 1; // Avoid division by zero
114
+ const range = actualMax - actualMin || 1;
127
115
  let lastLabelX = -Infinity;
128
- const minLabelSpacing = valueSpacing && valueSpacing > 0 ? valueSpacing : 40; // Use valueSpacing as min spacing if provided
116
+ const minLabelSpacing = valueSpacing && valueSpacing > 0 ? valueSpacing : 40;
129
117
  customValues.forEach((value) => {
130
118
  const x = originX + ((value - actualMin) / range) * chartWidth;
131
119
  const labelText = value.toString();
132
- // Check if this label would overlap with the previous one
133
120
  if (x - lastLabelX < minLabelSpacing && value > actualMin) {
134
- // Skip this label to prevent overlap (but still draw tick mark)
135
121
  ctx.beginPath();
136
122
  ctx.moveTo(x, originY);
137
123
  ctx.lineTo(x, originY + 5);
@@ -142,45 +128,36 @@ function drawXAxisTicks(ctx, originX, originY, axisEndX, minValue, maxValue, ste
142
128
  ctx.moveTo(x, originY);
143
129
  ctx.lineTo(x, originY + 5);
144
130
  ctx.stroke();
145
- // Measure label to ensure it doesn't overflow, add minimum 2px spacing
146
131
  const labelMetrics = ctx.measureText(labelText);
147
- const labelY = originY + 10; // 10px spacing from axis line
132
+ const labelY = originY + 10;
148
133
  ctx.fillText(labelText, x, labelY);
149
- lastLabelX = x; // Update last label center position
134
+ lastLabelX = x;
150
135
  });
151
136
  }
152
137
  else {
153
- // Range-based positioning - always position based on values, use valueSpacing only for label density
154
- const range = maxValue - minValue || 1; // Avoid division by zero
155
- // Calculate all tick positions first
138
+ const range = maxValue - minValue || 1;
156
139
  const tickValues = [];
157
140
  for (let value = minValue; value <= maxValue; value += step) {
158
141
  tickValues.push(value);
159
142
  }
160
- // Draw ticks, but skip labels if they're too close together
161
143
  let lastLabelX = -Infinity;
162
- const minLabelSpacing = valueSpacing && valueSpacing > 0 ? valueSpacing : 40; // Use valueSpacing as min spacing if provided
144
+ const minLabelSpacing = valueSpacing && valueSpacing > 0 ? valueSpacing : 40;
163
145
  for (const value of tickValues) {
164
146
  const x = originX + ((value - minValue) / range) * chartWidth;
165
147
  const labelText = value.toString();
166
- // Check if this label center is too close to the previous label center
167
148
  if (x - lastLabelX < minLabelSpacing && value > minValue) {
168
- // Skip this label to prevent overlap - but still draw the tick mark
169
149
  ctx.beginPath();
170
150
  ctx.moveTo(x, originY);
171
151
  ctx.lineTo(x, originY + 5);
172
152
  ctx.stroke();
173
153
  continue;
174
154
  }
175
- // Draw tick mark
176
155
  ctx.beginPath();
177
156
  ctx.moveTo(x, originY);
178
157
  ctx.lineTo(x, originY + 5);
179
158
  ctx.stroke();
180
- // Draw label with minimum 2px spacing
181
- const labelY = originY + 10; // 10px spacing from axis line
159
+ const labelY = originY + 10;
182
160
  ctx.fillText(labelText, x, labelY);
183
- // Update last label position (center of the label)
184
161
  lastLabelX = x;
185
162
  }
186
163
  }
@@ -218,7 +195,6 @@ function drawYAxisTicks(ctx, originX, originY, axisEndY, minValue, maxValue, ste
218
195
  });
219
196
  }
220
197
  else {
221
- // Position based on value range
222
198
  const range = maxValue - minValue || 1;
223
199
  customValues.forEach((value) => {
224
200
  const y = originY - ((value - minValue) / range) * chartHeight;
@@ -231,16 +207,13 @@ function drawYAxisTicks(ctx, originX, originY, axisEndY, minValue, maxValue, ste
231
207
  }
232
208
  }
233
209
  else {
234
- // Range-based positioning
235
210
  const range = maxValue - minValue || 1;
236
211
  let lastLabelY = Infinity;
237
- const minLabelSpacing = valueSpacing && valueSpacing > 0 ? valueSpacing : 30; // Vertical spacing
212
+ const minLabelSpacing = valueSpacing && valueSpacing > 0 ? valueSpacing : 30;
238
213
  for (let value = minValue; value <= maxValue; value += step) {
239
214
  const y = originY - ((value - minValue) / range) * chartHeight;
240
215
  const labelText = value.toString();
241
- // Check if this label would overlap with the previous one
242
216
  if (lastLabelY - y < minLabelSpacing && value > minValue) {
243
- // Skip this label to prevent overlap - but still draw the tick mark
244
217
  ctx.beginPath();
245
218
  ctx.moveTo(originX - 5, y);
246
219
  ctx.lineTo(originX, y);
@@ -252,7 +225,7 @@ function drawYAxisTicks(ctx, originX, originY, axisEndY, minValue, maxValue, ste
252
225
  ctx.lineTo(originX, y);
253
226
  ctx.stroke();
254
227
  ctx.fillText(labelText, originX - 10, y);
255
- lastLabelY = y; // Update last label position
228
+ lastLabelY = y;
256
229
  }
257
230
  }
258
231
  ctx.restore();
@@ -267,7 +240,6 @@ function drawGrid(ctx, originX, originY, axisEndX, axisEndY, xMin, xMax, xStep,
267
240
  ctx.setLineDash([2, 2]);
268
241
  const chartWidth = axisEndX - originX;
269
242
  const chartHeight = originY - axisEndY;
270
- // Draw vertical grid lines (based on X-axis values)
271
243
  if (xAxisCustomValues && xAxisCustomValues.length > 0) {
272
244
  const actualMin = Math.min(...xAxisCustomValues);
273
245
  const actualMax = Math.max(...xAxisCustomValues);
@@ -290,7 +262,6 @@ function drawGrid(ctx, originX, originY, axisEndX, axisEndY, xMin, xMax, xStep,
290
262
  ctx.stroke();
291
263
  }
292
264
  }
293
- // Draw horizontal grid lines (based on Y-axis values/range)
294
265
  if (yAxisCustomValues && yAxisCustomValues.length > 0) {
295
266
  const actualMin = Math.min(...yAxisCustomValues);
296
267
  const actualMax = Math.max(...yAxisCustomValues);
@@ -345,7 +316,6 @@ function calculateLegendDimensions(legend, fontSize, maxWidth, wrapTextEnabled =
345
316
  const boxSize = 15;
346
317
  const spacing = 10;
347
318
  const padding = paddingBox;
348
- // Create a temporary canvas to measure text
349
319
  const tempCanvas = (0, canvas_1.createCanvas)(1, 1);
350
320
  const tempCtx = tempCanvas.getContext('2d');
351
321
  tempCtx.font = `${fontSize}px Arial`;
@@ -384,12 +354,10 @@ async function drawLegendAtPosition(ctx, legend, legendX, legendY, fontSize, bac
384
354
  const spacing = 10;
385
355
  const padding = paddingBox ?? 8;
386
356
  ctx.font = `${fontSize}px Arial`;
387
- // Determine colors
388
357
  const isDarkBackground = backgroundColor === '#000000' || backgroundColor.toLowerCase() === 'black';
389
358
  const effectiveTextColor = textColor ?? (isDarkBackground ? '#FFFFFF' : '#000000');
390
359
  const effectiveBgColor = isDarkBackground ? 'rgba(0, 0, 0, 0.8)' : (backgroundColor.startsWith('rgba') || backgroundColor.startsWith('rgb') ? backgroundColor : 'rgba(255, 255, 255, 0.9)');
391
360
  const effectiveBorderColor = borderColor ?? (isDarkBackground ? '#FFFFFF' : '#000000');
392
- // Calculate dimensions with text wrapping support
393
361
  const textSpacing = 10;
394
362
  const effectiveMaxWidth = maxWidth ? maxWidth - padding * 2 - boxSize - textSpacing : undefined;
395
363
  let maxEntryWidth = 0;
@@ -412,7 +380,6 @@ async function drawLegendAtPosition(ctx, legend, legendX, legendY, fontSize, bac
412
380
  });
413
381
  const legendWidth = maxWidth ? maxWidth : maxEntryWidth + padding * 2;
414
382
  const legendHeight = entryHeights.reduce((sum, h, i) => sum + h + (i < entryHeights.length - 1 ? spacing : 0), 0) + padding * 2;
415
- // Draw legend background (gradient or color)
416
383
  ctx.beginPath();
417
384
  ctx.rect(legendX, legendY, legendWidth, legendHeight);
418
385
  fillWithGradientOrColor(ctx, backgroundGradient, effectiveBgColor, effectiveBgColor, { x: legendX, y: legendY, w: legendWidth, h: legendHeight });
@@ -427,7 +394,6 @@ async function drawLegendAtPosition(ctx, legend, legendX, legendY, fontSize, bac
427
394
  const entry = legend[index];
428
395
  const entryHeight = entryHeights[index];
429
396
  const centerY = currentY + entryHeight / 2;
430
- // Draw color box (gradient or color)
431
397
  ctx.beginPath();
432
398
  ctx.rect(legendX + padding, centerY - boxSize / 2, boxSize, boxSize);
433
399
  fillWithGradientOrColor(ctx, entry.gradient, entry.color || '#4A90E2', '#4A90E2', { x: legendX + padding, y: centerY - boxSize / 2, w: boxSize, h: boxSize });
@@ -522,25 +488,21 @@ function calculateResponsiveHeight(dataLength, options = {}) {
522
488
  const paddingTop = padding.top ?? 60;
523
489
  const paddingBottom = padding.bottom ?? 80;
524
490
  const minBarHeight = options.bars?.minHeight ?? 40;
525
- const barSpacing = options.bars?.spacing ?? 15; // Use same default spacing
526
- // Calculate minimum height needed: (number of bars * bar height) + (spacing between bars)
527
- // Each bar needs minBarHeight, and between each pair of bars we need barSpacing
491
+ const barSpacing = options.bars?.spacing ?? 15;
528
492
  const chartAreaHeight = dataLength * minBarHeight + (dataLength - 1) * barSpacing;
529
- // Add title height if needed (with margin below title)
530
493
  const titleHeight = options.labels?.title?.text ? (options.labels.title.fontSize ?? 24) + 20 : 0;
531
- const titleMargin = options.labels?.title?.text ? 20 : 0; // 20px gap between title and chart
532
- // Calculate x-axis label area height (tick labels + gap + axis label)
494
+ const titleMargin = options.labels?.title?.text ? 20 : 0;
533
495
  const tickFontSize = options.axes?.x?.tickFontSize ?? options.axes?.y?.tickFontSize ?? 12;
534
496
  const axisLabelFontSize = options.labels?.barLabelDefaults?.fontSize ?? 14;
535
497
  let xAxisLabelAreaHeight = 0;
536
498
  if (options.axes?.x?.label || options.axes?.x?.values || options.axes?.x?.range) {
537
- const tickLabelHeight = tickFontSize + 10; // 10px spacing below tick labels
538
- const xAxisLabelTextHeight = options.axes?.x?.label ? axisLabelFontSize + 2 : 0; // 2px minimum gap
539
- xAxisLabelAreaHeight = tickLabelHeight + (options.axes?.x?.label ? 8 : 0) + xAxisLabelTextHeight; // 8px gap between tick labels and axis label
499
+ const tickLabelHeight = tickFontSize + 10;
500
+ const xAxisLabelTextHeight = options.axes?.x?.label ? axisLabelFontSize + 2 : 0;
501
+ xAxisLabelAreaHeight = tickLabelHeight + (options.axes?.x?.label ? 8 : 0) + xAxisLabelTextHeight;
540
502
  }
541
503
  const yAxisLabelHeight = options.axes?.y?.label ? axisLabelFontSize + 20 : 0;
542
504
  const axisLabelHeight = Math.max(xAxisLabelAreaHeight, yAxisLabelHeight);
543
- const minBottomGap = 2; // Minimum 2px gap at bottom
505
+ const minBottomGap = 2;
544
506
  return paddingTop + titleHeight + titleMargin + chartAreaHeight + axisLabelHeight + minBottomGap + paddingBottom;
545
507
  }
546
508
  /**
@@ -550,17 +512,14 @@ function calculateResponsiveHeight(dataLength, options = {}) {
550
512
  * @returns Canvas buffer
551
513
  */
552
514
  async function createHorizontalBarChart(data, options = {}) {
553
- // Extract and map organized config to internal variables
554
515
  let width = options.dimensions?.width ?? 800;
555
516
  const padding = options.dimensions?.padding || {};
556
- // Appearance
557
517
  const backgroundColor = options.appearance?.backgroundColor ?? '#FFFFFF';
558
518
  const backgroundGradient = options.appearance?.backgroundGradient;
559
519
  const backgroundImage = options.appearance?.backgroundImage;
560
520
  const axisColor = options.appearance?.axisColor ?? options.axes?.x?.color ?? options.axes?.y?.color ?? '#000000';
561
521
  const axisWidth = options.appearance?.axisWidth ?? options.axes?.x?.width ?? options.axes?.y?.width ?? 2;
562
522
  const arrowSize = options.appearance?.arrowSize ?? 10;
563
- // Labels
564
523
  const chartTitle = options.labels?.title?.text;
565
524
  const chartTitleFontSize = options.labels?.title?.fontSize ?? 24;
566
525
  const showBarLabels = options.labels?.barLabelDefaults?.show ?? true;
@@ -569,48 +528,41 @@ async function createHorizontalBarChart(data, options = {}) {
569
528
  const showValues = options.labels?.valueLabelDefaults?.show ?? true;
570
529
  const valueFontSize = options.labels?.valueLabelDefaults?.fontSize ?? 12;
571
530
  const valueColor = options.labels?.valueLabelDefaults?.defaultColor ?? '#000000';
572
- // Axes
573
531
  const xAxisLabel = options.axes?.x?.label;
574
532
  const yAxisLabel = options.axes?.y?.label;
575
533
  const axisLabelColor = options.axes?.x?.labelColor ?? options.axes?.y?.labelColor ?? '#000000';
576
534
  const xAxisRange = options.axes?.x?.range;
577
535
  const xAxisValues = options.axes?.x?.values;
578
- const baseline = options.axes?.x?.baseline ?? 0; // Custom baseline value (default: 0)
536
+ const baseline = options.axes?.x?.baseline ?? 0;
579
537
  const yAxisValues = options.axes?.y?.values;
580
538
  const tickFontSize = options.axes?.x?.tickFontSize ?? options.axes?.y?.tickFontSize ?? 12;
581
539
  const xAxisTickColor = options.axes?.x?.tickColor ?? '#000000';
582
540
  const yAxisTickColor = options.axes?.y?.tickColor ?? '#000000';
583
541
  const xAxisValueSpacing = options.axes?.x?.valueSpacing;
584
542
  const yAxisValueSpacing = options.axes?.y?.valueSpacing;
585
- // Legend
586
543
  const showLegend = options.legend?.show ?? false;
587
544
  const legend = options.legend?.entries;
588
- const legendPosition = options.legend?.position ?? 'right'; // Default: right
589
- // Grid
545
+ const legendPosition = options.legend?.position ?? 'right';
590
546
  const showGrid = options.grid?.show ?? false;
591
547
  const gridColor = options.grid?.color ?? '#E0E0E0';
592
548
  const gridWidth = options.grid?.width ?? 1;
593
- // Chart type
594
549
  const chartType = options.type ?? 'standard';
595
- // Bars
596
550
  const minBarHeight = options.bars?.minHeight ?? 30;
597
551
  const barSpacing = options.bars?.spacing;
598
552
  const groupSpacing = options.bars?.groupSpacing ?? 10;
599
553
  const segmentSpacing = options.bars?.segmentSpacing ?? 2;
600
- const lollipopLineWidth = options.bars?.lineWidth ?? 2; // Line width for lollipop charts (default: 2)
601
- const lollipopDotSize = options.bars?.dotSize ?? 8; // Dot/circle size for lollipop charts (default: 8)
554
+ const lollipopLineWidth = options.bars?.lineWidth ?? 2;
555
+ const lollipopDotSize = options.bars?.dotSize ?? 8;
602
556
  const paddingTop = padding.top ?? 60;
603
557
  const paddingRight = padding.right ?? 80;
604
558
  const paddingBottom = padding.bottom ?? 80;
605
559
  const paddingLeft = padding.left ?? 100;
606
- // Calculate responsive height based on number of bars
607
560
  let baseHeight = calculateResponsiveHeight(data.length, options);
608
- // Calculate legend dimensions and adjust canvas size based on legend position
609
561
  let legendWidth = 0;
610
562
  let legendHeight = 0;
611
563
  let extraWidth = 0;
612
564
  let extraHeight = 0;
613
- const minLegendSpacing = 10; // Minimum spacing from chart area
565
+ const minLegendSpacing = 10;
614
566
  if (showLegend && legend && legend.length > 0) {
615
567
  const legendMaxWidth = options.legend?.maxWidth;
616
568
  const legendWrapText = options.legend?.wrapText !== false;
@@ -619,36 +571,27 @@ async function createHorizontalBarChart(data, options = {}) {
619
571
  legendWidth = legendDims.width;
620
572
  legendHeight = legendDims.height;
621
573
  const legendSpacing = options.legend?.spacing ?? 20;
622
- // Adjust canvas dimensions based on legend position
623
- // For left position, add extra space for Y-axis labels and bar labels
624
574
  if (legendPosition === 'left') {
625
- // Estimate Y-axis label width: measure potential category labels or numeric values
626
575
  const tempCanvas = (0, canvas_1.createCanvas)(1, 1);
627
576
  const tempCtx = tempCanvas.getContext('2d');
628
- let estimatedYAxisLabelWidth = 80; // Default estimate (category labels can be longer)
577
+ let estimatedYAxisLabelWidth = 80;
629
578
  if (tempCtx) {
630
- // Check if bar labels are on the left (they act as Y-axis labels)
631
579
  const barLabelFontSize = options.labels?.barLabelDefaults?.fontSize ?? 14;
632
580
  const showBarLabels = options.labels?.barLabelDefaults?.show ?? true;
633
581
  const barLabelPosition = options.labels?.barLabelDefaults?.defaultPosition ?? 'left';
634
582
  const hasLeftLabels = showBarLabels && (barLabelPosition === 'left' ||
635
583
  data.some(item => (item.labelPosition ?? barLabelPosition) === 'left'));
636
584
  if (hasLeftLabels) {
637
- // Measure category labels (bar labels) which are typically longer
638
585
  tempCtx.font = `${barLabelFontSize}px Arial`;
639
586
  data.forEach(d => {
640
587
  const labelWidth = tempCtx.measureText(d.label).width;
641
588
  estimatedYAxisLabelWidth = Math.max(estimatedYAxisLabelWidth, labelWidth);
642
589
  });
643
- // Add padding: 5px (label offset from originX) + 10px (spacing) = 15px total
644
590
  estimatedYAxisLabelWidth += 15;
645
591
  }
646
592
  else {
647
- // No left labels, but might have Y-axis numeric ticks
648
593
  tempCtx.font = `${tickFontSize}px Arial`;
649
- // Estimate for numeric Y-axis values if custom values are provided
650
- estimatedYAxisLabelWidth = 60; // Default for numeric values
651
- // Add padding: 10px (label offset) + 5px (tick) + 15px (spacing) = 30px total
594
+ estimatedYAxisLabelWidth = 60;
652
595
  estimatedYAxisLabelWidth += 30;
653
596
  }
654
597
  }
@@ -661,15 +604,9 @@ async function createHorizontalBarChart(data, options = {}) {
661
604
  extraHeight = legendHeight + legendSpacing + minLegendSpacing;
662
605
  }
663
606
  }
664
- // adjustedWidth and adjustedHeight are already calculated above
665
- // Determine X-axis (value axis) range
666
- // For grouped charts: find max value across all segments
667
- // For stacked charts: find max sum of values per category
668
- // For lollipop charts: same as standard (single value per bar)
669
607
  let allValues = [];
670
608
  if (chartType === 'grouped' || chartType === 'stacked' || chartType === 'lollipop') {
671
609
  if (chartType === 'grouped') {
672
- // For grouped: find max value across all segments
673
610
  data.forEach(d => {
674
611
  if (d.values && d.values.length > 0) {
675
612
  d.values.forEach(seg => allValues.push(seg.value));
@@ -680,7 +617,6 @@ async function createHorizontalBarChart(data, options = {}) {
680
617
  });
681
618
  }
682
619
  else {
683
- // For stacked: find max sum per category
684
620
  data.forEach(d => {
685
621
  if (d.values && d.values.length > 0) {
686
622
  const sum = d.values.reduce((acc, seg) => acc + seg.value, 0);
@@ -693,20 +629,17 @@ async function createHorizontalBarChart(data, options = {}) {
693
629
  }
694
630
  }
695
631
  else {
696
- // Standard chart: use value directly
697
632
  allValues = data.map(d => d.value ?? 0).filter(v => v !== undefined && v !== null);
698
633
  }
699
634
  let xMin, xMax;
700
635
  let xAxisCustomValues = xAxisValues;
701
636
  const hasExplicitXRange = xAxisRange && xAxisRange.min !== undefined && xAxisRange.max !== undefined;
702
- // Check if any bars have xStart/xEnd (value ranges)
703
637
  const hasValueRanges = data.some(d => d.xStart !== undefined || d.xEnd !== undefined);
704
638
  if (hasValueRanges) {
705
639
  const allXStarts = data.map(d => d.xStart ?? d.value ?? 0).filter(v => v !== undefined);
706
640
  const allXEnds = data.map(d => d.xEnd ?? d.value ?? 0).filter(v => v !== undefined);
707
641
  xMin = Math.min(...allXStarts, ...allXEnds);
708
642
  xMax = Math.max(...allXStarts, ...allXEnds);
709
- // Add some padding
710
643
  const xPadding = (xMax - xMin) * 0.1;
711
644
  xMin = Math.max(0, xMin - xPadding);
712
645
  xMax = xMax + xPadding;
@@ -718,7 +651,6 @@ async function createHorizontalBarChart(data, options = {}) {
718
651
  else if (hasExplicitXRange) {
719
652
  xMin = xAxisRange.min;
720
653
  xMax = xAxisRange.max;
721
- // Ensure baseline is within range
722
654
  const effectiveBaseline = baseline !== undefined ? baseline : 0;
723
655
  xMin = Math.min(xMin, effectiveBaseline);
724
656
  xMax = Math.max(xMax, effectiveBaseline);
@@ -728,11 +660,9 @@ async function createHorizontalBarChart(data, options = {}) {
728
660
  xMax = Math.max(...allValues, 1);
729
661
  const xPadding = (xMax - xMin) * 0.1;
730
662
  const effectiveBaseline = baseline !== undefined ? baseline : 0;
731
- // Ensure baseline is always included in the range
732
663
  xMin = Math.min(Math.max(0, xMin - xPadding), effectiveBaseline);
733
664
  xMax = xMax + xPadding;
734
665
  }
735
- // Determine Y-axis (category axis) range - similar to X-axis in standard chart
736
666
  const yAxisRange = options.axes?.y?.range;
737
667
  let yMin, yMax;
738
668
  let yAxisCustomValues = yAxisValues;
@@ -746,22 +676,18 @@ async function createHorizontalBarChart(data, options = {}) {
746
676
  yMax = yAxisRange.max;
747
677
  }
748
678
  else {
749
- // Auto-calculate from data indices (0 to data.length - 1)
750
679
  yMin = 0;
751
680
  yMax = data.length - 1;
752
681
  }
753
- // Validate data values against explicit axis ranges
754
682
  if (hasExplicitXRange || xAxisCustomValues) {
755
683
  const effectiveXMin = xAxisCustomValues ? Math.min(...xAxisCustomValues) : xAxisRange.min;
756
684
  const effectiveXMax = xAxisCustomValues ? Math.max(...xAxisCustomValues) : xAxisRange.max;
757
685
  data.forEach((item, itemIndex) => {
758
- // Check value (X-axis for horizontal bars)
759
686
  if (item.value !== undefined && (item.value < effectiveXMin || item.value > effectiveXMax)) {
760
687
  throw new Error(`Horizontal Bar Chart Error: Data value out of X-axis bounds.\n` +
761
688
  `Bar ${itemIndex} "${item.label || `at index ${itemIndex}`}" has value ${item.value}, ` +
762
689
  `which exceeds the X-axis range [${effectiveXMin}, ${effectiveXMax}].`);
763
690
  }
764
- // Check xStart and xEnd if they exist
765
691
  if (item.xStart !== undefined && (item.xStart < effectiveXMin || item.xStart > effectiveXMax)) {
766
692
  throw new Error(`Horizontal Bar Chart Error: Data value out of X-axis bounds.\n` +
767
693
  `Bar ${itemIndex} "${item.label || `at index ${itemIndex}`}" has xStart value ${item.xStart}, ` +
@@ -772,7 +698,6 @@ async function createHorizontalBarChart(data, options = {}) {
772
698
  `Bar ${itemIndex} "${item.label || `at index ${itemIndex}`}" has xEnd value ${item.xEnd}, ` +
773
699
  `which exceeds the X-axis range [${effectiveXMin}, ${effectiveXMax}].`);
774
700
  }
775
- // Check grouped/stacked values
776
701
  if (item.values && item.values.length > 0) {
777
702
  item.values.forEach((seg, segIndex) => {
778
703
  if (seg.value < effectiveXMin || seg.value > effectiveXMax) {
@@ -788,7 +713,6 @@ async function createHorizontalBarChart(data, options = {}) {
788
713
  const effectiveYMin = yAxisCustomValues ? Math.min(...yAxisCustomValues) : yAxisRange.min;
789
714
  const effectiveYMax = yAxisCustomValues ? Math.max(...yAxisCustomValues) : yAxisRange.max;
790
715
  data.forEach((item, itemIndex) => {
791
- // Check yStart and yEnd (Y-axis for horizontal bars)
792
716
  if (item.yStart !== undefined && (item.yStart < effectiveYMin || item.yStart > effectiveYMax)) {
793
717
  throw new Error(`Horizontal Bar Chart Error: Data value out of Y-axis bounds.\n` +
794
718
  `Bar ${itemIndex} "${item.label || `at index ${itemIndex}`}" has yStart value ${item.yStart}, ` +
@@ -801,14 +725,10 @@ async function createHorizontalBarChart(data, options = {}) {
801
725
  }
802
726
  });
803
727
  }
804
- // Legend dimensions already calculated above, no need to recalculate
805
- // Calculate adjusted dimensions (needed before creating canvas)
806
728
  const adjustedWidth = width + extraWidth;
807
729
  const adjustedHeight = baseHeight + extraHeight;
808
- // Create canvas
809
730
  const canvas = (0, canvas_1.createCanvas)(adjustedWidth, adjustedHeight);
810
731
  const ctx = canvas.getContext('2d');
811
- // Fill background (gradient, image, or color)
812
732
  if (backgroundImage) {
813
733
  try {
814
734
  const bgImage = await (0, canvas_1.loadImage)(backgroundImage);
@@ -816,7 +736,6 @@ async function createHorizontalBarChart(data, options = {}) {
816
736
  }
817
737
  catch (error) {
818
738
  console.warn(`Failed to load background image: ${backgroundImage}`, error);
819
- // Fallback to gradient or color if image fails to load
820
739
  fillWithGradientOrColor(ctx, backgroundGradient, backgroundColor, backgroundColor, {
821
740
  x: 0, y: 0, w: adjustedWidth, h: adjustedHeight
822
741
  });
@@ -829,165 +748,114 @@ async function createHorizontalBarChart(data, options = {}) {
829
748
  });
830
749
  ctx.fillRect(0, 0, adjustedWidth, adjustedHeight);
831
750
  }
832
- // Calculate axis positions with proper spacing
833
- // Title height: fontSize + spacing below title (minimum 20px gap)
834
751
  const titleHeight = chartTitle ? chartTitleFontSize + 20 : 0;
835
- // Calculate actual height needed for x-axis labels (tick labels + axis label)
836
752
  let xAxisLabelAreaHeight = 0;
837
753
  if (xAxisLabel || (xAxisCustomValues && xAxisCustomValues.length > 0) || xAxisRange) {
838
- // Measure tick label height (tickFontSize + spacing)
839
- const tickLabelHeight = tickFontSize + 10; // 10px spacing below tick labels
840
- // Measure x-axis label height if present
841
- const xAxisLabelTextHeight = xAxisLabel ? axisLabelFontSize + 2 : 0; // 2px minimum gap
842
- // Total height needed: tick labels + gap + axis label
843
- xAxisLabelAreaHeight = tickLabelHeight + (xAxisLabel ? 8 : 0) + xAxisLabelTextHeight; // 8px gap between tick labels and axis label
754
+ const tickLabelHeight = tickFontSize + 10;
755
+ const xAxisLabelTextHeight = xAxisLabel ? axisLabelFontSize + 2 : 0;
756
+ xAxisLabelAreaHeight = tickLabelHeight + (xAxisLabel ? 8 : 0) + xAxisLabelTextHeight;
844
757
  }
845
- // Y-axis label height (if present)
846
758
  const yAxisLabelHeight = yAxisLabel ? axisLabelFontSize + 20 : 0;
847
759
  const axisLabelHeight = Math.max(xAxisLabelAreaHeight, yAxisLabelHeight);
848
- // Adjust chart area based on legend position
849
- // Note: adjustedWidth and adjustedHeight are already calculated above (before canvas creation)
850
- // Chart area should use the ORIGINAL width/height, not adjusted, so legend space is added to canvas, not taken from chart
851
760
  let chartAreaLeft = paddingLeft;
852
- let chartAreaRight = width - paddingRight; // Use original width - chart area stays the same size
853
- // Add margin between title and chart area (minimum 2px)
854
- const titleMargin = chartTitle ? 20 : 0; // 20px gap between title and chart
761
+ let chartAreaRight = width - paddingRight;
762
+ const titleMargin = chartTitle ? 20 : 0;
855
763
  let chartAreaTop = paddingTop + titleHeight + titleMargin;
856
- let chartAreaBottom = baseHeight - paddingBottom; // Use baseHeight, not adjustedHeight - chart area stays the same size
764
+ let chartAreaBottom = baseHeight - paddingBottom;
857
765
  if (showLegend && legend && legend.length > 0) {
858
766
  const legendSpacing = options.legend?.spacing ?? 20;
859
767
  if (legendPosition === 'left') {
860
- // Calculate actual Y-axis label width (category labels or numeric values)
861
- let actualYAxisLabelWidth = 80; // Default estimate
768
+ let actualYAxisLabelWidth = 80;
862
769
  const tempCanvas = (0, canvas_1.createCanvas)(1, 1);
863
770
  const tempCtx = tempCanvas.getContext('2d');
864
771
  if (tempCtx) {
865
- // Check if bar labels are positioned on the left (they act as Y-axis labels)
866
772
  const barLabelFontSize = options.labels?.barLabelDefaults?.fontSize ?? 14;
867
773
  tempCtx.font = `${barLabelFontSize}px Arial`;
868
- // Check if bar labels are on the left side
869
774
  const hasLeftLabels = barLabelPosition === 'left' ||
870
775
  data.some(item => (item.labelPosition ?? barLabelPosition) === 'left');
871
776
  if (hasLeftLabels && showBarLabels) {
872
- // Measure category labels (bar labels) - these are the Y-axis labels
873
777
  data.forEach(d => {
874
778
  const labelWidth = tempCtx.measureText(d.label).width;
875
779
  actualYAxisLabelWidth = Math.max(actualYAxisLabelWidth, labelWidth);
876
780
  });
877
- // Add padding: 5px (label offset from originX) + 10px (spacing) = 15px total
878
781
  actualYAxisLabelWidth += 15;
879
782
  }
880
783
  else {
881
- // No left labels, but might have Y-axis numeric ticks
882
784
  tempCtx.font = `${tickFontSize}px Arial`;
883
- // Estimate for numeric Y-axis values if custom values are provided
884
- actualYAxisLabelWidth = 60; // Default for numeric values
885
- // Add padding: 10px (label offset) + 5px (tick) + 15px (spacing) = 30px total
785
+ actualYAxisLabelWidth = 60;
886
786
  actualYAxisLabelWidth += 30;
887
787
  }
888
788
  }
889
- // For left position, we need to shift chart area to the right to make room for legend
890
- // But the chart area width itself should remain the same (using original width)
891
789
  chartAreaLeft = paddingLeft + legendWidth + legendSpacing + actualYAxisLabelWidth;
892
- chartAreaRight = width - paddingRight; // Keep using original width
790
+ chartAreaRight = width - paddingRight;
893
791
  }
894
792
  else if (legendPosition === 'right') {
895
- // For right position, chart area stays at original position and size
896
- // Legend will be positioned to the right of chartAreaRight, in the extraWidth space
897
793
  chartAreaLeft = paddingLeft;
898
- chartAreaRight = width - paddingRight; // Use original width - chart area doesn't shrink
794
+ chartAreaRight = width - paddingRight;
899
795
  }
900
796
  else if (legendPosition === 'top') {
901
- // For top position, shift chart area down but keep same height
902
797
  chartAreaTop = paddingTop + titleHeight + legendHeight + legendSpacing + minLegendSpacing;
903
- chartAreaBottom = baseHeight - paddingBottom; // Use baseHeight, not adjustedHeight
798
+ chartAreaBottom = baseHeight - paddingBottom;
904
799
  }
905
800
  else if (legendPosition === 'bottom') {
906
- // For bottom position, chart area stays at original position
907
- // Legend will be positioned below in the extraHeight space
908
801
  chartAreaTop = paddingTop + titleHeight;
909
- chartAreaBottom = baseHeight - paddingBottom; // Use baseHeight, not adjustedHeight
802
+ chartAreaBottom = baseHeight - paddingBottom;
910
803
  }
911
804
  }
912
805
  const originX = chartAreaLeft;
913
- // Use baseHeight (not adjustedHeight) for originY calculation - chart area should be based on original dimensions
914
- // The adjustedHeight is only for canvas size expansion, not chart area positioning
915
- // This ensures the chart size stays constant and legend space is added to canvas, not taken from chart
916
- // Ensure at least 2px gap at bottom for labels
917
806
  const minBottomGap = 2;
918
807
  const originY = baseHeight - paddingBottom - xAxisLabelAreaHeight - minBottomGap;
919
808
  const axisEndY = chartAreaTop;
920
809
  const axisEndX = chartAreaRight;
921
- // Draw chart title
922
810
  if (chartTitle) {
923
811
  ctx.save();
924
812
  ctx.textAlign = 'center';
925
813
  ctx.textBaseline = 'top';
926
- // Title positioned with proper spacing from top
927
814
  const titleY = paddingTop + 10;
928
815
  const titleX = adjustedWidth / 2;
929
816
  await renderEnhancedText(ctx, chartTitle, titleX, titleY, options.labels?.title?.textStyle, chartTitleFontSize, options.labels?.title?.color, options.labels?.title?.gradient);
930
817
  ctx.restore();
931
818
  }
932
- // Set axis style
933
819
  ctx.strokeStyle = axisColor;
934
820
  ctx.fillStyle = axisColor;
935
821
  ctx.lineWidth = axisWidth;
936
822
  ctx.lineCap = 'round';
937
- // X-axis will be drawn after calculating zero line
938
- // Draw Y-axis (vertical - category axis)
939
823
  ctx.beginPath();
940
824
  ctx.moveTo(originX, originY);
941
825
  ctx.lineTo(originX, axisEndY);
942
826
  ctx.stroke();
943
- // Draw Y-axis arrow
944
- drawArrow(ctx, originX, axisEndY, -Math.PI / 2, arrowSize); // Y-axis arrow (up)
945
- // Calculate X-axis step
827
+ drawArrow(ctx, originX, axisEndY, -Math.PI / 2, arrowSize);
946
828
  const xStep = xAxisRange?.step ?? Math.ceil((xMax - xMin) / 10);
947
- // Calculate Y-axis step
948
829
  const yStep = yAxisRange?.step ?? 1;
949
- // Calculate chart area dimensions (needed for baseline calculation)
950
830
  const chartAreaWidth = axisEndX - originX;
951
- // Calculate baseline position for X-axis (custom baseline value, default is 0)
952
831
  const baselineX = originX + ((baseline - xMin) / (xMax - xMin)) * chartAreaWidth;
953
- // Draw X-axis at baseline position (horizontal line at originY)
954
832
  ctx.beginPath();
955
833
  ctx.moveTo(originX, originY);
956
834
  ctx.lineTo(axisEndX, originY);
957
835
  ctx.stroke();
958
- // Draw X-axis arrow
959
836
  drawArrow(ctx, axisEndX, originY, 0, arrowSize);
960
- // Draw X-axis ticks and labels at baseline position
961
837
  drawXAxisTicks(ctx, originX, originY, axisEndX, xMin, xMax, xStep, tickFontSize, xAxisCustomValues, xAxisValueSpacing, xAxisTickColor);
962
- // Draw Y-axis ticks and labels only if yAxisValues or yAxisRange is explicitly provided
963
- // For horizontal bar charts, we typically only want bar labels, not numeric y-axis values
964
838
  const shouldDrawYAxisTicks = (yAxisValues && yAxisValues.length > 0) || (options.axes?.y?.range && options.axes.y.range.min !== undefined && options.axes.y.range.max !== undefined);
965
839
  if (shouldDrawYAxisTicks) {
966
840
  drawYAxisTicks(ctx, originX, originY, axisEndY, yMin, yMax, yStep, tickFontSize, yAxisCustomValues, yAxisValueSpacing, yAxisTickColor);
967
841
  }
968
- // Draw axis labels
969
842
  if (xAxisLabel) {
970
843
  ctx.save();
971
844
  ctx.fillStyle = axisLabelColor;
972
845
  ctx.font = `${axisLabelFontSize}px Arial`;
973
846
  ctx.textAlign = 'center';
974
847
  ctx.textBaseline = 'top';
975
- // Position x-axis label with proper spacing: tick labels end at originY + tickFontSize + 10
976
- // Add 8px gap between tick labels and axis label, then position axis label
977
848
  const tickLabelBottom = originY + tickFontSize + 10;
978
- const xAxisLabelY = tickLabelBottom + 8; // 8px gap between tick labels and axis label
849
+ const xAxisLabelY = tickLabelBottom + 8;
979
850
  ctx.fillText(xAxisLabel, (originX + axisEndX) / 2, xAxisLabelY);
980
851
  ctx.restore();
981
852
  }
982
853
  if (yAxisLabel) {
983
- // Check if bar labels are on the left side - if so, position Y-axis label further left
984
854
  let maxBarLabelWidth = 0;
985
855
  if (showBarLabels) {
986
- // Check if default position or any bar has labels on the left
987
856
  const hasLeftLabels = barLabelPosition === 'left' ||
988
857
  data.some(item => (item.labelPosition ?? barLabelPosition) === 'left');
989
858
  if (hasLeftLabels) {
990
- // Calculate maximum width of bar labels
991
859
  ctx.save();
992
860
  ctx.font = `${axisLabelFontSize}px Arial`;
993
861
  data.forEach(item => {
@@ -1005,8 +873,6 @@ async function createHorizontalBarChart(data, options = {}) {
1005
873
  ctx.font = `${axisLabelFontSize}px Arial`;
1006
874
  ctx.textAlign = 'center';
1007
875
  ctx.textBaseline = 'bottom';
1008
- // Position Y-axis label further left if bar labels are on the left
1009
- // Add extra spacing (20px) after the bar labels
1010
876
  const labelX = originX - maxBarLabelWidth - 20 - 30;
1011
877
  const labelY = (originY + axisEndY) / 2;
1012
878
  ctx.translate(labelX, labelY);
@@ -1014,11 +880,9 @@ async function createHorizontalBarChart(data, options = {}) {
1014
880
  ctx.fillText(yAxisLabel, 0, 0);
1015
881
  ctx.restore();
1016
882
  }
1017
- // Draw grid lines if enabled
1018
883
  if (showGrid) {
1019
884
  drawGrid(ctx, originX, originY, axisEndX, axisEndY, xMin, xMax, xStep, yMin, yMax, yStep, yAxisCustomValues, xAxisCustomValues, gridColor, gridWidth);
1020
885
  }
1021
- // Draw legend if provided - positioned based on legendPosition option
1022
886
  if (showLegend && legend && legend.length > 0) {
1023
887
  const legendSpacing = options.legend?.spacing ?? 20;
1024
888
  const legendFontSize = options.legend?.fontSize ?? 16;
@@ -1028,78 +892,57 @@ async function createHorizontalBarChart(data, options = {}) {
1028
892
  const legendPadding = options.legend?.padding;
1029
893
  const legendMaxWidth = options.legend?.maxWidth;
1030
894
  const legendWrapText = options.legend?.wrapText !== false;
1031
- // Calculate legend position based on legendPosition option
1032
895
  let legendX, legendY;
1033
896
  const chartAreaHeight = originY - axisEndY;
1034
897
  const chartAreaWidth = axisEndX - originX;
1035
898
  switch (legendPosition) {
1036
899
  case 'top':
1037
- legendX = (adjustedWidth - legendWidth) / 2; // Centered horizontally
900
+ legendX = (adjustedWidth - legendWidth) / 2;
1038
901
  legendY = paddingTop + titleHeight + minLegendSpacing;
1039
902
  break;
1040
903
  case 'bottom':
1041
- legendX = (adjustedWidth - legendWidth) / 2; // Centered horizontally
904
+ legendX = (adjustedWidth - legendWidth) / 2;
1042
905
  legendY = adjustedHeight - paddingBottom - legendHeight - minLegendSpacing;
1043
906
  break;
1044
907
  case 'left':
1045
- // Position legend at the very left edge to make maximum room for Y-axis labels
1046
- // The chart area already accounts for legend width + label width, so position legend at leftmost
1047
908
  legendX = paddingLeft;
1048
- legendY = axisEndY + (chartAreaHeight - legendHeight) / 2; // Vertically centered in chart area
909
+ legendY = axisEndY + (chartAreaHeight - legendHeight) / 2;
1049
910
  break;
1050
911
  case 'right':
1051
912
  default:
1052
913
  legendX = axisEndX + minLegendSpacing;
1053
- legendY = axisEndY + (chartAreaHeight - legendHeight) / 2; // Vertically centered in chart area
914
+ legendY = axisEndY + (chartAreaHeight - legendHeight) / 2;
1054
915
  break;
1055
916
  }
1056
917
  await drawLegendAtPosition(ctx, legend, legendX, legendY, legendFontSize, legendBgColor || backgroundColor, legendTextColor, legendBorderColor, legendPadding, legendMaxWidth, legendWrapText, options.legend?.backgroundGradient, options.legend?.textGradient, options.legend?.textStyle);
1057
918
  }
1058
- // Calculate chart area dimensions (Y-axis area for bars)
1059
- // chartAreaWidth and baselineX already calculated above when drawing X-axis
1060
919
  const chartAreaHeight = originY - axisEndY;
1061
- // Calculate bar dimensions to fit within Y-axis bounds (between axisEndY and originY)
1062
920
  const calculatedBarSpacing = barSpacing ?? 15;
1063
- // Total spacing needed: spacing between bars (n-1) + initial spacing before first bar
1064
- const totalSpacing = data.length * calculatedBarSpacing; // Include spacing before first bar
921
+ const totalSpacing = data.length * calculatedBarSpacing;
1065
922
  const availableHeight = chartAreaHeight - totalSpacing;
1066
923
  const calculatedBarHeight = Math.max(minBarHeight, availableHeight / data.length);
1067
- // Verify all bars will fit
1068
924
  const totalBarsHeight = data.length * calculatedBarHeight + totalSpacing;
1069
925
  if (totalBarsHeight > chartAreaHeight) {
1070
- // Adjust bar height if needed to fit all bars
1071
926
  const adjustedBarHeight = Math.max(minBarHeight, (chartAreaHeight - totalSpacing) / data.length);
1072
927
  if (adjustedBarHeight < calculatedBarHeight) {
1073
928
  console.warn(`Bar height adjusted from ${calculatedBarHeight} to ${adjustedBarHeight} to fit all ${data.length} bars`);
1074
929
  }
1075
930
  }
1076
931
  const labelsToDraw = [];
1077
- // Track value label positions per bar (for adjusting bar label positions)
1078
932
  const valueLabelPositions = new Map();
1079
- // First pass: Draw all bars (no labels)
1080
933
  data.forEach((item, index) => {
1081
- // Calculate bar Y position - start from axisEndY (top) and space bars downward
1082
- // First bar starts at axisEndY, each subsequent bar: previous position + bar height + spacing
1083
934
  const barY = axisEndY + (index * (calculatedBarHeight + calculatedBarSpacing));
1084
935
  const barCenterY = barY + calculatedBarHeight / 2;
1085
- // Ensure bar stays within Y-axis bounds (between axisEndY and originY)
1086
936
  if (barY + calculatedBarHeight > originY) {
1087
- // Bar would exceed Y-axis bottom - skip it to prevent overflow
1088
- // This should not happen if chartAreaHeight is calculated correctly, but check anyway
1089
937
  console.warn(`Bar at index ${index} (${item.label}) would exceed chart bounds. Skipping.`);
1090
938
  return;
1091
939
  }
1092
- // Calculate bar position and dimensions for label positioning (used for all chart types)
1093
940
  let barX, barEndX, barLength;
1094
- // Handle grouped/stacked/lollipop vs standard charts
1095
941
  if ((chartType === 'grouped' || chartType === 'stacked' || chartType === 'lollipop') && item.values && item.values.length > 0) {
1096
- // Grouped or stacked chart
1097
942
  const segments = item.values;
1098
943
  const numSegments = segments.length;
1099
944
  if (chartType === 'grouped') {
1100
- // Grouped: bars side-by-side (vertically stacked in horizontal chart)
1101
945
  const segmentHeight = (calculatedBarHeight - (groupSpacing * (numSegments - 1))) / numSegments;
1102
- // Calculate overall bar bounds for label positioning (use max segment)
1103
946
  const maxSegment = segments.reduce((max, seg) => seg.value > max.value ? seg : max, segments[0]);
1104
947
  if (item.xStart !== undefined || item.xEnd !== undefined) {
1105
948
  const startValue = item.xStart ?? xMin;
@@ -1110,7 +953,6 @@ async function createHorizontalBarChart(data, options = {}) {
1110
953
  barEndX = originX + endRatio * chartAreaWidth;
1111
954
  }
1112
955
  else {
1113
- // Calculate based on positive/negative
1114
956
  if (maxSegment.value >= 0) {
1115
957
  const positiveRatio = (maxSegment.value - 0) / (xMax - xMin);
1116
958
  barX = baselineX;
@@ -1126,7 +968,6 @@ async function createHorizontalBarChart(data, options = {}) {
1126
968
  segments.forEach((segment, segIndex) => {
1127
969
  const segY = barY + (segIndex * (segmentHeight + groupSpacing));
1128
970
  const segCenterY = segY + segmentHeight / 2;
1129
- // Calculate segment bar position and length
1130
971
  let segBarX, segBarEndX;
1131
972
  if (item.xStart !== undefined || item.xEnd !== undefined) {
1132
973
  const startValue = item.xStart ?? xMin;
@@ -1137,7 +978,6 @@ async function createHorizontalBarChart(data, options = {}) {
1137
978
  segBarEndX = originX + endRatio * chartAreaWidth;
1138
979
  }
1139
980
  else {
1140
- // Calculate bar position based on positive/negative value
1141
981
  if (segment.value >= baseline) {
1142
982
  const positiveRatio = (segment.value - baseline) / (xMax - xMin);
1143
983
  segBarX = baselineX;
@@ -1150,12 +990,10 @@ async function createHorizontalBarChart(data, options = {}) {
1150
990
  }
1151
991
  }
1152
992
  const segBarLength = Math.abs(segBarEndX - segBarX);
1153
- // Draw segment bar with gradient or color
1154
993
  ctx.beginPath();
1155
994
  ctx.rect(segBarX, segY, segBarLength, segmentHeight);
1156
995
  fillWithGradientOrColor(ctx, segment.gradient || item.gradient, segment.color || item.color || '#4A90E2', '#4A90E2', { x: segBarX, y: segY, w: segBarLength, h: segmentHeight });
1157
996
  ctx.fill();
1158
- // Store value label for later drawing
1159
997
  const shouldShowValue = segment.showValue !== undefined ? segment.showValue : showValues;
1160
998
  if (shouldShowValue) {
1161
999
  labelsToDraw.push({
@@ -1172,10 +1010,8 @@ async function createHorizontalBarChart(data, options = {}) {
1172
1010
  });
1173
1011
  }
1174
1012
  else {
1175
- // Stacked: bars on top of each other (horizontally stacked in horizontal chart)
1176
1013
  let accumulatedLength = 0;
1177
1014
  segments.forEach((segment, segIndex) => {
1178
- // For stacked, separate positive and negative segments
1179
1015
  let segmentLength;
1180
1016
  let segBarX;
1181
1017
  if (segment.value >= baseline) {
@@ -1188,10 +1024,8 @@ async function createHorizontalBarChart(data, options = {}) {
1188
1024
  segmentLength = negativeRatio * chartAreaWidth;
1189
1025
  segBarX = baselineX - accumulatedLength - segmentLength;
1190
1026
  }
1191
- // Draw segment bar
1192
1027
  ctx.fillStyle = segment.color || item.color || '#4A90E2';
1193
1028
  ctx.fillRect(segBarX, barY, segmentLength, calculatedBarHeight);
1194
- // Store value label for later drawing
1195
1029
  const shouldShowValue = segment.showValue !== undefined ? segment.showValue : showValues;
1196
1030
  if (shouldShowValue && segmentLength > valueFontSize + 10) {
1197
1031
  labelsToDraw.push({
@@ -1207,15 +1041,12 @@ async function createHorizontalBarChart(data, options = {}) {
1207
1041
  }
1208
1042
  accumulatedLength += segmentLength;
1209
1043
  });
1210
- // Calculate overall bar bounds for label positioning
1211
1044
  barX = originX;
1212
1045
  barEndX = originX + accumulatedLength;
1213
1046
  barLength = accumulatedLength;
1214
- // Store total value label for later drawing
1215
1047
  const totalValue = segments.reduce((sum, seg) => sum + seg.value, 0);
1216
1048
  const shouldShowValue = item.showValue !== undefined ? item.showValue : showValues;
1217
1049
  if (shouldShowValue) {
1218
- // Calculate total position
1219
1050
  const totalPositive = segments.filter(s => s.value >= 0).reduce((sum, s) => sum + s.value, 0);
1220
1051
  const totalNegative = segments.filter(s => s.value < 0).reduce((sum, s) => sum + Math.abs(s.value), 0);
1221
1052
  const totalPositiveLength = (totalPositive / (xMax - xMin)) * chartAreaWidth;
@@ -1237,21 +1068,16 @@ async function createHorizontalBarChart(data, options = {}) {
1237
1068
  }
1238
1069
  }
1239
1070
  else if (chartType === 'lollipop') {
1240
- // Lollipop chart: line with dot at end (horizontal)
1241
1071
  const value = item.value ?? baseline;
1242
- // Calculate value X position
1243
1072
  let valueX;
1244
1073
  if (value >= baseline) {
1245
- // Value to the right of baseline
1246
1074
  const positiveRatio = (value - baseline) / (xMax - xMin);
1247
1075
  valueX = baselineX + positiveRatio * chartAreaWidth;
1248
1076
  }
1249
1077
  else {
1250
- // Value to the left of baseline
1251
1078
  const negativeRatio = (baseline - value) / (xMax - xMin);
1252
1079
  valueX = baselineX - negativeRatio * chartAreaWidth;
1253
1080
  }
1254
- // Draw horizontal line from baseline to value position
1255
1081
  ctx.save();
1256
1082
  ctx.strokeStyle = item.color || '#4A90E2';
1257
1083
  ctx.lineWidth = lollipopLineWidth;
@@ -1259,20 +1085,16 @@ async function createHorizontalBarChart(data, options = {}) {
1259
1085
  ctx.moveTo(baselineX, barCenterY);
1260
1086
  ctx.lineTo(valueX, barCenterY);
1261
1087
  ctx.stroke();
1262
- // Draw dot/circle at value position
1263
1088
  ctx.fillStyle = item.color || '#4A90E2';
1264
1089
  ctx.beginPath();
1265
1090
  ctx.arc(valueX, barCenterY, lollipopDotSize / 2, 0, Math.PI * 2);
1266
1091
  ctx.fill();
1267
- // Draw dot border for better visibility
1268
1092
  ctx.strokeStyle = item.color || '#4A90E2';
1269
1093
  ctx.lineWidth = 1;
1270
1094
  ctx.stroke();
1271
1095
  ctx.restore();
1272
- // Store value label for later drawing
1273
1096
  const shouldShowValue = item.showValue !== undefined ? item.showValue : showValues;
1274
1097
  if (shouldShowValue) {
1275
- // Store value label position for this bar (for adjusting bar label position)
1276
1098
  if (value >= baseline) {
1277
1099
  valueLabelPositions.set(index, { x: valueX + lollipopDotSize / 2 + 5, fontSize: valueFontSize, align: 'left' });
1278
1100
  }
@@ -1287,15 +1109,11 @@ async function createHorizontalBarChart(data, options = {}) {
1287
1109
  fontSize: valueFontSize
1288
1110
  });
1289
1111
  }
1290
- // Set bar bounds for label positioning
1291
1112
  barX = baselineX;
1292
1113
  barEndX = valueX;
1293
1114
  barLength = Math.abs(barEndX - barX);
1294
1115
  }
1295
1116
  else {
1296
- // Standard chart: single bar
1297
- // Calculate bar position and length
1298
- // If xStart/xEnd are provided, use them for bar range; otherwise use value
1299
1117
  if (item.xStart !== undefined || item.xEnd !== undefined) {
1300
1118
  const startValue = item.xStart ?? xMin;
1301
1119
  const endValue = item.xEnd ?? (item.value ?? 0);
@@ -1305,7 +1123,6 @@ async function createHorizontalBarChart(data, options = {}) {
1305
1123
  barEndX = originX + endRatio * chartAreaWidth;
1306
1124
  }
1307
1125
  else {
1308
- // Use value as end position, handle relative to baseline
1309
1126
  const value = item.value ?? baseline;
1310
1127
  if (value >= baseline) {
1311
1128
  const positiveRatio = (value - baseline) / (xMax - xMin);
@@ -1319,18 +1136,15 @@ async function createHorizontalBarChart(data, options = {}) {
1319
1136
  }
1320
1137
  }
1321
1138
  barLength = barEndX - barX;
1322
- // Draw horizontal bar
1323
1139
  ctx.beginPath();
1324
1140
  ctx.rect(barX, barY, barLength, calculatedBarHeight);
1325
1141
  fillWithGradientOrColor(ctx, item.gradient, item.color || '#4A90E2', '#4A90E2', { x: barX, y: barY, w: barLength, h: calculatedBarHeight });
1326
1142
  ctx.fill();
1327
- // Store value label for later drawing
1328
1143
  const shouldShowValue = item.showValue !== undefined ? item.showValue : showValues;
1329
1144
  if (shouldShowValue) {
1330
1145
  const value = item.value ?? baseline;
1331
1146
  const valueLabelX = value >= baseline ? barEndX + 5 : barX - 5;
1332
1147
  const valueLabelAlign = value >= baseline ? 'left' : 'right';
1333
- // Store value label position for this bar (for adjusting bar label position)
1334
1148
  if (value >= baseline) {
1335
1149
  valueLabelPositions.set(index, { x: valueLabelX, fontSize: valueFontSize, align: valueLabelAlign });
1336
1150
  }
@@ -1346,7 +1160,6 @@ async function createHorizontalBarChart(data, options = {}) {
1346
1160
  });
1347
1161
  }
1348
1162
  }
1349
- // Store bar label information for later drawing
1350
1163
  if (showBarLabels) {
1351
1164
  let labelX, labelY;
1352
1165
  let textAlign = 'right';
@@ -1362,16 +1175,13 @@ async function createHorizontalBarChart(data, options = {}) {
1362
1175
  case 'right':
1363
1176
  labelX = barEndX + 5;
1364
1177
  labelY = barCenterY;
1365
- // Check if there's a value label at the right - if so, position bar label to the right of it
1366
1178
  const valueLabelInfo = valueLabelPositions.get(index);
1367
1179
  if (valueLabelInfo && valueLabelInfo.align === 'left') {
1368
- // Value label is at right, so position bar label to the right of it
1369
- // Calculate spacing: value label width + gap
1370
1180
  ctx.save();
1371
1181
  ctx.font = `${valueLabelInfo.fontSize}px Arial`;
1372
1182
  const valueLabelWidth = ctx.measureText((item.value ?? baseline).toString()).width;
1373
1183
  ctx.restore();
1374
- const spacing = 5; // Gap between value and bar label
1184
+ const spacing = 5;
1375
1185
  labelX = valueLabelInfo.x + valueLabelWidth + spacing;
1376
1186
  }
1377
1187
  else {
@@ -1382,11 +1192,6 @@ async function createHorizontalBarChart(data, options = {}) {
1382
1192
  break;
1383
1193
  case 'top':
1384
1194
  labelX = barX + barLength / 2;
1385
- // Check if there's a value label - for horizontal charts, value labels are at the end (right side)
1386
- // So 'top' bar label won't conflict with value labels (they're on different axes)
1387
- // But we still need to check if value is shown and adjust if needed
1388
- // For horizontal charts, 'top' means above the bar, value labels are at the end
1389
- // So no conflict, but if we want to be safe, we can check
1390
1195
  labelY = barY - 5;
1391
1196
  textAlign = 'center';
1392
1197
  textBaseline = 'bottom';
@@ -1409,8 +1214,6 @@ async function createHorizontalBarChart(data, options = {}) {
1409
1214
  textAlign = 'right';
1410
1215
  textBaseline = 'middle';
1411
1216
  }
1412
- // Calculate label color (for 'inside' position, check if bar is dark)
1413
- // Use item.labelColor if provided, otherwise fall back to barLabelDefaults.defaultColor
1414
1217
  const defaultBarLabelColor = options.labels?.barLabelDefaults?.defaultColor ?? '#000000';
1415
1218
  let labelColor = item.labelColor || defaultBarLabelColor;
1416
1219
  if (currentLabelPosition === 'inside') {
@@ -1431,12 +1234,10 @@ async function createHorizontalBarChart(data, options = {}) {
1431
1234
  });
1432
1235
  }
1433
1236
  });
1434
- // Second pass: Draw all labels (values and bar labels) on top of everything
1435
1237
  for (const label of labelsToDraw) {
1436
1238
  ctx.save();
1437
1239
  ctx.textAlign = label.align;
1438
1240
  ctx.textBaseline = label.baseline;
1439
- // Determine text style and gradient based on label type
1440
1241
  let textStyle;
1441
1242
  let textGradient;
1442
1243
  if (label.type === 'bar') {