github.com/xyproto/orbiton/v2@v2.65.12-0.20240516144430-e10a419274ec/pandoc.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "os" 6 "os/exec" 7 "path/filepath" 8 9 "github.com/xyproto/env/v2" 10 "github.com/xyproto/files" 11 "github.com/xyproto/vt100" 12 ) 13 14 var ( 15 userConfigDir = env.Dir("XDG_CONFIG_HOME", "~/.config") 16 pandocTexFilename = filepath.Join(userConfigDir, "o", "pandoc.tex") 17 ) 18 19 const ( 20 listingsSetupTex = `% https://tex.stackexchange.com/a/179956/5116 21 \usepackage{xcolor} 22 \lstset{ 23 basicstyle=\ttfamily, 24 keywordstyle=\color[rgb]{0.13,0.29,0.53}\bfseries, 25 stringstyle=\color[rgb]{0.31,0.60,0.02}, 26 commentstyle=\color[rgb]{0.56,0.35,0.01}\itshape, 27 stepnumber=1, 28 numbersep=5pt, 29 backgroundcolor=\color[RGB]{248,248,248}, 30 showspaces=false, 31 showstringspaces=false, 32 showtabs=false, 33 tabsize=2, 34 captionpos=b, 35 breaklines=true, 36 breakatwhitespace=true, 37 breakautoindent=true, 38 escapeinside={\%*}{*)}, 39 linewidth=\textwidth, 40 basewidth=0.5em, 41 showlines=true, 42 } 43 ` 44 ) 45 46 // exportPandocPDF will render PDF from Markdown using pandoc 47 func (e *Editor) exportPandocPDF(c *vt100.Canvas, tty *vt100.TTY, status *StatusBar, pandocPath, pdfFilename string) error { 48 // This function used to be concurrent. There are some leftovers from this that could be refactored away. 49 50 status.ClearAll(c) 51 status.SetMessage("Rendering to PDF using Pandoc...") 52 status.ShowNoTimeout(c, e) 53 54 // The reason for writing to a temporary file is to be able to export without saving 55 // the currently edited file. 56 57 tempFilename := "" 58 f, err := os.CreateTemp(tempDir, "_o*.md") 59 if err != nil { 60 return err 61 } 62 defer os.Remove(tempFilename) 63 tempFilename = f.Name() 64 65 // TODO: Implement a SaveAs function 66 67 // Save to tmpfn 68 oldFilename := e.filename 69 e.filename = tempFilename 70 err = e.Save(c, tty) 71 if err != nil { 72 e.filename = oldFilename 73 status.ClearAll(c) 74 status.SetError(err) 75 status.Show(c, e) 76 return err 77 } 78 e.filename = oldFilename 79 80 // Check if the PAPERSIZE environment variable is set. Default to "a4". 81 papersize := env.Str("PAPERSIZE", "a4") 82 83 pandocCommand := exec.Command(pandocPath, "-fmarkdown-implicit_figures", "--toc", "-Vgeometry:left=1cm,top=1cm,right=1cm,bottom=2cm", "-Vpapersize:"+papersize, "-Vfontsize=12pt", "--pdf-engine=xelatex", "-o", pdfFilename) 84 85 expandedTexFilename := env.ExpandUser(pandocTexFilename) 86 87 // Write the Pandoc Tex style file, for configuring the listings package, if it does not already exist 88 if !files.Exists(expandedTexFilename) { 89 // First create the folder, if needed, in a best effort attempt 90 folderPath := filepath.Dir(expandedTexFilename) 91 os.MkdirAll(folderPath, os.ModePerm) 92 // Write the Pandoc Tex style file 93 err = os.WriteFile(expandedTexFilename, []byte(listingsSetupTex), 0o644) 94 if err != nil { 95 status.SetErrorMessage("Could not write " + pandocTexFilename + ": " + err.Error()) 96 status.Show(c, e) 97 return err 98 } 99 } 100 101 // use the listings package 102 pandocCommand.Args = append(pandocCommand.Args, "--listings", "-H"+expandedTexFilename) 103 104 // add output and input filenames 105 pandocCommand.Args = append(pandocCommand.Args, "-o"+pdfFilename, oldFilename) 106 107 // Save the command in a temporary file, using the current filename 108 saveCommand(pandocCommand) 109 110 // Use the temporary filename for the last argument, now that the command has been saved 111 pandocCommand.Args[len(pandocCommand.Args)-1] = tempFilename 112 113 if output, err := pandocCommand.CombinedOutput(); err != nil { 114 status.ClearAll(c) 115 116 // The program was executed, but failed 117 outputByteLines := bytes.Split(bytes.TrimSpace(output), []byte{'\n'}) 118 errorMessage := string(outputByteLines[len(outputByteLines)-1]) 119 120 if len(errorMessage) == 0 { 121 errorMessage = err.Error() 122 } 123 124 status.SetErrorMessage(errorMessage) 125 status.Show(c, e) 126 127 return err 128 } 129 130 status.ClearAll(c) 131 status.SetMessage("Saved " + pdfFilename) 132 status.ShowNoTimeout(c, e) 133 return nil 134 }