read-gedcom
Version:
Gedcom file reader
141 lines • 6.1 kB
JavaScript
import { tokenize } from './tokenizer';
import { buildTree } from './structurer';
import { decodeUtfBOM } from './decoding';
/**
* Supported Gedcom file encoding schemes.
*/
export var FileEncoding;
(function (FileEncoding) {
FileEncoding["Utf8"] = "UTF-8";
FileEncoding["Ansel"] = "ANSEL";
FileEncoding["Cp1252"] = "Cp1252";
FileEncoding["Macintosh"] = "Macintosh";
FileEncoding["Cp850"] = "Cp850";
FileEncoding["Utf16be"] = "UTF-16be";
FileEncoding["Utf16le"] = "UTF-16le";
})(FileEncoding || (FileEncoding = {}));
/**
* Extracts the file metadata (see {@link FileMetadata}). The metadata can then be used to guess the actual charset of the file.
* To circumvent the bootstrapping problem this function restricts its reading to the first lines of the file only, and
* decodes them as UTF-8.
* @param buffer The content of the file
* @param maxPeekBytes Maximum number of bytes to read
* @param maxPeekLines Maximum number of lines to read
*/
export const getFileMetadata = (buffer, maxPeekBytes = 5000, maxPeekLines = 200) => {
const { output: inputHead, bomCharset } = decodeUtfBOM(buffer.slice(0, maxPeekBytes)); // Start with UTF-8 since file can contain a BOM
const hasBOM = bomCharset !== null;
const it = tokenize(inputHead, false); // Non-strict mode: break silently on error
let i = 0;
const array = [];
for (const line of it) {
if (i >= maxPeekLines) {
break;
}
array.push(line);
i += 1;
}
const tree = buildTree(array);
const header = tree.children.filter(c => c.tag === "HEAD" /* Tag.Header */);
const char = header.flatMap(c => c.children.filter(n => n.tag === "CHAR" /* Tag.Character */ || n.tag === "CHARACTER" /* TagNonStandard.CharacterAlt */));
const charOpt = char.length > 0 ? char[0].value : null;
const source = header.flatMap(c => c.children.filter(n => n.tag === "SOUR" /* Tag.Source */));
const sourceOpt = source.length > 0 ? source[0].value : null;
const version = source.flatMap(c => c.children.filter(n => n.tag === "VERS" /* Tag.Version */));
const versionOpt = version.length > 0 ? version[0].value : null;
return { sourceEncoding: charOpt, sourceProvider: sourceOpt, sourceProviderVersion: versionOpt, fileHasBOM: hasBOM };
};
/**
* Detects the file charset using a set of heuristics. Has proven to work great in practice.
* If you encounter a file for which this procedure doesn't work as intended please do open an issue
* at: https://github.com/arbre-app/read-gedcom/issues.
* @param buffer The content of the file
*/
export const detectCharset = (buffer) => {
// eslint-disable-next-line
const { sourceEncoding, sourceProvider, sourceProviderVersion, fileHasBOM } = getFileMetadata(buffer);
// Estimate an encoding without knowing the provider
function estimateEncoding() {
if (sourceEncoding === "UTF-8" /* ValueCharacterEncoding.Utf8 */) {
return "UTF-8" /* FileEncoding.Utf8 */;
}
else if (sourceEncoding === "ANSEL" /* ValueCharacterEncoding.Ansel */) {
return "ANSEL" /* FileEncoding.Ansel */;
}
else if (sourceEncoding === "ASCII" /* ValueCharacterEncoding.Ascii */) {
return "Cp1252" /* FileEncoding.Cp1252 */;
}
else if (sourceEncoding === "ANSI" /* ValueCharacterEncoding.Ansi */) {
return "Cp1252" /* FileEncoding.Cp1252 */;
}
else if (sourceEncoding === "UNICODE" /* ValueCharacterEncoding.Unicode */) {
return "UTF-16be" /* FileEncoding.Utf16be */; // RFC 2781
}
else if (sourceEncoding === 'WINDOWS' || sourceEncoding === 'IBM WINDOWS') {
return "Cp1252" /* FileEncoding.Cp1252 */;
}
else if (sourceEncoding === 'MACINTOSH') {
return "Macintosh" /* FileEncoding.Macintosh */;
}
else if (sourceEncoding === 'IBMPC') {
return "Cp850" /* FileEncoding.Cp850 */;
}
else if (sourceEncoding === 'MSDOS') {
return "Cp850" /* FileEncoding.Cp850 */;
}
else if (sourceEncoding === 'UNIX') {
return "Cp1252" /* FileEncoding.Cp1252 */;
}
else if (sourceEncoding === 'UTF8') { // Spelling mistake
return "UTF-8" /* FileEncoding.Utf8 */;
}
else if (sourceEncoding === 'windows-1250') { // Our best guess (Rodokmen Pro)
return "Cp1252" /* FileEncoding.Cp1252 */;
}
else { // Unknown encoding
return "UTF-8" /* FileEncoding.Utf8 */; // Defaults to UTF-8
}
}
if (fileHasBOM) { // Short-circuit: must be one of UTF-{8,16,32}
return "UTF-8" /* FileEncoding.Utf8 */;
}
if (sourceProvider === 'GeneWeb') { // Geneweb
if (sourceEncoding === "ASCII" /* ValueCharacterEncoding.Ascii */) {
return "Cp1252" /* FileEncoding.Cp1252 */;
}
else if (sourceEncoding === "UTF-8" /* ValueCharacterEncoding.Utf8 */) {
return "UTF-8" /* FileEncoding.Utf8 */;
}
else {
return estimateEncoding();
}
}
else if (sourceProvider != null && sourceProvider.startsWith('HEREDIS')) { // Heredis
if (sourceEncoding === "ANSI" /* ValueCharacterEncoding.Ansi */) {
return "Cp1252" /* FileEncoding.Cp1252 */;
}
else {
return estimateEncoding();
}
}
else if (sourceProvider === 'GENEATIQUE') { // Généatique
if (sourceEncoding === "ANSEL" /* ValueCharacterEncoding.Ansel */) {
return "ANSEL" /* FileEncoding.Ansel */;
}
else {
return estimateEncoding();
}
}
else if (sourceProvider === 'Gramps') { // Gramps
if (sourceEncoding === "UTF-8" /* ValueCharacterEncoding.Utf8 */) {
return "UTF-8" /* FileEncoding.Utf8 */;
}
else {
return estimateEncoding();
}
}
else { // Unknown provider
return estimateEncoding();
}
};
//# sourceMappingURL=decoder.js.map