Fix mojibake when double byte text is written under a single byte font encoding - #532
Open
EugineD wants to merge 1 commit into
Open
Fix mojibake when double byte text is written under a single byte font encoding#532EugineD wants to merge 1 commit into
EugineD wants to merge 1 commit into
Conversation
TryDecode is only reached when the font table has mixed encodings and a high byte arrives while a single byte font encoding is active, so that runtime encoding is by definition suspect. Falling back to it therefore cannot recover the text. When the document declared a multi byte code page with \ansicpg, prefer it in that specific case: it can represent the double byte characters the single byte font encoding cannot. All other cases keep using the runtime encoding, so behaviour is unchanged when both encodings are single byte, when the runtime encoding is multi byte, or when no \ansicpg was declared. Fixes Sicos1977#529 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 14683058-3f09-4436-9483-e447aeca1d46
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When an encapsulated-HTML RTF body declares a double byte document code page (
\ansicpg932,\ansicpg949, ...) but part of the text is written while a\fcharset0font is active, that text is silently turned into mojibake. There is no?and noU+FFFD, because a single byte code page maps nearly every byte, so the corruption is easy to miss.This is #529. Using the
test1.msgattached there, the body textあああ(U+3042x3, Shift-JIS bytes82A0 82A0 82A0) comes out ofBodyHtmlasU+201A U+00A0x3 — which is exactly those same bytes decoded as cp1252.The reporter noticed it happens after a NO-BREAK SPACE, and that turns out to be mechanically correct. Word emits the
as{\f3\'a0}, and\f3is the only\fcharset0font involved; the Japanese text on the next line carries no font of its own and inherits that single byte encoding.Cause
TryDecodeis only ever called from one place:So by construction, whenever it runs, a high byte has arrived while a single byte font encoding is active. The runtime encoding is already suspect — that is the whole reason
TryDecodeexists — so falling back to it cannot recover the text.Charset detection does not help here either: the byte buffer is flushed per high byte run, so
CharsetDetectortypically sees only about two bytes and detects nothing. Execution drops to the fallback. (LoweringCharsetDetectionEncodingConfidenceLeveltherefore has no effect — I tested 0.90 down to 0.01 and the output was byte-identical.)Fix
Prefer the declared document code page in the one case where it is provably better — the runtime encoding is single byte and cannot represent double byte characters, while the document code page is multi byte and can:
This is intentionally narrow, so existing behaviour is preserved everywhere else:
\ansicpg1252with a\fcharset204font) — unchanged;\ansicpgdeclared — unchanged, via the new_documentCodePageDeclaredflag. This guard matters because_defaultEncodingis seeded withEncoding.Default, which is UTF-8 (multi byte) on .NET Core, and would otherwise start winning for documents that never declared a code page.Successful detection paths are untouched — a confident detection found in the font table still wins.
Tests
Three tests added to
RtfDocumentTests, all synthetic inline RTF, no binary fixtures:MixedCharsetFontsWithNoBreakSpaceBeforeDoubleByteText\ansicpg932with the -induced{\f1\'a0}font switchMixedCharsetFontsWithDoubleByteDocumentCodePage\ansicpg949(Korean)MixedCharsetFontsWithSingleByteDocumentCodePageKeepsFontEncoding\fcharset204font under\ansicpg1252must keep the font encodingBoth new failing tests genuinely fail on unpatched
master(Expected string length 3 but was 6and5 but was 10— each double byte character becoming two single byte ones), and the guard test passes both before and after, confirming the change is narrow.Full suite passes on net462, net8.0, net9.0 and net10.0: 59 passed, 0 failed, 2 skipped (up from 56).
MsgReader.csprojbuilds clean in Release across all six target frameworks.Side observation (not fixed here)
{\f3\'a0}is a scoped group, so the font should revert at the}. It does not:GroupStartandGroupEndare no-ops in the main loop, and_runtimeEncodingis never saved or restored with group nesting, so the font's encoding leaks past the group. That is a separate and older issue, which was harmless while the fallback was the document code page. I have left it alone to keep this PR focused, but it may be worth a look.