all repos — hastyscribe @ b41006865d0291df7ca4dd0b0408d80ac2df04fb

A professional markdown compiler.

Merge branch 'master' of github.com:h3rald/hastyscribe
h3rald h3rald@h3rald.com
Mon, 19 Apr 2021 20:00:35 +0200
commit

b41006865d0291df7ca4dd0b0408d80ac2df04fb

parent

a71105e09677538c043e4f671bab9b36da40b3e3

M README.mdREADME.md

@@ -23,6 +23,7 @@ * **\-\-output-file=<file>** causes HastyScribe to write output to a local file (Use [\-\-output-file=-](class:opt) to output to standard output).

* **\-\-watermark=<file>** causes HastyScribe to embed and display an image as a watermark throughout the document. * **\-\-fragment** causes HastyScribe to output just an HTML fragment instead of a full document, without embedding any image, font or stylesheet. * **\-\-dump=all|styles|fonts** causes HastyScribe to dump all resources/stylesheets/fonts to the current directory. + * **\-\-help** causes HastyScribe to display the usage information and quit. ## FAQs
D build_guide

@@ -1,1 +0,0 @@

-./hastyscribe doc/HastyScribe_UserGuide.md --field/version=1.12.0
M doc/-usage.mddoc/-usage.md

@@ -18,6 +18,7 @@ * [\-\-output-file=<file>](class:opt) causes {{hs}} to write output to a local file (Use [\-\-output-file=-](class:opt) to output to standard output).

* [\-\-watermark=<file>](class:opt) causes {{hs}} to embed and display an image as a watermark throughout the document. * [\-\-fragment](class:opt) causes {{hs}} to output just an HTML fragment instead of a full document, without embedding any image, font or stylesheet. * [\-\-dump=all|styles|fonts](class:opt) causes {{hs}} to dump all resources/stylesheets/fonts to the current directory. + * [\-\-help](class:opt) causes {{hs}} to display the usage information and quit. ## Linux/OSX Examples
M doc/HastyScribe_UserGuide.htmdoc/HastyScribe_UserGuide.htm

@@ -7451,9 +7451,9 @@

<p>The easiest way to get HastyScribe is by downloading one of the prebuilt binaries from the <a href="https://github.com/h3rald/hastyscribe/releases/download/v">Github Release Page</a>:</p> <ul> -<li><a href="https://github.com/h3rald/hastyscribe/releases/download/v1.12.0/hastyscribe_v1.12.0_macosx_x64.zip">HastyScribe for Mac OS X (x64)</a></li> -<li><a href="https://github.com/h3rald/hastyscribe/releases/download/v1.12.0/hastyscribe_v1.12.0_windows_x64.zip">HastyScribe for Windows (x64)</a></li> -<li><a href="https://github.com/h3rald/hastyscribe/releases/download/v1.12.0/hastyscribe_v1.12.0_linux_x64.zip">HastyScribe for Linux (x64)</a></li> +<li><a href="https://github.com/h3rald/hastyscribe/releases/download/v1.12.4/hastyscribe_v1.12.4_macosx_x64.zip">HastyScribe for Mac OS X (x64)</a></li> +<li><a href="https://github.com/h3rald/hastyscribe/releases/download/v1.12.4/hastyscribe_v1.12.4_windows_x64.zip">HastyScribe for Windows (x64)</a></li> +<li><a href="https://github.com/h3rald/hastyscribe/releases/download/v1.12.4/hastyscribe_v1.12.4_linux_x64.zip">HastyScribe for Linux (x64)</a></li> </ul>

@@ -7507,6 +7507,7 @@ <li><span class="opt">--output-file=&lt;file&gt;</span> causes HastyScribe to write output to a local file (Use <span class="opt">--output-file=-</span> to output to standard output).</li>

<li><span class="opt">--watermark=&lt;file&gt;</span> causes HastyScribe to embed and display an image as a watermark throughout the document.</li> <li><span class="opt">--fragment</span> causes HastyScribe to output just an HTML fragment instead of a full document, without embedding any image, font or stylesheet.</li> <li><span class="opt">--dump=all|styles|fonts</span> causes HastyScribe to dump all resources/stylesheets/fonts to the current directory.</li> +<li><span class="opt">--help</span> causes HastyScribe to display the usage information and quit.</li> </ul> </li> </ul>

