Skip to content

fix(regex): keep U+2028/U+2029 in Byte segments when Kanji mode is on - #413

Open
maximilliangrand wants to merge 1 commit into
soldair:masterfrom
maximilliangrand:fix/byte-regex-line-separators
Open

fix(regex): keep U+2028/U+2029 in Byte segments when Kanji mode is on#413
maximilliangrand wants to merge 1 commit into
soldair:masterfrom
maximilliangrand:fix/byte-regex-line-separators

Conversation

@maximilliangrand

Copy link
Copy Markdown

Bug

With Kanji mode enabled (a toSJISFunc supplied), a U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) in the input is silently dropped, so the encoded QR decodes to different data than was passed in. No error is thrown.

const QRCode = require('qrcode')
const toSJIS = require('qrcode/helper/to-sjis')

const input = '皿a' + '
' + '晒三'  // Byte '皿a' + U+2028, then Kanji '晒三'
const { segments } = QRCode.create(input, { toSJISFunc: toSJIS })

// master: Byte segment bytes = [231,154,191,97]           -> U+2028 dropped, QR encodes '皿a晒三'
// fixed:  Byte segment bytes = [231,154,191,97,226,128,168] -> U+2028 preserved, full input encoded

Cause

The Byte-mode matcher in lib/core/regex.js is built as (?:.|[\r\n]). In JavaScript, . (without the s flag) matches every code unit except the four line terminators \n, \r, U+2028, U+2029. Only \r and \n were added back, so U+2028/U+2029 match no segment regex at all (numeric/alphanumeric/kanji don't cover them either) and are dropped during segmentation.

This exports.BYTE regex is only used for Byte segments when Kanji mode is on (see lib/core/segments.js). The default path uses the BYTE_KANJI negated class ([^A-Z0-9 $%*+\-./:]+), which does match those code points, so default mode is unaffected.

Fix

Add the two missing separators to the class:

-const byte = '(?:(?![A-Z0-9 $%*+\\-./:]|' + kanji + ')(?:.|[\r\n]))+'
+const byte = '(?:(?![A-Z0-9 $%*+\\-./:]|' + kanji + ')(?:.|[\r\n\\u2028\\u2029]))+'

Evidence

Added two test/unit/core/segments.test.js cases mirroring the existing \n case. They fail on master (not ok 25/not ok 26) and pass with the fix (26/26). Inspecting QRCode.create().segments directly confirms the Byte segment retains the U+2028/U+2029 bytes with the fix and drops them without it.

The Byte-mode regex matches characters with (?:.|[\r\n]). In JS a dot
does not match any of the four line terminators (\n, \r, U+2028, U+2029),
and only \n and \r were added back. With Kanji mode enabled this regex is
used for Byte segments, so a U+2028 or U+2029 in the input matched no
segment regex at all and was silently dropped, corrupting the encoded
data. Add the two missing separators to the class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant