asciidoctor-dot-leader 1.0.0 → 1.2.0
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.
- checksums.yaml +4 -4
 - data/CHANGELOG.adoc +42 -0
 - data/README.adoc +53 -17
 - data/lib/asciidoctor/dot_leader/dot_leader_pdf.rb +180 -15
 - metadata +2 -1
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 2382e42c9d8e11c7427f3758ea61f111d4fa390fe99a6daf4c0f4eab46860f64
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 7c518c228291a4be4c236c9848f2e97a0e2d4ea94e617efc7bd79e4667ea14ba
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 726405e93b9ed14555643a2ba7d6a490f0f6ab075c54b7dbf16da785c638807f104dd812a0ca94a378316adcae5b8b2ff6df80f770daaee6018916b856f74da7
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 81cbc4675e9ebef09663bdf94460a94ad80b024c2f52e1f800bdde550c616fae9d23f551e2d925711e895d2796728cafee1ab07d56650ea8975c47aa69b70be8
         
     | 
    
        data/CHANGELOG.adoc
    ADDED
    
    | 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            = Changelog
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            All notable changes to this project will be documented in this file.
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            The format is based on https://keepachangelog.com/en/1.0.0/[Keep a Changelog],
         
     | 
| 
      
 6 
     | 
    
         
            +
            and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Versioning].
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            == [1.2.0] - 2025-10-31
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            This release adds support for column_boxes in asciidoctor-pdf link:https://asciidoctor.zulipchat.com/#narrow/channel/288690-users.2Fasciidoctor-pdf/topic/Implicit.20page.20break.20after.20column_box/near/503833058[^].
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            == [1.1.0] - 2025-10-31
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            === Fixed
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            This release fixes alignment and width calculation issues for the pdf version:
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            * All positioning uses a universal grid based on absolute page coordinates which ensures all dot leaders align vertically across all block types
         
     | 
| 
      
 20 
     | 
    
         
            +
            * Fixed width calculation for roles, such as `.small` and `.big`, by using fragment-specific font sizes
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            == [1.0.0] - 2025-07-20
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            === Added
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            Initial release of the asciidoctor-dot-leader extension with these features:
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            * Initial release of the asciidoctor-dot-leader extension
         
     | 
| 
      
 29 
     | 
    
         
            +
            * Supports the PDF backend via asciidoctor-pdf
         
     | 
| 
      
 30 
     | 
    
         
            +
            * Supports the HTML5 backend
         
     | 
| 
      
 31 
     | 
    
         
            +
            * Inline macro syntax: `dot:leader["left", "right"]`
         
     | 
| 
      
 32 
     | 
    
         
            +
            * Supports formatted text in left and right fragments
         
     | 
| 
      
 33 
     | 
    
         
            +
            * Handles inline images, superscript, and subscript
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            === Features
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            Key capabilities of the extension:
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            * Dot leader generation between left and right text
         
     | 
| 
      
 40 
     | 
    
         
            +
            * Configurable dot leader appearance via theme
         
     | 
| 
      
 41 
     | 
    
         
            +
            * Support for complex inline formatting
         
     | 
| 
      
 42 
     | 
    
         
            +
            * Compatible with multiple Asciidoctor backends
         
     | 
    
        data/README.adoc
    CHANGED
    
    | 
         @@ -1,14 +1,16 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            =  
     | 
| 
      
 1 
     | 
    
         
            +
            = Asciidoctor Dot Leader Inline Macro
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            This project provides an inline macro which `pass:[dot:leader["left", "right"]]` which inserts leaders between the values.
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            Dot . . . . . . . . . . . . . . . . . . . . . . . . Leaders
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
      
 7 
     | 
    
         
            +
            **Current Version:** 1.1.0 | link:CHANGELOG.adoc[View Changelog]
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
       7 
9 
     | 
    
         
             
            It currently supports three backends:
         
     | 
| 
       8 
10 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            *  
     | 
| 
       10 
     | 
    
         
            -
            *  
     | 
| 
       11 
     | 
    
         
            -
            *  
     | 
| 
      
 11 
     | 
    
         
            +
            * Asciidoctor (Ruby) - `backend-html5`
         
     | 
| 
      
 12 
     | 
    
         
            +
            * Asciidoctor-PDF (Ruby) - `backend-pdf`
         
     | 
| 
      
 13 
     | 
    
         
            +
            * Asciidoctor.js - `backend-webview-html5` - See <<asciidoctor-js-dot-leader>>
         
     | 
| 
       12 
14 
     | 
    
         | 
| 
       13 
15 
     | 
    
         
             
            .PDF
         
     | 
| 
       14 
16 
     | 
    
         
             
            image:resources/images/example-pdf-adoc-formatted.png[]
         
     | 
| 
         @@ -16,7 +18,7 @@ image:resources/images/example-pdf-adoc-formatted.png[] 
     | 
|
| 
       16 
18 
     | 
    
         
             
            .HTML5
         
     | 
| 
       17 
19 
     | 
    
         
             
            image:resources/images/example-html5-roles.png[]
         
     | 
| 
       18 
20 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
            . 
     | 
| 
      
 21 
     | 
    
         
            +
            .Asciidoctor.js
         
     | 
| 
       20 
22 
     | 
    
         
             
            image:resources/images/example-webview-html5-inline-passthroughs.png[]
         
     | 
| 
       21 
23 
     | 
    
         | 
| 
       22 
24 
     | 
    
         
             
            == Markup And Usage _Manual_ 
         
     | 
| 
         @@ -27,32 +29,58 @@ Markup examples, rendered output examples, and extensive compatibility informati 
     | 