@@ -7610,43 +7611,43 @@ </thead>

<tbody> <tr> <td><code>{{$timestamp}}</code> </td> -<td> 1598783377</td> +<td> 1617135534</td> </tr> <tr> <td><code>{{$date}}</code> </td> -<td> 2020-08-30</td> +<td> 2021-03-30</td> </tr> <tr> <td><code>{{$full-date}}</code> </td> -<td> Sunday, August 30, 2020</td> +<td> Tuesday, March 30, 2021</td> </tr> <tr> <td><code>{{$long-date}}</code> </td> -<td> August 30, 2020</td> +<td> March 30, 2021</td> </tr> <tr> <td><code>{{$medium-date}}</code> </td> -<td> Aug 30, 2020</td> +<td> Mar 30, 2021</td> </tr> <tr> <td><code>{{$short-date}}</code> </td> -<td> 8/30/20</td> +<td> 3/30/21</td> </tr> <tr> <td><code>{{$short-time}}</code> </td> -<td> 12:29 PM</td> +<td> 22:18 PM</td> </tr> <tr> <td><code>{{$short-time-24}}</code> </td> -<td> 12:29</td> +<td> 22:18</td> </tr> <tr> <td><code>{{$time}}</code> </td> -<td> 12:29:37 PM</td> +<td> 22:18:54 PM</td> </tr> <tr> <td><code>{{$time-24}}</code> </td> -<td> 12:29:37</td> +<td> 22:18:54</td> </tr> <tr> <td><code>{{$day}}</code> </td>

@@ -7658,23 +7659,23 @@ <td> 30</td>

</tr> <tr> <td><code>{{$month}}</code> </td> -<td> 08</td> +<td> 03</td> </tr> <tr> <td><code>{{$short-month}}</code> </td> -<td> 8</td> +<td> 3</td> </tr> <tr> <td><code>{{$year}}</code> </td> -<td> 2020</td> +<td> 2021</td> </tr> <tr> <td><code>{{$short-year}}</code> </td> -<td> 20</td> +<td> 21</td> </tr> <tr> <td><code>{{$weekday}}</code> </td> -<td> Sunday</td> +<td> Tuesday</td> </tr> <tr> <td><code>{{$weekday-abbr}}</code> </td>

@@ -7682,11 +7683,11 @@ <td> 30</td>

</tr> <tr> <td><code>{{$month-name}}</code> </td> -<td> August</td> +<td> March</td> </tr> <tr> <td><code>{{$month-name-abbr}}</code> </td> -<td> Aug</td> +<td> Mar</td> </tr> <tr> <td><code>{{$timezone-offset}}</code> </td>

@@ -8827,7 +8828,7 @@

<p style="text-align:center;"> <span class="fa-creative-commons"></span> <span class="fa-creative-commons-by"></span> <span class="fa-creative-commons-nc"></span> <span class="fa-creative-commons-nd"></span></p> </div> <div id="footer"> - <p><span class="copy"></span> Fabio Cevasco &ndash; August 30, 2020</p> + <p><span class="copy"></span> Fabio Cevasco &ndash; March 30, 2021</p> <p><span>Powered by</span> <a href="https://h3rald.com/hastyscribe"><span class="hastyscribe"></span></a></p> </div> </div>
M hastyscribe.nimblehastyscribe.nimble

@@ -20,7 +20,7 @@ bin = @["hastyscribe"]

srcDir = "src" installExt = @["nim", "json", "a", "css", "png", "svg", "woff", "c", "h", "in"] -requires "nim >= 0.19.0" +requires "nim >= 1.4.0" before install: exec "nifty install"
M src/hastyscribe.nimsrc/hastyscribe.nim

@@ -49,7 +49,7 @@ css*: string

js*: string watermark*: string fragment*: bool - HastyFields* = Table[string, proc():string] + HastyFields* = Table[string, string] HastySnippets* = Table[string, string] HastyMacros* = Table[string, string] HastyScribe* = object

@@ -62,53 +62,32 @@

