Refactor PDF content rendering: improve list indentation logic, add numbered code block rendering with gutter, and update text wrapping alignment.
This commit is contained in:
Generated
+6
-12
@@ -4,20 +4,12 @@
|
|||||||
<option name="autoReloadType" value="ALL" />
|
<option name="autoReloadType" value="ALL" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="c64f46d5-641a-468c-8fc1-94edec1f2deb" name="Changes" comment="Initial commit: added Markdown to IHK Chemnitz PDF converter with core structure and features, including YAML config, Goldmark parser, and PDF renderer.">
|
<list default="true" id="c64f46d5-641a-468c-8fc1-94edec1f2deb" name="Changes" comment="Remove outdated `toc_pages.txt`, add new Go modules for IHK Chemnitz PDF rendering including diagrams, tables, and TOC functionality.">
|
||||||
<change afterPath="$PROJECT_DIR$/it-berufe-handreichung-vo2020-data(1).pdf" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/MarkdownToIHKChemnits" beforeDir="false" afterPath="$PROJECT_DIR$/MarkdownToIHKChemnits" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/config.go" beforeDir="false" afterPath="$PROJECT_DIR$/config.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/img-000.png" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/img-001.png" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/img-002.png" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/img-003.png" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/main.go" beforeDir="false" afterPath="$PROJECT_DIR$/main.go" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/markdown_parser.go" beforeDir="false" afterPath="$PROJECT_DIR$/markdown_parser.go" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/markdown_parser.go" beforeDir="false" afterPath="$PROJECT_DIR$/markdown_parser.go" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/pdf_renderer.go" beforeDir="false" afterPath="$PROJECT_DIR$/pdf_renderer.go" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/pdf_content.go" beforeDir="false" afterPath="$PROJECT_DIR$/pdf_content.go" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/projektarbeit.pdf" beforeDir="false" afterPath="$PROJECT_DIR$/projektarbeit.pdf" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/projektarbeit.pdf" beforeDir="false" afterPath="$PROJECT_DIR$/projektarbeit.pdf" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/report.md" beforeDir="false" afterPath="$PROJECT_DIR$/report.md" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/report.md" beforeDir="false" afterPath="$PROJECT_DIR$/report.md" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/toc_pages.txt" beforeDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
@@ -101,7 +93,9 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="VcsManagerConfiguration">
|
<component name="VcsManagerConfiguration">
|
||||||
<MESSAGE value="Initial commit: added Markdown to IHK Chemnitz PDF converter with core structure and features, including YAML config, Goldmark parser, and PDF renderer." />
|
<MESSAGE value="Initial commit: added Markdown to IHK Chemnitz PDF converter with core structure and features, including YAML config, Goldmark parser, and PDF renderer." />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="Initial commit: added Markdown to IHK Chemnitz PDF converter with core structure and features, including YAML config, Goldmark parser, and PDF renderer." />
|
<MESSAGE value="Remove outdated IHK guideline text and refactor PDF renderer for improved modularity, DIN 5008 compliance, and glossary/abbreviation support." />
|
||||||
|
<MESSAGE value="Remove outdated `toc_pages.txt`, add new Go modules for IHK Chemnitz PDF rendering including diagrams, tables, and TOC functionality." />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="Remove outdated `toc_pages.txt`, add new Go modules for IHK Chemnitz PDF rendering including diagrams, tables, and TOC functionality." />
|
||||||
</component>
|
</component>
|
||||||
<component name="XDebuggerManager">
|
<component name="XDebuggerManager">
|
||||||
<breakpoint-manager>
|
<breakpoint-manager>
|
||||||
|
|||||||
Binary file not shown.
+2
-2
@@ -119,8 +119,8 @@ func RenderAST(doc ast.Node, content []byte, r *IHKRenderer) error {
|
|||||||
}
|
}
|
||||||
// Fall through: render as plain code block on error
|
// Fall through: render as plain code block on error
|
||||||
}
|
}
|
||||||
// Render non-diagram code blocks as monospace paragraphs
|
// Render as a numbered code block (gutter + monospace body).
|
||||||
r.RenderParagraphSpans([]InlineSpan{{Text: code, Code: true}})
|
r.RenderCodeBlock(lang, code)
|
||||||
return ast.WalkSkipChildren, nil
|
return ast.WalkSkipChildren, nil
|
||||||
|
|
||||||
// ── Images ────────────────────────────────────────────────────────────
|
// ── Images ────────────────────────────────────────────────────────────
|
||||||
|
|||||||
+86
-6
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/go-pdf/fpdf"
|
"github.com/go-pdf/fpdf"
|
||||||
)
|
)
|
||||||
@@ -89,13 +90,28 @@ func (r *IHKRenderer) RenderListItem(spans []InlineSpan, ordered bool, index, in
|
|||||||
r.pdf.SetFont("Helvetica", "", dinFontBody)
|
r.pdf.SetFont("Helvetica", "", dinFontBody)
|
||||||
|
|
||||||
lm, _, _, _ := r.pdf.GetMargins()
|
lm, _, _, _ := r.pdf.GetMargins()
|
||||||
indentMM := float64(indent+1) * 8.0
|
|
||||||
r.pdf.SetX(lm + indentMM)
|
// 3 mm base indent + 5 mm per nesting level (reduced from previous 8 mm per level)
|
||||||
|
indentMM := 3.0 + float64(indent)*5.0
|
||||||
|
|
||||||
prefix := "• "
|
prefix := "• "
|
||||||
if ordered {
|
if ordered {
|
||||||
prefix = strconv.Itoa(index) + ". "
|
prefix = strconv.Itoa(index) + ". "
|
||||||
}
|
}
|
||||||
|
prefixW := r.pdf.GetStringWidth(prefix) + 1.0
|
||||||
|
|
||||||
|
// textX is where the item text starts; wrapped lines must align here too.
|
||||||
|
textX := lm + indentMM + prefixW
|
||||||
|
textW := r.usableWidth() - indentMM - prefixW
|
||||||
|
|
||||||
|
// Temporarily move the left margin to textX so that MultiCell wraps
|
||||||
|
// continuation lines flush with the text, not back to the page margin.
|
||||||
|
r.pdf.SetLeftMargin(textX)
|
||||||
|
defer r.pdf.SetLeftMargin(lm)
|
||||||
|
|
||||||
|
// Render bullet/number; CellFormat advances X to textX automatically.
|
||||||
|
r.pdf.SetX(lm + indentMM)
|
||||||
|
r.pdf.CellFormat(prefixW, dinLineHtBody, r.tr(prefix), "", 0, "L", false, 0, "")
|
||||||
|
|
||||||
hasFormatting := false
|
hasFormatting := false
|
||||||
for _, s := range spans {
|
for _, s := range spans {
|
||||||
@@ -105,15 +121,13 @@ func (r *IHKRenderer) RenderListItem(spans []InlineSpan, ordered bool, index, in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uw := r.usableWidth() - indentMM
|
|
||||||
if !hasFormatting {
|
if !hasFormatting {
|
||||||
text := prefix
|
text := ""
|
||||||
for _, s := range spans {
|
for _, s := range spans {
|
||||||
text += s.Text
|
text += s.Text
|
||||||
}
|
}
|
||||||
r.pdf.MultiCell(uw, dinLineHtBody, r.tr(text), "", "L", false)
|
r.pdf.MultiCell(textW, dinLineHtBody, r.tr(text), "", "L", false)
|
||||||
} else {
|
} else {
|
||||||
r.pdf.Write(dinLineHtBody, r.tr(prefix))
|
|
||||||
for _, span := range spans {
|
for _, span := range spans {
|
||||||
style := fontStyle(span.Bold, span.Italic)
|
style := fontStyle(span.Bold, span.Italic)
|
||||||
if span.Code {
|
if span.Code {
|
||||||
@@ -254,6 +268,72 @@ func (r *IHKRenderer) RenderImage(path string, caption string) {
|
|||||||
r.pdf.Ln(dinSpaceAfterParagraph)
|
r.pdf.Ln(dinSpaceAfterParagraph)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Code block layout constants.
|
||||||
|
const (
|
||||||
|
codeFont = 9.0 // pt — Courier, smaller than body text
|
||||||
|
codeLineHt = 4.5 // mm ≈ 9 pt × 1.3 line spacing
|
||||||
|
codeGutterW = 11.0 // mm — column reserved for line numbers
|
||||||
|
)
|
||||||
|
|
||||||
|
// RenderCodeBlock renders a fenced code block with a line-number gutter.
|
||||||
|
//
|
||||||
|
// Layout:
|
||||||
|
//
|
||||||
|
// ┌──────┬──────────────────────────────────┐
|
||||||
|
// │ 1 │ package main │
|
||||||
|
// │ 2 │ │
|
||||||
|
// │ 3 │ func main() { … } │
|
||||||
|
// └──────┴──────────────────────────────────┘
|
||||||
|
//
|
||||||
|
// The language label is shown in small italic text above the block.
|
||||||
|
// Lines that exceed the printable width are clipped — code should be
|
||||||
|
// formatted to reasonable lengths before conversion.
|
||||||
|
func (r *IHKRenderer) RenderCodeBlock(lang, code string) {
|
||||||
|
lines := strings.Split(strings.TrimRight(code, "\n"), "\n")
|
||||||
|
if len(lines) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.pdf.Ln(dinSpaceAfterParagraph)
|
||||||
|
|
||||||
|
// Language label — top-right, italic, grey
|
||||||
|
if lang != "" {
|
||||||
|
r.pdf.SetFont("Helvetica", "I", dinFontCaption)
|
||||||
|
r.pdf.SetTextColor(100, 100, 100)
|
||||||
|
r.pdf.CellFormat(0, dinLineHtCaption, r.tr(lang), "", 1, "R", false, 0, "")
|
||||||
|
r.pdf.SetTextColor(0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
lm, _, _, bm := r.pdf.GetMargins()
|
||||||
|
_, pageH := r.pdf.GetPageSize()
|
||||||
|
uw := r.usableWidth()
|
||||||
|
codeW := uw - codeGutterW
|
||||||
|
|
||||||
|
r.pdf.SetFont("Courier", "", codeFont)
|
||||||
|
|
||||||
|
for i, line := range lines {
|
||||||
|
// Start a new page if this line would fall below the bottom margin.
|
||||||
|
if r.pdf.GetY()+codeLineHt > pageH-bm {
|
||||||
|
r.pdf.AddPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gutter: darker grey, right-aligned line number.
|
||||||
|
r.pdf.SetFillColor(218, 218, 218)
|
||||||
|
r.pdf.SetX(lm)
|
||||||
|
r.pdf.CellFormat(codeGutterW, codeLineHt,
|
||||||
|
fmt.Sprintf("%4d ", i+1), "", 0, "R", true, 0, "")
|
||||||
|
|
||||||
|
// Code line: lighter grey, left-aligned, small leading space.
|
||||||
|
r.pdf.SetFillColor(246, 246, 246)
|
||||||
|
r.pdf.CellFormat(codeW, codeLineHt,
|
||||||
|
r.tr(" "+line), "", 1, "L", true, 0, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset colours so subsequent content is unaffected.
|
||||||
|
r.pdf.SetFillColor(255, 255, 255)
|
||||||
|
r.pdf.Ln(dinSpaceAfterParagraph)
|
||||||
|
}
|
||||||
|
|
||||||
// fontStyle returns the fpdf font style string for a given bold/italic combination.
|
// fontStyle returns the fpdf font style string for a given bold/italic combination.
|
||||||
func fontStyle(bold, italic bool) string {
|
func fontStyle(bold, italic bool) string {
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
Binary file not shown.
@@ -48,7 +48,7 @@ manuelle Sorgfalt bei jedem Absatz.
|
|||||||
Ziel ist ein Go-Tool, das **Markdown** in PDF umwandelt und dabei alle
|
Ziel ist ein Go-Tool, das **Markdown** in PDF umwandelt und dabei alle
|
||||||
formalen Anforderungen der IHK Chemnitz erfüllt. Es soll:
|
formalen Anforderungen der IHK Chemnitz erfüllt. Es soll:
|
||||||
|
|
||||||
- die Prüfungsvorbereitung erleichtern,
|
- die Prüfungsvorbereitung erleichtern, die Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen unddie Qualität der Dokumente *einheitlich* sicherstellen und
|
||||||
- die Qualität der Dokumente *einheitlich* sicherstellen und
|
- die Qualität der Dokumente *einheitlich* sicherstellen und
|
||||||
- den Prozess vollständig automatisieren.
|
- den Prozess vollständig automatisieren.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user