Bug 1919799 - Add QR code generation for QR export. r=mkmelin,dandarnell

Differential Revision: https://phabricator.services.mozilla.com/D224327

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Martin Giger 2024-10-08 17:11:36 +00:00
Родитель f608ce48a6
Коммит 1d0e68af39
13 изменённых файлов: 1868 добавлений и 10 удалений

Просмотреть файл

@ -1335,3 +1335,37 @@ 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.
</pre>
<h1><a id="tb-qrcodejs"></a>qrcodejs License</h1>
<p>This license applies to the following files:</p>
<ul>
<li><code>third_party/qrcode</code></li>
</ul>
<pre>
BSD 2-Clause License
Copyright (c) 2020, Dan Jackson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</pre>

Просмотреть файл

@ -32,4 +32,5 @@
</li>
<li><a href="about:license#tb-sdp-transform">sdp-transform License</a></li>
<li><a href="about:license#tb-jwt-decode">jwt-decode License</a></li>
<li><a href="about:license#tb-qrcodejs">qrcodejs License</a></li>
</ul>

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -22,7 +22,7 @@ const { FeedUtils } = ChromeUtils.importESModule(
* @returns {nsIMsgAccount} Created account with associated default identity and
* servers.
*/
function createMailAccount(name, emailLocalPart, protocol) {
async function createMailAccount(name, emailLocalPart, protocol) {
const server = MailServices.accounts.createIncomingServer(
name,
"foo.invalid",
@ -31,15 +31,35 @@ function createMailAccount(name, emailLocalPart, protocol) {
server.password = "password";
const identity = MailServices.accounts.createIdentity();
identity.email = `${emailLocalPart}@foo.invalid`;
identity.fullName = name;
const account = MailServices.accounts.createAccount();
account.incomingServer = server;
account.addIdentity(identity);
const outgoing = MailServices.outgoingServer.createServer("smtp");
outgoing.QueryInterface(Ci.nsISmtpServer);
outgoing.username = name;
outgoing.hostname = "foo.invalid";
outgoing.port = 587;
identity.smtpServerKey = outgoing.key;
const login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(
Ci.nsILoginInfo
);
login.init(
"smtp://foo.invalid",
null,
"smtp://foo.invalid",
name,
"smtppass",
"",
""
);
await Services.logins.addLoginAsync(login);
// Setting password last since setting other things might clear it.
outgoing.password = "smtppass";
return account;
}
add_task(function test_getEligibleAccounts() {
add_task(async function test_getEligibleAccounts() {
const emptyEligibleAccounts = QRExport.getEligibleAccounts();
Assert.deepEqual(
@ -49,11 +69,15 @@ add_task(function test_getEligibleAccounts() {
);
// Eligible accounts
const imapAccount = createMailAccount("imap@foo.invalid", "imap", "imap");
const popAccount = createMailAccount("pop", "tinderbox", "pop3");
const imapAccount = await createMailAccount(
"imap@foo.invalid",
"imap",
"imap"
);
const popAccount = await createMailAccount("pop", "tinderbox", "pop3");
// Ineligible accounts
const unsupportedIncomingAuthAccount = createMailAccount(
const unsupportedIncomingAuthAccount = await createMailAccount(
"incomingauth",
"incomingauth",
"imap"
@ -61,7 +85,7 @@ add_task(function test_getEligibleAccounts() {
unsupportedIncomingAuthAccount.incomingServer.authMethod =
Ci.nsMsgAuthMethod.GSSAPI;
const unsupportedOutgoingAuthAccount = createMailAccount(
const unsupportedOutgoingAuthAccount = await createMailAccount(
"outgoingauth",
"outgoingauth",
"imap"
@ -71,7 +95,7 @@ add_task(function test_getEligibleAccounts() {
);
unspoortedAuthOutgoingServer.authMethod = Ci.nsMsgAuthMethod.GSSAPI;
const noOutgoingServerAccount = createMailAccount(
const noOutgoingServerAccount = await createMailAccount(
"nooutgoing",
"nooutgoing",
"imap"
@ -83,7 +107,7 @@ add_task(function test_getEligibleAccounts() {
unsupportedOutgoingAuthAccount,
noOutgoingServerAccount,
// Non-ASCII email
createMailAccount("nonascii", "unüblich", "imap"),
await createMailAccount("nonascii", "unüblich", "imap"),
// Different account types
MailServices.accounts.createLocalMailAccount(),
FeedUtils.createRssAccount("qrExport"),
@ -107,3 +131,157 @@ add_task(function test_getEligibleAccounts() {
MailServices.accounts.removeAccount(account, false);
}
});
add_task(async function test_getAccountData() {
const account = await createMailAccount("encode", "encode", "imap");
const dataWithoutPasswords = QRExport.getAccountData(account.key, false);
Assert.deepEqual(
dataWithoutPasswords,
[
[
0,
"foo.invalid",
143,
0,
1,
"encode",
"Mail for encode@foo.invalid",
"",
],
[
[
[0, "foo.invalid", 587, 0, 1, "encode", ""],
["encode@foo.invalid", "encode"],
],
],
],
"Should contain expected account data without passwords"
);
const dataWithPasswords = QRExport.getAccountData(account.key, true);
Assert.deepEqual(
dataWithPasswords,
[
[
0,
"foo.invalid",
143,
0,
1,
"encode",
"Mail for encode@foo.invalid",
"password",
],
[
[
[0, "foo.invalid", 587, 0, 1, "encode", "smtppass"],
["encode@foo.invalid", "encode"],
],
],
],
"Should contain expected account data with passwords"
);
MailServices.accounts.removeAccount(account, false);
});
add_task(function test_getQRData() {
const chunk = QRExport.getQRData(["foo", "bar"], 3, 9);
Assert.deepEqual(
chunk,
[1, [3, 9], "foo", "bar"],
"Should match data format v1"
);
});
add_task(function test_renderQR() {
const loremQR = QRExport.renderQR("lorem ipsum");
// Using Assert.ok for these assertions, so we don't log 7KB+ of data URI for
// every assertion.
Assert.ok(
/^data:image\/svg\+xml,[a-zA-Z0-9%-.]+$/.test(loremQR),
"Result should be a data URI for an SVG"
);
Assert.ok(
QRExport.renderQR("foo bar") != loremQR,
"Result should vary by input"
);
Assert.ok(
QRExport.renderQR("lorem ipsum") == loremQR,
"Should return consistent result"
);
});
add_task(async function test_getQRCode_single() {
const account = await createMailAccount("getcode", "getcode", "imap");
const qrCodes = QRExport.getQRCodes([account.key], true);
Assert.ok(Array.isArray(qrCodes), "Should get an array");
Assert.equal(
qrCodes.length,
1,
"Should only get a single chunk for one account"
);
Assert.ok(
/^data:image\/svg\+xml,[a-zA-Z0-9%-.]+$/.test(qrCodes[0]),
"QR code should be a data URI for an SVG"
);
MailServices.accounts.removeAccount(account, false);
});
add_task(async function test_getQRCode_multipleChunks() {
const accounts = [
await createMailAccount("accountone", "firstaccount", "imap"),
await createMailAccount("accounttwo", "secondaccount", "pop3"),
await createMailAccount(
"accountthree@foo.invalid",
"thirdaccountbutthisonewithalongmail",
"imap"
),
await createMailAccount("accountfour", "fourthaccount", "imap"),
];
const qrCodes = QRExport.getQRCodes(
accounts.map(account => account.key),
true
);
Assert.ok(Array.isArray(qrCodes), "Should get an array");
Assert.equal(qrCodes.length, 2, "Should get two QR codes for four accounts");
Assert.ok(
/^data:image\/svg\+xml,[a-zA-Z0-9%-.]+$/.test(qrCodes[0]),
"QR code 1 should be a data URI for an SVG"
);
Assert.ok(
/^data:image\/svg\+xml,[a-zA-Z0-9%-.]+$/.test(qrCodes[1]),
"QR code 2 should be a data URI for an SVG"
);
// Snapshot test to ensure the data format encoded in the QR code is
// consistent, since we can't check the full encoded JSON blob without
// decoding the QR code. Instead we assume that someone tested the current
// implementation and accepted it as correct.
// In case the snapshot needs to be updated, uncomment this line for a test
// run.
// await IOUtils.writeJSON(do_get_file("resources/qrdata.txt").path, qrCodes);
const qrCodeSnapshot = await IOUtils.readUTF8(
do_get_file("resources/qrdata.txt").path
);
Assert.ok(
JSON.stringify(qrCodes) === qrCodeSnapshot,
"Snapshot should match generated QR codes"
);
for (const account of accounts) {
MailServices.accounts.removeAccount(account, false);
}
});

Просмотреть файл

@ -4,6 +4,17 @@
import { MailServices } from "resource:///modules/MailServices.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
default: "resource:///modules/qrcode.mjs",
});
ChromeUtils.defineLazyGetter(lazy, "console", () =>
console.createInstance({
prefix: "QRExport",
maxLogLevel: "Warn",
})
);
/**
* Incoming server types supported by the Android app.
*/
@ -18,6 +29,37 @@ const UNSUPPORTED_AUTH_METHODS = new Set([
Ci.nsMsgAuthMethod.External,
]);
// QR Code data content constants
// These should match with https://github.com/thunderbird/thunderbird-android/blob/4ebcca8d893bc57df9ef293fdf7d6d8fe46becad/feature/migration/qrcode/src/main/kotlin/app/k9mail/feature/migration/qrcode/AccountData.kt#L48-L105
const QR_DATA_FORMAT_VERSION = 1;
const INCOMING_PROTOCOL = new Map([
["imap", 0],
["pop3", 1],
]);
const SOCKET_TYPES = new Map([
[Ci.nsMsgSocketType.plain, 0],
[Ci.nsMsgSocketType.alwaysSTARTTLS, 2],
[Ci.nsMsgSocketType.SSL, 3],
]);
const AUTH_METHODS = new Map([
[Ci.nsMsgAuthMethod.none, 0],
// [Ci.nsMsgAuthMethod.old, "old"],
[Ci.nsMsgAuthMethod.passwordCleartext, 1],
[Ci.nsMsgAuthMethod.passwordEncrypted, 2],
[Ci.nsMsgAuthMethod.GSSAPI, 3],
[Ci.nsMsgAuthMethod.NTLM, 4],
[Ci.nsMsgAuthMethod.External, 5],
// [Ci.nsMsgAuthMethod.secure, "secure"],
// [Ci.nsMsgAuthMethod.anything, "anything"],
[Ci.nsMsgAuthMethod.OAuth2, 6],
]);
const OUTGOING_PROTOCOL_SMTP = 0;
const ACCOUNTS_PER_QR_CODE = 3;
const MAX_CHUNK_LENGTH = 800;
export const QRExport = {
/**
* Eligible accounts fulfill:
@ -46,8 +88,8 @@ export const QRExport = {
if (!/^[\x00-\x7F]+$/.test(identity.email)) {
return false;
}
const outgoingServer = MailServices.outgoingServer.servers.find(
s => s.key == identity.smtpServerKey
const outgoingServer = MailServices.outgoingServer.getServerByKey(
identity.smtpServerKey
);
return (
outgoingServer instanceof Ci.nsISmtpServer &&
@ -55,4 +97,120 @@ export const QRExport = {
);
});
},
/**
* Generate the QR codes to export the selected accounts. Splits the accounts
* into chunks of 3 accounts per QR code.
*
* @param {string[]} accountKeys - Accounts that should be exported.
* @param {boolean} includePasswords - If passwords should be included in the export data.
* @returns {string[]} Returns an array of SVG URLs, each representing a QR code.
*/
getQRCodes(accountKeys, includePasswords) {
const accounts = accountKeys.map(key =>
this.getAccountData(key, includePasswords)
);
// For practical purposes each QR code should hold no more than 1000
// characters, optimally 800 characters maximum.
const chunkCount = Math.ceil(accounts.length / ACCOUNTS_PER_QR_CODE);
const qrCodes = [];
for (let i = 0; i < chunkCount; i++) {
const chunkOffset = i * ACCOUNTS_PER_QR_CODE;
const chunk = accounts
.slice(chunkOffset, chunkOffset + ACCOUNTS_PER_QR_CODE)
.flat();
const chunkPart = i + 1; // 1-indexed
const chunkData = this.getQRData(chunk, chunkPart, chunkCount);
const serializedChunk = JSON.stringify(chunkData);
if (serializedChunk.length > MAX_CHUNK_LENGTH) {
lazy.console.warn(
"Data for QR code",
chunkPart,
"is longer than expected, result might be hard to read"
);
}
qrCodes.push(this.renderQR(serializedChunk));
}
return qrCodes;
},
/**
* Generate the data for a QR code with a chunk of account data.
*
* @param {Array} data - Account data contained in this chunk.
* @param {number} part - 1-based index of this chunk.
* @param {number} count - Total number of QR codes.
* @returns {Array}
*/
getQRData(data, part, count) {
return [QR_DATA_FORMAT_VERSION, [part, count], ...data];
},
/**
* Generate a minimal account description for serialization to JSON.
*
* @param {string} accountKey - Key of the account to get the data for.
* @param {boolean} includePasswords - If the result should include passwords.
* @returns {Array} Array structure with account data to serialize to JSON.
* @see https://docs.google.com/document/d/1siSwPzNPkwq4BL5G3z2K4zzRJuL9N9zbPodMOggXbdA/edit for format
*/
getAccountData(accountKey, includePasswords) {
const account = MailServices.accounts.getAccount(accountKey);
const incomingServer = account.incomingServer;
const defaultSmtpServerKey = account.defaultIdentity.smtpServerKey;
const outgoingServer =
MailServices.outgoingServer.getServerByKey(defaultSmtpServerKey);
outgoingServer.QueryInterface(Ci.nsISmtpServer);
const identites = account.identities.filter(
identity =>
!identity.smtpServerKey ||
identity.smtpServerKey == defaultSmtpServerKey
);
return [
[
INCOMING_PROTOCOL.get(incomingServer.type),
incomingServer.hostName,
incomingServer.port,
SOCKET_TYPES.get(incomingServer.socketType),
AUTH_METHODS.get(incomingServer.authMethod),
incomingServer.username,
incomingServer.prettyName,
(includePasswords &&
!incomingServer.passwordPromptRequired &&
incomingServer.password) ||
"",
],
[
[
[
OUTGOING_PROTOCOL_SMTP,
outgoingServer.hostname,
outgoingServer.port,
SOCKET_TYPES.get(outgoingServer.socketType),
AUTH_METHODS.get(outgoingServer.authMethod),
outgoingServer.username,
(includePasswords &&
(outgoingServer.password ||
outgoingServer.wrappedJSObject._getPasswordWithoutUI())) ||
"",
],
...identites.map(identity => [identity.email, identity.fullName]),
],
],
];
},
/**
* Renders the given data into a QR code with L error correction.
*
* @param {string} data - Data to encode in the QR code.
* @returns {string} QR code rendered as SVG URI.
*/
renderQR(data) {
const qrOptions = {
errorCorrectionLevel: lazy.default.ErrorCorrectionLevel.L,
};
const matrix = lazy.default.generate(data, qrOptions);
return lazy.default.render("svg-uri", matrix);
},
};

4
third_party/moz.build поставляемый
Просмотреть файл

@ -7,6 +7,10 @@ DIRS += [
"asn1js",
]
EXTRA_JS_MODULES += [
"qrcode/qrcode.mjs",
]
if CONFIG["TB_LIBOTR_PREBUILT"]:
DEFINES["TB_LIBOTR_PREBUILT"] = CONFIG["TB_LIBOTR_PREBUILT"]

25
third_party/qrcode/LICENSE поставляемый Normal file
Просмотреть файл

@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2020, Dan Jackson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

120
third_party/qrcode/README.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,120 @@
# QR Code JS
Javascript QR Code generator. Derived from my C version: [qrcode](https://github.com/danielgjackson/qrcode).
## Demo Site
Generate your own SVG QR Code:
* [danielgjackson.github.io/qrcodejs](https://danielgjackson.github.io/qrcodejs)
## QR Codes in you terminal
If you have [Deno](https://deno.land/) installed, you can generate a QR Code in your terminal:
```bash
deno run https://danielgjackson.github.io/qrcodejs/qrcli.mjs 'Hello, World!'
```
...or write out a QR Code to a file:
```bash
deno run --allow-write https://danielgjackson.github.io/qrcodejs/qrcli.mjs --output:svg --file hello.svg 'Hello, World!'
```
## Getting started
### Example usage
Install (if using `npm`):
```bash
npm i -S https://github.com/danielgjackson/qrcodejs
```
<!--
Quick test (also works from a non-module):
```javascript
(async() => {
const { default: QrCode } = await import('qrcodejs');
console.log(QrCode.render('medium', QrCode.generate('Hello, World!')));
})();
```
-->
Example usage from an ECMAScript module (`.mjs` file):
```javascript
import QrCode from 'qrcodejs';
const data = 'Hello, World!';
const matrix = QrCode.generate(data);
const text = QrCode.render('medium', matrix);
console.log(text);
```
### Example web page usage
Example usage in a web page:
```html
<img>
<script type="module">
import QrCode from 'https://danielgjackson.github.io/qrcodejs/qrcode.mjs';
const data = 'Hello, World!';
const matrix = QrCode.generate(data);
const uri = QrCode.render('svg-uri', matrix);
document.querySelector('img').src = uri;
</script>
```
### Browser without a server
If you would like to use this directly as part of a browser-based app over the `file:` protocol (which disallows modules), you can easily convert this to a non-module `.js` file:
* Download [`qrcode.mjs`](https://raw.githubusercontent.com/danielgjackson/qrcodejs/master/qrcode.mjs) renamed as `qrcode.js`.
* Remove the last line from the file (`export default QrCode`).
* Ensure there is no `type="module"` attribute in your `<script src="qrcode.js"></script>` tag.
## API
### `QrCode.generate(data, options)`
* `data` - the text to encode in the QR Code.
* `options` - the configuration object for the QR Code (optional). Options include `errorCorrectionLevel` (0-3), `optimizeEcc` (boolean flag, default `true`, to maximize the error-correction level within the chosen output size), `minVersion`/`maxVersion` (1-40), `maskPattern` (0-7). Hints for the rendering stage are `invert` (boolean flag to invert the code, not as widely supported), and `quiet` (the size, in modules, of the quiet area around the code).
Returns a *matrix* that can be passed to the `render()` function.
### `QrCode.render(mode, matrix, options)`
* `mode` - the rendering mode, one of:
* `large` - Generate block-character text, each module takes 2x1 character cells.
* `medium` - Generate block-character text, fitting 1x2 modules in each character cell.
* `compact` - Generate block-character text, fitting 2x2 modules in each character cell.
* `svg` - Generate the contents for a scalable vector graphics file (`.svg`).
* `bmp` - Generate the contents for a bitmap file (`.bmp`).
* `svg-uri` - Generate a `data:` URI for an SVG file
* `bmp-uri` - Generate a `data:` URI for a BMP file.
The `-uri` modes can be, for example, directly used as the `src` for an `<img>` tag, or `url()` image in CSS.
* `matrix` - the matrix to draw, as returned by the `generate()` function.
* `options` - the configuration object (optional), depends on the chosen rendering `mode`:
* `svg` / `svg-uri`: `moduleSize` the unit dimensions of each module, `white` (boolean) output the non-set modules (otherwise will be transparent background), `moduleRound` proportion of how rounded the modules are, `finderRound` to hide the standard finder modules and instead output a shape with the specified roundness, `alignmentRound` to hide the standard alignment modules and instead output a shape with the specified roundness.
* `bmp` / `bmp-uri`: `scale` for the size of a module, `alpha` (boolean) to use a transparent background, `width`/`height` can set a specific image size (rather than scaling the matrix dimensions).
Returns the text or binary output from the chosen `mode`.

41
third_party/qrcode/moz.yaml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,41 @@
# All fields are mandatory unless otherwise noted
schema: 1
# Version of this schema
bugzilla:
# Bugzilla product and component for this directory and subdirectories.
product: Thunderbird
component: Account Manager
origin:
name: qrcodejs
description: JavaScript QR Code Generator
url: https://danielgjackson.github.io/qrcodejs
release: 86770ec12f0f9abee8728fc9018ab7bd0949f4bc
# Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS"
revision: 86770ec12f0f9abee8728fc9018ab7bd0949f4bc
# Revision to pull in
# Must be a long or short commit SHA (long preferred)
license: BSD-2-Clause
vendoring:
# Information needed to update the library automatically.
url: https://github.com/danielgjackson/qrcodejs
source-hosting: github
skip-vendoring-steps:
- hg-add
- spurious-check
- update-moz-build
exclude:
- '.*'
- '.vscode/settings.json'
- qrcli.mjs
- index.html

61
third_party/qrcode/outline.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,61 @@
# Outline-only QR Code
For a vector QR code that only uses stroke lines (no fill), replace the `<defs>` section of your *.svg* file with:
```svg
<style>
line, polyline, path, rect {
stroke: currentColor;
fill: none;
stroke-linecap: round;
stroke-width: 0.1;
}
</style>
<defs>
<!-- Filled data modules -->
<g id="b">
<!-- Multiply cross -->
<line x1="-0.25" y1="-0.25" x2="0.25" y2="0.25" />
<line x1="0.25" y1="-0.25" x2="-0.25" y2="0.25" />
<!-- Addition cross -->
<line x1="-0.354" y1="0" x2="0.354" y2="0" />
<line x1="0" y1="-0.354" x2="0" y2="0.354" />
</g>
<!-- Filled finder and alignment modules -->
<g id="bb">
<!-- Zig-zag fill (not used) -->
<!--
<polyline points="-0.5,-0.5 -0.25,-0.5 -0.5,-0.25 -0.5,0 0,-0.5 0.25,-0.5 -0.5,0.25 -0.5,0.5 0.5,-0.5 0.5,-0.25 -0.25,0.5 0,0.5 0.5,0 0.5,0.25 0.25,0.5 0.5,0.5" stroke="currentColor" stroke-width="0.25" stroke-linecap="round" fill="none" />
-->
<!-- Forward slash hatch -->
<line x1="-0.25" y1="-0.5" x2="-0.5" y2="-0.25" />
<line x1="0" y1="-0.5" x2="-0.5" y2="0" />
<line x1="0.25" y1="-0.5" x2="-0.5" y2="0.25" />
<line x1="0.5" y1="-0.5" x2="-0.5" y2="0.5" />
<line x1="0.5" y1="-0.25" x2="-0.25" y2="0.5" />
<line x1="0.5" y1="0" x2="0" y2="0.5" />
<line x1="0.5" y1="0.25" x2="0.25" y2="0.5" />
<!-- Backward slash hatch -->
<line x1="0.25" y1="-0.5" x2="0.5" y2="-0.25" />
<line x1="0" y1="-0.5" x2="0.5" y2="0" />
<line x1="-0.25" y1="-0.5" x2="0.5" y2="0.25" />
<line x1="-0.5" y1="-0.5" x2="0.5" y2="0.5" />
<line x1="-0.5" y1="-0.25" x2="0.25" y2="0.5" />
<line x1="-0.5" y1="0" x2="0" y2="0.5" />
<line x1="-0.5" y1="0.25" x2="-0.25" y2="0.5" />
</g>
<!-- Use the modules for finder/alignment -->
<use id="f" xlink:href="#bb" />
<use id="a" xlink:href="#bb" />
<!-- Do not use a particular shape for finder/alignment patterns -->
<path id="fc" d="" visibility="hidden" />
<path id="ac" d="" visibility="hidden" />
</defs>
```

26
third_party/qrcode/package.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,26 @@
{
"name": "qrcodejs",
"version": "0.0.0",
"description": "Javascript QR Code generator.",
"main": "qrcode.mjs",
"type": "module",
"scripts": {
"start": "node qrcli.mjs",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/danielgjackson/qrcodejs.git"
},
"keywords": [
"QR",
"Code",
"javascript"
],
"author": "Dan Jackson",
"license": "BSD-2-Clause",
"bugs": {
"url": "https://github.com/danielgjackson/qrcodejs/issues"
},
"homepage": "https://github.com/danielgjackson/qrcodejs#readme"
}

1208
third_party/qrcode/qrcode.mjs поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -14,6 +14,7 @@ comm/third_party/libgpg-error
comm/third_party/libotr
comm/third_party/niwcompat
comm/third_party/python
comm/third_party/qrcode
comm/third_party/rnp
comm/third_party/rust
comm/third_party/zlib