From 991be35538bd037e66acb132229012e66f443b01 Mon Sep 17 00:00:00 2001 From: Ben Mares <services-git-throwaway1@tensorial.com> Date: Sun, 28 Apr 2024 15:10:00 +0200 Subject: [PATCH 01/10] Revert "Make text option an argument instead of an option" This reverts commit c767a0ad38fabb722129fc0cc8d940d77cd9c632. This changes --text from typer.Argument to typer.Option This is a breaking change. It puts text on the same level as the other options like image or barcode. It is breaking because it now requires any text to be preceeded by `--text`. Ref: <https://github.com/labelle-org/labelle/pull/12#issuecomment-2079204101> and below. --- pyproject.toml | 12 ++++++------ src/labelle/cli/cli.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1997df6..161cf40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -92,13 +92,13 @@ commands = labelle --version labelle --help python -c "import labelle.gui.gui; print('GUI import succeeded')" - labelle --output console "single line" - labelle --output console-inverted "inverted" - labelle --output console multiple lines + labelle --output console --text "single line" + labelle --output console-inverted --text "inverted" + labelle --output console --text multiple --text lines labelle --output console --barcode "Barcode" --barcode-type code128 - labelle --output console --barcode-with-text "Barcode" --barcode-type code128 Caption - labelle --output console --qr QR - labelle --output console --qr QR Caption + labelle --output console --barcode-with-text "Barcode" --barcode-type code128 --text "Caption" + labelle --output console --qr "QR" + labelle --output console --qr "QR" --text "Caption" labelle --output console --picture ./labelle.png [testenv:{clean,build}] diff --git a/src/labelle/cli/cli.py b/src/labelle/cli/cli.py index 0ab3413..9152950 100755 --- a/src/labelle/cli/cli.py +++ b/src/labelle/cli/cli.py @@ -130,7 +130,7 @@ def default( ] = None, text: Annotated[ Optional[List[str]], - typer.Argument( + typer.Option( help="Text, each parameter gives a new line", rich_help_panel="Elements", ), From 3723595159a8823e5eebf700c949309048ed6898 Mon Sep 17 00:00:00 2001 From: Ben Mares <services-git-throwaway1@tensorial.com> Date: Sun, 5 May 2024 12:54:07 +0200 Subject: [PATCH 02/10] Hint on missing --text flag --- src/labelle/cli/cli.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/labelle/cli/cli.py b/src/labelle/cli/cli.py index 9152950..b22891e 100755 --- a/src/labelle/cli/cli.py +++ b/src/labelle/cli/cli.py @@ -6,12 +6,16 @@ # this notice are preserved. # === END LICENSE STATEMENT === import logging +import sys from pathlib import Path from typing import List, NoReturn, Optional import typer +from click.exceptions import ClickException +from click.exceptions import UsageError as ClickUsageError from rich.console import Console from rich.table import Table +from typer.rich_utils import rich_format_error from typing_extensions import Annotated from labelle import __version__ @@ -554,9 +558,35 @@ def default( output_bitmap(bitmap, output) +def add_hint_about_text_option(e: ClickException) -> None: + """Insert a suggestion to use the --text flag when a command is not found. + + In dymoprint the --text option was implicit. If labelle is invoked without + --text, it presents as a ClickUsageError with the message "No such command..." + We append to this error message a hint to use the --text flag. + """ + if isinstance(e, ClickUsageError): + # Enhance the error message for dymoprint users who are + # not used to the --text flag being mandatory. + if e.message.startswith("No such command '") and e.message.endswith("'."): + command = e.message[17:-2] + if " " in command: + command = f'"{command}"' + e.message += f""" Did you mean --text {command} ?""" + + def main() -> None: configure_logging() - app() + try: + app(standalone_mode=False) + except ClickException as e: + # Use standalone mode to avoid typer's default error handling here: + # <https://github.com/tiangolo/typer/blob/773927208fbf03d30d50fc39fe2a1a18b7bd93cb/typer/core.py#L207-L216> + # This allows us to add the following hint: + add_hint_about_text_option(e) + + rich_format_error(e) + sys.exit(e.exit_code) if __name__ == "__main__": From 02c4dbfafd8a44360aba80fb605c53dc799d5a25 Mon Sep 17 00:00:00 2001 From: Ben Mares <services-git-throwaway1@tensorial.com> Date: Sun, 5 May 2024 13:54:34 +0200 Subject: [PATCH 03/10] Add a test for the --text hint --- src/labelle/cli/tests/test_cli.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/labelle/cli/tests/test_cli.py diff --git a/src/labelle/cli/tests/test_cli.py b/src/labelle/cli/tests/test_cli.py new file mode 100644 index 0000000..7577a88 --- /dev/null +++ b/src/labelle/cli/tests/test_cli.py @@ -0,0 +1,25 @@ +import sys + +import pytest +from typer.testing import CliRunner + +from labelle.cli.cli import main + +runner = CliRunner() + + +def test_text_hint(monkeypatch): + # This is NOT the recommended way of testing Typer applications. + # We are doing it this way because we added additional error handling + # in main() which we need to test. + with monkeypatch.context() as m: + m.setattr(sys, "argv", ["labelle", "hello world"]) + with runner.isolation(input=None, env=None, color=False) as outstreams: + with pytest.raises(SystemExit): + main() + sys.stdout.flush() + stdout = outstreams[0].getvalue() + assert ( + b"""No such command 'hello world'. Did you mean --text "hello world" ?""" + in stdout + ) From 4d72722f57b4be6c77c6b5decb27765596844265 Mon Sep 17 00:00:00 2001 From: Ben Mares <services-git-throwaway1@tensorial.com> Date: Wed, 8 May 2024 09:59:30 +0200 Subject: [PATCH 04/10] Use shlex to ensure correct shell escaping --- src/labelle/cli/cli.py | 5 ++--- src/labelle/cli/tests/test_cli.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/labelle/cli/cli.py b/src/labelle/cli/cli.py index b22891e..4630cd0 100755 --- a/src/labelle/cli/cli.py +++ b/src/labelle/cli/cli.py @@ -6,6 +6,7 @@ # this notice are preserved. # === END LICENSE STATEMENT === import logging +import shlex import sys from pathlib import Path from typing import List, NoReturn, Optional @@ -570,9 +571,7 @@ def add_hint_about_text_option(e: ClickException) -> None: # not used to the --text flag being mandatory. if e.message.startswith("No such command '") and e.message.endswith("'."): command = e.message[17:-2] - if " " in command: - command = f'"{command}"' - e.message += f""" Did you mean --text {command} ?""" + e.message += f""" Did you mean {shlex.join(['--text', command])} ?""" def main() -> None: diff --git a/src/labelle/cli/tests/test_cli.py b/src/labelle/cli/tests/test_cli.py index 7577a88..346bf0b 100644 --- a/src/labelle/cli/tests/test_cli.py +++ b/src/labelle/cli/tests/test_cli.py @@ -20,6 +20,6 @@ def test_text_hint(monkeypatch): sys.stdout.flush() stdout = outstreams[0].getvalue() assert ( - b"""No such command 'hello world'. Did you mean --text "hello world" ?""" + b"""No such command 'hello world'. Did you mean --text 'hello world' ?""" in stdout ) From e8d8147d185c943c1ca418b30342d2993fdd1d15 Mon Sep 17 00:00:00 2001 From: Ben Mares <services-git-throwaway1@tensorial.com> Date: Wed, 8 May 2024 10:01:22 +0200 Subject: [PATCH 05/10] Update README with --text --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ddd19fa..f9c919b 100644 --- a/README.md +++ b/README.md @@ -145,15 +145,15 @@ Labelle includes the Carlito font, licensed under the ### Print text -```labelle MyText``` +```labelle --text MyText``` Multilines will be generated on whitespace -```labelle MyLine MySecondLine # Will print two Lines``` +```labelle --text MyLine --text MySecondLine # Will print two lines``` If you want whitespaces just enclose in " " -```labelle "prints a single line"``` +```labelle --text "prints a single line"``` ### Print QRCodes and Barcodes @@ -163,7 +163,7 @@ If you want whitespaces just enclose in " " Just add a text after your qr or barcode text -```labelle -qr "QR Content" "Cleartext printed"``` +```labelle -qr "QR Content" --text "Cleartext printed"``` ### Picture printing From 862fe9ac9322469e7576508ce37baca6426d171f Mon Sep 17 00:00:00 2001 From: Ben Mares <services-git-throwaway1@tensorial.com> Date: Wed, 8 May 2024 10:08:14 +0200 Subject: [PATCH 06/10] Use proper fenced code blocks in README --- README.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f9c919b..c70a1d3 100644 --- a/README.md +++ b/README.md @@ -145,31 +145,43 @@ Labelle includes the Carlito font, licensed under the ### Print text -```labelle --text MyText``` +```bash +labelle --text MyText +``` Multilines will be generated on whitespace -```labelle --text MyLine --text MySecondLine # Will print two lines``` +```bash +labelle --text MyLine --text MySecondLine # Will print two lines +``` If you want whitespaces just enclose in " " -```labelle --text "prints a single line"``` +```bash +labelle --text "prints a single line" +``` ### Print QRCodes and Barcodes -```labelle --help``` +```bash +labelle --help +``` ### Print Codes and Text Just add a text after your qr or barcode text -```labelle -qr "QR Content" --text "Cleartext printed"``` +```bash +labelle -qr "QR Content" --text "Cleartext printed" +``` ### Picture printing Any picture with JPEG standard may be printed. Beware it will be downsized to tape. -```labelle -p mypic.jpg ""``` +```bash +labelle -p mypic.jpg "" +``` Take care of the trailing "" - you may enter text here which gets printed in front of the image @@ -178,7 +190,9 @@ front of the image ### Run Labelle GUI -```labelle-gui``` +```bash +labelle-gui +``` ### GUI App Features From a7701ff2fb1dc515358703f7f2c648d4ba8dfe42 Mon Sep 17 00:00:00 2001 From: Ben Mares <services-git-throwaway1@tensorial.com> Date: Wed, 8 May 2024 10:27:30 +0200 Subject: [PATCH 07/10] Improve README modes section --- README.md | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index c70a1d3..ca8ca7d 100644 --- a/README.md +++ b/README.md @@ -143,29 +143,34 @@ Labelle includes the Carlito font, licensed under the ## Modes -### Print text +### Overview + +For a comprehensive list of options, run ```bash -labelle --text MyText +labelle --help ``` -Multilines will be generated on whitespace +### Preview + +To save tape, you can preview the label without printing it ```bash -labelle --text MyLine --text MySecondLine # Will print two lines +labelle --output=console --text Hi ``` -If you want whitespaces just enclose in " " +### Text + +If your text includes whitespace or any other characters like `<` or `$` that are +interpreted by your shell, then the text must be quoted. ```bash -labelle --text "prints a single line" +labelle --text 'Price: $3.50' ``` -### Print QRCodes and Barcodes +Multiple text arguments will stack on top of each other as separate lines -```bash -labelle --help -``` +```labelle --text "first line" --text "second line"``` ### Print Codes and Text @@ -177,15 +182,12 @@ labelle -qr "QR Content" --text "Cleartext printed" ### Picture printing -Any picture with JPEG standard may be printed. Beware it will be downsized to tape. +Any commonly-supported raster image may be printed. ```bash -labelle -p mypic.jpg "" +labelle --picture labelle.png ``` -Take care of the trailing "" - you may enter text here which gets printed in -front of the image - ## GUI ### Run Labelle GUI From ce4f97fa766fa0085f4fb860e488a28a64f4a70b Mon Sep 17 00:00:00 2001 From: Ben Mares <services-git-throwaway1@tensorial.com> Date: Wed, 8 May 2024 10:31:47 +0200 Subject: [PATCH 08/10] Fix --qr --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca8ca7d..1c79cdc 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,7 @@ Multiple text arguments will stack on top of each other as separate lines Just add a text after your qr or barcode text ```bash -labelle -qr "QR Content" --text "Cleartext printed" +labelle --qr "QR Content" --text "Cleartext printed" ``` ### Picture printing From 7edc2622ed6a2a2006ba621afd0ce21437bd0b5b Mon Sep 17 00:00:00 2001 From: Ben Mares <services-git-throwaway1@tensorial.com> Date: Wed, 8 May 2024 10:32:21 +0200 Subject: [PATCH 09/10] Fix multiline --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c79cdc..d17759b 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,9 @@ labelle --text 'Price: $3.50' Multiple text arguments will stack on top of each other as separate lines -```labelle --text "first line" --text "second line"``` +```bash +labelle --text "first line" --text "second line" +``` ### Print Codes and Text From 26adfac4fbe2d59889d34ba393b6b6f327f1bb9b Mon Sep 17 00:00:00 2001 From: Ben Mares <services-git-throwaway1@tensorial.com> Date: Wed, 8 May 2024 10:36:59 +0200 Subject: [PATCH 10/10] Add preview images to README --- README.md | 10 ++++++++++ doc/3-50.png | Bin 0 -> 1356 bytes doc/hi.png | Bin 0 -> 407 bytes doc/labelle-label.png | Bin 0 -> 1030 bytes doc/qr-with-text.png | Bin 0 -> 1865 bytes doc/two-lines.png | Bin 0 -> 904 bytes 6 files changed, 10 insertions(+) create mode 100644 doc/3-50.png create mode 100644 doc/hi.png create mode 100644 doc/labelle-label.png create mode 100644 doc/qr-with-text.png create mode 100644 doc/two-lines.png diff --git a/README.md b/README.md index d17759b..a07416b 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,8 @@ To save tape, you can preview the label without printing it labelle --output=console --text Hi ``` + + ### Text If your text includes whitespace or any other characters like `<` or `$` that are @@ -168,12 +170,16 @@ interpreted by your shell, then the text must be quoted. labelle --text 'Price: $3.50' ``` + + Multiple text arguments will stack on top of each other as separate lines ```bash labelle --text "first line" --text "second line" ``` + + ### Print Codes and Text Just add a text after your qr or barcode text @@ -182,6 +188,8 @@ Just add a text after your qr or barcode text labelle --qr "QR Content" --text "Cleartext printed" ``` + + ### Picture printing Any commonly-supported raster image may be printed. @@ -190,6 +198,8 @@ Any commonly-supported raster image may be printed. labelle --picture labelle.png ``` + + ## GUI ### Run Labelle GUI diff --git a/doc/3-50.png b/doc/3-50.png new file mode 100644 index 0000000000000000000000000000000000000000..31660693cba3a9700c07c4edceed5b755c23d554 GIT binary patch literal 1356 zcmb8vYdF&j0LSq^I+9$gWs)NCq|vj;t+A6^teY$vi(1drmf1?h$!+JXi*B3HT<VND z_FyV=Sz^i1#s6T4MsBmj!fZw{X~psKysS6B=leXr_n+HGj|3R%Z_)<<zz`MahXw#G z`bwYB(_L9yT-7-M_-#GP&pYG_ODUP^<=8=W-$wNgEjtcw<vZOBX>T)sIovQnWo**+ z)aQ1Nx~HuMl4tO9T0n&^O>5t+M!?Ds2bgSw1A3-V;JTgx;Im2>u-4Z3%jD<ovGEC= zg0%53iyC%)u_dty(roq>;n1;US)fa+tbKgJWYa$K31d`N{G(KAU=L|xhDW)_tEoP6 z_J9x+$(;FRIvnxi?oyn{YQR+GDTUZ`^4iAN`=TM9?9S7^`!Eo!zw{vG$KaU>ry9mI z8X2t3DGc}V4O0|79WLv$FBZ>Qwv|A9(dP>xvy`d?znhNR)t5t!Rq|K;g#A5(AYLe7 z^|uo2cyK36Fe7B7;2Eo;R-@T9ujVO*OVqPDp9MB>eUGUR9VEr~7=!vZLzt(?v_~~6 zY0W~<8->gnLF>B3&!{iD-V|Dp{7r<&i-;u11W9!pp%5bX1@olKoFZK)7}giN9!fMe z5jX^yvngHfSZUu}DL$ey_Ptzj$u11zP!8D)#_na-k`mU^*D#(d4%g4ZO1kFyz?hTL z5qW9_ORBMGErMXolzQ|Q(Uy$__U%2*-4<<Vi)2+@9O+Pn+NebWj-m)g_-(Ko{;lJr z<nqilBcXengW9|>?=RihK(2k6`<i~*H1b}4;{7nWdU;93-uob&!i}hO-W)4}iOxI# zzxv`r-WKw=y3p74FZAbrzoooS$i6sl)i@8$eAt-8$@Op;UY2G*PIz*ye2UVd($A}u zQax$G+_O5HB}CG6L+^nsEKOV&Lzozvo_6{(oIL#cPF1A=3X=QJu@B3`AS(c=i|_4- z7V9`2qvo&{caW<bX&I$z2HDGEM$XvTZQ9u#+==bwXuA*syWio*t>ME=*O^e*Ed5n5 z%;N-#f2SWIWmZKl{t-rN<PtyacN;KbxEJx|v8n7TB2N7lXJl6o3nPC_kH0AQxeKyR zh0lBKIxcGW#ziH%czQQIfLtAwLxt^b)RJHWRJ|!cR$zG^#^wqa&!`vd5FWhXnyDKB z4W`|7=jj<hqk0xsscqr7ipb96;rALp%w7?DajFZ8%Ko+aJ-&FU+6L}AMvq6@HnbS! zjc^lIeKA{J2ffVq1+x$Ki)A}GhP0;hw&mwi+`c(HSx7&u{4tbus;mRPph!rqii_Kw z+SnH|DQ68C*(^BQYY<5YY+Hx2TA6<a{X8#IYE3{0tdF5xZJXrLW30x$FbdHR!KhOP zNS|d7c+XQNFhOXtWVbAvd^cx@Oq^p1n)j=xRE}zm;m~AzDb`XYk-U~(*qHO>2Qo!P z1Vi{S0Yc~P{vB(pqnQ~MU*ABQKSl4keo|GOJ8v}k5EOsTs&TC8!fOPB_L60K9AyIr zIwa)UeHLVE0wwRc<2Li(s2H|orIbl#GK1Rj5wcd&@lO2cy2b1sGp}bD(qLB#bGii7 z*cECT-hJ%DE>Ts3`StlSMbhZ5Sx%XbPGUmb(T5f==bKuLlviU&&n+vISpAQZk4OWe lsC9?%2JDxq`30!Hub$lhcRC~!y|khvfbu`$$MeBm{SThni{}6U literal 0 HcmV?d00001 diff --git a/doc/hi.png b/doc/hi.png new file mode 100644 index 0000000000000000000000000000000000000000..159d42f3b74cb599fbe72c76602db90c096c1350 GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^%YitWgAGWExlQe3U|@{%ba4!+nDh3IcUQB6h(n<E zmw(%1X12OKTA-*Xe$P^QZpVYCKOJMM-`6@^czfNNA^eEvG>el$>XDCByyv8JPSz=$ zr0TZ`x8SeeU->>{t^FD??_cHglailgp6&jcc|@{M^wARs_xacE%l`16>$QL9TTLIJ zDUyYzq3Zt8g>7@3=g2%O<k|mBXO`R(<H-nHq!$VBzl)!VX7eYFmnol4?=@9}I&0@M zo1f3jUH<f+B-*jp=KV32y99Cs(OL+ryvw*KMr~)Pkc1*Iz!^MU{an^LB{Ts5To<sU literal 0 HcmV?d00001 diff --git a/doc/labelle-label.png b/doc/labelle-label.png new file mode 100644 index 0000000000000000000000000000000000000000..1d952d62ce3b3dba8f5ea95a122bd237347acc4f GIT binary patch literal 1030 zcmeAS@N?(olHy`uVBq!ia0vp^8-O^PgAGVtHsP0NU|>G$>EaktG3V{vi+PJ3co?Q% z{`+6QSl>O>a$9+$#mbL8Arpl@2&Ebv>%NtH|9dTi$kX%N`57MYaquj4OwizEa?TWx z@M=B~AjQ&lNx@(WTjN3#gw(&f-wc~>ZhL#X{X^)ye`~}faVrIyayjgQ|CxZ=EU`B) zH|GlKvfle%n{(UjTkZd!JB7WEZJ2g%*XqZwW*kp{>0#aYRJ_hcy)ZOQ(s{l8Bd>RM z(+g$dzdyHD>93!m?sR(1=9%hU(_-?{k4{d0`8IXU%?hi_W#{)Lo18f?E#mm4s`HbT zPc=<jk-mA^9;FwxdxaM(zh#ko9Gc`Wpj$Mh^69>7m#S-flut`Y#-~Mihy9J*bus9H z;T!M0Q)`zd9Ix7WWqoPogQ+UlPClF~uq*6)-%jP*m;YNmeRI0^)q@pseOck!!Pyr} zVx@a+Z|8mcInU?y>>Y-2M<=`}n|<&7jboD{({o;z`zP6&&Ah%mms9a$ONZY7xK727 zOSU|cd4GE2gmONn|Ig1XjY#*?o_uOX9H-&xx$EV!)deCFPbwO(nVP%zlk%-&r|)!B z^RbjgtnJ<Fy?b5d+8L|OBRXC;A7BjROjvvU>$;wW(W#q`d*^?*db?Xd+eMaRwaEnU zZ29cHqT<gKJ}`x)O?|yr+TiuqSG_W88V~SYOlg^_sO$K2+dbbocjCF%A8!x8b^b_i z;`y-5!1ZUAtnD*D{or1B$=A)co!cZM?x#c+ow~nwW$gO*+TSl5mQ0&FRdw2fduNl( zqskQb@MyNq*?MBlC52OEVat_feYo&;+lw1kmxB_zLnYVz+c5oT>9x2@J|(NnwA*D% z<{p^GpsiFHe9S#&=awbgl3rAlv%Xa;vTh82@}X)+*tNN-4<qlFZ@pcp7F@n)?t$bZ zrq3n5Exi||o_zM$rHDt>uNR%!U&Q0TZ0P}^^~D>MB1(4JU1pj+*Wlibxkqgm@XtAs zKjF>8czwwqI@-FM?+a|^=BN>R|Lpy$nlB~q8xM$YaM`UC|NPy--3|}T6H@QLQ0ohN zSgY`Xx8&NCt!u8{o$x-;Xb(F}9rNb6_cLoB$Z-5|TU0eI%A6fFwLfsNvd{RZDPSR3 zwf84D7YHl<e%5@TGvGgnoY45$@qx>U13wiClu{nl2v|sND$wJY!%+)j3qCo^j*@90 r`Dxbi|NT5_Li2d>=NZ(T_n#p;`74Xtb%Qm){LSF$>gTe~DWM4fC#LF^ literal 0 HcmV?d00001 diff --git a/doc/qr-with-text.png b/doc/qr-with-text.png new file mode 100644 index 0000000000000000000000000000000000000000..6410df05cca60232c040b5930d07112b3346cfea GIT binary patch literal 1865 zcmb`ITRhtd7RUcux2cBO(plFv)2&vRoe{;-$uh0VxJ5CB1c?sfl0=$DwJAYuM~!N; z$`+Fpty_sCED32QqEj10QI*ssf__|^EGg<1jcj%w_Gw@C^Vyg4IS=Rje&^+PzCT@p z!$3!@j{pDw3i#G98~_e^==vCALtTx(Fj4^kM#lpDzWMG3eN`^w)`Cv}uNI0Av}m{w zy@Ztk27B^cma<zQPmH^wpX~456rjw2JmA2JqFZMg<AA)c^gRII-yX679FE=s0mlk@ z3<2W{@rQxipS9`%zaE<afXn~$m{up>vbvXjuti{xV|z*fX7)B#LrxdX2?r*C_dc_p zVzei`qO!*@uWz1c+iRR2{Sz$ACcuiG8G4!3laNnRB%@hSYM=kDkaxxNe3I?z>1+Jy zjkHDkzO1Z2|4M&JA8D$l@V0*;TUbgq_gWMy9HG%0(%HwZqnA&~5^*!(=Tg!zOIiCW zE&B_0uiZYR^|?KYT@Leh!Sj0@pD}8daDRF5SkK<<`BALQqqS+Me*puzI_OM1QBha6 zNlEt-8su3;CmioZ%$3<x7$*YJQmz#(oKs1wO1qyb+SJQ)hIr^j+q4^v@6pwh<=Bza zR<!f5YP;%FW3Ge5g=WOZuOrA#%Chh`adCEN<vf8S{2nW29hkyZ>H#N4;^Ulw^^P=b zvaG>2O0hT2G6<dw6>B?P7|UH9iiw%h9t<%I18!O#1?T-E4i&HmW#g6V-JGP|%^mmW zVem_CG&*)hS)X5ptjcARekR8LjQ<U^7Jr!b+CL0p(&WVM4Fq*(<bhYG-4{8{<B0UL zRG3yhBG0v`?FRl71bs-?_7ib_!ZY}jABU`M3Q|X1N!wvDDAl|`CSQ*5{-j?%+cL9^ z*f)4@F2ujpY;8<AJnl5tWPjikVfe)%48yY*#2JgjuQm!QAbe~jYA6`8bF==VrtRGQ z`XtTopbx}QE_z?so`XznTdTXsYfq7K*Pd+)`s&{#ZLn7l(N_G;7Mub<h1*knTm2M9 zCMsKB|2T$~BKJdWf%)+8PD^@>M7r|q4{@c^vBDhJ3+Un$T2I54x{y)ee$xRF0;g!K z_1T8n5#_b(2x0bGf{05@ezY*%fqD5%(#({_wsSa=go@9)_B?)QS&^eY|C^|Qg_L(8 zzdTq=%Gj$_Y8BDD%sQ5TC+9&zKRHrRZ>~ANFC6i(qDg`WJ9oBWBN^1~a(-^1%i@+w z-tJy4B#z0(6swdI5Rcog`$G=u8mYR}XjG|$E7I-TsX^xW(zd-S%LZ17*G<OTG>%%T z5~2;{mmP}!coIF(vPGCnuG4wYgySB!K&02t`xJ57CMp7B+%+*?NDQD6VDX!aXWBP2 z%Sw3SSFwM6af4;hqt^Gp1v$8hBHxH!T0U)yi{FH%G_c4#vBZ`)g~l={ySU!ABPj>b zO-$b})qkJjn&3WPN2XN0?#z@BW1_Zr$SbBVlW0AT?#D?VR8g}Guc@`Pdmzg0`432a zPhY3mwokq-c&oJ&48Idi&bF}Whl#k-wt*q~0wy#3wX#|mO36})1F5x0%@>y9VwQHg z#$Q%*XH@e;VrAICggz&*mE>ua@80E@yTV^jRqaeUCPa8g$1)!^X{tg@oO16pen&;z zt`tWLSnHi=!kOW(NL3}?E1|NO@?ww=w0a~4bDsgS7{!b*q}q9Nl{J0xU2HNWNZ2np zm>MMfJIyxlrLs^M2)x5CT+aD)8Rr8-(55x0NBeex7+7^5nfrb7S(DbW8e)_tmvEh< z3B-!R7bFbNoh4GT@aa$oOmUj2yY=cdQUGFZI^ha*9~B&!tl`8BRu0EaHlMQv6`w+H z3u+HyqzUhG0u_xMNjEHpH=a{KbXvUbd@r;XSKda(UOODB=?+PEY?MfgJL{&;VTzOc zCK+Q558Z-JyahY&C})EpC$Cp4BeTFv)x#~)9fTU?$7E4dIipX0L5LbF`zQ1zC;5=E zZgzJ-L<D@nW>CI0tG?G^X+*#ipaN0-)JFDQWkru;M4I(``O4K!LVaegz&qsbJdP({ z-gYy!_UbG1emZ<(6Po36X@ft7$NHsawPVSvSvz<6owGw+>XXd%Nj>C@1#*z=7v=VX z2Tu6$A~R|^EZm2w6kg;uER{$`UCV4?ChmA@29r&p5}g`d@V_70<OGD&vxrV{$4mE@ zUN}lkWht5!(3$x#3*eYYCz}@Wz<t)^>7*HOC0L|mM1t-KD9s!fF{tj&Ql4I3X5znK z!X-N&nS;+1<$Lx2haB9Pua90aqCSc@h5Y|f^IyQVjxKt|Ab=x_LUoJ-0xrV+Sm)#O F{|N}{p=SU9 literal 0 HcmV?d00001 diff --git a/doc/two-lines.png b/doc/two-lines.png new file mode 100644 index 0000000000000000000000000000000000000000..78f5b48ffed99cdc61579b7ac304d4c30697dde9 GIT binary patch literal 904 zcmeAS@N?(olHy`uVBq!ia0vp^-+?%qgAGWo4-!7cz`*S9>EaktG3V{w+tX$_2(Vt5 zF!lfc)K4i%JlD5PeRk!Zzp)l`kIXNosLM9zKl3Y`JHOhT;Rnydwtz#t3tOa}IL%vx z@;FrQ2zc#KoKoPdflp%px_b@UK9|2de||lGW|d8Od;a{W+x=_*F77MR{r<Ue|JOU$ zURTXp_A>kRqG?4B>t;Ovxj*df&vIS4^6*X3`&1RLaQ;5<+iUBcUHe0VwC)6WcIN#I z>fU?3>;2h9`}OvJ`?d6Wf6jU9*w>rGnWL`l-NN6-oUAGM?%p}J?>|2tce%&m+_E+| zmiyJj)x1u2Z9)&@vT~#O?md70o$tY7-JhY25z+4-e_Yz|{_lS`i^Px94a=LS2_L?+ zbo*-E-18H2W2(>In$VifQ68A?Z^Be<ue$50ZEowMN}au?d3&uL_p|Gkr>~!!ou~g) zE9xD8gjBWKSrzp+&a6O=RH0C>Rm*Q~mO0uS=It^&1KT%pl)Gn7PES8v(Eee~+vS`` z3TFiTZrIvfy}G)t-C@r?v+DNTbv~!>UcAO~YvyrxxyA2&g=V?P95<;}SC}*nE!1D% z$;tY8pxa9$`9cvm0(L#E>0S4uJaZ=~MpjI^`)RYNS=8$6IkD?fZRf92_|&Pl#Y^LU zsMa0#RgE&0f3F$op8mS((Z$V8C#K3j58+(eAM~)!Lg>-8S5kYUmoJW=`X`Td;@Z^x zo~l#oPW9}JUfsAQ?0UrSIp40`zx_Ms`^`tK+5KDB{GJ`+^m4J!j`JC|9P9pAEuF%p zr1{&h$>{E<Ygr1)GB%4tSY_w_zqlV5f2$e;JZDb({V>OLLYDEG@@IR0g|D3Ud#3&; z<=C4A_IDH?wDE1xkbkGT_NwW*c^d0%Hts17KI0MM6dA5|=Y0rs=E`-2WizMEPO5qt zsG2IGweU}9p3~bGzUn~rar!&ou6=#t_4IA)+xM2<e=0P`OXKvZmg?7fOXrm@w3U0m zb)J9u-Sc^`S47=koubv}u)-)gZ-pD-v`cJie`W6e^yiyJopa~OlCtDnm{r-UW| D-`uxh literal 0 HcmV?d00001