Skip to content

RTF dictionary export fails with encoding errorΒ #1705

Closed

Description

@mkrnr

Describe the bug

The export of a dictionary to the RTF format can fail with an encoding error.

First reported by @jthlim on discord.

To Reproduce

Steps to reproduce the behavior:

  1. Right-click on a larger dictionary like main.json
  2. Click Save dictionaries as...
  3. Click Create a copy of each dictionary
  4. In the last drop-down, select RTF dictionaries
  5. An encoding error occurs: Exception: creating dictionary /Users/martin/Downloads/main - Copy.rtf failed. 'charmap' codec can't encode character '\u0301' in position 24: character maps to <undefined>

Expected behavior

The dictionary is exported.

Operating system

  • OS: macOS 15.3.1
  • Plover Version 4.0.0

Activity

  1. changed the title [-]RTF dictionary fails with encoding error[/-] [+]RTF dictionary export fails with encoding error[/+] on Mar 11, 2025
  2. r-tae commented on Mar 11, 2025

    @r-tae

    Minimal reproduction:

    1705-reproduction.json

    The issue is with U+0301 : COMBINING ACUTE ACCENT {stress mark; Greek oxia, tonos}, as the error says, although there are other chars that cause it; the first report had the problem as \u1edf (which is from "TPA*": "phở",)

  3. r-tae commented on Mar 11, 2025

    @r-tae

    Getting rid of just those two entries makes main.json work again

  4. sammdot commented on Mar 11, 2025

    @sammdot
    Member

    We know that Plover encodes RTF files using code page 1252, which is the de facto standard code page that professional CAT programs use. It doesn't exactly have a facility for encoding characters outside the code page, unlike UTF-8 which uses multiple bytes.

    According to the RTF 1.6 spec, Unicode characters can be encoded using UTF-16 and the \uN control word. Examples:

    • The character that spawned this issue report, ở U+1EDF, can be "escaped" in RTF as \u7903 (7903 = 0x1EDF).
    • The spec says Unicode values greater than 32767 must be expressed as negative numbers, so 속 U+C18D can be escaped not as \u49549 ❌, but as \u-15987 (49549 – 65536).
    • Unicode codepoints above U+10000 can be expressed in two bytes using surrogate pairs: 😊 U+1F60A would be treated as two bytes, U+D83D U+DE0A, which can be escaped as \u-10179\u-8694.

    I don't think the Python encoder supports any of this, so it'll have to be done on Plover's end.

    The RTF spec also says \uN should be followed by a fallback character β€” in our case I think ? should suffice for non-Unicode-supporting CAT programs.

    For what it's worth, if we want to break from the RTF spec slightly for convenience, we could simply write \uN with one positive number and not have a fallback, e.g. \u128522 instead of \u-10179 ?\u-8694 ?.

  5. jthlim commented on Mar 16, 2025

    @jthlim

    fwiw.. macOS TextEdit.app encodes rtf δ½ ε₯½πŸ˜€πŸ˜ƒ as:

    \uc0\u20320 \u22909 \uc0\u55357 \u56832 \u55357 \u56835 
    

    So they're using surrogate pairs, and they're not using negative numbers like the spec suggests.

  6. sammdot commented on Mar 16, 2025

    @sammdot
    Member

    I tested this myself, and found that it accepted both positive and negative versions, as long as you used surrogate pairs:

    {\rtf1\ansi
    \u20320\u22909\u55357\u56832\u55357\u56835\~
    \u20320\u22909\u-10179\u-8704\u-10179\u-8701
    \par
    \u7903 {\*\cxcomment U+1EDF}
    \par
    \u49549\~
    \u-15987 {\*\cxcomment U+C18D}
    \par
    \u55357\u56842\~
    \u-10179\u-8694\~
    \u128522 {\*\cxcomment U+1F60A}
    \par
    A\~\u65601
    }

    δ½ ε₯½πŸ˜€πŸ˜ƒΒ δ½ ε₯½πŸ˜€πŸ˜ƒ
    ở
    속 속
    😊 😊 
    AΒ A

    The last two lines are curious though β€” \u128522 was rendered as U+F60A, so it looks like it just clips the numbers to below 65536, and you can see that with \u65601 (0x10041) being equivalent to A (U+0041). Not really relevant to this issue, just an interesting find.

    I'm fine with the approach of using surrogate pairs but positive numbers since Mac supports it, and it's unlikely pro software would even bother supporting Unicode to this extent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions