You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

582 lines
22 KiB

4 years ago
  1. import os, sys
  2. import lesscpy
  3. from shutil import copyfile, rmtree
  4. from jupyter_core.paths import jupyter_config_dir, jupyter_data_dir
  5. from glob import glob
  6. from tempfile import mkstemp
  7. # path to local site-packages/jupyterthemes
  8. package_dir = os.path.dirname(os.path.realpath(__file__))
  9. # path to user jupyter-themes dir
  10. user_dir = os.path.join(os.path.expanduser('~'), '.jupyter-themes')
  11. # path to save tempfile with style_less before reading/compiling
  12. _, tempfile = mkstemp('.less')
  13. _, vimtemp = mkstemp('.less')
  14. # path to install custom.css file (~/.jupyter/custom/)
  15. jupyter_home = jupyter_config_dir()
  16. jupyter_data = jupyter_data_dir()
  17. jupyter_custom = os.path.join(jupyter_home, 'custom')
  18. jupyter_custom_fonts = os.path.join(jupyter_custom, 'fonts')
  19. jupyter_customcss = os.path.join(jupyter_custom, 'custom.css')
  20. jupyter_customjs = os.path.join(jupyter_custom, 'custom.js')
  21. jupyter_nbext = os.path.join(jupyter_data, 'nbextensions')
  22. # theme colors, layout, and font directories
  23. layouts_dir = os.path.join(package_dir, 'layout')
  24. styles_dir = os.path.join(package_dir, 'styles')
  25. styles_dir_user = os.path.join(user_dir, 'styles')
  26. fonts_dir = os.path.join(package_dir, 'fonts')
  27. defaults_dir = os.path.join(package_dir, 'defaults')
  28. # default custom.css/js files to override JT on reset
  29. defaultCSS = os.path.join(defaults_dir, 'custom.css')
  30. defaultJS = os.path.join(defaults_dir, 'custom.js')
  31. # layout files for notebook, codemirror, cells, mathjax, & vim ext
  32. nb_style = os.path.join(layouts_dir, 'notebook.less')
  33. cm_style = os.path.join(layouts_dir, 'codemirror.less')
  34. cl_style = os.path.join(layouts_dir, 'cells.less')
  35. ex_style = os.path.join(layouts_dir, 'extras.less')
  36. vim_style = os.path.join(layouts_dir, 'vim.less')
  37. comp_style = os.path.join(layouts_dir, 'completer.less')
  38. theme_name_file = os.path.join(jupyter_custom, 'current_theme.txt')
  39. def fileOpen(filename, mode):
  40. if sys.version_info[0]==3:
  41. return open(filename, mode, encoding='utf8', errors='ignore')
  42. else:
  43. return open(filename, mode)
  44. def check_directories():
  45. # Ensure all install dirs exist
  46. if not os.path.isdir(jupyter_home):
  47. os.makedirs(jupyter_home)
  48. if not os.path.isdir(jupyter_custom):
  49. os.makedirs(jupyter_custom)
  50. if not os.path.isdir(jupyter_custom_fonts):
  51. os.makedirs(jupyter_custom_fonts)
  52. if not os.path.isdir(jupyter_data):
  53. os.makedirs(jupyter_data)
  54. if not os.path.isdir(jupyter_nbext):
  55. os.makedirs(jupyter_nbext)
  56. def less_to_css(style_less):
  57. """ write less-compiled css file to jupyter_customcss in jupyter_dir
  58. """
  59. with fileOpen(tempfile, 'w') as f:
  60. f.write(style_less)
  61. os.chdir(package_dir)
  62. style_css = lesscpy.compile(tempfile)
  63. style_css += '\n\n'
  64. return style_css
  65. def write_final_css(style_css):
  66. # install style_css to .jupyter/custom/custom.css
  67. with fileOpen(jupyter_customcss, 'w') as custom_css:
  68. custom_css.write(style_css)
  69. def install_precompiled_theme(theme):
  70. # for Python 3.5, install selected theme from precompiled defaults
  71. compiled_dir = os.path.join(styles_dir, 'compiled')
  72. compiled_dir_user = os.path.join(styles_dir_user, 'compiled')
  73. if (os.path.isdir(compiled_dir_user) and
  74. '{}.css'.format(theme) in os.listdir(compiled_dir_user)):
  75. theme_src = os.path.join(compiled_dir_user, '{}.css'.format(theme))
  76. else:
  77. theme_src = os.path.join(compiled_dir, '{}.css'.format(theme))
  78. theme_dst = os.path.join(jupyter_custom, 'custom.css')
  79. copyfile(theme_src, theme_dst)
  80. def send_fonts_to_jupyter(font_file_path):
  81. fname = font_file_path.split(os.sep)[-1]
  82. copyfile(font_file_path, os.path.join(jupyter_custom_fonts, fname))
  83. def delete_font_files():
  84. for fontfile in os.listdir(jupyter_custom_fonts):
  85. abspath = os.path.join(jupyter_custom_fonts, fontfile)
  86. os.remove(abspath)
  87. def convert_fontsizes(fontsizes):
  88. # if triple digits, move decimal (105 --> 10.5)
  89. fontsizes = [str(fs) for fs in fontsizes]
  90. for i, fs in enumerate(fontsizes):
  91. if len(fs) >= 3:
  92. fontsizes[i] = '.'.join([fs[:-1], fs[-1]])
  93. elif int(fs) > 25:
  94. fontsizes[i] = '.'.join([fs[0], fs[-1]])
  95. return fontsizes
  96. def set_font_properties(style_less,
  97. nbfont=None,
  98. tcfont=None,
  99. monofont=None,
  100. monosize=11,
  101. tcfontsize=13,
  102. nbfontsize=13,
  103. prfontsize=95,
  104. dffontsize=93,
  105. outfontsize=85,
  106. mathfontsize=100,
  107. dfonts=False):
  108. """Parent function for setting notebook, text/md, and
  109. codecell font-properties
  110. """
  111. fontsizes = [monosize, nbfontsize, tcfontsize, prfontsize, dffontsize, outfontsize]
  112. monosize, nbfontsize, tcfontsize, prfontsize, dffontsize, outfontsize = convert_fontsizes(fontsizes)
  113. if dfonts==True:
  114. monofont, tcfont, nbfont = ['monospace', 'sans-serif', 'sans-serif']
  115. else:
  116. if monofont is not None:
  117. monofont, monofpath = stored_font_dicts(monofont)
  118. style_less = import_fonts(style_less, monofont, monofpath)
  119. else:
  120. monofont='monospace'
  121. if tcfont is not None:
  122. tcfont, tcfontpath = stored_font_dicts(tcfont)
  123. style_less = import_fonts(style_less, tcfont, tcfontpath)
  124. else:
  125. tcfont='sans-serif'
  126. if nbfont is not None:
  127. if nbfont == 'proxima':
  128. nbfont, tcfont = ["'Proxima Nova'"]*2
  129. style_less = proxima_nova_imports(style_less)
  130. else:
  131. nbfont, nbfontpath = stored_font_dicts(nbfont)
  132. style_less = import_fonts(style_less, nbfont, nbfontpath)
  133. else:
  134. nbfont='sans-serif'
  135. style_less += '/* Set Font-Type and Font-Size Variables */\n'
  136. # font names and fontfamily info for codecells, notebook & textcells
  137. style_less += '@monofont: {}; \n'.format(monofont)
  138. style_less += '@notebook-fontfamily: {}; \n'.format(nbfont)
  139. style_less += '@text-cell-fontfamily: {}; \n'.format(tcfont)
  140. # font size for codecells, main notebook, notebook-sub, & textcells
  141. style_less += '@monofontsize: {}pt; \n'.format(monosize)
  142. style_less += '@monofontsize-sub: {}pt; \n'.format(float(monosize) - 1)
  143. style_less += '@nb-fontsize: {}pt; \n'.format(nbfontsize)
  144. style_less += '@nb-fontsize-sub: {}pt; \n'.format(float(nbfontsize) - 1)
  145. style_less += '@text-cell-fontsize: {}pt; \n'.format(tcfontsize)
  146. style_less += '@df-header-fontsize: {}pt; \n'.format(float(dffontsize) + 1)
  147. style_less += '@df-fontsize: {}pt; \n'.format(dffontsize)
  148. style_less += '@output-font-size: {}pt; \n'.format(outfontsize)
  149. style_less += '@prompt-fontsize: {}pt; \n'.format(prfontsize)
  150. style_less += '@mathfontsize: {}%; \n'.format(mathfontsize)
  151. style_less += '\n\n'
  152. style_less += '/* Import Theme Colors and Define Layout Variables */\n'
  153. return style_less
  154. def import_fonts(style_less, fontname, font_subdir):
  155. """Copy all custom fonts to ~/.jupyter/custom/fonts/ and
  156. write import statements to style_less
  157. """
  158. ftype_dict = {'woff2': 'woff2',
  159. 'woff': 'woff',
  160. 'ttf': 'truetype',
  161. 'otf': 'opentype',
  162. 'svg': 'svg'}
  163. define_font = (
  164. "@font-face {{font-family: {fontname};\n\tfont-weight:"
  165. "{weight};\n\tfont-style: {style};\n\tsrc: local('{fontname}'),"
  166. "\n\turl('fonts{sepp}{fontfile}') format('{ftype}');}}\n")
  167. fontname = fontname.split(',')[0]
  168. fontpath = os.path.join(fonts_dir, font_subdir)
  169. for fontfile in os.listdir(fontpath):
  170. if '.txt' in fontfile or 'DS_' in fontfile:
  171. continue
  172. weight = 'normal'
  173. style = 'normal'
  174. if 'medium' in fontfile:
  175. weight = 'medium'
  176. elif 'ital' in fontfile:
  177. style = 'italic'
  178. ft = ftype_dict[fontfile.split('.')[-1]]
  179. style_less += define_font.format(
  180. fontname=fontname,
  181. weight=weight,
  182. style=style,
  183. sepp='/',
  184. fontfile=fontfile,
  185. ftype=ft)
  186. send_fonts_to_jupyter(os.path.join(fontpath, fontfile))
  187. return style_less
  188. def style_layout(style_less,
  189. theme='grade3',
  190. cursorwidth=2,
  191. cursorcolor='default',
  192. cellwidth='980',
  193. lineheight=170,
  194. margins='auto',
  195. vimext=False,
  196. toolbar=False,
  197. nbname=False,
  198. kernellogo=False,
  199. altprompt=False,
  200. altmd=False,
  201. altout=False,
  202. hideprompt=False):
  203. """Set general layout and style properties of text and code cells"""
  204. # write theme name to ~/.jupyter/custom/ (referenced by jtplot.py)
  205. with fileOpen(theme_name_file, 'w') as f:
  206. f.write(theme)
  207. if (os.path.isdir(styles_dir_user) and
  208. '{}.less'.format(theme) in os.listdir(styles_dir_user)):
  209. theme_relpath = os.path.relpath(
  210. os.path.join(styles_dir_user, theme), package_dir)
  211. else:
  212. theme_relpath = os.path.relpath(
  213. os.path.join(styles_dir, theme), package_dir)
  214. style_less += '@import "{}";\n'.format(theme_relpath)
  215. textcell_bg = '@cc-input-bg'
  216. promptText = '@input-prompt'
  217. promptBG = '@cc-input-bg'
  218. promptPadding = '.25em'
  219. promptBorder = '2px solid @prompt-line'
  220. tcPromptBorder = '2px solid @tc-prompt-std'
  221. promptMinWidth = 11.5
  222. outpromptMinWidth = promptMinWidth # remove + 3 since it will overlay output print() text
  223. tcPromptWidth = promptMinWidth + 3
  224. tcPromptFontsize = "@prompt-fontsize"
  225. ccOutputBG = '@cc-output-bg-default'
  226. if theme == 'grade3':
  227. textcell_bg = '@notebook-bg'
  228. if altprompt:
  229. promptPadding = '.1em'
  230. promptMinWidth = 8
  231. outpromptMinWidth = promptMinWidth + 3
  232. tcPromptWidth = promptMinWidth + 3
  233. promptText = 'transparent'
  234. tcPromptBorder = '2px solid transparent'
  235. if altmd:
  236. textcell_bg = '@notebook-bg'
  237. tcPromptBorder = '2px dotted @tc-border-selected'
  238. if altout:
  239. ccOutputBG = '@notebook-bg'
  240. if margins != 'auto':
  241. margins = '{}px'.format(margins)
  242. if '%' not in cellwidth:
  243. cellwidth = str(cellwidth) + 'px'
  244. style_less += '@container-margins: {};\n'.format(margins)
  245. style_less += '@cell-width: {}; \n'.format(cellwidth)
  246. style_less += '@cc-line-height: {}%; \n'.format(lineheight)
  247. style_less += '@text-cell-bg: {}; \n'.format(textcell_bg)
  248. style_less += '@cc-prompt-width: {}ex; \n'.format(promptMinWidth)
  249. style_less += '@cc-prompt-bg: {}; \n'.format(promptBG)
  250. style_less += '@cc-output-bg: {}; \n'.format(ccOutputBG)
  251. style_less += '@prompt-text: {}; \n'.format(promptText)
  252. style_less += '@prompt-padding: {}; \n'.format(promptPadding)
  253. style_less += '@prompt-border: {}; \n'.format(promptBorder)
  254. style_less += '@prompt-min-width: {}ex; \n'.format(promptMinWidth)
  255. style_less += '@out-prompt-min-width: {}ex; \n'.format(outpromptMinWidth)
  256. style_less += '@tc-prompt-width: {}ex; \n'.format(tcPromptWidth)
  257. style_less += '@tc-prompt-border: {}; \n'.format(tcPromptBorder)
  258. style_less += '@cursor-width: {}px; \n'.format(cursorwidth)
  259. style_less += '@cursor-info: @cursor-width solid {}; \n'.format(
  260. cursorcolor)
  261. style_less += '@tc-prompt-fontsize: {}; \n'.format(tcPromptFontsize)
  262. style_less += '\n\n'
  263. # read-in notebook.less (general nb style)
  264. with fileOpen(nb_style, 'r') as notebook:
  265. style_less += notebook.read() + '\n'
  266. # read-in cells.less (cell layout)
  267. with fileOpen(cl_style, 'r') as cells:
  268. style_less += cells.read() + '\n'
  269. # read-in extras.less (misc layout)
  270. with fileOpen(ex_style, 'r') as extras:
  271. style_less += extras.read() + '\n'
  272. # read-in codemirror.less (syntax-highlighting)
  273. with fileOpen(cm_style, 'r') as codemirror:
  274. style_less += codemirror.read() + '\n'
  275. with fileOpen(comp_style, 'r') as codemirror:
  276. style_less += codemirror.read() + '\n'
  277. style_less += toggle_settings(
  278. toolbar, nbname, hideprompt, kernellogo) + '\n'
  279. if vimext:
  280. set_vim_style(theme)
  281. return style_less
  282. def toggle_settings(
  283. toolbar=False, nbname=False, hideprompt=False, kernellogo=False):
  284. """Toggle main notebook toolbar (e.g., buttons), filename,
  285. and kernel logo."""
  286. toggle = ''
  287. if toolbar:
  288. toggle += 'div#maintoolbar {margin-left: 8px !important;}\n'
  289. toggle += '.toolbar.container {width: 100% !important;}\n'
  290. else:
  291. toggle += 'div#maintoolbar {display: none !important;}\n'
  292. if nbname:
  293. toggle += ('span.save_widget span.filename {margin-left: 8px; height: initial;'
  294. 'font-size: 100%; color: @nb-name-fg; background-color:'
  295. '@cc-input-bg;}\n')
  296. toggle += ('span.save_widget span.filename:hover {color:'
  297. '@nb-name-hover; background-color: @cc-input-bg;}\n')
  298. toggle += ('#menubar {padding-top: 4px; background-color:'
  299. '@notebook-bg;}\n')
  300. else:
  301. toggle += '#header-container {display: none !important;}\n'
  302. if hideprompt:
  303. toggle += 'div.prompt.input_prompt {display: none !important;}\n'
  304. toggle += 'div.prompt.output_prompt {width: 5ex !important;}\n'
  305. toggle += 'div.out_prompt_overlay.prompt:hover {width: 5ex !important; min-width: 5ex !important;}\n'
  306. toggle += (
  307. '.CodeMirror-gutters, .cm-s-ipython .CodeMirror-gutters'
  308. '{ position: absolute; left: 0; top: 0; z-index: 3; width: 2em; '
  309. 'display: inline-block !important; }\n')
  310. toggle += ('div.cell.code_cell .input { border-left: 5px solid @cm-gutters !important; border-bottom-left-radius: 5px; border-top-left-radius: 5px; }\n')
  311. if kernellogo:
  312. toggle += '@kernel-logo-display: block;'
  313. else:
  314. toggle += '@kernel-logo-display: none;'
  315. return toggle
  316. def proxima_nova_imports(style_less):
  317. style_less += """@font-face {
  318. font-family: 'Proxima Nova Bold';
  319. src: url('fonts/Proxima Nova Alt Bold-webfont.eot');
  320. src: url('fonts/Proxima Nova Alt Bold-webfont.eot?#iefix') format('embedded-opentype'),
  321. url('fonts/Proxima Nova Alt Bold-webfont.woff2') format('woff2'),
  322. url('fonts/Proxima Nova Alt Bold-webfont.woff') format('woff'),
  323. url('fonts/Proxima Nova Alt Bold-webfont.ttf') format('truetype'),
  324. url('fonts/Proxima Nova Alt Bold-webfont.svg#proxima_nova_altbold') format('svg');
  325. font-weight: 600;
  326. font-style: normal;
  327. }
  328. @font-face {
  329. font-family: 'Proxima Nova';
  330. src: url('fonts/Proxima Nova Alt Regular-webfont.eot');
  331. src: url('fonts/Proxima Nova Alt Regular-webfont.eot?#iefix') format('embedded-opentype'),
  332. url('fonts/Proxima Nova Alt Regular-webfont.woff') format('woff'),
  333. url('fonts/Proxima Nova Alt Regular-webfont.ttf') format('truetype'),
  334. url('fonts/Proxima Nova Alt Regular-webfont.svg#proxima_nova_altregular') format('svg');
  335. font-weight: 400;
  336. font-style: normal;
  337. }"""
  338. font_subdir = os.path.join(fonts_dir, "sans-serif/proximasans")
  339. fontpath = os.path.join(fonts_dir, font_subdir)
  340. for fontfile in os.listdir(font_subdir):
  341. send_fonts_to_jupyter(os.path.join(fontpath, fontfile))
  342. return style_less
  343. def set_mathjax_style(style_css, mathfontsize):
  344. """Write mathjax settings, set math fontsize
  345. """
  346. jax_style = """<script>
  347. MathJax.Hub.Config({
  348. "HTML-CSS": {
  349. /*preferredFont: "TeX",*/
  350. /*availableFonts: ["TeX", "STIX"],*/
  351. styles: {
  352. scale: %d,
  353. ".MathJax_Display": {
  354. "font-size": %s,
  355. }
  356. }
  357. }
  358. });\n</script>
  359. """ % (int(mathfontsize), '"{}%"'.format(str(mathfontsize)))
  360. style_css += jax_style
  361. return style_css
  362. def set_vim_style(theme):
  363. """Add style and compatibility with vim notebook extension"""
  364. vim_jupyter_nbext = os.path.join(jupyter_nbext, 'vim_binding')
  365. if not os.path.isdir(vim_jupyter_nbext):
  366. os.makedirs(vim_jupyter_nbext)
  367. vim_less = '@import "styles{}";\n'.format(''.join([os.sep, theme]))
  368. with open(vim_style, 'r') as vimstyle:
  369. vim_less += vimstyle.read() + '\n'
  370. with open(vimtemp, 'w') as vtemp:
  371. vtemp.write(vim_less)
  372. os.chdir(package_dir)
  373. vim_css = lesscpy.compile(vimtemp)
  374. vim_css += '\n\n'
  375. # install vim_custom_css to ...nbextensions/vim_binding/vim_binding.css
  376. vim_custom_css = os.path.join(vim_jupyter_nbext, 'vim_binding.css')
  377. with open(vim_custom_css, 'w') as vim_custom:
  378. vim_custom.write(vim_css)
  379. def reset_default(verbose=False):
  380. """Remove custom.css and custom fonts"""
  381. paths = [jupyter_custom, jupyter_nbext]
  382. for fpath in paths:
  383. custom = '{0}{1}{2}.css'.format(fpath, os.sep, 'custom')
  384. try:
  385. os.remove(custom)
  386. except Exception:
  387. pass
  388. try:
  389. delete_font_files()
  390. except Exception:
  391. check_directories()
  392. delete_font_files()
  393. copyfile(defaultCSS, jupyter_customcss)
  394. copyfile(defaultJS, jupyter_customjs)
  395. if os.path.exists(theme_name_file):
  396. os.remove(theme_name_file)
  397. if verbose:
  398. print("Reset css and font defaults in:\n{} &\n{}".format(*paths))
  399. def set_nb_theme(name):
  400. """Set theme from within notebook """
  401. from IPython.core.display import HTML
  402. styles_dir = os.path.join(package_dir, 'styles/compiled/')
  403. css_path = glob('{0}/{1}.css'.format(styles_dir, name))[0]
  404. customcss = open(css_path, "r").read()
  405. return HTML(''.join(['<style> ', customcss, ' </style>']))
  406. def get_colors(theme='grade3', c='default', get_dict=False):
  407. if theme == 'grade3':
  408. cdict = {'default': '#ff711a',
  409. 'b': '#1e70c7',
  410. 'o': '#ff711a',
  411. 'r': '#e22978',
  412. 'p': '#AA22FF',
  413. 'g': '#2ecc71'}
  414. else:
  415. cdict = {'default': '#0095ff',
  416. 'b': '#0095ff',
  417. 'o': '#ff914d',
  418. 'r': '#DB797C',
  419. 'p': '#c776df',
  420. 'g': '#94c273'}
  421. cdict['x'] = '@cc-input-fg'
  422. if get_dict:
  423. return cdict
  424. return cdict[c]
  425. def get_alt_prompt_text_color(theme):
  426. altColors = {'grade3': '#FF7823',
  427. 'oceans16': '#667FB1',
  428. 'chesterish': '#0b98c8',
  429. 'onedork': '#94c273',
  430. 'monokai': '#94c273'}
  431. return altColors[theme]
  432. def stored_font_dicts(fontcode, get_all=False):
  433. fonts = {'mono':
  434. {'anka': ['Anka/Coder', 'anka-coder'],
  435. 'anonymous': ['Anonymous Pro', 'anonymous-pro'],
  436. 'aurulent': ['Aurulent Sans Mono', 'aurulent'],
  437. 'bitstream': ['Bitstream Vera Sans Mono', 'bitstream-vera'],
  438. 'bpmono': ['BPmono', 'bpmono'],
  439. 'code': ['Code New Roman', 'code-new-roman'],
  440. 'consolamono': ['Consolamono', 'consolamono'],
  441. 'cousine': ['Cousine', 'cousine'],
  442. 'dejavu': ['DejaVu Sans Mono', 'dejavu'],
  443. 'droidmono': ['Droid Sans Mono', 'droidmono'],
  444. 'fira': ['Fira Mono', 'fira'],
  445. 'firacode': ['Fira Code', 'firacode'],
  446. 'generic': ['Generic Mono', 'generic'],
  447. 'hack': ['Hack', 'hack'],
  448. 'hasklig': ['Hasklig', 'hasklig'],
  449. 'iosevka' : ['Iosevka', 'iosevka'],
  450. 'inputmono': ['Input Mono', 'inputmono'],
  451. 'inconsolata': ['Inconsolata-g', 'inconsolata-g'],
  452. 'liberation': ['Liberation Mono', 'liberation'],
  453. 'meslo': ['Meslo', 'meslo'],
  454. 'office': ['Office Code Pro', 'office-code-pro'],
  455. 'oxygen': ['Oxygen Mono', 'oxygen'],
  456. 'roboto': ['Roboto Mono', 'roboto'],
  457. 'saxmono': ['saxMono', 'saxmono'],
  458. 'source': ['Source Code Pro', 'source-code-pro'],
  459. 'sourcemed': ['Source Code Pro Medium', 'source-code-medium'],
  460. 'ptmono': ['PT Mono', 'ptmono'],
  461. 'ubuntu': ['Ubuntu Mono', 'ubuntu']},
  462. 'sans':
  463. {'droidsans': ['Droid Sans', 'droidsans'],
  464. 'opensans': ['Open Sans', 'opensans'],
  465. 'ptsans': ['PT Sans', 'ptsans'],
  466. 'sourcesans': ['Source Sans Pro', 'sourcesans'],
  467. 'robotosans': ['Roboto', 'robotosans'],
  468. 'latosans': ['Lato', 'latosans'],
  469. 'exosans': ['Exo_2', 'exosans'],
  470. 'proxima': ['Proxima Nova', 'proximasans']},
  471. 'serif':
  472. {'ptserif': ['PT Serif', 'ptserif'],
  473. 'ebserif': ['EB Garamond', 'ebserif'],
  474. 'loraserif': ['Lora', 'loraserif'],
  475. 'merriserif': ['Merriweather', 'merriserif'],
  476. 'crimsonserif': ['Crimson Text', 'crimsonserif'],
  477. 'georgiaserif': ['Georgia', 'georgiaserif'],
  478. 'neutonserif': ['Neuton', 'neutonserif'],
  479. 'cardoserif': ['Cardo Serif', 'cardoserif'],
  480. 'goudyserif': ['Goudy Serif', 'goudyserif']}}
  481. if get_all:
  482. return fonts
  483. if fontcode in list(fonts['mono']):
  484. fontname, fontdir = fonts['mono'][fontcode]
  485. fontfam = 'monospace'
  486. elif fontcode in list(fonts['sans']):
  487. fontname, fontdir = fonts['sans'][fontcode]
  488. fontfam = 'sans-serif'
  489. elif fontcode in list(fonts['serif']):
  490. fontname, fontdir = fonts['serif'][fontcode]
  491. fontfam = 'serif'
  492. else:
  493. print("\n\tOne of the fonts you requested is not available\n\tSetting all fonts to default")
  494. return ''
  495. fontdir = os.sep.join([fontfam, fontdir])
  496. return '"{}", {}'.format(fontname, fontfam), fontdir