glyphs.clj 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. (ns fira-code.glyphs
  2. (:refer-clojure :exclude [load])
  3. (:require
  4. [clojure.java.io :as io]
  5. [clojure.string :as str]
  6. [fipp.edn :as fipp]
  7. [fira-code.coll :as coll]
  8. [flatland.ordered.map :refer [ordered-map]]))
  9. (def ^:dynamic *str)
  10. (def ^:dynamic *pos)
  11. (defn current-char [] (nth @*str @*pos))
  12. (defn advance! [] (swap! *pos inc))
  13. (declare parse-anything!)
  14. (defn skip-ws! []
  15. (loop []
  16. (case (current-char)
  17. \space (do (advance!) (recur))
  18. \newline (do (advance!) (recur))
  19. nil)))
  20. (defn parse-escaped-string! []
  21. (skip-ws!)
  22. (when (= \" (current-char))
  23. (let [sb (StringBuilder.)]
  24. (->
  25. (loop []
  26. (advance!)
  27. (let [ch (current-char)]
  28. (cond
  29. (= ch \\) (do (.append sb \\) (advance!) (.append sb (current-char)) (recur))
  30. (= ch \") (do (advance!) (str sb))
  31. :else (do (.append sb ch) (recur)))))
  32. (str/replace "\\012" "\n")
  33. (str/replace "\\\"" "\"")
  34. (str/replace "\\\\" "\\")))))
  35. (defn parse-string! []
  36. (skip-ws!)
  37. (let [sb (StringBuilder.)]
  38. (loop []
  39. (let [ch (current-char)]
  40. (cond
  41. (#{\space \newline \{ \} \( \) \; \, \" \=} ch) sb
  42. :else (do (.append sb ch) (advance!) (recur)))))
  43. (let [res (str sb)]
  44. (cond
  45. (re-matches #"-?[1-9][0-9]*" res) (Integer/parseInt res)
  46. (re-matches #"-?[0-9]+\.[0-9]+" res) (Double/parseDouble res)
  47. (re-matches #"[a-zA-Z][a-zA-Z\.0-9]*" res) (keyword res)
  48. :else res))))
  49. (defn expect [c]
  50. (assert (= c (current-char))
  51. (str "Expected '" c
  52. "', found " (current-char)
  53. " at " @*pos
  54. " around here:\n" (subs @*str (max 0 (- @*pos 100)) (min (count @*str) (+ @*pos 100))))))
  55. (defn parse-map! []
  56. (skip-ws!)
  57. (when (= \{ (current-char))
  58. (advance!)
  59. (loop [m (ordered-map)]
  60. (skip-ws!)
  61. (if (= \} (current-char))
  62. (do (advance!) m)
  63. (let [k (or (parse-escaped-string!) (parse-string!))
  64. _ (do (skip-ws!) (expect \=) (advance!))
  65. v (parse-anything!)
  66. v (if (keyword? v) (name v) v)
  67. _ (do (skip-ws!) (expect \;) (advance!))]
  68. (recur (assoc m k v)))))))
  69. (defn parse-list! []
  70. (skip-ws!)
  71. (when (= \( (current-char))
  72. (advance!)
  73. (loop [l []]
  74. (skip-ws!)
  75. (if (= \) (current-char))
  76. (do (advance!) l)
  77. (let [v (parse-anything!)
  78. _ (skip-ws!)
  79. _ (when (not= \) (current-char))
  80. (expect \,)
  81. (advance!))]
  82. (recur (conj l v)))))))
  83. (defn parse-anything! []
  84. (skip-ws!)
  85. (or
  86. (parse-map!)
  87. (parse-list!)
  88. (parse-escaped-string!)
  89. (parse-string!)))
  90. (defn parse [s]
  91. (binding [*str (atom s)
  92. *pos (atom 0)]
  93. (parse-anything!)))
  94. (def escapes {"\n" "\\012"
  95. "\"" "\\\""
  96. "\\" "\\\\"})
  97. (def escape-re #"[\n\"\\]")
  98. (defn- serialize-impl [form]
  99. (cond
  100. (string? form) (if (re-matches #"[a-zA-Z0-9._/]+" form)
  101. form
  102. (str \" (str/replace form escape-re escapes) \"))
  103. (keyword? form) (name form)
  104. (number? form) (str form)
  105. (instance? clojure.lang.MapEntry form)
  106. (str
  107. (serialize-impl (key form))
  108. " = "
  109. (if (= ".appVersion" (key form)) ;; https://github.com/googlefonts/glyphsLib/issues/209
  110. (str \" (val form) \")
  111. (serialize-impl (val form)))
  112. ";")
  113. (sequential? form) (if (empty? form)
  114. "(\n)"
  115. (str "(\n" (str/join ",\n" (map serialize-impl form)) "\n)"))
  116. (map? form) (if (empty? form)
  117. "{\n}"
  118. (str "{\n" (str/join "\n" (map serialize-impl form)) "\n}"))))
  119. (defn serialize [font]
  120. (str (serialize-impl font) "\n"))
  121. ; (-> (slurp "FiraCode.glyphs") parse serialize (->> (spit "FiraCode_saved.glyphs")))
  122. (defn load [path]
  123. (println (str "Parsing '" path "'..."))
  124. (parse (slurp path)))
  125. (defn save! [path font]
  126. (println (str "Saving '" path "'..."))
  127. (spit path (serialize font)))
  128. (defn -main [& args]
  129. (let [font (-> (slurp "FiraCode.glyphs") parse)]
  130. (with-open [os (io/writer "clojure/FiraCode.edn")]
  131. (binding [*out* os]
  132. (fipp/pprint font {:width 200})))))
  133. (defn update-code [font key name f & args]
  134. (let [idx (coll/index-of #(= (:name %) name) (get font key))]
  135. (assert (>= idx 0) (str "Can’t find " key "[name=\"" name "\"], got " (str/join ", " (map :name (get font key)))))
  136. (apply update-in font [key idx :code] f args)))
  137. (defn lines [s]
  138. (inc (count (re-seq #"\n" s))))
  139. (defn words [s]
  140. (count (re-seq #"[^\s]+" s)))
  141. (defn set-feature [font name feature]
  142. (let [idx (coll/index-of #(= (:name %) name) (:features font))]
  143. (if (pos? idx)
  144. (do
  145. (println " replacing feature" name "with" (lines (:code feature)) "lines")
  146. (assoc-in font [:features idx] feature))
  147. (do
  148. (println " appending to feature" name (lines (:code feature)) "lines")
  149. (update font :features conj feature)))))
  150. (defn set-class [font name class]
  151. (let [idx (coll/index-of #(= (:name %) name) (:classes font))]
  152. (if (pos? idx)
  153. (do
  154. (println " replacing class" name "with" (words (:code class)) "entries")
  155. (assoc-in font [:classes idx] class))
  156. (do
  157. (println " appending to class" name (words (:code class)) "entries")
  158. (update font :classes conj class)))))
  159. (def weights
  160. {:Light "B67F0F2D-EC95-4CB8-966E-23AE86958A69"
  161. :Regular "UUID0"
  162. :Bold "4B7A3BAF-EAD8-4024-9BEA-BB1DE86CFCFA"})
  163. (defn layer [l]
  164. { :id (condp = (:layerId l)
  165. (:Light weights) "Light"
  166. (:Regular weights) "Regular"
  167. (:Bold weights) "Bold"
  168. (:layerId l))
  169. :width (:width l) })
  170. (defn save-not600 []
  171. (let [font (-> (slurp "FiraCode.glyphs") parse)]
  172. (with-open [os (io/writer "clojure/FiraCode_not600.edn")]
  173. (binding [*out* os]
  174. (let [glyphs (for [glyph (:glyphs font)
  175. :when (->> (:layers glyph)
  176. (filter #(contains? (set (vals weights)) (:layerId %)))
  177. (every? #(= 600 (:width %)))
  178. (not))]
  179. {:glyphname (:glyphname glyph)
  180. :layers (mapv layer (:layers glyph))})]
  181. (doseq [glyph glyphs]
  182. (fipp/pprint glyph {:width 200}))
  183. (count glyphs))))))
  184. ;; (-main)
  185. ;; (save-not600)
  186. ;; (-> (slurp "FiraCode.glyphs") parse keys)
  187. ;;