forked from Yara724/api
80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
import { Injectable, Logger } from "@nestjs/common";
|
|
|
|
/**
|
|
* Characters stripped from plate text during normalization.
|
|
* These invisible Unicode code-points can sneak in via copy-paste
|
|
* or OCR and break exact-match lookups against the plate letter map.
|
|
*/
|
|
const INVISIBLE_CHARS = [
|
|
"\u200C", // Zero-Width Non-Joiner
|
|
"\u200D", // Zero-Width Joiner
|
|
"\u200E", // Left-to-Right Mark
|
|
"\u200F", // Right-to-Left Mark
|
|
"\uFEFF", // BOM / Zero-Width No-Break Space
|
|
];
|
|
|
|
const INVISIBLE_RE = new RegExp(`[${INVISIBLE_CHARS.join("")}]`, "g");
|
|
|
|
/**
|
|
* Normalize a Persian license-plate character (or short text) so that
|
|
* visually-identical Unicode variants collapse to the canonical form
|
|
* used in the plate letter mapping.
|
|
*
|
|
* Steps (in order):
|
|
* 1. NFKC normalization — folds compatibility equivalents
|
|
* 2. Strip invisible Unicode characters (ZWJ, ZWNJ, LTR/RTL marks, BOM)
|
|
* 3. Convert Arabic ي (U+064A) → Persian ی (U+06CC)
|
|
* 4. Trim leading/trailing whitespace
|
|
*/
|
|
export function normalizePlateText(text: string): string {
|
|
if (!text) return text;
|
|
|
|
let result = String(text);
|
|
|
|
// 1. Unicode NFKC normalization
|
|
result = result.normalize("NFKC");
|
|
|
|
// 2. Remove invisible characters
|
|
result = result.replace(INVISIBLE_RE, "");
|
|
|
|
// 3. Arabic ي → Persian ی
|
|
result = result.replace(/\u064A/g, "\u06CC");
|
|
|
|
// 4. Trim whitespace
|
|
result = result.trim();
|
|
|
|
return result;
|
|
}
|
|
|
|
@Injectable()
|
|
export class PlateNormalizerService {
|
|
private readonly logger = new Logger(PlateNormalizerService.name);
|
|
|
|
/** @see module-level {@link normalizePlateText} */
|
|
normalizePlateText(text: string): string {
|
|
return normalizePlateText(text);
|
|
}
|
|
|
|
/**
|
|
* Normalize a plate letter and, if it doesn't resolve in the given
|
|
* mapping, log the raw Unicode code-points for debugging.
|
|
*
|
|
* @returns the numeric code from `mapping`, or `null` when the letter
|
|
* is unknown after normalization.
|
|
*/
|
|
resolvePlateLetterCode(
|
|
letter: string,
|
|
mapping: Record<string, number>,
|
|
): number | null {
|
|
const normalized = this.normalizePlateText(letter);
|
|
|
|
const code = mapping[normalized];
|
|
if (code !== undefined) return code;
|
|
|
|
this.logger.debug(
|
|
`Unknown plate letter: ${normalized} [${Array.from(normalized).map((c) => `U+${c.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0")}`).join(", ")}]`,
|
|
);
|
|
return null;
|
|
}
|
|
}
|