From 24d8c4a2252b651a0b0d0ec28598112faa136478 Mon Sep 17 00:00:00 2001 From: Jaidyn Ann <10477760+JadedCtrl@users.noreply.github.com> Date: Thu, 29 Jun 2023 21:38:20 -0500 Subject: [PATCH] =?UTF-8?q?=E2=80=9CFinish=E2=80=9D=20the=20Figlet=20parse?= =?UTF-8?q?r-&-renderer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: There is no text-smushing. Without that, any line takes up too much space! --- figlet.lisp | 97 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 22 deletions(-) diff --git a/figlet.lisp b/figlet.lisp index 892bfde..c9bd98b 100644 --- a/figlet.lisp +++ b/figlet.lisp @@ -19,6 +19,20 @@ (in-package :figlet) + +;;; ——————————————————————————————————— +;;; Misc. utilities +;;; ——————————————————————————————————— +(defun contains-char-p (character string) + "Whether or not a STRING contains the given CHARACTER." + (str:containsp (string character) string)) + + +(defun characters (string) + "Return a list of a STRING’s characters." + (loop for char across string + collect char)) + (defun string->integer (string) "Convert a string to a number, potentially in 0x… hexadecimal form. @@ -30,38 +44,77 @@ If no number is parsed out, return NIL." (ignore-errors (parse-integer string :radix radix)))) -(defun parse-lines (lines &optional (font-plist '()) (current-symbol 32)) +(defun unlines (strings) + "Wrapper around STR:UNLINES that removes all non-strings from the STRINGS list." + (str:unlines (remove-if-not #'stringp strings))) + + + +;;; ——————————————————————————————————— +;;; Font-parsing +;;; ——————————————————————————————————— +(defun parse-lines (lines &optional (font-plist '()) (current-charcode 32)) "Parse a list of lines from a Figlet font-file (.FLF) into a plist associating a character with its respective string in the font-file. (#\A \"TEXT-ART-A\" #\B \"TEXT-ART-B\" …)" (if lines (let* ((line (car lines)) - (sans-@ (cl:string-trim "@" line)) - (last-of-symbol-p (str:ends-with-p "@@" line)) - (not-art-line (not (str:ends-with-p "@" line))) - (first-word (car (str:split " " line))) - (first-word-num (string->integer first-word)) - (new-symbol-header-p (and not-art-line first-word-num))) - (cond (new-symbol-header-p - (parse-lines (cdr lines) font-plist first-word-num)) - ((not not-art-line) - (setf (getf font-plist (code-char current-symbol)) - (format nil "~A~A~%" - (or (getf font-plist (code-char current-symbol)) - "") - sans-@)) - (parse-lines (cdr lines) font-plist (if last-of-symbol-p - (+ current-symbol 1) - current-symbol))) - ('t - (parse-lines (cdr lines) font-plist current-symbol)))) + (sans-@ (string-trim "@" line)) ;; Lines are terminated by ‘@’ + (last-of-symbol-p (str:ends-with-p "@@" line)) ;; Character-art is terminated by ‘@@’ + (not-art-line-p (not (str:ends-with-p "@" line))) ;; If no @ at all, line’s a comment or header + (first-word-num (string->integer (car (str:words line)))) ;; If header line, this’ll be a character-code + (current-art (ignore-errors (getf font-plist (code-char current-charcode))))) + (cond + ;; This is a header for a new char-art of specific char-code. + ((and not-art-line-p first-word-num) + (parse-lines (cdr lines) font-plist first-word-num)) + ;; If a line of char-art, amass it! + ((not not-art-line-p) + (setf (getf font-plist (code-char current-charcode)) + (unlines (list current-art + (str:replace-all (getf font-plist :space-char) " " sans-@)))) + (parse-lines (cdr lines) font-plist (if last-of-symbol-p + (+ current-charcode 1) + current-charcode))) + ;; This is the first line of the file, the header line. + ((str:starts-with-p "flf2a" line) + (setf (getf font-plist :space-char) + (subseq line 5 6)) ;; A char (often $) to substitute spaces. + (parse-lines (cdr lines) font-plist current-charcode)) + ;; If none of the above, it’s a comment! + ('t + (setf (getf font-plist :comments) + (unlines (list (getf font-plist :comments) line))) + (parse-lines (cdr lines) font-plist current-charcode)))) font-plist)) -(defun figlet-font-plist (path) +(defun figlet-font-plist (font-path) "Parse a Figlet font-file (.FLF) into a plist associating a character with its respective string in the font-file. (#\A \"TEXT-ART-A\" #\B \"TEXT-ART-B\" …)" (parse-lines (str:lines - (alexandria:read-file-into-string path)))) + (alexandria:read-file-into-string font-path)))) + + + +;;; ——————————————————————————————————— +;;; Output of Figlet-style strings +;;; ——————————————————————————————————— +(defun figlet-string (string &key (font-path nil) (font-plist (figlet-font-plist font-path))) + (if (contains-char-p #\newline string) + (mapcar (lambda (line) (figlet-string line :font-path font-path :font-plist font-plist)) + (str:lines string)) + (let* ((char-lines + (mapcar (lambda (char) + (str:lines (getf font-plist char))) + (characters string))) + (lines’-parts + (loop for i to (- (length (car char-lines)) 1) + collect (mapcar (lambda (lines) + (nth i lines)) + char-lines)))) + (str:unlines (mapcar (lambda (line-parts) + (reduce #'str:concat line-parts)) + lines’-parts)))))