@@ -65,7 +65,8 @@ interface
6565 Ignore, // denotes following control can be ignored
6666 FirstLineOffset, // first line indent in twips (relative to \li)
6767 LeftIndent, // left indent in twips
68- TabStop // sets a tab stop in twips
68+ TabStop, // sets a tab stop in twips
69+ UnicodeCharSize // number of bytes of a given \uN Unicode character
6970 );
7071 { $ScopedEnums off}
7172
@@ -81,7 +82,7 @@ TRTF = record
8182 ' fcharset' , ' fnil' , ' froman' , ' fswiss' , ' fmodern' , ' fscript' , ' fdecor' ,
8283 ' ftech' , ' colortbl' , ' red' , ' green' , ' blue' , ' info' , ' title' , ' pard' ,
8384 ' par' , ' plain' , ' f' , ' cf' , ' b' , ' i' , ' ul' , ' fs' , ' sb' , ' sa' , ' u' , ' upr' ,
84- ' ud' , ' *' , ' fi' , ' li' , ' tx'
85+ ' ud' , ' *' , ' fi' , ' li' , ' tx' , ' uc '
8586 );
8687 strict private
8788
@@ -134,13 +135,13 @@ TRTF = record
134135 // / </returns>
135136 // / <remarks>Converted characters are escaped if necessary. Any characters
136137 // / that are not valid in the required code page are encoded in a Unicode
137- // / RTF control word with <c>?</c> as the non-Unicode fallback.</remarks>
138+ // / RTF control word with a non-Unicode fallback.</remarks>
138139 class function MakeSafeText (const AText: string; const ACodePage: Integer):
139140 ASCIIString; static;
140141
141142 // / <summary>Creates an RTF destination in a Unicode safe way.</summary>
142143 // / <param name="ADestCtrl"><c>TRTFControl</c> [in] Required destination
143- // / control.</param>
144+ // / control.</param>
144145 // / <param name="ADestText"><c>string</c> [in] Unicode text to be included
145146 // / in the destination.</param>
146147 // / <param name="ACodePage"><c>Integer</c> [in] ANSI Code page to use for
@@ -152,8 +153,7 @@ TRTF = record
152153 // / containing the encoded text, escaped as necessary. Should any
153154 // / characters in <c>ADestText</c> be incompatible with the code page then
154155 // / two sub-destinations are created, one containing Unicode characters and
155- // / the other containing ANSI text, with unknown characters flagged with
156- // / "error" characters such as <c>?</c>.</remarks>
156+ // / the other containing ANSI text.</remarks>
157157 class function UnicodeSafeDestination (const ADestCtrl: TRTFControl;
158158 const ADestText: string; const ACodePage: Integer): ASCIIString; static;
159159 end ;
@@ -250,6 +250,20 @@ class function TRTF.HexEscape(const Ch: AnsiChar): ASCIIString;
250250
251251class function TRTF.MakeSafeText (const AText: string; const ACodePage: Integer):
252252 ASCIIString;
253+
254+ function MakeSafeChar (const AChar: AnsiChar): ASCIIString;
255+ begin
256+ if (AChar < #$20 ) or ((AChar >= #$7F ) and (AChar <= #$FF)) then
257+ // Not an ASCII character
258+ Result := HexEscape(AChar)
259+ else if (AChar = ' {' ) or (AChar = ' \' ) or (AChar = ' }' ) then
260+ // Reserved RTF character: must be escaped
261+ Result := Escape(AChar)
262+ else
263+ // Valid character, use as is
264+ Result := ASCIIString(AChar);
265+ end ;
266+
253267var
254268 Ch: Char; // each Unicode character in TheText
255269 AnsiChars: TArray<AnsiChar>; // translation of a Ch into the ANSI code page
@@ -264,23 +278,23 @@ class function TRTF.MakeSafeText(const AText: string; const ACodePage: Integer):
264278 begin
265279 // Conversion succeeded: check process each ANSI char
266280 for AnsiCh in AnsiChars do
267- begin
268- if (AnsiCh < #$20 ) or ((AnsiCh >= #$7F ) and (AnsiCh <= #$FF)) then
269- // Not an ASCII character
270- Result := Result + HexEscape(AnsiCh)
271- else if (Ch = ' {' ) or (Ch = ' \' ) or (Ch = ' }' ) then
272- // Reserved RTF character: must be escaped
273- Result := Result + Escape(AnsiCh)
274- else
275- // Valid character, use as is
276- Result := Result + ASCIIString(AnsiCh);
277- end ;
281+ Result := Result + MakeSafeChar(AnsiCh)
278282 end
279283 else
280- // Conversion failed: we store Unicode char in a Unicode control word
284+ begin
285+ // Conversion failed: create a Unicode character followed by fallback
286+ // ANSI character
281287 Result := Result
288+ + ControlWord(TRTFControl.UnicodeCharSize, 1 )
282289 + ControlWord(TRTFControl.UnicodeChar, SmallInt(Ord(Ch)))
283- + ' ?' ; // fallback "unprintable" value
290+ + ' ' ;
291+ if Length(AnsiChars) = 1 then
292+ // Single alternate character: output it
293+ Result := Result + MakeSafeChar(AnsiChars[0 ])
294+ else
295+ // Can't get alternate: use '?'
296+ Result := Result + ' ?' ;
297+ end ;
284298 end ;
285299end ;
286300
0 commit comments