kerykeion 5.1.8__py3-none-any.whl → 5.1.10__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.

Potentially problematic release.


This version of kerykeion might be problematic. Click here for more details.

@@ -10,7 +10,7 @@ from math import ceil
10
10
  from datetime import datetime
11
11
  from pathlib import Path
12
12
  from string import Template
13
- from typing import Any, Mapping, Optional, Union, get_args
13
+ from typing import Any, Mapping, Optional, Sequence, Union, get_args
14
14
 
15
15
  import swisseph as swe
16
16
  from scour.scour import scourString
@@ -173,6 +173,10 @@ class ChartDrawer:
173
173
  _DEFAULT_FULL_WIDTH_WITH_TABLE = 1250
174
174
  _DEFAULT_ULTRA_WIDE_WIDTH = 1320
175
175
 
176
+ _VERTICAL_PADDING_TOP = 15
177
+ _VERTICAL_PADDING_BOTTOM = 15
178
+ _TITLE_SPACING = 8
179
+
176
180
  _ASPECT_LIST_ASPECTS_PER_COLUMN = 14
177
181
  _ASPECT_LIST_COLUMN_WIDTH = 105
178
182
 
@@ -608,6 +612,9 @@ class ChartDrawer:
608
612
 
609
613
  delta_height = max(self.height - minimum_height, 0)
610
614
 
615
+ # Move title up for synastry charts
616
+ offsets["title"] = -10
617
+
611
618
  offsets["wheel"] += delta_height
612
619
  offsets["aspect_grid"] += delta_height
613
620
  offsets["aspect_list"] += delta_height
@@ -664,8 +671,10 @@ class ChartDrawer:
664
671
  return collected
665
672
 
666
673
  def _dynamic_viewbox(self) -> str:
667
- """Return the viewBox string based on current width/height."""
668
- return f"0 0 {int(self.width)} {int(self.height)}"
674
+ """Return the viewBox string based on current width/height with vertical padding."""
675
+ min_y = -self._VERTICAL_PADDING_TOP
676
+ viewbox_height = int(self.height) + self._VERTICAL_PADDING_TOP + self._VERTICAL_PADDING_BOTTOM
677
+ return f"0 {min_y} {int(self.width)} {viewbox_height}"
669
678
 
670
679
  def _wheel_only_viewbox(self, margin: int = 20) -> str:
671
680
  """Return a tight viewBox for the wheel-only template.
@@ -770,18 +779,65 @@ class ChartDrawer:
770
779
  # Secondary houses grid default x ~ 1015
771
780
  secondary_houses_grid_right = 1015 + 120
772
781
  extents.append(secondary_houses_grid_right)
773
- first_house_comparison_grid_right = 1090 + 180
774
- second_house_comparison_grid_right = 1290 + 180
775
- extents.extend([first_house_comparison_grid_right, second_house_comparison_grid_right])
782
+ if self.second_obj is not None:
783
+ point_column_label = self._translate("point", "Point")
784
+ first_subject_label = self._truncate_name(self.first_obj.name, 8, "…", True) # type: ignore[union-attr]
785
+ second_subject_label = self._truncate_name(self.second_obj.name, 8, "…", True) # type: ignore[union-attr]
786
+
787
+ first_columns = [
788
+ f"{first_subject_label} {point_column_label}",
789
+ first_subject_label,
790
+ second_subject_label,
791
+ ]
792
+ second_columns = [
793
+ f"{second_subject_label} {point_column_label}",
794
+ second_subject_label,
795
+ first_subject_label,
796
+ ]
797
+
798
+ first_grid_width = self._estimate_house_comparison_grid_width(
799
+ column_labels=first_columns,
800
+ include_radix_column=True,
801
+ include_title=True,
802
+ )
803
+ second_grid_width = self._estimate_house_comparison_grid_width(
804
+ column_labels=second_columns,
805
+ include_radix_column=True,
806
+ include_title=False,
807
+ )
808
+
809
+ first_house_comparison_grid_right = 1090 + first_grid_width
810
+ second_house_comparison_grid_right = 1290 + second_grid_width
811
+ extents.extend([first_house_comparison_grid_right, second_house_comparison_grid_right])
776
812
 
777
813
  if self.chart_type == "Transit":
778
814
  # House comparison grid at x ~ 1030
779
- house_comparison_grid_right = 1030 + 180
815
+ transit_columns = [
816
+ self._translate("transit_point", "Transit Point"),
817
+ self._translate("house_position", "Natal House"),
818
+ ]
819
+ transit_grid_width = self._estimate_house_comparison_grid_width(
820
+ column_labels=transit_columns,
821
+ include_radix_column=False,
822
+ include_title=True,
823
+ minimum_width=170.0,
824
+ )
825
+ house_comparison_grid_right = 980 + transit_grid_width
780
826
  extents.append(house_comparison_grid_right)
781
827
 
782
828
  if self.chart_type == "DualReturnChart":
783
- # House comparison grid at x ~ 1030
784
- house_comparison_grid_right = 1030 + 320
829
+ # House comparison grid translated to x ~ 1100
830
+ dual_return_columns = [
831
+ self._translate("return_point", "Return Point"),
832
+ self._translate("Return", "DualReturnChart"),
833
+ self._translate("Natal", "Natal"),
834
+ ]
835
+ dual_return_grid_width = self._estimate_house_comparison_grid_width(
836
+ column_labels=dual_return_columns,
837
+ include_radix_column=True,
838
+ include_title=True,
839
+ )
840
+ house_comparison_grid_right = 1100 + dual_return_grid_width
785
841
  extents.append(house_comparison_grid_right)
786
842
 
787
843
  # Conservative safety padding
@@ -839,6 +895,86 @@ class ChartDrawer:
839
895
  # imposed by the title area.
840
896
  return max(per_column, min(allowed_capacity, max_capacity_by_top))
841
897
 
898
+ @staticmethod
899
+ def _estimate_text_width(text: str, font_size: int) -> float:
900
+ """Very rough text width estimation in pixels based on font size."""
901
+ if not text:
902
+ return 0.0
903
+ average_char_width = float(font_size)
904
+ return max(float(font_size), len(text) * average_char_width)
905
+
906
+ def _get_active_point_display_names(self) -> list[str]:
907
+ """Return localized labels for the currently active celestial points."""
908
+ language_map = {}
909
+ fallback_map = {}
910
+
911
+ if hasattr(self, "_language_model"):
912
+ language_map = self._language_model.celestial_points.model_dump()
913
+ if hasattr(self, "_fallback_language_model"):
914
+ fallback_map = self._fallback_language_model.celestial_points.model_dump()
915
+
916
+ display_names: list[str] = []
917
+ for point in self.active_points:
918
+ key = str(point)
919
+ label = language_map.get(key) or fallback_map.get(key) or key
920
+ display_names.append(str(label))
921
+ return display_names
922
+
923
+ def _estimate_house_comparison_grid_width(
924
+ self,
925
+ *,
926
+ column_labels: Sequence[str],
927
+ include_radix_column: bool,
928
+ include_title: bool,
929
+ minimum_width: float = 250.0,
930
+ ) -> int:
931
+ """
932
+ Approximate the rendered width for a house comparison grid in the current locale.
933
+
934
+ Args:
935
+ column_labels: Ordered labels for the header row.
936
+ include_radix_column: Whether a third numeric column is rendered.
937
+ include_title: Include the localized title in the width estimation.
938
+ minimum_width: Absolute lower bound to prevent extreme shrinking.
939
+ """
940
+ font_size_body = 10
941
+ font_size_title = 14
942
+ minimum_grid_width = float(minimum_width)
943
+
944
+ active_names = self._get_active_point_display_names()
945
+ max_name_width = max(
946
+ (self._estimate_text_width(name, font_size_body) for name in active_names),
947
+ default=self._estimate_text_width("Sun", font_size_body),
948
+ )
949
+ width_candidates: list[float] = []
950
+
951
+ name_start = 15
952
+ width_candidates.append(name_start + max_name_width)
953
+
954
+ value_offsets = [90]
955
+ if include_radix_column:
956
+ value_offsets.append(140)
957
+ value_samples = ("12", "-", "0")
958
+ max_value_width = max((self._estimate_text_width(sample, font_size_body) for sample in value_samples))
959
+ for offset in value_offsets:
960
+ width_candidates.append(offset + max_value_width)
961
+
962
+ header_offsets = [0, 77]
963
+ if include_radix_column:
964
+ header_offsets.append(132)
965
+ for idx, offset in enumerate(header_offsets):
966
+ label = column_labels[idx] if idx < len(column_labels) else ""
967
+ if not label:
968
+ continue
969
+ width_candidates.append(offset + self._estimate_text_width(label, font_size_body))
970
+
971
+ if include_title:
972
+ title_label = self._translate("house_position_comparison", "House Position Comparison")
973
+ width_candidates.append(self._estimate_text_width(title_label, font_size_title))
974
+
975
+ grid_width = max(width_candidates, default=minimum_grid_width)
976
+ return int(max(grid_width, minimum_grid_width))
977
+
842
978
  def _minimum_width_for_chart_type(self) -> int:
843
979
  """Baseline width to avoid compressing core groups too tightly."""
844
980
  wheel_right = 100 + (2 * self.main_radius)
@@ -851,7 +987,7 @@ class ChartDrawer:
851
987
  if self.chart_type == "DualReturnChart":
852
988
  return max(int(baseline), self._DEFAULT_ULTRA_WIDE_WIDTH // 2)
853
989
  if self.chart_type == "Transit":
854
- return max(int(baseline), self._DEFAULT_FULL_WIDTH // 2)
990
+ return max(int(baseline), 450)
855
991
  return max(int(baseline), self._DEFAULT_NATAL_WIDTH)
856
992
 
857
993
  def _update_width_to_content(self) -> None:
@@ -21,41 +21,41 @@
21
21
  <g kr:node="Main_Chart">
22
22
  <g kr:node="Top_Left_Text" transform="translate(0,$title_translate_y)">
23
23
  <text x="20" y="22" style="fill: $paper_color_0; font-size: 24px">$stringTitle</text>
24
- <text x="20" y="50" style="fill: $paper_color_0; font-size: 10px">$top_left_0</text>
25
- <text x="20" y="62" style="fill: $paper_color_0; font-size: 10px">$top_left_1</text>
26
- <text x="20" y="74" style="fill: $paper_color_0; font-size: 10px">$top_left_2</text>
27
- <text x="20" y="86" style="fill: $paper_color_0; font-size: 10px">$top_left_3</text>
28
- <text x="20" y="98" style="fill: $paper_color_0; font-size: 10px">$top_left_4</text>
29
- <text x="20" y="110" style="fill: $paper_color_0; font-size: 10px">$top_left_5</text>
24
+ <text x="20" y="58" style="fill: $paper_color_0; font-size: 10px">$top_left_0</text>
25
+ <text x="20" y="70" style="fill: $paper_color_0; font-size: 10px">$top_left_1</text>
26
+ <text x="20" y="82" style="fill: $paper_color_0; font-size: 10px">$top_left_2</text>
27
+ <text x="20" y="94" style="fill: $paper_color_0; font-size: 10px">$top_left_3</text>
28
+ <text x="20" y="106" style="fill: $paper_color_0; font-size: 10px">$top_left_4</text>
29
+ <text x="20" y="118" style="fill: $paper_color_0; font-size: 10px">$top_left_5</text>
30
30
  </g>
31
31
 
32
32
  <!-- Elements -->
33
33
  <g kr:node="Elements_Percentages" transform="translate(0,$elements_translate_y)">
34
- <text x="20" y="130" style="fill: $paper_color_0; font-size: 10px;">$elements_string</text>
35
- <text x="20" y="144"
34
+ <text x="20" y="138" style="fill: $paper_color_0; font-size: 10px;">$elements_string</text>
35
+ <text x="20" y="152"
36
36
  style="fill: var(--kerykeion-chart-color-fire-percentage); font-size: 10px;">
37
37
  $fire_string</text>
38
- <text x="20" y="156"
38
+ <text x="20" y="164"
39
39
  style="fill: var(--kerykeion-chart-color-earth-percentage); font-size: 10px;">
40
40
  $earth_string</text>
41
- <text x="20" y="168"
41
+ <text x="20" y="176"
42
42
  style="fill: var(--kerykeion-chart-color-air-percentage); font-size: 10px;">
43
43
  $air_string</text>
44
- <text x="20" y="180"
44
+ <text x="20" y="188"
45
45
  style="fill: var(--kerykeion-chart-color-water-percentage); font-size: 10px;">
46
46
  $water_string</text>
47
47
  </g>
48
48
 
49
49
  <!-- Qualities -->
50
50
  <g kr:node="Qualities_Percentages" transform="translate(0,$qualities_translate_y)">
51
- <text x="20" y="200" style="fill: $paper_color_0; font-size: 10px;">$qualities_string</text>
52
- <text x="20" y="214"
51
+ <text x="20" y="208" style="fill: $paper_color_0; font-size: 10px;">$qualities_string</text>
52
+ <text x="20" y="222"
53
53
  style="fill: var(--kerykeion-chart-color-cardinal-percentage); font-size: 10px;">
54
54
  $cardinal_string</text>
55
- <text x="20" y="226"
55
+ <text x="20" y="234"
56
56
  style="fill: var(--kerykeion-chart-color-fixed-percentage); font-size: 10px;">
57
57
  $fixed_string</text>
58
- <text x="20" y="238"
58
+ <text x="20" y="246"
59
59
  style="fill: var(--kerykeion-chart-color-mutable-percentage); font-size: 10px;">
60
60
  $mutable_string</text>
61
61
  </g>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kerykeion
3
- Version: 5.1.8
3
+ Version: 5.1.10
4
4
  Summary: A Python library for astrological calculations, including natal charts, houses, planetary aspects, and SVG chart generation.
5
5
  Project-URL: Homepage, https://www.kerykeion.net/
6
6
  Project-URL: Repository, https://github.com/g-battaglia/kerykeion
@@ -14,11 +14,11 @@ kerykeion/aspects/__init__.py,sha256=csJmxvLdBu-bHuW676f3dpY__Qyc6LwRyrpWVTh3n1A
14
14
  kerykeion/aspects/aspects_factory.py,sha256=XCuOOpo0ZW7sihYT2f50bLVpebEk19b9EtEjXonwte0,24156
15
15
  kerykeion/aspects/aspects_utils.py,sha256=2YloOKOmN0h2d6J8A1vCe-y4A2mWNoOUYCGDVwGl4gA,5794
16
16
  kerykeion/charts/__init__.py,sha256=i9NMZ7LdkllPlqQSi1or9gTobHbROGDKmJhBDO4R0mA,128
17
- kerykeion/charts/chart_drawer.py,sha256=C3NI-_2UugD9YWnakq3asICJolX9gsQLM2aP3n6hOq4,124658
17
+ kerykeion/charts/chart_drawer.py,sha256=AQGtJsUWRm_D-RH1w1Q9GRD6cRtgmjDQN1e-UMOtO3Y,130645
18
18
  kerykeion/charts/charts_utils.py,sha256=iwgDhVc_GwKuQBZ6JNmn6RWVILD5KIcjbrnDTR3wrAc,70911
19
19
  kerykeion/charts/draw_planets.py,sha256=tIj3FeLLomVSbCaZUxfw_qBEB3pxi4EIEhqaeTgTyTY,29061
20
20
  kerykeion/charts/templates/aspect_grid_only.xml,sha256=v3QtNMjk-kBdUTfB0r6thg--Ta_tNFdRQCzdk5PAycY,86429
21
- kerykeion/charts/templates/chart.xml,sha256=oUI9hV--4rdsVFqZJ8ChpgC2ZgfnqlL2Tl2vco1vlOc,92095
21
+ kerykeion/charts/templates/chart.xml,sha256=QhCSkORLw61cXycF2fMmCLsSailPyIxLbVtsR-CXsKI,92096
22
22
  kerykeion/charts/templates/wheel_only.xml,sha256=05k2KiVMP-5NW0e8PZrTX-VPn5x-SaNzEYPgU1_OKa0,87775
23
23
  kerykeion/charts/themes/black-and-white.css,sha256=IIoy9wyB4znrvPvmallKmgDW4HMJPywTv5IZsolaEp4,6543
24
24
  kerykeion/charts/themes/classic.css,sha256=xvBiI4DtY5fMaA7ilxGl26VEDY1VzkOTWm1XOs9n-ec,5158
@@ -57,7 +57,7 @@ kerykeion/sweph/ast28/se28978s.se1,sha256=nU2Qp-ELc_tzFnRc1QT6uVueWXEipvhYDgfQRX
57
57
  kerykeion/sweph/ast50/se50000s.se1,sha256=9jTrPlIrZMOBWC9cNgwzcfz0KBHdXFZoY9-NZ_HtECo,15748
58
58
  kerykeion/sweph/ast90/se90377s.se1,sha256=bto2x4LtBv8b1ej1XhVFYq-kfHO9cczbKV9U1f9UVu4,10288
59
59
  kerykeion/sweph/ast90/se90482s.se1,sha256=uHxz6bP4K8zgtQFrlWFwxrYfmqm5kXxsg6OYhAIUbAA,16173
60
- kerykeion-5.1.8.dist-info/METADATA,sha256=l4rJD4aLJrCesNRr_LmF9RDRMSQP7uKrV6hA6DxLvAs,61802
61
- kerykeion-5.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
62
- kerykeion-5.1.8.dist-info/licenses/LICENSE,sha256=UTLH8EdbAsgQei4PA2PnBCPGLSZkq5J-dhkyJuXgWQU,34273
63
- kerykeion-5.1.8.dist-info/RECORD,,
60
+ kerykeion-5.1.10.dist-info/METADATA,sha256=KMaQJSdjbOrF-W6uowiZUDoN-WHDAMy3oCH7PO3kS1I,61803
61
+ kerykeion-5.1.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
62
+ kerykeion-5.1.10.dist-info/licenses/LICENSE,sha256=UTLH8EdbAsgQei4PA2PnBCPGLSZkq5J-dhkyJuXgWQU,34273
63
+ kerykeion-5.1.10.dist-info/RECORD,,