if logging.getHandlers().len == 0: newNiftyLogger().addHandler() -proc initFields(fields: HastyFields): HastyFields = - result = initTable[string, proc():string]() +proc initFields(fields: HastyFields): HastyFields {.gcsafe.} = + result = initTable[string, string]() for key, value in fields.pairs: result[key] = value var now = getTime().local() - result["timestamp"] = proc():string = - return $now.toTime.toUnix().int - result["date"] = proc():string = - return now.format("yyyy-MM-dd") - result["full-date"] = proc():string = - return now.format("dddd, MMMM d, yyyy") - result["long-date"] = proc():string = - return now.format("MMMM d, yyyy") - result["medium-date"] = proc():string = - return now.format("MMM d, yyyy") - result["short-date"] = proc():string = - return now.format("M/d/yy") - result["short-time-24"] = proc():string = - return now.format("HH:mm") - result["short-time"] = proc():string = - return now.format("HH:mm tt") - result["time-24"] = proc():string = - return now.format("HH:mm:ss") - result["time"] = proc():string = - return now.format("HH:mm:ss tt") - result["day"] = proc():string = - return now.format("dd") - result["month"] = proc():string = - return now.format("MM") - result["year"] = proc():string = - return now.format("yyyy") - result["short-day"] = proc():string = - return now.format("d") - result["short-month"] = proc():string = - return now.format("M") - result["short-year"] = proc():string = - return now.format("yy") - result["weekday"] = proc():string = - return now.format("dddd") - result["weekday-abbr"] = proc():string = - return now.format("dd") - result["month-name"] = proc():string = - return now.format("MMMM") - result["month-name-abbr"] = proc():string = - return now.format("MMM") - result["timezone-offset"] = proc():string = - return now.format("zzz") + result["timestamp"] = $now.toTime.toUnix().int + result["date"] = now.format("yyyy-MM-dd") + result["full-date"] = now.format("dddd, MMMM d, yyyy") + result["long-date"] = now.format("MMMM d, yyyy") + result["medium-date"] = now.format("MMM d, yyyy") + result["short-date"] = now.format("M/d/yy") + result["short-time-24"] = now.format("HH:mm") + result["short-time"] = now.format("HH:mm tt") + result["time-24"] = now.format("HH:mm:ss") + result["time"] = now.format("HH:mm:ss tt") + result["day"] = now.format("dd") + result["month"] = now.format("MM") + result["year"] = now.format("yyyy") + result["short-day"] = now.format("d") + result["short-month"] = now.format("M") + result["short-year"] = now.format("yy") + result["weekday"] = now.format("dddd") + result["weekday-abbr"] = now.format("dd") + result["month-name"] = now.format("MMMM") + result["month-name-abbr"] = now.format("MMM") + result["timezone-offset"] = now.format("zzz") proc newHastyScribe*(options: HastyOptions, fields: HastyFields): HastyScribe = return HastyScribe(options: options, fields: initFields(fields), snippets: initTable[string, string](), macros: initTable[string, string](), document: "")

@@ -152,10 +131,6 @@ imgcontent = encode_image_file(current_dir & imgfile, imgformat)

let imgrep = img.replace("\"" & img_file & "\"", "\"" & imgcontent & "\"") doc = doc.replace(img, imgrep) hs.document = doc - -proc add_jump_to_top_links(hs: var HastyScribe) = - hs.document = hs.document.replacef(peg"{'</h' [23456] '>'}", "<a href=\"#document-top\" title=\"Go to top\"></a>$1") - proc embed_fonts(): string= let fonts = @[

@@ -173,7 +148,7 @@ create_font_face(sourcesanspro_boldit_font, "Source Sans Pro", "italic", 700)

] return style_tag(fonts.join); -proc preprocess(hs: var HastyScribe, document, dir: string, offset = 0): string +proc preprocess*(hs: var HastyScribe, document, dir: string, offset = 0): string proc applyHeadingOffset(contents: string, offset: int): string = if offset == 0:

@@ -270,7 +245,7 @@ result = result.replace(instance, "")

