typsphinx 0.4.1__tar.gz → 0.4.2__tar.gz
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.
- {typsphinx-0.4.1 → typsphinx-0.4.2}/PKG-INFO +2 -2
- {typsphinx-0.4.1 → typsphinx-0.4.2}/README.md +1 -1
- {typsphinx-0.4.1 → typsphinx-0.4.2}/pyproject.toml +1 -1
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_translator.py +360 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx/translator.py +116 -4
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx.egg-info/PKG-INFO +2 -2
- {typsphinx-0.4.1 → typsphinx-0.4.2}/LICENSE +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/setup.cfg +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_admonitions.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_builder.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_builder_requirement13.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_config.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_config_other_options.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_config_template_mapping.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_config_toctree_defaults.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_documentation_configuration.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_documentation_installation.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_documentation_usage.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_entry_points.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_examples_basic.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_extension.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_inline_references.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_integration_advanced.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_integration_basic.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_integration_multi_doc.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_integration_nested_toctree.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_math_fallback.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_math_mitex.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_math_native.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_nested_toctree_paths.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_pdf_generation.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_template_codly.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_template_engine.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_template_mitex.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/tests/test_toctree_requirement13.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx/__init__.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx/builder.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx/pdf.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx/template_engine.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx/templates/base.typ +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx/writer.py +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx.egg-info/SOURCES.txt +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx.egg-info/dependency_links.txt +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx.egg-info/entry_points.txt +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx.egg-info/requires.txt +0 -0
- {typsphinx-0.4.1 → typsphinx-0.4.2}/typsphinx.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: typsphinx
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Sphinx extension for Typst output
|
|
5
5
|
Author-email: Sphinx Typst Contributors <noreply@example.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -364,5 +364,5 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
|
|
|
364
364
|
|
|
365
365
|
---
|
|
366
366
|
|
|
367
|
-
**Status**: Stable (v0.4.
|
|
367
|
+
**Status**: Stable (v0.4.2) - Production ready
|
|
368
368
|
**Python**: 3.9+ | **Sphinx**: 5.0+ | **Typst**: 0.11.1+
|
|
@@ -2622,6 +2622,221 @@ def test_table_normal_cells_without_spanning(simple_document, mock_builder):
|
|
|
2622
2622
|
assert "Cell 2" in output
|
|
2623
2623
|
|
|
2624
2624
|
|
|
2625
|
+
# Empty table cell tests (Issue #68)
|
|
2626
|
+
|
|
2627
|
+
|
|
2628
|
+
def test_table_empty_cells(simple_document, mock_builder):
|
|
2629
|
+
"""Test that empty table cells are wrapped in content blocks.
|
|
2630
|
+
|
|
2631
|
+
Related to Issue #68: Empty table cells cause Typst compilation errors.
|
|
2632
|
+
Empty cells should be wrapped in {} to generate valid Typst syntax.
|
|
2633
|
+
"""
|
|
2634
|
+
from typsphinx.translator import TypstTranslator
|
|
2635
|
+
|
|
2636
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
2637
|
+
|
|
2638
|
+
# Create table with all empty cells
|
|
2639
|
+
table = nodes.table()
|
|
2640
|
+
tgroup = nodes.tgroup(cols=2)
|
|
2641
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
2642
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
2643
|
+
|
|
2644
|
+
tbody = nodes.tbody()
|
|
2645
|
+
row1 = nodes.row()
|
|
2646
|
+
entry1 = nodes.entry() # Empty cell
|
|
2647
|
+
entry2 = nodes.entry() # Empty cell
|
|
2648
|
+
row1 += entry1
|
|
2649
|
+
row1 += entry2
|
|
2650
|
+
tbody += row1
|
|
2651
|
+
tgroup += tbody
|
|
2652
|
+
table += tgroup
|
|
2653
|
+
|
|
2654
|
+
table.walkabout(translator)
|
|
2655
|
+
output = translator.astext()
|
|
2656
|
+
|
|
2657
|
+
# Should contain empty content blocks
|
|
2658
|
+
assert "{}" in output
|
|
2659
|
+
# Should NOT contain bare commas
|
|
2660
|
+
assert ",," not in output
|
|
2661
|
+
assert " ,\n" not in output
|
|
2662
|
+
|
|
2663
|
+
|
|
2664
|
+
def test_table_mixed_empty_and_content(simple_document, mock_builder):
|
|
2665
|
+
"""Test table with both empty and non-empty cells.
|
|
2666
|
+
|
|
2667
|
+
Related to Issue #68: Empty table cells cause Typst compilation errors.
|
|
2668
|
+
Tables with mixed empty and non-empty cells should generate valid Typst syntax.
|
|
2669
|
+
"""
|
|
2670
|
+
from typsphinx.translator import TypstTranslator
|
|
2671
|
+
|
|
2672
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
2673
|
+
|
|
2674
|
+
# Create table: A | (empty) / (empty) | D
|
|
2675
|
+
table = nodes.table()
|
|
2676
|
+
tgroup = nodes.tgroup(cols=2)
|
|
2677
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
2678
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
2679
|
+
|
|
2680
|
+
tbody = nodes.tbody()
|
|
2681
|
+
|
|
2682
|
+
# Row 1: A | empty
|
|
2683
|
+
row1 = nodes.row()
|
|
2684
|
+
entry1 = nodes.entry()
|
|
2685
|
+
entry1 += nodes.paragraph(text="A")
|
|
2686
|
+
entry2 = nodes.entry() # Empty
|
|
2687
|
+
row1 += entry1
|
|
2688
|
+
row1 += entry2
|
|
2689
|
+
tbody += row1
|
|
2690
|
+
|
|
2691
|
+
# Row 2: empty | D
|
|
2692
|
+
row2 = nodes.row()
|
|
2693
|
+
entry3 = nodes.entry() # Empty
|
|
2694
|
+
entry4 = nodes.entry()
|
|
2695
|
+
entry4 += nodes.paragraph(text="D")
|
|
2696
|
+
row2 += entry3
|
|
2697
|
+
row2 += entry4
|
|
2698
|
+
tbody += row2
|
|
2699
|
+
|
|
2700
|
+
tgroup += tbody
|
|
2701
|
+
table += tgroup
|
|
2702
|
+
|
|
2703
|
+
table.walkabout(translator)
|
|
2704
|
+
output = translator.astext()
|
|
2705
|
+
|
|
2706
|
+
# Should contain content for non-empty cells
|
|
2707
|
+
assert "A" in output
|
|
2708
|
+
assert "D" in output
|
|
2709
|
+
# Should contain empty content blocks
|
|
2710
|
+
assert "{}" in output
|
|
2711
|
+
# Should NOT contain bare commas
|
|
2712
|
+
assert ",," not in output
|
|
2713
|
+
|
|
2714
|
+
|
|
2715
|
+
def test_table_empty_colspan_cells(simple_document, mock_builder):
|
|
2716
|
+
"""Test that empty cells with colspan use table.cell with empty content.
|
|
2717
|
+
|
|
2718
|
+
Related to Issue #68: Empty table cells cause Typst compilation errors.
|
|
2719
|
+
Empty cells with colspan should use table.cell({}, colspan: N) syntax.
|
|
2720
|
+
"""
|
|
2721
|
+
from typsphinx.translator import TypstTranslator
|
|
2722
|
+
|
|
2723
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
2724
|
+
|
|
2725
|
+
# Create table with empty colspan cell
|
|
2726
|
+
table = nodes.table()
|
|
2727
|
+
tgroup = nodes.tgroup(cols=2)
|
|
2728
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
2729
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
2730
|
+
|
|
2731
|
+
tbody = nodes.tbody()
|
|
2732
|
+
row1 = nodes.row()
|
|
2733
|
+
|
|
2734
|
+
# Empty cell with colspan=2
|
|
2735
|
+
entry1 = nodes.entry(morecols=1) # Empty, spans 2 columns
|
|
2736
|
+
row1 += entry1
|
|
2737
|
+
tbody += row1
|
|
2738
|
+
tgroup += tbody
|
|
2739
|
+
table += tgroup
|
|
2740
|
+
|
|
2741
|
+
table.walkabout(translator)
|
|
2742
|
+
output = translator.astext()
|
|
2743
|
+
|
|
2744
|
+
# Should use table.cell with empty content
|
|
2745
|
+
assert "table.cell({}" in output
|
|
2746
|
+
assert "colspan: 2" in output
|
|
2747
|
+
# Should NOT contain bare commas or missing content
|
|
2748
|
+
assert "table.cell(," not in output
|
|
2749
|
+
|
|
2750
|
+
|
|
2751
|
+
def test_table_empty_rowspan_cells(simple_document, mock_builder):
|
|
2752
|
+
"""Test that empty cells with rowspan use table.cell with empty content.
|
|
2753
|
+
|
|
2754
|
+
Related to Issue #68: Empty table cells cause Typst compilation errors.
|
|
2755
|
+
Empty cells with rowspan should use table.cell({}, rowspan: N) syntax.
|
|
2756
|
+
"""
|
|
2757
|
+
from typsphinx.translator import TypstTranslator
|
|
2758
|
+
|
|
2759
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
2760
|
+
|
|
2761
|
+
# Create table with empty rowspan cell
|
|
2762
|
+
table = nodes.table()
|
|
2763
|
+
tgroup = nodes.tgroup(cols=2)
|
|
2764
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
2765
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
2766
|
+
|
|
2767
|
+
tbody = nodes.tbody()
|
|
2768
|
+
row1 = nodes.row()
|
|
2769
|
+
|
|
2770
|
+
# Empty cell with rowspan=2
|
|
2771
|
+
entry1 = nodes.entry(morerows=1) # Empty, spans 2 rows
|
|
2772
|
+
entry2 = nodes.entry()
|
|
2773
|
+
entry2 += nodes.paragraph(text="B")
|
|
2774
|
+
row1 += entry1
|
|
2775
|
+
row1 += entry2
|
|
2776
|
+
tbody += row1
|
|
2777
|
+
|
|
2778
|
+
# Row 2
|
|
2779
|
+
row2 = nodes.row()
|
|
2780
|
+
entry3 = nodes.entry()
|
|
2781
|
+
entry3 += nodes.paragraph(text="C")
|
|
2782
|
+
row2 += entry3
|
|
2783
|
+
tbody += row2
|
|
2784
|
+
|
|
2785
|
+
tgroup += tbody
|
|
2786
|
+
table += tgroup
|
|
2787
|
+
|
|
2788
|
+
table.walkabout(translator)
|
|
2789
|
+
output = translator.astext()
|
|
2790
|
+
|
|
2791
|
+
# Should use table.cell with empty content
|
|
2792
|
+
assert "table.cell({}" in output
|
|
2793
|
+
assert "rowspan: 2" in output
|
|
2794
|
+
# Should NOT contain bare commas or missing content
|
|
2795
|
+
assert "table.cell(," not in output
|
|
2796
|
+
|
|
2797
|
+
|
|
2798
|
+
def test_table_empty_colspan_rowspan_cells(simple_document, mock_builder):
|
|
2799
|
+
"""Test that empty cells with both colspan and rowspan use table.cell with empty content.
|
|
2800
|
+
|
|
2801
|
+
Related to Issue #68: Empty table cells cause Typst compilation errors.
|
|
2802
|
+
Empty cells with both colspan and rowspan should use table.cell({}, colspan: M, rowspan: N) syntax.
|
|
2803
|
+
"""
|
|
2804
|
+
from typsphinx.translator import TypstTranslator
|
|
2805
|
+
|
|
2806
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
2807
|
+
|
|
2808
|
+
# Create table with empty colspan+rowspan cell
|
|
2809
|
+
table = nodes.table()
|
|
2810
|
+
tgroup = nodes.tgroup(cols=2)
|
|
2811
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
2812
|
+
tgroup += nodes.colspec(colwidth=1)
|
|
2813
|
+
|
|
2814
|
+
tbody = nodes.tbody()
|
|
2815
|
+
row1 = nodes.row()
|
|
2816
|
+
|
|
2817
|
+
# Empty cell with colspan=2, rowspan=2
|
|
2818
|
+
entry1 = nodes.entry(morecols=1, morerows=1) # Empty, spans 2x2
|
|
2819
|
+
row1 += entry1
|
|
2820
|
+
tbody += row1
|
|
2821
|
+
|
|
2822
|
+
# Row 2 (no cells needed due to spanning)
|
|
2823
|
+
row2 = nodes.row()
|
|
2824
|
+
tbody += row2
|
|
2825
|
+
|
|
2826
|
+
tgroup += tbody
|
|
2827
|
+
table += tgroup
|
|
2828
|
+
|
|
2829
|
+
table.walkabout(translator)
|
|
2830
|
+
output = translator.astext()
|
|
2831
|
+
|
|
2832
|
+
# Should use table.cell with empty content
|
|
2833
|
+
assert "table.cell({}" in output
|
|
2834
|
+
assert "colspan: 2" in output
|
|
2835
|
+
assert "rowspan: 2" in output
|
|
2836
|
+
# Should NOT contain bare commas or missing content
|
|
2837
|
+
assert "table.cell(," not in output
|
|
2838
|
+
|
|
2839
|
+
|
|
2625
2840
|
# API description nodes tests (Issue #55)
|
|
2626
2841
|
|
|
2627
2842
|
|
|
@@ -2858,3 +3073,148 @@ def test_full_api_description_structure(simple_document, mock_builder):
|
|
|
2858
3073
|
assert "Builder class for Typst output." in output
|
|
2859
3074
|
assert 'strong(text("Parameters")' in output or "Parameters" in output
|
|
2860
3075
|
assert "app - Sphinx application" in output
|
|
3076
|
+
|
|
3077
|
+
|
|
3078
|
+
# Image path adjustment tests (Issue #69)
|
|
3079
|
+
|
|
3080
|
+
|
|
3081
|
+
def test_image_path_adjustment_root(simple_document, mock_builder):
|
|
3082
|
+
"""Test that root document image paths are not adjusted.
|
|
3083
|
+
|
|
3084
|
+
Related to Issue #69: Image paths in root documents should remain unchanged.
|
|
3085
|
+
"""
|
|
3086
|
+
from typsphinx.translator import TypstTranslator
|
|
3087
|
+
|
|
3088
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
3089
|
+
|
|
3090
|
+
# Set current_docname to root document
|
|
3091
|
+
mock_builder.current_docname = "index"
|
|
3092
|
+
|
|
3093
|
+
# Create image node
|
|
3094
|
+
image = nodes.image(uri="images/logo.png")
|
|
3095
|
+
image["width"] = "200px"
|
|
3096
|
+
|
|
3097
|
+
image.walkabout(translator)
|
|
3098
|
+
output = translator.astext()
|
|
3099
|
+
|
|
3100
|
+
# Root document: path should NOT be adjusted
|
|
3101
|
+
assert 'image("images/logo.png"' in output
|
|
3102
|
+
assert "../images" not in output
|
|
3103
|
+
assert "width: 200px" in output
|
|
3104
|
+
|
|
3105
|
+
|
|
3106
|
+
def test_image_path_adjustment_nested(simple_document, mock_builder):
|
|
3107
|
+
"""Test that nested document image paths are adjusted with ../ prefix.
|
|
3108
|
+
|
|
3109
|
+
Related to Issue #69: Nested documents need relative path adjustment.
|
|
3110
|
+
"""
|
|
3111
|
+
from typsphinx.translator import TypstTranslator
|
|
3112
|
+
|
|
3113
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
3114
|
+
|
|
3115
|
+
# Set current_docname to nested document
|
|
3116
|
+
mock_builder.current_docname = "chapter1/section1"
|
|
3117
|
+
|
|
3118
|
+
# Create image node with source-root-relative path
|
|
3119
|
+
image = nodes.image(uri="images/logo.png")
|
|
3120
|
+
image["width"] = "200px"
|
|
3121
|
+
|
|
3122
|
+
image.walkabout(translator)
|
|
3123
|
+
output = translator.astext()
|
|
3124
|
+
|
|
3125
|
+
# Nested document: path should be adjusted
|
|
3126
|
+
assert 'image("../images/logo.png"' in output
|
|
3127
|
+
assert "width: 200px" in output
|
|
3128
|
+
|
|
3129
|
+
|
|
3130
|
+
def test_image_path_adjustment_deep_nested(simple_document, mock_builder):
|
|
3131
|
+
"""Test that deeply nested documents get correct number of ../ prefixes.
|
|
3132
|
+
|
|
3133
|
+
Related to Issue #69: Deep nesting requires multiple ../ levels.
|
|
3134
|
+
"""
|
|
3135
|
+
from typsphinx.translator import TypstTranslator
|
|
3136
|
+
|
|
3137
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
3138
|
+
|
|
3139
|
+
# Set current_docname to deeply nested document
|
|
3140
|
+
mock_builder.current_docname = "part1/chapter1/section1"
|
|
3141
|
+
|
|
3142
|
+
# Create image node
|
|
3143
|
+
image = nodes.image(uri="images/logo.png")
|
|
3144
|
+
|
|
3145
|
+
image.walkabout(translator)
|
|
3146
|
+
output = translator.astext()
|
|
3147
|
+
|
|
3148
|
+
# Deeply nested: should have ../../ prefix
|
|
3149
|
+
assert 'image("../../images/logo.png"' in output
|
|
3150
|
+
|
|
3151
|
+
|
|
3152
|
+
def test_image_path_adjustment_cross_directory(simple_document, mock_builder):
|
|
3153
|
+
"""Test that cross-directory image references are correctly adjusted.
|
|
3154
|
+
|
|
3155
|
+
Related to Issue #69: References from chapter1/ to chapter2/ images.
|
|
3156
|
+
"""
|
|
3157
|
+
from typsphinx.translator import TypstTranslator
|
|
3158
|
+
|
|
3159
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
3160
|
+
|
|
3161
|
+
# Set current document in chapter1
|
|
3162
|
+
mock_builder.current_docname = "chapter1/section1"
|
|
3163
|
+
|
|
3164
|
+
# Reference image in chapter2
|
|
3165
|
+
image = nodes.image(uri="chapter2/images/diagram.png")
|
|
3166
|
+
|
|
3167
|
+
image.walkabout(translator)
|
|
3168
|
+
output = translator.astext()
|
|
3169
|
+
|
|
3170
|
+
# Cross-directory: ../chapter2/images/diagram.png
|
|
3171
|
+
assert 'image("../chapter2/images/diagram.png"' in output
|
|
3172
|
+
|
|
3173
|
+
|
|
3174
|
+
def test_image_path_adjustment_same_directory(simple_document, mock_builder):
|
|
3175
|
+
"""Test that same-directory images use simple relative paths.
|
|
3176
|
+
|
|
3177
|
+
Related to Issue #69: Images in the same directory as the document.
|
|
3178
|
+
"""
|
|
3179
|
+
from typsphinx.translator import TypstTranslator
|
|
3180
|
+
|
|
3181
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
3182
|
+
|
|
3183
|
+
# Both in chapter1/
|
|
3184
|
+
mock_builder.current_docname = "chapter1/section1"
|
|
3185
|
+
|
|
3186
|
+
# Image also in chapter1/
|
|
3187
|
+
image = nodes.image(uri="chapter1/local-image.png")
|
|
3188
|
+
|
|
3189
|
+
image.walkabout(translator)
|
|
3190
|
+
output = translator.astext()
|
|
3191
|
+
|
|
3192
|
+
# Same directory: just the filename
|
|
3193
|
+
assert 'image("local-image.png"' in output
|
|
3194
|
+
assert "../" not in output
|
|
3195
|
+
|
|
3196
|
+
|
|
3197
|
+
def test_image_path_adjustment_subdirectory(simple_document, mock_builder):
|
|
3198
|
+
"""Test that subdirectory images use relative paths to child folders.
|
|
3199
|
+
|
|
3200
|
+
Related to Issue #69: Images in subdirectories relative to the document.
|
|
3201
|
+
Example: chapter1/section1.rst references chapter1/img/diagram.jpeg
|
|
3202
|
+
"""
|
|
3203
|
+
from typsphinx.translator import TypstTranslator
|
|
3204
|
+
|
|
3205
|
+
translator = TypstTranslator(simple_document, mock_builder)
|
|
3206
|
+
|
|
3207
|
+
# Document in chapter1/
|
|
3208
|
+
mock_builder.current_docname = "chapter1/section1"
|
|
3209
|
+
|
|
3210
|
+
# Image in chapter1/img/ (subdirectory of same directory)
|
|
3211
|
+
image = nodes.image(uri="chapter1/img/diagram.jpeg")
|
|
3212
|
+
image["width"] = "250px"
|
|
3213
|
+
|
|
3214
|
+
image.walkabout(translator)
|
|
3215
|
+
output = translator.astext()
|
|
3216
|
+
|
|
3217
|
+
# Subdirectory: img/diagram.jpeg (relative to current directory)
|
|
3218
|
+
assert 'image("img/diagram.jpeg"' in output
|
|
3219
|
+
assert "width: 250px" in output
|
|
3220
|
+
assert "../" not in output # No need to go up
|
|
@@ -1215,7 +1215,7 @@ class TypstTranslator(SphinxTranslator):
|
|
|
1215
1215
|
|
|
1216
1216
|
# Normal cell (no spanning)
|
|
1217
1217
|
if colspan == 1 and rowspan == 1:
|
|
1218
|
-
return f"{indent}{content},\n"
|
|
1218
|
+
return f"{indent}{{{content}}},\n"
|
|
1219
1219
|
|
|
1220
1220
|
# Cell with spanning - use table.cell()
|
|
1221
1221
|
params = []
|
|
@@ -1225,7 +1225,7 @@ class TypstTranslator(SphinxTranslator):
|
|
|
1225
1225
|
params.append(f"rowspan: {rowspan}")
|
|
1226
1226
|
|
|
1227
1227
|
params_str = ", ".join(params)
|
|
1228
|
-
return f"{indent}table.cell({content}, {params_str}),\n"
|
|
1228
|
+
return f"{indent}table.cell({{{content}}}, {params_str}),\n"
|
|
1229
1229
|
|
|
1230
1230
|
def depart_table(self, node: nodes.table) -> None:
|
|
1231
1231
|
"""
|
|
@@ -1464,18 +1464,25 @@ class TypstTranslator(SphinxTranslator):
|
|
|
1464
1464
|
Visit an image node.
|
|
1465
1465
|
|
|
1466
1466
|
Generates image() function call (no # prefix in code mode).
|
|
1467
|
+
Adjusts image paths for nested documents (Issue #69).
|
|
1467
1468
|
|
|
1468
1469
|
Args:
|
|
1469
1470
|
node: The image node
|
|
1470
1471
|
"""
|
|
1471
1472
|
uri = node.get("uri", "")
|
|
1472
1473
|
|
|
1474
|
+
# Get current document name for path adjustment (Issue #69)
|
|
1475
|
+
current_docname = getattr(self.builder, "current_docname", None)
|
|
1476
|
+
|
|
1477
|
+
# Adjust path based on output file location (Issue #69)
|
|
1478
|
+
adjusted_uri = self._compute_relative_image_path(uri, current_docname)
|
|
1479
|
+
|
|
1473
1480
|
# Add proper indentation if inside a figure
|
|
1474
1481
|
if self.in_figure:
|
|
1475
|
-
self.add_text(f' image("{
|
|
1482
|
+
self.add_text(f' image("{adjusted_uri}"')
|
|
1476
1483
|
else:
|
|
1477
1484
|
# No # prefix in code mode
|
|
1478
|
-
self.add_text(f'image("{
|
|
1485
|
+
self.add_text(f'image("{adjusted_uri}"')
|
|
1479
1486
|
|
|
1480
1487
|
# Add optional attributes
|
|
1481
1488
|
if "width" in node:
|
|
@@ -1699,6 +1706,111 @@ class TypstTranslator(SphinxTranslator):
|
|
|
1699
1706
|
|
|
1700
1707
|
return relative_path
|
|
1701
1708
|
|
|
1709
|
+
def _compute_relative_image_path(
|
|
1710
|
+
self, image_uri: str, current_docname: Optional[str]
|
|
1711
|
+
) -> str:
|
|
1712
|
+
"""
|
|
1713
|
+
Compute relative path for image() function.
|
|
1714
|
+
|
|
1715
|
+
Adjusts image URIs from source-root-relative to output-file-relative.
|
|
1716
|
+
This is similar to _compute_relative_include_path() but for images.
|
|
1717
|
+
|
|
1718
|
+
Args:
|
|
1719
|
+
image_uri: Image URI from Sphinx (source-root-relative)
|
|
1720
|
+
current_docname: Current document name (e.g., "chapter1/section1")
|
|
1721
|
+
|
|
1722
|
+
Returns:
|
|
1723
|
+
Adjusted relative path for Typst image()
|
|
1724
|
+
|
|
1725
|
+
Examples:
|
|
1726
|
+
>>> _compute_relative_image_path("images/logo.png", "chapter1/section1")
|
|
1727
|
+
"../images/logo.png"
|
|
1728
|
+
>>> _compute_relative_image_path("images/logo.png", "index")
|
|
1729
|
+
"images/logo.png"
|
|
1730
|
+
>>> _compute_relative_image_path("images/logo.png", None)
|
|
1731
|
+
"images/logo.png"
|
|
1732
|
+
|
|
1733
|
+
Notes:
|
|
1734
|
+
This implements Issue #69 fix for nested document image paths.
|
|
1735
|
+
Uses the same logic as _compute_relative_include_path() from Issue #5.
|
|
1736
|
+
"""
|
|
1737
|
+
from pathlib import PurePosixPath
|
|
1738
|
+
|
|
1739
|
+
logger.debug(
|
|
1740
|
+
f"Computing relative image path: uri={image_uri}, "
|
|
1741
|
+
f"current={current_docname}"
|
|
1742
|
+
)
|
|
1743
|
+
|
|
1744
|
+
# Fallback to absolute path if current_docname is None
|
|
1745
|
+
if not current_docname:
|
|
1746
|
+
logger.debug(f"No current document, using absolute path: {image_uri}")
|
|
1747
|
+
return image_uri
|
|
1748
|
+
|
|
1749
|
+
current_path = PurePosixPath(current_docname)
|
|
1750
|
+
image_path = PurePosixPath(image_uri)
|
|
1751
|
+
current_dir = current_path.parent
|
|
1752
|
+
|
|
1753
|
+
logger.debug(
|
|
1754
|
+
f"Path components: current_dir={current_dir}, image_path={image_path}"
|
|
1755
|
+
)
|
|
1756
|
+
|
|
1757
|
+
# Root directory case: use absolute path (backward compatibility)
|
|
1758
|
+
if current_dir == PurePosixPath("."):
|
|
1759
|
+
logger.debug(
|
|
1760
|
+
f"Current document is in root directory, "
|
|
1761
|
+
f"using absolute path: {image_uri}"
|
|
1762
|
+
)
|
|
1763
|
+
return image_uri
|
|
1764
|
+
|
|
1765
|
+
# Try to compute relative path
|
|
1766
|
+
try:
|
|
1767
|
+
rel_path = image_path.relative_to(current_dir)
|
|
1768
|
+
result = str(rel_path)
|
|
1769
|
+
logger.debug(
|
|
1770
|
+
f"Same directory reference: {current_dir} -> {image_path}, "
|
|
1771
|
+
f"result: {result}"
|
|
1772
|
+
)
|
|
1773
|
+
return result
|
|
1774
|
+
except ValueError:
|
|
1775
|
+
# Different directory trees - build path via common parent
|
|
1776
|
+
logger.debug(
|
|
1777
|
+
"Cross-directory reference detected, calculating via common parent"
|
|
1778
|
+
)
|
|
1779
|
+
|
|
1780
|
+
current_parts = current_dir.parts
|
|
1781
|
+
image_parts = image_path.parts
|
|
1782
|
+
|
|
1783
|
+
# Find common parent by comparing path components
|
|
1784
|
+
common_length = 0
|
|
1785
|
+
for i, (c, img) in enumerate(zip(current_parts, image_parts)):
|
|
1786
|
+
if c == img:
|
|
1787
|
+
common_length = i + 1
|
|
1788
|
+
else:
|
|
1789
|
+
break
|
|
1790
|
+
|
|
1791
|
+
logger.debug(
|
|
1792
|
+
f"Common parent depth: {common_length}, "
|
|
1793
|
+
f"current_parts={current_parts}, image_parts={image_parts}"
|
|
1794
|
+
)
|
|
1795
|
+
|
|
1796
|
+
# Build path: "../" from current to common parent
|
|
1797
|
+
up_count = len(current_parts) - common_length
|
|
1798
|
+
up_path = "../" * up_count if up_count > 0 else ""
|
|
1799
|
+
|
|
1800
|
+
# Build path: from common parent to image
|
|
1801
|
+
down_parts = image_parts[common_length:]
|
|
1802
|
+
down_path = "/".join(down_parts) if down_parts else ""
|
|
1803
|
+
|
|
1804
|
+
relative_path: str = up_path + down_path
|
|
1805
|
+
|
|
1806
|
+
logger.debug(
|
|
1807
|
+
f"Cross-directory path calculation: up_count={up_count}, "
|
|
1808
|
+
f"up_path='{up_path}', down_path='{down_path}', "
|
|
1809
|
+
f"result: {relative_path}"
|
|
1810
|
+
)
|
|
1811
|
+
|
|
1812
|
+
return relative_path
|
|
1813
|
+
|
|
1702
1814
|
def visit_toctree(self, node: nodes.Node) -> None:
|
|
1703
1815
|
"""
|
|
1704
1816
|
Visit a toctree node (Sphinx table of contents tree).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: typsphinx
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Sphinx extension for Typst output
|
|
5
5
|
Author-email: Sphinx Typst Contributors <noreply@example.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -364,5 +364,5 @@ See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
|
|
|
364
364
|
|
|
365
365
|
---
|
|
366
366
|
|
|
367
|
-
**Status**: Stable (v0.4.
|
|
367
|
+
**Status**: Stable (v0.4.2) - Production ready
|
|
368
368
|
**Python**: 3.9+ | **Sphinx**: 5.0+ | **Typst**: 0.11.1+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|