mpl-richtext 0.1.5__py3-none-any.whl → 0.1.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mpl_richtext/__init__.py CHANGED
@@ -4,8 +4,9 @@ mpl-richtext: Rich text rendering for Matplotlib
4
4
 
5
5
  from .core import richtext
6
6
  from .version import __version__
7
+ from .utils import format_nepali_number
7
8
 
8
- __all__ = ['richtext', '__version__']
9
+ __all__ = ['richtext', '__version__', 'format_nepali_number']
9
10
 
10
11
  __author__ = 'Rabin Katel'
11
12
  __email__ = 'kattelrabinraja13@gmail.com'
mpl_richtext/core.py CHANGED
@@ -25,6 +25,26 @@ def _needs_complex_shaping(text: str) -> bool:
25
25
  return True
26
26
  return False
27
27
 
28
+ def _resolve_font_path(kwargs: Dict[str, Any]) -> Optional[str]:
29
+ """Helper to resolve font file path from text kwargs."""
30
+ fp = kwargs.get('fontproperties')
31
+ if fp:
32
+ return findfont(fp)
33
+
34
+ font = kwargs.get('fontfamily') or kwargs.get('family')
35
+ if not font:
36
+ # Fallback to default
37
+ font = plt.rcParams['font.family'][0]
38
+
39
+ if isinstance(font, list):
40
+ font = font[0]
41
+
42
+ try:
43
+ fp = FontProperties(family=font)
44
+ return findfont(fp)
45
+ except Exception:
46
+ return None
47
+
28
48
  def richtext(
29
49
  x: float,
30
50
  y: float,
@@ -352,17 +372,11 @@ def _get_text_metrics(text: str, ax: Axes, renderer, **text_kwargs) -> tuple:
352
372
  kwargs.pop('underline', None)
353
373
 
354
374
  # Try shaping if available
355
- if HAS_HARFBUZZ:
356
- font = kwargs.get('fontfamily') or kwargs.get('family')
375
+ # Only use HarfBuzz measurement if the text actually needs complex shaping.
376
+ # Otherwise, trust Matplotlib's native measurement which handles font fallback (e.g. lists of fonts) better.
377
+ if HAS_HARFBUZZ and _needs_complex_shaping(text):
378
+ path = _resolve_font_path(kwargs)
357
379
  try:
358
- if not font:
359
- font = plt.rcParams['font.family'][0]
360
- if isinstance(font, list):
361
- font = font[0]
362
-
363
- fp = FontProperties(family=font)
364
- path = findfont(fp)
365
-
366
380
  if path:
367
381
  fontsize = kwargs.get('fontsize') or kwargs.get('size') or plt.rcParams['font.size']
368
382
  shaper = HarfbuzzShaper(path)
@@ -411,30 +425,29 @@ def _get_text_height(text: str, ax: Axes, renderer, **text_kwargs) -> float:
411
425
  # Try shaping-based height for Devanagari fonts
412
426
  # This avoids measuring with Latin chars that the font might not have
413
427
  if HAS_HARFBUZZ:
428
+ path = _resolve_font_path(kwargs)
414
429
  try:
415
- font = kwargs.get('fontfamily') or kwargs.get('family')
416
- if not font:
417
- font = plt.rcParams['font.family'][0]
418
- if isinstance(font, list):
419
- font = font[0]
430
+ # Check if this is a known Devanagari font (simplified check via path for now?
431
+ # Or assume if resolving worked and contained Devanagari chars earlier...
432
+ # Actually valid logic: if we are here and path resolves, we trust it?
433
+ # But the original code restricted it to known fonts.
434
+ # Let's keep it generally open if path is found, OR check font name.
420
435
 
421
- # Check if this is a known Devanagari font
422
- devanagari_fonts = ['Noto Sans Devanagari', 'Kalimati', 'Mangal', 'Lohit Devanagari', 'Madan']
423
- if font and any(df.lower() in str(font).lower() for df in devanagari_fonts):
424
- fp = FontProperties(family=font)
425
- path = findfont(fp)
436
+ if path:
437
+ # Optional: check for Devanagari-likeness if needed, but path resolution implies intent.
438
+ # However, for height specifically we only wanted this for specific fonts to avoid 'Hg'.
439
+ # Let's be permissive if path is found since we use shaper now.
426
440
 
427
- if path:
428
- fontsize = kwargs.get('fontsize') or kwargs.get('size') or plt.rcParams['font.size']
429
- shaper = HarfbuzzShaper(path)
430
- height_points = shaper.get_font_height(fontsize)
431
-
432
- # Convert points -> pixels -> data
433
- pixels = renderer.points_to_pixels(height_points)
434
- from matplotlib.transforms import Bbox
435
- bbox_display = Bbox.from_bounds(0, 0, 0, pixels)
436
- bbox_data = bbox_display.transformed(ax.transData.inverted())
437
- return bbox_data.height
441
+ fontsize = kwargs.get('fontsize') or kwargs.get('size') or plt.rcParams['font.size']
442
+ shaper = HarfbuzzShaper(path)
443
+ height_points = shaper.get_font_height(fontsize)
444
+
445
+ # Convert points -> pixels -> data
446
+ pixels = renderer.points_to_pixels(height_points)
447
+ from matplotlib.transforms import Bbox
448
+ bbox_display = Bbox.from_bounds(0, 0, 0, pixels)
449
+ bbox_data = bbox_display.transformed(ax.transData.inverted())
450
+ return bbox_data.height
438
451
  except Exception:
439
452
  pass # Fallback to native
440
453
 
@@ -581,15 +594,7 @@ def _draw_lines(
581
594
 
582
595
  if HAS_HARFBUZZ and _needs_complex_shaping(word):
583
596
  try:
584
- font = text_kwargs.get('fontfamily') or text_kwargs.get('family')
585
- if not font:
586
- font = plt.rcParams['font.family'][0]
587
- if isinstance(font, list):
588
- font = font[0]
589
-
590
- fp = FontProperties(family=font)
591
- path = findfont(fp)
592
-
597
+ path = _resolve_font_path(text_kwargs)
593
598
  if path:
594
599
  t = ShapedText(current_x, baseline_y, word, font_path=path, **text_kwargs)
595
600
  ax.add_artist(t)
mpl_richtext/shaping.py CHANGED
@@ -281,7 +281,9 @@ class ShapedText(Text):
281
281
 
282
282
  glyph_trans = Affine2D().translate(gx, gy) + base_transform + align_transform + placement_transform
283
283
 
284
- renderer.draw_path(gc, path, glyph_trans)
284
+ from matplotlib.colors import to_rgba
285
+ rgba_color = to_rgba(self.get_color(), alpha=self.get_alpha())
286
+ renderer.draw_path(gc, path, glyph_trans, rgbFace=rgba_color)
285
287
 
286
288
  gc.restore()
287
289
 
mpl_richtext/utils.py ADDED
@@ -0,0 +1,72 @@
1
+ """
2
+ Utility functions for mpl-richtext
3
+ """
4
+
5
+
6
+ def format_nepali_number(number):
7
+ """
8
+ Format a number using the Nepali/Indian numbering system.
9
+
10
+ Uses the 3-2-2... grouping pattern from right to left.
11
+ Handles already-formatted input by stripping existing commas first.
12
+
13
+ Args:
14
+ number: int, float, or string representing a number.
15
+ Can include existing comma formatting.
16
+
17
+ Returns:
18
+ str: The number formatted with Nepali-style comma placement.
19
+
20
+ Examples:
21
+ >>> format_nepali_number(2553871)
22
+ '25,53,871'
23
+ >>> format_nepali_number("2,553,871") # Western format → Nepali
24
+ '25,53,871'
25
+ >>> format_nepali_number(1234567.89)
26
+ '12,34,567.89'
27
+ >>> format_nepali_number(-2553871)
28
+ '-25,53,871'
29
+ """
30
+ # Convert to string and strip existing commas
31
+ num_str = str(number).replace(',', '')
32
+
33
+ # Handle negative numbers
34
+ is_negative = num_str.startswith('-')
35
+ if is_negative:
36
+ num_str = num_str[1:]
37
+
38
+ # Separate integer and decimal parts
39
+ if '.' in num_str:
40
+ integer_part, decimal_part = num_str.split('.', 1)
41
+ else:
42
+ integer_part = num_str
43
+ decimal_part = None
44
+
45
+ # Apply Nepali grouping: first 3 digits from right, then 2 digits each
46
+ if len(integer_part) <= 3:
47
+ formatted = integer_part
48
+ else:
49
+ # Take last 3 digits
50
+ result = [integer_part[-3:]]
51
+ remaining = integer_part[:-3]
52
+
53
+ # Group remaining digits in pairs of 2 from right to left
54
+ while len(remaining) > 2:
55
+ result.insert(0, remaining[-2:])
56
+ remaining = remaining[:-2]
57
+
58
+ # Add any remaining digits (1 or 2)
59
+ if remaining:
60
+ result.insert(0, remaining)
61
+
62
+ formatted = ','.join(result)
63
+
64
+ # Rejoin with decimal part if present
65
+ if decimal_part is not None:
66
+ formatted = f"{formatted}.{decimal_part}"
67
+
68
+ # Add negative sign back
69
+ if is_negative:
70
+ formatted = f"-{formatted}"
71
+
72
+ return formatted
mpl_richtext/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '0.1.5'
1
+ __version__ = '0.1.8'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mpl-richtext
3
- Version: 0.1.5
3
+ Version: 0.1.8
4
4
  Summary: Rich text rendering for Matplotlib with multi-color and multi-style support
5
5
  Home-page: https://github.com/ra8in/mpl-richtext
6
6
  Author: Rabin Katel
@@ -0,0 +1,10 @@
1
+ mpl_richtext/__init__.py,sha256=c-efxeIDOSU6EXIMfbLzBi4h3Rx4h88kx9U1Lx2DD1Q,310
2
+ mpl_richtext/core.py,sha256=S_BrL3SlsDcUpDlmwXmiRKPvguGASjbLYinH63dDJ5g,24772
3
+ mpl_richtext/shaping.py,sha256=wvn0PREs3SXNgnuIVVnxIWWtNyaQt5lhAbFmeem9U7w,10850
4
+ mpl_richtext/utils.py,sha256=25zBLnXKm_7Hg8x8fsDMVpB1DRWsmtfMJe96CY8vrHA,2094
5
+ mpl_richtext/version.py,sha256=zemvJ5zjFE6SQT2xmkxc-ZYwNkUTCEX7mz3Epb2qztE,21
6
+ mpl_richtext-0.1.8.dist-info/licenses/LICENSE,sha256=3ldFhzXg_DuFYRbdYJaN661E5PAzUpVjWxFCxVm7kG8,1067
7
+ mpl_richtext-0.1.8.dist-info/METADATA,sha256=dEZoRwIcuG4MRygIx04uNQsNmJRVgmIKAGJPV9cVIow,5443
8
+ mpl_richtext-0.1.8.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
+ mpl_richtext-0.1.8.dist-info/top_level.txt,sha256=3K_jSX3xxD2Dhaqm4jtejlO0HI_5tl7F7AMq4lGk6xw,13
10
+ mpl_richtext-0.1.8.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- mpl_richtext/__init__.py,sha256=430UvdXDNTa9xCoQ48OT_lzHrFCW_xCuVaTr1apEVq4,246
2
- mpl_richtext/core.py,sha256=DGzlbJkWTSzLOdhHVHVor2hPnw4Ra_bJIFhZnCnb-I4,24474
3
- mpl_richtext/shaping.py,sha256=E-RrjWE7JvAvVi407xwFjCuzfQ4vpoSKv4YWhzfob40,10705
4
- mpl_richtext/version.py,sha256=CjpRE9sT1AfD9Ft__Fhhop9Bh9IBCVEmm9E9TX1aV2k,21
5
- mpl_richtext-0.1.5.dist-info/licenses/LICENSE,sha256=3ldFhzXg_DuFYRbdYJaN661E5PAzUpVjWxFCxVm7kG8,1067
6
- mpl_richtext-0.1.5.dist-info/METADATA,sha256=oMbscy02JU86mlSEnYqcagm3XgIwrIFghkV8p1DCFxM,5443
7
- mpl_richtext-0.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
8
- mpl_richtext-0.1.5.dist-info/top_level.txt,sha256=3K_jSX3xxD2Dhaqm4jtejlO0HI_5tl7F7AMq4lGk6xw,13
9
- mpl_richtext-0.1.5.dist-info/RECORD,,