|
| 
       27 
29 
     | 
    
         
             
            * link:dot_leader_example.html[] 
         
     | 
| 
       28 
30 
     | 
    
         
             
            * Or clone the project and open the link:dot_leader_example.adoc[] file with Visual Studio Code's Asciidoc's preview window.
         
     | 
| 
       29 
31 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
            ==  
     | 
| 
      
 32 
     | 
    
         
            +
            == Asciidoctor and Asciidoctor-PDF (Ruby) Installation and Usage
         
     | 
| 
       31 
33 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
      
 34 
     | 
    
         
            +
            The asciidoctor-dot-leader is available as a Ruby Gem.
         
     | 
| 
       33 
35 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
      
 36 
     | 
    
         
            +
            https://rubygems.org/gems/asciidoctor-dot-leader[^]
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            Complete these steps to use:
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            . Install the required Gems:
         
     | 
| 
      
 41 
     | 
    
         
            +
            +
         
     | 
| 
      
 42 
     | 
    
         
            +
            [source]
         
     | 
| 
      
 43 
     | 
    
         
            +
            ----
         
     | 
| 
      
 44 
     | 
    
         
            +
            gem install asciidoctor
         
     | 
| 
      
 45 
     | 
    
         
            +
            gem install asciidoctor-dot-leader
         
     | 
| 
      
 46 
     | 
    
         
            +
            ----
         
     | 
| 
      
 47 
     | 
    
         
            +
            +
         
     | 
| 
      
 48 
     | 
    
         
            +
            . Render the document:
         
     | 
| 
      
 49 
     | 
    
         
            +
            +
         
     | 
| 
      
 50 
     | 
    
         
            +
            [source]
         
     | 
| 
      
 51 
     | 
    
         
            +
            ----
         
     | 
| 
      
 52 
     | 
    
         
            +
            asciidoctor -r asciidoctor-dot-leader dot_leader_example.adoc
         
     | 
| 
      
 53 
     | 
    
         
            +
            ----
         
     | 
| 
      
 54 
     | 
    
         
            +
            +
         
     | 
| 
      
 55 
     | 
    
         
            +
            . [For PDF] Install Asciidoctor-PDF:
         
     | 
| 
      
 56 
     | 
    
         
            +
            +
         
     | 
| 
      
 57 
     | 
    
         
            +
            [source]
         
     | 
| 
      
 58 
     | 
    
         
            +
            ----
         
     | 
| 
      
 59 
     | 
    
         
            +
            gem install asciidoctor-pdf
         
     | 
| 
      
 60 
     | 
    
         
            +
            ----
         
     | 
| 
      
 61 
     | 
    
         
            +
            +
         
     | 
| 
      
 62 
     | 
    
         
            +
            . [For PDF] Render the PDF:
         
     | 
| 
      
 63 
     | 
    
         
            +
            +
         
     | 
| 
      
 64 
     | 
    
         
            +
            [source]
         
     | 
| 
      
 65 
     | 
    
         
            +
            ----
         
     | 
| 
      
 66 
     | 
    
         
            +
            asciidoctor-pdf -r asciidoctor-dot-leader dot_leader_example.adoc
         
     | 
| 
      
 67 
     | 
    
         
            +
            ----
         
     | 
| 
       40 
68 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
      
 69 
     | 
    
         
            +
            === Ruby GEM Build Instructions (for 白一百 baiyibai)
         
     | 
| 
       42 
70 
     | 
    
         | 
| 
       43 
71 
     | 
    
         
             
            ```
         
     | 
| 
       44 
72 
     | 
    
         
             
            gem build asciidoctor-dot-leader.gemspec 
         
     | 
| 
       45 
     | 
    
         
            -
            gem install ./asciidoctor-dot-leader-1. 
     | 
| 
      
 73 
     | 
    
         
            +
            gem install ./asciidoctor-dot-leader-1.1.0.gem
         
     | 
| 
       46 
74 
     | 
    
         
             
            ```
         
     | 
| 
       47 
75 
     | 
    
         | 
| 
       48 
