From cf68b2c202022a81d2e1e5b258b4b688ad559c11 Mon Sep 17 00:00:00 2001 From: Mathieu PATUREL Date: Sat, 16 Nov 2019 14:56:03 +1100 Subject: [PATCH] Set the maxwidth for images (fix #48) It didn't look pretty when images where larger than the viewport, and it "broke" word wrap (because it stretched the phantom, and hence lines wrapped further, see #34) Sublime Text's minihtml only supports width and height attributes on img tags, therefore, we have to determine the aspect ratio of the images ourselves if we want to set a maxsize (so that we can set the height). We use a hacky function copy pasted from stackoverflow to determine the size of common format of images using std lib python. --- .gitattributes | 4 +- MarkdownLivePreview.py | 25 ++++++-- live-testing/test.md | 53 ++++++++++++++++- markdown2html.py | 96 ++++++++++++++++++++++++++---- resources/404.base64 | 2 + resources/convertresources.py | 32 ++++++++-- resources/loading.base64 | 4 +- resources/loading.png | Bin 953 -> 7409 bytes resources/loading.xcf | Bin 0 -> 31996 bytes resources/transparent-loading.png | Bin 0 -> 953 bytes 10 files changed, 192 insertions(+), 24 deletions(-) create mode 100644 resources/loading.xcf create mode 100644 resources/transparent-loading.png diff --git a/.gitattributes b/.gitattributes index dcc40d8..6252750 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,3 @@ docs/ export-ignore -resources/*.png export-ignore -resources/*.py export-ignore \ No newline at end of file +resources/ +!resources/*.base64 \ No newline at end of file diff --git a/MarkdownLivePreview.py b/MarkdownLivePreview.py index f5ebb04..c23f276 100644 --- a/MarkdownLivePreview.py +++ b/MarkdownLivePreview.py @@ -7,7 +7,9 @@ original_window: the regular window preview_window: the window with the markdown file and the preview """ +import time import os.path +import struct import sublime import sublime_plugin @@ -24,8 +26,10 @@ resources = {} def plugin_loaded(): global DELAY - resources["base64_404_image"] = get_resource("404.base64") - resources["base64_loading_image"] = get_resource("loading.base64") + resources["base64_404_image"] = parse_image_resource(get_resource("404.base64")) + resources["base64_loading_image"] = parse_image_resource( + get_resource("loading.base64") + ) resources["stylesheet"] = get_resource("stylesheet.css") # FIXME: how could we make this setting update without restarting sublime text # and not loading it every update as well @@ -90,7 +94,7 @@ class OpenMarkdownPreviewCommand(sublime_plugin.TextCommand): markdown_view.set_syntax_file(syntax_file) markdown_view.settings().set( - MARKDOWN_VIEW_INFOS, {"original_window_id": original_window_id} + MARKDOWN_VIEW_INFOS, {"original_window_id": original_window_id,}, ) def is_enabled(self): @@ -208,11 +212,17 @@ class MarkdownLivePreviewListener(sublime_plugin.EventListener): total_region = sublime.Region(0, markdown_view.size()) markdown = markdown_view.substr(total_region) + preview_view = markdown_view.window().active_view_in_group(1) + viewport_width = preview_view.viewport_extent()[0] + basepath = os.path.dirname(markdown_view.file_name()) html = markdown2html( - markdown, basepath, partial(self._update_preview, markdown_view), resources, + markdown, + basepath, + partial(self._update_preview, markdown_view), + resources, + viewport_width, ) - print(html) self.phantom_sets[markdown_view.id()].update( [ @@ -239,6 +249,11 @@ def get_resource(resource): return sublime.load_resource(path) +def parse_image_resource(text): + width, height, base64_image = text.splitlines() + return base64_image, (int(width), int(height)) + + # try to reload the resources if we save this file try: plugin_loaded() diff --git a/live-testing/test.md b/live-testing/test.md index 2be7c65..9485a64 100644 --- a/live-testing/test.md +++ b/live-testing/test.md @@ -1 +1,52 @@ -# hello world +# Why? +Why do you want to use fancy symbols in your standard monospace font? Obviously to have a fancy prompt like mine :-) + +![prompt](https://github.com/gabrielelana/awesome-terminal-fonts/raw/master/why.png) + +And because when you live in a terminal a symbol can convey more informations in less space creating a dense and beautiful (for those who have a certain aesthetic taste) informative workspace + +Heavily inspired by and the relative patch script from **Kim Silkebækken** (kim.silkebaekken+vim@gmail.com) + +## Patching vs Fallback +There are two strategies that could be used to have symbols in a terminal +* you can take a bunch of symbol fonts, your favourite monospace font and merge them together (patching strategy) +* you can use a feature of `freetype2` font engine, basically you can say that whenever the current font doesn't have a glyph for a certain codepoint then fallback and go look into other fonts (fallback strategy) + +Initially I used the first strategy, later I switched to the second. The patching strategy it's more reliable and portable, the problem is that you need to patch every monospace font you want to use and patching a single font it's a lot of manual fine tuning. If you want you can find all previous patched fonts in [patching-strategy branch](https://github.com/gabrielelana/awesome-terminal-fonts/tree/patching-strategy) + +## Font Maps +Referring to glyphs by codepints (eg. `\uf00c`) in your scripts or shell configuration it's not recommended because icon fonts like [Font Awesome](http://fontawesome.io/) use [code points ranges](https://en.wikipedia.org/wiki/Private_Use_Areas) those ranges are not disciplined by the unicode consortium, every font can associate every glyphs to those codepoints. This means that [Font Awesome](http://fontawesome.io/) can choose to move glyphs around freely, today `\uf00c` is associated to the `check` symbol, tomorrow it can be associated to something else. Moreover, more than one icon font can use the same codepoint for different glyphs and if we want to use them both we need to move one of them. So, if you use a codepoint to refer to a glyph after an update that codepoint can point to another glyph. To avoid this situation you can use the font maps in the `./build` directory, font maps are scripts which define shell variables that give names to glyphs, by sourcing those files in your shell you can refer to glyphs by name (eg. `$CODEPOINT_OF_AWESOME_CHECK`). + +TLDR: don't refer to glyphs by codepoints (eg. `\uf00c`) but by name (eg. `$CODEPOINT_OF_AWESOME_CHECK`) to make your scripts and shell configurations resilient to future updates. To do that don't forget to copy font maps (`*.sh` files) in the `./build` directory in your home directory and to source them in your shell startup + +## Included Fonts +In this repository you can find a bunch of fonts that I use as symbol fonts with the relative font maps +* **Font Awesome 4.7.0**: `./fonts/fontawesome-regular.ttf`, for further informations and license see http://fortawesome.github.io/Font-Awesome +* **Devicons 1.8.0**: `./fonts/devicons-regular.ttf`, for further informations and license see https://github.com/vorillaz/devicons +* **Octicons 1.0.0**: `./fonts/octicons-regular.ttf`, for further informations and license see https://github.com/blog/1135-the-making-of-octicons +* **Pomicons 1.0.0**: `./fonts/pomicons-regular.ttf`, for further informations and license see https://github.com/gabrielelana/pomicons + +## How to install (Linux) +* copy all the fonts from `./build` directory to `~/.fonts` directory +* copy all the font maps (all `*.sh` files) from `./build` directory to `~/.fonts` directory +* run `fc-cache -fv ~/.fonts` to let freetype2 know of those fonts +* customize the configuration file `./config/10-symbols.conf` replacing `PragmataPro` with the name of the font you want to use in the terminal (I will add more fonts in the future so that this step could be skippable) +* copy the above configuration file to `~/.config/fontconfig/conf.d` directory +* source the font maps (`source ~/.fonts/*.sh`) in your shell startup script (eg. `~/.bashrc` or `~/.zshrc`) + +### Arch Linux +We have been included in the [official repositories](https://www.archlinux.org/packages/community/any/awesome-terminal-fonts/), so if you are running an Arch Linux +* run `pacman -Syu awesome-terminal-fonts` + +## How to install (OSX) +* follow [this detailed instructions](https://github.com/gabrielelana/awesome-terminal-fonts/wiki/OS-X) contributed by [@inkrement](https://github.com/inkrement) +* copy all the fonts maps (all `*.sh` files) from `./build` directory to `~/.fonts` directory +* source the font maps (`source ~/.fonts/*.sh`) in your shell startup script (eg. `~/.bashrc` or `~/.zshrc`) +* If it still doesn't work, consider to use the [patching strategy](#patching-vs-fallback) + +## How to install (Windows) +* make sure you have permissions to execute Powershell scripts in your machine. To do so, open Windows Powershell as Administrator and paste & run the following command `Set-ExecutionPolicy RemoteSigned` +* then run the install script `./install.ps1` + +## License +[MIT](https://github.com/gabrielelana/awesome-terminal-fonts/blob/master/LICENSE) diff --git a/markdown2html.py b/markdown2html.py index e9bd6a3..c5c7efc 100644 --- a/markdown2html.py +++ b/markdown2html.py @@ -5,6 +5,8 @@ and makes it a lot easier to think about, and for anyone who would want to, test markdown2html is just a pure function """ +import io +import struct import os.path import concurrent.futures import urllib.request @@ -30,7 +32,7 @@ class LoadingError(Exception): pass -def markdown2html(markdown, basepath, re_render, resources): +def markdown2html(markdown, basepath, re_render, resources, viewport_width): """ converts the markdown to html, loads the images and puts in base64 for sublime to understand them correctly. That means that we are responsible for loading the images from the internet. Hence, we take in re_render, which is just a function we @@ -58,13 +60,16 @@ def markdown2html(markdown, basepath, re_render, resources): path = os.path.realpath(os.path.expanduser(os.path.join(basepath, src))) try: - base64 = get_base64_image(path, re_render) + base64, (width, height) = get_base64_image(path, re_render) except FileNotFoundError as e: - base64 = resources["base64_404_image"] + base64, (width, height) = resources["base64_404_image"] except LoadingError: - base64 = resources["base64_loading_image"] + base64, (width, height) = resources["base64_loading_image"] img_element["src"] = base64 + if width > viewport_width: + img_element["width"] = viewport_width + img_element["height"] = viewport_width * (height / width) # remove comments, because they pollute the console with error messages for comment_element in soup.find_all( @@ -100,10 +105,12 @@ def get_base64_image(path, re_render): """ Gets the base64 for the image (local and remote images). re_render is a callback which is called when we finish loading an image from the internet to trigger an update of the preview (the image will then be loaded from the cache) + + return base64_data, (width, height) """ def callback(path, future): - # altering image_cache is "safe" to do because callback is called in the same + # altering images_cache is "safe" to do because callback is called in the same # thread as add_done_callback: # > Added callables are called in the order that they were added and are always # > called in a thread belonging to the process that added them @@ -120,14 +127,23 @@ def get_base64_image(path, re_render): executor.submit(load_image, path).add_done_callback(partial(callback, path)) raise LoadingError() - with open(path, "rb") as fp: - image = "data:image/png;base64," + base64.b64encode(fp.read()).decode("utf-8") - images_cache[path] = image - return image + with open(path, "rb") as fhandle: + image_content = fhandle.read() + width, height = get_image_size(io.BytesIO(image_content), path) + + image = "data:image/png;base64," + base64.b64encode(image_content).decode( + "utf-8" + ) + images_cache[path] = image, (width, height) + return images_cache[path] def load_image(url): with urllib.request.urlopen(url, timeout=60) as conn: + + image_content = conn.read() + width, height = get_image_size(io.BytesIO(image_content), url) + content_type = conn.info().get_content_type() if "image" not in content_type: raise ValueError( @@ -135,7 +151,60 @@ def load_image(url): url, content_type ) ) - return "data:image/png;base64," + base64.b64encode(conn.read()).decode("utf-8") + return ( + "data:image/png;base64," + base64.b64encode(image_content).decode("utf-8"), + (width, height), + ) + + +def get_image_size(fhandle, pathlike): + """ Thanks to https://stackoverflow.com/a/20380514/6164984 for providing the basis + of a working solution. + + fhandle should be a seekable stream. It's not the best for non-seekable streams, + but in our case, we have to load the whole stream into memory anyway because base64 + library only accepts bytes-like objects, and not streams. + + pathlike is the filename/path/url of the image so that we can guess the file format + """ + + format_ = os.path.splitext(os.path.basename(pathlike))[1][1:] + + head = fhandle.read(24) + if len(head) != 24: + return "invalid head" + if format_ == "png": + check = struct.unpack(">i", head[4:8])[0] + if check != 0x0D0A1A0A: + return + width, height = struct.unpack(">ii", head[16:24]) + elif format_ == "gif": + width, height = struct.unpack("H", fhandle.read(2))[0] - 2 + # We are at a SOFn block + fhandle.seek(1, 1) # Skip `precision' byte. + height, width = struct.unpack(">HH", fhandle.read(4)) + except Exception as e: # IGNORE:W0703 + raise e + else: + return "unknown format {!r}".format(format_) + return width, height def independent_markdown2html(markdown): @@ -143,5 +212,10 @@ def independent_markdown2html(markdown): markdown, ".", lambda: None, - {"base64_404_image": "", "base64_loading_image": "", "stylesheet": ""}, + { + "base64_404_image": ("", (0, 0)), + "base64_loading_image": ("", (0, 0)), + "stylesheet": "", + }, + 960, ) diff --git a/resources/404.base64 b/resources/404.base64 index d82c610..2b78083 100644 --- a/resources/404.base64 +++ b/resources/404.base64 @@ -1 +1,3 @@ +100 +43 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 \ No newline at end of file diff --git a/resources/convertresources.py b/resources/convertresources.py index ed1d52a..a1474f7 100644 --- a/resources/convertresources.py +++ b/resources/convertresources.py @@ -1,9 +1,33 @@ """ A small script to convert the images into base64 data """ +import struct from base64 import b64encode -with open("404.png", "rb") as png, open("404.base64", "wb") as base64: - base64.write(b64encode(png.read())) -with open("loading.png", "rb") as png, open("loading.base64", "wb") as base64: - base64.write(b64encode(png.read())) +def get_image_size(fhandle): + """https://stackoverflow.com/a/20380514/6164984""" + head = fhandle.read(24) + if len(head) != 24: + return + + # always going to be png + check = struct.unpack(">i", head[4:8])[0] + if check != 0x0D0A1A0A: + raise ValueError("invalid check (?)") + + width, height = struct.unpack(">ii", head[16:24]) + return width, height + + +def make_cache(image_name): + with open("{}.png".format(image_name), "rb") as png, open( + "{}.base64".format(image_name), "wb" + ) as base64: + width, height = get_image_size(png) + png.seek(0) + base64.write(bytes("{}\n{}\n".format(width, height), encoding="utf-8")) + base64.write(b64encode(png.read())) + + +make_cache("404") +make_cache("loading") diff --git a/resources/loading.base64 b/resources/loading.base64 index 3fcbe3d..07ed4b3 100644 --- a/resources/loading.base64 +++ b/resources/loading.base64 @@ -1 +1,3 @@ -R0lGODlhZABkAPU+AAAAAAwNDRweHyYpKzg8Pzo+QUBFSERJTEdMT05UV1NYXFVbX1hfY1lfZGFobWJpbmhvdGxzeHF5fnJ6gHV9g3Z+hHqDiXuEin+IjoCIjoKLkYKMkoSNk4eQl4iSmIqTmouUm42XnY+ZoJKco5OdpJOepZSeppahqJeiqZmjqpumrZ6psJ+qsqOutqSvt6SwuKezu6i0vKm1vay4wK66wq+7w6+8xLK+xrK/x7TAybXCy7bDy7jEzbjFzrzJ0gAAACH5BAUAAD4AIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAZABkAAAG/kCfcEgsGo/IpHLJbDqf0Kh0Sq1ar9isdsvter/gsHhMLpvP6LR6zW673/C4fE6v2+/4vH7P7/v/gIGCg4SFhoeIiW8IAAAUilUpjQABkEsmMUchkwBIOTOQBQICGUabk0ctFhYdiSajAgOZRKeNRjkYqxaghyuwAgxFtZ1FJBe6NokHvya0nEUzuhYgijG/B86oRCDSOZAPv6VCw0SquiiWNwOwAzfjz0I8uasYPIMvDQ0kRhm/Ee/afKiQ1sIIDBAgkuUxQKDhA29ERMHK9GJSJR85pLUiwkOELgx6Goo0sG/IK1gVhCig9MjHimOreAmBMU+XngciRTrAMSQB/qxmR6KtEjGko7Shey7kbGgA6A0GBz4oOUjCno8YNXWp6NOCwVICD6UYPQqiBiANDHNOkILiqIYVg2Y0yPlAikddICASQtuwJJQY9OAimqFCZpRPei0pPnKjg4fHkB936JBYyg4VmDNrVlH5zYMFoEOLZgDBSoejqDfQEc1atBXUsOl8bi26bpUNsKWpnlPjg+PIj32brZJjs/HOi5PjiMFzCo4ZyAWpqCBhwgspMFa9NZRjg4TvEjZCEQFzWvQ9KiiA/+73SVtpGAT7mcFh/XcPVqH0uCsNhDs+J9gnAQXX+cADCSDMggRVVtGE2lZ6fCAgfkPcdYFhRAhlAVHxxfCnC4d42EdghtII1hYGLgjxki6GOSiNHtR990F+QpymizcZ0SNEjquI1+FHetDHQYFEuCANhBpaMMRAuqRYxEEJDSLPR1YlWVRN9Vjy3ioFCWHlEC6Uh44iOcB0gQck2kSEB90o4sEFx1yY5irQ9JdIDdIANcSXRBiDzGAfVcbnELiwmEgHx3Q5p5JGmOPjIdAF9eIRnyRnhA1AWvqEn4pq6umnoIYq6qiklmrqqaimquqqrLbq6quwxirrrLTWauuttwYBADs= \ No newline at end of file +128 +128 +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 \ No newline at end of file diff --git a/resources/loading.png b/resources/loading.png index 183ae2d99189f8eedec016fc9fda9d772a909048..30e67eb905a679fd41f1a76286979e2c1d1650dc 100644 GIT binary patch literal 7409 zcmV zaB^>EX>4U6ba`-PAZ2)IW&i+q+TEI2mLn^YhTmC5F99$NgXJ)uQ#80nvn-;%*zFt!-@A-#A@AsdM zhi@Nb4hgUF^=mz!^9RTE`vq1$$LG(-gRRdv?K4pNHt_oqzq7ypoYy`BN&Y&XKEH?l zxo(snmh<_a*Js$z;J^M}j9_eKJjo#+NfLQ(eivZ|(q-_R-gC`4SVzjz9kV5W9PKyZ3yp zZj?%-YWOVbx23#yoHz-kyvr&N zDVBV)@tX6>@{TpJ-1CyJ6!1tl8|m3W8o63zO*ovt%_Y72_IJOP8h74_Gjn3F#1+4L zgg-j??>xQ@bg!3gO0j*574ym-aHipz^JmT?A?bc%sxQ9p5q_wf_(Nia4C)JWV}T>i zuOX(U@3kc_&%${cONd{sk!*hkAVl0-nGDrkQmjcd)f6pfONlr(@-vuumXaqKNUC|K zaLJ9(36@*-dvlGYcW&1Fbr@(N5h_)T)X+dAs}!YvtTbw9sAvhQTGg~^*HNXGsyllX{oY)C+uWhWgh461NEtI_W zy>1^%!e}?|Zltcn3s?+S37Y!I^VD(M-0%{mR4!=0hn&tp9l+uidtZvZp1pbsGv2vo z2ig0%g?=n!C%vKt)IHMCI9euHSPG0exQ9v2nb)MBW zp^xoq*&zK~tsTu}ogj|>Hpmz$>SvzTF8Fb1Bb!k79A);~&z97VjB{&GlRllhwkX>e z98zo}=)GCfR9epuAWhZyLs~6pB_fRMIH1LN!+@|yETBdMkAdg=cpYIo^ z>c`&Yd2;!Dmy_8k10rT>vDPtw&rZ;5-?J@%4RJ!P#c~QDbcQsw$ZCkvztSb)PfsY> zA_`@~yWSqRcM`eLnp+9P21QwDn%)+b9E4kQ0`11^2Y3bS=mxuJEtFB&U@Tfzw0A>C z?`xGdiKIS}n4|)pvyL%u3Klb?l04-JMKWJQ0*`u>rs{`4yxN_%!dM3hXQr3iNQ$EPMR!izySG6nDV=H$Fh0xB1A_yoDPC4($yoJI(wr@qC5lElZxk1lG z)U}ycO`KNJ>3+*Z{>Cg?StWci&SagQ%U*Q~=nXp9ASEP)vTqjP>iBi`9D7);5bB~0Egv_>|}X!(2Y4mkt<2c@&bOZ6OP5f>Vcru22DGJS=6 z44==Qh_;<_XC`zODWu`lKS8hqa+HuHAnNW{o?N~@UDGK@NvB-{@}MLr%6f-_ylsKR znT2{xuCsx~hiKi0h+f2HkVS4&St7?uncFj|Hz!+Z?{p4g02ioPJ2X;>Jiu6P-P3S9FNm%SU3Cl|(?p!Hx2UyIDHbHF`57g}~)1wda@03!syledWwy40=7^z0jTGjUT;uJiz4+ZfO( zF@T$4LKt!&*Ou$5!k8HbIFR&GpapSwxQ_I1l%2{Muw<{hlKy#U1`uL_`3qIb`l2J* zfQ*V8fawHQ2x(#Er-I_FaNSxXQE1+f)@p#3BXK~9-5Mc!G=bwaLJe^UTgX}@38o?) zzz>WsEFahc24Jx(j$)f$s~�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 literal 953 zcmV;q14jHuNk%w1VPpVg0QEiq0000C4GkO~A0{a)I6OZ(K0!c5NJL3YM@&ynR9912 zTvc0NSYKmVUu0ouZDMI|Xm50Eb9ixiesX$%b$x?&euR31iF<^Ke~6BNh>n7bk%Ekp zgpHGjke7&(n2M8{i zuC}DFx1_MRr?b1Lw7jXcy{x#vuDZgnyTh-%#InA|vcJc)z{$13%eKSIxWvu4#m>CR z(f|MeA^8La002G!A^!_bMO0HmK~P09E-(WD0000i00000WB_CU00#a*pKwSl8jr}N za>;BupU|juO08P2*sONT?Rvl9uy{-^o6qR9dd+UT-|)D6POsbV_`H74@B9CNfr5jC zg@%WSiEju102GQ0d`0sgFJN209~F{Gokp?fam zY-j-J4F(53C^<;No{tzcb*bpng27S(52YAA3Ze!e4GeQmkb>fwx};ddK?w~a&qr1z zSyWB+Sn}Q~1qos05H6I5jv_h0bb$vX+C+p0-6-P01;<>N<|@$imq-oL05!yyL>Pn2 zU>{Y+=McECm`Df5bUCMzMwsK{AZqiaYU-(| brmE_ythVavtFXo@>#VfaYU{1H1_1y&aJ8`j diff --git a/resources/loading.xcf b/resources/loading.xcf new file mode 100644 index 0000000000000000000000000000000000000000..29a16d06f48c8b110d3ddce996a335c30741b5b2 GIT binary patch literal 31996 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 |MkW|2Xv(n>q=g3wdkFKKE^jxFX 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 literal 0 HcmV?d00001 diff --git a/resources/transparent-loading.png b/resources/transparent-loading.png new file mode 100644 index 0000000000000000000000000000000000000000..183ae2d99189f8eedec016fc9fda9d772a909048 GIT binary patch literal 953 zcmV;q14jHuNk%w1VPpVg0QEiq0000C4GkO~A0{a)I6OZ(K0!c5NJL3YM@&ynR9912 zTvc0NSYKmVUu0ouZDMI|Xm50Eb9ixiesX$%b$x?&euR31iF<^Ke~6BNh>n7bk%Ekp zgpHGjke7&(n2M8{i zuC}DFx1_MRr?b1Lw7jXcy{x#vuDZgnyTh-%#InA|vcJc)z{$13%eKSIxWvu4#m>CR z(f|MeA^8La002G!A^!_bMO0HmK~P09E-(WD0000i00000WB_CU00#a*pKwSl8jr}N za>;BupU|juO08P2*sONT?Rvl9uy{-^o6qR9dd+UT-|)D6POsbV_`H74@B9CNfr5jC zg@%WSiEju102GQ0d`0sgFJN209~F{Gokp?fam zY-j-J4F(53C^<;No{tzcb*bpng27S(52YAA3Ze!e4GeQmkb>fwx};ddK?w~a&qr1z zSyWB+Sn}Q~1qos05H6I5jv_h0bb$vX+C+p0-6-P01;<>N<|@$imq-oL05!yyL>Pn2 zU>{Y+=McECm`Df5bUCMzMwsK{AZqiaYU-(| brmE_ythVavtFXo@>#VfaYU{1H1_1y&aJ8`j literal 0 HcmV?d00001