From 8ebe9b459a3e9aeb7527053bbee6100026e379e2 Mon Sep 17 00:00:00 2001 From: SeedSigner <65379725+SeedSigner@users.noreply.github.com> Date: Fri, 18 Feb 2022 12:10:41 -0600 Subject: [PATCH] Revert "CompactSeedQR format" --- README.md | 211 ++++- docs/12_word_seed.pdf | Bin 0 -> 21302 bytes docs/24_word_seed.pdf | Bin 0 -> 48486 bytes .../printable_templates => }/Seed_QR_Card.pdf | Bin docs/developer_tips.md | 33 - docs/manual_installation.md | 336 -------- ...plate_generator.py => qr_code_template.py} | 87 +- docs/qr_code_template_21x21.html | 515 +++++++++++ docs/qr_code_template_25x25.html | 716 ++++++++++++++++ docs/qr_code_template_29x29.html | 803 ++++++++++++++++++ docs/qr_formats.md | 6 +- docs/seed_qr/README.md | 482 ----------- docs/seed_qr/img/compact_12word.png | Bin 1060 -> 0 bytes docs/seed_qr/img/compact_24word.png | Bin 1212 -> 0 bytes docs/seed_qr/img/handmade_qr.jpg | Bin 205637 -> 0 bytes docs/seed_qr/img/phone_screenshot_compact.jpg | Bin 223256 -> 0 bytes .../seed_qr/img/phone_screenshot_standard.jpg | Bin 86284 -> 0 bytes docs/seed_qr/img/qrcode_capacity.png | Bin 17805 -> 0 bytes docs/seed_qr/img/seedqr_plate.jpg | Bin 176975 -> 0 bytes docs/seed_qr/img/standard_12word.png | Bin 1184 -> 0 bytes docs/seed_qr/img/standard_24word.png | Bin 1315 -> 0 bytes .../seed_qr/img/standard_24word_with_logo.png | Bin 6531 -> 0 bytes docs/seed_qr/img/vector1_compact_24word.png | Bin 1179 -> 0 bytes docs/seed_qr/img/vector1_standard_24word.png | Bin 1340 -> 0 bytes docs/seed_qr/img/vector2_compact_24word.png | Bin 1202 -> 0 bytes docs/seed_qr/img/vector2_standard_24word.png | Bin 1312 -> 0 bytes docs/seed_qr/img/vector3_compact_24word.png | Bin 1189 -> 0 bytes docs/seed_qr/img/vector3_standard_24word.png | Bin 1346 -> 0 bytes docs/seed_qr/img/vector4_compact_12word.png | Bin 1084 -> 0 bytes docs/seed_qr/img/vector4_standard_12word.png | Bin 1188 -> 0 bytes docs/seed_qr/img/vector5_compact_12word.png | Bin 1059 -> 0 bytes docs/seed_qr/img/vector5_standard_12word.png | Bin 1182 -> 0 bytes docs/seed_qr/img/vector6_compact_12word.png | Bin 1057 -> 0 bytes docs/seed_qr/img/vector6_standard_12word.png | Bin 1205 -> 0 bytes docs/seed_qr/img/zxing_screenshot.png | Bin 332912 -> 0 bytes .../12words_seedqr_template.pdf | Bin 236772 -> 0 bytes .../24words_seedqr_template.pdf | Bin 333718 -> 0 bytes .../12words_compactseedqr_template.pdf | Bin 52979 -> 0 bytes .../24words_compactseedqr_template.pdf | Bin 243395 -> 0 bytes requirements.txt | 4 +- src/default_settings.ini | 3 +- src/seedsigner/controller.py | 29 +- src/seedsigner/helpers/path.py | 1 - src/seedsigner/helpers/qr.py | 29 +- src/seedsigner/models/decode_qr.py | 186 ++-- src/seedsigner/models/encode_qr.py | 52 +- src/seedsigner/models/qr_type.py | 3 +- src/seedsigner/models/settings.py | 22 +- src/seedsigner/views/menu_view.py | 25 +- src/seedsigner/views/seed_tools_view.py | 93 +- src/seedsigner/views/settings_tools_view.py | 28 +- src/seedsigner/views/view.py | 2 - src/settings.ini | 3 +- tests/test_controller.py | 52 +- tests/test_decodepsbtqr.py | 56 +- tests/test_encodepsbtqr.py | 2 +- tests/test_seedqr.py | 106 --- 57 files changed, 2386 insertions(+), 1499 deletions(-) create mode 100644 docs/12_word_seed.pdf create mode 100644 docs/24_word_seed.pdf rename docs/{seed_qr/printable_templates => }/Seed_QR_Card.pdf (100%) delete mode 100644 docs/manual_installation.md rename docs/{seed_qr/printable_templates/template_generator.py => qr_code_template.py} (69%) create mode 100644 docs/qr_code_template_21x21.html create mode 100644 docs/qr_code_template_25x25.html create mode 100644 docs/qr_code_template_29x29.html delete mode 100644 docs/seed_qr/README.md delete mode 100644 docs/seed_qr/img/compact_12word.png delete mode 100644 docs/seed_qr/img/compact_24word.png delete mode 100644 docs/seed_qr/img/handmade_qr.jpg delete mode 100644 docs/seed_qr/img/phone_screenshot_compact.jpg delete mode 100644 docs/seed_qr/img/phone_screenshot_standard.jpg delete mode 100644 docs/seed_qr/img/qrcode_capacity.png delete mode 100644 docs/seed_qr/img/seedqr_plate.jpg delete mode 100644 docs/seed_qr/img/standard_12word.png delete mode 100644 docs/seed_qr/img/standard_24word.png delete mode 100644 docs/seed_qr/img/standard_24word_with_logo.png delete mode 100644 docs/seed_qr/img/vector1_compact_24word.png delete mode 100644 docs/seed_qr/img/vector1_standard_24word.png delete mode 100644 docs/seed_qr/img/vector2_compact_24word.png delete mode 100644 docs/seed_qr/img/vector2_standard_24word.png delete mode 100644 docs/seed_qr/img/vector3_compact_24word.png delete mode 100644 docs/seed_qr/img/vector3_standard_24word.png delete mode 100644 docs/seed_qr/img/vector4_compact_12word.png delete mode 100644 docs/seed_qr/img/vector4_standard_12word.png delete mode 100644 docs/seed_qr/img/vector5_compact_12word.png delete mode 100644 docs/seed_qr/img/vector5_standard_12word.png delete mode 100644 docs/seed_qr/img/vector6_compact_12word.png delete mode 100644 docs/seed_qr/img/vector6_standard_12word.png delete mode 100644 docs/seed_qr/img/zxing_screenshot.png delete mode 100644 docs/seed_qr/printable_templates/12words_seedqr_template.pdf delete mode 100644 docs/seed_qr/printable_templates/24words_seedqr_template.pdf delete mode 100644 docs/seed_qr/printable_templates/compact_seedqr/12words_compactseedqr_template.pdf delete mode 100644 docs/seed_qr/printable_templates/compact_seedqr/24words_compactseedqr_template.pdf delete mode 100644 tests/test_seedqr.py diff --git a/README.md b/README.md index 7ac92928..624c2ed7 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,6 @@  ---------------- - -* [Project Summary](#project-summary) -* [Shopping List](#shopping-list) -* [Software Installation](#software-installation) - * [Verifying Your Software](#verifying-your-software) -* [Enclosure Designs](#enclosure-designs) -* [SeedQR Printable Templates](#seedqr-printable-templates) -* [Manual Installation Instructions](#manual-installation-instructions) - - --------------- # Project Summary @@ -44,7 +33,7 @@ If you have specific questions about the project, our [Telegram Group](https://t * Responsive, event-driven user interface ### Considerations: -* Built for compatibility with Specter Desktop, Sparrow, and BlueWallet Vaults +* Built for compatibility with Specter-desktop, Sparrow and BlueWallet Vaults * Device takes up to 60 seconds to boot before menu appears (be patient!) * Always test your setup before transfering larger amounts of bitcoin (try testnet first!) * Taproot not quite yet supported @@ -78,12 +67,14 @@ Notes: --------------- -# Software Installation -The quickest and easiest way to install the software is to download the most recent "seedsigner_X_X_X.zip" file in the [software releases](https://github.com/SeedSigner/seedsigner/releases) section of this repository. +# Important Note on Software Installation -After downloading the .zip file, extract the seedsigner .img file, and write it to a MicroSD card (at least 4GB in size or larger). Then install the MicroSD in the assembled hardware and off you go. If your goal is a more trustless installation, you can follow the [manual installation instructions](docs/manual_installation.md). +The quickest and easiest way to install the software is to download the most recent "seedsigner_X_X_X.zip" file in the [software releases](https://github.com/SeedSigner/seedsigner/releases) section of this repository. After downloading the .zip file, extract the seedsigner .img file, and write it to a MicroSD card (at least 4GB in size or larger). Then install the MicroSD in the assembled hardware and off you go. If your goal is a more trustless installation, you can follow the manual software installation instructions below. + +--------------- + +# Important Note on Software Authenticity Verification -## Verifying Your Software You can verify the data integrity and authenticity of the latest release with as little as three commands (though moving forward you will have to replace the version in the following commands with the version number you are attempting to validate). This process assumes that you have navigated to a folder where you have these four relevant files present: * seedsigner_pubkey.gpg (from the main folder of this repo) @@ -143,27 +134,181 @@ The upper and lower portions of the enclosure can be printed using a standard FD --------------- -# SeedQR Printable Templates -You can use SeedSigner to export your seed to a hand-transcribed SeedQR format that enables you to instantly load your seed back into SeedSigner. +# Manual Software Installation Instructions: -[More information about SeedQRs](docs/seed_qr/README.md) +Begin by acquiring a specific copy of the Raspberry Pi Lite operating system, dated 2021-05-28; this version can be found here: -
![]() |
S3g`*E>&yrmc^ztk=TQmW#e28rEnkk4#e zpR&W-4Z^t7APw6MGETqsA+|Ym-EMb1Z8@0-iN>)pmmEy_b2-?*V2ZT=S8i!*v$Mk; zQWA~-mwx;&_Ve!}>p%DNzjXcoyIaXI84jNXa)sDaetM5{Nh!VrMYn6f!gTv9Jw_79nIJSm$0V} zM`S2|{*ZH#QO--GCFf|w+<`{u2Q1>&0Wm}o2K?|t6bWx%(vPI$V|kowo|FwbTJDao zx`rEFc{tL-UA1 UCH`8{c%?Q_d7}lM_ zQg?49AcUcOe5 e7$|6IclfOQ=DkZpg!KC!)FypbRolzcfOY`uI}f?iB~PsXm3H$)ut1kZ?}PwS@X zm7BKZCrPH>xbI*f2p3Z{(aq57Fl{SHI+9v@-@!=GM+|MEo29pC+E$pPmP+x!;gn#w zn4+ofJw28)ZAD2ssag*lP79`rp-puQ^yJU9-A_81dgH-=nDQB)xkXh^2j;Dx*x>uW zT@uD8_}UjYY{&4D$hyb?7XCTi7|e(i4D~iLfQo;P1_UHwsLIHIMc7 MjtnRVX>MUg#9*igkpTrD4G4(B zPz8|zX&?; W>@NCN`YFcdea4@h$zGeQMJae(@OG$23;L$QGRfHWXL z0Yfo^`hYYbKn_DOg8G0oA(#;|7>X9u2c!W3QW%N~)CZ&i0TLLB4Acjt3BhdOF@1Q< z8$1Sy#{}Xr)_9Bx9>aylY~V0`ILsRy28qK2;xN`Yj0z6Jg~M!YVEQ&NZ#FQ<4NTw$ z#(D#zvVq~+z-+8z`qnXT)-lL+OyD}kdL5&(j^SFzY^-7W)-Z3@FvvAb;2Oqy4WqJ# z;abCNtYZ3BF>h8e$W=_>D#m&hqq2(OTE%RvVER@tZ&uQyZGNd8!h2zZ{cys*_0v5= z3m)tE!b7uEG1a_-6l~iT(7|mB$jScH_GjAyn%uE~!nQ3SZ<(UQTOTK>t geoac=N+{2C8FC#Ab;eIQsA-Vwb1 z%NF0FhWj3j8v)lU-0azPG`__p%L8Bf!31A5!an(!X;l%5MYz3t!+6 Jvy*<@1w0`_j;vMP?ZyoGg=-m5zSI% zRo2Oqy!O)Ra+Ood6V}OkEqp&~G43Vv*Lb~|fP2jA+M-XZ3`L8o9#bynvaFv(nU@cU zP%T!7QZ00T$;KB^;pd~^Yq5w{y!>!B?j`p;&%z)|?U`$p@$S`u0^ZLk(;M=*@n^W~ z^=0FIHj*zFqrR-gHcH{-Q*$=*WL_8B^a};pJ!}oiJr+}Cc-=`&bhpPtR_6uPXO2!) z#=><21x974&rvL1hipBgSED{^uIZwmUJ8B{JAQKfeb3x5&wz0Go=IW&_xibwz@R|2 zhnxfH({4T8Y85$7YWJbQbm_I{sQqy~O-I`20__|xsEHm>OPlui)nTXh@W4QN%iOsp zVPCez+6`U%k J2UPMGww1CezZ@`Z|V8y zWar!@Zc^vl6Q`5ucR#ki?4qAMFDXv#VoO2&@Wg0S_>Ia(u$( b^AMXR@*@3ZpC_8; znofGl&m%+Tr+D719lu;t*@usRezo#-NTur1FK)LtR84Q0R}rN*DtpCq&R$JUA$h}% znNU`_${S+CbfY!BYgZtRA;_tdoV3jSd33>U1Wnac(5@QN;u+0dj85+nIifjdyCq}= z=U+zA7vCjuP`ZSPnQ(qJ!`b@M|4<63@{{nS)`= }JLC*`3+FR|{5^ z(D}wOui fw}-9Yhz(wh^+ccUXz4`g+z=YEZBdL-C0Ixq%uJQvkxR_!HLnl zK*B1luxZia+KKN)m{*>D2)|#IAY_jirz2*fry_KH@Y_t1PTG^b=TAw@{4%6uZdLfE z@}0q(ESV&%!1;}1klNINSx*( yEhicA54;~$upIu0sRe$Dp=1u@l za{PrkX#)!;)pL%g98XPOMM!2}*78HpjZz4aQW>^0_KyuB8PPY!m *M!#Lz3pNR6bW-rn(*ck_y@8wCWjo3e%m{i*t7vaH0cjDw@Ld4CB zg4 YTSJ*klcrv(=?D`MDqK2%e{SEay~= z%cr8;d?t9X{^^-X*s|}lka;YXtP^?cF<~hhDW{CIR4H?sFv3MpblLZ^xrtLN8HY}u zt1^g0=o@r8y}MwTKibIv<-uB05&bUX^dGo`%pn)}Q!^^)a&zAy_f{XntybEqe6d<9 zZ*V66W@U$M!W!RDDIUxzWmY0w^pk!~zFh$&TN17Sy)}K(ogBbCP;Z%OI0||bE@=WI zJ?}3V=BU#-9Y`!;aB8Ij-JR;%UdAZ>brbaQTY867Gq|B&|EuY}=h2~BG;h2@>Y3fj z=sUOeT^5VBIYY6Bsq7ZDrC>?7>#^E!teL%X)-jC^9T;lkBg_c?>ESKjZ`aLck3H04 zd39*ODDm8nN+^bd^ervbwY$eN8}FUnuU<=D3?fBko@Q#PT8>bTgL_^KS^MaSvr}O` z }N&7Ut-~3^^j}5T&NMXJD(ze-0^ce+iDF@u8(olCuFS>kKbh2gfOFS;I zhc1Q6F~0_+e_fGcm5(*mGs?xv>M`1CsE#`J>&02R1<;FMWt!;Yr;L!?@3>ad8ZC~9 zGS8M(ILOf4nEmY$JmggXOXc|?y8Fih;uU(*!)-vJYso;C&YqyAj)zEV=Fv6gK^LiR z2T6^7suZ7xMO(pGL>QX&!b)YFGJG` %7I)grYt<^ zogUkx8+?LMcPB~}?<74;$<%AOPrMv3TKjmZ$sNwE-XNNisnH<1lw>j`IC4Jkla2$@ zt3%PT<}xVQ#|AKHNzq#LM&0SDi*R_WZ1L)q*Cs5gjzeB%yd$sPb!8fK5lT)aJlUkT zRw7g6aJRt6Gx$jaTV|Q$!O5n^>?>-$EARHa@IF{&MPI=zkwG^X*=nEO)Q216LU3 zJ$sFBk--+VRpQ@lxy4IjBEvATwY~?h-*^
WD;Z6Ql;U?4_g<~{q d0uFwXsngYqYMu=b1a3|#C9PL z9 (XbYAv4qF4m#m=)0Y8!oF&jqmV9=NVtG3RsH_u$w)u ujGY8aa?Gb)m|aSVd+059Nfr6oO&=RUBA(31aFa@HG-vCe zq&Z00)rDn3)#)H}Ltc!&f=crH9+FbIi~jTMCJ4HOJ>D>WwzA#lPsug-$#YAGZVuL^ zC?<~hilp2uHXibaUGMA`PXrFF)pR);JkL3OL2Q2kbh%l2KwQeb-l8KEekgBn207?@ zumDR-nSbgO;i3?2dA`OKz4vO@Et1=AV4*!80Jh4q{rqgXyPeeT!Zp-DI`fXuh2dcs z&hGsI4-wAnXto-*@MCLT;^c@~83yTZ7gh<2Y&4w1rty7E%eM!m+LSJ)Pt7SNa9#Wc zOqY&H$nTCZ-c^SBcyDpPM~s}9d2Z-?nE8*EwDWq8HMjqSE$%;JTeH#H#7t4`${EJZ zRh^P)ED?jakV2Phg%J-&)FkZ_*X4U3%Gpsml#aNc60L_Wf-ZyN1HBI%mM>5Y-#aBi zJAALV={Pz)I*j%B!MObxi|d)sYb 4=XwN_C~)3W%nhekw@Qe>z_ka0qFC9Bzg#`(MDDTT3KfDWWXY-$eXVQv$Hji~ zL3rfF>Rt3?d(q<63U-8lQ= E8W1BZ1yjso0F~)b @51Ern8e^l z;j0k!YMP@YQv&cc!F2S#A{ FgpFYab8q*Z)g@$JgWTtLx# z%4?CY;|`EYs|(E0u_LZJHQpADh`7kA6(?vImFbADTv)sQyK_^Oz*iqt5YvE`%%g)* zrxs`hp*HG;X4ew_#lI0H`Zt(Q`J#y?qY{*FsQNlV2N dSWxrMMMg{*)39B4X|Omqpx?0aOmW?p2N|F9 zOWGUwVLdh3Vd~TZj!^BAITJD&W m170*g#@$)m0wb)3=O5?7*ee{YnyR2c;+Y#}AX2OLQ3%2G* zqJ-j~hW0rUCOZS7V=?6s5+NcKf5{;0!) dNoV)+t%2I#e1DcuFi!$}1k0tM!}o{rk7c)!t$Ws;V=3F5$-aEb)) z;<4R1HllaI$zuH|;_=!0vwQ`0yG`s*ICQ;pJobUoW)3rV*?$Er^v_1icwSn;9#gBt zs-Lu%Dst#?Rs(H+S!L9j-j5?p9sWvX#_@qP8FPtv3~?~A)Y;rrrsJP}yfWVq8X<5M zd-g=WqB09|Q8Qh?b|Je^8l 6 APsb#(uuM*)=}yc;QCtF!SF9`!?ifN~S$% ziAW2-EUFmMC&!~XVn15K9Z(*o{?9ark)-|i8w3poZU;HG=GuP(bwz7gF@cI^)tx`p zWi1>c8C_HH9DczeWb6o)iqc=TGHhqjV}$(Pn<)a7mr?(yldu0^i>W=uruo;a57|ZU zrZRqz?HENr&Aq#stR?nzf+vEuVl!QwVIKshnj#&lluU7et2yp?qdc$T+D=6Udt`xG z bwr|#knt5KO=+ou?YBBY4nG8K@r(&x9)9K_cJ3E!zaWql<1=_48x%$|5m5Tg! z2R-?p9ir#uo8V7uxfvd{a9&9p8ZR$zb6CSBaEgX!uxaRrUs MSvC!{}d#}&Ib(_qm#LZiyU0acnq{7C)7{owzrScKj?8=pR|k#xgk!+Pp(Zu zkIN2!mOj+{7W2wQHLzrmkA{0?|7qvuzjJXf*js%h5Vs80o&;OI_rpKU*(Rxz_dtVX zbY4~|Y )`Y+NIrx+c~KMWw5{7)7fNWw*~ zkvgm~gAu?T4q8&$e670xV$%VsKT0;2WCqKJ!4K89R ot{!Jj# zQ1<9Xv)(A-0;(zlV4h36-GmD#u(HU^tmJk?lC$vAjb@b`FRvfHj>=12&4G&$MCF~4 zsh!;Ji0C#jO4ltwUhm9mXj8D@5O(>DEw@aY3FDp>JSdEOs5^404V~frSP1&5m$c@d z@HXQnd>o8<(X(L8E7P^ro^}V00PN!PC<`~{mBEOMT0iYLw0bhAm`hZdS6sD@tlY&K zW*O~u>8{O?e^&5?(lD77t?sAU6G;WT&l(j_j`X@mcSlA$#%V8qcWtEEfE=mau3t$y zEYWF$@3M8$S5)2sCm?&$wG0_sUMi4&(V`l=>*WXS^sZ5vhop^F`k6W*>rJN3A$v%c zRGq{;qFx+MEgfiZY_gqy$1~4gC4H&3OVfzy64=F@#5fJ%x%VHrUnFySK9hc6T`*$3 zu87XDNkQ;zwk2nFgzJ1lEO*JvJJZLc&P~mw!81uO>Pssq*G@OMokvg{vd@LPmc_J- zw;z5V>i=SRw_`|yVzM&FTT#{s5w{S=XP@}wm1-~9%uO?o>|ttuLJ?E_+)7+?8JJWy zf@j}&eR=hWnrHZYYcBpS_va8R*N~e}Kkb(1css5o6mq1Ft9vf^aJ8#)824m u!bXyry#TZeT`$@`x;s-7*O4V!0Mh}+)#H5 zx#*sI$3@m_fG4H-UOCJoMzFymDf9hXV6&UK=}GXia%Nv~nRhoq$V9)0Og ^k& zM}cc!;q`CxCmmR3e`LNA7{R?qfAV&hMRI;Djmk*Mb^GRRm{}0jemK!0Eo#;1SwoVn zMUqhTdu-+0&x@5KGGhq_N&?A@4SAZd(tzp*8>^kYPw>p^%-5HyC|as4`&Z63`G2*Tr5_L1->irQd*CGUC%^PxoVSe2{Pb74;5(lxY`;wo z+zkfa#k&8m)L@UwIgm5%%}SG%O;21(Q=@y|(309um7c)LC(EJ(l&?oNy#R?{TubYg z6A6er=R2vWNQGxeUVL~zb?12p->^*e!!09Re~p4@y%Wv&5`UvJL#BI;Qcrqr8S#RJ zVv1*;C_Xn_Z?d9QJcGHfVYDCdAi-of!=$ISYTf=TlfrXS+_UvYwKUt6`niL6a~xda zSC$FEuP|~82^O!KiyuA )Afc9Y=EgNAvUo~+xiqy~%QWqxxp zs(;Vyf8uAzm+yuAjE$jJ0cY2j0*>rt06n7WL+7+2RCDIR&s58VLK1JodkH?s` CHM*cpXl+NvvJKZG-B3yp{7MKk5Y+w(m$_61EF8_bFn{G13@d!^fH*Y^Cj_aPzc zXu{9sDchlW*N-EIt${N+F)t4BHo@vY6TO}ue=LSzEcaH&-f&CATw9!0%jP+;H;+z+ zlE#83s#jo+l>%K=w+}4_kvEZ<=tImafr~s@UzhlfyQ49OSb=d7Z7eW%;SQZiaI7fe zj8Pz;^=`%Ra{>rGm81A?s0e;;i(;em0PMCsrgu43 T4JX1 zCI}Xy$n(psO5$C8OQn(O&4zlZ6}Ey$)Tm;hDi~$uRYP}1zSVC_KDS9i_|6znPkUno z4T2%$@MXcN9JzZlNhy;zDBR97pC>pI%F|`PlA7n0!9M@Mg?7k-pH@=0i?OH~;sF2- zqZfDiMgy7jIs5XG8`=%h{}nXs>h4T>m$dZso5~LA%`_ltb1Vq4i7ygB#&^zP5M~&< z4IDDM=xsv5M2i3*#P4i2s3_W*Y?uwy0CHIL7&sDyX6a#=ITbv_j(ZDyD4GTu0!EjX z=c9oi(H~97BvdXFZ2Hxj8{xnUBTp)g`w2D!0$0$P^lR?DhoG?eNT`nl@!}ae%~*3K zf;9ov8-jHWp`L3eI9d>Uvt7c_{UXa#p5`VenmLTG>Nb``x!dER+ f2!AAwkTuAt7OxI%J50m+mq zLYm*)_BuD&Yo=WBWhW9B6sdv&io})lonXCyZNs`uVkm@|4MYl%ugB9tkBWt=<-f%u zoIN{ii7?+RBPgvWFvftr2HG(54+IrH Hqs`)CE85{Te&;x1IsS=Ld>D|1D1xuuoAZ8FrGBD)I#{^ znd7_5a}riHHm532x5kr#y(Af-03r30;1wbT#FP_-5-AltOhEW|TI#NmKl|ehqtS>N zX#>@pq7yZa6ZC%L(!;_PB-=^W$N@sC`sv{q1py&lxQ!^^3oDZprQo=36r$e6)BqsT zHeo+GBnbqO7IK?3`yRWpC>Rd$a$t-CNZ*O>O@ SNnO=~TA_dks#3n|5#IEDWPtYs%;Im8@@)GUD>Nw?Fxw>taK4S9 zaLhI%^~L*TN~WgTjri+?8A&M&X4+5CMn@nQaLh!G3}8M5&d+(?D#M5908vIsTd91& z-#`&UJJL^}9_$%~=-Gn|%u{q>oTX>mX-L{V2(9c&7_*y=@jKx0V-?h>;S3S$*aAE{ zq;)~5vLPrx2b F^NRgGsLzxTjTqkjCxj|QVKYTO088> zVmnwmCwS70_C_mKd}(%j?sYB@RLd32I?3%Ez(SIl+G;_A6l_38t1jL~Ai&Js`MX0z zQM=*UHa=unc&_17&@LYG9bXz$ BK1o#K7b8#)(kJp(SY$Naj_?A z@7~Dn^9JAv@I-p;5Y8; aIje5I1bb01iBe@%w~( zIF>@XIpM0iq!8?TVrnEl`}=X8KjoHSAdB2Y(MY96Kv@A7Y*^UKG-Q7}fF7_CEgkIh zU+od{F;?0X_D|_j0oKQ6+QS3*v^HT&!$Ja|R?nSWdI2i~V+*q`0y`tgSXmqI*lUwj zE$F!bWBVh660X(*NW#P`E-)c*Y4-6ATZJ;!1N|Z6wh5@oLTvDiAQqHrv+s7eG<1DR z)?btom 2Dy~1Gv#m7g?iWsH~fprr;#XNe&56J%H}06aWPSL=%1u z_iOPb2NX3jArmlGU`x~mRYOVUK+Z4Wf4v4p0i8mwgv>AAjaxDXK}R_xAktewQ~gE< zL&F^6((+EemoW7MBQke$P_c>!)X?q&klxbm)8)|aF=q0NF12Jb;Q-Q%L!g(H>c~Z6 zbFqQu((B@YfX2;{>CmmwZ~-b;LQ?I=1yJ+9+2CUPq`OAf{=|(4t+6xKd+qr7+O zE3{)B-)L|P0UK90)6nn8kxD^|+c8k%Kr!DO2yp!5oUi`&P_~!k55nHp>;^r$#AmB= zjU9Z7N^TKYg-xY|7i-{@bUtC*gB~I#2-YAaA}6?IK9pt{0iJ1Nw<;ANsgYhR2LKBV zb>yG>-j|?VM3&DAaH3qzpjd|(;DxB4WywhQl6HeM=X{@`UNWU6JHomEHf9v)>T`Y= zBf73JUS$S{!eH@sqwf0TRL>G-qqMvR>oV~n>bpkik23$*^n?vB=}!4ikQ)liYFZ`e zWE=;?pnM9&oi_0d2oG=*NtaL2$7&Yx^SAA~$5VFKh^WXoXOPTOs9ivpSdgsWF6!8b z&Lb~5XK)Nl6`f}zumB)1hI|4on8=c>A1x}}h&Iz4+vr?xowH!br*EC{{~>ASK{`zX z`Qw~=%{SZpWt5saf9Oo{3=>^$k`psExKBUEt??oi@-N1dAF9GtmPL^2@ss!Fvst7R zLpS&x@ggcBcaAHwzm`@y`p`Os^rAADV_bR-hM0P)uM&vz=+nT>^_Efch`HH3&Ik|b z@}*Ld4?7J;<1 OW@?>}NPfLgm-#abA zF?`QhVp(FQroW}w%IRF_!)Cqtr@K{7bDH
N+WcqUK2&w Q*G2uq7kzwXs$Id K#nX#EE(H~@ZSs!{N>l`(x0)mxJ#IsfTbi`#RQO_?H*MV(m&Ivs0 z-U3|$Ls(rITk4U_o$}!x7ezmt{^_Rzl+nkZMxtKHh@wHJBt5dt@`idw7ZVxDju_Rv z`7qw-5Yi c1f__nK9R0EyKtN>CWiq@>)1F>h65 zU19rDibbu-m8qo6HP#XBb^ } zY&MejMG@S$M9MT#TZ-GN(Fd;?uN)%t;7L6aMAElR>iwO%HJ0btAAySVty`Z6i1}oj zzt9?ix+IxOQiZo!-dK7&UB&wQ39%(qLw8m3yzvU9_4-fW@6IV|1FJlALpsB5Gr4o} z%f{C@*Na|%d?--mYt_A>Go09_0q*6M?G7^K7A;P#)N?*!?S9zrkWRA`-+Iwe8OvR2 zV(~E@8Uyh8$@Hr$#w#hUR5Re(OqEHZDi(aV$)zU|VbN8r9Dpxc_aOojgSm5x1C$D+ z0j4 N3)A+hUJn3@Lx|@V=%OmI(sm;!<$$t=w;U rVhCQeBMP_TnYdpEFLQT}gOSPoiC`X+Qxg&>#GbihWL?6%?LB wwkXd90Em1w$`r$X zUW}NYvz{IJ)dv~{HP;uiw_0b| oZ>Q{!=R2%6k6jt^q}u3UKGhj#>`@>|gn6Vb~QV zEOUX{ZJfg?Ym=f@G9@5<6c%tIKk5!eaCzO{tUUQT@9+iy9soh9ah0VNaIDJNN}Pi$ zYt6uo0JcM%lm#yk+nJ@cZkrMWec7GzP5lvvV4*Q$nR!nA+?pA}e2&}fbp++~g*W~{ z;gSDsNcla0>i^W8aRMXeEo8~OWj%d;nHymBzj-#m^!M!MSsXvYdpl?*>HKO9FtU6M z`4s6!L90wsT`j8mMIY?gDm!@tOF bEK7;C~5vpJ;|^hdls{BJOdh;ees# buqo>W36IR5_AjJR|03Lvp)g2ft)#NKgn+td{O`E0LGPVf(DUzRPadEFf znGe@Vy^y4L|Im?xBQP&73x0F@fG1$EL%h5|cL+ZEO?U^K>Y=iMn1z6!YVm(KJKwjc zU8NC-d!Yt)4Ou@57%4iNpu;Et9hCkZbP&(>(t;>E&cR@%R)_)yCf6U14}_c$x_=XM zK>i!%0Og`bXbt>}Y{1m~<^5Qvo@=;3aK^ShGKB#ymW01dum69LLr%ocvIHb!N&7M1 z)q0u9yuhQt`#Ct|pqtTqyoovJ>4bMf;}+Dgg*v2RD=So6y*0KW2NO-*M4^xO3V@O| zj{)9xQzq&Wl9Bf>)5|d9xZ#sF-~ovw%ctl%IA6#1fN3fl;oD9V2^8Mo@R&*RMdNm$ zmjxL(Yu?c^x*suOyRLZ-GWw61%-pFmZ=sZxkIAq)z3V9*{HF$cm|j@zvhm7MBDY8K zVdZ?gv*bJLcF`r^=kLg27jJWLY8y(3QhW`U@y&@*&)-NS?H3Ka2mAq&b`-tePieV7 zb5#={MPAX{bT)`HR338j!6RRtsw2$LoI_<9Mu42Ia|NzQ-A&_Uptc2hTZ`DAa!z1V z6GQ2o0>A@thSn6c7h|0uKYwR}_S) 0>LRKTs$q^5-8dW zPZFNHT9aMR-3~Mt3ER*8V{Tqj+6bV5h8#d!&a*axibEpy6U~>DfTg;L2LgjW9rE;N zq<1DJc Hw5 _m;)W1WH zqr0Ha8eahY4qFSFL`88MdxDmsha0 v9%W?sF)-Bi^nz z@fPeDQ|~i-E)f7gRmiiPPe8Jiw5$7aV2tqQ1<0~^Xd((`it8r7a L;2{NrhoL>Q$d&EPA8-V) ?}{2D&(lMW zE_p^S0O=Xsa-g{k#F7W~o&MesAYm^6oMR!uWB?6VBF4-RWjt@|t3#Kb_N$!sTt0mn zxW2+?H{%f^XD+rf CH&K-Ht@iO_Dm@GsLaF*o`ykv- zSU{L~0wT$*eT;z5Z~lK8!3f-QAZ?P{0&gG*6abo_ArJ6L0zeO8^Wdov2(gJaWC4(V zbkptM()JP-2}t%14d;9yRM#m?rJ^Cg@=!lPGL}U!uC_rkfMnkR^&pfhPvEi_!Rh}K z0S%hCjV& Mz75PPL5s(2^gSi-6E(Zy3#w=<>D_S3?{c933|K)1_mdEc^) z$8)V_tOvv1pw6LN%PGzxQ%5iG`Q^z!Ek~VG8B;$)bkTvI?wO{^8X70E>BD#Y@^Taw zJWEgzK$jYeGP*q<(k^}&{|UT*sxWR&MWR4LSveJnT*7*%(V%yw<(=21Wr_ZRSy{QI zh_xe6=KqSh?~2W{a!4aZn+M)Kx-D4#Qh`p1*3`fLu$gur*xtH#i+>xDH}pG2j(A48 z=q9!%Kc?Ow-mN|e{C@~lFsA@6_rc_(37`~yYG~3`pbJz7ZR&${qA$`dtnbiE_0(n8 z3mE_;l&Fs|mT*YZK!d`h 0M7B*xQw8^bMEtbELP9VrnVRE~SpDs |5)yW7T&)(5rYR>ATs>4ijF*0|cdEj8q%7jwKz_0>P#@2#PTKeqUv{ zr2 HPP zI*{27iNQg~G+C{x6xvKAc(H>b0SO7AG c>9AH|3bT S0@n-GHTa+Z_M6x~piFCbO3Qgx;> zHzI@=15YHLlK_AOOD^H)l=+XhpKVWIZUdI*CGSy{_|J-K6Al5Z@70)ej+R&=t08K& z`tp Lv)R&VF2fujQi4*M;Sd1^KKYJ|!YPHA>rEks`FzNYKA3Qn$4eUS^F5$9DP zA`yh=_J}Cf?Gd7@b&c(6Wd5Dih)LpaH`}BUw+{VXbg|WE)bD?HYw78r%A+gs`wmo^ z9t|ioKj!zB{vr5w;lOq6V-j~LZmSOP|Ig$Pq&0h{yWBUMLzzzz9GU_G%cOY#LPQfl zh){mWuIeHj@C5quYg}FRJNoj<<$Nr~768#qNoO?E$G1H76gsmCi2m&}tN)^UAOrbA zjL&slh;-S~JWQNYBl-WSd5GKiQzLu;a47^rW%KAC Quq?X|%;K=}TW55))dXM> z1|)FDKA?Laj3iJ##N)Q-j*YOiqFKG%A0i=-V)bc3LM9~zfStC>{tu-C)YTBrLp N^S z-p|Ryq+I;;nSd7{oHfx}9Y)l RbhfDFoVtYC$o-~E!r5xx6t(d7@ek4DIZIJHd(=mEkv zCsRFNJt@g+ggCbT3+tNU%4SG(zPrK^RH=(PR96e BTk@pd+}Om;}%q|CBbAk0~G^9m7906oEg%DHuWYzPdO{fmQ#{LJ`nD=afwC zh9P|n;LJAJI+oWF3sZ@q@6vBUZt7o-cSO*xHx@f*a3Lo&Jjp~var5%d`MEl?0ZR1~ z!JD9luu8x&5|9wZSEnGPaz};q#e0=_f^nx-1TgS_%DNI=Zeq5Zo;{?Ix=wV2Am!1b zAjC~JLhWhTt`~M=?d@QlD}fLON5vo)`wtiVcO($ $FRP)dW{l$ zM49hIJ?^Mqd#NTXY&OA9URIE`5*<^?K8mJJ)H(Y48BpX7N^{4!^p9bYMracNz>9q( z@kzKwbETuHpoN5Z5Ut;RM6mapMAcF9*YV)Uq<=u`1UIBxpjyqlt_rZ%o@H>x-FIb} zh{`Q4XLqUqbvqTqye@g5Op)KKWo~)x1n$^*r#$L~UtvKj;3P}c(R1hk`p@?xWELXz zjAL8=PNY;plU=O!oc;kk((*L!AAeh-fmS=v{c^f(7Ad3#Dyye{GsOp;UCutqF$x`o zf)4mH0I%W^Lz5(a>wvGgFZh#c)#22F@2raJjvi<@-4>isZyes1B(Thus;G|oFtPKo zC~sB+ln6R8vJC(bOMJupr);Y*!+ZmHxxuXOiUwBz$|mR<@pSrMIRN3*4ghc#QBEv! ztzF@DRp;!JRDr0)yIprT=8lGLE(247g}wTqRQbiVI{-k>_b{6!a8~zO`!N)FFj$?w zG!FkRNBFm5f!zt (;Y+9;}@?&8U)VW(a7!o`_27t9%tr2dtfV72xB8;jk+TF zO5^E4aY`dY-?Xzn3157%g8Q5cPrND;oX8XqHbZ(13 *VXcM~y*M?R|qTEpw-2z@|C+NoH(b?Pbhu3a1D38De3ew 9Fz#87(5gj;r@aaAxzhIY-iSz`|~x$bcAj50NH3h9ucb=t`OLX$go9 z--uVo+qdFdi?2oWfmyUgu>XwHM=SiSMUNA7`C#BpDHNMiwS{p2m$h|@txTC}lCjTG z7>NF%v$QpKdmfED9_AP9 I^e;JIwggdgcs!a-s&nNmbp!8%kob3kJ-QJa zXLh`COK~7J99kOC LDH%~C856=RJ_SnOF z?GM<>#HfmC`}hf^**=NO4 m+BtQ@`L)tNyY~`nZnCQ!Xgv;VijTEYIMj-(S7@8N7ZK`pB|veIUY` z$XpNUe4N4?{K6pv){46N5A|>C8GlY$KKK1>1!>Ud`ekFD?>d3>JPXbnVy~uO)%L`% z=2@{+$FH)fjoYsm#Si7GR-hk!9P*bq_{t|@nr8vsytH3!_!Y_TY0)39c-)upx(1G! z!bw4**<95+kb~;D*WGC7^yxc)3C=;Cz!;tdX^?Ej7}aC-gUI{>Cy3;?2W@i2M_}nI zjDVT_Gpi?k1Q9vppKWFq@OeZ&ef4k0@n3vKkHduHu>&1jgfa1%pqw}3S9^a Ub*O5{pYXtmjIU+=#yYmGRGwqCk1%Uj@!lJLv%$yndIV5G#m=CE~+aq0o=o6 zCu?)>=`Gn7m~k&GzBYMwp4MA3US^}^(M$v{Jb_+AJ5OmU9|K-51N5ck`5doK#i|b! zZp`Yfaye08lXOGISPP!GbSF#_-tuBivSu3VcE65zmK9JIgMxtV93xscEYSOddKjKw z{_?dDDkM;qdB%AHDs}>*VgVlt-V>AL1!PoTUqF9Ds yMW zk{jIR;bpCij+zaIr?wqz^HUM8d}G(BdUS$cynHv-+hRQ0`B)ly$q)CDsBq; ;o?%@m`IoroIyE&pzcFdrk9Q z{i$WPTR`sb25 bwAFN1BRfpq~0NJ?H)30^KduR8fwd6B;waq}Bu*RE0 zq_J1Juj<;b#zKI-J2Tytfa4O0u|gm@Xf|MEZz~8*?@V4!Hh=N`AR@|D7StMKL6yqY z?2mBiy!{t@=IZnUWxvfAXa2J_ jE-p&V!m~A7?PJUT 3=Pe3T-Z60?iY zTQ6e8d$q?IBZ6mzH&p^`?8{#t&rZnnPICOZc{4s|MtUD~0=zN1(9kXjdiO=iC#-yl zC#S`W*v}jKWXp)-IkvCrfB@j_{K>nunUiD=H=yPK*|NCv`)K}R1UzZ$6Ki|IYge3; zZ kZpl(UIpa;JqvFK*=_R z+K!VPCl98DtP{|KKg;$zQ~F&<+7WOXoI8)sb9d^${@bjEp8cP^7O_$lH)gNDy%x7? zbC0F}@>;&OdaBj$7-#Ox?a4hm6^7odGL;LJYm1zL$_0@#TO9`8Gy~1$o$f(ae_>jB z?y~L|W_u-eqGea-Rka_la4)7qgMNzHRAeT04#17l-{78#*U?AWUw^1t?()&&F>+0p zC8P84eh7i7H@x&3PZ~+-*~zi_o)BC4`;xqFr=Ou55Ovs?2Ck(%(R51BWa8fq;yhmu znc*T;XS|kQy6G$1{`}4@fdGQIIv $Llm_s5XN2 zmBV`LF7w`+3Z%6?f56Q6ff%Ve3v!C|;-_l*K@)nZrGYr7F6UX}VsiuP(c`4oXse2Z zSrS|C7JJC4yHhT{-Qys@;yofpZBu449_N|6^j#@sy0-0nZneSQ!+k#sAMWy?l>fRF z2{s=zT+Ve-%B0dszcY^PFoVXCyYoIb^sig5DZLF(G;e+LkS&EXz&~Axi;}Tf@x{li z*BsHBXnj7+3Gvb9GmHii&ur20< #RtekCpBbW3h*QLj_-|aDX z=);IX|2ID=UA N0Zw(*8R~jzgbJuU#(1^ zXYc6z%kZR_#h?8&uyv)wCzme%ZP$YhGykg8$Kj9aeFA17`C*U?sH+?QnBL~Sn#-ou zFIagndr7?8v+XZ?eP8w6WBnbxIuG(1{$m2QAN0_zNNBvS@w5YDy4{^x*|H+8^}?LT zhZ~=8ufc}0=cDu5&FFf!eMHoV+>pbqtIe!bw{f+m?dtlbG@bR>m%5hTVgIM&)4EiQ zs#`xLx94@yXKKOwF5VM+ItNvCf)AIVYQ4QPdb+TVi>jSSi|}Sv6pwwHbKKFR-QldK zyU#U0I=9OYE#d!euGQbIp4MaB)z6%!JH@p0>9r~A^sLp+-F)UvYF(pGgUx(+%#q(` zZF3lUqYH-F*M)_?S&^DrIN7yj(tg_@c%ojndytNqz7-W6i3!7h9X1w-`46r*-YN zW&i57mH!P%Yp|m8+B!qh+OFua*6}WUcnz-IAnfJVosSOv?htWh%fsz?KP^q(m-cnF z%8m}r>UDQr(9^JDp!dGCPpa1NZeIHD>Tkb}TA1SQ*xdJeCBHO>72a!Ww}g+bYwLaw zEH*iBTa^5IVxRfKz0#`*cCIV7CY(Cv)#|+s4Q+mD0?TQY?=4HtU-k3)J_X&6ES$wS zM|$U`EvY6tCN#U*{Yc@}gua)eENe0{gTMOkUuyOh2j?%(sFk$Nhq)J6^23(BKQPV0 zyB}FF%jg{DeW;Y(=s0o1{jHnN9P81_!{(R6Z~Q~| ;w=;}w4_STL^mf~m zu(&m&z8viLCcR6G_<+f4`c)6euD>*)O}qTQDaW=wo;&nfiwpaE{b(6;?5}-(!2vB= zIhu>d7GBK~#y;9{?@s=sXZP;?cJk$(oMn@RJ-f%ed7C=2FtKK+8=WzBSWa%tWAVh7 zV~>p3exOtHR->B47Tg=V=EB_Y6pt@U+84hnOnp7fC2{1}cRoJ; S^MkxwqD& ze(1Si)Q;k*hmZ7gpMA{z=jqQpigSN>_}cT?g_3>4`}IffQIX$wBEM0EE_+!@(1>^o z<=)dA3GYY=v?RtQCxlrNDR|Wkyx}FRmnDe`#V>bp53-~rQSN>*(9m1{^pQXP`a8IH zN=fR{D+#^x1upHvQ0Sd4f$(+~_s(Nr={&rC5oEsQ#AlD6@B53KePqB5$NAlSl&_ zA82ifO-iKD>s$gI+ yJ&;EQkMeai0@aqcFAf9WhA<#(OfJ~sQ;;3PODp*=+=nFUtYHw zx Jr0{?>bjcCiX }4oCVzoAnihHFCqQGkz?Xyj-5G0Txh|%R??Cw`91_@*g z!klz!6T2A-k61&Sq>Zq;KnjC>haI-56@&yb2(kJ+jJ^G8MInKVLRbQQ+Qcdfr0|F} zv`Jb%s|%*Ea++fkwQEHofs8_|E)QdOzgkg9Amaq)q*I&Ny-|3?8rmdh9IFecuox`Z zwoO_q3JIjYW_5WOyZhCOLL%weFejbb#O{s4Bi7I+=|_yVW?WE5oUWDyb16foj1mgN zc?AGl4CyHe7E%(Rp`=JmNg;%id> UTR4lkVV2T<-o=0VX=CGmyFP+Ah8 zoB)*sRM{x|B`|JyuXuAT1!k!H!6z=d^apDk124rT5H_dFyRmk98ComyvWk9f=~@x- z?e14A^0EqVjwG ;a%@m#?w3ZyuETdlQeFp@r-)8*Ybd%c@h 5U nr6@@r53Smw jk>3#kZp=0dDi6yiw_Bk1xl zcK53lg?Mr PWMu@tSSrO-xw&4q>0S0MBNfmR_ zrylHv{(0+RNVFsBa?PThu9?%othAywi#h32Pig%6r>#c;n`~K+6l{euvyq|~C3$H| z@ nO>(C`pUfk(g%4NNhCdg1<(Cof$T#fxBr%^;%+O`__nMaY!2M{}vY@I@-NR zV_-;Vi3d>bI57|+?v1P^wdP5LZZzm}s78am9#sPe(25GX#Hw70 z-D5qe>|RluHtO60Z|@#>PAf%HtOpq}D?4;AWsy?KC+$ZEk^X7jlUb6{_C^GiznAo+ z(!FAJ+Gw;D sc3UpkflY2A}4m=Pa6D0if4-D5|o z);;O(acnM^t+dyJEI3HnIwipmN{U2qHo}0rgHP-uD9KuY{$CDF@TBXrHK{wsg(b(p z(H95z?&jD?%GDCv0+m?Gz%ZWTl!P>_B{mpHKxxDW50J>pQymnxLKrV*N*N`N($0EH zu{WGj>~5y;24>h^BVC}_T}m<7ZsgsO6eF>gcK1<=-DwowfCe=p=>oig3jUHVQ0(-e z@MaF!_aU{!n-SnIxurA+X{DG_taMVmbbq9kItnkw!78Y9fnueE!iyoWZX#WPi}~=E zbXygbR!UJRq@>ynCDkY>Tr~)!aCQtIc-Bxj+d;-6Rr+M`1b@p~mx2ly4aRivDNi=} ze3MaqMnXCW6_j9s9Z5ZsV_-Xy0WQPr$P-TZe#(a4Jt`&&j#h;d=aRrgh+|4 4ChKX(6A9B3Kv 3#{ z_i_+kWJnPMp8jGX `kWKc?Bo$ev xbnNd+lnP(V`Q4oT<7NGVJpn+zGqwji5y zK1$IRWRn2}*%oAz@dZf**<`puP~mute71$;3&%@J!3fzFWRp&ADB6N-GE^bkf^0H! zA*mpn3}i?u$R_Q&SMUYdWSB#?1=(cOLsCID83Ykj*mPZriOQ%nigGf_B)X$fI4T99 zL~9f`N--))My+KCOOTN#E=9N`8CuKum!yK&GB_rwAhwK*NGgae10|9QV#~ORpdx1} z!zhvpw^T+|Bo%I{46;Zn+|p9aMUvs3mO?O+jCEm+F;K|>6lxt)Xvk)PY&{=oNVI@# z?eNgDq_i?h-YVoVrSz7BY~(Rq>D; G)Rr~1;N3 3X?69PQXg}YIXW&9@reYah=qkS{pN(Bez6g8sH!TT zj+ZIYZ h6}&Ckvvc|E{ue9zeSc|e$iS*z z1LsfPIHdO1x7k5ARs}VxG@@&V;0~QW`ZjM|+z6+R>j$lKNjJ3Uv@_z9tVO@N_Fms( z^}4Vr-0}@GS43oec4_zJXOF&iTzu!dO#WqVc1iZjrK27W&&_^W^g- 4g=aa4{TOa?)T>Wi@4$i*EPq=Ox|IYEV0cj9RvMy)81w^s&KptH70$2 zW_o_Hx!IHGiYE@=%G pHTSC9di=X?A58D$y=(IDzdkQMmVA2mpr2hEuv=s6 zt@V2?HZT@-;ohn5?Xo28 Qo-^ zVCIn2T{p8^yyONn*pYq3`1$3zaRC>zm+oty+jwLB6M=U}rk`v#G2NdUIcH|&p6|Xo z+E$!#`#Zy#VeHHe!(wOO4~WR0YH~W-@IrrMzqSDd%sBoGy=Cg1tvSKd+ `1sVY-A@J%_3^p4uk-oeS``M5cAPe>QT=DhyJjrAx% eu122dNE!!rl0=D+PUVZdDfea^ R$ZI^X>abRDTqGJG)g@%%g_u zpLhlizw=9BesP`8Km6wExt-r%{o>=y@WoGV1RU9D4)@P_Ugy%oZXS!1BQhIK2+r)? z%yU8SA@!bZWIdYGqYgXeTncq8tUL37xa-}5vktpw^xV~FOw%!o5}mGXY+4j{Wb(;5 z^`{L!HR00DNj(;9+w9lqdh?INo##y~aoYWgdmHa_`r!Q;0k7t{oTz^NVnOaIFBk8F z)6a}qvZi6x9orxJ{I) G_?S4Nej}R_o PQKl+q43qfp?%K1*!I!Rh+R(;op-bz`sDJNHPQW->|QiFBOqn&nU5-X9c!Q7 z!eQ&Sjms{*cO~Wa=2ve e>6ZLX1x*OyKq`+V)vgpU9dIF5kR+pv|EA zR|4Mc&~IyV!#CBF@`9&L9{T#)uYOP9eKn538LeVc_pjgB?APZ0jx%C@?+EW0pS3mc zgZzq3o@P_kBesrQQM=*F!0Lm3xf11hIyWn1OH{j^{`{NC+i$ouc1~{Z7hae$#H-WD zYRi7;_}${`FX|jTF?saBpv~WW9^^f!cJj!))^CnJ6w=JpNFmYm(1-bE+vUe#BjdL4 z4abC!Jv_Dfhkq?T&}F9Ii SC=julGTsVFCv=@ycw^dp@VdBv*pM?r{pF9j${Ns}wMITI#|9x59_U1df z2EX-4o%UtHni~z8U61JZ!YTL7zyVjH^LNdu9N4nY@^>=p`gCR5E`00N*TtjHboOw_ zvmtM*EZtl9^NXr?etz<}V&|0;uUwyA)aGdJ`d1!8eg6r3*Dum$Kd&~^_s^>F>**tn z>oykMs@2I&n0G7MoH2XM2eo37t`<$&HQ%F2&7)y{mwz%$Z5#LClM`<5dAi&m=F+O9 zOa3=iM>Xs=vAF1L=a-&dTMiVut$y=jU;AButj}NQxgv1hHw76*B_%BewZB N*qa^sc+1;oX8d`OHU)-e%VH>sIfbE2n+e&KkKqw6*hWr?}B$I}J_}a{`M0?7g6I zyOKw%f6D0bq~b^~<0 hCQl%@^;RHhSw`k zNp0yKIADq4*KUU2i{}NVWF(lJD@V_&@tfH#yk}^|YmeLhm1uUUcgojz?P%8aO7pzx zocP%Jn}q0kyEjj|@X@j#*|)zPGao&A_RyT5io^cgc6rP0xJT)}Lc-LpQ_r97SO0`1 zuV0JwI&`&FHO`;v+NtHt&eP+k9PxIo*YuiCzk0j6uVQ?EnR3&?cV_x2r@Xd{cb cZk*Tt`+zd0Tr z$xz;4a5IRY?*1stgd~qM8Qn}K6V!+yw+p3t-VI =-2@|~jXbDHWJOR5 z&lo^qA|zoZT7=ueK;ekv;3|#=9-Q&->!H#c%CdqRZ1^yW{ZVx&l;%V?6VLHXf6#Wv z5InBZnkvpsWd)aqD`~WtRR$~j&)PTSfZ_c~k5U~P?ey6+=c@@`Rp-w0u6gCq=Jw;l zTCFp0ntr3<(-(6xXYYG|`>VW?qqARSXS3%!{-?>lb@e}ee(3JFIg2NF-;OuTb8;EH z>bg&U$n?`pkNBP4>l{AxVn%PL3MJp&uXmv0&N*K-`po#YF ?@wdI0B z+HyYYOY(oqxH~d=zH|1>%GncBnwcif@15B(qREfbFXgz_ycFCoL6~xU-r*mPU1+%W zVc$U$1Di*MU0yJx=J;mEhn`rqdd0T~W1d|t?6~?(^#hG3j8M LGpcqc` z4${N7(0`#a#X-s+LH$ibMQZ#@tW2YM8Z9j=Xt0KYN>2(J%b8F*ub_cdqV*pIjpkWY zk5|xGnnOpA6*NX<(XxPu-pSSV?0=R7g|Oz?)d}qrpX~X)G$1DXyh?4wYb)G?M|9 zy_GZ`{F#a-BEO?-XJXI_iWSX>O72!P8kNAUXeLyLwW6_TwZ@9Zp%Sx_CKynGMoBXX zs0wUF6Hy+mqzQ~#FW~l7IuK}7%(uE$W6T1px<|CYq3Ve8TG4>YM@pKYF=jzT`M9#3 zsG(=kh}PV!+R>;`Y(+Dv?Nrpzv&f;^jpAAc)^=1n5Cya&N6`*4brj4LG{(T7#RV&x z+BO&ijmp@{c3`?{exgb?#$ZB)Q027-L8TYQNULRou&k07$~bBouO5q$L5m^EevO<; zFN{%C%Vsp8T9MVYf@;n%G>xie%67B?EhSjdj4FS?fMZkX1vX@>X`E`FGc*{2st*&Y z8Cmr!sOBI87pwHb7*z9!VT`Id$S^!A5-a;)7}Y#t7*6Fk7zQwx>ROX(9sxVm9AsD; zRUDQ5vIe!SfooT{<53CQ>RLu^Yb>XlgNz6`$og6yz@{~g1Fx;38PTGovR{r<>zQL! zevRP-wD@UtEkImr8a$#?JwAh4_Ym`};{x1Dv<+3+2X9jOXNKp{5{t5(iAKwhRy16V zRnmA-t!Ds!s{3JLHQF(1-D|`Y#w4iyfyso*^~$jbta^S3P#9LV)6l74LRD-lej0Wq zXxP1IL8SYej?F)&xO4k3=n-6w&gdf*)%zsIUbK#cfqPnnz11%4=y8dT57|#zL^Q zrh#bIcEEB9yGlwhL)Mhw0F~xmQ6musgk%mCzTq2mfu8M#a~J#_kYQqyIUxz-LKfm0 M@8IInDWLQJ0Uck2&Hw-a literal 0 HcmV?d00001 diff --git a/docs/seed_qr/printable_templates/Seed_QR_Card.pdf b/docs/Seed_QR_Card.pdf similarity index 100% rename from docs/seed_qr/printable_templates/Seed_QR_Card.pdf rename to docs/Seed_QR_Card.pdf diff --git a/docs/developer_tips.md b/docs/developer_tips.md index b068e1db..db988f13 100644 --- a/docs/developer_tips.md +++ b/docs/developer_tips.md @@ -27,36 +27,3 @@ For the seed in the snippet, you should see: Take the output and paste it into a [QR code generator](https://www.the-qrcode-generator.com/). Start up SeedSigner's UI to import a seed from a QR code. Scan the new QR code and you're good to go! - - -# Advanced developer notes - -## Backup an SD card - -You can back up and restore any size SD card but the process is a little inefficient so the bigger the source SD card, the bigger the backup will be and the longer it'll take to create (and even longer to image back to a new SD card), even if most of the card is blank. - -You can restore a backup image to an SD card of the same or larger size. So it's strongly recommended to do repetitive development work on a smaller card that's easier to backup and restore. Once the image is stabilized, then write it to a bigger card, if necessary (that being said, there's really no reason to use a large SD card for SeedSigner. An 8GB SD card is more than big enough). - -Insert the SD card into a Mac/Linux machine and create a compressed img file. - -First verify the name of your SD card: - -``` -# Mac: -diskutil list - -# Linux: -sudo fdisk -l -``` - -It will most likely be `/dev/disk1` on most systems. - -Now we use `dd` to clone and `gzip` to compress it. Note that we reference the SD card by adding an `r` in front of the disk name. This speeds up the cloning considerably. - -``` -sudo dd if=/dev/rdisk1 conv=sparse bs=4m | gzip -9 > seedsigner.img.gz -``` - -The process should take about 15 minutes and will typically generate a roughly 1.1GB image. - -To restore from your backup image, just use the Raspberry Pi Imager. Remember that you can only write it to an SD card of equal or greater size than the original SD card. diff --git a/docs/manual_installation.md b/docs/manual_installation.md deleted file mode 100644 index fc1f0b44..00000000 --- a/docs/manual_installation.md +++ /dev/null @@ -1,336 +0,0 @@ -# Manual Installation Instructions - -Begin by acquiring a specific copy of the Raspberry Pi Lite operating system, dated 2021-05-28; this version can be found here: - -https://downloads.raspberrypi.org/raspios_lite_armhf/images/raspios_lite_armhf-2021-05-28/ - -Best practice is to verify the downloaded .zip file containing the Raspberry Pi Lite OS matches the published SHA256 hash of the file; for additional reference that hash is: c5dad159a2775c687e9281b1a0e586f7471690ae28f2f2282c90e7d59f64273c. After verifying the file's data integrity, you can decompress the .zip file to obtain the operating system image that it contains. You can then use Balena's Etcher tool (https://www.balena.io/etcher/) to write the Raspberry Pi Lite software image to a memory card (4 GB or larger). It's important to note that an image authoring tool must be used (the operating system image cannot be simply copied into a file storage partition on the memory card). - -The manual SeedSigner installation and configuration process requires an internet connection on the device to download the necessary libraries and code. But because the Pi Zero 1.3 does not have onboard wifi, you have two options: - -1. Run these steps on a separate Raspberry Pi 2/3/4 or Zero W which can connect to the internet and then transfer the SD card to the Pi Zero 1.3 when complete. -2. OR configure the Pi Zero 1.3 directly by relaying through your computer's internet connection over USB. See instructions [here](usb_relay.md). - -For the following steps you'll need to either connect a keyboard & monitor to the network-connected Raspberry Pi you are working with, or SSH into the Pi if you're familiar with that process. - -### Configure the Pi -First things first, verify that you are using the correct version of the Raspberry Pi Lite operating system by typing the command: -``` -cat /etc/os-release -``` -The output of this command should match the following text: -``` -PRETTY_NAME="Raspbian GNU/Linux 10 (buster)" -NAME="Raspbian GNU/Linux" -VERSION_ID="10" -VERSION="10 (buster)" -VERSION_CODENAME=buster -ID=raspbian -ID_LIKE=debian -HOME_URL="http://www.raspbian.org/" -SUPPORT_URL="http://www.raspbian.org/RaspbianForums" -BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs" -``` - -Now launch the Raspberry Pi's System Configuration tool using the command: -``` -sudo raspi-config -``` - -Set the following: -* `Interface Options`: - * `Camera`: enable - * `SPI`: enable -* `Localisation Options`: - * `Locale`: arrow up and down through the list and select or deselect languages with the spacebar. - * Deselect the default language option that is selected - * Select `en_US.UTF-8 UTF-8` for US English -* You will also need to configure the WiFi settings if you are using the #1 option above to connect to the internet - -When you exit the System Configuration tool, you will be prompted to reboot the system; allow the system to reboot and continue with these instructions. - - -### Change the default password -Change the system's password from the default "raspberry". Do do this, use the command: -``` -passwd -``` -You will be prompted to enter the current password (raspberry) and then to enter a new password twice. In our prepared release image, the password used is `AirG@pped!` (without quotes). - - -### Install dependencies -``` -sudo apt-get update && sudo apt-get install -y wiringpi python3-pip python3-numpy python-pil libopenjp2-7 git python3-opencv python3-picamera libatlas-base-dev qrencode -``` - -### Install `zbar` -v0.4.6 requires `zbar` at 0.23.x or higher. - -see: [https://github.com/mchehab/zbar](https://github.com/mchehab/zbar) - -#### Get the `zbar` binary -``` -curl -L http://raspbian.raspberrypi.org/raspbian/pool/main/z/zbar/libzbar0_0.23.90-1_armhf.deb --output libzbar0_0.23.90-1_armhf.deb -sudo apt install ./libzbar0_0.23.90-1_armhf.deb -``` - -#### Or install `zbar` by compiling from source -This step isn't necessary. It's only if you prefer to compile `zbar` from source. The whole process takes about 30 minutes. -``` -sudo apt-get install -y autopoint - -cd ~/ -git clone https://github.com/mchehab/zbar.git -cd zbar -git checkout 0.23.90 -``` - -Configure the compiler settings and compile. -``` -autoreconf -vfi && ./configure -make -make check -sudo make install -``` - -### Install the [C library for Broadcom BCM 2835](http://www.airspayce.com/mikem/bcm2835/) -``` -wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.60.tar.gz -tar zxvf bcm2835-1.60.tar.gz -cd bcm2835-1.60/ -sudo ./configure -sudo make && sudo make check && sudo make install -cd .. -rm bcm2835-1.60.tar.gz -``` - -### Set up `virtualenv` -``` -pip3 install virtualenvwrapper -``` - -Edit your bash profile with the command `nano ~/.profile` and add the following to the end: -``` -export WORKON_HOME=$HOME/.envs -export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3 -source /home/pi/.local/bin/virtualenvwrapper.sh -``` -Then `CTRL-X` and `y` to exit and save changes. - -Now create the python virtualenv for SeedSigner with these two commands: -``` -source ~/.profile -mkvirtualenv --python=python3 seedsigner-env -cd seedsigner -setvirtualenvproject -``` - -For convenience you can configure your `.profile` to auto-activate the SeedSigner virtualenv when you ssh in. Once again `nano ~/.profile` and add at the end: -``` -workon seedsigner-env -``` - -Optional: If you're going to be testing new code on the SeedSigner, you'll find yourself often needing to kill the SeedSigner code that automatically runs at startup (we'll be configuring this further down). As an extra convenience you can list the process id so that you can then kill it from the terminal: -``` -ps aux | grep main.py -``` - -Save your changes with `CTRL-X` and `y`. - -Now when you `ssh` in you'll see something like: -``` -pi 297 65.4 9.7 74096 36736 ? Rsl 09:26 10:29 /home/pi/.envs/seedsigner-env/bin/python main.py -pi 857 0.0 0.4 7332 1876 pts/0 S+ 09:42 0:00 grep --color=auto main.py -``` - -The top line is our SeedSigner code running. To stop it, run: -``` -kill 297 -``` -Where `297` is the process id listed in the output above (it'll be different each time). - - -### Download the SeedSigner code: -``` -git clone https://github.com/SeedSigner/seedsigner -cd seedsigner -``` - -If you want to run a specific branch within the main SeedSigner repo, switch to it with: -``` -git checkout yourtargetbranch -``` - -And if you want to test a pull request (PR), for example PR #123: -``` -git fetch origin pull/123/head:pr_123 -git checkout pr_123 -``` - -where `pr_123` is any name you want to give to the new branch in your local repo that will hold the PR. - - -### Install Python `pip` dependencies: -``` -pip3 install -r requirements.txt -cd .. -``` - -#### `pyzbar` -Note: The `requirements.txt` installs a fork of the python `pyzbar` repo (for now pointing to the fork in Keith's `kdmukai` github account [https://github.com/kdmukai/pyzbar](https://github.com/kdmukai/pyzbar)). - -The fork is required because the main `pyzbar` repo has been abandoned. This [github issue](https://github.com/NaturalHistoryMuseum/pyzbar/issues/124#issuecomment-971967091) discusses the changes needed in order to support reading binary data from `zbar`, which is required for our `CompactSeedQR` format which writes byte data instead of strings. The changes specifically reference the following PRs which have already been merged into Keith's fork: -* [PR 76](https://github.com/NaturalHistoryMuseum/pyzbar/pull/76/files): enables scanning to continue even when a null byte (`x\00`) is found. -* [PR 82](https://github.com/NaturalHistoryMuseum/pyzbar/pull/82): enable `zbar`'s new binary mode. Note that this PR has a trivial bug that was fixed in Keith's fork. - -### Configure `systemd` to run SeedSigner at boot: -``` -sudo nano /etc/systemd/system/seedsigner.service -``` - -Add the following contents to the text file that was created: -``` -[Unit] -Description=Seedsigner - -[Service] -User=pi -WorkingDirectory=/home/pi/seedsigner/src/ -ExecStart=/home/pi/.envs/seedsigner-env/bin/python main.py -Restart=always - -[Install] -WantedBy=multi-user.target -``` - -_Note: If you'll be testing new code on the SeedSigner, you'll want to omit the `Restart=always` line._ - -Use `CTRL-X` and `y` to exit and save changes. - -Run `sudo systemctl enable seedsigner.service` to enable service on boot. (This will restart the seedsigner code automatically at startup and if it crashes.) - -Now reboot the Raspberry Pi with the command: -``` -sudo reboot -``` - -After the Raspberry Pi reboots, you should see the SeedSigner splash screen and the SeedSigner menu subsequently appear on the LCD screen (note that it can take up to 60 seconds for the menu to appear). - -### Further OS modifications -Disable and remove the system's virtual memory / swap file with the commands: -``` -sudo apt remove dphys-swapfile -y -sudo apt autoremove -y -sudo rm /var/swap -``` - -## Local testing and development -For those who will use the SeedSigner installation for testing/development, it can be helpful to change the system's host name so it doesn't potentially conflict with other Raspberry Pis that may already be present on your network. (For those who don't plan to use the installation for testing or development, you can skip this portion of the process.) To change the host name first edit the "hostname" with the command: -``` -sudo nano /etc/hostname -``` -and change "raspberrypi" to "seedsigner" (or another name). Use `CTRL-X` and `y` to exit and save changes. You'll also need to edit the "hosts" file with the command: -``` -sudo nano /etc/hosts -``` -and change "raspberrypi" to "seedsigner" (or the other name you previously chose). Use `CTRL-X` and `y` to exit and save changes. - -### Set a static IP -Your local machine that `ssh`s into the SeedSigner can sometimes get confused if you're connecting to different SeedSigners that are all identified as `pi@seedsigner.local`. In this case it helps to set a static ip and just `ssh` directly to that instead. - -First find your current `nameserver`: -``` -sudo cat /etc/resolv.conf -``` - -This is the address of your local machine that is connected to your SeedSigner via usb (or it'll be the wifi router's address if you're using a Raspi with wifi and are keeping it enabled for `ssh` access). - -Set a static ip: `sudo nano /etc/dhcpcd.conf` and add to the end: -``` -interface usb0 -static ip_address=192.168.1.200/24 -static routers=192.168.1.254 -static domain_name_servers=192.168.1.254 -``` - -* `interface` will be `usb0` for usb connections; `wlan0` for wifi. -* `static ip_address` is the ip address you want the SeedSigner to use. It should match the `nameserver` ip you found above for all but the last part of the ip (note: the `/24` should always be included as-is). -* `static routers` should be your `nameserver` ip. -* `static domain_name_servers` should also be the `nameserver` ip. - -`CTRL-X` and `y` to save changes. - -After your next reboot, access this SeedSigner using its new static ip: -``` -ssh pi@192.168.1.200 -``` - -### More convenient `ssh` access: -Power SeedSigner devs will find themselves connecting to a lot of different SeedSigners. This can cause headaches with `ssh`'s built-in protections; a different device that uses the same `ssh` credentials is normally a potential spoofing attack. But we're doing this to ourselves on purpose and so we can carve out exceptions. - -On your local machine, run `nano ~/.ssh/config` and add to the end: -``` -host seedsigner.local - StrictHostKeyChecking no - UserKnownHostsFile /dev/null - User pi - LogLevel QUIET - -host 192.168.1.200 - StrictHostKeyChecking no - UserKnownHostsFile /dev/null - User pi - LogLevel QUIET -``` - -The first entry prevents warnings for the default `pi@seedsigner.local` connections. - -The second entry does the same for a specific static ip; you'll want this if you configure all your SeedSigners to use the same static ip. - -`CTRL-X` and `y` to save changes. - - -#### Bypass `ssh` password -You can also configure the SeedSigner so that you don't have to enter the `pi` password when you `ssh` in. - -run `ssh-copy-id` with the same values that you connect via `ssh`: -``` -ssh-copy-id pi@seedsigner.local - -# or if you're connecting over static ip, something like: -ssh-copy-id pi@192.168.1.200 -``` - -You'll be prompted to enter the password to complete it. - -_Note: If you don't have any ssh keys on your local machine, you'll need to create a set with `ssh-keygen -t ed25519 -C "your_email@example.com"`. Then try running `ssh-copy-id` again._ - - -## Disable wifi/Bluetooth when using other Raspi boards -If you plan to use your installation on a Raspberry Pi that is not a Zero version 1.3, but rather on a Raspberry Pi that has WiFi and Bluetooth capabilities, it is a good idea to disable the following WiFi & Bluetooth, as well as other relevant services (assuming you are not creating this installation for testing/development purposes). Enter the followiing commands to disable WiFi, Bluetooth, & other relevant services: -``` -sudo systemctl disable bluetooth.service -sudo systemctl disable wpa_supplicant.service -sudo systemctl disable dhcpcd.service -sudo systemctl disable sshd.service -sudo systemctl disable networking.service -sudo systemctl disable dphys-swapfile.service -sudo ifconfig wlan0 down -``` -Please note that if you are using WiFi to connect/interact with your Raspberry Pi, the last command will sever that connection. - -You can now safely power the Raspberry Pi off from the SeedSigner main menu. - -If you do not plan to use your installation for testing/development, it is also a good idea to disable WiFi and Bluetooth by editing the config.txt file found in the installation's "boot" partition. You can add the following text to the end of that file with any simple text editor (Windows: Notepad, Mac: TextEdit, Linux: nano): -``` -dtoverlay=disable-bt -dtoverlay=pi3-disable-wifi -``` - -If you used option #2 above and don't plan to continue to access your SeedSigner via SSH over USB, it is a good idea to reverse the steps you took to enable it -- those instructions can be found near the end of [this guide](usb_relay.md). - -Please remember that it can take up to a minute for the GUI to appear when powering your SeedSigner on. - - -### Optional: Run the tests -see: [tests/README.md](../tests/README.md) diff --git a/docs/seed_qr/printable_templates/template_generator.py b/docs/qr_code_template.py similarity index 69% rename from docs/seed_qr/printable_templates/template_generator.py rename to docs/qr_code_template.py index f0ed2f27..0ddd092f 100644 --- a/docs/seed_qr/printable_templates/template_generator.py +++ b/docs/qr_code_template.py @@ -1,9 +1,11 @@ import math -def generate_qr_template(qr_size, num_words=24, block_size=5, show_timing_marks=False): - if num_words == 24: +def generate_qr_template(qr_size, block_size=5, show_timing_marks=False): + if qr_size == 29: + num_words = 24 word_cols = 2 else: + num_words = 12 word_cols = 1 html = """ @@ -31,24 +33,18 @@ def generate_qr_template(qr_size, num_words=24, block_size=5, show_timing_marks= td { padding: 0; margin: 0; - width: 12px; + border: 1px dotted #c8c8c8; + width: 11px; height: 12px; } - .qrcell { - text-align: center; - font-size: 0.35em; - color: black; - width: 13px; - height: 9px; - } .qr_table { - margin-left: 4em; - display: inline-block; + margin-top: 5em; + margin-right: 4em; + float: right; } .filled { background-color: black; - border: none; - color: white; + border: 1px solid black; } .col_name, .row_name { text-align: center; @@ -56,19 +52,13 @@ def generate_qr_template(qr_size, num_words=24, block_size=5, show_timing_marks= color: #aaa; } .col_name { - border-top: 1px solid #bbb; border-bottom: 1px solid #bbb; height: 2em; } .row_name { border-right: 1px solid #bbb; - border-left: 1px solid #bbb; width: 2em; } - .first_td { - border-top: 1px solid #bbb; - border-left: 1px solid #bbb; - } .col_block_divider { border-right: 1px solid #bbb; } @@ -76,11 +66,10 @@ def generate_qr_template(qr_size, num_words=24, block_size=5, show_timing_marks= border-bottom: 1px solid #bbb; } .no_border { - border: none !important; + border: none; } .word_list_table { - border: 1px solid #aaa; """ if num_words == 12: @@ -92,7 +81,6 @@ def generate_qr_template(qr_size, num_words=24, block_size=5, show_timing_marks= float: left; } .word_list { - font-size: 0.5em; text-align: right; padding-top: 2em; padding-left: 1em; @@ -133,7 +121,7 @@ def generate_qr_template(qr_size, num_words=24, block_size=5, show_timing_marks= y_names = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,X,Y,Z" html += """ """ - html += f"""
" html += " +""" + html += f""" " @@ -141,46 +129,37 @@ def generate_qr_template(qr_size, num_words=24, block_size=5, show_timing_marks= for i in range(0, qr_size): html += """ """ for j in range(0, math.ceil(qr_size/block_size)): html += f""" {j + 1} """ html += "\n""" if i % block_size == 0: - html += f""" \n""" html += "{y_names.split(",")[int(i / block_size)]} """ + html += f"""{y_names.split(",")[int(i / block_size)]} """ for j in range(0, qr_size): - html += f"""· """ + html += f"""""" html += """