From 0255762ab48e8482bb9dc72ffafa7ee960458d43 Mon Sep 17 00:00:00 2001 From: Santiago Lezica Date: Wed, 17 Mar 2021 15:28:04 -0300 Subject: [PATCH] Release v2.1.0 --- .gitignore | 1 + README.md | 45 +- address_generator.go | 19 +- electrum/client.go | 2 +- go.mod | 7 +- go.sum | 24 +- keys_generator.go | 72 +- main.go | 428 +++++++--- recovery-tool | 7 + recovery_tool.go | 55 +- scanner/scanner.go | 71 +- scanner/task.go | 61 +- sweeper.go | 89 +-- .../github.com/logrusorgru/aurora/.gitignore | 34 + .../github.com/logrusorgru/aurora/.travis.yml | 9 + .../github.com/logrusorgru/aurora/AUTHORS.md | 8 + .../logrusorgru/aurora/CHANGELOG.md | 59 ++ vendor/github.com/logrusorgru/aurora/LICENSE | 24 + .../github.com/logrusorgru/aurora/README.md | 314 ++++++++ .../github.com/logrusorgru/aurora/aurora.go | 725 +++++++++++++++++ .../aurora/aurora_black_standard.png | Bin 0 -> 37415 bytes .../aurora/aurora_colors_black.png | Bin 0 -> 3328 bytes .../aurora/aurora_colors_white.png | Bin 0 -> 3066 bytes .../logrusorgru/aurora/aurora_formats.gif | Bin 0 -> 16212 bytes .../logrusorgru/aurora/aurora_grayscale.png | Bin 0 -> 5198 bytes .../aurora/aurora_rarely_supported.png | Bin 0 -> 5283 bytes .../aurora/aurora_white_standard.png | Bin 0 -> 23857 bytes vendor/github.com/logrusorgru/aurora/color.go | 398 ++++++++++ .../github.com/logrusorgru/aurora/disable.png | Bin 0 -> 626 bytes .../github.com/logrusorgru/aurora/enable.png | Bin 0 -> 606 bytes .../logrusorgru/aurora/gopher_aurora.png | Bin 0 -> 16387 bytes .../github.com/logrusorgru/aurora/printf.png | Bin 0 -> 3308 bytes .../github.com/logrusorgru/aurora/simple.png | Bin 0 -> 2131 bytes .../github.com/logrusorgru/aurora/sprintf.go | 68 ++ .../github.com/logrusorgru/aurora/sprintf.png | Bin 0 -> 1858 bytes vendor/github.com/logrusorgru/aurora/value.go | 745 ++++++++++++++++++ vendor/github.com/logrusorgru/aurora/wrap.go | 558 +++++++++++++ vendor/github.com/muun/libwallet/LICENSE | 67 +- .../muun/libwallet/challenge_keys.go | 63 +- .../muun/libwallet/emergency_kit.go | 13 +- vendor/github.com/muun/libwallet/go.mod | 3 +- vendor/github.com/muun/libwallet/go.sum | 26 +- .../muun/libwallet/sphinx/sphinx.go | 15 +- .../pdfcpu/pdfcpu/pkg/api/booklet.go | 149 ++++ .../github.com/pdfcpu/pdfcpu/pkg/api/nup.go | 26 +- .../pdfcpu/pdfcpu/pkg/font/install.go | 4 +- .../pdfcpu/pdfcpu/pkg/pdfcpu/booklet.go | 416 ++++++++++ .../pdfcpu/pdfcpu/pkg/pdfcpu/bookmarks.go | 73 +- .../pdfcpu/pdfcpu/pkg/pdfcpu/boxes.go | 33 +- .../pdfcpu/pdfcpu/pkg/pdfcpu/configuration.go | 1 + .../pdfcpu/pdfcpu/pkg/pdfcpu/create.go | 174 ++-- .../pdfcpu/pkg/pdfcpu/createAnnotations.go | 11 +- .../pdfcpu/pdfcpu/pkg/pdfcpu/doc.go | 1 + .../pdfcpu/pdfcpu/pkg/pdfcpu/matrix.go | 60 ++ .../pdfcpu/pdfcpu/pkg/pdfcpu/nup.go | 366 ++++++--- .../pdfcpu/pdfcpu/pkg/pdfcpu/parse.go | 6 +- .../pdfcpu/pdfcpu/pkg/pdfcpu/read.go | 17 +- .../pdfcpu/pdfcpu/pkg/pdfcpu/stamp.go | 44 +- .../pdfcpu/pkg/pdfcpu/validate/xReftable.go | 2 +- .../pdfcpu/pdfcpu/pkg/pdfcpu/version.go | 2 +- .../pdfcpu/pdfcpu/pkg/pdfcpu/write.go | 4 - .../pdfcpu/pdfcpu/pkg/pdfcpu/writeObjects.go | 2 +- vendor/modules.txt | 6 +- 63 files changed, 4699 insertions(+), 708 deletions(-) create mode 100755 recovery-tool create mode 100644 vendor/github.com/logrusorgru/aurora/.gitignore create mode 100644 vendor/github.com/logrusorgru/aurora/.travis.yml create mode 100644 vendor/github.com/logrusorgru/aurora/AUTHORS.md create mode 100644 vendor/github.com/logrusorgru/aurora/CHANGELOG.md create mode 100644 vendor/github.com/logrusorgru/aurora/LICENSE create mode 100644 vendor/github.com/logrusorgru/aurora/README.md create mode 100644 vendor/github.com/logrusorgru/aurora/aurora.go create mode 100644 vendor/github.com/logrusorgru/aurora/aurora_black_standard.png create mode 100644 vendor/github.com/logrusorgru/aurora/aurora_colors_black.png create mode 100644 vendor/github.com/logrusorgru/aurora/aurora_colors_white.png create mode 100644 vendor/github.com/logrusorgru/aurora/aurora_formats.gif create mode 100644 vendor/github.com/logrusorgru/aurora/aurora_grayscale.png create mode 100644 vendor/github.com/logrusorgru/aurora/aurora_rarely_supported.png create mode 100644 vendor/github.com/logrusorgru/aurora/aurora_white_standard.png create mode 100644 vendor/github.com/logrusorgru/aurora/color.go create mode 100644 vendor/github.com/logrusorgru/aurora/disable.png create mode 100644 vendor/github.com/logrusorgru/aurora/enable.png create mode 100644 vendor/github.com/logrusorgru/aurora/gopher_aurora.png create mode 100644 vendor/github.com/logrusorgru/aurora/printf.png create mode 100644 vendor/github.com/logrusorgru/aurora/simple.png create mode 100644 vendor/github.com/logrusorgru/aurora/sprintf.go create mode 100644 vendor/github.com/logrusorgru/aurora/sprintf.png create mode 100644 vendor/github.com/logrusorgru/aurora/value.go create mode 100644 vendor/github.com/logrusorgru/aurora/wrap.go create mode 100644 vendor/github.com/pdfcpu/pdfcpu/pkg/api/booklet.go create mode 100644 vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/booklet.go create mode 100644 vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/matrix.go diff --git a/.gitignore b/.gitignore index fd49033..b8919f4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .idea neutrino_test +/bin diff --git a/README.md b/README.md index 4abaa77..c94c682 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,45 @@ ![muun](https://muun.com/images/github-banner-v2.png) -## About +Welcome! -You can use this tool to swipe all the funds in your muun account to an address of your choosing. +You can use this tool to transfer all funds from your Muun wallet to an address of your choosing. -To do this you will need: -* The recovery code, that you set up when you created your muun account -* The two encrypted private keys that you exported from your muun wallet -* A destination Bitcoin address where all your funds will be sent +**This process requires no collaboration from Muun to work**. We wholeheartedly believe that self-custodianship +is an essential right, and we want to create a world in which people have complete and exclusive +control over their own money. Bitcoin has finally made this possible. -The process of scanning the blockchain to recover your funds can take several hours, please be ready to keep it running. The scan starts at the block your wallet was created to make it faster, but depending on when that was it can take long. +## Usage -## Setup +To execute a recovery, you will need: + +1. **Your Recovery Code**, which you wrote down during your security setup +2. **Your Emergency Kit PDF**, which you exported from the app +3. **Your destination bitcoin address**, where all your funds will be sent + +Once you have that, you must: 1. Install [golang](https://golang.org/) 2. Open a terminal window -3. Run this code: +3. Run: -``` -git clone https://github.com/muun/recovery -cd recovery -go run -mod=vendor . -``` + git clone https://github.com/muun/recovery + cd recovery + ./recovery-tool + +The recovery process takes only a few minutes (depending on your connection). ## Questions -If you have any questions, contact us at contact@muun.com +If you have any questions, we'll be happy to answer them. Contact us at [contact@muun.com](mailto:contact@muun.com) ## Auditing -* Most of the key handling and transaction crafting operations happens in the **libwallet** module. -* All the blockchain scan code is in the **neutrino** module. +Begin by reading `main.go`, and follow calls to other files and modules as you see fit. We always work +to improve code quality and readability with each release, so that auditing is easier and more effective. + +The low-level encryption, key handling and transaction crafting code can be found in the `libwallet` +module, and it's the same our iOS and Android applications use. + ## Responsible Disclosure @@ -38,4 +47,4 @@ Send us an email to report any security related bugs or vulnerabilities at [secu You can encrypt your email message using our public PGP key. -Public key fingerprint: `1299 28C1 E79F E011 6DA4 C80F 8DB7 FD0F 61E6 ED76` +Public key fingerprint: `1299 28C1 E79F E011 6DA4 C80F 8DB7 FD0F 61E6 ED76` \ No newline at end of file diff --git a/address_generator.go b/address_generator.go index 07eba7e..40d5b54 100644 --- a/address_generator.go +++ b/address_generator.go @@ -29,7 +29,24 @@ func (g *AddressGenerator) Addresses() map[string]signingDetails { return g.addrs } -func (g *AddressGenerator) Generate() { +// Stream returns a channel that emits all addresses generated. +func (g *AddressGenerator) Stream() chan libwallet.MuunAddress { + ch := make(chan libwallet.MuunAddress) + + go func() { + g.generate() + + for _, details := range g.Addresses() { + ch <- details.Address + } + + close(ch) + }() + + return ch +} + +func (g *AddressGenerator) generate() { g.generateChangeAddrs() g.generateExternalAddrs() g.generateContactAddrs(100) diff --git a/electrum/client.go b/electrum/client.go index 6e165a2..862eccf 100644 --- a/electrum/client.go +++ b/electrum/client.go @@ -71,7 +71,7 @@ type BroadcastResponse struct { type UnspentRef struct { TxHash string `json:"tx_hash"` TxPos int `json:"tx_pos"` - Value int `json:"value"` + Value int64 `json:"value"` Height int `json:"height"` } diff --git a/go.mod b/go.mod index b886fb4..99a2a95 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,9 @@ go 1.12 require ( github.com/btcsuite/btcd v0.21.0-beta - github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect github.com/btcsuite/btcutil v1.0.2 - github.com/btcsuite/btcwallet v0.11.1-0.20200612012534-48addcd5591a // indirect - github.com/btcsuite/btcwallet/walletdb v1.3.3 // indirect - github.com/lightninglabs/neutrino v0.11.1-0.20200316235139-bffc52e8f200 // indirect - github.com/muun/libwallet v0.7.0 + github.com/logrusorgru/aurora v2.0.3+incompatible + github.com/muun/libwallet v0.8.0 ) replace github.com/lightninglabs/neutrino => github.com/muun/neutrino v0.0.0-20190914162326-7082af0fa257 diff --git a/go.sum b/go.sum index baf2afe..78724e4 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= git.schwanenlied.me/yawning/bsaes.git v0.0.0-20180720073208-c0276d75487e/go.mod h1:BWqTsj8PgcPriQJGl7el20J/7TuT1d/hSyFDXMEpoEo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ= github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82/go.mod h1:GbuBk21JqF+driLX3XtJYNZjGa45YDoa9IqCTzNSfEc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -192,6 +191,8 @@ github.com/lightningnetwork/lnd/queue v1.0.4 h1:8Dq3vxAFSACPy+pKN88oPFhuCpCoAACh github.com/lightningnetwork/lnd/queue v1.0.4/go.mod h1:YTkTVZCxz8tAYreH27EO3s8572ODumWrNdYW2E/YKxg= github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qpwh6l+AkQEZwU= github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0= +github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= +github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 h1:sjOGyegMIhvgfq5oaue6Td+hxZuf3tDC8lAPrFldqFw= github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796/go.mod h1:3p7ZTf9V1sNPI5H8P3NkTFF4LuwMdPl2DodF60qAKqY= github.com/ltcsuite/ltcutil v0.0.0-20181217130922-17f3b04680b6/go.mod h1:8Vg/LTOO0KYa/vlHWJ6XZAevPQThGH5sufO0Hrou/lA= @@ -205,11 +206,10 @@ github.com/miekg/dns v0.0.0-20171125082028-79bfde677fa8/go.mod h1:W1PPwlIAgtquWB github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg= github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/muun/libwallet v0.7.0 h1:FfPt+L7WN02qIgG9oJgVc9wBs7fw9w6PgHOsEI56o60= -github.com/muun/libwallet v0.7.0/go.mod h1:CB5ooFhTjbewO1YlP74Hnlf1PHWZhTU58g7LU3c2+fw= +github.com/muun/libwallet v0.8.0 h1:TtMsKr5O8OWUW5khZHpptokkKuPkXhOThLZ/ck4jXPM= +github.com/muun/libwallet v0.8.0/go.mod h1:fzmqBImU+ktQ5YDCM1MwXBl6vARC+73/ILGJMU/u96w= github.com/muun/neutrino v0.0.0-20190914162326-7082af0fa257 h1:NW17wq2gZlEFeW3/Zx3wSmqlD0wKGf7YvhpP+CNCsbE= github.com/muun/neutrino v0.0.0-20190914162326-7082af0fa257/go.mod h1:awTrhbCWjWNH4yVwZ4IE7nZbvpQ27e7OyD+jao7wRxA= -github.com/muun/recovery v0.3.0 h1:YyCXcuGx+SluVa0bHsyaXiowB67rdpJ6AudKv8QGvEE= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -218,8 +218,8 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pdfcpu/pdfcpu v0.3.8 h1:wdKii186dzmr/aP/fkJl2s9yT3TZcwc1VqgfabNymGI= -github.com/pdfcpu/pdfcpu v0.3.8/go.mod h1:EfJ1EIo3n5+YlGF53DGe1yF1wQLiqK1eqGDN5LuKALs= +github.com/pdfcpu/pdfcpu v0.3.9 h1:gHPreswsOGwe1zViJxufbvNZf0xhK4mxj/r1CwLp958= +github.com/pdfcpu/pdfcpu v0.3.9/go.mod h1:EfJ1EIo3n5+YlGF53DGe1yF1wQLiqK1eqGDN5LuKALs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -259,16 +259,12 @@ golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190823064033-3a9bac650e44/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -276,11 +272,7 @@ golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20200720140940-1a48f808d81f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -329,12 +321,8 @@ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/keys_generator.go b/keys_generator.go index ca797f7..af8c9a6 100644 --- a/keys_generator.go +++ b/keys_generator.go @@ -1,44 +1,64 @@ package main import ( - "encoding/hex" - log "log" + "fmt" - "github.com/btcsuite/btcutil/base58" "github.com/muun/libwallet" + "github.com/muun/libwallet/emergencykit" ) var defaultNetwork = libwallet.Mainnet() -func buildExtendedKeys(rawKey1, rawKey2, recoveryCode string) ( - *libwallet.DecryptedPrivateKey, - *libwallet.DecryptedPrivateKey) { +func decodeKeysFromInput(rawKey1 string, rawKey2 string) ([]*libwallet.EncryptedPrivateKeyInfo, error) { + key1, err := libwallet.DecodeEncryptedPrivateKey(rawKey1) + if err != nil { + return nil, fmt.Errorf("failed to decode first key: %w", err) + } - // Always take the salt from the second key (the same salt was used, but our older key format - // is missing the salt on the first key): - salt := extractSalt(rawKey2) + key2, err := libwallet.DecodeEncryptedPrivateKey(rawKey2) + if err != nil { + return nil, fmt.Errorf("failed to decode second key: %w", err) + } + + return []*libwallet.EncryptedPrivateKeyInfo{key1, key2}, nil +} + +func decodeKeysFromMetadata(meta *emergencykit.Metadata) ([]*libwallet.EncryptedPrivateKeyInfo, error) { + decodedKeys := make([]*libwallet.EncryptedPrivateKeyInfo, len(meta.EncryptedKeys)) + + for i, metaKey := range meta.EncryptedKeys { + decodedKeys[i] = &libwallet.EncryptedPrivateKeyInfo{ + Version: meta.Version, + Birthday: meta.BirthdayBlock, + EphPublicKey: metaKey.DhPubKey, + CipherText: metaKey.EncryptedPrivKey, + Salt: metaKey.Salt, + } + } + + return decodedKeys, nil +} + +func decryptKeys(encryptedKeys []*libwallet.EncryptedPrivateKeyInfo, recoveryCode string) ([]*libwallet.DecryptedPrivateKey, error) { + // Always take the salt from the second key (the same salt was used for all keys, but our legacy + // key format did not include it in the first key): + salt := encryptedKeys[1].Salt decryptionKey, err := libwallet.RecoveryCodeToKey(recoveryCode, salt) if err != nil { - log.Fatalf("failed to process recovery code: %v", err) + return nil, fmt.Errorf("failed to process recovery code: %w", err) } - key1, err := decryptionKey.DecryptKey(rawKey1, defaultNetwork) - if err != nil { - log.Fatalf("failed to decrypt first key: %v", err) + decryptedKeys := make([]*libwallet.DecryptedPrivateKey, len(encryptedKeys)) + + for i, encryptedKey := range encryptedKeys { + decryptedKey, err := decryptionKey.DecryptKey(encryptedKey, defaultNetwork) + if err != nil { + return nil, fmt.Errorf("failed to decrypt key %d: %w", i, err) + } + + decryptedKeys[i] = decryptedKey } - key2, err := decryptionKey.DecryptKey(rawKey2, defaultNetwork) - if err != nil { - log.Fatalf("failed to decrypt second key: %v", err) - } - - return key1, key2 -} - -func extractSalt(rawKey string) string { - bytes := base58.Decode(rawKey) - saltBytes := bytes[len(bytes)-8:] - - return hex.EncodeToString(saltBytes) + return decryptedKeys, nil } diff --git a/main.go b/main.go index 4c25335..2ab25fd 100644 --- a/main.go +++ b/main.go @@ -1,58 +1,124 @@ package main import ( + "flag" "fmt" - "log" "os" + "regexp" "strconv" "strings" "github.com/btcsuite/btcutil" + "github.com/logrusorgru/aurora" "github.com/muun/libwallet" + "github.com/muun/libwallet/emergencykit" + "github.com/muun/recovery/scanner" ) +const version = "2.1.0" + func main() { - printWelcomeMessage() + // Pick up command-line arguments: + flag.Parse() + args := flag.Args() - recoveryCode := readRecoveryCode() - - userRawKey := readKey("first encrypted private key") - muunRawKey := readKey("second encrypted private key") - - userKey, muunKey := buildExtendedKeys(userRawKey, muunRawKey, recoveryCode) - userKey.Key.Path = "m/1'/1'" - - sweepAddress := readSweepAddress() - - fmt.Println("") - fmt.Println("\nStarting scan of all your addresses. This may take a while") - - sweeper := Sweeper{ - UserKey: userKey.Key, - MuunKey: muunKey.Key, - Birthday: muunKey.Birthday, - SweepAddress: sweepAddress, + // Ensure correct form: + if len(args) > 1 { + printUsage() + os.Exit(0) } - utxos, err := sweeper.GetUTXOs() + // Welcome! + printWelcomeMessage() + + // We're going to need a few things to move forward with the recovery process. Let's make a list + // so we keep them in mind: + var recoveryCode string + var encryptedKeys []*libwallet.EncryptedPrivateKeyInfo + var destinationAddress btcutil.Address + + // First on our list is the Recovery Code. This is the time to go looking for that piece of paper: + recoveryCode = readRecoveryCode() + + // Good! Now, on to those keys. We need to read them and decrypt them: + encryptedKeys, err := readBackupFromInputOrPDF(flag.Arg(0)) if err != nil { exitWithError(err) } - fmt.Println("") - - if len(utxos) > 0 { - fmt.Printf("The recovery tool has found the following confirmed UTXOs:\n%v", utxos) - } else { - fmt.Printf("No confirmed UTXOs found") - fmt.Println() - return + decryptedKeys, err := decryptKeys(encryptedKeys, recoveryCode) + if err != nil { + exitWithError(err) } + + decryptedKeys[0].Key.Path = "m/1'/1'" // a little adjustment for legacy users. + + // Finally, we need the destination address to sweep the funds: + destinationAddress = readAddress() + + sayBlock(` + Starting scan of all possible addresses. This will take a few minutes. + `) + + transactionID := doRecovery(decryptedKeys, destinationAddress) + + sayBlock(` + Transaction sent! You can check the status here: https://blockstream.info/tx/%v + (it will appear in Blockstream after a short delay) + + We appreciate all kinds of feedback. If you have any, send it to {blue contact@muun.com} + `, transactionID) +} + +// doRecovery runs the scan & sweep process, and returns the ID of the broadcasted transaction. +func doRecovery(decryptedKeys []*libwallet.DecryptedPrivateKey, destinationAddress btcutil.Address) string { + addrGen := NewAddressGenerator(decryptedKeys[0].Key, decryptedKeys[1].Key) + utxoScanner := scanner.NewScanner() + + addresses := addrGen.Stream() + + sweeper := Sweeper{ + UserKey: decryptedKeys[0].Key, + MuunKey: decryptedKeys[1].Key, + Birthday: decryptedKeys[1].Birthday, + SweepAddress: destinationAddress, + } + + reports := utxoScanner.Scan(addresses) + + say("► {white Finding servers...}") + + var lastReport *scanner.Report + for lastReport = range reports { + printReport(lastReport) + } + fmt.Println() + fmt.Println() + + if lastReport.Err != nil { + exitWithError(fmt.Errorf("error while scanning addresses: %w", lastReport.Err)) + } + + say("{green ✓ Scan complete}\n") + utxos := lastReport.UtxosFound + + if len(utxos) == 0 { + sayBlock("No funds were discovered\n\n") + return "" + } + + var total int64 + for _, utxo := range utxos { + total += utxo.Amount + say("• {white %d} sats in %s\n", utxo.Amount, utxo.Address.Address()) + } + + say("\n— {white %d} sats total\n", total) txOutputAmount, txWeightInBytes, err := sweeper.GetSweepTxAmountAndWeightInBytes(utxos) if err != nil { - printError(err) + exitWithError(err) } fee := readFee(txOutputAmount, txWeightInBytes) @@ -60,67 +126,89 @@ func main() { // Then we re-build the sweep tx with the actual fee sweepTx, err := sweeper.BuildSweepTx(utxos, fee) if err != nil { - printError(err) + exitWithError(err) } - fmt.Println("Transaction ready to be sent") + sayBlock("Sending transaction...") err = sweeper.BroadcastTx(sweepTx) if err != nil { - printError(err) + exitWithError(err) } - fmt.Printf("Transaction sent! You can check the status here: https://blockstream.info/tx/%v", sweepTx.TxHash().String()) - fmt.Println("") - fmt.Printf("We appreciate all kinds of feedback. If you have any, send it to contact@muun.com") - fmt.Println("") + return sweepTx.TxHash().String() } -func printError(err error) { - log.Printf("The recovery tool failed with the following error: %v", err.Error()) - log.Printf("") - log.Printf("You can try again or contact us at support@muun.com") - panic(err) +func exitWithError(err error) { + sayBlock(` + {red Error!} + The Recovery Tool encountered a problem. Please, try again. + + If the problem persists, contact {blue support@muun.com} and include this: + + ――― {white error report} ――― + %v + ―――――――――――――――――――― + + We're always there to help. + `, err) + + os.Exit(1) } func printWelcomeMessage() { - fmt.Println("Welcome to Muun's Recovery Tool") - fmt.Println("") - fmt.Println("You can use this tool to transfer all funds from your Muun account to an") - fmt.Println("address of your choosing.") - fmt.Println("") - fmt.Println("To do this you will need:") - fmt.Println("1. Your Recovery Code, which you wrote down during your security setup") - fmt.Println("2. Your two encrypted private keys, which you exported from your wallet") - fmt.Println("3. A destination bitcoin address where all your funds will be sent") - fmt.Println("") - fmt.Println("If you have any questions, we'll be happy to answer them. Contact us at support@muun.com") - fmt.Println("") + say(` + {blue Muun Recovery Tool v%s} + + To recover your funds, you will need: + + 1. {yellow Your Recovery Code}, which you wrote down during your security setup + 2. {yellow Your Emergency Kit PDF}, which you exported from the app + 3. {yellow Your destination bitcoin address}, where all your funds will be sent + + If you have any questions, we'll be happy to answer them. Contact us at {blue support@muun.com} + `, version) +} + +func printUsage() { + fmt.Println("Usage: recovery-tool [optional: path to Emergency Kit PDF]") +} + +func printReport(report *scanner.Report) { + var total int64 + for _, utxo := range report.UtxosFound { + total += utxo.Amount + } + + say("\r► {white Scanned addresses}: %d | {white Sats found}: %d", report.ScannedAddresses, total) } func readRecoveryCode() string { - fmt.Println("") - fmt.Printf("Enter your Recovery Code") - fmt.Println() - fmt.Println("(it looks like this: 'ABCD-1234-POW2-R561-P120-JK26-12RW-45TT')") - fmt.Print("> ") - var userInput string - fmt.Scan(&userInput) - userInput = strings.TrimSpace(userInput) + sayBlock(` + {yellow Enter your Recovery Code} + (it looks like this: 'ABCD-1234-POW2-R561-P120-JK26-12RW-45TT') + `) + var userInput string + ask(&userInput) + + userInput = strings.TrimSpace(userInput) finalRC := strings.ToUpper(userInput) if strings.Count(finalRC, "-") != 7 { - fmt.Printf("Invalid recovery code. Did you add the '-' separator between each 4-characters segment?") - fmt.Println() - fmt.Println("Please, try again") + say(` + Invalid recovery code. Did you add the '-' separator between each 4-characters segment? + Please, try again + `) return readRecoveryCode() } if len(finalRC) != 39 { - fmt.Println("Your recovery code must have 39 characters") - fmt.Println("Please, try again") + say(` + Your recovery code must have 39 characters + Please, try again + `) return readRecoveryCode() } @@ -128,12 +216,68 @@ func readRecoveryCode() string { return finalRC } +func readBackupFromInputOrPDF(optionalPDF string) ([]*libwallet.EncryptedPrivateKeyInfo, error) { + // Here we have two possible flows, depending on whether the PDF was provided (pick up the + // encrypted backup automatically) or not (manual input). If we try for the automatic flow and fail, + // we can fall back to the manual one. + + // Read metadata from the PDF, if given: + if optionalPDF != "" { + encryptedKeys, err := readBackupFromPDF(optionalPDF) + + if err == nil { + return encryptedKeys, nil + } + + // Hmm. Okay, we'll confess and fall back to manual input. + say(` + Couldn't read the PDF automatically: %v + Please, enter your data manually + `, err) + } + + // Ask for manual input, if we have no PDF or couldn't read it: + encryptedKeys, err := readBackupFromInput() + if err != nil { + return nil, err + } + + return encryptedKeys, nil +} + +func readBackupFromInput() ([]*libwallet.EncryptedPrivateKeyInfo, error) { + firstRawKey := readKey("first encrypted private key") + secondRawKey := readKey("second encrypted private key") + + decodedKeys, err := decodeKeysFromInput(firstRawKey, secondRawKey) + if err != nil { + return nil, err + } + + return decodedKeys, nil +} + +func readBackupFromPDF(path string) ([]*libwallet.EncryptedPrivateKeyInfo, error) { + reader := &emergencykit.MetadataReader{SrcFile: path} + + metadata, err := reader.ReadMetadata() + if err != nil { + return nil, err + } + + decodedKeys, err := decodeKeysFromMetadata(metadata) + if err != nil { + return nil, err + } + + return decodedKeys, nil +} + func readKey(keyType string) string { - fmt.Println("") - fmt.Printf("Enter your %v", keyType) - fmt.Println() - fmt.Println("(it looks like this: '9xzpc7y6sNtRvh8Fh...')") - fmt.Print("> ") + sayBlock(` + {yellow Enter your %v} + (it looks like this: '9xzpc7y6sNtRvh8Fh...') + `, keyType) // NOTE: // Users will most likely copy and paste their keys from the Emergency Kit PDF. In this case, @@ -144,51 +288,60 @@ func readKey(keyType string) string { // Given the line lengths actually found in our Emergency Kits, we have a simple solution for now: // scan a minimum length of characters. Pasing from current versions of the Emergency Kit will // only go past a minimum length when the key being entered is complete, in all cases. - userInput := scanMultiline(libwallet.EncodedKeyLengthLegacy) + userInput := askMultiline(libwallet.EncodedKeyLengthLegacy) if len(userInput) < libwallet.EncodedKeyLengthLegacy { // This is obviously invalid. Other problems will be detected later on, during the actual // decoding and decryption stage. - fmt.Println("The key you entered doesn't look valid\nPlease, try again") + say(` + The key you entered doesn't look valid + Please, try again + `) + return readKey(keyType) } return userInput } -func readSweepAddress() btcutil.Address { - fmt.Println("") - fmt.Println("Enter your destination bitcoin address") - fmt.Print("> ") +func readAddress() btcutil.Address { + sayBlock(` + {yellow Enter your destination bitcoin address} + `) + var userInput string - fmt.Scan(&userInput) + ask(&userInput) + userInput = strings.TrimSpace(userInput) addr, err := btcutil.DecodeAddress(userInput, &chainParams) if err != nil { - fmt.Println("This is not a valid bitcoin address") - fmt.Println("") - fmt.Println("Please, try again") + say(` + This is not a valid bitcoin address + Please, try again + `) - return readSweepAddress() + return readAddress() } return addr } func readFee(totalBalance, weight int64) int64 { - fmt.Println("") - fmt.Printf("Enter the fee in satoshis per byte. Tx weight: %v bytes. You can check the status of the mempool here: https://bitcoinfees.earn.com/#fees", weight) - fmt.Println() - fmt.Println("(Example: 5)") - fmt.Print("> ") + sayBlock(` + {yellow Enter the fee rate (sats/byte)} + Your transaction weighs %v bytes. You can get suggestions in https://bitcoinfees.earn.com/#fees + `, weight) + var userInput string - fmt.Scan(&userInput) + ask(&userInput) + feeInSatsPerByte, err := strconv.ParseInt(userInput, 10, 64) if err != nil || feeInSatsPerByte <= 0 { - fmt.Printf("The fee must be a number") - fmt.Println("") - fmt.Println("Please, try again") + say(` + The fee must be a whole number + Please, try again + `) return readFee(totalBalance, weight) } @@ -196,9 +349,10 @@ func readFee(totalBalance, weight int64) int64 { totalFee := feeInSatsPerByte * weight if totalBalance-totalFee < 546 { - fmt.Printf("The fee is too high. The amount left must be higher than dust") - fmt.Println("") - fmt.Println("Please, try again") + say(` + The fee is too high. The remaining amount after deducting is too low to send. + Please, try again + `) return readFee(totalBalance, weight) } @@ -207,32 +361,84 @@ func readFee(totalBalance, weight int64) int64 { } func readConfirmation(value, fee int64, address string) { - fmt.Println("") - fmt.Printf("About to send %v satoshis with fee: %v satoshis to %v", value, fee, address) - fmt.Println() - fmt.Println("Confirm? (y/n)") - fmt.Print("> ") + sayBlock(` + {whiteUnderline Summary} + {white Amount}: %v sats + {white Fee}: %v sats + {white Destination}: %v + + {yellow Confirm?} (y/n) + `, value, fee, address) + var userInput string - fmt.Scan(&userInput) + ask(&userInput) if userInput == "y" || userInput == "Y" { return } if userInput == "n" || userInput == "N" { - log.Println() - log.Printf("Recovery tool stopped") - log.Println() - log.Printf("You can try again or contact us at support@muun.com") + sayBlock(` + Recovery tool stopped + You can try again or contact us at {blue support@muun.com} + `) os.Exit(1) } - fmt.Println() - fmt.Println("You can only enter 'y' to accept or 'n' to cancel") + say(`You can only enter 'y' to confirm or 'n' to cancel`) + + fmt.Print("\n\n") readConfirmation(value, fee, address) } -func scanMultiline(minChars int) string { +var leadingIndentRe = regexp.MustCompile("^[ \t]+") +var colorRe = regexp.MustCompile(`\{(\w+?) ([^\}]+?)\}`) + +func say(message string, v ...interface{}) { + noEmptyLine := strings.TrimLeft(message, " \n") + firstIndent := leadingIndentRe.FindString(noEmptyLine) + + noIndent := strings.ReplaceAll(noEmptyLine, firstIndent, "") + + noTrailingSpace := strings.TrimRight(noIndent, " \t") + + withColors := colorRe.ReplaceAllStringFunc(noTrailingSpace, func(match string) string { + groups := colorRe.FindStringSubmatch(match) + return applyColor(groups[1], groups[2]) + }) + + fmt.Printf(withColors, v...) +} + +func sayBlock(message string, v ...interface{}) { + fmt.Println() + say(message, v...) +} + +func applyColor(colorName string, text string) string { + boldText := aurora.Bold(text) // in most terminals, bold colors are prettier and highlight better + + switch colorName { + case "red": + return aurora.Red(boldText).String() + case "blue": + return aurora.Blue(boldText).String() + case "yellow": + return aurora.Yellow(boldText).String() + case "green": + return aurora.Green(boldText).String() + case "white": + return aurora.White(boldText).String() + case "whiteUnderline": + return aurora.Underline(boldText).White().String() + } + + panic("No such color: " + colorName) +} + +func askMultiline(minChars int) string { + fmt.Print("➜ ") + var result strings.Builder for result.Len() < minChars { @@ -245,7 +451,7 @@ func scanMultiline(minChars int) string { return result.String() } -func exitWithError(reason error) { - fmt.Println("\nError while scanning. Can't continue. Please, try again later.") - os.Exit(1) +func ask(result *string) { + fmt.Print("➜ ") + fmt.Scan(result) } diff --git a/recovery-tool b/recovery-tool new file mode 100755 index 0000000..ff433b0 --- /dev/null +++ b/recovery-tool @@ -0,0 +1,7 @@ +#!/bin/bash + +# Move to the repository root: +cd "$(dirname "${BASH_SOURCE[0]}")" + +# Go! +go run -mod=vendor . -- "$@" \ No newline at end of file diff --git a/recovery_tool.go b/recovery_tool.go index e87b789..f9bb01e 100644 --- a/recovery_tool.go +++ b/recovery_tool.go @@ -2,51 +2,59 @@ package main import ( "bytes" - "fmt" + "encoding/hex" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/muun/libwallet" + "github.com/muun/recovery/scanner" ) -func buildSweepTx(utxos []*RelevantTx, sweepAddress btcutil.Address, fee int64) []byte { +func buildSweepTx(utxos []*scanner.Utxo, sweepAddress btcutil.Address, fee int64) ([]byte, error) { tx := wire.NewMsgTx(2) value := int64(0) for _, utxo := range utxos { - tx.AddTxIn(wire.NewTxIn(&utxo.Outpoint, []byte{}, [][]byte{})) - value += utxo.Satoshis - } + chainHash, err := chainhash.NewHashFromStr(utxo.TxID) + if err != nil { + return nil, err + } - fmt.Println() - fmt.Printf("Total balance in satoshis: %v", value) - fmt.Println() + outpoint := wire.OutPoint{ + Hash: *chainHash, + Index: uint32(utxo.OutputIndex), + } + + tx.AddTxIn(wire.NewTxIn(&outpoint, []byte{}, [][]byte{})) + value += utxo.Amount + } value -= fee script, err := txscript.PayToAddrScript(sweepAddress) if err != nil { - printError(err) + return nil, err } tx.AddTxOut(wire.NewTxOut(value, script)) writer := &bytes.Buffer{} err = tx.Serialize(writer) if err != nil { - panic(err) + return nil, err } if fee != 0 { readConfirmation(value, fee, sweepAddress.String()) } - return writer.Bytes() + return writer.Bytes(), nil } -func buildSignedTx(utxos []*RelevantTx, sweepTx []byte, userKey *libwallet.HDPrivateKey, +func buildSignedTx(utxos []*scanner.Utxo, sweepTx []byte, userKey *libwallet.HDPrivateKey, muunKey *libwallet.HDPrivateKey) (*wire.MsgTx, error) { inputList := &libwallet.InputList{} @@ -59,7 +67,7 @@ func buildSignedTx(utxos []*RelevantTx, sweepTx []byte, userKey *libwallet.HDPri pstx, err := libwallet.NewPartiallySignedTransaction(inputList, sweepTx) if err != nil { - printError(err) + return nil, err } signedTx, err := pstx.FullySign(userKey, muunKey) @@ -72,17 +80,18 @@ func buildSignedTx(utxos []*RelevantTx, sweepTx []byte, userKey *libwallet.HDPri return wireTx, nil } +// input is a minimal type that implements libwallet.Input type input struct { - tx *RelevantTx + utxo *scanner.Utxo muunSignature []byte } func (i *input) OutPoint() libwallet.Outpoint { - return &outpoint{tx: i.tx} + return &outpoint{utxo: i.utxo} } func (i *input) Address() libwallet.MuunAddress { - return i.tx.SigningDetails.Address + return i.utxo.Address } func (i *input) UserSignature() []byte { @@ -105,18 +114,24 @@ func (i *input) IncomingSwap() libwallet.InputIncomingSwap { return nil } +// outpoint is a minimal type that implements libwallet.Outpoint type outpoint struct { - tx *RelevantTx + utxo *scanner.Utxo } func (o *outpoint) TxId() []byte { - return o.tx.Outpoint.Hash.CloneBytes() + raw, err := hex.DecodeString(o.utxo.TxID) + if err != nil { + panic(err) // we wrote this hex value ourselves, no input from anywhere else + } + + return raw } func (o *outpoint) Index() int { - return int(o.tx.Outpoint.Index) + return o.utxo.OutputIndex } func (o *outpoint) Amount() int64 { - return o.tx.Satoshis + return o.utxo.Amount } diff --git a/scanner/scanner.go b/scanner/scanner.go index 648af19..0b335fa 100644 --- a/scanner/scanner.go +++ b/scanner/scanner.go @@ -39,22 +39,33 @@ type Scanner struct { log *utils.Logger } +// Report contains information about an ongoing scan. +type Report struct { + ScannedAddresses int + UtxosFound []*Utxo + Err error +} + // Utxo references a transaction output, plus the associated MuunAddress and script. type Utxo struct { TxID string OutputIndex int - Amount int + Amount int64 Address libwallet.MuunAddress Script []byte } // scanContext contains the synchronization objects for a single Scanner round, to manage Tasks. type scanContext struct { + // Task management: addresses chan libwallet.MuunAddress - results chan Utxo - errors chan error + results chan *scanTaskResult done chan struct{} wg *sync.WaitGroup + + // Progress reporting: + reports chan *Report + reportCache *Report } // NewScanner creates an initialized Scanner. @@ -67,34 +78,54 @@ func NewScanner() *Scanner { } // Scan an address space and return all relevant transactions for a sweep. -func (s *Scanner) Scan(addresses chan libwallet.MuunAddress) ([]Utxo, error) { - var results []Utxo +func (s *Scanner) Scan(addresses chan libwallet.MuunAddress) <-chan *Report { var waitGroup sync.WaitGroup // Create the Context that goroutines will share: ctx := &scanContext{ addresses: addresses, - results: make(chan Utxo), - errors: make(chan error), + results: make(chan *scanTaskResult), done: make(chan struct{}), wg: &waitGroup, + + reports: make(chan *Report), + reportCache: &Report{ + ScannedAddresses: 0, + UtxosFound: []*Utxo{}, + }, } // Start the scan in background: + go s.startCollect(ctx) go s.startScan(ctx) + return ctx.reports +} + +func (s *Scanner) startCollect(ctx *scanContext) { // Collect all results until the done signal, or abort on the first error: for { select { - case err := <-ctx.errors: - close(ctx.done) // send the done signal ourselves - return nil, err - case result := <-ctx.results: - results = append(results, result) + newReport := *ctx.reportCache // create a new private copy + ctx.reportCache = &newReport + + if result.Err != nil { + ctx.reportCache.Err = s.log.Errorf("Scan failed: %w", result.Err) + ctx.reports <- ctx.reportCache + + close(ctx.done) // failed after several retries, we give up and terminate all tasks + close(ctx.reports) // close the report channel to let callers know we're done + return + } + + ctx.reportCache.ScannedAddresses += len(result.Task.addresses) + ctx.reportCache.UtxosFound = append(ctx.reportCache.UtxosFound, result.Utxos...) + ctx.reports <- ctx.reportCache case <-ctx.done: - return results, nil + close(ctx.reports) // close the report channel to let callers know we're done + return } } } @@ -148,18 +179,8 @@ func (s *Scanner) scanBatch(ctx *scanContext, client *electrum.Client, batch []l exit: ctx.done, } - // Do the thing: - addressResults, err := task.Execute() - - if err != nil { - ctx.errors <- s.log.Errorf("Scan failed: %w", err) - return - } - - // Send back all results: - for _, result := range addressResults { - ctx.results <- result - } + // Do the thing and send back the result: + ctx.results <- task.Execute() } func streamBatches(addresses chan libwallet.MuunAddress) chan []libwallet.MuunAddress { diff --git a/scanner/task.go b/scanner/task.go index a63a7cb..5a8e4eb 100644 --- a/scanner/task.go +++ b/scanner/task.go @@ -20,10 +20,16 @@ type scanTask struct { exit chan struct{} } +// scanTaskResult contains a summary of the execution of a task. +type scanTaskResult struct { + Task *scanTask + Utxos []*Utxo + Err error +} + // Execute obtains the Utxo set for the Task address, implementing a retry strategy. -func (t *scanTask) Execute() ([]Utxo, error) { - results := make(chan []Utxo) - errors := make(chan error) +func (t *scanTask) Execute() *scanTaskResult { + results := make(chan *scanTaskResult) timeout := time.After(t.timeout) // Keep the last error around, in case we reach the timeout and want to know the reason: @@ -31,61 +37,60 @@ func (t *scanTask) Execute() ([]Utxo, error) { for { // Attempt to run the task: - go t.tryExecuteAsync(results, errors) + go t.tryExecuteAsync(results) // Wait until a result is sent, the timeout is reached or the task canceled, capturing errors // errors along the way: select { case <-t.exit: - return []Utxo{}, nil // stop retrying when we get the done signal + return t.exitResult() // stop retrying when we get the done signal case result := <-results: - return result, nil + if result.Err == nil { + return result // we're done! nice work everyone. + } - case err := <-errors: - lastError = err + lastError = result.Err // keep retrying when an attempt fails case <-timeout: - return nil, fmt.Errorf("Task timed out. Last error: %w", lastError) + return t.errorResult(fmt.Errorf("Task timed out. Last error: %w", lastError)) // stop on timeout } } } -func (t *scanTask) tryExecuteAsync(results chan []Utxo, errors chan error) { +func (t *scanTask) tryExecuteAsync(results chan *scanTaskResult) { // Errors will almost certainly arise from Electrum server failures, which are extremely // common. Unreachable IPs, dropped connections, sudden EOFs, etc. We'll run this task, assuming // the servers are at fault when something fails, disconnecting and cycling them as we retry. - result, err := t.tryExecute() + result := t.tryExecute() - if err != nil { + if result.Err != nil { t.client.Disconnect() - errors <- err - return } results <- result } -func (t *scanTask) tryExecute() ([]Utxo, error) { +func (t *scanTask) tryExecute() *scanTaskResult { // If our client is not connected, make an attempt to connect to a server: if !t.client.IsConnected() { err := t.client.Connect(t.servers.NextServer()) if err != nil { - return nil, err + return t.errorResult(err) } } // Prepare the output scripts for all given addresses: outputScripts, err := getOutputScripts(t.addresses) if err != nil { - return nil, err + return t.errorResult(err) } // Prepare the index hashes that Electrum requires to list outputs: indexHashes, err := getIndexHashes(outputScripts) if err != nil { - return nil, err + return t.errorResult(err) } // Call Electrum to get the unspent output list, grouped by index for each address: @@ -98,15 +103,15 @@ func (t *scanTask) tryExecute() ([]Utxo, error) { } if err != nil { - return nil, err + return t.errorResult(err) } // Compile the results into a list of `Utxos`: - var utxos []Utxo + var utxos []*Utxo for i, unspentRefGroup := range unspentRefGroups { for _, unspentRef := range unspentRefGroup { - newUtxo := Utxo{ + newUtxo := &Utxo{ TxID: unspentRef.TxHash, OutputIndex: unspentRef.TxPos, Amount: unspentRef.Value, @@ -118,7 +123,7 @@ func (t *scanTask) tryExecute() ([]Utxo, error) { } } - return utxos, nil + return t.successResult(utxos) } func (t *scanTask) listUnspentWithBatching(indexHashes []string) ([][]electrum.UnspentRef, error) { @@ -145,6 +150,18 @@ func (t *scanTask) listUnspentWithoutBatching(indexHashes []string) ([][]electru return unspentRefGroups, nil } +func (t *scanTask) errorResult(err error) *scanTaskResult { + return &scanTaskResult{Task: t, Err: err} +} + +func (t *scanTask) successResult(utxos []*Utxo) *scanTaskResult { + return &scanTaskResult{Task: t, Utxos: utxos} +} + +func (t *scanTask) exitResult() *scanTaskResult { + return &scanTaskResult{Task: t} +} + // getIndexHashes calculates all the Electrum index hashes for a list of output scripts. func getIndexHashes(outputScripts [][]byte) ([]string, error) { indexHashes := make([]string, len(outputScripts)) diff --git a/sweeper.go b/sweeper.go index 199515f..260cb1b 100644 --- a/sweeper.go +++ b/sweeper.go @@ -9,7 +9,6 @@ import ( "github.com/muun/recovery/scanner" "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" @@ -27,55 +26,7 @@ type Sweeper struct { SweepAddress btcutil.Address } -// RelevantTx contains a PKScipt, an Address an a boolean to check if its spent or not -type RelevantTx struct { - PkScript []byte - Address string - Spent bool - Satoshis int64 - SigningDetails signingDetails - Outpoint wire.OutPoint -} - -func (tx *RelevantTx) String() string { - return fmt.Sprintf("outpoint %v:%v for %v sats on path %v", - tx.Outpoint.Hash, tx.Outpoint.Index, tx.Satoshis, tx.SigningDetails.Address.DerivationPath()) -} - -func (s *Sweeper) GetUTXOs() ([]*RelevantTx, error) { - addresses := s.generateAddresses() - - results, err := scanner.NewScanner().Scan(addresses) - if err != nil { - return nil, fmt.Errorf("error while scanning addresses: %w", err) - } - - txs, err := buildRelevantTxs(results) - if err != nil { - return nil, fmt.Errorf("error while crafting transaction: %w", err) - } - - return txs, nil -} - -func (s *Sweeper) generateAddresses() chan libwallet.MuunAddress { - ch := make(chan libwallet.MuunAddress) - - go func() { - g := NewAddressGenerator(s.UserKey, s.MuunKey) - g.Generate() - - for _, details := range g.Addresses() { - ch <- details.Address - } - - close(ch) - }() - - return ch -} - -func (s *Sweeper) GetSweepTxAmountAndWeightInBytes(utxos []*RelevantTx) (outputAmount int64, weightInBytes int64, err error) { +func (s *Sweeper) GetSweepTxAmountAndWeightInBytes(utxos []*scanner.Utxo) (outputAmount int64, weightInBytes int64, err error) { // we build a sweep tx with 0 fee with the only purpose of checking its signed size zeroFeeSweepTx, err := s.BuildSweepTx(utxos, 0) if err != nil { @@ -88,12 +39,16 @@ func (s *Sweeper) GetSweepTxAmountAndWeightInBytes(utxos []*RelevantTx) (outputA return outputAmount, weightInBytes, nil } -func (s *Sweeper) BuildSweepTx(utxos []*RelevantTx, fee int64) (*wire.MsgTx, error) { +func (s *Sweeper) BuildSweepTx(utxos []*scanner.Utxo, fee int64) (*wire.MsgTx, error) { derivedMuunKey, err := s.MuunKey.DeriveTo("m/1'/1'") if err != nil { return nil, err } - sweepTx := buildSweepTx(utxos, s.SweepAddress, fee) + sweepTx, err := buildSweepTx(utxos, s.SweepAddress, fee) + if err != nil { + return nil, err + } + return buildSignedTx(utxos, sweepTx, s.UserKey, derivedMuunKey) } @@ -124,33 +79,3 @@ func (s *Sweeper) BroadcastTx(tx *wire.MsgTx) error { return nil } - -// buildRelevantTxs prepares the output from Scanner for crafting. -func buildRelevantTxs(utxos []scanner.Utxo) ([]*RelevantTx, error) { - var relevantTxs []*RelevantTx - - for _, utxo := range utxos { - address := utxo.Address.Address() - - chainHash, err := chainhash.NewHashFromStr(utxo.TxID) - if err != nil { - return nil, err - } - - relevantTx := &RelevantTx{ - PkScript: utxo.Script, - Address: address, - Spent: false, - Satoshis: int64(utxo.Amount), - SigningDetails: signingDetails{utxo.Address}, - Outpoint: wire.OutPoint{ - Hash: *chainHash, - Index: uint32(utxo.OutputIndex), - }, - } - - relevantTxs = append(relevantTxs, relevantTx) - } - - return relevantTxs, nil -} diff --git a/vendor/github.com/logrusorgru/aurora/.gitignore b/vendor/github.com/logrusorgru/aurora/.gitignore new file mode 100644 index 0000000..dbcb7cc --- /dev/null +++ b/vendor/github.com/logrusorgru/aurora/.gitignore @@ -0,0 +1,34 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof +*.out + +# coverage + +cover.html + +# benchcmp + +*.cmp + diff --git a/vendor/github.com/logrusorgru/aurora/.travis.yml b/vendor/github.com/logrusorgru/aurora/.travis.yml new file mode 100644 index 0000000..570e361 --- /dev/null +++ b/vendor/github.com/logrusorgru/aurora/.travis.yml @@ -0,0 +1,9 @@ +language: go +go: + - tip +before_install: + - go get github.com/axw/gocov/gocov + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/logrusorgru/aurora/AUTHORS.md b/vendor/github.com/logrusorgru/aurora/AUTHORS.md new file mode 100644 index 0000000..0ee9e3e --- /dev/null +++ b/vendor/github.com/logrusorgru/aurora/AUTHORS.md @@ -0,0 +1,8 @@ +AUTHORS +======= + +- Konstantin Ivanov @logrusorgru +- Mattias Eriksson @snaggen +- Ousmane Traore @otraore +- Simon Legner @simon04 +- Sevenate @sevenate diff --git a/vendor/github.com/logrusorgru/aurora/CHANGELOG.md b/vendor/github.com/logrusorgru/aurora/CHANGELOG.md new file mode 100644 index 0000000..ad0a202 --- /dev/null +++ b/vendor/github.com/logrusorgru/aurora/CHANGELOG.md @@ -0,0 +1,59 @@ +Changes +======= + +--- +16:05:02 +Thursday, July 2, 2020 + +Change license from the WTFPL to the Unlicense due to pkg.go.dev restriction. + +--- +15:39:40 +Wednesday, April 17, 2019 + +- Bright background and foreground colors +- 8-bit indexed colors `Index`, `BgIndex` +- 24 grayscale colors `Gray`, `BgGray` +- `Yellow` and `BgYellow` methods, mark Brow and BgBrown as deprecated + Following specifications, correct name of the colors are yellow, but + by historical reason they are called brown. Both, the `Yellow` and the + `Brown` methods (including `Bg+`) represents the same colors. The Brown + are leaved for backward compatibility until Go modules production release. +- Additional formats + + `Faint` that is opposite to the `Bold` + + `DoublyUnderline` + + `Fraktur` + + `Italic` + + `Underline` + + `SlowBlink` with `Blink` alias + + `RapidBlink` + + `Reverse` that is alias for the `Inverse` + + `Conceal` with `Hidden` alias + + `CrossedOut` with `StrikeThrough` alias + + `Framed` + + `Encircled` + + `Overlined` +- Add AUTHORS.md file and change all copyright notices. +- `Reset` method to create clear value. `Reset` method that replaces + `Bleach` method. The `Bleach` method was marked as deprecated. + +--- + +14:25:49 +Friday, August 18, 2017 + +- LICENSE.md changed to LICENSE +- fix email in README.md +- add "no warranty" to README.md +- set proper copyright date + +--- + +16:59:28 +Tuesday, November 8, 2016 + +- Rid out off sync.Pool +- Little optimizations (very little) +- Improved benchmarks + +--- diff --git a/vendor/github.com/logrusorgru/aurora/LICENSE b/vendor/github.com/logrusorgru/aurora/LICENSE new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/vendor/github.com/logrusorgru/aurora/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/vendor/github.com/logrusorgru/aurora/README.md b/vendor/github.com/logrusorgru/aurora/README.md new file mode 100644 index 0000000..e0afce1 --- /dev/null +++ b/vendor/github.com/logrusorgru/aurora/README.md @@ -0,0 +1,314 @@ +Aurora +====== + +[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/logrusorgru/aurora?tab=doc) +[![Unlicense](https://img.shields.io/badge/license-unlicense-blue.svg)](http://unlicense.org/) +[![Build Status](https://travis-ci.org/logrusorgru/aurora.svg)](https://travis-ci.org/logrusorgru/aurora) +[![Coverage Status](https://coveralls.io/repos/logrusorgru/aurora/badge.svg?branch=master)](https://coveralls.io/r/logrusorgru/aurora?branch=master) +[![GoReportCard](https://goreportcard.com/badge/logrusorgru/aurora)](https://goreportcard.com/report/logrusorgru/aurora) +[![Gitter](https://img.shields.io/badge/chat-on_gitter-46bc99.svg?logo=data:image%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMTQiIHdpZHRoPSIxNCI%2BPGcgZmlsbD0iI2ZmZiI%2BPHJlY3QgeD0iMCIgeT0iMyIgd2lkdGg9IjEiIGhlaWdodD0iNSIvPjxyZWN0IHg9IjIiIHk9IjQiIHdpZHRoPSIxIiBoZWlnaHQ9IjciLz48cmVjdCB4PSI0IiB5PSI0IiB3aWR0aD0iMSIgaGVpZ2h0PSI3Ii8%2BPHJlY3QgeD0iNiIgeT0iNCIgd2lkdGg9IjEiIGhlaWdodD0iNCIvPjwvZz48L3N2Zz4%3D&logoWidth=10)](https://gitter.im/logrusorgru/aurora) + +Ultimate ANSI colors for Golang. The package supports Printf/Sprintf etc. + + +![aurora logo](https://github.com/logrusorgru/aurora/blob/master/gopher_aurora.png) + +# TOC + +- [Installation](#installation) +- [Usage](#usage) + + [Simple](#simple) + + [Printf](#printf) + + [aurora.Sprintf](#aurorasprintf) + + [Enable/Disable colors](#enabledisable-colors) +- [Chains](#chains) +- [Colorize](#colorize) +- [Grayscale](#grayscale) +- [8-bit colors](#8-bit-colors) +- [Supported Colors & Formats](#supported-colors--formats) + + [All colors](#all-colors) + + [Standard and bright colors](#standard-and-bright-colors) + + [Formats are likely supported](#formats-are-likely-supported) + + [Formats are likely unsupported](#formats-are-likely-unsupported) +- [Limitations](#limitations) + + [Windows](#windows) + + [TTY](#tty) +- [Licensing](#licensing) + +# Installation + +Get +``` +go get -u github.com/logrusorgru/aurora +``` +Test +``` +go test -cover github.com/logrusorgru/aurora +``` + +# Usage + +### Simple + +```go +package main + +import ( + "fmt" + + . "github.com/logrusorgru/aurora" +) + +func main() { + fmt.Println("Hello,", Magenta("Aurora")) + fmt.Println(Bold(Cyan("Cya!"))) +} + +``` + +![simple png](https://github.com/logrusorgru/aurora/blob/master/simple.png) + +### Printf + +```go +package main + +import ( + "fmt" + + . "github.com/logrusorgru/aurora" +) + +func main() { + fmt.Printf("Got it %d times\n", Green(1240)) + fmt.Printf("PI is %+1.2e\n", Cyan(3.14)) +} + +``` + +![printf png](https://github.com/logrusorgru/aurora/blob/master/printf.png) + +### aurora.Sprintf + +```go +package main + +import ( + "fmt" + + . "github.com/logrusorgru/aurora" +) + +func main() { + fmt.Println(Sprintf(Magenta("Got it %d times"), Green(1240))) +} + +``` + +![sprintf png](https://github.com/logrusorgru/aurora/blob/master/sprintf.png) + +### Enable/Disable colors + +```go +package main + +import ( + "fmt" + "flag" + + "github.com/logrusorgru/aurora" +) + +// colorizer +var au aurora.Aurora + +var colors = flag.Bool("colors", false, "enable or disable colors") + +func init() { + flag.Parse() + au = aurora.NewAurora(*colors) +} + +func main() { + // use colorizer + fmt.Println(au.Green("Hello")) +} + +``` +Without flags: +![disable png](https://github.com/logrusorgru/aurora/blob/master/disable.png) + +With `-colors` flag: +![enable png](https://github.com/logrusorgru/aurora/blob/master/enable.png) + +# Chains + +The following samples are equal + +```go +x := BgMagenta(Bold(Red("x"))) +``` + +```go +x := Red("x").Bold().BgMagenta() +``` + +The second is more readable + +# Colorize + +There is `Colorize` function that allows to choose some colors and +format from a side + +```go + +func getColors() Color { + // some stuff that returns appropriate colors and format +} + +// [...] + +func main() { + fmt.Println(Colorize("Greeting", getColors())) +} + +``` +Less complicated example + +```go +x := Colorize("Greeting", GreenFg|GrayBg|BoldFm) +``` + +Unlike other color functions and methods (such as Red/BgBlue etc) +a `Colorize` clears previous colors + +```go +x := Red("x").Colorize(BgGreen) // will be with green background only +``` + +# Grayscale + +```go +fmt.Println(" ", + Gray(1-1, " 00-23 ").BgGray(24-1), + Gray(4-1, " 03-19 ").BgGray(20-1), + Gray(8-1, " 07-15 ").BgGray(16-1), + Gray(12-1, " 11-11 ").BgGray(12-1), + Gray(16-1, " 15-07 ").BgGray(8-1), + Gray(20-1, " 19-03 ").BgGray(4-1), + Gray(24-1, " 23-00 ").BgGray(1-1), +) +``` + +![grayscale png](https://github.com/logrusorgru/aurora/blob/master/aurora_grayscale.png) + +# 8-bit colors + +Methods `Index` and `BgIndex` implements 8-bit colors. + +| Index/BgIndex | Meaning | Foreground | Background | +| -------------- | --------------- | ---------- | ---------- | +| 0- 7 | standard colors | 30- 37 | 40- 47 | +| 8- 15 | bright colors | 90- 97 | 100-107 | +| 16-231 | 216 colors | 38;5;n | 48;5;n | +| 232-255 | 24 grayscale | 38;5;n | 48;5;n | + + +# Supported colors & formats + +- formats + + bold (1) + + faint (2) + + doubly-underline (21) + + fraktur (20) + + italic (3) + + underline (4) + + slow blink (5) + + rapid blink (6) + + reverse video (7) + + conceal (8) + + crossed out (9) + + framed (51) + + encircled (52) + + overlined (53) +- background and foreground colors, including bright + + black + + red + + green + + yellow (brown) + + blue + + magenta + + cyan + + white + + 24 grayscale colors + + 216 8-bit colors + +### All colors + +![linux png](https://github.com/logrusorgru/aurora/blob/master/aurora_colors_black.png) +![white png](https://github.com/logrusorgru/aurora/blob/master/aurora_colors_white.png) + +### Standard and bright colors + +![linux black standard png](https://github.com/logrusorgru/aurora/blob/master/aurora_black_standard.png) +![linux white standard png](https://github.com/logrusorgru/aurora/blob/master/aurora_white_standard.png) + +### Formats are likely supported + +![formats supported gif](https://github.com/logrusorgru/aurora/blob/master/aurora_formats.gif) + +### Formats are likely unsupported + +![formats rarely supported png](https://github.com/logrusorgru/aurora/blob/master/aurora_rarely_supported.png) + +# Limitations + +There is no way to represent `%T` and `%p` with colors using +a standard approach + +```go +package main + +import ( + "fmt" + + . "github.com/logrusorgru/aurora" +) + +func main() { + r := Red("red") + var i int + fmt.Printf("%T %p\n", r, Green(&i)) +} +``` + +Output will be without colors + +``` +aurora.value %!p(aurora.value={0xc42000a310 768 0}) +``` + +The obvious workaround is `Red(fmt.Sprintf("%T", some))` + +### Windows + +The Aurora provides ANSI colors only, so there is no support for Windows. That said, there are workarounds available. +Check out these comments to learn more: + +- [Using go-colorable](https://github.com/logrusorgru/aurora/issues/2#issuecomment-299014211). +- [Using registry for Windows 10](https://github.com/logrusorgru/aurora/issues/10#issue-476361247). + +### TTY + +The Aurora has no internal TTY detectors by design. Take a look + [this comment](https://github.com/logrusorgru/aurora/issues/2#issuecomment-299030108) if you want turn +on colors for a terminal only, and turn them off for a file. + +### Licensing + +Copyright © 2016-2020 The Aurora Authors. This work is free. +It comes without any warranty, to the extent permitted by applicable +law. You can redistribute it and/or modify it under the terms of the +the Unlicense. See the LICENSE file for more details. + + diff --git a/vendor/github.com/logrusorgru/aurora/aurora.go b/vendor/github.com/logrusorgru/aurora/aurora.go new file mode 100644 index 0000000..3b30230 --- /dev/null +++ b/vendor/github.com/logrusorgru/aurora/aurora.go @@ -0,0 +1,725 @@ +// +// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved. +// This program is free software. It comes without any warranty, +// to the extent permitted by applicable law. You can redistribute +// it and/or modify it under the terms of the Unlicense. See LICENSE +// file for more details or see below. +// + +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to +// + +// Package aurora implements ANSI-colors +package aurora + +import ( + "fmt" +) + +// An Aurora implements colorizer interface. +// It also can be a non-colorizer +type Aurora interface { + + // Reset wraps given argument returning Value + // without formats and colors. + Reset(arg interface{}) Value + + // + // Formats + // + // + // Bold or increased intensity (1). + Bold(arg interface{}) Value + // Faint, decreased intensity (2). + Faint(arg interface{}) Value + // + // DoublyUnderline or Bold off, double-underline + // per ECMA-48 (21). + DoublyUnderline(arg interface{}) Value + // Fraktur, rarely supported (20). + Fraktur(arg interface{}) Value + // + // Italic, not widely supported, sometimes + // treated as inverse (3). + Italic(arg interface{}) Value + // Underline (4). + Underline(arg interface{}) Value + // + // SlowBlink, blinking less than 150 + // per minute (5). + SlowBlink(arg interface{}) Value + // RapidBlink, blinking 150+ per minute, + // not widely supported (6). + RapidBlink(arg interface{}) Value + // Blink is alias for the SlowBlink. + Blink(arg interface{}) Value + // + // Reverse video, swap foreground and + // background colors (7). + Reverse(arg interface{}) Value + // Inverse is alias for the Reverse + Inverse(arg interface{}) Value + // + // Conceal, hidden, not widely supported (8). + Conceal(arg interface{}) Value + // Hidden is alias for the Conceal + Hidden(arg interface{}) Value + // + // CrossedOut, characters legible, but + // marked for deletion (9). + CrossedOut(arg interface{}) Value + // StrikeThrough is alias for the CrossedOut. + StrikeThrough(arg interface{}) Value + // + // Framed (51). + Framed(arg interface{}) Value + // Encircled (52). + Encircled(arg interface{}) Value + // + // Overlined (53). + Overlined(arg interface{}) Value + + // + // Foreground colors + // + // + // Black foreground color (30) + Black(arg interface{}) Value + // Red foreground color (31) + Red(arg interface{}) Value + // Green foreground color (32) + Green(arg interface{}) Value + // Yellow foreground color (33) + Yellow(arg interface{}) Value + // Brown foreground color (33) + // + // Deprecated: use Yellow instead, following specification + Brown(arg interface{}) Value + // Blue foreground color (34) + Blue(arg interface{}) Value + // Magenta foreground color (35) + Magenta(arg interface{}) Value + // Cyan foreground color (36) + Cyan(arg interface{}) Value + // White foreground color (37) + White(arg interface{}) Value + // + // Bright foreground colors + // + // BrightBlack foreground color (90) + BrightBlack(arg interface{}) Value + // BrightRed foreground color (91) + BrightRed(arg interface{}) Value + // BrightGreen foreground color (92) + BrightGreen(arg interface{}) Value + // BrightYellow foreground color (93) + BrightYellow(arg interface{}) Value + // BrightBlue foreground color (94) + BrightBlue(arg interface{}) Value + // BrightMagenta foreground color (95) + BrightMagenta(arg interface{}) Value + // BrightCyan foreground color (96) + BrightCyan(arg interface{}) Value + // BrightWhite foreground color (97) + BrightWhite(arg interface{}) Value + // + // Other + // + // Index of pre-defined 8-bit foreground color + // from 0 to 255 (38;5;n). + // + // 0- 7: standard colors (as in ESC [ 30–37 m) + // 8- 15: high intensity colors (as in ESC [ 90–97 m) + // 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) + // 232-255: grayscale from black to white in 24 steps + // + Index(n uint8, arg interface{}) Value + // Gray from 0 to 23. + Gray(n uint8, arg interface{}) Value + + // + // Background colors + // + // + // BgBlack background color (40) + BgBlack(arg interface{}) Value + // BgRed background color (41) + BgRed(arg interface{}) Value + // BgGreen background color (42) + BgGreen(arg interface{}) Value + // BgYellow background color (43) + BgYellow(arg interface{}) Value + // BgBrown background color (43) + // + // Deprecated: use BgYellow instead, following specification + BgBrown(arg interface{}) Value + // BgBlue background color (44) + BgBlue(arg interface{}) Value + // BgMagenta background color (45) + BgMagenta(arg interface{}) Value + // BgCyan background color (46) + BgCyan(arg interface{}) Value + // BgWhite background color (47) + BgWhite(arg interface{}) Value + // + // Bright background colors + // + // BgBrightBlack background color (100) + BgBrightBlack(arg interface{}) Value + // BgBrightRed background color (101) + BgBrightRed(arg interface{}) Value + // BgBrightGreen background color (102) + BgBrightGreen(arg interface{}) Value + // BgBrightYellow background color (103) + BgBrightYellow(arg interface{}) Value + // BgBrightBlue background color (104) + BgBrightBlue(arg interface{}) Value + // BgBrightMagenta background color (105) + BgBrightMagenta(arg interface{}) Value + // BgBrightCyan background color (106) + BgBrightCyan(arg interface{}) Value + // BgBrightWhite background color (107) + BgBrightWhite(arg interface{}) Value + // + // Other + // + // BgIndex of 8-bit pre-defined background color + // from 0 to 255 (48;5;n). + // + // 0- 7: standard colors (as in ESC [ 40–47 m) + // 8- 15: high intensity colors (as in ESC [100–107 m) + // 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) + // 232-255: grayscale from black to white in 24 steps + // + BgIndex(n uint8, arg interface{}) Value + // BgGray from 0 to 23. + BgGray(n uint8, arg interface{}) Value + + // + // Special + // + // Colorize removes existing colors and + // formats of the argument and applies given. + Colorize(arg interface{}, color Color) Value + + // + // Support methods + // + // Sprintf allows to use colored format. + Sprintf(format interface{}, args ...interface{}) string +} + +// NewAurora returns a new Aurora interface that +// will support or not support colors depending +// the enableColors argument +func NewAurora(enableColors bool) Aurora { + if enableColors { + return aurora{} + } + return auroraClear{} +} + +// no colors + +type auroraClear struct{} + +func (auroraClear) Reset(arg interface{}) Value { return valueClear{arg} } + +func (auroraClear) Bold(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Faint(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) DoublyUnderline(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Fraktur(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Italic(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Underline(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) SlowBlink(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) RapidBlink(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Blink(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Reverse(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Inverse(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Conceal(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Hidden(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) CrossedOut(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) StrikeThrough(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Framed(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Encircled(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Overlined(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Black(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Red(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Green(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Yellow(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Brown(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Blue(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Magenta(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Cyan(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) White(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BrightBlack(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BrightRed(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BrightGreen(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BrightYellow(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BrightBlue(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BrightMagenta(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BrightCyan(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BrightWhite(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Index(_ uint8, arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Gray(_ uint8, arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBlack(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgRed(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgGreen(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgYellow(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBrown(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBlue(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgMagenta(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgCyan(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgWhite(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBrightBlack(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBrightRed(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBrightGreen(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBrightYellow(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBrightBlue(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBrightMagenta(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBrightCyan(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgBrightWhite(arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgIndex(_ uint8, arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) BgGray(_ uint8, arg interface{}) Value { + return valueClear{arg} +} + +func (auroraClear) Colorize(arg interface{}, _ Color) Value { + return valueClear{arg} +} + +func (auroraClear) Sprintf(format interface{}, args ...interface{}) string { + if str, ok := format.(string); ok { + return fmt.Sprintf(str, args...) + } + return fmt.Sprintf(fmt.Sprint(format), args...) +} + +// colorized + +type aurora struct{} + +func (aurora) Reset(arg interface{}) Value { + return Reset(arg) +} + +func (aurora) Bold(arg interface{}) Value { + return Bold(arg) +} + +func (aurora) Faint(arg interface{}) Value { + return Faint(arg) +} + +func (aurora) DoublyUnderline(arg interface{}) Value { + return DoublyUnderline(arg) +} + +func (aurora) Fraktur(arg interface{}) Value { + return Fraktur(arg) +} + +func (aurora) Italic(arg interface{}) Value { + return Italic(arg) +} + +func (aurora) Underline(arg interface{}) Value { + return Underline(arg) +} + +func (aurora) SlowBlink(arg interface{}) Value { + return SlowBlink(arg) +} + +func (aurora) RapidBlink(arg interface{}) Value { + return RapidBlink(arg) +} + +func (aurora) Blink(arg interface{}) Value { + return Blink(arg) +} + +func (aurora) Reverse(arg interface{}) Value { + return Reverse(arg) +} + +func (aurora) Inverse(arg interface{}) Value { + return Inverse(arg) +} + +func (aurora) Conceal(arg interface{}) Value { + return Conceal(arg) +} + +func (aurora) Hidden(arg interface{}) Value { + return Hidden(arg) +} + +func (aurora) CrossedOut(arg interface{}) Value { + return CrossedOut(arg) +} + +func (aurora) StrikeThrough(arg interface{}) Value { + return StrikeThrough(arg) +} + +func (aurora) Framed(arg interface{}) Value { + return Framed(arg) +} + +func (aurora) Encircled(arg interface{}) Value { + return Encircled(arg) +} + +func (aurora) Overlined(arg interface{}) Value { + return Overlined(arg) +} + +func (aurora) Black(arg interface{}) Value { + return Black(arg) +} + +func (aurora) Red(arg interface{}) Value { + return Red(arg) +} + +func (aurora) Green(arg interface{}) Value { + return Green(arg) +} + +func (aurora) Yellow(arg interface{}) Value { + return Yellow(arg) +} + +func (aurora) Brown(arg interface{}) Value { + return Brown(arg) +} + +func (aurora) Blue(arg interface{}) Value { + return Blue(arg) +} + +func (aurora) Magenta(arg interface{}) Value { + return Magenta(arg) +} + +func (aurora) Cyan(arg interface{}) Value { + return Cyan(arg) +} + +func (aurora) White(arg interface{}) Value { + return White(arg) +} + +func (aurora) BrightBlack(arg interface{}) Value { + return BrightBlack(arg) +} + +func (aurora) BrightRed(arg interface{}) Value { + return BrightRed(arg) +} + +func (aurora) BrightGreen(arg interface{}) Value { + return BrightGreen(arg) +} + +func (aurora) BrightYellow(arg interface{}) Value { + return BrightYellow(arg) +} + +func (aurora) BrightBlue(arg interface{}) Value { + return BrightBlue(arg) +} + +func (aurora) BrightMagenta(arg interface{}) Value { + return BrightMagenta(arg) +} + +func (aurora) BrightCyan(arg interface{}) Value { + return BrightCyan(arg) +} + +func (aurora) BrightWhite(arg interface{}) Value { + return BrightWhite(arg) +} + +func (aurora) Index(index uint8, arg interface{}) Value { + return Index(index, arg) +} + +func (aurora) Gray(n uint8, arg interface{}) Value { + return Gray(n, arg) +} + +func (aurora) BgBlack(arg interface{}) Value { + return BgBlack(arg) +} + +func (aurora) BgRed(arg interface{}) Value { + return BgRed(arg) +} + +func (aurora) BgGreen(arg interface{}) Value { + return BgGreen(arg) +} + +func (aurora) BgYellow(arg interface{}) Value { + return BgYellow(arg) +} + +func (aurora) BgBrown(arg interface{}) Value { + return BgBrown(arg) +} + +func (aurora) BgBlue(arg interface{}) Value { + return BgBlue(arg) +} + +func (aurora) BgMagenta(arg interface{}) Value { + return BgMagenta(arg) +} + +func (aurora) BgCyan(arg interface{}) Value { + return BgCyan(arg) +} + +func (aurora) BgWhite(arg interface{}) Value { + return BgWhite(arg) +} + +func (aurora) BgBrightBlack(arg interface{}) Value { + return BgBrightBlack(arg) +} + +func (aurora) BgBrightRed(arg interface{}) Value { + return BgBrightRed(arg) +} + +func (aurora) BgBrightGreen(arg interface{}) Value { + return BgBrightGreen(arg) +} + +func (aurora) BgBrightYellow(arg interface{}) Value { + return BgBrightYellow(arg) +} + +func (aurora) BgBrightBlue(arg interface{}) Value { + return BgBrightBlue(arg) +} + +func (aurora) BgBrightMagenta(arg interface{}) Value { + return BgBrightMagenta(arg) +} + +func (aurora) BgBrightCyan(arg interface{}) Value { + return BgBrightCyan(arg) +} + +func (aurora) BgBrightWhite(arg interface{}) Value { + return BgBrightWhite(arg) +} + +func (aurora) BgIndex(n uint8, arg interface{}) Value { + return BgIndex(n, arg) +} + +func (aurora) BgGray(n uint8, arg interface{}) Value { + return BgGray(n, arg) +} + +func (aurora) Colorize(arg interface{}, color Color) Value { + return Colorize(arg, color) +} + +func (aurora) Sprintf(format interface{}, args ...interface{}) string { + return Sprintf(format, args...) +} diff --git a/vendor/github.com/logrusorgru/aurora/aurora_black_standard.png b/vendor/github.com/logrusorgru/aurora/aurora_black_standard.png new file mode 100644 index 0000000000000000000000000000000000000000..83658d60950ceb262d7f8595fa1a4380997fd216 GIT binary patch literal 37415 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 DIM9Wk literal 0 HcmV?d00001 diff --git a/vendor/github.com/logrusorgru/aurora/aurora_colors_black.png b/vendor/github.com/logrusorgru/aurora/aurora_colors_black.png new file mode 100644 index 0000000000000000000000000000000000000000..61b1602a7efa7578bd6b8b6be536d61208d163b5 GIT binary patch literal 3328 zcmds3drVVT7{5LOOsa*Lp*#%e;ue7cTXhjMy<-%iGlDLJQl>!JusEO?s65NP3LB3V z#JQ1SSXMW)ktqsUb+roKD3(WITt^8Hfi@j>l~hcL$sOIsEr#=j5LIJKy=f z?{{*(?>y>`C&X>t;QmPvd9byHTeg~6UEAMPOxY33B_AG69iQeD&71JepR4Z`-uOrVky+r3D_5`{_ zpL?$@4=6$PeeNzK;ILi5Go;u- zvgVe}_Pw->jhxl3|NawKW}FW|`stW0gIDWb*I|l^54#0f)LjeN(nRJonA6)AWcweW zcYa0hTS#dWsp+vbs`1_i9o*7n7f!8`a=sGnS+_gthoPe(HavC4hL;(f{2Ygq-#!KB z0Hb-}?)wz4uI_7nPgm`^_jtWWk2X9zpA^~W0tAZqjwZM z?fv1jydQjTM`W@cUawev1qu`NJWQ*6jttfW{ng!f1J8pDsFmaH^2ux(qIL z(>F&%#fq_TL9yu8s^iH8L@}NZqdv00$dZmRqt(#c_@{NIhG}~tVIyR!1X+6iN16<` z<1#6ZhlVb{kT|sy``GDG=dO<#n&iz;cwd8*p7$c^6wYUu^ou zY<6dsRj_#?Z752?Wh3)erFb!w&^7w|0}W|Nh=D>6myga{`Kf7P6t!X)0Pe1M?)Vbd zVaW8nHL}!n(n9nq@&RLSqAZL}E$L$bO<&!1l~Kp8m|GzNqKeuynE6{;z6J+}7&YY= lT~Al2V3nTRmQJtcztT4f9eTh1%C@y_$c;_l-1;D+@*k(y=c@n! literal 0 HcmV?d00001 diff --git a/vendor/github.com/logrusorgru/aurora/aurora_colors_white.png b/vendor/github.com/logrusorgru/aurora/aurora_colors_white.png new file mode 100644 index 0000000000000000000000000000000000000000..ec42b072375c8972426a23a43415d46413f6534a GIT binary patch literal 3066 zcmds3eM}Q)7(WN2XhREIV*?Ut+#k-kj7>MTg^z<#R*CW%VEEftxFhoZwoCTT>P$q(X8)SE(R9N*!0Zuf5o1*+<5oOSAWK&pmm5 z&(}To-21$rq^HV)Hid11ASft#k2Dj40z7~p4-5cQG<-?`9sxyrWm1S+yV`GbH37-S z_xI#hK#=h2+P&cc(s~03dCKIJB%aMbgfH0fS(5M@APQ9`|6 zgObWp7AL=cuQ@y1iH1a%hQs^SxNo}qD1jV(b3>=U1#1%X7KH4l`*65K)#wd%kt*Z54+pyNp~(j zRR0_;QR^!TCN8M`KrUcOJ>$=Mxp+a1(96{Zs=Z9GblR(SQu0)USL7eERtvBsAK&D8 zxdi_>FB7aA*FPHur48|}DN$IOy8b3|;WX0xhLjNu+;yXNPPb1;`d zvlF)dt=%#lr{1QK71u@C{7(~AJB>uu50OAUs<`hxb%QGKcMSFFT0rNX2xbB71=9k zVK^qY!ewf%sF+jRs1iGq_bINuaEGxTq1lJ~ot7*cS<-b3{Wc7PqnoMzyB@~cJ*B;3 zA}pzP_RJOxo@}Oem(%RgY^UWr7u8>&hiw2<{R8X-pj;0(0q}&K-3R0vj>I@iwr_!@#90+yfy&mVAF{sdZk zCQv-mCx6q>Ut_1wE%C152(@_q-w8YRi`XZ(pfjsmuZq}{E0YS0S%;us(6zp}m!8eQ z{aS`2M%S-CLZvko`s7Z&Yrg7crpNEz`qfu(rOSMu97$_) z3+Z(WsWHDF9u6+~Y@J_6S-PF`i$mFb8kreBm4I2g(zK`X$bx}a9OUDrnM4=0yu93w z&p8}ld6@aTm+KJibp^f|>(%K5tJMO5;56P_V2E;%$KCf+T$IUVYR5|yie3+M-~1&| z7DviO7(Q5CHA=W2TKi{7LnQ$W+hUGq((Lq59f)|VUf6&~#GU{V^(cq9sIjgz9ursT zoZ5ka{2qho<5%*Ggu!3{4$WrsEScRa4_d9(6=HIF5W`>xnVXll6MRQLWLHI-i{h o=NAT?-|zOGAmanD|K`Lw0^47T>RjB18rK3RC#6btZ@yFe8}H+G>;M1& literal 0 HcmV?d00001 diff --git a/vendor/github.com/logrusorgru/aurora/aurora_formats.gif b/vendor/github.com/logrusorgru/aurora/aurora_formats.gif new file mode 100644 index 0000000000000000000000000000000000000000..670dd1c370f09826220de127d630be0c1130bb21 GIT binary patch literal 16212 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 IzO;V-FAHD{(*OVf literal 0 HcmV?d00001 diff --git a/vendor/github.com/logrusorgru/aurora/aurora_rarely_supported.png b/vendor/github.com/logrusorgru/aurora/aurora_rarely_supported.png new file mode 100644 index 0000000000000000000000000000000000000000..51b7b6d2d95d18c5d6fa8757396a4fcd74f0dac2 GIT binary patch literal 5283 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 RChl*blDxWH@$0uA{|gBzT4MkJ literal 0 HcmV?d00001 diff --git a/vendor/github.com/logrusorgru/aurora/aurora_white_standard.png b/vendor/github.com/logrusorgru/aurora/aurora_white_standard.png new file mode 100644 index 0000000000000000000000000000000000000000..1fe99d787b49e22e4f0246611c7ecca15f071086 GIT binary patch literal 23857 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 ui6Bxx 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 +// + +package aurora + +// A Color type is a color. It can contain +// one background color, one foreground color +// and a format, including ideogram related +// formats. +type Color uint + +/* + + Developer note. + + The int type is architecture depended and can be + represented as int32 or int64. + + Thus, we can use 32-bits only to be fast and + cross-platform. + + All supported formats requires 14 bits. It is + first 14 bits. + + A foreground color requires 8 bit + 1 bit (presence flag). + And the same for background color. + + The Color representations + + [ bg 8 bit ] [fg 8 bit ] [ fg/bg 2 bits ] [ fm 14 bits ] + + https://play.golang.org/p/fq2zcNstFoF + +*/ + +// Special formats +const ( + BoldFm Color = 1 << iota // 1 + FaintFm // 2 + ItalicFm // 3 + UnderlineFm // 4 + SlowBlinkFm // 5 + RapidBlinkFm // 6 + ReverseFm // 7 + ConcealFm // 8 + CrossedOutFm // 9 + + FrakturFm // 20 + DoublyUnderlineFm // 21 or bold off for some systems + + FramedFm // 51 + EncircledFm // 52 + OverlinedFm // 53 + + InverseFm = ReverseFm // alias to ReverseFm + BlinkFm = SlowBlinkFm // alias to SlowBlinkFm + HiddenFm = ConcealFm // alias to ConcealFm + StrikeThroughFm = CrossedOutFm // alias to CrossedOutFm + + maskFm = BoldFm | FaintFm | + ItalicFm | UnderlineFm | + SlowBlinkFm | RapidBlinkFm | + ReverseFm | + ConcealFm | CrossedOutFm | + + FrakturFm | DoublyUnderlineFm | + + FramedFm | EncircledFm | OverlinedFm + + flagFg Color = 1 << 14 // presence flag (14th bit) + flagBg Color = 1 << 15 // presence flag (15th bit) + + shiftFg = 16 // shift for foreground (starting from 16th bit) + shiftBg = 24 // shift for background (starting from 24th bit) +) + +// Foreground colors and related formats +const ( + + // 8 bits + + // [ 0; 7] - 30-37 + // [ 8; 15] - 90-97 + // [ 16; 231] - RGB + // [232; 255] - grayscale + + BlackFg Color = (iota << shiftFg) | flagFg // 30, 90 + RedFg // 31, 91 + GreenFg // 32, 92 + YellowFg // 33, 93 + BlueFg // 34, 94 + MagentaFg // 35, 95 + CyanFg // 36, 96 + WhiteFg // 37, 97 + + BrightFg Color = ((1 << 3) << shiftFg) | flagFg // -> 90 + + // the BrightFg itself doesn't represent + // a color, thus it has not flagFg + + // 5 bits + + // BrownFg represents brown foreground color. + // + // Deprecated: use YellowFg instead, following specifications + BrownFg = YellowFg + + // + maskFg = (0xff << shiftFg) | flagFg +) + +// Background colors and related formats +const ( + + // 8 bits + + // [ 0; 7] - 40-47 + // [ 8; 15] - 100-107 + // [ 16; 231] - RGB + // [232; 255] - grayscale + + BlackBg Color = (iota << shiftBg) | flagBg // 40, 100 + RedBg // 41, 101 + GreenBg // 42, 102 + YellowBg // 43, 103 + BlueBg // 44, 104 + MagentaBg // 45, 105 + CyanBg // 46, 106 + WhiteBg // 47, 107 + + BrightBg Color = ((1 << 3) << shiftBg) | flagBg // -> 100 + + // the BrightBg itself doesn't represent + // a color, thus it has not flagBg + + // 5 bits + + // BrownBg represents brown foreground color. + // + // Deprecated: use YellowBg instead, following specifications + BrownBg = YellowBg + + // + maskBg = (0xff << shiftBg) | flagBg +) + +const ( + availFlags = "-+# 0" + esc = "\033[" + clear = esc + "0m" +) + +// IsValid returns true always +// +// Deprecated: don't use this method anymore +func (c Color) IsValid() bool { + return true +} + +// Nos returns string like 1;7;31;45. It +// may be an empty string for empty color. +// If the zero is true, then the string +// is prepended with 0; +func (c Color) Nos(zero bool) string { + return string(c.appendNos(make([]byte, 0, 59), zero)) +} + +func appendCond(bs []byte, cond, semi bool, vals ...byte) []byte { + if !cond { + return bs + } + return appendSemi(bs, semi, vals...) +} + +// if the semi is true, then prepend with semicolon +func appendSemi(bs []byte, semi bool, vals ...byte) []byte { + if semi { + bs = append(bs, ';') + } + return append(bs, vals...) +} + +func itoa(t byte) string { + var ( + a [3]byte + j = 2 + ) + for i := 0; i < 3; i, j = i+1, j-1 { + a[j] = '0' + t%10 + if t = t / 10; t == 0 { + break + } + } + return string(a[j:]) +} + +func (c Color) appendFg(bs []byte, zero bool) []byte { + + if zero || c&maskFm != 0 { + bs = append(bs, ';') + } + + // 0- 7 : 30-37 + // 8-15 : 90-97 + // > 15 : 38;5;val + + switch fg := (c & maskFg) >> shiftFg; { + case fg <= 7: + // '3' and the value itself + bs = append(bs, '3', '0'+byte(fg)) + case fg <= 15: + // '9' and the value itself + bs = append(bs, '9', '0'+byte(fg&^0x08)) // clear bright flag + default: + bs = append(bs, '3', '8', ';', '5', ';') + bs = append(bs, itoa(byte(fg))...) + } + return bs +} + +func (c Color) appendBg(bs []byte, zero bool) []byte { + + if zero || c&(maskFm|maskFg) != 0 { + bs = append(bs, ';') + } + + // 0- 7 : 40- 47 + // 8-15 : 100-107 + // > 15 : 48;5;val + + switch fg := (c & maskBg) >> shiftBg; { + case fg <= 7: + // '3' and the value itself + bs = append(bs, '4', '0'+byte(fg)) + case fg <= 15: + // '1', '0' and the value itself + bs = append(bs, '1', '0', '0'+byte(fg&^0x08)) // clear bright flag + default: + bs = append(bs, '4', '8', ';', '5', ';') + bs = append(bs, itoa(byte(fg))...) + } + return bs +} + +func (c Color) appendFm9(bs []byte, zero bool) []byte { + + bs = appendCond(bs, c&ItalicFm != 0, + zero || c&(BoldFm|FaintFm) != 0, + '3') + bs = appendCond(bs, c&UnderlineFm != 0, + zero || c&(BoldFm|FaintFm|ItalicFm) != 0, + '4') + // don't combine slow and rapid blink using only + // on of them, preferring slow blink + if c&SlowBlinkFm != 0 { + bs = appendSemi(bs, + zero || c&(BoldFm|FaintFm|ItalicFm|UnderlineFm) != 0, + '5') + } else if c&RapidBlinkFm != 0 { + bs = appendSemi(bs, + zero || c&(BoldFm|FaintFm|ItalicFm|UnderlineFm) != 0, + '6') + } + + // including 1-2 + const mask6i = BoldFm | FaintFm | + ItalicFm | UnderlineFm | + SlowBlinkFm | RapidBlinkFm + + bs = appendCond(bs, c&ReverseFm != 0, + zero || c&(mask6i) != 0, + '7') + bs = appendCond(bs, c&ConcealFm != 0, + zero || c&(mask6i|ReverseFm) != 0, + '8') + bs = appendCond(bs, c&CrossedOutFm != 0, + zero || c&(mask6i|ReverseFm|ConcealFm) != 0, + '9') + + return bs +} + +// append 1;3;38;5;216 like string that represents ANSI +// color of the Color; the zero argument requires +// appending of '0' before to reset previous format +// and colors +func (c Color) appendNos(bs []byte, zero bool) []byte { + + if zero { + bs = append(bs, '0') // reset previous + } + + // formats + // + + if c&maskFm != 0 { + + // 1-2 + + // don't combine bold and faint using only on of them, preferring bold + + if c&BoldFm != 0 { + bs = appendSemi(bs, zero, '1') + } else if c&FaintFm != 0 { + bs = appendSemi(bs, zero, '2') + } + + // 3-9 + + const mask9 = ItalicFm | UnderlineFm | + SlowBlinkFm | RapidBlinkFm | + ReverseFm | ConcealFm | CrossedOutFm + + if c&mask9 != 0 { + bs = c.appendFm9(bs, zero) + } + + // 20-21 + + const ( + mask21 = FrakturFm | DoublyUnderlineFm + mask9i = BoldFm | FaintFm | mask9 + ) + + if c&mask21 != 0 { + bs = appendCond(bs, c&FrakturFm != 0, + zero || c&mask9i != 0, + '2', '0') + bs = appendCond(bs, c&DoublyUnderlineFm != 0, + zero || c&(mask9i|FrakturFm) != 0, + '2', '1') + } + + // 50-53 + + const ( + mask53 = FramedFm | EncircledFm | OverlinedFm + mask21i = mask9i | mask21 + ) + + if c&mask53 != 0 { + bs = appendCond(bs, c&FramedFm != 0, + zero || c&mask21i != 0, + '5', '1') + bs = appendCond(bs, c&EncircledFm != 0, + zero || c&(mask21i|FramedFm) != 0, + '5', '2') + bs = appendCond(bs, c&OverlinedFm != 0, + zero || c&(mask21i|FramedFm|EncircledFm) != 0, + '5', '3') + } + + } + + // foreground + if c&maskFg != 0 { + bs = c.appendFg(bs, zero) + } + + // background + if c&maskBg != 0 { + bs = c.appendBg(bs, zero) + } + + return bs +} diff --git a/vendor/github.com/logrusorgru/aurora/disable.png b/vendor/github.com/logrusorgru/aurora/disable.png new file mode 100644 index 0000000000000000000000000000000000000000..0dd1d63f7bb1ead8384c9cc34904b8e17365c27c GIT binary patch literal 626 zcmV-&0*(ENP)KlFh4WQ5c8ceYUjaqZkV*$$)>r#6YAoKv5L_1BDDs z%p^)CMlui+LJ1ie7$|1iMaf5D7a7RJj_kr2xb35J@9}Z&;NF2}wDxbkYprL$y#O!% zBak1QVYAt}T<)@NG#ZIS;&y(QHvo{!<<)9cEEdzL&*uw=!{43R?RIXr+wFGGX0u1~ zyD*-rzHmOD=kqxLP!#n@{)iAW@}TCFZ?hr@wkm_#B$QPk`8dOV(A)eHtnl1~``z~!D^uXp*RC~7bmTzB+S zVzF3U#tHyPlEg3!K@b3l#bN|O&}E0i;aQ2h+#1a3bb34u1fkJr)N1wl zeBSMLIF5gpS1J{|-F`ZqR;!i4V8Ae}SS&sy0RZWAdc9uj^?IF7x8LuV%Vo3Kq$NtF zl1L=-`~4(I777Jg;`cn0$&e&zHk&yd&S*69`~92E=JwMsUi@o5090}t+<|&sJ^%m! M07*qoM6N<$f=y^4T>t<8 literal 0 HcmV?d00001 diff --git a/vendor/github.com/logrusorgru/aurora/enable.png b/vendor/github.com/logrusorgru/aurora/enable.png new file mode 100644 index 0000000000000000000000000000000000000000..a488367cd6141bb7362c5c06ac45e4d5e7c43e31 GIT binary patch literal 606 zcmV-k0-^nhP)Kls{_}K^Vs0nVVa7GPBupNkR%y5kxHX3)ol$MJy7* zfP(rB0xBtN>_imACY4wOZ4?cN5i1J|K|4862>td$Y4M7At#dlD)gw zNuFx!b6dWG^XQ_loP?@V2Va zvG%8Yf67;jsu#anr2CPLGN8UG05H<@C!-?bZ88}$SAZ2&kI95%t?r*UvAFw%I} zJ?#Cux-)~JnW5invO}Pj zRgohA(2=g$wc@F^ZnxA@C)=joN6LEQtJqUt734tnll0Bz=`(GE=q7)ZzkIc*AP4J+ zm3_$o09nX7WR*Ru2z02$bHUGW%&}}rqYc?j;umo_2DPN@W43+N1_L_QvMIOn#WTUr zanO*jXN&+z_$R5&|Zsi_@% literal 0 HcmV?d00001 diff --git a/vendor/github.com/logrusorgru/aurora/gopher_aurora.png b/vendor/github.com/logrusorgru/aurora/gopher_aurora.png new file mode 100644 index 0000000000000000000000000000000000000000..8f61bb20d2476a00974ca708fa3f946f0c0607c3 GIT binary patch literal 16387 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/logrusorgru/aurora/printf.png b/vendor/github.com/logrusorgru/aurora/printf.png new file mode 100644 index 0000000000000000000000000000000000000000..5978844aac3b666c5e5304e5fe0b5218d7bee110 GIT binary patch literal 3308 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 JPDHLkV1hY+DUJXD literal 0 HcmV?d00001 diff --git a/vendor/github.com/logrusorgru/aurora/sprintf.go b/vendor/github.com/logrusorgru/aurora/sprintf.go new file mode 100644 index 0000000..b92d593 --- /dev/null +++ b/vendor/github.com/logrusorgru/aurora/sprintf.go @@ -0,0 +1,68 @@ +// +// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved. +// This program is free software. It comes without any warranty, +// to the extent permitted by applicable law. You can redistribute +// it and/or modify it under the terms of the Unlicense. See LICENSE +// file for more details or see below. +// + +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to +// + +package aurora + +import ( + "fmt" +) + +// Sprintf allows to use Value as format. For example +// +// v := Sprintf(Red("total: +3.5f points"), Blue(3.14)) +// +// In this case "total:" and "points" will be red, but +// 3.14 will be blue. But, in another example +// +// v := Sprintf(Red("total: +3.5f points"), 3.14) +// +// full string will be red. And no way to clear 3.14 to +// default format and color +func Sprintf(format interface{}, args ...interface{}) string { + switch ft := format.(type) { + case string: + return fmt.Sprintf(ft, args...) + case Value: + for i, v := range args { + if val, ok := v.(Value); ok { + args[i] = val.setTail(ft.Color()) + continue + } + } + return fmt.Sprintf(ft.String(), args...) + } + // unknown type of format (we hope it's a string) + return fmt.Sprintf(fmt.Sprint(format), args...) +} diff --git a/vendor/github.com/logrusorgru/aurora/sprintf.png b/vendor/github.com/logrusorgru/aurora/sprintf.png new file mode 100644 index 0000000000000000000000000000000000000000..df2b2cc0a5dca2abae6981915352550b5a3bede8 GIT binary patch literal 1858 zcmV-I2fg@-P)NvXFSX0<=Y8i}JS~-MW$w;Wy7Xn~Wf$R^ z<4Qi#$02SJx#$6T0DxLddEfL3yM$DPeu$P{k$fFKxlNXiOVko7qlGS|eH}jO6)6=F zzAt#+^!m>G3QrbNC7JbiBb&}_z7`fqeSXs4DL~FgeV|{dV&8Qj~xFc&-r7gIb{Z$f`~=VA3CLT4Ob>;?A=e$qc-P7G;7Rcuud2Tui0T{o^H zK=nfXR(`8QNE7BnN3^3q-VXo_Vh%VDJnnehO}bUAj_RWgGYy#{9LUk-@7f{kxLennav~uWY?T7I(^u6 zI6XBl9-GUC+$N`g_tGKk5af0l$mFg2sJq}`0RXsPblcxO!bSLd{A?8~Y!~FWWWr4p z{i?`yIM;`FR-M^=t#_>N_IH=jWk*~`+U53#nTL-_$Bs+K0brkVUz^;vh2OH8S-sD> zuiM|ff?fdt&p4lXTYkHett_QW*D`B2avL)QPyis4G703D#+Mnn0-~T^sBew5_9#67 zFsKc_;eF#N@u}CtuTPqjMP?BIOc_&?=47s&1+c&xH^u=#xActEym)M`+@O|H;t#~g zTM;k;0J!gR)Dr6Uk=yAtghT)Up25s2hHw#xNBB0jgV~TxXEt9JSR?v~H{pGPePT!( zT0}0|EADj?&h5hX=3sMQybl0|wc&E6d=s~6R3G&ao-hty(XK>rWOiQwKmcJ=r=o`n z4tYq=1?2(>ssSR1y_|Nj$oDJ?ZfYOkvPNd*AJBj=Btp_Y)P&3?S$GhyWq ziWxBfM|v!4)`l~v zC;+%+-m<_dp-L_%F56KDasvS{~Q1q#H?mk ze;EJJ&e{`)NQf}!$1!Hb1eE!wavwZ*!#)r7&vy<9022R51P%vMx6T7jV~02LMtgsl}AirQB0|Hl4Hj0)YN_zda_{xMo~4!CX!)PwinkFyMjsz(9Op z6}{?J&#Qg$zVzPYTz)okwz06Wv9R$S<(&wQ$fS$`hjF-D>3&*#8Uvm(r)s%c7P04| z=j@gVVBX2QvefUe~nQ~la6p0;~9*VJCC!aSb)J8GXOy8QVI?g7&i>#sv+(b&43y2i{}=f zLuZDsYzz?`xu{&+CGHXlaZnq?7A~Pm-c#R8Sc#p&PSsM!j4{p9);ZPzz_dA?-kY3? zrt$P;?Q$7iwoTZ!ncs{7_b2*4)js`L`xpbR7wW6o>L3oD44%Ac-n3f`FviXC4cvxW zzP6kxA2Y^2(?82?^{R1o1-+tPs9)Foi5_*c*G#4jN$8e9L4&O zF76ho#S~x5^L5D|Vo>6riS%Cf+bp6{{BLmp(7({FgvCF}bDKHyrWraFvWNH_$e}aC zR~Uyka2qk8~+rgW8~Ksg-PH4OjE1<54&19*7Ubt@s+pnmWF&manbm wswGlti?)4~_$arYJo3mRk390oBagY^KRVS;zqnLs5&!@I07*qoM6N<$g7>e6vH$=8 literal 0 HcmV?d00001 diff --git a/vendor/github.com/logrusorgru/aurora/value.go b/vendor/github.com/logrusorgru/aurora/value.go new file mode 100644 index 0000000..feda25a --- /dev/null +++ b/vendor/github.com/logrusorgru/aurora/value.go @@ -0,0 +1,745 @@ +// +// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved. +// This program is free software. It comes without any warranty, +// to the extent permitted by applicable law. You can redistribute +// it and/or modify it under the terms of the Unlicense. See LICENSE +// file for more details or see below. +// + +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to +// + +package aurora + +import ( + "fmt" + "strconv" + "unicode/utf8" +) + +// A Value represents any printable value +// with it's color +type Value interface { + // String returns string with colors. If there are any color + // or format the string will be terminated with \033[0m + fmt.Stringer + // Format implements fmt.Formatter interface + fmt.Formatter + // Color returns value's color + Color() Color + // Value returns value's value (welcome to the tautology club) + Value() interface{} + + // internals + tail() Color + setTail(Color) Value + + // Bleach returns copy of original value without colors + // + // Deprecated: use Reset instead. + Bleach() Value + // Reset colors and formats + Reset() Value + + // + // Formats + // + // + // Bold or increased intensity (1). + Bold() Value + // Faint, decreased intensity, reset the Bold (2). + Faint() Value + // + // DoublyUnderline or Bold off, double-underline + // per ECMA-48 (21). It depends. + DoublyUnderline() Value + // Fraktur, rarely supported (20). + Fraktur() Value + // + // Italic, not widely supported, sometimes + // treated as inverse (3). + Italic() Value + // Underline (4). + Underline() Value + // + // SlowBlink, blinking less than 150 + // per minute (5). + SlowBlink() Value + // RapidBlink, blinking 150+ per minute, + // not widely supported (6). + RapidBlink() Value + // Blink is alias for the SlowBlink. + Blink() Value + // + // Reverse video, swap foreground and + // background colors (7). + Reverse() Value + // Inverse is alias for the Reverse + Inverse() Value + // + // Conceal, hidden, not widely supported (8). + Conceal() Value + // Hidden is alias for the Conceal + Hidden() Value + // + // CrossedOut, characters legible, but + // marked for deletion (9). + CrossedOut() Value + // StrikeThrough is alias for the CrossedOut. + StrikeThrough() Value + // + // Framed (51). + Framed() Value + // Encircled (52). + Encircled() Value + // + // Overlined (53). + Overlined() Value + + // + // Foreground colors + // + // + // Black foreground color (30) + Black() Value + // Red foreground color (31) + Red() Value + // Green foreground color (32) + Green() Value + // Yellow foreground color (33) + Yellow() Value + // Brown foreground color (33) + // + // Deprecated: use Yellow instead, following specification + Brown() Value + // Blue foreground color (34) + Blue() Value + // Magenta foreground color (35) + Magenta() Value + // Cyan foreground color (36) + Cyan() Value + // White foreground color (37) + White() Value + // + // Bright foreground colors + // + // BrightBlack foreground color (90) + BrightBlack() Value + // BrightRed foreground color (91) + BrightRed() Value + // BrightGreen foreground color (92) + BrightGreen() Value + // BrightYellow foreground color (93) + BrightYellow() Value + // BrightBlue foreground color (94) + BrightBlue() Value + // BrightMagenta foreground color (95) + BrightMagenta() Value + // BrightCyan foreground color (96) + BrightCyan() Value + // BrightWhite foreground color (97) + BrightWhite() Value + // + // Other + // + // Index of pre-defined 8-bit foreground color + // from 0 to 255 (38;5;n). + // + // 0- 7: standard colors (as in ESC [ 30–37 m) + // 8- 15: high intensity colors (as in ESC [ 90–97 m) + // 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) + // 232-255: grayscale from black to white in 24 steps + // + Index(n uint8) Value + // Gray from 0 to 24. + Gray(n uint8) Value + + // + // Background colors + // + // + // BgBlack background color (40) + BgBlack() Value + // BgRed background color (41) + BgRed() Value + // BgGreen background color (42) + BgGreen() Value + // BgYellow background color (43) + BgYellow() Value + // BgBrown background color (43) + // + // Deprecated: use BgYellow instead, following specification + BgBrown() Value + // BgBlue background color (44) + BgBlue() Value + // BgMagenta background color (45) + BgMagenta() Value + // BgCyan background color (46) + BgCyan() Value + // BgWhite background color (47) + BgWhite() Value + // + // Bright background colors + // + // BgBrightBlack background color (100) + BgBrightBlack() Value + // BgBrightRed background color (101) + BgBrightRed() Value + // BgBrightGreen background color (102) + BgBrightGreen() Value + // BgBrightYellow background color (103) + BgBrightYellow() Value + // BgBrightBlue background color (104) + BgBrightBlue() Value + // BgBrightMagenta background color (105) + BgBrightMagenta() Value + // BgBrightCyan background color (106) + BgBrightCyan() Value + // BgBrightWhite background color (107) + BgBrightWhite() Value + // + // Other + // + // BgIndex of 8-bit pre-defined background color + // from 0 to 255 (48;5;n). + // + // 0- 7: standard colors (as in ESC [ 40–47 m) + // 8- 15: high intensity colors (as in ESC [100–107 m) + // 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) + // 232-255: grayscale from black to white in 24 steps + // + BgIndex(n uint8) Value + // BgGray from 0 to 24. + BgGray(n uint8) Value + + // + // Special + // + // Colorize removes existing colors and + // formats of the argument and applies given. + Colorize(color Color) Value +} + +// Value without colors + +type valueClear struct { + value interface{} +} + +func (vc valueClear) String() string { return fmt.Sprint(vc.value) } + +func (vc valueClear) Color() Color { return 0 } +func (vc valueClear) Value() interface{} { return vc.value } + +func (vc valueClear) tail() Color { return 0 } +func (vc valueClear) setTail(Color) Value { return vc } + +func (vc valueClear) Bleach() Value { return vc } +func (vc valueClear) Reset() Value { return vc } + +func (vc valueClear) Bold() Value { return vc } +func (vc valueClear) Faint() Value { return vc } +func (vc valueClear) DoublyUnderline() Value { return vc } +func (vc valueClear) Fraktur() Value { return vc } +func (vc valueClear) Italic() Value { return vc } +func (vc valueClear) Underline() Value { return vc } +func (vc valueClear) SlowBlink() Value { return vc } +func (vc valueClear) RapidBlink() Value { return vc } +func (vc valueClear) Blink() Value { return vc } +func (vc valueClear) Reverse() Value { return vc } +func (vc valueClear) Inverse() Value { return vc } +func (vc valueClear) Conceal() Value { return vc } +func (vc valueClear) Hidden() Value { return vc } +func (vc valueClear) CrossedOut() Value { return vc } +func (vc valueClear) StrikeThrough() Value { return vc } +func (vc valueClear) Framed() Value { return vc } +func (vc valueClear) Encircled() Value { return vc } +func (vc valueClear) Overlined() Value { return vc } + +func (vc valueClear) Black() Value { return vc } +func (vc valueClear) Red() Value { return vc } +func (vc valueClear) Green() Value { return vc } +func (vc valueClear) Yellow() Value { return vc } +func (vc valueClear) Brown() Value { return vc } +func (vc valueClear) Blue() Value { return vc } +func (vc valueClear) Magenta() Value { return vc } +func (vc valueClear) Cyan() Value { return vc } +func (vc valueClear) White() Value { return vc } +func (vc valueClear) BrightBlack() Value { return vc } +func (vc valueClear) BrightRed() Value { return vc } +func (vc valueClear) BrightGreen() Value { return vc } +func (vc valueClear) BrightYellow() Value { return vc } +func (vc valueClear) BrightBlue() Value { return vc } +func (vc valueClear) BrightMagenta() Value { return vc } +func (vc valueClear) BrightCyan() Value { return vc } +func (vc valueClear) BrightWhite() Value { return vc } +func (vc valueClear) Index(uint8) Value { return vc } +func (vc valueClear) Gray(uint8) Value { return vc } + +func (vc valueClear) BgBlack() Value { return vc } +func (vc valueClear) BgRed() Value { return vc } +func (vc valueClear) BgGreen() Value { return vc } +func (vc valueClear) BgYellow() Value { return vc } +func (vc valueClear) BgBrown() Value { return vc } +func (vc valueClear) BgBlue() Value { return vc } +func (vc valueClear) BgMagenta() Value { return vc } +func (vc valueClear) BgCyan() Value { return vc } +func (vc valueClear) BgWhite() Value { return vc } +func (vc valueClear) BgBrightBlack() Value { return vc } +func (vc valueClear) BgBrightRed() Value { return vc } +func (vc valueClear) BgBrightGreen() Value { return vc } +func (vc valueClear) BgBrightYellow() Value { return vc } +func (vc valueClear) BgBrightBlue() Value { return vc } +func (vc valueClear) BgBrightMagenta() Value { return vc } +func (vc valueClear) BgBrightCyan() Value { return vc } +func (vc valueClear) BgBrightWhite() Value { return vc } +func (vc valueClear) BgIndex(uint8) Value { return vc } +func (vc valueClear) BgGray(uint8) Value { return vc } +func (vc valueClear) Colorize(Color) Value { return vc } + +func (vc valueClear) Format(s fmt.State, verb rune) { + // it's enough for many cases (%-+020.10f) + // % - 1 + // availFlags - 3 (5) + // width - 2 + // prec - 3 (.23) + // verb - 1 + // -------------- + // 10 + format := make([]byte, 1, 10) + format[0] = '%' + var f byte + for i := 0; i < len(availFlags); i++ { + if f = availFlags[i]; s.Flag(int(f)) { + format = append(format, f) + } + } + var width, prec int + var ok bool + if width, ok = s.Width(); ok { + format = strconv.AppendInt(format, int64(width), 10) + } + if prec, ok = s.Precision(); ok { + format = append(format, '.') + format = strconv.AppendInt(format, int64(prec), 10) + } + if verb > utf8.RuneSelf { + format = append(format, string(verb)...) + } else { + format = append(format, byte(verb)) + } + fmt.Fprintf(s, string(format), vc.value) +} + +// Value within colors + +type value struct { + value interface{} // value as it + color Color // this color + tailColor Color // tail color +} + +func (v value) String() string { + if v.color != 0 { + if v.tailColor != 0 { + return esc + v.color.Nos(true) + "m" + + fmt.Sprint(v.value) + + esc + v.tailColor.Nos(true) + "m" + } + return esc + v.color.Nos(false) + "m" + fmt.Sprint(v.value) + clear + } + return fmt.Sprint(v.value) +} + +func (v value) Color() Color { return v.color } + +func (v value) Bleach() Value { + v.color, v.tailColor = 0, 0 + return v +} + +func (v value) Reset() Value { + v.color, v.tailColor = 0, 0 + return v +} + +func (v value) tail() Color { return v.tailColor } + +func (v value) setTail(t Color) Value { + v.tailColor = t + return v +} + +func (v value) Value() interface{} { return v.value } + +func (v value) Format(s fmt.State, verb rune) { + + // it's enough for many cases (%-+020.10f) + // % - 1 + // availFlags - 3 (5) + // width - 2 + // prec - 3 (.23) + // verb - 1 + // -------------- + // 10 + // + + // \033[ 5 + // 0;1;3;4;5;7;8;9;20;21;51;52;53 30 + // 38;5;216 8 + // 48;5;216 8 + // m 1 + // + + // \033[0m 7 + // + // x2 (possible tail color) + // + // 10 + 59 * 2 = 128 + + format := make([]byte, 0, 128) + if v.color != 0 { + format = append(format, esc...) + format = v.color.appendNos(format, v.tailColor != 0) + format = append(format, 'm') + } + format = append(format, '%') + var f byte + for i := 0; i < len(availFlags); i++ { + if f = availFlags[i]; s.Flag(int(f)) { + format = append(format, f) + } + } + var width, prec int + var ok bool + if width, ok = s.Width(); ok { + format = strconv.AppendInt(format, int64(width), 10) + } + if prec, ok = s.Precision(); ok { + format = append(format, '.') + format = strconv.AppendInt(format, int64(prec), 10) + } + if verb > utf8.RuneSelf { + format = append(format, string(verb)...) + } else { + format = append(format, byte(verb)) + } + if v.color != 0 { + if v.tailColor != 0 { + // set next (previous) format clearing current one + format = append(format, esc...) + format = v.tailColor.appendNos(format, true) + format = append(format, 'm') + } else { + format = append(format, clear...) // just clear + } + } + fmt.Fprintf(s, string(format), v.value) +} + +func (v value) Bold() Value { + v.color = (v.color &^ FaintFm) | BoldFm + return v +} + +func (v value) Faint() Value { + v.color = (v.color &^ BoldFm) | FaintFm + return v +} + +func (v value) DoublyUnderline() Value { + v.color |= DoublyUnderlineFm + return v +} + +func (v value) Fraktur() Value { + v.color |= FrakturFm + return v +} + +func (v value) Italic() Value { + v.color |= ItalicFm + return v +} + +func (v value) Underline() Value { + v.color |= UnderlineFm + return v +} + +func (v value) SlowBlink() Value { + v.color = (v.color &^ RapidBlinkFm) | SlowBlinkFm + return v +} + +func (v value) RapidBlink() Value { + v.color = (v.color &^ SlowBlinkFm) | RapidBlinkFm + return v +} + +func (v value) Blink() Value { + return v.SlowBlink() +} + +func (v value) Reverse() Value { + v.color |= ReverseFm + return v +} + +func (v value) Inverse() Value { + return v.Reverse() +} + +func (v value) Conceal() Value { + v.color |= ConcealFm + return v +} + +func (v value) Hidden() Value { + return v.Conceal() +} + +func (v value) CrossedOut() Value { + v.color |= CrossedOutFm + return v +} + +func (v value) StrikeThrough() Value { + return v.CrossedOut() +} + +func (v value) Framed() Value { + v.color |= FramedFm + return v +} + +func (v value) Encircled() Value { + v.color |= EncircledFm + return v +} + +func (v value) Overlined() Value { + v.color |= OverlinedFm + return v +} + +func (v value) Black() Value { + v.color = (v.color &^ maskFg) | BlackFg + return v +} + +func (v value) Red() Value { + v.color = (v.color &^ maskFg) | RedFg + return v +} + +func (v value) Green() Value { + v.color = (v.color &^ maskFg) | GreenFg + return v +} + +func (v value) Yellow() Value { + v.color = (v.color &^ maskFg) | YellowFg + return v +} + +func (v value) Brown() Value { + return v.Yellow() +} + +func (v value) Blue() Value { + v.color = (v.color &^ maskFg) | BlueFg + return v +} + +func (v value) Magenta() Value { + v.color = (v.color &^ maskFg) | MagentaFg + return v +} + +func (v value) Cyan() Value { + v.color = (v.color &^ maskFg) | CyanFg + return v +} + +func (v value) White() Value { + v.color = (v.color &^ maskFg) | WhiteFg + return v +} + +func (v value) BrightBlack() Value { + v.color = (v.color &^ maskFg) | BrightFg | BlackFg + return v +} + +func (v value) BrightRed() Value { + v.color = (v.color &^ maskFg) | BrightFg | RedFg + return v +} + +func (v value) BrightGreen() Value { + v.color = (v.color &^ maskFg) | BrightFg | GreenFg + return v +} + +func (v value) BrightYellow() Value { + v.color = (v.color &^ maskFg) | BrightFg | YellowFg + return v +} + +func (v value) BrightBlue() Value { + v.color = (v.color &^ maskFg) | BrightFg | BlueFg + return v +} + +func (v value) BrightMagenta() Value { + v.color = (v.color &^ maskFg) | BrightFg | MagentaFg + return v +} + +func (v value) BrightCyan() Value { + v.color = (v.color &^ maskFg) | BrightFg | CyanFg + return v +} + +func (v value) BrightWhite() Value { + v.color = (v.color &^ maskFg) | BrightFg | WhiteFg + return v +} + +func (v value) Index(n uint8) Value { + v.color = (v.color &^ maskFg) | (Color(n) << shiftFg) | flagFg + return v +} + +func (v value) Gray(n uint8) Value { + if n > 23 { + n = 23 + } + v.color = (v.color &^ maskFg) | (Color(232+n) << shiftFg) | flagFg + return v +} + +func (v value) BgBlack() Value { + v.color = (v.color &^ maskBg) | BlackBg + return v +} + +func (v value) BgRed() Value { + v.color = (v.color &^ maskBg) | RedBg + return v +} + +func (v value) BgGreen() Value { + v.color = (v.color &^ maskBg) | GreenBg + return v +} + +func (v value) BgYellow() Value { + v.color = (v.color &^ maskBg) | YellowBg + return v +} + +func (v value) BgBrown() Value { + return v.BgYellow() +} + +func (v value) BgBlue() Value { + v.color = (v.color &^ maskBg) | BlueBg + return v +} + +func (v value) BgMagenta() Value { + v.color = (v.color &^ maskBg) | MagentaBg + return v +} + +func (v value) BgCyan() Value { + v.color = (v.color &^ maskBg) | CyanBg + return v +} + +func (v value) BgWhite() Value { + v.color = (v.color &^ maskBg) | WhiteBg + return v +} + +func (v value) BgBrightBlack() Value { + v.color = (v.color &^ maskBg) | BrightBg | BlackBg + return v +} + +func (v value) BgBrightRed() Value { + v.color = (v.color &^ maskBg) | BrightBg | RedBg + return v +} + +func (v value) BgBrightGreen() Value { + v.color = (v.color &^ maskBg) | BrightBg | GreenBg + return v +} + +func (v value) BgBrightYellow() Value { + v.color = (v.color &^ maskBg) | BrightBg | YellowBg + return v +} + +func (v value) BgBrightBlue() Value { + v.color = (v.color &^ maskBg) | BrightBg | BlueBg + return v +} + +func (v value) BgBrightMagenta() Value { + v.color = (v.color &^ maskBg) | BrightBg | MagentaBg + return v +} + +func (v value) BgBrightCyan() Value { + v.color = (v.color &^ maskBg) | BrightBg | CyanBg + return v +} + +func (v value) BgBrightWhite() Value { + v.color = (v.color &^ maskBg) | BrightBg | WhiteBg + return v +} + +func (v value) BgIndex(n uint8) Value { + v.color = (v.color &^ maskBg) | (Color(n) << shiftBg) | flagBg + return v +} + +func (v value) BgGray(n uint8) Value { + if n > 23 { + n = 23 + } + v.color = (v.color &^ maskBg) | (Color(232+n) << shiftBg) | flagBg + return v +} + +func (v value) Colorize(color Color) Value { + v.color = color + return v +} diff --git a/vendor/github.com/logrusorgru/aurora/wrap.go b/vendor/github.com/logrusorgru/aurora/wrap.go new file mode 100644 index 0000000..44e1aa6 --- /dev/null +++ b/vendor/github.com/logrusorgru/aurora/wrap.go @@ -0,0 +1,558 @@ +// +// Copyright (c) 2016-2020 The Aurora Authors. All rights reserved. +// This program is free software. It comes without any warranty, +// to the extent permitted by applicable law. You can redistribute +// it and/or modify it under the terms of the Unlicense. See LICENSE +// file for more details or see below. +// + +// +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. +// +// In jurisdictions that recognize copyright laws, the author or authors +// of this software dedicate any and all copyright interest in the +// software to the public domain. We make this dedication for the benefit +// of the public at large and to the detriment of our heirs and +// successors. We intend this dedication to be an overt act of +// relinquishment in perpetuity of all present and future rights to this +// software under copyright law. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// For more information, please refer to +// + +package aurora + +// Colorize wraps given value into Value with +// given colors. For example +// +// s := Colorize("some", BlueFg|GreenBg|BoldFm) +// +// returns a Value with blue foreground, green +// background and bold. Unlike functions like +// Red/BgBlue/Bold etc. This function clears +// all previous colors and formats. Thus +// +// s := Colorize(Red("some"), BgBlue) +// +// clears red color from value +func Colorize(arg interface{}, color Color) Value { + if val, ok := arg.(value); ok { + val.color = color + return val + } + return value{arg, color, 0} +} + +// Reset wraps given argument returning Value +// without formats and colors. +func Reset(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Reset() + } + return value{value: arg} +} + +// +// Formats +// + +// Bold or increased intensity (1). +func Bold(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Bold() + } + return value{value: arg, color: BoldFm} +} + +// Faint decreases intensity (2). +// The Faint rejects the Bold. +func Faint(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Faint() + } + return value{value: arg, color: FaintFm} +} + +// DoublyUnderline or Bold off, double-underline +// per ECMA-48 (21). +func DoublyUnderline(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.DoublyUnderline() + } + return value{value: arg, color: DoublyUnderlineFm} +} + +// Fraktur is rarely supported (20). +func Fraktur(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Fraktur() + } + return value{value: arg, color: FrakturFm} +} + +// Italic is not widely supported, sometimes +// treated as inverse (3). +func Italic(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Italic() + } + return value{value: arg, color: ItalicFm} +} + +// Underline (4). +func Underline(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Underline() + } + return value{value: arg, color: UnderlineFm} +} + +// SlowBlink makes text blink less than +// 150 per minute (5). +func SlowBlink(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.SlowBlink() + } + return value{value: arg, color: SlowBlinkFm} +} + +// RapidBlink makes text blink 150+ per +// minute. It is not widely supported (6). +func RapidBlink(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.RapidBlink() + } + return value{value: arg, color: RapidBlinkFm} +} + +// Blink is alias for the SlowBlink. +func Blink(arg interface{}) Value { + return SlowBlink(arg) +} + +// Reverse video, swap foreground and +// background colors (7). +func Reverse(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Reverse() + } + return value{value: arg, color: ReverseFm} +} + +// Inverse is alias for the Reverse +func Inverse(arg interface{}) Value { + return Reverse(arg) +} + +// Conceal hides text, preserving an ability to select +// the text and copy it. It is not widely supported (8). +func Conceal(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Conceal() + } + return value{value: arg, color: ConcealFm} +} + +// Hidden is alias for the Conceal +func Hidden(arg interface{}) Value { + return Conceal(arg) +} + +// CrossedOut makes characters legible, but +// marked for deletion (9). +func CrossedOut(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.CrossedOut() + } + return value{value: arg, color: CrossedOutFm} +} + +// StrikeThrough is alias for the CrossedOut. +func StrikeThrough(arg interface{}) Value { + return CrossedOut(arg) +} + +// Framed (51). +func Framed(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Framed() + } + return value{value: arg, color: FramedFm} +} + +// Encircled (52). +func Encircled(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Encircled() + } + return value{value: arg, color: EncircledFm} +} + +// Overlined (53). +func Overlined(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Overlined() + } + return value{value: arg, color: OverlinedFm} +} + +// +// Foreground colors +// +// + +// Black foreground color (30) +func Black(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Black() + } + return value{value: arg, color: BlackFg} +} + +// Red foreground color (31) +func Red(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Red() + } + return value{value: arg, color: RedFg} +} + +// Green foreground color (32) +func Green(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Green() + } + return value{value: arg, color: GreenFg} +} + +// Yellow foreground color (33) +func Yellow(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Yellow() + } + return value{value: arg, color: YellowFg} +} + +// Brown foreground color (33) +// +// Deprecated: use Yellow instead, following specification +func Brown(arg interface{}) Value { + return Yellow(arg) +} + +// Blue foreground color (34) +func Blue(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Blue() + } + return value{value: arg, color: BlueFg} +} + +// Magenta foreground color (35) +func Magenta(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Magenta() + } + return value{value: arg, color: MagentaFg} +} + +// Cyan foreground color (36) +func Cyan(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Cyan() + } + return value{value: arg, color: CyanFg} +} + +// White foreground color (37) +func White(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.White() + } + return value{value: arg, color: WhiteFg} +} + +// +// Bright foreground colors +// + +// BrightBlack foreground color (90) +func BrightBlack(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BrightBlack() + } + return value{value: arg, color: BrightFg | BlackFg} +} + +// BrightRed foreground color (91) +func BrightRed(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BrightRed() + } + return value{value: arg, color: BrightFg | RedFg} +} + +// BrightGreen foreground color (92) +func BrightGreen(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BrightGreen() + } + return value{value: arg, color: BrightFg | GreenFg} +} + +// BrightYellow foreground color (93) +func BrightYellow(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BrightYellow() + } + return value{value: arg, color: BrightFg | YellowFg} +} + +// BrightBlue foreground color (94) +func BrightBlue(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BrightBlue() + } + return value{value: arg, color: BrightFg | BlueFg} +} + +// BrightMagenta foreground color (95) +func BrightMagenta(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BrightMagenta() + } + return value{value: arg, color: BrightFg | MagentaFg} +} + +// BrightCyan foreground color (96) +func BrightCyan(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BrightCyan() + } + return value{value: arg, color: BrightFg | CyanFg} +} + +// BrightWhite foreground color (97) +func BrightWhite(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BrightWhite() + } + return value{value: arg, color: BrightFg | WhiteFg} +} + +// +// Other +// + +// Index of pre-defined 8-bit foreground color +// from 0 to 255 (38;5;n). +// +// 0- 7: standard colors (as in ESC [ 30–37 m) +// 8- 15: high intensity colors (as in ESC [ 90–97 m) +// 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) +// 232-255: grayscale from black to white in 24 steps +// +func Index(n uint8, arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Index(n) + } + return value{value: arg, color: (Color(n) << shiftFg) | flagFg} +} + +// Gray from 0 to 24. +func Gray(n uint8, arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.Gray(n) + } + if n > 23 { + n = 23 + } + return value{value: arg, color: (Color(232+n) << shiftFg) | flagFg} +} + +// +// Background colors +// +// + +// BgBlack background color (40) +func BgBlack(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgBlack() + } + return value{value: arg, color: BlackBg} +} + +// BgRed background color (41) +func BgRed(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgRed() + } + return value{value: arg, color: RedBg} +} + +// BgGreen background color (42) +func BgGreen(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgGreen() + } + return value{value: arg, color: GreenBg} +} + +// BgYellow background color (43) +func BgYellow(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgYellow() + } + return value{value: arg, color: YellowBg} +} + +// BgBrown background color (43) +// +// Deprecated: use BgYellow instead, following specification +func BgBrown(arg interface{}) Value { + return BgYellow(arg) +} + +// BgBlue background color (44) +func BgBlue(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgBlue() + } + return value{value: arg, color: BlueBg} +} + +// BgMagenta background color (45) +func BgMagenta(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgMagenta() + } + return value{value: arg, color: MagentaBg} +} + +// BgCyan background color (46) +func BgCyan(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgCyan() + } + return value{value: arg, color: CyanBg} +} + +// BgWhite background color (47) +func BgWhite(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgWhite() + } + return value{value: arg, color: WhiteBg} +} + +// +// Bright background colors +// + +// BgBrightBlack background color (100) +func BgBrightBlack(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgBrightBlack() + } + return value{value: arg, color: BrightBg | BlackBg} +} + +// BgBrightRed background color (101) +func BgBrightRed(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgBrightRed() + } + return value{value: arg, color: BrightBg | RedBg} +} + +// BgBrightGreen background color (102) +func BgBrightGreen(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgBrightGreen() + } + return value{value: arg, color: BrightBg | GreenBg} +} + +// BgBrightYellow background color (103) +func BgBrightYellow(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgBrightYellow() + } + return value{value: arg, color: BrightBg | YellowBg} +} + +// BgBrightBlue background color (104) +func BgBrightBlue(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgBrightBlue() + } + return value{value: arg, color: BrightBg | BlueBg} +} + +// BgBrightMagenta background color (105) +func BgBrightMagenta(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgBrightMagenta() + } + return value{value: arg, color: BrightBg | MagentaBg} +} + +// BgBrightCyan background color (106) +func BgBrightCyan(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgBrightCyan() + } + return value{value: arg, color: BrightBg | CyanBg} +} + +// BgBrightWhite background color (107) +func BgBrightWhite(arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgBrightWhite() + } + return value{value: arg, color: BrightBg | WhiteBg} +} + +// +// Other +// + +// BgIndex of 8-bit pre-defined background color +// from 0 to 255 (48;5;n). +// +// 0- 7: standard colors (as in ESC [ 40–47 m) +// 8- 15: high intensity colors (as in ESC [100–107 m) +// 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) +// 232-255: grayscale from black to white in 24 steps +// +func BgIndex(n uint8, arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgIndex(n) + } + return value{value: arg, color: (Color(n) << shiftBg) | flagBg} +} + +// BgGray from 0 to 24. +func BgGray(n uint8, arg interface{}) Value { + if val, ok := arg.(Value); ok { + return val.BgGray(n) + } + if n > 23 { + n = 23 + } + return value{value: arg, color: (Color(n+232) << shiftBg) | flagBg} +} diff --git a/vendor/github.com/muun/libwallet/LICENSE b/vendor/github.com/muun/libwallet/LICENSE index 088de6a..502321a 100644 --- a/vendor/github.com/muun/libwallet/LICENSE +++ b/vendor/github.com/muun/libwallet/LICENSE @@ -1,54 +1,21 @@ -Microsoft Reference Source License (Ms-RSL) -=========================================== +The MIT License (MIT) -This license governs use of the accompanying software. If you use the software, -you accept this license. If you do not accept the license, do not use the -software. +Copyright (c) 2021 Muun Wallet, Inc. -1. Definitions --------------- +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The terms "reproduce", "reproduction" and "distribution" have the same meaning -here as under U.S. copyright law. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -"You" means the licensee of the software. - -"Your company" means the company you worked for when you downloaded the -software. - -"Reference use" means use of the software within your company as a reference, in -read only form, for the sole purposes of debugging your products, maintaining -your products, or enhancing the interoperability of your products with the -software, and specifically excludes the right to distribute the software outside -of your company. - -Licensed patents" means any Licensor patent claims which read directly on the -software as distributed by the Licensor under this license. - -2. Grant of Rights ------------------- - -(A) Copyright Grant - Subject to the terms of this license, the Licensor grants -you a non-transferable, non-exclusive, worldwide, royalty-free copyright license -to reproduce the software for reference use. - -(B) Patent Grant - Subject to the terms of this license, the Licensor grants you -a non-transferable, non-exclusive, worldwide, royalty-free patent license under -licensed patents for reference use. - -3. Limitations --------------- - -(A) No Trademark License - This license does not grant you any rights to use the -Licensor's name, logo, or trademarks. - -(B) If you begin patent litigation against the Licensor over patents that you -think may apply to the software (including a cross-claim or counterclaim in a -lawsuit), your license to the software ends automatically. - -(C) The software is licensed "as-is." You bear the risk of using it. The -Licensor gives no express warranties, guarantees or conditions. You may have -additional consumer rights under your local laws which this license cannot -change. To the extent permitted under your local laws, the Licensor excludes the -implied warranties of merchantability, fitness for a particular purpose and -non-infringement. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/muun/libwallet/challenge_keys.go b/vendor/github.com/muun/libwallet/challenge_keys.go index 0f8a6de..51902ca 100644 --- a/vendor/github.com/muun/libwallet/challenge_keys.go +++ b/vendor/github.com/muun/libwallet/challenge_keys.go @@ -32,6 +32,15 @@ type encryptedPrivateKey struct { Salt []byte // (optional) 8-byte salt } +// EncryptedPrivateKeyInfo is a Gomobile-compatible version of EncryptedPrivateKey using hex-encoding. +type EncryptedPrivateKeyInfo struct { + Version int + Birthday int + EphPublicKey string + CipherText string + Salt string +} + type DecryptedPrivateKey struct { Key *HDPrivateKey Birthday int @@ -69,8 +78,17 @@ func (k *ChallengePrivateKey) PubKey() *ChallengePublicKey { return &ChallengePublicKey{pubKey: k.key.PubKey()} } -func (k *ChallengePrivateKey) DecryptKey(encryptedKey string, network *Network) (*DecryptedPrivateKey, error) { - decoded, err := decodeEncryptedPrivateKey(encryptedKey) +func (k *ChallengePrivateKey) DecryptRawKey(encryptedKey string, network *Network) (*DecryptedPrivateKey, error) { + decoded, err := DecodeEncryptedPrivateKey(encryptedKey) + if err != nil { + return nil, err + } + + return k.DecryptKey(decoded, network) +} + +func (k *ChallengePrivateKey) DecryptKey(decodedInfo *EncryptedPrivateKeyInfo, network *Network) (*DecryptedPrivateKey, error) { + decoded, err := unwrapEncryptedPrivateKey(decodedInfo) if err != nil { return nil, err } @@ -94,7 +112,7 @@ func (k *ChallengePrivateKey) DecryptKey(encryptedKey string, network *Network) }, nil } -func decodeEncryptedPrivateKey(encodedKey string) (*encryptedPrivateKey, error) { +func DecodeEncryptedPrivateKey(encodedKey string) (*EncryptedPrivateKeyInfo, error) { reader := bytes.NewReader(base58.Decode(encodedKey)) version, err := reader.ReadByte() if err != nil { @@ -136,12 +154,12 @@ func decodeEncryptedPrivateKey(encodedKey string) (*encryptedPrivateKey, error) } } - result := &encryptedPrivateKey{ - Version: version, - Birthday: birthday, - EphPublicKey: rawPubEph, - CipherText: ciphertext, - Salt: recoveryCodeSalt, + result := &EncryptedPrivateKeyInfo{ + Version: int(version), + Birthday: int(birthday), + EphPublicKey: hex.EncodeToString(rawPubEph), + CipherText: hex.EncodeToString(ciphertext), + Salt: hex.EncodeToString(recoveryCodeSalt), } return result, nil @@ -150,3 +168,30 @@ func decodeEncryptedPrivateKey(encodedKey string) (*encryptedPrivateKey, error) func shouldHaveSalt(encodedKey string) bool { return len(encodedKey) > EncodedKeyLengthLegacy // not military-grade logic, but works for now } + +func unwrapEncryptedPrivateKey(info *EncryptedPrivateKeyInfo) (*encryptedPrivateKey, error) { + ephPublicKey, err := hex.DecodeString(info.EphPublicKey) + if err != nil { + return nil, err + } + + cipherText, err := hex.DecodeString(info.CipherText) + if err != nil { + return nil, err + } + + salt, err := hex.DecodeString(info.Salt) + if err != nil { + return nil, err + } + + unwrapped := &encryptedPrivateKey{ + Version: uint8(info.Version), + Birthday: uint16(info.Birthday), + EphPublicKey: ephPublicKey, + CipherText: cipherText, + Salt: salt, + } + + return unwrapped, nil +} diff --git a/vendor/github.com/muun/libwallet/emergency_kit.go b/vendor/github.com/muun/libwallet/emergency_kit.go index 0fc5e4e..3bb947f 100644 --- a/vendor/github.com/muun/libwallet/emergency_kit.go +++ b/vendor/github.com/muun/libwallet/emergency_kit.go @@ -1,7 +1,6 @@ package libwallet import ( - "encoding/hex" "encoding/json" "fmt" @@ -96,12 +95,12 @@ func createEmergencyKitMetadata(ekParams *EKInput) (*emergencykit.Metadata, erro // boundary to craft the object here. // Decode both keys, to extract their inner properties: - firstKey, err := decodeEncryptedPrivateKey(ekParams.FirstEncryptedKey) + firstKey, err := DecodeEncryptedPrivateKey(ekParams.FirstEncryptedKey) if err != nil { return nil, fmt.Errorf("createEkMetadata failed to decode first key: %w", err) } - secondKey, err := decodeEncryptedPrivateKey(ekParams.SecondEncryptedKey) + secondKey, err := DecodeEncryptedPrivateKey(ekParams.SecondEncryptedKey) if err != nil { return nil, fmt.Errorf("createEkMetadata failed to decode second key: %w", err) } @@ -128,10 +127,10 @@ func createEmergencyKitMetadata(ekParams *EKInput) (*emergencykit.Metadata, erro return metadata, nil } -func createEmergencyKitMetadataKey(key *encryptedPrivateKey) *emergencykit.MetadataKey { +func createEmergencyKitMetadataKey(key *EncryptedPrivateKeyInfo) *emergencykit.MetadataKey { return &emergencykit.MetadataKey{ - DhPubKey: hex.EncodeToString(key.EphPublicKey), - EncryptedPrivKey: hex.EncodeToString(key.CipherText), - Salt: hex.EncodeToString(key.Salt), + DhPubKey: key.EphPublicKey, + EncryptedPrivKey: key.CipherText, + Salt: key.Salt, } } diff --git a/vendor/github.com/muun/libwallet/go.mod b/vendor/github.com/muun/libwallet/go.mod index 5e9299f..8d9afc1 100644 --- a/vendor/github.com/muun/libwallet/go.mod +++ b/vendor/github.com/muun/libwallet/go.mod @@ -10,10 +10,9 @@ require ( github.com/lightningnetwork/lightning-onion v1.0.1 github.com/lightningnetwork/lnd v0.10.4-beta github.com/miekg/dns v1.1.29 // indirect - github.com/pdfcpu/pdfcpu v0.3.8 + github.com/pdfcpu/pdfcpu v0.3.9 github.com/pkg/errors v0.9.1 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 - golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect google.golang.org/protobuf v1.25.0 gopkg.in/gormigrate.v1 v1.6.0 diff --git a/vendor/github.com/muun/libwallet/go.sum b/vendor/github.com/muun/libwallet/go.sum index 9eba078..18b9c69 100644 --- a/vendor/github.com/muun/libwallet/go.sum +++ b/vendor/github.com/muun/libwallet/go.sum @@ -3,7 +3,6 @@ cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT git.schwanenlied.me/yawning/bsaes.git v0.0.0-20180720073208-c0276d75487e h1:F2x1bq7RaNCIuqYpswggh1+c1JmwdnkHNC9wy1KDip0= git.schwanenlied.me/yawning/bsaes.git v0.0.0-20180720073208-c0276d75487e/go.mod h1:BWqTsj8PgcPriQJGl7el20J/7TuT1d/hSyFDXMEpoEo= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e h1:n+DcnTNkQnHlwpsrHoQtkrJIO7CBx029fw6oR4vIob4= github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ= github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82 h1:MG93+PZYs9PyEsj/n5/haQu2gK0h4tUtSy9ejtMwWa0= @@ -68,10 +67,6 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/champo/mobile v0.0.0-20201225234154-3393de95d3bb h1:Doj1b3qkFX5zakU7uJ1lpsER6GNS4R65Zbfrpz2fIWE= -github.com/champo/mobile v0.0.0-20201225234154-3393de95d3bb/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= -github.com/champo/mobile v0.0.0-20201226003606-ef8e5756cda7 h1:jbaq2lXHNbmLj9Ab3upCbYSZ/j/TQ6yzDwie/pNyfqA= -github.com/champo/mobile v0.0.0-20201226003606-ef8e5756cda7/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -226,8 +221,8 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pdfcpu/pdfcpu v0.3.8 h1:wdKii186dzmr/aP/fkJl2s9yT3TZcwc1VqgfabNymGI= -github.com/pdfcpu/pdfcpu v0.3.8/go.mod h1:EfJ1EIo3n5+YlGF53DGe1yF1wQLiqK1eqGDN5LuKALs= +github.com/pdfcpu/pdfcpu v0.3.9 h1:gHPreswsOGwe1zViJxufbvNZf0xhK4mxj/r1CwLp958= +github.com/pdfcpu/pdfcpu v0.3.9/go.mod h1:EfJ1EIo3n5+YlGF53DGe1yF1wQLiqK1eqGDN5LuKALs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -269,16 +264,12 @@ golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190823064033-3a9bac650e44/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM= golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -286,10 +277,7 @@ golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd h1:ePuNC7PZ6O5BzgPn9bZayERXBdfZjUYoXEf5BTfDfh8= -golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -312,6 +300,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -328,19 +317,15 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk= -golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -368,8 +353,10 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v1 v1.0.1/go.mod h1:3NjfXwocQRYAPTq4/fzX+CwUhPRcR/azYRhj8G+LqMo= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gormigrate.v1 v1.6.0 h1:XpYM6RHQPmzwY7Uyu+t+xxMXc86JYFJn4nEc9HzQjsI= gopkg.in/gormigrate.v1 v1.6.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw= @@ -377,6 +364,7 @@ gopkg.in/macaroon-bakery.v2 v2.0.1/go.mod h1:B4/T17l+ZWGwxFSZQmlBwp25x+og7OkhETf gopkg.in/macaroon.v2 v2.0.0/go.mod h1:+I6LnTMkm/uV5ew/0nsulNjL16SK4+C8yDmRUzHR17I= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/muun/libwallet/sphinx/sphinx.go b/vendor/github.com/muun/libwallet/sphinx/sphinx.go index 8176dfe..2f00536 100644 --- a/vendor/github.com/muun/libwallet/sphinx/sphinx.go +++ b/vendor/github.com/muun/libwallet/sphinx/sphinx.go @@ -41,22 +41,23 @@ func Validate( if err != nil { return err } + + amountToForward := payload.ForwardingInfo().AmountToForward + if amount != 0 && amountToForward > amount { + return fmt.Errorf( + "sphinx payment amount does not match (%v != %v)", amount, amountToForward, + ) + } + // Validate payment secret if it exists if payload.MPP != nil { paymentAddr := payload.MPP.PaymentAddr() - amountToForward := payload.ForwardingInfo().AmountToForward total := payload.MultiPath().TotalMsat() if !bytes.Equal(paymentAddr[:], paymentSecret) { return errors.New("sphinx payment secret does not match") } - if amount != 0 && amountToForward > amount { - return fmt.Errorf( - "sphinx payment amount does not match (%v != %v)", amount, amountToForward, - ) - } - if amountToForward < total { return fmt.Errorf("payment is multipart. forwarded amt = %v, total amt = %v", amountToForward, total) } diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/api/booklet.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/api/booklet.go new file mode 100644 index 0000000..18846db --- /dev/null +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/api/booklet.go @@ -0,0 +1,149 @@ +/* + Copyright 2021 The pdfcpu Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package api + +import ( + "io" + "os" + "time" + + "github.com/pdfcpu/pdfcpu/pkg/log" + "github.com/pdfcpu/pdfcpu/pkg/pdfcpu" +) + +// BookletFromImages creates a booklet from images. +func BookletFromImages(conf *pdfcpu.Configuration, imageFileNames []string, nup *pdfcpu.NUp) (*pdfcpu.Context, error) { + if nup.PageDim == nil { + // Set default paper size. + nup.PageDim = pdfcpu.PaperSize[nup.PageSize] + } + + ctx, err := pdfcpu.CreateContextWithXRefTable(conf, nup.PageDim) + if err != nil { + return nil, err + } + + pagesIndRef, err := ctx.Pages() + if err != nil { + return nil, err + } + + // This is the page tree root. + pagesDict, err := ctx.DereferenceDict(*pagesIndRef) + if err != nil { + return nil, err + } + + err = pdfcpu.BookletFromImages(ctx, imageFileNames, nup, pagesDict, pagesIndRef) + + return ctx, err +} + +// Booklet arranges PDF pages on larger sheets of paper and writes the result to w. +func Booklet(rs io.ReadSeeker, w io.Writer, imgFiles, selectedPages []string, nup *pdfcpu.NUp, conf *pdfcpu.Configuration) error { + if conf == nil { + conf = pdfcpu.NewDefaultConfiguration() + } + conf.Cmd = pdfcpu.BOOKLET + + log.Info.Printf("%s", nup) + + // below is very similar to api.NUp + var ( + ctx *pdfcpu.Context + err error + ) + + if nup.ImgInputFile { + + if ctx, err = BookletFromImages(conf, imgFiles, nup); err != nil { + return err + } + + } else { + + if ctx, _, _, err = readAndValidate(rs, conf, time.Now()); err != nil { + return err + } + + if err := ctx.EnsurePageCount(); err != nil { + return err + } + + pages, err := PagesForPageSelection(ctx.PageCount, selectedPages, true) + if err != nil { + return err + } + + if err = ctx.BookletFromPDF(pages, nup); err != nil { + return err + } + } + + if conf.ValidationMode != pdfcpu.ValidationNone { + if err = ValidateContext(ctx); err != nil { + return err + } + } + + if err = WriteContext(ctx, w); err != nil { + return err + } + + log.Stats.Printf("XRefTable:\n%s\n", ctx) + + return nil +} + +// BookletFile rearranges PDF pages or images into a booklet layout and writes the result to outFile. +func BookletFile(inFiles []string, outFile string, selectedPages []string, nup *pdfcpu.NUp, conf *pdfcpu.Configuration) (err error) { + //if nup.ImgInputFile { + // return fmt.Errorf("image file input not yet supported for booklet") + //} + + var f1, f2 *os.File + + // booklet from a PDF + if f1, err = os.Open(inFiles[0]); err != nil { + return err + } + + if f2, err = os.Create(outFile); err != nil { + return err + } + log.CLI.Printf("writing %s...\n", outFile) + + defer func() { + if err != nil { + if f1 != nil { + f1.Close() + } + f2.Close() + return + } + if f1 != nil { + if err = f1.Close(); err != nil { + return + } + } + err = f2.Close() + return + + }() + + return Booklet(f1, f2, inFiles, selectedPages, nup, conf) +} diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/api/nup.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/api/nup.go index 8b9ef8f..37351ee 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/api/nup.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/api/nup.go @@ -25,26 +25,36 @@ import ( "github.com/pdfcpu/pdfcpu/pkg/pdfcpu" ) -// PDFNUp returns an NUp configuration for Nup-ing PDF files. -func PDFNUp(val int, desc string) (*pdfcpu.NUp, error) { +// PDFNUpConfig returns an NUp configuration for Nup-ing PDF files. +func PDFNUpConfig(val int, desc string) (*pdfcpu.NUp, error) { return pdfcpu.PDFNUpConfig(val, desc) } -// ImageNUp returns an NUp configuration for Nup-ing image files. -func ImageNUp(val int, desc string) (*pdfcpu.NUp, error) { +// ImageNUpConfig returns an NUp configuration for Nup-ing image files. +func ImageNUpConfig(val int, desc string) (*pdfcpu.NUp, error) { return pdfcpu.ImageNUpConfig(val, desc) } -// PDFGrid returns a grid configuration for Nup-ing PDF files. -func PDFGrid(rows, cols int, desc string) (*pdfcpu.NUp, error) { +// PDFGridConfig returns a grid configuration for Grid-ing PDF files. +func PDFGridConfig(rows, cols int, desc string) (*pdfcpu.NUp, error) { return pdfcpu.PDFGridConfig(rows, cols, desc) } -// ImageGrid returns a grid configuration for Nup-ing image files. -func ImageGrid(rows, cols int, desc string) (*pdfcpu.NUp, error) { +// ImageGridConfig returns a grid configuration for Grid-ing image files. +func ImageGridConfig(rows, cols int, desc string) (*pdfcpu.NUp, error) { return pdfcpu.ImageGridConfig(rows, cols, desc) } +// PDFBookletConfig returns an NUp configuration for Booklet-ing PDF files. +func PDFBookletConfig(val int, desc string) (*pdfcpu.NUp, error) { + return pdfcpu.PDFBookletConfig(val, desc) +} + +// ImageBookletConfig returns an NUp configuration for Booklet-ing image files. +func ImageBookletConfig(val int, desc string) (*pdfcpu.NUp, error) { + return pdfcpu.ImageBookletConfig(val, desc) +} + // NUpFromImage creates a single page n-up PDF for one image // or a sequence of n-up pages for more than one image. func NUpFromImage(conf *pdfcpu.Configuration, imageFileNames []string, nup *pdfcpu.NUp) (*pdfcpu.Context, error) { diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/font/install.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/font/install.go index 8546351..93da0ab 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/font/install.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/font/install.go @@ -296,7 +296,7 @@ func (t table) parseNamingTable(fd *ttf) error { // table "name" count := int(t.uint16(2)) stringOffset := t.uint16(4) - nameID := uint16(0) + var nameID uint16 baseOff := 6 for i := 0; i < count; i++ { recOff := baseOff + i*12 @@ -406,7 +406,7 @@ func (t table) parseCMapFormat4(fd *ttf) error { } idDelta := uint32(t.uint16(deltaOff + i*2)) idRangeOff := int(t.uint16(rangeOff + i*2)) - v := uint16(0) + var v uint16 for c, j := startCode, 0; c <= endCode && c != 0xFFFF; c++ { if idRangeOff > 0 { v = t.uint16(rangeOff + i*2 + idRangeOff + j*2) diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/booklet.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/booklet.go new file mode 100644 index 0000000..e69a2b8 --- /dev/null +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/booklet.go @@ -0,0 +1,416 @@ +/* + Copyright 2021 The pdfcpu Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package pdfcpu + +import ( + "bytes" + "fmt" + "io" + "os" + + "github.com/pkg/errors" +) + +// DefaultBookletConfig returns the default configuration for a booklet +func DefaultBookletConfig() *NUp { + nup := DefaultNUpConfig() + nup.Margin = 0 + nup.Border = false + nup.BookletGuides = false + return nup +} + +// PDFBookletConfig returns an NUp configuration for booklet-ing PDF files. +func PDFBookletConfig(val int, desc string) (*NUp, error) { + nup := DefaultBookletConfig() + if desc != "" { + if err := ParseNUpDetails(desc, nup); err != nil { + return nil, err + } + } + return nup, ParseNUpValue(val, nup) +} + +// ImageBookletConfig returns an NUp configuration for booklet-ing image files. +func ImageBookletConfig(val int, desc string) (*NUp, error) { + nup, err := PDFBookletConfig(val, desc) + if err != nil { + return nil, err + } + nup.ImgInputFile = true + return nup, nil +} + +func getPageNumber(pageNumbers []int, n int) int { + if n >= len(pageNumbers) { + // Zero represents blank page at end of booklet. + return 0 + } + return pageNumbers[n] +} + +func drawGuideLineLabel(w io.Writer, x, y float64, s string, mb *Rectangle, fm FontMap, rot int) { + fontName := "Helvetica" + td := TextDescriptor{ + FontName: fontName, + FontKey: fm.EnsureKey(fontName), + FontSize: 9, + Scale: 1.0, + ScaleAbs: true, + StrokeCol: Black, + FillCol: Black, + X: x, + Y: y, + Rotation: float64(rot), + Text: s, + } + WriteMultiLine(w, mb, nil, td) +} + +func drawScissor(w io.Writer, mb *Rectangle, fm FontMap) { + fontName := "ZapfDingbats" + td := TextDescriptor{ + FontName: fontName, + FontKey: fm.EnsureKey(fontName), + FontSize: 12, + Scale: 1.0, + ScaleAbs: true, + StrokeCol: Black, + FillCol: Black, + X: 0, + Y: mb.Height()/2 - 4, + Text: string([]byte{byte(34)}), + } + WriteMultiLine(w, mb, nil, td) +} + +func drawBookletGuides(nup *NUp, w io.Writer) FontMap { + width := nup.PageDim.Width + height := nup.PageDim.Height + var fm FontMap = FontMap{} + mb := RectForDim(nup.PageDim.Width, nup.PageDim.Height) + + SetLineWidth(w, 0) + SetStrokeColor(w, Gray) + + switch nup.N() { + case 2: + // Draw horizontal folding line. + fmt.Fprint(w, "[3] 0 d ") + DrawLine(w, 0, height/2, width, height/2) + drawGuideLineLabel(w, 1, height/2+2, "Fold here", mb, fm, 0) + case 4: + // Draw vertical folding line. + fmt.Fprint(w, "[3] 0 d ") + DrawLine(w, width/2, 0, width/2, height) + drawGuideLineLabel(w, width/2-23, 20, "Fold here", mb, fm, 90) + + // Draw horizontal cutting line. + fmt.Fprint(w, "[3] 0 d ") + DrawLine(w, 0, height/2, width, height/2) + drawGuideLineLabel(w, width, height/2+2, "Fold & Cut here", mb, fm, 0) + + // Draw scissors over cutting line. + drawScissor(w, mb, fm) + } + + return fm +} + +type bookletPage struct { + number int + rotate bool +} + +func nup2OutputPageNr(inputPageNr, inputPageCount int, pageNumbers []int) (int, bool) { + var p int + if inputPageNr%2 == 0 { + p = inputPageCount - 1 - inputPageNr/2 + } else { + p = (inputPageNr - 1) / 2 + } + pageNr := getPageNumber(pageNumbers, p) + + // Rotate odd output pages (the back sides) by 180 degrees. + var rotate bool + if inputPageNr%4 < 2 { + rotate = true + } + return pageNr, rotate +} + +func nup4OutputPageNr(inputPageNr int, inputPageCount int, pageNumbers []int) (int, bool) { + bookletPageNumber := inputPageNr / 4 + var p int + if bookletPageNumber%2 == 0 { + // front side + switch inputPageNr % 4 { + case 0: + p = inputPageCount - 1 - bookletPageNumber + case 1: + p = bookletPageNumber + case 2: + p = inputPageCount/2 + bookletPageNumber + case 3: + p = inputPageCount/2 - 1 - bookletPageNumber + } + } else { + // back side + switch inputPageNr % 4 { + case 0: + p = bookletPageNumber + case 1: + p = inputPageCount - 1 - bookletPageNumber + case 2: + p = inputPageCount/2 - 1 - bookletPageNumber + case 3: + p = inputPageCount/2 + bookletPageNumber + } + } + pageNr := getPageNumber(pageNumbers, p) + + // Rotate bottom row of each output page by 180 degrees. + var rotate bool + if inputPageNr%4 >= 2 { + rotate = true + } + return pageNr, rotate +} + +func sortSelectedPagesForBooklet(pages IntSet, nup *NUp) []bookletPage { + pageNumbers := sortSelectedPages(pages) + pageCount := len(pageNumbers) + + // A sheet of paper consists of 2 consecutive output pages. + sheetPageCount := 2 * nup.N() + + // pageCount must be a multiple of the number of pages per sheet. + // If not, we will insert blank pages at the end of the booklet. + if pageCount%sheetPageCount != 0 { + pageCount += sheetPageCount - pageCount%sheetPageCount + } + + bookletPages := make([]bookletPage, pageCount) + + switch nup.N() { + case 2: + // (output page, input page) = [(1,n), (2,1), (3, n-1), (4, 2), (5, n-2), (6, 3), ...] + for i := 0; i < pageCount; i++ { + pageNr, rotate := nup2OutputPageNr(i, pageCount, pageNumbers) + bookletPages[i].number = pageNr + bookletPages[i].rotate = rotate + } + + case 4: + // (output page, input page) = [(1,n), (2,1), (3, n/2+1), (4, n/2-0), (5, 2), (6, n-1), (7, n/2-1), (8, n/2+2) ...] + for i := 0; i < pageCount; i++ { + pageNr, rotate := nup4OutputPageNr(i, pageCount, pageNumbers) + bookletPages[i].number = pageNr + bookletPages[i].rotate = rotate + } + } + + return bookletPages +} + +func (ctx *Context) bookletPages(selectedPages IntSet, nup *NUp, pagesDict Dict, pagesIndRef *IndirectRef) error { + xRefTable := ctx.XRefTable + var buf bytes.Buffer + formsResDict := NewDict() + rr := rectsForGrid(nup) + + for i, bp := range sortSelectedPagesForBooklet(selectedPages, nup) { + + if i > 0 && i%len(rr) == 0 { + + // Wrap complete booklet page. + if err := wrapUpPage(ctx, nup, formsResDict, buf, pagesDict, pagesIndRef); err != nil { + return err + } + + buf.Reset() + formsResDict = NewDict() + } + + rDest := rr[i%len(rr)] + + if bp.number == 0 { + // This is an empty page at the end of a booklet. + if nup.BgColor != nil { + FillRectStacked(&buf, rDest, *nup.BgColor) + } + continue + } + + consolidateRes := true + d, inhPAttrs, err := ctx.PageDict(bp.number, consolidateRes) + if err != nil { + return err + } + if d == nil { + return errors.Errorf("pdfcpu: unknown page number: %d\n", i) + } + + // Retrieve content stream bytes. + bb, err := xRefTable.PageContent(d) + if err == errNoContent { + continue + } + if err != nil { + return err + } + + // Create an object for this resDict in xRefTable. + ir, err := ctx.IndRefForNewObject(inhPAttrs.resources) + if err != nil { + return err + } + + cropBox := inhPAttrs.mediaBox + if inhPAttrs.cropBox != nil { + cropBox = inhPAttrs.cropBox + } + formIndRef, err := createNUpFormForPDF(xRefTable, ir, bb, cropBox) + if err != nil { + return err + } + + formResID := fmt.Sprintf("Fm%d", i) + formsResDict.Insert(formResID, *formIndRef) + + // Append to content stream of page i. + nUpTilePDFBytes(&buf, cropBox, rDest, formResID, nup, bp.rotate) + } + + // Wrap incomplete booklet page. + return wrapUpPage(ctx, nup, formsResDict, buf, pagesDict, pagesIndRef) +} + +// BookletFromPDF creates a booklet version of the PDF represented by xRefTable. +func (ctx *Context) BookletFromPDF(selectedPages IntSet, nup *NUp) error { + n := int(nup.Grid.Width * nup.Grid.Height) + if !(n == 2 || n == 4) { + return fmt.Errorf("pdfcpu: booklet must have n={2,4} pages per sheet, got %d", n) + } + + var mb *Rectangle + + if nup.PageDim == nil { + nup.PageDim = PaperSize[nup.PageSize] + } + + mb = RectForDim(nup.PageDim.Width, nup.PageDim.Height) + + pagesDict := Dict( + map[string]Object{ + "Type": Name("Pages"), + "Count": Integer(0), + "MediaBox": mb.Array(), + }, + ) + + pagesIndRef, err := ctx.IndRefForNewObject(pagesDict) + if err != nil { + return err + } + + nup.PageDim = &Dim{mb.Width(), mb.Height()} + + if err = ctx.bookletPages(selectedPages, nup, pagesDict, pagesIndRef); err != nil { + return err + } + + // Replace original pagesDict. + rootDict, err := ctx.Catalog() + if err != nil { + return err + } + + rootDict.Update("Pages", *pagesIndRef) + return nil +} + +// BookletFromImages creates a booklet version of the image sequence represented by fileNames. +func BookletFromImages(ctx *Context, fileNames []string, nup *NUp, pagesDict Dict, pagesIndRef *IndirectRef) error { + // The order of images in fileNames corresponds to a desired booklet page sequence. + selectedPages := IntSet{} + for i := 1; i <= len(fileNames); i++ { + selectedPages[i] = true + } + + if nup.PageGrid { + nup.PageDim.Width *= nup.Grid.Width + nup.PageDim.Height *= nup.Grid.Height + } + + xRefTable := ctx.XRefTable + formsResDict := NewDict() + var buf bytes.Buffer + rr := rectsForGrid(nup) + + for i, bp := range sortSelectedPagesForBooklet(selectedPages, nup) { + + if i > 0 && i%len(rr) == 0 { + + // Wrap complete booklet page. + if err := wrapUpPage(ctx, nup, formsResDict, buf, pagesDict, pagesIndRef); err != nil { + return err + } + + buf.Reset() + formsResDict = NewDict() + } + + rDest := rr[i%len(rr)] + + if bp.number == 0 { + // This is an empty page at the end of a booklet. + if nup.BgColor != nil { + FillRectStacked(&buf, rDest, *nup.BgColor) + } + continue + } + + f, err := os.Open(fileNames[bp.number-1]) + if err != nil { + return err + } + + imgIndRef, w, h, err := createImageResource(xRefTable, f) + if err != nil { + return err + } + + if err := f.Close(); err != nil { + return err + } + + formIndRef, err := createNUpFormForImage(xRefTable, imgIndRef, w, h, i) + if err != nil { + return err + } + + formResID := fmt.Sprintf("Fm%d", i) + formsResDict.Insert(formResID, *formIndRef) + + // Append to content stream of booklet page i. + nUpTilePDFBytes(&buf, RectForDim(float64(w), float64(h)), rr[i%len(rr)], formResID, nup, bp.rotate) + } + + // Wrap incomplete booklet page. + return wrapUpPage(ctx, nup, formsResDict, buf, pagesDict, pagesIndRef) +} diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/bookmarks.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/bookmarks.go index cef65fe..bc9e3fd 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/bookmarks.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/bookmarks.go @@ -16,7 +16,11 @@ limitations under the License. package pdfcpu -import "github.com/pkg/errors" +import ( + "strings" + + "github.com/pkg/errors" +) var ( errNoBookmarks = errors.New("pdfcpu: no bookmarks available") @@ -75,6 +79,33 @@ func (ctx *Context) positionToOutlineTreeLevel1() (Dict, *IndirectRef, error) { return d, first, nil } +func (ctx *Context) dereferenceDestPageNumber(dest Object) (IndirectRef, error) { + var ir IndirectRef + switch dest := dest.(type) { + case Name: + arr, err := ctx.dereferenceDestinationArray(dest.Value()) + if err != nil { + return ir, err + } + ir = arr[0].(IndirectRef) + case StringLiteral: + arr, err := ctx.dereferenceDestinationArray(dest.Value()) + if err != nil { + return ir, err + } + ir = arr[0].(IndirectRef) + case HexLiteral: + arr, err := ctx.dereferenceDestinationArray(dest.Value()) + if err != nil { + return ir, err + } + ir = arr[0].(IndirectRef) + case Array: + ir = dest[0].(IndirectRef) + } + return ir, nil +} + // BookmarksForOutlineLevel1 returns bookmarks incliuding page span info. func (ctx *Context) BookmarksForOutlineLevel1() ([]Bookmark, error) { d, first, err := ctx.positionToOutlineTreeLevel1() @@ -91,39 +122,29 @@ func (ctx *Context) BookmarksForOutlineLevel1() ([]Bookmark, error) { return nil, err } - title, _ := Text(d["Title"]) + s, _ := Text(d["Title"]) + var sb strings.Builder + for i := 0; i < len(s); i++ { + b := s[i] + if b >= 32 { + if b == 32 { + b = '_' + } + sb.WriteByte(b) + } + } + title := sb.String() dest, found := d["Dest"] if !found { return nil, errNoBookmarks } - var ir IndirectRef - dest, _ = ctx.Dereference(dest) - switch dest := dest.(type) { - case Name: - arr, err := ctx.dereferenceDestinationArray(dest.Value()) - if err != nil { - return nil, err - } - ir = arr[0].(IndirectRef) - case StringLiteral: - arr, err := ctx.dereferenceDestinationArray(dest.Value()) - if err != nil { - return nil, err - } - ir = arr[0].(IndirectRef) - case HexLiteral: - arr, err := ctx.dereferenceDestinationArray(dest.Value()) - if err != nil { - return nil, err - } - ir = arr[0].(IndirectRef) - case Array: - ir = dest[0].(IndirectRef) - + ir, err := ctx.dereferenceDestPageNumber(dest) + if err != nil { + return nil, err } pageFrom, err := ctx.PageNumber(ir.ObjectNumber.Value()) diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/boxes.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/boxes.go index e6a3a74..ae18425 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/boxes.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/boxes.go @@ -487,7 +487,7 @@ func parseBoxBy3MarginVals(s, s1, s2, s3 string, abs bool, u DisplayUnit) (*Box, } func parseBoxBy4Percentages(s, s1, s2, s3, s4 string) (*Box, error) { - // 10% 15.5% 10% + // 10% 15% 15% 10% // Parse top margin. s1 = s1[:len(s1)-1] if len(s1) == 0 { @@ -499,23 +499,24 @@ func parseBoxBy4Percentages(s, s1, s2, s3, s4 string) (*Box, error) { } tm := pct / 100 + // Parse right margin. if s2[len(s2)-1] != '%' { return nil, errors.Errorf("pdfcpu: invalid box definition: %s", s) } - // Parse hor margin. s2 = s2[:len(s2)-1] if len(s2) == 0 { return nil, errors.Errorf("pdfcpu: invalid box definition: %s", s) } - hm, err := parseBoxPercentage(s2) + pct, err = strconv.ParseFloat(s1, 64) if err != nil { return nil, err } + rm := pct / 100 + // Parse bottom margin. if s3[len(s3)-1] != '%' { return nil, errors.Errorf("pdfcpu: invalid box definition: %s", s) } - // Parse bottom margin. s3 = s3[:len(s3)-1] if len(s3) == 0 { return nil, errors.Errorf("pdfcpu: invalid box definition: %s", s) @@ -525,11 +526,29 @@ func parseBoxBy4Percentages(s, s1, s2, s3, s4 string) (*Box, error) { return nil, err } bm := pct / 100 + + // Parse left margin. + if s4[len(s4)-1] != '%' { + return nil, errors.Errorf("pdfcpu: invalid box definition: %s", s) + } + s4 = s4[:len(s4)-1] + if len(s4) == 0 { + return nil, errors.Errorf("pdfcpu: invalid box definition: %s", s) + } + pct, err = strconv.ParseFloat(s3, 64) + if err != nil { + return nil, err + } + lm := pct / 100 + if tm+bm >= 1 { return nil, errors.Errorf("pdfcpu: vertical margin overflow: %s", s) } + if rm+lm >= 1 { + return nil, errors.Errorf("pdfcpu: horizontal margin overflow: %s", s) + } - return &Box{MLeft: hm, MRight: hm, MTop: tm, MBot: bm}, nil + return &Box{MLeft: lm, MRight: rm, MTop: tm, MBot: bm}, nil } func parseBoxBy4MarginVals(s, s1, s2, s3, s4 string, abs bool, u DisplayUnit) (*Box, error) { @@ -554,14 +573,14 @@ func parseBoxBy4MarginVals(s, s1, s2, s3, s4 string, abs bool, u DisplayUnit) (* return nil, err } - // Parse botton margin. + // Parse bottom margin. bm, err := strconv.ParseFloat(s3, 64) if err != nil { return nil, err } // Parse left margin. - lm, err := strconv.ParseFloat(s1, 64) + lm, err := strconv.ParseFloat(s4, 64) if err != nil { return nil, err } diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/configuration.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/configuration.go index d3a8262..f151059 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/configuration.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/configuration.go @@ -85,6 +85,7 @@ const ( REMOVEPAGES ROTATE NUP + BOOKLET INFO CHEATSHEETSFONTS INSTALLFONTS diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/create.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/create.go index fe7021b..2af31af 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/create.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/create.go @@ -19,6 +19,7 @@ package pdfcpu import ( "bytes" "fmt" + "io" "math" "strconv" "strings" @@ -186,64 +187,73 @@ func decodeUTF8ToByte(s string) string { } // SetLineJoinStyle sets the line join style for stroking operations. -func SetLineJoinStyle(b *bytes.Buffer, s LineJoinStyle) { - b.WriteString(fmt.Sprintf("%d j ", s)) +func SetLineJoinStyle(w io.Writer, s LineJoinStyle) { + fmt.Fprintf(w, "%d j ", s) } // SetLineWidth sets line width for stroking operations. -func SetLineWidth(b *bytes.Buffer, w float64) { - b.WriteString(fmt.Sprintf("%.2f w ", w)) +func SetLineWidth(w io.Writer, width float64) { + fmt.Fprintf(w, "%.2f w ", width) } // DrawLine draws the path from P to Q. -func DrawLine(b *bytes.Buffer, xp, yp, xq, yq float64) { - b.WriteString(fmt.Sprintf("%.2f %.2f m %.2f %.2f l s ", xp, yp, xq, yq)) +func DrawLine(w io.Writer, xp, yp, xq, yq float64) { + fmt.Fprintf(w, "%.2f %.2f m %.2f %.2f l s ", xp, yp, xq, yq) } // DrawRect strokes a rectangular path for r. -func DrawRect(b *bytes.Buffer, r *Rectangle) { - b.WriteString(fmt.Sprintf("%.2f %.2f %.2f %.2f re s ", r.LL.X, r.LL.Y, r.Width(), r.Height())) +func DrawRect(w io.Writer, r *Rectangle) { + fmt.Fprintf(w, "%.2f %.2f %.2f %.2f re s ", r.LL.X, r.LL.Y, r.Width(), r.Height()) } // DrawAndFillRect strokes and fills a rectangular path for r. -func DrawAndFillRect(b *bytes.Buffer, r *Rectangle) { - b.WriteString(fmt.Sprintf("%.2f %.2f %.2f %.2f re B ", r.LL.X, r.LL.Y, r.Width(), r.Height())) +func DrawAndFillRect(w io.Writer, r *Rectangle) { + fmt.Fprintf(w, "%.2f %.2f %.2f %.2f re B ", r.LL.X, r.LL.Y, r.Width(), r.Height()) } // SetFillColor sets the fill color. -func SetFillColor(bb *bytes.Buffer, c SimpleColor) { - bb.WriteString(fmt.Sprintf("%.2f %.2f %.2f rg ", c.R, c.G, c.B)) +func SetFillColor(w io.Writer, c SimpleColor) { + fmt.Fprintf(w, "%.2f %.2f %.2f rg ", c.R, c.G, c.B) } // SetStrokeColor sets the stroke color. -func SetStrokeColor(bb *bytes.Buffer, c SimpleColor) { - bb.WriteString(fmt.Sprintf("%.2f %.2f %.2f RG ", c.R, c.G, c.B)) +func SetStrokeColor(w io.Writer, c SimpleColor) { + fmt.Fprintf(w, "%.2f %.2f %.2f RG ", c.R, c.G, c.B) } // FillRect draws and fills a rectangle using r, g, b. -func FillRect(bb *bytes.Buffer, rect *Rectangle, c SimpleColor) { - SetFillColor(bb, c) - DrawAndFillRect(bb, rect) +func FillRect(w io.Writer, rect *Rectangle, c SimpleColor) { + SetFillColor(w, c) + DrawAndFillRect(w, rect) +} + +// FillRectStacked is a safe way to fill a rectangle within a page content stream. +func FillRectStacked(w io.Writer, r *Rectangle, c SimpleColor) { + fmt.Fprintf(w, "q ") + SetLineWidth(w, 0) + SetStrokeColor(w, c) + FillRect(w, r, c) + fmt.Fprintf(w, "Q ") } // DrawGrid draws an x * y grid on r using strokeCol and fillCol. -func DrawGrid(bb *bytes.Buffer, x, y int, r *Rectangle, strokeCol SimpleColor, fillCol *SimpleColor) { - SetLineWidth(bb, 0) - SetStrokeColor(bb, strokeCol) +func DrawGrid(w io.Writer, x, y int, r *Rectangle, strokeCol SimpleColor, fillCol *SimpleColor) { + SetLineWidth(w, 0) + SetStrokeColor(w, strokeCol) if fillCol != nil { - FillRect(bb, r, *fillCol) + FillRect(w, r, *fillCol) } s := r.Width() / float64(x) for i := 0; i <= x; i++ { x := r.LL.X + float64(i)*s - DrawLine(bb, x, r.LL.Y, x, r.UR.Y) + DrawLine(w, x, r.LL.Y, x, r.UR.Y) } s = r.Height() / float64(y) for i := 0; i <= y; i++ { y := r.LL.Y + float64(i)*s - DrawLine(bb, r.LL.X, y, r.UR.X, y) + DrawLine(w, r.LL.X, y, r.UR.X, y) } } @@ -314,7 +324,7 @@ func calcBoundingBoxForJLines(lines []string, x, y, w float64, fontName string, } // DrawHairCross draw a haircross with origin x/y. -func DrawHairCross(buf *bytes.Buffer, x, y float64, r *Rectangle) { +func DrawHairCross(w io.Writer, x, y float64, r *Rectangle) { x1, y1 := x, y if x == 0 { x1 = r.LL.X + r.Width()/2 @@ -322,10 +332,10 @@ func DrawHairCross(buf *bytes.Buffer, x, y float64, r *Rectangle) { if y == 0 { y1 = r.LL.Y + r.Height()/2 } - SetLineWidth(buf, 0) - SetStrokeColor(buf, Black) - DrawLine(buf, r.LL.X, y1, r.LL.X+r.Width(), y1) // Horizontal line - DrawLine(buf, x1, r.LL.Y, x1, r.LL.Y+r.Height()) // Vertical line + SetLineWidth(w, 0) + SetStrokeColor(w, Black) + DrawLine(w, r.LL.X, y1, r.LL.X+r.Width(), y1) // Horizontal line + DrawLine(w, x1, r.LL.Y, x1, r.LL.Y+r.Height()) // Vertical line } func prepBytes(s, fontName string) string { @@ -346,14 +356,14 @@ func prepBytes(s, fontName string) string { return *s1 } -func writeStringToBuf(buf *bytes.Buffer, s string, x, y float64, strokeCol, fillCol SimpleColor, rm RenderMode, fontName string) { +func writeStringToBuf(w io.Writer, s string, x, y float64, strokeCol, fillCol SimpleColor, rm RenderMode, fontName string) { s = prepBytes(s, fontName) - buf.WriteString(fmt.Sprintf("BT 0 Tw %.2f %.2f %.2f RG %.2f %.2f %.2f rg %.2f %.2f Td %d Tr (%s) Tj ET ", - strokeCol.R, strokeCol.G, strokeCol.B, fillCol.R, fillCol.G, fillCol.B, x, y, rm, s)) + fmt.Fprintf(w, "BT 0 Tw %.2f %.2f %.2f RG %.2f %.2f %.2f rg %.2f %.2f Td %d Tr (%s) Tj ET ", + strokeCol.R, strokeCol.G, strokeCol.B, fillCol.R, fillCol.G, fillCol.B, x, y, rm, s) } -func setFont(b *bytes.Buffer, fontID string, fontSize float32) { - b.WriteString(fmt.Sprintf("BT /%s %.2f Tf ET ", fontID, fontSize)) +func setFont(w io.Writer, fontID string, fontSize float32) { + fmt.Fprintf(w, "BT /%s %.2f Tf ET ", fontID, fontSize) } func calcBoundingBox(s string, x, y float64, fontName string, fontSize int) *Rectangle { @@ -632,9 +642,9 @@ func createBoundingBoxForColumn(r *Rectangle, x, y *float64, return box } -func flushJustifiedStringToBuf(buf *bytes.Buffer, s string, x, y float64, strokeCol, fillCol SimpleColor, rm RenderMode) { - buf.WriteString(fmt.Sprintf("BT 0 Tw %.2f %.2f %.2f RG %.2f %.2f %.2f rg %.2f %.2f Td %d Tr %s ET ", - strokeCol.R, strokeCol.G, strokeCol.B, fillCol.R, fillCol.G, fillCol.B, x, y, rm, s)) +func flushJustifiedStringToBuf(w io.Writer, s string, x, y float64, strokeCol, fillCol SimpleColor, rm RenderMode) { + fmt.Fprintf(w, "BT 0 Tw %.2f %.2f %.2f RG %.2f %.2f %.2f rg %.2f %.2f Td %d Tr %s ET ", + strokeCol.R, strokeCol.G, strokeCol.B, fillCol.R, fillCol.G, fillCol.B, x, y, rm, s) } func scaleXForRegion(x float64, mediaBox, region *Rectangle) float64 { @@ -645,43 +655,59 @@ func scaleYForRegion(y float64, mediaBox, region *Rectangle) float64 { return y / mediaBox.Width() * region.Width() } -func drawMargins(buf *bytes.Buffer, c SimpleColor, colBB *Rectangle, borderWidth, mLeft, mRight, mTop, mBot float64) { - SetLineWidth(buf, 0) - SetStrokeColor(buf, c) +func drawMargins(w io.Writer, c SimpleColor, colBB *Rectangle, borderWidth, mLeft, mRight, mTop, mBot float64) { + if mLeft <= 0 && mRight <= 0 && mTop <= 0 && mBot <= 0 { + return + } - r := RectForWidthAndHeight(colBB.LL.X+borderWidth, colBB.LL.Y+borderWidth, colBB.Width()-2*borderWidth, mBot) - FillRect(buf, r, c) + fmt.Fprintf(w, "q ") + SetLineWidth(w, 0) + SetStrokeColor(w, c) + var r *Rectangle - r = RectForWidthAndHeight(colBB.LL.X+borderWidth, colBB.Height()-borderWidth-mTop, colBB.Width()-2*borderWidth, mTop) - FillRect(buf, r, c) + if mBot > 0 { + r = RectForWidthAndHeight(colBB.LL.X+borderWidth, colBB.LL.Y+borderWidth, colBB.Width()-2*borderWidth, mBot) + FillRect(w, r, c) + } - r = RectForWidthAndHeight(colBB.LL.X+borderWidth, colBB.LL.Y+borderWidth+mBot, mLeft, colBB.Height()-2*borderWidth-mTop-mBot) - FillRect(buf, r, c) + if mTop > 0 { + r = RectForWidthAndHeight(colBB.LL.X+borderWidth, colBB.UR.Y-borderWidth-mTop, colBB.Width()-2*borderWidth, mTop) + FillRect(w, r, c) + } - r = RectForWidthAndHeight(colBB.UR.X-borderWidth-mRight, colBB.LL.Y+borderWidth+mBot, mRight, colBB.Height()-2*borderWidth-mTop-mBot) - FillRect(buf, r, c) + if mLeft > 0 { + r = RectForWidthAndHeight(colBB.LL.X+borderWidth, colBB.LL.Y+borderWidth+mBot, mLeft, colBB.Height()-2*borderWidth-mTop-mBot) + FillRect(w, r, c) + } + + if mRight > 0 { + r = RectForWidthAndHeight(colBB.UR.X-borderWidth-mRight, colBB.LL.Y+borderWidth+mBot, mRight, colBB.Height()-2*borderWidth-mTop-mBot) + FillRect(w, r, c) + } + + fmt.Fprintf(w, "Q ") } -func renderBackgroundAndBorder(buf *bytes.Buffer, td TextDescriptor, borderWidth float64, colBB *Rectangle) { - SetLineJoinStyle(buf, td.BorderStyle) +func renderBackgroundAndBorder(w io.Writer, td TextDescriptor, borderWidth float64, colBB *Rectangle) { + SetLineJoinStyle(w, td.BorderStyle) if td.ShowBackground { - SetLineWidth(buf, borderWidth) + SetLineWidth(w, borderWidth) c := td.BackgroundCol if td.ShowBorder { c = td.BorderCol } - SetStrokeColor(buf, c) + SetStrokeColor(w, c) r := RectForWidthAndHeight(colBB.LL.X+borderWidth/2, colBB.LL.Y+borderWidth/2, colBB.Width()-borderWidth, colBB.Height()-borderWidth) - FillRect(buf, r, td.BackgroundCol) + FillRect(w, r, td.BackgroundCol) } else if td.ShowBorder { - SetLineWidth(buf, borderWidth) - SetStrokeColor(buf, td.BorderCol) + SetLineWidth(w, borderWidth) + SetStrokeColor(w, td.BorderCol) r := RectForWidthAndHeight(colBB.LL.X+borderWidth/2, colBB.LL.Y+borderWidth/2, colBB.Width()-borderWidth, colBB.Height()-borderWidth) - DrawRect(buf, r) + DrawRect(w, r) } } -func renderText(buf *bytes.Buffer, lines []string, td TextDescriptor, x, y float64, fontName string, fontSize int) { +func renderText(w io.Writer, lines []string, td TextDescriptor, x, y float64, fontName string, fontSize int) { lh := font.LineHeight(fontName, fontSize) for _, s := range lines { if td.HAlign != AlignJustify { @@ -697,16 +723,16 @@ func renderText(buf *bytes.Buffer, lines []string, td TextDescriptor, x, y float lineBB.Translate(-dx, 0) if td.ShowLineBB { // Draw line bounding box. - SetStrokeColor(buf, Black) - DrawRect(buf, lineBB) + SetStrokeColor(w, Black) + DrawRect(w, lineBB) } - writeStringToBuf(buf, s, x-dx, y, td.StrokeCol, td.FillCol, td.RMode, fontName) + writeStringToBuf(w, s, x-dx, y, td.StrokeCol, td.FillCol, td.RMode, fontName) y -= lh continue } if len(s) > 0 { - flushJustifiedStringToBuf(buf, s, x, y, td.StrokeCol, td.FillCol, td.RMode) + flushJustifiedStringToBuf(w, s, x, y, td.StrokeCol, td.FillCol, td.RMode) } y -= lh } @@ -715,7 +741,7 @@ func renderText(buf *bytes.Buffer, lines []string, td TextDescriptor, x, y float // WriteColumn writes a text column using s at position x/y using a certain font, fontsize and a desired horizontal and vertical alignment. // Enforce a desired column width by supplying a width > 0 (especially useful for justified text). // It returns the bounding box of this column. -func WriteColumn(buf *bytes.Buffer, mediaBox, region *Rectangle, td TextDescriptor, width float64) *Rectangle { +func WriteColumn(w io.Writer, mediaBox, region *Rectangle, td TextDescriptor, width float64) *Rectangle { x, y, dx, dy := td.X, td.Y, td.Dx, td.Dy mTop, mBot, mLeft, mRight := td.MTop, td.MBot, td.MLeft, td.MRight s, fontSize, borderWidth := td.Text, td.FontSize, td.BorderWidth @@ -780,9 +806,9 @@ func WriteColumn(buf *bytes.Buffer, mediaBox, region *Rectangle, td TextDescript td.Scale, td.ScaleAbs, td.ParIndent, td.FontName, &fontSize, &lines) - setFont(buf, td.FontKey, float32(fontSize)) + setFont(w, td.FontKey, float32(fontSize)) m := calcRotateTransformMatrix(td.Rotation, x, y, colBB) - fmt.Fprintf(buf, "q %.2f %.2f %.2f %.2f %.2f %.2f cm ", m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1]) + fmt.Fprintf(w, "q %.2f %.2f %.2f %.2f %.2f %.2f cm ", m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1]) x -= colBB.LL.X y -= colBB.LL.Y @@ -790,21 +816,21 @@ func WriteColumn(buf *bytes.Buffer, mediaBox, region *Rectangle, td TextDescript // Render background and border. if td.ShowTextBB { - renderBackgroundAndBorder(buf, td, borderWidth, colBB) + renderBackgroundAndBorder(w, td, borderWidth, colBB) } // Render margins. if td.ShowMargins { - drawMargins(buf, LightGray, colBB, borderWidth, mLeft, mRight, mTop, mBot) + drawMargins(w, LightGray, colBB, borderWidth, mLeft, mRight, mTop, mBot) } // Render text. - renderText(buf, lines, td, x, y, td.FontName, fontSize) + renderText(w, lines, td, x, y, td.FontName, fontSize) - buf.WriteString("Q ") + fmt.Fprintf(w, "Q ") if td.HairCross { - DrawHairCross(buf, x0, y0, r) + DrawHairCross(w, x0, y0, r) } return colBB @@ -812,8 +838,8 @@ func WriteColumn(buf *bytes.Buffer, mediaBox, region *Rectangle, td TextDescript // WriteMultiLine writes s at position x/y using a certain font, fontsize and a desired horizontal and vertical alignment. // It returns the bounding box of this text column. -func WriteMultiLine(buf *bytes.Buffer, mediaBox, region *Rectangle, td TextDescriptor) *Rectangle { - return WriteColumn(buf, mediaBox, region, td, 0) +func WriteMultiLine(w io.Writer, mediaBox, region *Rectangle, td TextDescriptor) *Rectangle { + return WriteColumn(w, mediaBox, region, td, 0) } func anchorPosAndAlign(a anchor, r *Rectangle) (x, y float64, hAlign HAlignment, vAlign VAlignment) { @@ -841,22 +867,22 @@ func anchorPosAndAlign(a anchor, r *Rectangle) (x, y float64, hAlign HAlignment, } // WriteMultiLineAnchored writes multiple lines with anchored position and returns its bounding box. -func WriteMultiLineAnchored(buf *bytes.Buffer, mediaBox, region *Rectangle, td TextDescriptor, a anchor) *Rectangle { +func WriteMultiLineAnchored(w io.Writer, mediaBox, region *Rectangle, td TextDescriptor, a anchor) *Rectangle { r := mediaBox if region != nil { r = region } td.X, td.Y, td.HAlign, td.VAlign = anchorPosAndAlign(a, r) - return WriteMultiLine(buf, mediaBox, region, td) + return WriteMultiLine(w, mediaBox, region, td) } // WriteColumnAnchored writes a justified text column with anchored position and returns its bounding box. -func WriteColumnAnchored(buf *bytes.Buffer, mediaBox, region *Rectangle, td TextDescriptor, a anchor, width float64) *Rectangle { +func WriteColumnAnchored(w io.Writer, mediaBox, region *Rectangle, td TextDescriptor, a anchor, width float64) *Rectangle { r := mediaBox if region != nil { r = region } td.HAlign = AlignJustify td.X, td.Y, _, td.VAlign = anchorPosAndAlign(a, r) - return WriteColumn(buf, mediaBox, region, td, width) + return WriteColumn(w, mediaBox, region, td, width) } diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/createAnnotations.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/createAnnotations.go index 68b9c25..4e14b66 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/createAnnotations.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/createAnnotations.go @@ -17,7 +17,6 @@ limitations under the License. package pdfcpu import ( - "path" "path/filepath" "time" ) @@ -522,7 +521,7 @@ func createPopupAnnotation(xRefTable *XRefTable, pageIndRef IndirectRef, annotRe func createFileAttachmentAnnotation(xRefTable *XRefTable, pageIndRef IndirectRef, annotRect Array) (*IndirectRef, error) { - // macOS starts up iTunes for FileAttachments. + // macOS starts up iTunes for audio file attachments. fileName := testAudioFileWAV @@ -531,7 +530,7 @@ func createFileAttachmentAnnotation(xRefTable *XRefTable, pageIndRef IndirectRef return nil, err } - fn := path.Base(fileName) + fn := filepath.Base(fileName) fileSpecDict, err := xRefTable.NewFileSpecDict(fn, encodeUTF16String(fn), "attached by pdfcpu", *ir) if err != nil { return nil, err @@ -571,7 +570,7 @@ func createFileSpecDict(xRefTable *XRefTable, fileName string) (Dict, error) { if err != nil { return nil, err } - fn := path.Base(fileName) + fn := filepath.Base(fileName) return xRefTable.NewFileSpecDict(fn, encodeUTF16String(fn), "attached by pdfcpu", *ir) } @@ -993,7 +992,7 @@ func createEmbeddedGoToAction(xRefTable *XRefTable) (*IndirectRef, error) { "T": Dict( map[string]Object{ "R": Name("C"), - "N": StringLiteral(f), + "N": StringLiteral(filepath.Base(f)), }, ), }, @@ -1041,7 +1040,7 @@ func createLinkAnnotationDictWithLaunchAction(xRefTable *XRefTable, pageIndRef I map[string]Object{ "Type": Name("Action"), "S": Name("Launch"), - "F": StringLiteral(".\\/golang.pdf"), // e.g pdf, wav.. + "F": StringLiteral("golang.pdf"), "Win": Dict( map[string]Object{ "F": StringLiteral("golang.pdf"), diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/doc.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/doc.go index 1806de0..f5559a8 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/doc.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/doc.go @@ -6,6 +6,7 @@ It provides an API and a command line interface. Supported are all versions up t The commands are: attachments list, add, remove, extract embedded file attachments + booklet arrange pages onto larger sheets of paper to make a booklet or zine boxes list, add, remove page boundaries for selected pages changeopw change owner password changeupw change user password diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/matrix.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/matrix.go new file mode 100644 index 0000000..a0cd2a2 --- /dev/null +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/matrix.go @@ -0,0 +1,60 @@ +/* +Copyright 2018 The pdfcpu Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pdfcpu + +import "fmt" + +type matrix [3][3]float64 + +var identMatrix = matrix{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}} + +func (m matrix) multiply(n matrix) matrix { + var p matrix + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + for k := 0; k < 3; k++ { + p[i][j] += m[i][k] * n[k][j] + } + } + } + return p +} + +func (m matrix) String() string { + return fmt.Sprintf("%3.2f %3.2f %3.2f\n%3.2f %3.2f %3.2f\n%3.2f %3.2f %3.2f\n", + m[0][0], m[0][1], m[0][2], + m[1][0], m[1][1], m[1][2], + m[2][0], m[2][1], m[2][2]) +} + +func calcTransformMatrix(sx, sy, sin, cos, dx, dy float64) matrix { + // Scale + m1 := identMatrix + m1[0][0] = sx + m1[1][1] = sy + // Rotate + m2 := identMatrix + m2[0][0] = cos + m2[0][1] = sin + m2[1][0] = -sin + m2[1][1] = cos + // Translate + m3 := identMatrix + m3[2][0] = dx + m3[2][1] = dy + return m1.multiply(m2).multiply(m3) +} diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/nup.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/nup.go index 6133bdd..7499337 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/nup.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/nup.go @@ -31,10 +31,9 @@ import ( ) var ( - errInvalidGridID = errors.New("pdfcpu: nup: n: one of 2, 3, 4, 6, 8, 9, 12, 16") - errInvalidGridDims = errors.New("pdfcpu: grid: dimensions: m >= 0, n >= 0") - errInvalidNUpConfig = errors.New("pdfcpu: nup: invalid configuration string. Please consult pdfcpu help nup") - errInvalidGridConfig = errors.New("pdfcpu: nup: invalid configuration string. Please consult pdfcpu help grid") + errInvalidGridID = errors.New("pdfcpu nup: n must be one of 2, 3, 4, 6, 8, 9, 12, 16") + errInvalidGridDims = errors.New("pdfcpu grid: dimensions: m >= 0, n >= 0") + errInvalidNUpConfig = errors.New("pdfcpu: invalid configuration string") ) var ( @@ -54,12 +53,15 @@ var ( type nUpParamMap map[string]func(string, *NUp) error var nupParamMap = nUpParamMap{ - "dimensions": parseDimensionsNUp, - "formsize": parsePageFormatNUp, - "papersize": parsePageFormatNUp, - "orientation": parseOrientation, - "border": parseElementBorder, - "margin": parseElementMargin, + "dimensions": parseDimensionsNUp, + "formsize": parsePageFormatNUp, + "papersize": parsePageFormatNUp, + "orientation": parseOrientation, + "border": parseElementBorder, + "margin": parseElementMargin, + "backgroundcolor": parseSheetBackgroundColor, + "bgcolor": parseSheetBackgroundColor, + "guides": parseBookletGuides, } // Handle applies parameter completion and if successful @@ -87,16 +89,18 @@ func (m nUpParamMap) Handle(paramPrefix, paramValueStr string, nup *NUp) error { // NUp represents the command details for the command "NUp". type NUp struct { - PageDim *Dim // Page dimensions in display unit. - PageSize string // Paper size eg. A4L, A4P, A4(=default=A4P), see paperSize.go - UserDim bool // true if one of dimensions or paperSize provided overriding the default. - Orient orientation // One of rd(=default),dr,ld,dl - Grid *Dim // Intra page grid dimensions eg (2,2) - PageGrid bool // Create a mxn grid of pages for PDF inputfiles only (think "extra page n-Up"). - ImgInputFile bool // Process image or PDF input files. - Margin int // Cropbox for n-Up content. - Border bool // Draw bounding box. - InpUnit DisplayUnit // input display unit. + PageDim *Dim // Page dimensions in display unit. + PageSize string // Paper size eg. A4L, A4P, A4(=default=A4P), see paperSize.go + UserDim bool // true if one of dimensions or paperSize provided overriding the default. + Orient orientation // One of rd(=default),dr,ld,dl + Grid *Dim // Intra page grid dimensions eg (2,2) + PageGrid bool // Create a mxn grid of pages for PDF inputfiles only (think "extra page n-Up"). + ImgInputFile bool // Process image or PDF input files. + Margin int // Cropbox for n-Up content. + Border bool // Draw bounding box. + BookletGuides bool // Draw folding and cutting lines + InpUnit DisplayUnit // input display unit. + BgColor *SimpleColor // background color } // DefaultNUpConfig returns the default NUp configuration. @@ -114,6 +118,11 @@ func (nup NUp) String() string { nup.PageSize, *nup.PageDim, nup.Orient, *nup.Grid, nup.PageGrid, nup.ImgInputFile) } +// N returns the nUp value. +func (nup NUp) N() int { + return int(nup.Grid.Height * nup.Grid.Width) +} + type orientation int func (o orientation) String() string { @@ -192,6 +201,19 @@ func parseElementBorder(s string, nup *NUp) error { return nil } +func parseBookletGuides(s string, nup *NUp) error { + switch strings.ToLower(s) { + case "on", "true": + nup.BookletGuides = true + case "off", "false": + nup.BookletGuides = false + default: + return errors.New("pdfcpu: booklet guides, please provide one of: on/off true/false") + } + + return nil +} + func parseElementMargin(s string, nup *NUp) error { f, err := strconv.ParseFloat(s, 64) if err != nil { @@ -207,15 +229,19 @@ func parseElementMargin(s string, nup *NUp) error { return nil } +func parseSheetBackgroundColor(s string, nup *NUp) error { + c, err := parseColor(s) + if err != nil { + return err + } + nup.BgColor = &c + return nil +} + // ParseNUpDetails parses a NUp command string into an internal structure. func ParseNUpDetails(s string, nup *NUp) error { - err1 := errInvalidNUpConfig - if nup.PageGrid { - err1 = errInvalidGridConfig - } - if s == "" { - return err1 + return errInvalidNUpConfig } ss := strings.Split(s, ",") @@ -224,7 +250,7 @@ func ParseNUpDetails(s string, nup *NUp) error { ss1 := strings.Split(s, ":") if len(ss1) != 2 { - return err1 + return errInvalidNUpConfig } paramPrefix := strings.TrimSpace(ss1[0]) @@ -379,94 +405,159 @@ func rectsForGrid(nup *NUp) []*Rectangle { return rr } -// Calculate the matrix for transforming rectangle r1 with lower left corner in the origin into rectangle r2. -func calcTransMatrixForRect(r1, r2 *Rectangle, image bool) matrix { - var ( - w, h float64 - dx, dy float64 - rot float64 - ) - - if r2.Landscape() && r1.Portrait() || r2.Portrait() && r1.Landscape() { - rot = 90 - r1.UR.X, r1.UR.Y = r1.UR.Y, r1.UR.X +func bestFitRectIntoRect(rSrc, rDest *Rectangle) (w, h, dx, dy, rot float64) { + if rSrc.FitsWithin(rDest) { + // Translate rSrc into center of rDest without scaling. + w = rSrc.Width() + h = rSrc.Height() + dx = rDest.Width()/2 - rSrc.Width()/2 + dy = rDest.Height()/2 - rSrc.Height()/2 + return } - if r1.FitsWithin(r2) { - // Translate r1 into center of r2 w/o scaling up. - w = r1.Width() - h = r1.Height() - } else if r1.AspectRatio() <= r2.AspectRatio() { - // Scale down r1 height to fit into r2 height. - h = r2.Height() - w = r1.ScaledWidth(h) - } else { - // Scale down r1 width to fit into r2 width. - w = r2.Width() - h = r1.ScaledHeight(w) - } - - dx = r2.LL.X - r1.LL.X*w/r1.Width() + r2.Width()/2 - w/2 - dy = r2.LL.Y - r1.LL.Y*h/r1.Height() + r2.Height()/2 - h/2 - - if rot > 0 { - dx += w - if !image { - w /= r1.Width() - h /= r1.Height() + if rSrc.Landscape() { + if rDest.Landscape() { + if rSrc.AspectRatio() > rDest.AspectRatio() { + w = rDest.Width() + h = rSrc.ScaledHeight(w) + dy = (rDest.Height() - h) / 2 + } else { + h = rDest.Height() + w = rSrc.ScaledWidth(h) + dx = (rDest.Width() - w) / 2 + } + } else { + rot = 90 + if 1/rSrc.AspectRatio() < rDest.AspectRatio() { + w = rDest.Height() + h = rSrc.ScaledHeight(w) + dx = (rDest.Width() - h) / 2 + } else { + h = rDest.Width() + w = rSrc.ScaledWidth(h) + dy = (rDest.Height() - w) / 2 + } } - w, h = h, w - } else if !image { - w /= r1.Width() - h /= r1.Height() + return } - // Scale - m1 := identMatrix - m1[0][0] = w - m1[1][1] = h + if rSrc.Portrait() { + if rDest.Portrait() { + if rSrc.AspectRatio() < rDest.AspectRatio() { + h = rDest.Height() + w = rSrc.ScaledWidth(h) + dx = (rDest.Width() - w) / 2 + } else { + w = rDest.Width() + h = rSrc.ScaledHeight(w) + dy = (rDest.Height() - h) / 2 + } + } else { + rot = 90 + if 1/rSrc.AspectRatio() > rDest.AspectRatio() { + h = rDest.Width() + w = rSrc.ScaledWidth(h) + dy = (rDest.Height() - w) / 2 + } else { + w = rDest.Height() + h = rSrc.ScaledHeight(w) + dx = (rDest.Width() - h) / 2 + } + } + return + } - // Rotate - m2 := identMatrix - sin := math.Sin(float64(rot) * float64(degToRad)) - cos := math.Cos(float64(rot) * float64(degToRad)) - m2[0][0] = cos - m2[0][1] = sin - m2[1][0] = -sin - m2[1][1] = cos + w = rDest.Height() + if rDest.Portrait() { + w = rDest.Width() + } + h = w + dx = rDest.Width()/2 - rSrc.Width()/2 + dy = rDest.Height()/2 - rSrc.Height()/2 - // Translate - m3 := identMatrix - m3[2][0] = dx - m3[2][1] = dy - - return m1.multiply(m2).multiply(m3) + return } -func nUpTilePDFBytes(wr io.Writer, r1, r2 *Rectangle, formResID string, nup *NUp) { +func nUpTilePDFBytes(wr io.Writer, rSrc, rDest *Rectangle, formResID string, nup *NUp, rotate bool) { + + // rScr is a rectangular region represented by form formResID in form space. + + // rDest is an arbitrary rectangular region in dest space. + // It is the location where we want the form content to get rendered on a "best fit" basis. + // Accounting for the aspect ratios of rSrc and rDest "best fit" tries to fit the largest version of rScr into rDest. + // This may result in a 90 degree rotation. + + // rotate indicates if we need to apply a post rotation of 180 degrees eg for booklets. + // Draw bounding box. if nup.Border { fmt.Fprintf(wr, "[]0 d 0.1 w %.2f %.2f m %.2f %.2f l %.2f %.2f l %.2f %.2f l s ", - r2.LL.X, r2.LL.Y, r2.UR.X, r2.LL.Y, r2.UR.X, r2.UR.Y, r2.LL.X, r2.UR.Y, + rDest.LL.X, rDest.LL.Y, rDest.UR.X, rDest.LL.Y, rDest.UR.X, rDest.UR.Y, rDest.LL.X, rDest.UR.Y, ) } - // Apply margin. - croppedRect := r2.CroppedCopy(float64(nup.Margin)) + // Apply margin to rDest which potentially makes it smaller. + rDestCr := rDest.CroppedCopy(float64(nup.Margin)) - m := calcTransMatrixForRect(r1, croppedRect, nup.ImgInputFile) + // Calculate transform matrix. + // Best fit translation of a source rectangle into a destination rectangle. + w, h, dx, dy, r := bestFitRectIntoRect(rSrc, rDestCr) + + if nup.BgColor != nil { + if nup.ImgInputFile { + // Fill background. + FillRectStacked(wr, rDest, *nup.BgColor) + } else if nup.Margin > 0 { + // Fill margins. + m := float64(nup.Margin) + drawMargins(wr, *nup.BgColor, rDest, 0, m, m, m, m) + } + } + + // Apply additional rotation. + if rotate { + r += 180 + } + + sx := w + sy := h + if !nup.ImgInputFile { + sx /= rSrc.Width() + sy /= rSrc.Height() + } + + sin := math.Sin(r * float64(degToRad)) + cos := math.Cos(r * float64(degToRad)) + + switch r { + case 90: + dx += h + case 180: + dx += w + dy += h + case 270: + dy += w + } + + dx += rDestCr.LL.X + dy += rDestCr.LL.Y + + m := calcTransformMatrix(sx, sy, sin, cos, dx, dy) + + // Apply transform matrix and display form. fmt.Fprintf(wr, "q %.2f %.2f %.2f %.2f %.2f %.2f cm /%s Do Q ", m[0][0], m[0][1], m[1][0], m[1][1], m[2][0], m[2][1], formResID) } -func nUpImagePDFBytes(wr io.Writer, imgWidth, imgHeight int, nup *NUp, formResID string) { +func nUpImagePDFBytes(w io.Writer, imgWidth, imgHeight int, nup *NUp, formResID string) { for _, r := range rectsForGrid(nup) { - nUpTilePDFBytes(wr, RectForDim(float64(imgWidth), float64(imgHeight)), r, formResID, nup) + // Append to content stream. + nUpTilePDFBytes(w, RectForDim(float64(imgWidth), float64(imgHeight)), r, formResID, nup, false) } } -func createNUpForm(xRefTable *XRefTable, imgIndRef *IndirectRef, w, h, i int) (*IndirectRef, error) { +func createNUpFormForImage(xRefTable *XRefTable, imgIndRef *IndirectRef, w, h, i int) (*IndirectRef, error) { imgResID := fmt.Sprintf("Im%d", i) bb := RectForDim(float64(w), float64(h)) @@ -508,14 +599,14 @@ func createNUpForm(xRefTable *XRefTable, imgIndRef *IndirectRef, w, h, i int) (* return xRefTable.IndRefForNewObject(sd) } -func createNUpFormForPDFResource(xRefTable *XRefTable, resDict *IndirectRef, content []byte, cropBox *Rectangle) (*IndirectRef, error) { +func createNUpFormForPDF(xRefTable *XRefTable, resDict *IndirectRef, content []byte, cropBox *Rectangle) (*IndirectRef, error) { sd := StreamDict{ Dict: Dict( map[string]Object{ "Type": Name("XObject"), "Subtype": Name("Form"), "BBox": cropBox.Array(), - "Matrix": NewIntegerArray(1, 0, 0, 1, 0, 0), + "Matrix": NewNumberArray(1, 0, 0, 1, -cropBox.LL.X, -cropBox.LL.Y), "Resources": *resDict, }, ), @@ -548,7 +639,7 @@ func NewNUpPageForImage(xRefTable *XRefTable, fileName string, parentIndRef *Ind resID := 0 - formIndRef, err := createNUpForm(xRefTable, imgIndRef, w, h, resID) + formIndRef, err := createNUpFormForImage(xRefTable, imgIndRef, w, h, resID) if err != nil { return nil, err } @@ -578,7 +669,6 @@ func NewNUpPageForImage(xRefTable *XRefTable, fileName string, parentIndRef *Ind return nil, err } - // mediabox = physical page dimensions dim := nup.PageDim mediaBox := RectForDim(dim.Width, dim.Height) @@ -614,12 +704,27 @@ func NUpFromOneImage(ctx *Context, fileName string, nup *NUp, pagesDict Dict, pa func wrapUpPage(ctx *Context, nup *NUp, d Dict, buf bytes.Buffer, pagesDict Dict, pagesIndRef *IndirectRef) error { xRefTable := ctx.XRefTable + var fm FontMap + if nup.BookletGuides { + // For booklets only. + fm = drawBookletGuides(nup, &buf) + } + resourceDict := Dict( map[string]Object{ "XObject": d, }, ) + fontRes, err := fontResources(xRefTable, fm) + if err != nil { + return err + } + + if len(fontRes) > 0 { + resourceDict["Font"] = fontRes + } + resIndRef, err := xRefTable.IndRefForNewObject(resourceDict) if err != nil { return err @@ -635,7 +740,6 @@ func wrapUpPage(ctx *Context, nup *NUp, d Dict, buf bytes.Buffer, pagesDict Dict return err } - // mediabox = physical page dimensions dim := nup.PageDim mediaBox := RectForDim(dim.Width, dim.Height) @@ -675,19 +779,39 @@ func NUpFromMultipleImages(ctx *Context, fileNames []string, nup *NUp, pagesDict var buf bytes.Buffer rr := rectsForGrid(nup) - for i, fileName := range fileNames { + // fileCount must be a multiple of n. + // If not, we will insert blank pages at the end. + fileCount := len(fileNames) + if fileCount%nup.N() != 0 { + fileCount += nup.N() - fileCount%nup.N() + } + + for i := 0; i < fileCount; i++ { if i > 0 && i%len(rr) == 0 { - // Wrap complete nUp page. if err := wrapUpPage(ctx, nup, formsResDict, buf, pagesDict, pagesIndRef); err != nil { return err } - buf.Reset() formsResDict = NewDict() } + rDest := rr[i%len(rr)] + + var fileName string + if i < len(fileNames) { + fileName = fileNames[i] + } + + if fileName == "" { + // This is an empty page at the end. + if nup.BgColor != nil { + FillRectStacked(&buf, rDest, *nup.BgColor) + } + continue + } + f, err := os.Open(fileName) if err != nil { return err @@ -702,7 +826,7 @@ func NUpFromMultipleImages(ctx *Context, fileNames []string, nup *NUp, pagesDict return err } - formIndRef, err := createNUpForm(xRefTable, imgIndRef, w, h, i) + formIndRef, err := createNUpFormForImage(xRefTable, imgIndRef, w, h, i) if err != nil { return err } @@ -710,14 +834,15 @@ func NUpFromMultipleImages(ctx *Context, fileNames []string, nup *NUp, pagesDict formResID := fmt.Sprintf("Fm%d", i) formsResDict.Insert(formResID, *formIndRef) - nUpTilePDFBytes(&buf, RectForDim(float64(w), float64(h)), rr[i%len(rr)], formResID, nup) + // Append to content stream of page i. + nUpTilePDFBytes(&buf, RectForDim(float64(w), float64(h)), rr[i%len(rr)], formResID, nup, false) } // Wrap incomplete nUp page. return wrapUpPage(ctx, nup, formsResDict, buf, pagesDict, pagesIndRef) } -func sortedSelectedPages(pages IntSet) []int { +func sortSelectedPages(pages IntSet) []int { var pageNumbers []int for k, v := range pages { if v { @@ -728,27 +853,52 @@ func sortedSelectedPages(pages IntSet) []int { return pageNumbers } +func nupPageNumber(i int, sortedPageNumbers []int) int { + var pageNumber int + if i < len(sortedPageNumbers) { + pageNumber = sortedPageNumbers[i] + } + return pageNumber +} + func (ctx *Context) nupPages(selectedPages IntSet, nup *NUp, pagesDict Dict, pagesIndRef *IndirectRef) error { var buf bytes.Buffer xRefTable := ctx.XRefTable formsResDict := NewDict() rr := rectsForGrid(nup) - for i, p := range sortedSelectedPages(selectedPages) { + sortedPageNumbers := sortSelectedPages(selectedPages) + pageCount := len(sortedPageNumbers) + // pageCount must be a multiple of n. + // If not, we will insert blank pages at the end. + if pageCount%nup.N() != 0 { + pageCount += nup.N() - pageCount%nup.N() + } + + for i := 0; i < pageCount; i++ { if i > 0 && i%len(rr) == 0 { - // Wrap complete nUp page. if err := wrapUpPage(ctx, nup, formsResDict, buf, pagesDict, pagesIndRef); err != nil { return err } - buf.Reset() formsResDict = NewDict() } + rDest := rr[i%len(rr)] + + pageNumber := nupPageNumber(i, sortedPageNumbers) + if pageNumber == 0 { + // This is an empty page at the end of a booklet. + if nup.BgColor != nil { + FillRectStacked(&buf, rDest, *nup.BgColor) + } + continue + } + consolidateRes := true - d, inhPAttrs, err := ctx.PageDict(p, consolidateRes) + d, inhPAttrs, err := ctx.PageDict(pageNumber, consolidateRes) if err != nil { return err } @@ -775,7 +925,7 @@ func (ctx *Context) nupPages(selectedPages IntSet, nup *NUp, pagesDict Dict, pag if inhPAttrs.cropBox != nil { cropBox = inhPAttrs.cropBox } - formIndRef, err := createNUpFormForPDFResource(xRefTable, ir, bb, cropBox) + formIndRef, err := createNUpFormForPDF(xRefTable, ir, bb, cropBox) if err != nil { return err } @@ -783,8 +933,8 @@ func (ctx *Context) nupPages(selectedPages IntSet, nup *NUp, pagesDict Dict, pag formResID := fmt.Sprintf("Fm%d", i) formsResDict.Insert(formResID, *formIndRef) - // inhPAttrs.mediaBox - nUpTilePDFBytes(&buf, cropBox, rr[i%len(rr)], formResID, nup) + // Append to content stream of page i. + nUpTilePDFBytes(&buf, cropBox, rDest, formResID, nup, false) } // Wrap incomplete nUp page. diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/parse.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/parse.go index 7688344..6ae876e 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/parse.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/parse.go @@ -495,7 +495,7 @@ func parseName(line *string) (*Name, error) { l = forwardParseBuf(l, 1) // cut off on whitespace or delimiter - eok, _ := positionToNextWhitespaceOrChar(l, "/<>()[]") + eok, _ := positionToNextWhitespaceOrChar(l, "/<>()[]%") if eok < 0 { // Name terminated by eol. *line = "" @@ -516,7 +516,7 @@ func parseName(line *string) (*Name, error) { func processDictKeys(line *string, relaxed bool) (Dict, error) { l := *line - eol := false + var eol bool d := NewDict() for !strings.HasPrefix(l, ">>") { key, err := parseName(&l) @@ -621,7 +621,7 @@ func noBuf(l *string) bool { } func startParseNumericOrIndRef(l string) (string, string, int) { - i1, _ := positionToNextWhitespaceOrChar(l, "/<([]>") + i1, _ := positionToNextWhitespaceOrChar(l, "/<([]>%") var l1 string if i1 > 0 { l1 = l[i1:] diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/read.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/read.go index ec80159..bea407b 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/read.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/read.go @@ -828,7 +828,7 @@ func scanTrailerDictRemainder(s *bufio.Scanner, line string, buf bytes.Buffer) ( var i, j, k int buf.WriteString(line) - buf.WriteString(" ") + buf.WriteString("\x0a") log.Read.Printf("scanTrailer dictBuf after start tag: <%s>\n", line) line = line[2:] @@ -841,7 +841,7 @@ func scanTrailerDictRemainder(s *bufio.Scanner, line string, buf bytes.Buffer) ( return "", err } buf.WriteString(line) - buf.WriteString(" ") + buf.WriteString("\x0a") log.Read.Printf("scanTrailer dictBuf next line: <%s>\n", line) } @@ -869,7 +869,7 @@ func scanTrailerDictRemainder(s *bufio.Scanner, line string, buf bytes.Buffer) ( return "", err } buf.WriteString(line) - buf.WriteString(" ") + buf.WriteString("\x0a") log.Read.Printf("scanTrailer dictBuf next line: <%s>\n", line) } else { // Yes << @@ -1091,13 +1091,14 @@ func bypassXrefSection(ctx *Context) error { for { line, err := scanLineRaw(s) + //println(line) if err != nil { break } if withinXref { offset += int64(len(line) + eolCount) if withinTrailer { - bb = append(bb, ' ') + bb = append(bb, '\n') bb = append(bb, line...) i := strings.Index(line, "startxref") if i >= 0 { @@ -1662,6 +1663,14 @@ func object(ctx *Context, offset int64, objNr, genNr int) (o Object, endInd, str log.Read.Printf("object %d: non matching objNr(%d) or generationNumber(%d) tags found.\n", objNr, *objectNr, *generationNr) } + l = strings.TrimSpace(l) + if len(l) == 0 { + // 7.3.9 + // Specifying the null object as the value of a dictionary entry (7.3.7, "Dictionary Objects") + // shall be equivalent to omitting the entry entirely. + return nil, endInd, streamInd, streamOffset, err + } + o, err = parseObject(&l) return o, endInd, streamInd, streamOffset, err diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/stamp.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/stamp.go index 2b4b490..9d559b5 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/stamp.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/stamp.go @@ -121,29 +121,6 @@ var wmParamMap = watermarkParamMap{ "strokecolor": parseStrokeColor, } -type matrix [3][3]float64 - -var identMatrix = matrix{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}} - -func (m matrix) multiply(n matrix) matrix { - var p matrix - for i := 0; i < 3; i++ { - for j := 0; j < 3; j++ { - for k := 0; k < 3; k++ { - p[i][j] += m[i][k] * n[k][j] - } - } - } - return p -} - -func (m matrix) String() string { - return fmt.Sprintf("%3.2f %3.2f %3.2f\n%3.2f %3.2f %3.2f\n%3.2f %3.2f %3.2f\n", - m[0][0], m[0][1], m[0][2], - m[1][0], m[1][1], m[1][2], - m[2][0], m[2][1], m[2][2]) -} - // SimpleColor is a simple rgb wrapper. type SimpleColor struct { R, G, B float32 // intensities between 0 and 1. @@ -205,7 +182,7 @@ type Watermark struct { Rotation float64 // rotation to apply in degrees. -180 <= x <= 180 Diagonal int // paint along the diagonal. UserRotOrDiagonal bool // true if one of rotation or diagonal provided overriding the default. - Opacity float64 // opacity the watermark. 0 <= x <= 1 + Opacity float64 // opacity of the watermark. 0 <= x <= 1 RenderMode RenderMode // fill=0, stroke=1 fill&stroke=2 Scale float64 // relative scale factor: 0 <= x <= 1, absolute scale factor: 0 <= x ScaleEff float64 // effective scale factor @@ -846,7 +823,7 @@ func (wm *Watermark) calcBoundingBox(pageNr int) { return } -func (wm *Watermark) calcTransformMatrix() *matrix { +func (wm *Watermark) calcTransformMatrix() matrix { var sin, cos float64 r := wm.Rotation @@ -868,25 +845,16 @@ func (wm *Watermark) calcTransformMatrix() *matrix { sin = math.Sin(float64(r) * float64(degToRad)) cos = math.Cos(float64(r) * float64(degToRad)) - // 1) Rotate - m1 := identMatrix - m1[0][0] = cos - m1[0][1] = sin - m1[1][0] = -sin - m1[1][1] = cos - - // 2) Translate - m2 := identMatrix var dy float64 if !wm.isImage() && !wm.isPDF() { dy = wm.bb.LL.Y } ll := lowerLeftCorner(wm.vp.Width(), wm.vp.Height(), wm.bb.Width(), wm.bb.Height(), wm.Pos) - m2[2][0] = ll.X + wm.bb.Width()/2 + float64(wm.Dx) + sin*(wm.bb.Height()/2+dy) - cos*wm.bb.Width()/2 - m2[2][1] = ll.Y + wm.bb.Height()/2 + float64(wm.Dy) - cos*(wm.bb.Height()/2+dy) - sin*wm.bb.Width()/2 - m := m1.multiply(m2) - return &m + dx := ll.X + wm.bb.Width()/2 + float64(wm.Dx) + sin*(wm.bb.Height()/2+dy) - cos*wm.bb.Width()/2 + dy = ll.Y + wm.bb.Height()/2 + float64(wm.Dy) - cos*(wm.bb.Height()/2+dy) - sin*wm.bb.Width()/2 + + return calcTransformMatrix(1, 1, sin, cos, dx, dy) } func onTopString(onTop bool) string { diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/validate/xReftable.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/validate/xReftable.go index 0ba1e27..8561b19 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/validate/xReftable.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/validate/xReftable.go @@ -124,7 +124,7 @@ func validateNames(xRefTable *pdf.XRefTable, rootDict pdf.Dict, required bool, s if err != nil { return err } - if d == nil { + if d == nil || len(d) == 0 { continue } diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/version.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/version.go index 7456138..dd46f1f 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/version.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/version.go @@ -23,7 +23,7 @@ import ( ) // VersionStr is the current pdfcpu version. -var VersionStr = "v0.3.8 dev" +var VersionStr = "v0.3.9 dev" // Version is a type for the internal representation of PDF versions. type Version int diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/write.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/write.go index cf5faa0..9080b85 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/write.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/write.go @@ -769,10 +769,6 @@ func writeXRefStream(ctx *Context) error { w := ctx.Write - if err = w.WriteEol(); err != nil { - return err - } - if _, err = w.WriteString("startxref"); err != nil { return err } diff --git a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/writeObjects.go b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/writeObjects.go index 41a41a3..c26982d 100644 --- a/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/writeObjects.go +++ b/vendor/github.com/pdfcpu/pdfcpu/pkg/pdfcpu/writeObjects.go @@ -404,7 +404,7 @@ func writeStream(w *WriteContext, sd StreamDict) (int64, error) { return 0, errors.Errorf("writeStream: failed to write raw content: %d bytes written - streamlength:%d", c, *sd.StreamLength) } - e, err := w.WriteString("endstream") + e, err := w.WriteString(fmt.Sprintf("%sendstream", w.Eol)) if err != nil { return 0, errors.Wrapf(err, "writeStream: failed to write raw content") } diff --git a/vendor/modules.txt b/vendor/modules.txt index 9d858c0..68138b2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -120,6 +120,8 @@ github.com/lightningnetwork/lnd/clock github.com/lightningnetwork/lnd/queue # github.com/lightningnetwork/lnd/ticker v1.0.0 github.com/lightningnetwork/lnd/ticker +# github.com/logrusorgru/aurora v2.0.3+incompatible +github.com/logrusorgru/aurora # github.com/ltcsuite/ltcd v0.0.0-20190101042124-f37f8bf35796 github.com/ltcsuite/ltcd/chaincfg github.com/ltcsuite/ltcd/chaincfg/chainhash @@ -128,7 +130,7 @@ github.com/ltcsuite/ltcd/wire github.com/mattn/go-sqlite3 # github.com/miekg/dns v1.1.29 github.com/miekg/dns -# github.com/muun/libwallet v0.7.0 +# github.com/muun/libwallet v0.8.0 github.com/muun/libwallet github.com/muun/libwallet/addresses github.com/muun/libwallet/aescbc @@ -141,7 +143,7 @@ github.com/muun/libwallet/recoverycode github.com/muun/libwallet/sphinx github.com/muun/libwallet/swaps github.com/muun/libwallet/walletdb -# github.com/pdfcpu/pdfcpu v0.3.8 +# github.com/pdfcpu/pdfcpu v0.3.9 github.com/pdfcpu/pdfcpu/internal/config github.com/pdfcpu/pdfcpu/internal/corefont/metrics github.com/pdfcpu/pdfcpu/pkg/api