# Field Usage: # {{$timestamp}} -proc parse_fields(hs: var HastyScribe, document: string): string = +proc parse_fields(hs: var HastyScribe, document: string): string {.gcsafe.} = let peg_field = peg""" field <- '{{' \s* '$' {id} \s* '}}' id <- [a-zA-Z0-9_-]+

@@ -281,7 +256,7 @@ var matches:array[0..0, string]

discard field.match(peg_field, matches) var id = matches[0].strip if hs.fields.hasKey(id): - result = result.replace(field, hs.fields[id]()) + result = result.replace(field, hs.fields[id]) else: warn "Field '" & id & "' not defined." result = result.replace(field, "")

@@ -346,7 +321,7 @@ discard anchor.match(peg_anchor, matches)

var id = matches[0] result = result.replace(anchor, " <a id=\""&id&"\"></a>") -proc preprocess(hs: var HastyScribe, document, dir: string, offset = 0): string = +proc preprocess*(hs: var HastyScribe, document, dir: string, offset = 0): string = result = hs.parse_transclusions(document, dir, offset) result = hs.parse_fields(result) result = hs.parse_snippets(result)

@@ -429,8 +404,10 @@ # Date parsing and validation

var timeinfo: DateTime = local(getTime()) - if parse_date(metadata.date, timeinfo) == false: - discard parse_date(getDateStr(), timeinfo) + try: + timeinfo = parse(metadata.date, "yyyy-MM-dd") + except: + timeinfo = parse(getDateStr(), "yyyy-MM-dd") hs.document = """<!doctype html> <html lang="en">

@@ -463,7 +440,7 @@ $js

</body>""" % ["title_tag", title_tag, "header_tag", header_tag, "author", metadata.author, "author_footer", author_footer, "date", timeinfo.format("MMMM d, yyyy"), "toc", toc, "main_css_tag", main_css_tag, "user_css_tag", user_css_tag, "headings", headings, "body", hs.document, "fonts_css_tag", embed_fonts(), "internal_css_tag", metadata.css, "watermark_css_tag", watermark_css_tag, "js", user_js_tag] hs.embed_images(dir) - hs.add_jump_to_top_links() + hs.document = add_jump_to_top_links(hs.document) return hs.document proc compile*(hs: var HastyScribe, input_file: string) =

@@ -490,7 +467,7 @@