76 
     | 
    
         
             
            [#asciidoctor-js-dot-leader]
         
     | 
| 
       49 
     | 
    
         
            -
            ==  
     | 
| 
      
 77 
     | 
    
         
            +
            == Asciidoctor.js Dot Leader Inline Macro
         
     | 
| 
       50 
78 
     | 
    
         | 
| 
       51 
79 
     | 
    
         
             
            This project provides an inline macro, `pass:[dot:leader["left", "right"]]`, which inserts dot leaders between the provided values. 
         
     | 
| 
       52 
80 
     | 
    
         | 
| 
       53 
81 
     | 
    
         
             
            === Target Application
         
     | 
| 
       54 
82 
     | 
    
         | 
| 
       55 
     | 
    
         
            -
            This has only been tested with the  Microsoft Visual Studio Code's AsciiDoc extension's _Preview_ function which uses the ` 
     | 
| 
      
 83 
     | 
    
         
            +
            This has only been tested with the  Microsoft Visual Studio Code's AsciiDoc extension's _Preview_ function which uses the `backend-webview-html5`.
         
     | 
| 
       56 
84 
     | 
    
         | 
| 
       57 
85 
     | 
    
         
             
            === Usage
         
     | 
| 
       58 
86 
     | 
    
         | 
| 
         @@ -83,6 +111,14 @@ module.exports = require('../../asciidoctorjs-extension/dot_leader.js') 
     | 
|
| 
       83 
111 
     | 
    
         
             
            .. Enable the Asciidoc > Extensions: *Register Workspace Extensions* option.
         
     | 
| 
       84 
112 
     | 
    
         
             
            . Open a document containing `pass:[dot:leader[]]` inline macros, and press the _Preview_ button.
         
     | 
| 
       85 
113 
     | 
    
         | 
| 
      
 114 
     | 
    
         
            +
            == Todo
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
            There are a few items which need fixing:
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
            * HTML5
         
     | 
| 
      
 119 
     | 
    
         
            +
            ** [ ] Include .css automatically?
         
     | 
| 
      
 120 
     | 
    
         
            +
            * webview-html5 (Asciidoctor.js) for Microsoft Visual Studio Code and possibly others
         
     | 
| 
      
 121 
     | 
    
         
            +
            ** [ ] Release on npm
         
     | 
| 
       86 
122 
     | 
    
         | 
| 
       87 
123 
     | 
    
         
             
            == Copyright
         
     | 
| 
       88 
124 
     | 
    
         | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # dot_leader_pdf.rb 2025-07-20 白一百 baiyibai
         
     | 
| 
       2 
2 
     | 
    
         
             
            # https://gitlab.com/baiyibai/asciidoctor-pdf-dot-leader
         
     | 
| 
       3 
     | 
    
         
            -
            # This inline macro uses  
     | 
| 
      
 3 
     | 
    
         
            +
            # This inline macro uses Asciidoctor-PDF's ToC generation code as a jumping off point, but departs from it.
         
     | 
| 
       4 
4 
     | 
    
         
             
            # Use '':dot-leader-scale: 0.6' to set the document-wide factor
         
     | 
| 
       5 
5 
     | 
    
         
             
            require 'asciidoctor-pdf' unless defined? ::Asciidoctor::Converter::PdfConverter
         
     | 
| 
       6 
6 
     | 
    
         
             
            class DotLeader < (Asciidoctor::Converter.for 'pdf')
         
     | 
| 
         @@ -9,7 +9,8 @@ class DotLeader < (Asciidoctor::Converter.for 'pdf') 
     | 
|
| 
       9 
9 
     | 
    
         
             
              def rendered_width_of_fragments(fragments, scale, roles = [])
         
     | 
| 
       10 
10 
     | 
    
         
             
                  fragments.sum do |fragment|
         
     | 
| 
       11 
11 
     | 
    
         
             
                  styles = fragment[:styles] || Set.new
         
     | 
| 
       12 
     | 
    
         
            -
                  size  
     | 
| 
      
 12 
     | 
    
         
            +
                  # Use fragment's size if specified (for roles like .small, .big), otherwise use current font size
         
     | 
| 
      
 13 
     | 
    
         
            +
                  size = fragment[:size] || font_size
         
     | 
| 
       13 
14 
     | 
    
         
             
                  size *= scale if styles.include?(:subscript) || styles.include?(:superscript)
         
     | 
| 
       14 
15 
     | 
    
         
             
                  font_style = if styles.include?(:bold) && styles.include?(:italic)
         
     | 
| 
       15 
16 
     | 
    
         
             
                    :bold_italic
         
     | 
| 
         @@ -43,6 +44,12 @@ class DotLeader < (Asciidoctor::Converter.for 'pdf') 
     | 
|
| 
       43 
44 
     | 
    
         | 
| 
       44 
45 
     | 
    
         
             
                left_side = node.attr('l_text') || ''
         
     | 
| 
       45 
46 
     | 
    
         
             
                right_side = node.attr('r_text') || ''    
         
     | 
| 
      
 47 
     | 
    
         
            +
                
         
     | 
| 
      
 48 
     | 
    
         
            +
                # DEBUG: Initial context information
         
     | 
| 
      
 49 
     | 
    
         
            +
                is_column_box = defined?(::Prawn::Document::ColumnBox) && bounds.is_a?(::Prawn::Document::ColumnBox)
         
     | 
| 
      
 50 
     | 
    
         
            +
                current_column = is_column_box ? bounds.current_column : 'N/A'
         
     | 
| 
      
 51 
     | 
    
         
            +
                # log :info, %(DOT_LEADER_DEBUG: Starting - left="#{left_side}", right="#{right_side}", page=#{page_number}, cursor=#{cursor}, is_column=#{is_column_box}, current_column=#{current_column})
         
     | 
| 
      
 52 
     | 
    
         
            +
                
         
     | 
| 
       46 
53 
     | 
    
         
             
                if left_side.empty? # Allow items only on the right
         
     | 
| 
       47 
54 
     | 
    
         
             
                  left_side = ' '
         
     | 
| 
       48 
55 
     | 
    
         
             
                end
         
     | 
| 
         @@ -75,6 +82,8 @@ class DotLeader < (Asciidoctor::Converter.for 'pdf') 
     | 
|
| 
       75 
82 
     | 
    
         
             
                  }
         
     | 
| 
       76 
83 
     | 
    
         
             
                end
         
     | 
| 
       77 
84 
     | 
    
         | 
| 
      
 85 
     | 
    
         
            +
                # log :info, %(DOT_LEADER_DEBUG: Dot leader config - text="#{dot_leader[:text]}", width=#{dot_leader[:width]}, spacer_width=#{dot_leader[:spacer_width]})
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
       78 
87 
     | 
    
         
             
                # 2025-07-17 BYB: Set toc_font_info
         
     | 
| 
       79 
88 
     | 
    
         
             
                toc_font_info = theme_font :toc do
         
     | 
| 
       80 
89 
     | 
    
         
             
                  { font: font, size: @font_size }
         
     | 
| 
         @@ -102,14 +111,12 @@ class DotLeader < (Asciidoctor::Converter.for 'pdf') 
     | 
|
| 
       102 
111 
     | 
    
         
             
                line_metrics = calc_line_metrics @base_line_height
         
     | 
| 
       103 
112 
     | 
    
         | 
| 
       104 
113 
     | 
    
         
             
                # 2025-07-17 BYB: This needs to be modified
         
     | 
| 
       105 
     | 
    
         
            -
                right_side_placeholder_width = rendered_width_of_string right_side * @toc_max_pagenum_digits
         
     | 
| 
       106 
     | 
    
         
            -
                # puts right_side_placeholder_width.inspect # Various widths
         
     | 
| 
       107 
     | 
    
         
            -
                right_side_placeholder_width = 0 # BYB: Not a problem for now
         
     | 
| 
       108 
     | 
    
         
            -
                
         
     | 
| 
       109 
114 
     | 
    
         
             
                right_side_placeholder_width = rendered_width_of_fragments right_side_fragments, scale, node.roles.to_a
         
     | 
| 
       110 
115 
     | 
    
         | 
| 
       111 
116 
     | 
    
         
             
                left_side_test_width = rendered_width_of_fragments left_side_fragments, scale, node.roles.to_a
         
     | 
| 
       112 
117 
     | 
    
         | 
| 
      
 118 
     | 
    
         
            +
                # log :info, %(DOT_LEADER_DEBUG: Text widths - left=#{left_side_test_width}, right=#{right_side_placeholder_width})
         
     | 
| 
      
 119 
     | 
    
         
            +
                
         
     | 
| 
       113 
120 
     | 
    
         
             
                # 2025-07-17 Set hanging_indent to toc; potentially this could be styled
         
     | 
| 
       114 
121 
     | 
    
         
             
                hanging_indent = @theme.toc_hanging_indent
         
     | 
| 
       115 
122 
     | 
    
         
             
                start_page_number = page_number
         
     | 
| 
         @@ -122,7 +129,7 @@ class DotLeader < (Asciidoctor::Converter.for 'pdf') 
     | 
|
| 
       122 
129 
     | 
    
         
             
                  end
         
     | 
| 
       123 
130 
     | 
    
         | 
| 
       124 
131 
     | 
    
         
             
                  if right_side_placeholder_width > bounds.width
         
     | 
| 
       125 
     | 
    
         
            -
                     
     | 
| 
      
 132 
     | 
    
         
            +
                    log :warn, %(DOT_LEADER_DEBUG: Right side too wide; skipping: #{left_side} - right_width=#{right_side_placeholder_width}, bounds_width=#{bounds.width})
         
     | 
| 
       126 
133 
     | 
    
         
             
                    return nil
         
     | 
| 
       127 
134 
     | 
    
         
             
                  end
         
     | 
| 
       128 
135 
     | 
    
         | 
| 
         @@ -134,6 +141,8 @@ class DotLeader < (Asciidoctor::Converter.for 'pdf') 
     | 
|
| 
       134 
141 
     | 
    
         
             
                  start_cursor = last_fragment_cursor if last_fragment_position.page_number > start_page_number || (start_cursor - last_fragment_cursor) > line_metrics.height
         
     | 
| 
       135 
142 
     | 
    
         
             
                end
         
     | 
| 
       136 
143 
     | 
    
         | 
| 
      
 144 
     | 
    
         
            +
                # log :info, %(DOT_LEADER_DEBUG: After left text - start_dots=#{start_dots}, start_cursor=#{start_cursor})
         
     | 
| 
      
 145 
     | 
    
         
            +
                
         
     | 
| 
       137 
146 
     | 
    
         
             
                # 2025-07-17 No loop
         
     | 
| 
       138 
147 
     | 
    
         
             
                # NOTE: this will leave behind a gap where this entry would have been
         
     | 
| 
       139 
148 
     | 
    
         
             
                # break unless start_dots
         
     | 
| 
         @@ -152,19 +161,174 @@ class DotLeader < (Asciidoctor::Converter.for 'pdf') 
     | 
|
| 
       152 
161 
     | 
    
         
             
                    # NOTE: the same font is used for dot leaders throughout toc
         
     | 
| 
       153 
162 
     | 
    
         
             
                    set_font toc_font_info[:font], dot_leader[:font_size]
         
     | 
| 
       154 
163 
     | 
    
         
             
                    font_style dot_leader[:font_style]
         
     | 
| 
       155 
     | 
    
         
            -
                    #  
     | 
| 
       156 
     | 
    
         
            -
                     
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
      
 164 
     | 
    
         
            +
                    # For true vertical alignment, dots must be at consistent absolute positions across all lines
         
     | 
| 
      
 165 
     | 
    
         
            +
                    # CONTEXT-AWARE GRID ALIGNMENT:
         
     | 
| 
      
 166 
     | 
    
         
            +
                    # Use appropriate grid origin for each context to ensure proper alignment
         
     | 
| 
      
 167 
     | 
    
         
            +
                    # while maintaining correct positioning within bounds
         
     | 
| 
      
 168 
     | 
    
         
            +
                    
         
     | 
| 
      
 169 
     | 
    
         
            +
                    # Calculate the absolute position where dots will start
         
     | 
| 
      
 170 
     | 
    
         
            +
                    # Note: start_dots may already be absolute in some contexts (like right column)
         
     | 
| 
      
 171 
     | 
    
         
            +
                    # Check if start_dots is already in absolute coordinates
         
     | 
| 
      
 172 
     | 
    
         
            +
                    if start_dots > bounds.width
         
     | 
| 
      
 173 
     | 
    
         
            +
                      # start_dots appears to be already absolute (larger than column width)
         
     | 
| 
      
 174 
     | 
    
         
            +
                      start_dots_absolute = start_dots
         
     | 
| 
      
 175 
     | 
    
         
            +
                    else
         
     | 
| 
      
 176 
     | 
    
         
            +
                      # start_dots is relative to bounds, convert to absolute
         
     | 
| 
      
 177 
     | 
    
         
            +
                      start_dots_absolute = start_dots + bounds.absolute_left
         
     | 
| 
      
 178 
     | 
    
         
            +
                    end
         
     | 
| 
      
 179 
     | 
    
         
            +
                    
         
     | 
| 
      
 180 
     | 
    
         
            +
                    if defined?(::Prawn::Document::ColumnBox) && bounds.is_a?(::Prawn::Document::ColumnBox)
         
     | 
| 
      
 181 
     | 
    
         
            +
                      # In column context: use UNIVERSAL grid origin (page margin) for alignment across all columns
         
     | 
| 
      
 182 
     | 
    
         
            +
                      # This ensures dots align consistently across left and right columns
         
     | 
| 
      
 183 
     | 
    
         
            +
                      
         
     | 
| 
      
 184 
     | 
    
         
            +
                      # Get column information for debugging
         
     | 
| 
      
 185 
     | 
    
         
            +
                      current_col = bounds.current_column
         
     | 
| 
      
 186 
     | 
    
         
            +
                      parent_bounds = bounds.instance_variable_get(:@parent)
         
     | 
| 
      
 187 
     | 
    
         
            +
                      total_columns = bounds.instance_variable_get(:@columns) || 2
         
     | 
| 
      
 188 
     | 
    
         
            +
                      
         
     | 
| 
      
 189 
     | 
    
         
            +
                      # Calculate the actual column dimensions from the parent
         
     | 
| 
      
 190 
     | 
    
         
            +
                      parent_width = parent_bounds.width
         
     | 
| 
      
 191 
     | 
    
         
            +
                      column_width = parent_width / total_columns
         
     | 
| 
      
 192 
     | 
    
         
            +
                      total_spacer_width = parent_width - (column_width * total_columns)
         
     | 
| 
      
 193 
     | 
    
         
            +
                      spacer_width = total_columns > 1 ? total_spacer_width / (total_columns - 1) : 0
         
     | 
| 
      
 194 
     | 
    
         
            +
                      column_offset = current_col * (column_width + spacer_width)
         
     | 
| 
      
 195 
     | 
    
         
            +
                      
         
     | 
| 
      
 196 
     | 
    
         
            +
                      # UNIVERSAL GRID: Use page margin as grid origin for all columns
         
     | 
| 
      
 197 
     | 
    
         
            +
                      # This ensures dots align across the entire page
         
     | 
| 
      
 198 
     | 
    
         
            +
                      grid_origin = page.margins[:left]
         
     | 
| 
      
 199 
     | 
    
         
            +
                      
         
     | 
| 
      
 200 
     | 
    
         
            +
                      # log :info, %(DOT_LEADER_DEBUG: Column context - current_column=#{current_col}, total_columns=#{total_columns}, column_width=#{column_width}, spacer_width=#{spacer_width}, column_offset=#{column_offset}, bounds_left=#{bounds.absolute_left}, grid_origin=#{grid_origin})
         
     | 
| 
      
 201 
     | 
    
         
            +
                    else
         
     | 
| 
      
 202 
     | 
    
         
            +
                      # In normal context: use the page margin as grid origin to ignore indentation
         
     | 
| 
      
 203 
     | 
    
         
            +
                      # This ensures consistent alignment across all list levels
         
     | 
| 
      
 204 
     | 
    
         
            +
                      grid_origin = page.margins[:left]
         
     | 
| 
      
 205 
     | 
    
         
            +
                      # log :info, %(DOT_LEADER_DEBUG: Normal context - bounds_left=#{bounds.absolute_left}, page_margin=#{page.margins[:left]}, grid_origin=#{grid_origin})
         
     | 
| 
      
 206 
     | 
    
         
            +
                    end
         
     | 
| 
      
 207 
     | 
    
         
            +
                    
         
     | 
| 
      
 208 
     | 
    
         
            +
                    # Calculate the position relative to the grid origin for alignment
         
     | 
| 
      
 209 
     | 
    
         
            +
                    start_dots_from_grid_origin = start_dots_absolute - grid_origin
         
     | 
| 
      
 210 
     | 
    
         
            +
                    
         
     | 
| 
      
 211 
     | 
    
         
            +
                    # log :info, %(DOT_LEADER_DEBUG: Position calculations - start_dots_absolute=#{start_dots_absolute}, start_dots_from_grid_origin=#{start_dots_from_grid_origin})
         
     | 
| 
      
 212 
     | 
    
         
            +
                    
         
     | 
| 
      
 213 
     | 
    
         
            +
                    # Find the first grid position after the left text ends
         
     | 
| 
      
 214 
     | 
    
         
            +
                    # Grid positions are at multiples of dot_leader[:width] from the grid origin
         
     | 
| 
      
 215 
     | 
    
         
            +
                    first_dot_grid_index = (start_dots_from_grid_origin / dot_leader[:width]).ceil
         
     | 
| 
      
 216 
     | 
    
         
            +
                    first_dot_position_absolute = grid_origin + (first_dot_grid_index * dot_leader[:width])
         
     | 
| 
      
 217 
     | 
    
         
            +
                    
         
     | 
| 
      
 218 
     | 
    
         
            +
                    # Convert back to the local bounds coordinate system for rendering
         
     | 
| 
      
 219 
     | 
    
         
            +
                    # Use universal grid but fix coordinate conversion for right column
         
     | 
| 
      
 220 
     | 
    
         
            +
                    first_dot_position = first_dot_position_absolute - bounds.absolute_left
         
     | 
| 
      
 221 
     | 
    
         
            +
                    
         
     | 
| 
      
 222 
     | 
    
         
            +
                    # Special handling for columns beyond the first (right column in 2-column layout)
         
     | 
| 
      
 223 
     | 
    
         
            +
                    # We need to recalculate using the universal grid to ensure proper alignment
         
     | 
| 
      
 224 
     | 
    
         
            +
                    if defined?(::Prawn::Document::ColumnBox) && bounds.is_a?(::Prawn::Document::ColumnBox)
         
     | 
| 
      
 225 
     | 
    
         
            +
                      current_col = bounds.current_column
         
     | 
| 
      
 226 
     | 
    
         
            +
                      if current_col > 0
         
     | 
| 
      
 227 
     | 
    
         
            +
                        # Right column case: recalculate using universal grid
         
     | 
| 
      
 228 
     | 
    
         
            +
                        # We need to find the first grid position within the column that comes AFTER the left text
         
     | 
| 
      
 229 
     | 
    
         
            +
                      
         
     | 
| 
      
 230 
     | 
    
         
            +
                      # Calculate the left text width to ensure dots start after it
         
     | 
| 
      
 231 
     | 
    
         
            +
                      left_text_width = rendered_width_of_fragments left_side_fragments, scale, node.roles.to_a
         
     | 
| 
      
 232 
     | 
    
         
            +
                      
         
     | 
| 
      
 233 
     | 
    
         
            +
                      # Find the equivalent universal grid position that aligns with other columns
         
     | 
| 
      
 234 
     | 
    
         
            +
                      # Calculate where text ends (without spacer first)
         
     | 
| 
      
 235 
     | 
    
         
            +
                      # Use the absolute left position of the current column plus the text width
         
     | 
| 
      
 236 
     | 
    
         
            +
                      text_end_no_spacer = bounds.absolute_left + left_text_width
         
     | 
| 
      
 237 
     | 
    
         
            +
                      # Add the spacer to get the absolute position where dots should start
         
     | 
| 
      
 238 
     | 
    
         
            +
                      text_end_absolute = text_end_no_spacer + dot_leader[:spacer_width]
         
     | 
| 
      
 239 
     | 
    
         
            +
                      
         
     | 
| 
      
 240 
     | 
    
         
            +
                      # Find the first universal grid position after the text ends
         
     | 
| 
      
 241 
     | 
    
         
            +
                      # This ensures alignment with single column and other contexts
         
     | 
| 
      
 242 
     | 
    
         
            +
                      text_end_from_grid_origin = text_end_absolute - grid_origin
         
     | 
| 
      
 243 
     | 
    
         
            +
                      universal_grid_index = (text_end_from_grid_origin / dot_leader[:width]).ceil
         
     | 
| 
      
 244 
     | 
    
         
            +
                      
         
     | 
| 
      
 245 
     | 
    
         
            +
                      # Check if the previous grid position would still fit (with minimal spacing)
         
     | 
| 
      
 246 
     | 
    
         
            +
                      # This allows us to fit one more dot if the previous grid position comes after the actual text
         
     | 
| 
      
 247 
     | 
    
         
            +
                      if universal_grid_index > 0
         
     | 
| 
      
 248 
     | 
    
         
            +
                        previous_grid_position = grid_origin + ((universal_grid_index - 1) * dot_leader[:width])
         
     | 
| 
      
 249 
     | 
    
         
            +
                        # Check spacing from actual text end (not including the full spacer)
         
     | 
| 
      
 250 
     | 
    
         
            +
                        spacing_from_text = previous_grid_position - text_end_no_spacer
         
     | 
| 
      
 251 
     | 
    
         
            +
                        # Use previous position if it's at least 1pt after the text (minimal spacing)
         
     | 
| 
      
 252 
     | 
    
         
            +
                        if spacing_from_text >= 1.0
         
     | 
| 
      
 253 
     | 
    
         
            +
                          universal_grid_index -= 1
         
     | 
| 
      
 254 
     | 
    
         
            +
                        end
         
     | 
| 
      
 255 
     | 
    
         
            +
                      end
         
     | 
| 
      
 256 
     | 
    
         
            +
                      
         
     | 
| 
      
 257 
     | 
    
         
            +
                      # Calculate the absolute position using the universal grid
         
     | 
| 
      
 258 
     | 
    
         
            +
                      adjusted_position_absolute = grid_origin + (universal_grid_index * dot_leader[:width])
         
     | 
| 
      
 259 
     | 
    
         
            +
                      first_dot_position = adjusted_position_absolute - bounds.absolute_left
         
     | 
| 
      
 260 
     | 
    
         
            +
                      first_dot_position_absolute = adjusted_position_absolute
         
     | 
| 
      
 261 
     | 
    
         
            +
                      # CRITICAL: Update the grid index to match the adjusted position
         
     | 
| 
      
 262 
     | 
    
         
            +
                      first_dot_grid_index = universal_grid_index
         
     | 
| 
      
 263 
     | 
    
         
            +
                      
         
     | 
| 
      
 264 
     | 
    
         
            +
                        # log :info, %(DOT_LEADER_DEBUG: Right column adjustment - left_text_width=#{left_text_width}, text_end_absolute=#{text_end_absolute}, text_end_from_grid_origin=#{text_end_from_grid_origin}, universal_grid_index=#{universal_grid_index}, adjusted_grid_index=#{first_dot_grid_index}, adjusted_position=#{first_dot_position})
         
     | 
| 
      
 265 
     | 
    
         
            +
                      else
         
     | 
| 
      
 266 
     | 
    
         
            +
                        # log :info, %(DOT_LEADER_DEBUG: Left column - no adjustment needed)
         
     | 
| 
      
 267 
     | 
    
         
            +
                      end
         
     | 
| 
      
 268 
     | 
    
         
            +
                    else
         
     | 
| 
      
 269 
     | 
    
         
            +
                      # log :info, %(DOT_LEADER_DEBUG: Standard conversion - first_dot_position_absolute=#{first_dot_position_absolute}, bounds.absolute_left=#{bounds.absolute_left}, first_dot_position=#{first_dot_position})
         
     | 
| 
       158 
270 
     | 
    
         
             
                    end
         
     | 
| 
       159 
     | 
    
         
            -
                     
     | 
| 
       160 
     | 
    
         
            -
                    #  
     | 
| 
      
 271 
     | 
    
         
            +
                    
         
     | 
| 
      
 272 
     | 
    
         
            +
                    # log :info, %(DOT_LEADER_DEBUG: First dot - grid_index=#{first_dot_grid_index}, position_absolute=#{first_dot_position_absolute}, position_local=#{first_dot_position})
         
     | 
| 
      
 273 
     | 
    
         
            +
                    
         
     | 
| 
      
 274 
     | 
    
         
            +
                    # Check if the first dot is too close to the left text
         
     | 
| 
      
 275 
     | 
    
         
            +
                    # Calculate the actual left text width for precise spacing check
         
     | 
| 
      
 276 
     | 
    
         
            +
                    left_text_width = rendered_width_of_fragments left_side_fragments, scale, node.roles.to_a
         
     | 
| 
      
 277 
     | 
    
         
            +
                    space_after_left_text = first_dot_position - left_text_width
         
     | 
| 
      
 278 
     | 
    
         
            +
                    
         
     | 
| 
      
 279 
     | 
    
         
            +
                    dot_stripped = dot_leader[:text].rstrip
         
     | 
| 
      
 280 
     | 
    
         
            +
                    dot_stripped_width = rendered_width_of_string dot_stripped
         
     | 
| 
      
 281 
     | 
    
         
            +
                    
         
     | 
| 
      
 282 
     | 
    
         
            +
                    # Calculate where the right text will start (in absolute coordinates)
         
     | 
| 
      
 283 
     | 
    
         
            +
                    right_text_start_local = bounds.width - right_side_width - dot_leader[:spacer_width]
         
     | 
| 
      
 284 
     | 
    
         
            +
                    
         
     | 
| 
      
 285 
     | 
    
         
            +
                    # Convert to absolute coordinates: bounds.absolute_left already accounts for column position
         
     | 
| 
      
 286 
     | 
    
         
            +
                    right_text_start_absolute = right_text_start_local + bounds.absolute_left
         
     | 
| 
      
 287 
     | 
    
         
            +
                    
         
     | 
| 
      
 288 
     | 
    
         
            +
                    # Find the last grid position before the right text starts (using the same grid origin)
         
     | 
| 
      
 289 
     | 
    
         
            +
                    # Ensure the COMPLETE dot (start + width) fits before right text
         
     | 
| 
      
 290 
     | 
    
         
            +
                    right_text_from_grid_origin = right_text_start_absolute - grid_origin
         
     | 
| 
      
 291 
     | 
    
         
            +
                    
         
     | 
| 
      
 292 
     | 
    
         
            +
                    # Calculate the last grid index where a complete dot would fit
         
     | 
| 
      
 293 
     | 
    
         
            +
                    # A dot at position N has its right edge at N + dot_leader[:width]
         
     | 
| 
      
 294 
     | 
    
         
            +
                    # The target: (last_dot_grid_index * dot_leader[:width]) + dot_leader[:width] <= right_text_from_grid_origin
         
     | 
| 
      
 295 
     | 
    
         
            +
                    # Simplified: last_dot_grid_index <= (right_text_from_grid_origin / dot_leader[:width]) - 1
         
     | 
| 
      
 296 
     | 
    
         
            +
                    # Add some buffer space to ensure the dot does not touch the right text
         
     | 
| 
      
 297 
     | 
    
         
            +
                    last_possible_dot_end = right_text_from_grid_origin - (1.0 * dot_stripped_width)
         
     | 
| 
      
 298 
     | 
    
         
            +
                    last_dot_grid_index = (last_possible_dot_end / dot_leader[:width]).floor
         
     | 
| 
      
 299 
     | 
    
         
            +
                    
         
     | 
| 
      
 300 
     | 
    
         
            +
                    # log :info, %(DOT_LEADER_DEBUG: Right text - start_local=#{right_text_start_local}, start_absolute=#{right_text_start_absolute}, from_grid_origin=#{right_text_from_grid_origin})
         
     | 
| 
      
 301 
     | 
    
         
            +
                    
         
     | 
| 
      
 302 
     | 
    
         
            +
                    # Calculate the number of dots that fit on the grid between left and right text
         
     | 
| 
      
 303 
     | 
    
         
            +
                    num_dots = [last_dot_grid_index - first_dot_grid_index + 1, 0].max
         
     | 
| 
      
 304 
     | 
    
         
            +
                    # log :info, %(DOT_LEADER_DEBUG: Last dot - last_possible_end=#{last_possible_dot_end}, last_grid_index=#{last_dot_grid_index}, num_dots=#{num_dots})
         
     | 
| 
      
 305 
     | 
    
         
            +
             
     | 
| 
      
 306 
     | 
    
         
            +
                    # Calculate the indent to position the first dot at the correct grid position
         
     | 
| 
      
 307 
     | 
    
         
            +
                    dot_indent = first_dot_position
         
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
      
 309 
     | 
    
         
            +
                    # log :info, %(DOT_LEADER_DEBUG: Final render - dot_indent=#{dot_indent}, num_dots=#{num_dots}, dot_text="#{dot_leader[:text]}")
         
     | 
| 
      
 310 
     | 
    
         
            +
             
     | 
| 
       161 
311 
     | 
    
         
             
                    fragment_positions = []
         
     | 
| 
       162 
312 
     | 
    
         
             
                    right_side_fragments.each do |fragment|
         
     | 
| 
       163 
313 
     | 
    
         
             
                      fragment_positions << (fragment_position = ::Asciidoctor::PDF::FormattedText::FragmentPositionRenderer.new)
         
     | 
| 
       164 
314 
     | 
    
         
             
                      (fragment[:callback] ||= []) << fragment_position
         
     | 
| 
       165 
315 
     | 
    
         
             
                    end
         
     | 
| 
      
 316 
     | 
    
         
            +
                    
         
     | 
| 
      
 317 
     | 
    
         
            +
                    # Render the dots with a proper indent to align to the grid
         
     | 
| 
      
 318 
     | 
    
         
            +
                    if num_dots > 0
         
     | 
| 
      
 319 
     | 
    
         
            +
                      indent dot_indent, 0 do
         
     | 
| 
      
 320 
     | 
    
         
            +
                        typeset_formatted_text [
         
     | 
| 
      
 321 
     | 
    
         
            +
                          { text: dot_leader[:text] * num_dots, color: dot_leader[:font_color] }
         
     | 
| 
      
 322 
     | 
    
         
            +
                        ], line_metrics, align: :left
         
     | 
| 
      
 323 
     | 
    
         
            +
                      end
         
     | 
| 
      
 324 
     | 
    
         
            +
                      # log :info, %(DOT_LEADER_DEBUG: Dots rendered successfully)
         
     | 
| 
      
 325 
     | 
    
         
            +
                    else
         
     | 
| 
      
 326 
     | 
    
         
            +
                      log :warn, %(DOT_LEADER_DEBUG: No dots to render - num_dots=#{num_dots})
         
     | 
| 
      
 327 
     | 
    
         
            +
                    end
         
     | 
| 
      
 328 
     | 
    
         
            +
                    
         
     | 
| 
      
 329 
     | 
    
         
            +
                    # Move cursor back to render right text
         
     | 
| 
      
 330 
     | 
    
         
            +
                    move_cursor_to start_cursor
         
     | 
| 
       166 
331 
     | 
    
         
             
                    typeset_formatted_text [
         
     | 
| 
       167 
     | 
    
         
            -
                    { text: dot_leader[:text] * num_dots, color: dot_leader[:font_color] },
         
     | 
| 
       168 
332 
     | 
    
         
             
                    dot_leader[:spacer],
         
     | 
| 
       169 
333 
     | 
    
         
             
                    *right_side_fragments.map { |fragment|
         
     | 
| 
       170 
334 
     | 
    
         
             
                      fragment.dup.tap do |f|
         
     | 
| 
         @@ -179,12 +343,13 @@ class DotLeader < (Asciidoctor::Converter.for 'pdf') 
     | 
|
| 
       179 
343 
     | 
    
         
             
                  ], line_metrics, align: :right
         
     | 
| 
       180 
344 
     | 
    
         
             
                  end
         
     | 
| 
       181 
345 
     | 
    
         
             
                else
         
     | 
| 
      
 346 
     | 
    
         
            +
                  # log :info, %(DOT_LEADER_DEBUG: Skipping dot rendering - width=#{dot_leader[:width]}, levels_check=#{dot_leader[:levels] ? (dot_leader[:levels].include? entry_level.pred) : true})
         
     | 
| 
       182 
347 
     | 
    
         
             
                  typeset_formatted_text [{ text: right_side, color: @font_color, anchor: entry_anchor }], line_metrics, align: :right
         
     | 
| 
       183 
348 
     | 
    
         
             
                end
         
     | 
| 
       184 
349 
     | 
    
         
             
                move_cursor_to end_cursor
         
     | 
| 
       185 
350 
     | 
    
         | 
| 
      
 351 
     | 
    
         
            +
                # log :info, %(DOT_LEADER_DEBUG: Completed - final_cursor=#{cursor})
         
     | 
| 
       186 
352 
     | 
    
         
             
                # Return nil to avoid numbers being printed out
         
     | 
| 
       187 
353 
     | 
    
         
             
                nil
         
     | 
| 
       188 
     | 
    
         
            -
             
     | 
| 
       189 
354 
     | 
    
         
             
              end
         
     | 
| 
       190 
     | 
    
         
            -
            end
         
     | 
| 
      
 355 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: asciidoctor-dot-leader
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.2.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - 白一百 baiyibai
         
     | 
| 
         @@ -30,6 +30,7 @@ executables: [] 
     | 
|
| 
       30 
30 
     | 
    
         
             
            extensions: []
         
     | 
| 
       31 
31 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       32 
32 
     | 
    
         
             
            files:
         
     | 
| 
      
 33 
     | 
    
         
            +
            - CHANGELOG.adoc
         
     | 
| 
       33 
34 
     | 
    
         
             
            - LICENSE
         
     | 
| 
       34 
35 
     | 
    
         
             
            - README.adoc
         
     | 
| 
       35 
36 
     | 
    
         
             
            - lib/asciidoctor-dot-leader.rb
         
     |