Skip to content

fix: reset MagicEncode after font switch (ESC M) and hw init (ESC @)#729

Open
larsblumberg wants to merge 3 commits intopython-escpos:masterfrom
larsblumberg:fix/reset-magic-encoding-after-font-switch
Open

fix: reset MagicEncode after font switch (ESC M) and hw init (ESC @)#729
larsblumberg wants to merge 3 commits intopython-escpos:masterfrom
larsblumberg:fix/reset-magic-encoding-after-font-switch

Conversation

@larsblumberg
Copy link

Bug description

Some printers (confirmed: Netum NT-5890K) silently reset their active code page back to the factory default after a font switch (ESC M n). MagicEncode was unaware of this hardware-side reset: its cached self.encoding remained stale, causing subsequent text() calls to skip CODEPAGE_CHANGE. Non-ASCII characters were then sent in the previously-active encoding (e.g. CP1257) but interpreted by the printer under its default code page, producing garbled output.

ESC @ (hw("INIT")) is defined in the ESC/POS spec as a full printer reset that restores all settings to factory defaults, including the active code page — so the same fix applies there by spec.

Proposed fix

Fix: add MagicEncode.reset_encoding() which clears both self.encoding and self.encoder.used_encodings, and call it from set() after every font change and from hw() after INIT. This forces a fresh code page selection and a CODEPAGE_CHANGE re-emission before the next text output.

Why used_encodings must also be cleared:
self.encoding = None is enough to ensure a CODEPAGE_CHANGE is emitted. However, used_encodings biases find_suitable_encoding() toward previously- used code pages. After a reset, that preference is stale: on NT-5890K the previously-used CP1257 does not function correctly after ESC M, so MagicEncode would re-select it, emit CODEPAGE_CHANGE → CP1257, and send e.g. ü as 0xFC — wrong in the printer's default code page. Clearing used_encodings removes the stale bias and lets slot-number ordering take over, landing on CP850 where ü = 0x81, a byte that is correct in virtually every Western code page regardless of whether the printer honours the code page switch.

Demo of the the bug and the applied fix:

photo_2026-03-21_13-43-25

Future work

Follow-up: used_encodings could be removed from MagicEncode entirely. self.encoding is the mechanism that actually avoids redundant switches: it keeps the current code page as long as it can encode the next character, without consulting used_encodings at all. used_encodings is only consulted when a switch is already unavoidable — at which point it cannot prevent any switch, it can only gamble on which encoding might be needed again later. That saves at most one future switch in the rare case where the same non-default code page is needed again after having been forced away. Removing used_encodings would also make reset_encoding() unnecessary: with only self.encoding to clear, callers would just write self.magic.encoding = None directly, with no need for a helper method.

larsblumberg and others added 2 commits March 21, 2026 13:37
Some printers (confirmed: NT-5890K) silently reset their active code page
back to the factory default after a font switch (`ESC M n`). `MagicEncode`
was unaware of this hardware-side reset: its cached `self.encoding` remained
stale, causing subsequent `text()` calls to skip `CODEPAGE_CHANGE`. Non-ASCII
characters were then sent in the previously-active encoding (e.g. CP1257)
but interpreted by the printer under its default code page, producing
garbled output.

`ESC @` (`hw("INIT")`) is defined in the ESC/POS spec as a full printer reset
that restores all settings to factory defaults, including the active code
page — so the same fix applies there by spec.

Fix: add `MagicEncode.reset_encoding()` which clears both `self.encoding`
and `self.encoder.used_encodings`, and call it from `set()` after every
font change and from `hw()` after `INIT`. This forces a fresh code page
selection and a `CODEPAGE_CHANGE` re-emission before the next text output.

Why `used_encodings` must also be cleared:
`self.encoding = None` is enough to ensure a `CODEPAGE_CHANGE` is emitted.
However, `used_encodings` biases `find_suitable_encoding()` toward previously-
used code pages. After a reset, that preference is stale: on NT-5890K the
previously-used CP1257 does not function correctly after `ESC M`, so
`MagicEncode` would re-select it, emit `CODEPAGE_CHANGE` → CP1257, and send
e.g. `ü` as `0xFC` — wrong in the printer's default code page. Clearing
`used_encodings` removes the stale bias and lets slot-number ordering take
over, landing on CP850 where `ü` = `0x81`, a byte that is correct in
virtually every Western code page regardless of whether the printer
honours the code page switch.

Follow-up: `used_encodings` could be removed from `MagicEncode` entirely.
`self.encoding` is the mechanism that actually avoids redundant switches: it
keeps the current code page as long as it can encode the next character,
without consulting `used_encodings` at all. `used_encodings` is only consulted
when a switch is already unavoidable — at which point it cannot prevent
any switch, it can only gamble on which encoding might be needed again
later. That saves at most one future switch in the rare case where the
same non-default code page is needed again after having been forced away.
Removing `used_encodings` would also make `reset_encoding()` unnecessary:
with only `self.encoding` to clear, callers would just write
`self.magic.encoding = None` directly, with no need for a helper method.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers the four key behaviours introduced by the fix:

- reset_encoding() sets self.encoding to None
- reset_encoding() clears encoder.used_encodings
- the next write after reset always re-emits CODEPAGE_CHANGE (even
  for the same encoding that was active before the reset)
- after clearing used_encodings, find_suitable_encoding picks the
  lowest-slot encoding rather than the previously-used high-slot one
  (the exact scenario that caused the NT-5890K umlaut bug)
larsblumberg added a commit to larsblumberg/python-escpos that referenced this pull request Mar 21, 2026
larsblumberg added a commit to larsblumberg/python-escpos that referenced this pull request Mar 21, 2026
@larsblumberg larsblumberg force-pushed the fix/reset-magic-encoding-after-font-switch branch from c9a87b6 to c9370ec Compare March 21, 2026 13:30
@larsblumberg larsblumberg force-pushed the fix/reset-magic-encoding-after-font-switch branch from c9370ec to 052a8f4 Compare March 21, 2026 13:30
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