Merge pull request #41 from eSlider/fix/put-txt-fixed-width
fix(docs): fixed-width txt as monospace code block
This commit is contained in:
@@ -101,11 +101,14 @@ func (t Tools) TXTToDOCX(txtPath, docxPath string) error {
|
|||||||
return t.MDToDOCX(tmpMD, docxPath)
|
return t.MDToDOCX(tmpMD, docxPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxtToMarkdown converts plain text to Markdown that preserves line breaks in DOCX output.
|
// TxtToMarkdown converts plain text to Markdown for DOCX output.
|
||||||
// Single newlines become hard breaks; blank lines stay paragraph separators.
|
// Prose text: each line is a hard break. Fixed-width extracts (INE, pdftotext -layout):
|
||||||
|
// wrapped in a fenced code block (monospace, columns preserved).
|
||||||
func TxtToMarkdown(content string) string {
|
func TxtToMarkdown(content string) string {
|
||||||
content = strings.ReplaceAll(content, "\r\n", "\n")
|
content = normalizeTxtNewlines(content)
|
||||||
content = strings.ReplaceAll(content, "\r", "\n")
|
if isFixedWidthTxt(content) {
|
||||||
|
return "```\n" + content + "\n```\n"
|
||||||
|
}
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
for _, line := range strings.Split(content, "\n") {
|
for _, line := range strings.Split(content, "\n") {
|
||||||
if strings.TrimSpace(line) == "" {
|
if strings.TrimSpace(line) == "" {
|
||||||
@@ -118,6 +121,33 @@ func TxtToMarkdown(content string) string {
|
|||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeTxtNewlines(content string) string {
|
||||||
|
content = strings.ReplaceAll(content, "\r\n", "\n")
|
||||||
|
return strings.ReplaceAll(content, "\r", "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// isFixedWidthTxt detects pdftotext -layout style extracts (many indented/spaced columns).
|
||||||
|
func isFixedWidthTxt(content string) bool {
|
||||||
|
lines := strings.Split(content, "\n")
|
||||||
|
if len(lines) < 8 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
indented, long := 0, 0
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.TrimSpace(line) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(line) >= 72 {
|
||||||
|
long++
|
||||||
|
}
|
||||||
|
if len(line) > 0 && (line[0] == ' ' || line[0] == '\t') {
|
||||||
|
indented++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n := len(lines)
|
||||||
|
return indented*100/n >= 20 || (long >= 5 && indented*100/n >= 10)
|
||||||
|
}
|
||||||
|
|
||||||
// MDToDOCX writes a DOCX next to or at outPath from a Markdown file.
|
// MDToDOCX writes a DOCX next to or at outPath from a Markdown file.
|
||||||
func (t Tools) MDToDOCX(mdPath, docxPath string) error {
|
func (t Tools) MDToDOCX(mdPath, docxPath string) error {
|
||||||
if Ext(mdPath) != ".md" && Ext(mdPath) != ".markdown" {
|
if Ext(mdPath) != ".md" && Ext(mdPath) != ".markdown" {
|
||||||
|
|||||||
@@ -34,6 +34,25 @@ func TestNeedsOCR_Image(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTxtToMarkdown_FixedWidthUsesCodeBlock(t *testing.T) {
|
||||||
|
var lines []string
|
||||||
|
for i := 0; i < 12; i++ {
|
||||||
|
lines = append(lines, " column layout line "+strings.Repeat("x", 40))
|
||||||
|
}
|
||||||
|
in := strings.Join(lines, "\n")
|
||||||
|
md := TxtToMarkdown(in)
|
||||||
|
if !strings.HasPrefix(md, "```\n") || !strings.Contains(md, "```") {
|
||||||
|
t.Fatalf("expected code block: %q", md[:min(80, len(md))])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
func TestTxtToMarkdownPreservesLines(t *testing.T) {
|
func TestTxtToMarkdownPreservesLines(t *testing.T) {
|
||||||
in := "line1\nline2\n\nline4"
|
in := "line1\nline2\n\nline4"
|
||||||
md := TxtToMarkdown(in)
|
md := TxtToMarkdown(in)
|
||||||
|
|||||||
Reference in New Issue
Block a user