when isMainModule: let usage = " HastyScribe v" & pkgVersion & " - Self-contained Markdown Compiler" & """ - (c) 2013-2018 Fabio Cevasco + (c) 2013-2020 Fabio Cevasco Usage: hastyscribe <markdown_file_or_glob> [options]

@@ -507,13 +484,14 @@ (Use "--output-file=-" to output to stdout)

--watermark=<file> Use the image in <file> as a watermark. --fragment If specified, an HTML fragment will be generated, without embedding images, fonts, or stylesheets. - --dump=all|styles|fonts Dumps all resources/stylesheets/fonts to the current directory.""" + --dump=all|styles|fonts Dumps all resources/stylesheets/fonts to the current directory. + --help Display the usage information.""" var input = "" var files = newSeq[string](0) var options = HastyOptions(toc: true, output: "", css: "", watermark: "", fragment: false) - var fields = initTable[string, proc():string]() + var fields = initTable[string, string]() var dumpdata = "" # Parse Parameters

@@ -541,27 +519,16 @@ of "output-file":

options.output = val of "fragment": options.fragment = true + of "help": + echo usage + quit(1) else: if key.startsWith("field/"): let val = val - fields[key.replace("field/", "")] = proc(): string = - return val + fields[key.replace("field/", "")] = val discard else: discard - - if dumpdata != "": - if input == "": - quit(usage, 1) - elif options.css == "": - quit(usage, 4) - elif options.output == "": - quit(usage, 5) - elif options.watermark == "": - quit(usage, 6) - - if input == "": - quit(usage, 0) for file in walkFiles(input): files.add(file)
M src/hastyscribepkg/config.nimsrc/hastyscribepkg/config.nim

@@ -1,4 +1,4 @@

const - pkgVersion* = "1.12.1" + pkgVersion* = "1.12.4" pkgAuthor* = "Fabio Cevasco" pkgDescription* = "Self-contained markdown compiler generating self-contained HTML documents"
M src/hastyscribepkg/markdown.nimsrc/hastyscribepkg/markdown.nim

@@ -112,7 +112,9 @@ MKD_EMBED* = MKD_NOLINKS or MKD_NOIMAGE or MKD_TAGTEXT

## High Level API -import strutils +import + strutils, + pegs const DefaultFlags = MKD_TOC or MKD_1_COMPAT or MKD_EXTRA_FOOTNOTE or MKD_DLEXTRA or MKD_FENCEDCODE or MKD_GITHUBTAGS or MKD_HTML5ANCHOR or MKD_LATEX

@@ -145,19 +147,23 @@ if (f == 0):

flags = DefaultFlags else: flags = uint32(f) - # Check if metadata is present - var lns = s.splitLines + # Check if Pandoc style metadata is present var valid_metadata = false - var offset = 0 - if (lns[0][0] == '%') and (lns[1][0] == '%') and (lns[2][0] == '%'): - valid_metadata = true - else: - valid_metadata = false - if lns[0][0] == '%': - offset = 2 - if lns[1][0] == '%': - offset = 3 - var str = cstring(lns[offset..lns.len-1].join("\n")) + var contents = s + let peg_pandoc = peg""" + definition <- ^{line} {line}? {line}? + line <- '\%' @ \n + """ + var matches: array[0..2, string] + let (s, e) = contents.findBounds(peg_pandoc, matches) + # the pattern must start at the beginning of the file + if s == 0: + if matches[0] != "" and matches[1] != "" and matches[2] != "": + valid_metadata = true + else: + # incomplete metadata, remove the whole pandoc section to not confuse discount + contents = contents[e-1 .. ^1] + var str = cstring(contents) var mmiot = mkd_string(str, cint(str.len-1), flags) if valid_metadata: data.title = $mkd_doc_title(mmiot)
M src/hastyscribepkg/utils.nimsrc/hastyscribepkg/utils.nim

@@ -2,39 +2,23 @@ import

base64, os, strutils, - sequtils, - pegs, - times + pegs import consts -proc parse_date*(date: string, timeinfo: var DateTime): bool = - var parts = date.split('-').map(proc(i:string): int = - try: - i.parseInt - except: - 0 - ) - if parts.len < 3: - return false - try: - timeinfo = DateTime(year: parts[0], month: Month(parts[1]), monthday: parts[2]) - # Fix invalid dates (e.g. Feb 31st -> Mar 3rd) - timeinfo = local(timeinfo.toTime); - return true - except: - return false - proc style_tag*(css: string): string = result = "<style>$1</style>" % [css] + +proc style_link_tag*(css: string): string = + result = "<link rel=\"stylesheet\" href=\"$1\"/>" % [css] proc encode_image*(contents, format: string): string = let enc_contents = contents.encode return "data:image/$format;base64,$enc_contents" % ["format", format, "enc_contents", enc_contents] proc encode_image_file*(file, format: string): string = - if (file.existsFile): + if (file.fileExists): let contents = file.readFile return encode_image(contents, format) else:

@@ -51,12 +35,20 @@ result = ""

else: let img = imgfile.encode_image_file(imgfile.image_format) result = (watermark_style % [img]).style_tag + +proc add_jump_to_top_links*(document: string): string = + result = document.replacef(peg"{'</h' [23456] '>'}", "<a href=\"#document-top\" title=\"Go to top\"></a>$1") proc encode_font*(font, format: string): string = let enc_contents = font.encode return "data:application/$format;charset=utf-8;base64,$enc_contents" % ["format", format, "enc_contents", enc_contents] -proc create_font_face*(font, family, style: string, weight: int): string= +proc create_font_face*(font, family, style: string, weight: int, embed=true): string= + var font_src = "" + if embed: + font_src = encode_font(font, "x-font-woff") + else: + font_src = font return """ @font-face { font-family:"$family";

@@ -66,6 +58,6 @@ font-weight:$weight;

-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } - """ % ["family", family, "font", encode_font(font, "x-font-woff"), "style", style, "weight", $weight] + """ % ["family", family, "font", font_src, "style", style, "weight", $weight]