|
| 1 | +# DPI Metadata Enhancement for MicroTeX SVG Output |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This document describes the enhancement made to `microtex_rs` to embed DPI (dots per inch) metadata in generated SVG output. This metadata is essential for downstream processors (like PDF generators) to correctly size and position SVG content. |
| 6 | + |
| 7 | +## Problem Statement |
| 8 | + |
| 9 | +When MicroTeX renders LaTeX formulas to SVG, the generated SVG contains width and height attributes in pixels, but there was no indication of the DPI used during rendering. Downstream processors (like `genpdfi_extended`) had to make assumptions about the DPI, leading to incorrect sizing when the actual DPI differed from the assumed value. |
| 10 | + |
| 11 | +For example: |
| 12 | +- MicroTeX renders at 720 DPI (default) |
| 13 | +- SVG generated with `width="120px"` (a 12pt formula) |
| 14 | +- A PDF generator assuming 300 DPI would incorrectly calculate the formula's physical size |
| 15 | +- Result: Formula appears ~2.4x larger than intended |
| 16 | + |
| 17 | +## Solution |
| 18 | + |
| 19 | +### New Public Function: `add_dpi_to_svg()` |
| 20 | + |
| 21 | +A public function has been added to embed DPI metadata into SVG elements: |
| 22 | + |
| 23 | +```rust |
| 24 | +/// Adds DPI metadata to an SVG string as a `data-dpi` attribute. |
| 25 | +pub fn add_dpi_to_svg(svg: &str, dpi: i32) -> String |
| 26 | +``` |
| 27 | + |
| 28 | +**Behavior:** |
| 29 | +- Locates the `<svg>` opening tag in the SVG content |
| 30 | +- Injects a `data-dpi="XXX"` attribute before the closing `>` |
| 31 | +- Returns the modified SVG with metadata embedded |
| 32 | +- Returns original SVG unchanged if no `<svg>` tag is found |
| 33 | + |
| 34 | +**Example:** |
| 35 | + |
| 36 | +Input: |
| 37 | +```xml |
| 38 | +<svg xmlns="http://www.w3.org/2000/svg" width="100" height="50"> |
| 39 | +``` |
| 40 | + |
| 41 | +Output (with dpi=720): |
| 42 | +```xml |
| 43 | +<svg xmlns="http://www.w3.org/2000/svg" width="100" height="50" data-dpi="720"> |
| 44 | +``` |
| 45 | + |
| 46 | +### Modified Methods |
| 47 | + |
| 48 | +The following public methods now automatically embed DPI metadata in their output: |
| 49 | + |
| 50 | +#### 1. `MicroTex::render()` |
| 51 | +- **Location:** `src/lib.rs:936-981` |
| 52 | +- **Change:** SVG output is processed through `add_dpi_to_svg()` before being returned |
| 53 | +- **Impact:** Every rendered SVG now contains the DPI value used during rendering |
| 54 | + |
| 55 | +#### 2. `MicroTex::render_to_svg_with_metrics()` |
| 56 | +- **Location:** `src/lib.rs:1038-1122` |
| 57 | +- **Change:** SVG extracted from JSON response is processed through `add_dpi_to_svg()` before being included in `RenderResult` |
| 58 | +- **Impact:** Both SVG rendering methods now consistently embed DPI metadata |
| 59 | + |
| 60 | +## Implementation Details |
| 61 | + |
| 62 | +### Function: `add_dpi_to_svg()` |
| 63 | +- **File:** `src/lib.rs:813-852` |
| 64 | +- **Type:** Public function |
| 65 | +- **Algorithm:** |
| 66 | + 1. Search for `<svg` opening tag in the SVG string |
| 67 | + 2. If found, locate the closing `>` character |
| 68 | + 3. Insert ` data-dpi="XXX"` before the `>` |
| 69 | + 4. Return the modified string |
| 70 | + 5. If no `<svg>` tag found or malformed, return original string unchanged |
| 71 | + |
| 72 | +**Key Features:** |
| 73 | +- Safe: Returns original string on any parsing error (no exceptions) |
| 74 | +- Efficient: Single-pass string manipulation |
| 75 | +- Robust: Handles XML declarations, namespaces, and attributes |
| 76 | +- Preserves: All existing SVG content and structure |
| 77 | + |
| 78 | +## Testing |
| 79 | + |
| 80 | +### Unit Tests Added |
| 81 | +Six comprehensive tests verify the `add_dpi_to_svg()` function: |
| 82 | + |
| 83 | +```rust |
| 84 | +test tests::test_add_dpi_to_svg_simple ... ok |
| 85 | +test tests::test_add_dpi_to_svg_with_namespace ... ok |
| 86 | +test tests::test_add_dpi_to_svg_different_dpi_values ... ok |
| 87 | +test tests::test_add_dpi_to_svg_no_svg_tag ... ok |
| 88 | +test tests::test_add_dpi_to_svg_malformed ... ok |
| 89 | +test tests::test_add_dpi_to_svg_preserves_content ... ok |
| 90 | +``` |
| 91 | + |
| 92 | +**Test Coverage:** |
| 93 | +1. **Simple SVG**: Verifies basic attribute injection with standard namespace |
| 94 | +2. **Namespace handling**: Tests with full SVG namespace declaration |
| 95 | +3. **Multiple DPI values**: Confirms correct values (300, 720) are embedded |
| 96 | +4. **No SVG tag**: Ensures graceful fallback when no `<svg>` tag exists |
| 97 | +5. **Malformed SVG**: Validates error handling for incomplete tags |
| 98 | +6. **Content preservation**: Confirms that SVG body content remains unchanged |
| 99 | + |
| 100 | +### Test Results |
| 101 | +All tests pass: |
| 102 | +- **Unit tests:** 26 passed |
| 103 | +- **Binary tests:** 3 passed |
| 104 | +- **Integration tests:** 7 passed |
| 105 | +- **Multiple renderer tests:** 1 passed |
| 106 | +- **Example validation:** 7 passed |
| 107 | +- **Total:** 44 tests passed, 0 failed |
| 108 | + |
| 109 | +### Example Verification |
| 110 | +The `simple_formula` example was updated to verify DPI embedding in real-world scenarios. Output confirms: |
| 111 | +``` |
| 112 | +✓ data-dpi attribute found! (for Einstein's E=mc²) |
| 113 | +✓ data-dpi attribute found! (for Pythagorean theorem) |
| 114 | +✓ data-dpi attribute found! (for Quadratic formula) |
| 115 | +✓ data-dpi attribute found! (for Sum notation) |
| 116 | +✓ data-dpi attribute found! (for Integration) |
| 117 | +``` |
| 118 | + |
| 119 | +## Impact on Downstream Processors |
| 120 | + |
| 121 | +### For `genpdfi_extended` |
| 122 | +The PDF generator can now: |
| 123 | +1. Read the `data-dpi` attribute from SVG |
| 124 | +2. Use the actual rendering DPI (not assumed values) for sizing calculations |
| 125 | +3. Correctly position SVG content in PDF pages |
| 126 | +4. Fix the issue where large formulas were incorrectly placed and cut off |
| 127 | + |
| 128 | +### Example Usage in `genpdfi_extended` |
| 129 | + |
| 130 | +```rust |
| 131 | +// In src/elements/images.rs (ImageSource::intrinsic_size) |
| 132 | +ImageSource::Svg(svg) => { |
| 133 | + let mmpi: f32 = 25.4; |
| 134 | + |
| 135 | + // Extract actual DPI from data-dpi attribute |
| 136 | + let actual_dpi = svg.dpi.unwrap_or_else(|| { |
| 137 | + // Fallback: try to extract from SVG string's data-dpi attribute |
| 138 | + extract_data_dpi_attribute(&svg_content) |
| 139 | + .unwrap_or(300.0) |
| 140 | + }); |
| 141 | + |
| 142 | + let width_px = svg.width.map(|px| px.0 as f32).unwrap_or(100.0); |
| 143 | + let height_px = svg.height.map(|px| px.0 as f32).unwrap_or(100.0); |
| 144 | + |
| 145 | + Size::new( |
| 146 | + mmpi * (width_px / actual_dpi), |
| 147 | + mmpi * (height_px / actual_dpi), |
| 148 | + ) |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +## Backward Compatibility |
| 153 | + |
| 154 | +✅ **Fully backward compatible** |
| 155 | + |
| 156 | +- The change only adds metadata; existing SVG functionality is not affected |
| 157 | +- SVG files without the `data-dpi` attribute work as before |
| 158 | +- Downstream processors can ignore the attribute if they don't need it |
| 159 | +- No breaking changes to the public API |
| 160 | +- All existing tests pass without modification |
| 161 | + |
| 162 | +## Benefits |
| 163 | + |
| 164 | +1. **Correct Sizing**: Downstream processors can now accurately determine SVG dimensions |
| 165 | +2. **Self-Documenting**: SVG files contain metadata about their rendering context |
| 166 | +3. **Robustness**: Eliminates guessing about DPI values |
| 167 | +4. **Standardization**: Establishes a convention for encoding DPI in SVG |
| 168 | +5. **Debugging**: Easier to troubleshoot sizing issues with explicit DPI information |
| 169 | + |
| 170 | +## Files Modified |
| 171 | + |
| 172 | +- **`src/lib.rs`** |
| 173 | + - Added public function `add_dpi_to_svg()` (lines 813-852) |
| 174 | + - Modified `MicroTex::render()` to use `add_dpi_to_svg()` (line 945) |
| 175 | + - Modified `MicroTex::render_to_svg_with_metrics()` to use `add_dpi_to_svg()` (line 1078) |
| 176 | + - Added 6 unit tests for `add_dpi_to_svg()` (lines 1513-1563) |
| 177 | + |
| 178 | +- **`examples/simple_formula.rs`** |
| 179 | + - Updated to verify `data-dpi` attribute in output (lines 39-42) |
| 180 | + |
| 181 | +## Testing Commands |
| 182 | + |
| 183 | +```bash |
| 184 | +# Run all tests in microtex_rs |
| 185 | +cargo test |
| 186 | + |
| 187 | +# Run only DPI-related tests |
| 188 | +cargo test --lib add_dpi |
| 189 | + |
| 190 | +# Run the simple formula example with verification |
| 191 | +cargo run --example simple_formula |
| 192 | +``` |
| 193 | + |
| 194 | +## Related Issues |
| 195 | + |
| 196 | +This change addresses the core issue in `genpdfi_extended` where SVG formulas were incorrectly sized due to DPI assumptions. See: `genpdfi_extended/ANALYSIS_PDF_LAYOUT_DEFECT.md` |
| 197 | + |
| 198 | +## Version |
| 199 | + |
| 200 | +- **microtex_rs:** v0.1 (enhanced) |
| 201 | +- **Date:** January 11, 2026 |
| 202 | +- **Status:** ✅ Complete and tested |
0 commit comments