@@ -131,6 +131,87 @@ type PdfRenderer struct {
131131 ColumnWidths map [ast.Node ][]float64
132132}
133133
134+ // TOCEntry represents a table of contents entry
135+ type TOCEntry struct {
136+ Level int
137+ Title string
138+ ID string
139+ }
140+
141+ // TOCVisitor implements ast.NodeVisitor to collect headers
142+ type TOCVisitor struct {
143+ entries []TOCEntry
144+ }
145+
146+ // Visit implements the ast.NodeVisitor interface
147+ func (v * TOCVisitor ) Visit (node ast.Node , entering bool ) ast.WalkStatus {
148+ if ! entering {
149+ return ast .GoToNext
150+ }
151+
152+ // Check if the node is a heading
153+ if heading , ok := node .(* ast.Heading ); ok {
154+ // Extract the text content from the heading
155+ title := ExtractTextFromNode (heading )
156+ if title != "" {
157+ // Create a simple ID from the title (lowercase, replace spaces with hyphens)
158+ id := strings .ToLower (strings .ReplaceAll (strings .TrimSpace (title ), " " , "-" ))
159+ // Remove special characters for cleaner IDs
160+ id = strings .ReplaceAll (id , "." , "" )
161+ id = strings .ReplaceAll (id , "," , "" )
162+ id = strings .ReplaceAll (id , "!" , "" )
163+ id = strings .ReplaceAll (id , "?" , "" )
164+
165+ entry := TOCEntry {
166+ Level : heading .Level ,
167+ Title : title ,
168+ ID : id ,
169+ }
170+ v .entries = append (v .entries , entry )
171+ }
172+ }
173+
174+ return ast .GoToNext
175+ }
176+
177+ // ExtractTextFromNode recursively extracts text content from AST nodes
178+ func ExtractTextFromNode (node ast.Node ) string {
179+ var text strings.Builder
180+
181+ ast .WalkFunc (node , func (node ast.Node , entering bool ) ast.WalkStatus {
182+ if entering {
183+ switch n := node .(type ) {
184+ case * ast.Text :
185+ text .Write (n .Literal )
186+ case * ast.Code :
187+ text .Write (n .Literal )
188+ }
189+ }
190+ return ast .GoToNext
191+ })
192+
193+ return text .String ()
194+ }
195+
196+ // GetTOCEntries returns TOC entries
197+ func GetTOCEntries (content []byte ) ([]TOCEntry , error ) {
198+
199+ // Create parser with extensions
200+ extensions := parser .CommonExtensions | parser .AutoHeadingIDs
201+ p := parser .NewWithExtensions (extensions )
202+
203+ // Parse the markdown content
204+ doc := markdown .Parse (content , p )
205+
206+ // Create visitor to collect TOC entries
207+ visitor := & TOCVisitor {}
208+
209+ // Walk the AST and collect headers
210+ ast .Walk (doc , visitor )
211+
212+ return visitor .entries , nil
213+ }
214+
134215// SetLightTheme sets theme to 'light'
135216func (r * PdfRenderer ) SetLightTheme () {
136217 r .BackgroundColor = Colorlookup ("white" )
0 commit comments