arsd/characterencodings.d

363 lines
14 KiB
D
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
This is meant to help get data from the wild into utf8 strings
so you can work with them easily inside D.
The main function is convertToUtf8(), which takes a byte array
of your raw data (a byte array because it isn't really a D string
yet until it is utf8), and a runtime string telling it's current
encoding.
The current encoding argument is meant to come from the data's
metadata, and is flexible on exact format - it is case insensitive
and takes several variations on the names.
This way, you should be able to send it the encoding string directly
from an XML document, a HTTP header, or whatever you have, and it
ought to just work.
Example:
auto data = cast(immutable(ubyte)[])
std.file.read("my-windows-file.txt");
string utf8String = convertToUtf8(data, "windows-1252");
// utf8String can now be used
The encodings currently implemented for decoding are:
UTF-8 (a no-op; it simply casts the array to string)
UTF-16,
UTF-32,
Windows-1252,
ISO 8859 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, and 16.
It treats ISO 8859-1, Latin-1, and Windows-1252 the same way, since
those labels are pretty much de-facto the same thing in wild documents.
This module currently makes no attempt to look at control characters.
*/
module arsd.characterencodings;
import std.string;
import std.array;
import std.conv;
/// Takes data from a given character encoding and returns it as UTF-8
string convertToUtf8(immutable(ubyte)[] data, string dataCharacterEncoding) {
// just to normalize the passed string...
auto encoding = dataCharacterEncoding.toLower();
encoding = encoding.replace(" ", "");
encoding = encoding.replace("-", "");
encoding = encoding.replace("_", "");
// should be good enough.
switch(encoding) {
default:
throw new Exception("I don't know how to convert " ~ dataCharacterEncoding ~ " to UTF-8");
break;
// since the input is immutable, these are ok too.
// just want to cover all the bases with one runtime function.
case "utf16":
return to!string(cast(wstring) data);
case "utf32":
return to!string(cast(dstring) data);
case "utf8":
return cast(string) data;
// and now the various 8 bit encodings we support.
case "windows1252":
return decodeImpl(data, ISO_8869_1, Windows_1252);
case "latin1":
case "iso88591":
// Why am I putting Windows_1252 here? A lot of
// stuff in the wild is mislabeled, so this will
// do some good in the Just Works department.
// Regardless, I don't handle the
// control char set in that zone anyway right now.
return decodeImpl(data, ISO_8869_1, Windows_1252);
case "iso88592":
return decodeImpl(data, ISO_8869_2);
case "iso88593":
return decodeImpl(data, ISO_8869_3);
case "iso88594":
return decodeImpl(data, ISO_8869_4);
case "iso88595":
return decodeImpl(data, ISO_8869_5);
case "iso88596":
return decodeImpl(data, ISO_8869_6);
case "iso88597":
return decodeImpl(data, ISO_8869_7);
case "iso88598":
return decodeImpl(data, ISO_8869_8);
case "iso88599":
return decodeImpl(data, ISO_8869_9);
case "iso885910":
return decodeImpl(data, ISO_8869_10);
case "iso885911":
return decodeImpl(data, ISO_8869_11);
case "iso885913":
return decodeImpl(data, ISO_8869_13);
case "iso885914":
return decodeImpl(data, ISO_8869_14);
case "iso885915":
return decodeImpl(data, ISO_8869_15);
case "iso885916":
return decodeImpl(data, ISO_8869_16);
}
assert(0);
}
// this function actually does the work, using the translation tables
// below.
string decodeImpl(in ubyte[] data, in dchar[] chars160to255, in dchar[] chars128to159 = null)
out(ret) {
import std.utf;
validate(ret);
}
body {
string utf8;
/// I'm sure this could be a lot more efficient, but whatever, it
/// works.
foreach(octet; data) {
if(octet < 128)
utf8 ~= cast(char) octet;
else if(octet < 160) {
if(chars128to159 !is null)
utf8 ~= chars128to159[octet - 128];
else
utf8 ~= " ";
} else {
utf8 ~= chars160to255[octet - 160];
}
}
return utf8;
}
// Here come the translation tables.
// this table gives characters for decimal 128 through 159.
// the < 128 characters are the same as ascii, and > 159 the same as
// iso 8859 1, seen below.
immutable dchar[] Windows_1252 = [
'€', ' ', '', 'ƒ', '„', '…', '†', '‡',
'ˆ', '‰', 'Š', '', 'Œ', ' ', 'Ž', ' ',
' ', '', '', '“', '”', '•', '', '—',
'˜', '™', 'š', '', 'œ', ' ', 'ž', 'Ÿ'];
// the following tables give the characters from decimal 160 up to 255
// in the given encodings.
immutable dchar[] ISO_8869_1 = [
' ', '¡', '¢', '£', '¤', '¥', '¦', '§',
'¨', '©', 'ª', '«', '¬', '­', '®', '¯',
'°', '±', '²', '³', '´', 'µ', '¶', '·',
'¸', '¹', 'º', '»', '¼', '½', '¾', '¿',
'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç',
'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï',
'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', '×',
'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß',
'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç',
'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï',
'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', '÷',
'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'þ', 'ÿ'];
immutable dchar[] ISO_8869_2 = [
' ', 'Ą', '˘', 'Ł', '¤', 'Ľ', 'Ś', '§',
'¨', 'Š', 'Ş', 'Ť', 'Ź', '­', 'Ž', 'Ż',
'°', 'ą', '˛', 'ł', '´', 'ľ', 'ś', 'ˇ',
'¸', 'š', 'ş', 'ť', 'ź', '˝', 'ž', 'ż',
'Ŕ', 'Á', 'Â', 'Ă', 'Ä', 'Ĺ', 'Ć', 'Ç',
'Č', 'É', 'Ę', 'Ë', 'Ě', 'Í', 'Î', 'Ď',
'Đ', 'Ń', 'Ň', 'Ó', 'Ô', 'Ő', 'Ö', '×',
'Ř', 'Ů', 'Ú', 'Ű', 'Ü', 'Ý', 'Ţ', 'ß',
'ŕ', 'á', 'â', 'ă', 'ä', 'ĺ', 'ć', 'ç',
'č', 'é', 'ę', 'ë', 'ě', 'í', 'î', 'ď',
'đ', 'ń', 'ň', 'ó', 'ô', 'ő', 'ö', '÷',
'ř', 'ů', 'ú', 'ű', 'ü', 'ý', 'ţ', '˙'];
immutable dchar[] ISO_8869_3 = [
' ', 'Ħ', '˘', '£', '¤', ' ', 'Ĥ', '§',
'¨', 'İ', 'Ş', 'Ğ', 'Ĵ', '­', ' ', 'Ż',
'°', 'ħ', '²', '³', '´', 'µ', 'ĥ', '·',
'¸', 'ı', 'ş', 'ğ', 'ĵ', '½', ' ', 'ż',
'À', 'Á', 'Â', ' ', 'Ä', 'Ċ', 'Ĉ', 'Ç',
'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï',
' ', 'Ñ', 'Ò', 'Ó', 'Ô', 'Ġ', 'Ö', '×',
'Ĝ', 'Ù', 'Ú', 'Û', 'Ü', 'Ŭ', 'Ŝ', 'ß',
'à', 'á', 'â', ' ', 'ä', 'ċ', 'ĉ', 'ç',
'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï',
' ', 'ñ', 'ò', 'ó', 'ô', 'ġ', 'ö', '÷',
'ĝ', 'ù', 'ú', 'û', 'ü', 'ŭ', 'ŝ', '˙'];
immutable dchar[] ISO_8869_4 = [
' ', 'Ą', 'ĸ', 'Ŗ', '¤', 'Ĩ', 'Ļ', '§',
'¨', 'Š', 'Ē', 'Ģ', 'Ŧ', '­', 'Ž', '¯',
'°', 'ą', '˛', 'ŗ', '´', 'ĩ', 'ļ', 'ˇ',
'¸', 'š', 'ē', 'ģ', 'ŧ', 'Ŋ', 'ž', 'ŋ',
'Ā', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Į',
'Č', 'É', 'Ę', 'Ë', 'Ė', 'Í', 'Î', 'Ī',
'Đ', 'Ņ', 'Ō', 'Ķ', 'Ô', 'Õ', 'Ö', '×',
'Ø', 'Ų', 'Ú', 'Û', 'Ü', 'Ũ', 'Ū', 'ß',
'ā', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'į',
'č', 'é', 'ę', 'ë', 'ė', 'í', 'î', 'ī',
'đ', 'ņ', 'ō', 'ķ', 'ô', 'õ', 'ö', '÷',
'ø', 'ų', 'ú', 'û', 'ü', 'ũ', 'ū', '˙'];
immutable dchar[] ISO_8869_5 = [
' ', 'Ё', 'Ђ', 'Ѓ', 'Є', 'Ѕ', 'І', 'Ї',
'Ј', 'Љ', 'Њ', 'Ћ', 'Ќ', '­', 'Ў', 'Џ',
'А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ж', 'З',
'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П',
'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч',
'Ш', 'Щ', 'Ъ', 'Ы', 'Ь', 'Э', 'Ю', 'Я',
'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з',
'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п',
'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч',
'ш', 'щ', 'ъ', 'ы', 'ь', 'э', 'ю', 'я',
'№', 'ё', 'ђ', 'ѓ', 'є', 'ѕ', 'і', 'ї',
'ј', 'љ', 'њ', 'ћ', 'ќ', '§', 'ў', 'џ'];
immutable dchar[] ISO_8869_6 = [
' ', ' ', ' ', ' ', '¤', ' ', ' ', ' ',
' ', ' ', ' ', ' ', '،', '­', ' ', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
' ', ' ', ' ', '؛', ' ', ' ', ' ', '؟',
' ', 'ء', 'آ', 'أ', 'ؤ', 'إ', 'ئ', 'ا',
'ب', 'ة', 'ت', 'ث', 'ج', 'ح', 'خ', 'د',
'ذ', 'ر', 'ز', 'س', 'ش', 'ص', 'ض', 'ط',
'ظ', 'ع', 'غ', ' ', ' ', ' ', ' ', ' ',
'ـ', 'ف', 'ق', 'ك', 'ل', 'م', 'ن', 'ه',
'و', 'ى', 'ي', 'ً', 'ٌ', 'ٍ', 'َ', 'ُ',
'ِ', 'ّ', 'ْ', ' ', ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '];
immutable dchar[] ISO_8869_7 = [
' ', '', '', '£', '€', '₯', '¦', '§',
'¨', '©', 'ͺ', '«', '¬', '­', ' ', '―',
'°', '±', '²', '³', '΄', '΅', 'Ά', '·',
'Έ', 'Ή', 'Ί', '»', 'Ό', '½', 'Ύ', 'Ώ',
'ΐ', 'Α', 'Β', 'Γ', 'Δ', 'Ε', 'Ζ', 'Η',
'Θ', 'Ι', 'Κ', 'Λ', 'Μ', 'Ν', 'Ξ', 'Ο',
'Π', 'Ρ', ' ', 'Σ', 'Τ', 'Υ', 'Φ', 'Χ',
'Ψ', 'Ω', 'Ϊ', 'Ϋ', 'ά', 'έ', 'ή', 'ί',
'ΰ', 'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η',
'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο',
'π', 'ρ', 'ς', 'σ', 'τ', 'υ', 'φ', 'χ',
'ψ', 'ω', 'ϊ', 'ϋ', 'ό', 'ύ', 'ώ', ' '];
immutable dchar[] ISO_8869_8 = [
' ', ' ', '¢', '£', '¤', '¥', '¦', '§',
'¨', '©', '×', '«', '¬', '­', '®', '¯',
'°', '±', '²', '³', '´', 'µ', '¶', '·',
'¸', '¹', '÷', '»', '¼', '½', '¾', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', '‗',
'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח',
'ט', 'י', 'ך', 'כ', 'ל', 'ם', 'מ', 'ן',
'נ', 'ס', 'ע', 'ף', 'פ', 'ץ', 'צ', 'ק',
// v v those are wrong
'ר', 'ש', 'ת', ' ', ' ', ' ', ' ', ' ']; // FIXME: those ones marked wrong are supposed to be left to right and right to left markers, not spaces.
immutable dchar[] ISO_8869_9 = [
' ', '¡', '¢', '£', '¤', '¥', '¦', '§',
'¨', '©', 'ª', '«', '¬', '­', '®', '¯',
'°', '±', '²', '³', '´', 'µ', '¶', '·',
'¸', '¹', 'º', '»', '¼', '½', '¾', '¿',
'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç',
'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï',
'Ğ', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', '×',
'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'İ', 'Ş', 'ß',
'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç',
'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï',
'ğ', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', '÷',
'ø', 'ù', 'ú', 'û', 'ü', 'ı', 'ş', 'ÿ'];
immutable dchar[] ISO_8869_10 = [
' ', 'Ą', 'Ē', 'Ģ', 'Ī', 'Ĩ', 'Ķ', '§',
'Ļ', 'Đ', 'Š', 'Ŧ', 'Ž', '­', 'Ū', 'Ŋ',
'°', 'ą', 'ē', 'ģ', 'ī', 'ĩ', 'ķ', '·',
'ļ', 'đ', 'š', 'ŧ', 'ž', '―', 'ū', 'ŋ',
'Ā', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Į',
'Č', 'É', 'Ę', 'Ë', 'Ė', 'Í', 'Î', 'Ï',
'Ð', 'Ņ', 'Ō', 'Ó', 'Ô', 'Õ', 'Ö', 'Ũ',
'Ø', 'Ų', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß',
'ā', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'į',
'č', 'é', 'ę', 'ë', 'ė', 'í', 'î', 'ï',
'ð', 'ņ', 'ō', 'ó', 'ô', 'õ', 'ö', 'ũ',
'ø', 'ų', 'ú', 'û', 'ü', 'ý', 'þ', 'ĸ'];
immutable dchar[] ISO_8869_11 = [
' ', 'ก', 'ข', 'ฃ', 'ค', 'ฅ', 'ฆ', 'ง',
'จ', 'ฉ', 'ช', 'ซ', 'ฌ', 'ญ', 'ฎ', 'ฏ',
'ฐ', 'ฑ', 'ฒ', 'ณ', 'ด', 'ต', 'ถ', 'ท',
'ธ', 'น', 'บ', 'ป', 'ผ', 'ฝ', 'พ', 'ฟ',
'ภ', 'ม', 'ย', 'ร', 'ฤ', 'ล', 'ฦ', 'ว',
'ศ', 'ษ', 'ส', 'ห', 'ฬ', 'อ', 'ฮ', 'ฯ',
'ะ', 'ั', 'า', 'ำ', 'ิ', 'ี', 'ึ', 'ื',
'ุ', 'ู', 'ฺ', ' ', ' ', ' ', ' ', '฿',
'เ', 'แ', 'โ', 'ใ', 'ไ', 'ๅ', 'ๆ', '็',
'่', '้', '๊', '๋', '์', 'ํ', '๎', '๏',
'', '๑', '๒', '๓', '๔', '๕', '๖', '๗',
'๘', '๙', '๚', '๛', ' ', ' ', ' ', ' '];
immutable dchar[] ISO_8869_13 = [
' ', '”', '¢', '£', '¤', '„', '¦', '§',
'Ø', '©', 'Ŗ', '«', '¬', '­', '®', 'Æ',
'°', '±', '²', '³', '“', 'µ', '¶', '·',
'ø', '¹', 'ŗ', '»', '¼', '½', '¾', 'æ',
'Ą', 'Į', 'Ā', 'Ć', 'Ä', 'Å', 'Ę', 'Ē',
'Č', 'É', 'Ź', 'Ė', 'Ģ', 'Ķ', 'Ī', 'Ļ',
'Š', 'Ń', 'Ņ', 'Ó', 'Ō', 'Ő', 'Ö', '×',
'Ų', 'Ł', 'Ś', 'Ū', 'Ü', 'Ż', 'Ž', 'ß',
'ą', 'į', 'ā', 'ć', 'ä', 'å', 'ę', 'ē',
'č', 'é', 'ź', 'ė', 'ģ', 'ķ', 'ī', 'ļ',
'š', 'ń', 'ņ', 'ó', 'ō', 'ő', 'ö', '÷',
'ų', 'ł', 'ś', 'ū', 'ü', 'ż', 'ž', ''];
immutable dchar[] ISO_8869_14 = [
' ', 'Ḃ', 'ḃ', '£', 'Ċ', 'ċ', 'Ḋ', '§',
'Ẁ', '©', 'Ẃ', 'ḋ', 'Ỳ', '­', '®', 'Ÿ',
'Ḟ', 'ḟ', 'Ġ', 'ġ', 'Ṁ', 'ṁ', '¶', 'Ṗ',
'ẁ', 'ṗ', 'ẃ', 'Ṡ', 'ỳ', 'Ẅ', 'ẅ', 'ṡ',
'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç',
'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï',
'Ŵ', 'Ñ', 'Ò', 'Ó', 'Ô', 'Ő', 'Ö', 'Ṫ',
'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Ŷ', 'ß',
'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç',
'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï',
'ŵ', 'ñ', 'ò', 'ó', 'ô', 'ő', 'ö', 'ṫ',
'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ŷ', 'ÿ'];
immutable dchar[] ISO_8869_15 = [
' ', '¡', '¢', '£', '€', '¥', 'Š', '§',
'š', '©', 'ª', '«', '¬', '­', '®', '¯',
'°', '±', '²', '³', 'Ž', 'µ', '¶', '·',
'ž', '¹', 'º', '»', 'Œ', 'œ', 'Ÿ', '¿',
'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç',
'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï',
'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Ő', 'Ö', '×',
'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß',
'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç',
'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï',
'ð', 'ñ', 'ò', 'ó', 'ô', 'ő', 'ö', '÷',
'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'þ', 'ÿ'];
immutable dchar[] ISO_8869_16 = [
' ', 'Ą', 'ą', 'Ł', '€', '„', 'Š', '§',
'š', '©', 'Ș', '«', 'Ź', '­', 'ź', 'Ż',
'°', '±', 'Č', 'ł', 'Ž', '”', '¶', '·',
'ž', 'č', 'ș', '»', 'Œ', 'œ', 'Ÿ', 'ż',
'À', 'Á', 'Â', 'Ă', 'Ä', 'Ć', 'Æ', 'Ç',
'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï',
'Ð', 'Ń', 'Ò', 'Ó', 'Ô', 'Ő', 'Ö', 'Ś',
'Ű', 'Ù', 'Ú', 'Û', 'Ü', 'Ę', 'Ț', 'ß',
'à', 'á', 'â', 'ă', 'ä', 'ć', 'æ', 'ç',
'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï',
'đ', 'ń', 'ò', 'ó', 'ô', 'ő', 'ö', 'ś',
'ű', 'ù', 'ú', 'û', 'ü', 'ę', 'ț', 'ÿ'];