Skip to content
Snippets Groups Projects
Select Git revision
  • 57deb525c6852f46d7ee5a22c98a7e24b6438599
  • master default protected
  • 1.31
  • 4.34.1
  • 4.34.0
  • 4.33.1
  • 4.33.0
  • 4.32.2
  • 4.32.1
  • 4.32.0
  • 4.31.0
  • 4.30.1
  • 4.30.0
  • 4.29.1
  • 4.29.0
  • 4.28.0
  • 4.27.0
  • 4.26.0
  • 4.25.5
  • 4.25.4
  • 4.25.3
  • 4.25.2
  • 4.25.1
23 results

Monster.Data.Datasource.html

Blame
  • pdf.go 8.37 KiB
    // Copyright 2023 schukai GmbH
    // SPDX-License-Identifier: AGPL-3.0
    
    package document
    
    import (
    	"fmt"
    	"gitlab.schukai.com/oss/utilities/documentation-manager/environment"
    	"gitlab.schukai.com/oss/utilities/documentation-manager/translations"
    	"gitlab.schukai.com/oss/utilities/documentation-manager/utils"
    	"io/ioutil"
    	"os"
    	"path"
    	"path/filepath"
    	"regexp"
    	"strings"
    	"text/template"
    	"time"
    )
    
    type BuildPdfEnvironment struct {
    	SourcePath string
    	DateFormat string
    	OutputPath string
    	Verbose    bool
    	Templates  struct {
    		Latex    string
    		Markdown string
    	}
    }
    
    func (t *BuildPdfEnvironment) GetComponentsTemplates() map[string]string {
    	return map[string]string{}
    }
    
    type PdfDataset struct {
    	Documents     string
    	CreatedFormat string
    }
    
    func NewPdfDataset(env BuildPdfEnvironment) (*PdfDataset, error) {
    
    	files, err := getFiles(&env, env.SourcePath)
    	if err != nil {
    		return nil, err
    	}
    
    	mapFiles, keys := buildFileMap(files)
    
    	d := &PdfDataset{}
    
    	docs := []string{}
    	for _, key := range keys {
    
    		text := mapFiles[key].textMeta.text
    
    		text, s1Map := utils.MaskCodeBlocks(text, mapFiles[key].relSourcePath, 3)
    		text, s2Map := utils.MaskCodeBlocks(text, mapFiles[key].relSourcePath, 1)
    
    		text = convertHeadlines(text, mapFiles[key].level, mapFiles[key].textMeta.meta.Level)
    		text = convertAwesomeBoxesToLatex(text)
    		text = convertImages(text, mapFiles[key].baseDir)
    		text = convertCircledNumbersToLatex(text)
    		text = replaceKbdToLatex(text)
    		text = replaceRelativeLinksToLatex(text, mapFiles[key], mapFiles)
    
    		text = utils.InsertCodeBlocks(text, s2Map)
    		text = utils.InsertCodeBlocks(text, s1Map)
    
    		docs = append(docs, "\\newpage{}"+text)
    	}
    
    	d.Documents = strings.Join(docs, "\n")
    
    	format := environment.State.GetDocumentDateFormat("")
    	now := time.Now()
    	d.CreatedFormat = now.Format(format)
    
    	if env.Verbose {
    		fmt.Println(d.Documents)
    	}
    
    	return d, nil
    }
    
    func BuildPDF(env BuildPdfEnvironment) error {
    
    	output := env.OutputPath
    
    	if env.DateFormat != "" {
    		dateFormat = env.DateFormat
    	}
    
    	if !filepath.IsAbs(output) {
    		pwd := os.Getenv("PWD")
    		output = path.Clean(path.Join(pwd, output))
    	}
    
    	if output == "" {
    		environment.ExitWithError(2, "the output option must be specified")
    	}
    
    	fileInfo, err := os.Stat(output)
    	if err != nil {
    		if !os.IsNotExist(err) {
    			environment.ExitWithError(2, "cannot stat output file", err.Error())
    		}
    	} else {
    		if fileInfo.IsDir() {
    			environment.ExitWithError(2, "output file is a directory", output)
    		}
    	}
    
    	file, err := ioutil.TempFile(os.TempDir(), environment.State.GetInfo().Mnemonic)
    	checkError(err)
    
    	defer func() {
    		file.Close()
    		os.Remove(file.Name())
    	}()
    
    	content := environment.ReadTemplate(env.Templates.Markdown)
    	t, err := template.New("pdf").Funcs(getFuncMap(&env)).Parse(content)
    	checkError(err)
    
    	d, err := NewPdfDataset(env)
    	checkError(err)
    
    	err = t.Execute(file, d)
    	checkError(err)
    
    	luaFilter := createLuaFile()
    	defer func() {
    		if luaFilter != nil {
    			luaFilter.Close()
    			os.Remove(luaFilter.Name())
    		}
    	}()
    
    	runPandoc(file.Name(), output, env.Templates.Latex, luaFilter.Name(), env.Verbose)
    	return nil
    
    }
    
    func replaceRelativeLinksToLatex(content string, f *SourceFile, fileMap SourceFileMap) string {
    
    	label := "link_" + f.hash
    	content = "\\hypertarget{" + label + "}{ } \n" + strings.TrimSpace(content) + "\n"
    
    	regEx := regexp.MustCompile(`(?:^|[^!])(?P<match>\[(?P<label>[^]]*)\]\((?P<path>[^)]*)\))`)
    	matches := regEx.FindAllStringSubmatch(content, -1)
    	if matches == nil {
    		return content
    	}
    
    	for _, match := range matches {
    		result := make(map[string]string)
    		for i, name := range regEx.SubexpNames() {
    			if i != 0 && name != "" {
    				result[name] = match[i]
    			}
    		}
    
    		if filepath.IsAbs(result["path"]) {
    			//environment.State.AddWarning(translations.T.Sprintf("Absolute path not found: %s", result["path"]))
    			continue
    		}
    
    		if utils.IsUrl(result["path"]) {
    			continue
    		}
    
    		d := filepath.Dir(f.relSourcePath)
    		p := filepath.Join(d, result["path"])
    
    		p = strings.Split(p, "#")[0]
    
    		ext := filepath.Ext(p)
    		if ext == "" {
    			environment.State.AddWarning(translations.T.Sprintf("No extension, of the link %s, in the file %s is not supported.", p, f.absSourcePath))
    			continue
    		}
    
    		if ext != ".md" && ext != ".markdown" {
    			environment.State.AddWarning(translations.T.Sprintf("The extension %s, of the link %s, in the file %s is not supported.", ext, p, f.absSourcePath))
    			continue
    		}
    
    		s := fileMap.findByRelativePath(p)
    		if s == nil {
    			environment.State.AddWarning(translations.T.Sprintf("relative path %s, in file %s, cannot be resolved", result["path"], f.absSourcePath))
    			continue
    		}
    
    		replace := "\\hyperlink{link_" + s.hash + "}{" + escapeLatexSpecialChars(result["label"]) + "}"
    		content = strings.Replace(content, result["match"], replace, -1)
    
    	}
    
    	return content
    }
    
    func createLuaFile() *os.File {
    
    	tmp, err := ioutil.TempFile(os.TempDir(), "lua-filter")
    	if err != nil {
    		environment.ExitWithError(2, "A temporary file cannot be created", err.Error())
    	}
    
    	tmp.WriteString(`
    function RawBlock (raw)
    	return raw.format:match "html"
    	and pandoc.read(raw.text, "html").blocks
    	or raw
    end
    `)
    
    	return tmp
    
    }
    
    // https://ftp.gwdg.de/pub/ctan/graphics/awesomebox/awesomebox.pdf
    func convertAwesomeBoxesToLatex(content string) string {
    
    	regEx := regexp.MustCompile(`(?m)(?P<matches>!!!\s*(?P<type>[^\s]+)\s+?(?P<title>[^\n]*)\n(?P<lines>(?P<lastline>[[:blank:]]+[^\n]+\n)+))`)
    
    	matches := regEx.FindAllStringSubmatch(content, -1)
    	if matches == nil {
    		return content
    	}
    
    	for _, match := range matches {
    		result := make(map[string]string)
    		for i, name := range regEx.SubexpNames() {
    			if i != 0 && name != "" {
    				result[name] = match[i]
    			}
    		}
    
    		boxtype := "note"
    
    		switch {
    		case utils.Contains([]string{"notebox", "note", "info"}, result["type"]):
    			boxtype = "note"
    		case utils.Contains([]string{"tipbox", "tip", "hint"}, result["type"]):
    			boxtype = "tip"
    
    		case utils.Contains([]string{"warningbox", "warning", "warn"}, result["type"]):
    			boxtype = "warning"
    
    		case utils.Contains([]string{"cautionbox", "caution", "danger"}, result["type"]):
    			boxtype = "caution"
    
    		case utils.Contains([]string{"importantbox", "important"}, result["type"]):
    			boxtype = "important"
    		}
    
    		c := ""
    
    		t := escapeLatexSpecialChars(result["title"])
    		if t != "" {
    			c += "\\textbf{" + utils.TrimQuotes(t) + "}\n"
    		}
    
    		lines := result["lines"]
    		lines = convertAwesomeBoxesMarkdownWithPandoc(lines)
    
    		c += "\n" + lines + "\n" //+escapeLatexSpecialChars(result["lastline"])) + "\n"
    		awesomebox := `\begin{` + boxtype + `block}` + c + "\n" + `\end{` + boxtype + `block}`
    
    		content = strings.Replace(content, result["matches"], "\n"+awesomebox+"\n", 1)
    
    	}
    
    	return content
    
    }
    
    func convertAwesomeBoxesMarkdownWithPandoc(content string) string {
    	output := runInlinePandoc(escapeLatexSpecialChars(utils.TrimLines(content)))
    	return output
    }
    
    // The following characters play a special role in LaTeX and are called special printing characters, or simply special characters.
    //
    //	# $ % & ~ _ ^ \ { }
    //
    // Whenever you put one of these special characters into your file, you are doing something special. If you simply want the character
    // to be printed just as any other letter, include a \ in front of the character. For example, \$ will produce $ in your output.
    //
    // The exception to the rule is the \ itself because \\ has its own special meaning. A \ is produced by typing $\backslash$ in your file.
    //
    // The meaning of these characters are:
    //
    // ~ (tilde) unbreakable space, use it whenever you want to leave a space which is unbreakable, and cannot expand or shrink, as e.q. in names: A.~U.~Thor.
    // $ (dollar sign) to start an finish math mode.
    // _ (underscore) for subscripts in math mode.
    // ^ (hat) for superscripts in math mode.
    // \ (backslash) starting commands, which extend until the first non-alphanumerical character. The space following the command is swallowed. The following line results in what expected:
    //
    //	The \TeX nician is an expert in \TeX{} language.
    //
    // {} (curly brackets) to group and separate commands from its surroundings. Must appear in pairs.
    func escapeLatexSpecialChars(content string) string {
    
    	result := ""
    
    	runes := []rune(content)
    	for i, k := range runes {
    
    		if k == '\\' {
    			result += "\\textbackslash{}"
    			continue
    		} else if utils.Contains([]string{"#", "$", "%", "&", "~", "_", "^", "\\", "{", "}"}, string(k)) {
    			if i == 0 || runes[i-1] != '\\' {
    				result += "\\"
    			}
    
    		}
    
    		result += string(k)
    
    	}
    
    	return result
    
    }