haker.info | Etyczny hacking |

haker.info  — Etyczny hacking

 Dekodowanie kodu Aztec 2D z dowodu rejestracyjnego

04 stycznia 2019 godz. 00:35    Dawid Farbaniec    2290 słów

1. Kod Aztec 2D z dowodu rejestracyjnego

W dowodzie rejestracyjnym pojazdu można zauważyć kod dwuwymiarowy. Jest to kod Aztec 2D i jak się można domyślić, w założeniach miał usprawnić odczyt danych z tego dokumentu.

Mało by mnie to interesowało, gdyby nie fakt, że sposób kodowania w nim danych - do niedawna - znany był tylko wąskiej grupie ludzi. Taka się wydaje powszechna rzecz ten kod i takie ograniczenia dostępu do informacji.

Kod Aztec 2D na dowodzie rejestracyjnym
Rysunek 1.1. Przykładowy kod Aztec 2D na dowodzie rejestracyjnym

2. Metoda kodowania danych w dowodzie rej. pojazdu

Na rysunku 2.1. poniżej przedstawiono schemat poszczególnych kroków jakie należy podjąć, aby móc odczytać dane zeskanowane z kodu Aztec 2D dowodu rejestracyjnego pojazdu.

Dekodowanie kodu Aztec 2D z dowodu rejestracyjnego
Rysunek 2.1. Metoda dekodowania znaku Aztec 2D z dowodu rejestracyjnego

Postaram się poniżej rozwinąć trochę opis poszczególnych etapów:

  • Skanowanie: Aby zeskanować kod Aztec 2D z polskiego dowodu rejestracyjnego pojazdu można użyć gotowych skanerów kodów Aztec/2D. Po zeskanowaniu otrzymamy ciąg znaków.
  • Dekodowanie Base64: otrzymany ciąg znaków jest kodowany poprzez algorytm Base64. Dodatkowo można się spotkać z dodanym jednym dodatkowym znakiem na końcu. Należy to wziąć pod uwagę i pominąć ten znak, gdy zauważymy, że funkcja Base64 nie działa poprawnie.
  • Kompresja NRV2E: Na tym etapie dane są skompresowane i należy je zdekompresować algorytmem NRV2E, którego implementację można znaleźć m.in. w darmowej bibliotece UCL autorstwa Markus F.X.J. Oberhumer (http://www.oberhumer.com/opensource/ucl/). Funkcja ucl_nrv2e_decompress_8().
  • Dane czyste (plain): Po tych wszystkich etapach otrzymujemy dane tekstowe z kodowaniem UTF16-LE, gdzie poszczególne informacje są oddzielone znakiem pionowej kreski |, której kod to 0x7C.

3. Dekodowanie Aztec 2D z dowodu rej. pojazdu — kody źródłowe


3.1. C#.NET — kod źródłowy (License)


VehicleDocumentAztecDecoder.cs

/*  * C#.NET implementation of NRV2E decompression algorithm  * - by http://haker.info A.D. 2019  * Based on original UCL library written by:  * Markus F.X.J. Oberhumer <[email protected]>  * http://www.oberhumer.com/opensource/ucl/  *  * This program is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License as published by  * the Free Software Foundation, either version 3 of the License, or  * (at your option) any later version.  * This program is distributed in the hope that it will be useful,  * but WITHOUT ANY WARRANTY; without even the implied warranty of  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  * GNU General Public License for more details. */ using System; using System.Text; namespace FreeAztecVehicleDocDecoder { public class VehicleDocumentAztecDecoder { private const int START_OFFSET = 4; private byte[] src; private int ilen = START_OFFSET; private int currentByte; private int currentBit; private byte[] dst; public string Decode(string text) { byte[] decoded = Base64Decode(text); byte[] decompressed = DecompressNRV2E(decoded); return Encoding.Unicode.GetString(decompressed); } private byte[] DecompressNRV2E(byte[] sourceData) { src = sourceData; uint olen = 0, last_m_off = 1; dst = new byte[BitConverter.ToInt32(src, 0)]; while (ilen < src.Length) { uint m_off, m_len; while (GetBit() == 1) { dst[olen++] = src[ilen++]; } m_off = 1; while (true) { m_off = m_off * 2 + GetBit(); if (GetBit() == 1) break; m_off = (m_off - 1) * 2 + GetBit(); } if (m_off == 2) { m_off = last_m_off; m_len = GetBit(); } else { m_off = (m_off - 3) * 256 + src[ilen++]; if (m_off == 0xffffffff) break; m_len = (m_off ^ 0xffffffff) & 1; m_off >>= 1; last_m_off = ++m_off; } if (m_len > 0) m_len = (uint)1 + GetBit(); else if (GetBit() == 1) m_len = (uint)3 + GetBit(); else { m_len++; do { m_len = m_len * 2 + GetBit(); } while (GetBit() == 0); m_len += 3; } m_len += (uint)(m_off > 0x500 ? 1 : 0); uint m_pos; m_pos = olen - m_off; dst[olen++] = dst[m_pos++]; do dst[olen++] = dst[m_pos++]; while (--m_len > 0); } return dst; } private byte GetBit() { if (ilen >= src.Length) throw new Exception("Przesunięcie jest poza zakresem."); if (currentBit == 0) { currentByte = src[ilen++]; currentBit = 8; } return (byte)(((uint)currentByte >> --currentBit) & 1); } private byte[] Base64Decode(string textToDecode) { if (string.IsNullOrWhiteSpace(textToDecode)) return new byte[0]; if (textToDecode.Length % 2 == 1) { textToDecode = textToDecode.Substring(0, textToDecode.Length - 1); } return Convert.FromBase64String(textToDecode); } } }

VehicleRegInfoParser.cs

/*  * C#.NET decoder of Aztec 2D used on Polish Vehicle Registration Documents  * - by http://haker.info A.D. 2019  * Based on original UCL library written by:  * Markus F.X.J. Oberhumer <[email protected]>  * http://www.oberhumer.com/opensource/ucl/  *  * This program is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License as published by  * the Free Software Foundation, either version 3 of the License, or  * (at your option) any later version.  * This program is distributed in the hope that it will be useful,  * but WITHOUT ANY WARRANTY; without even the implied warranty of  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  * GNU General Public License for more details. */ using System.Collections.Generic; namespace FreeAztecVehicleDocDecoder { public class VehicleRegInfoParser { public List<Field> ParseAztecDecodedText(string text) { List<Field> infoFields = new List<Field>(); string[] splitted = text.Split(new[] { '|' }); infoFields.Add(new Field("XXC1", splitted[0], "Wersja protokołu")); infoFields.Add(new Field("Seria dowodu rej", splitted[1], "Seria i numer dowodu")); infoFields.Add(new Field("?", splitted[2], "Kod terytorialny urzędu rejestrującego")); infoFields.Add(new Field("Organ wydający", splitted[3], "Organ wydający - Nazwa")); infoFields.Add(new Field("Organ wydający Linia 2", splitted[4], "Organ wydający - Gmina")); infoFields.Add(new Field("Organ wydający Linia 3", splitted[5], "Organ wydający - Ulica i nr")); infoFields.Add(new Field("Organ wydający Linia 4", splitted[6], "Organ wydający - Kod i miejscowość")); infoFields.Add(new Field("A", splitted[7], "Numer rejestracyjny pojazdu")); infoFields.Add(new Field("D.1", splitted[8], "Marka")); infoFields.Add(new Field("D.2", splitted[9], "Typ homologacji")); infoFields.Add(new Field("D.2", splitted[10], "Wariant homologacji")); infoFields.Add(new Field("D.2", splitted[11], "Wersja homologacji")); infoFields.Add(new Field("D.3", splitted[12], "Model")); infoFields.Add(new Field("E", splitted[13], "VIN")); infoFields.Add(new Field("I", splitted[14], "Data wydania dowodu rejestracyjnego")); infoFields.Add(new Field("H", splitted[15], "Okres ważności dowodu")); infoFields.Add(new Field("C.1.1", splitted[16], "Nazwa właściciela")); infoFields.Add(new Field("C.1.1", splitted[17], "Imiona")); infoFields.Add(new Field("C.1.1", splitted[18], "Nazwisko")); infoFields.Add(new Field("?", splitted[19], "?")); infoFields.Add(new Field("C.1.1", splitted[20], "PESEL")); infoFields.Add(new Field("C.1.3", splitted[21], "Kod pocztowy")); infoFields.Add(new Field("C.1.3", splitted[22], "Gmina")); infoFields.Add(new Field("C.1.3", splitted[23], "Miejscowość")); infoFields.Add(new Field("C.1.3", splitted[24], "Ulica")); infoFields.Add(new Field("C.1.3", splitted[25], "Nr domu")); infoFields.Add(new Field("C.1.3", splitted[26], "Nr mieszkania")); infoFields.Add(new Field("C.1.3", splitted[27], "Nazwa właściciela")); infoFields.Add(new Field("C.1.3", splitted[28], "Imiona")); infoFields.Add(new Field("C.1.3", splitted[29], "Nazwisko")); infoFields.Add(new Field("C.1.3", splitted[30], "?")); infoFields.Add(new Field("C.2.2", splitted[31], "PESEL")); infoFields.Add(new Field("C.2.3", splitted[32], "Kod pocztowy")); infoFields.Add(new Field("C.2.3", splitted[33], "Gmina")); infoFields.Add(new Field("C.2.3", splitted[34], "Miejscowość")); infoFields.Add(new Field("C.2.3", splitted[35], "Ulica")); infoFields.Add(new Field("C.2.3", splitted[36], "Nr domu")); infoFields.Add(new Field("C.2.3", splitted[37], "Nr mieszkania")); infoFields.Add(new Field("F.1", splitted[38], "Maksymalna masa całkowita [kg]")); infoFields.Add(new Field("F.2", splitted[39], "Maksymalna masa całkowita [kg]")); infoFields.Add(new Field("F.3", splitted[40], "Maksymalna masa całkowita [kg]")); infoFields.Add(new Field("G", splitted[41], "Masa własna")); infoFields.Add(new Field("J", splitted[42], "Kategoria pojazdu")); infoFields.Add(new Field("K", splitted[43], "Numer świadectwa homologacji typu pojazdu")); infoFields.Add(new Field("L", splitted[44], "Liczba osi")); infoFields.Add(new Field("O.1", splitted[45], "Maksymalna masa całkowita przyczepy z hamulcem")); infoFields.Add(new Field("O.2", splitted[46], "Maksymalna mas całkowita przyczepy bez hamulca")); infoFields.Add(new Field("Q", splitted[47], "Stosunek mocy do masy (w KW/kg)")); infoFields.Add(new Field("P.1", splitted[48], "Pojemność silnika [cm3]")); infoFields.Add(new Field("P.2", splitted[49], "Moc silnika [kW]")); infoFields.Add(new Field("P.3", splitted[50], "Rodzaj paliwa")); infoFields.Add(new Field("B", splitted[51], "Data pierwszej rejestracji pojazdu")); infoFields.Add(new Field("S.1", splitted[52], "Liczba miejsc siedzących")); infoFields.Add(new Field("S.2", splitted[53], "Liczba miejsc stojących")); infoFields.Add(new Field("Rodzaj pojazdu", splitted[54], "")); infoFields.Add(new Field("Przeznaczenie", splitted[55], "")); infoFields.Add(new Field("Rok produkcji", splitted[56], "")); infoFields.Add(new Field("Dopuszczalna ładowność", splitted[57], "")); infoFields.Add(new Field("Największy dopuszczalny nacisk osi", splitted[58], "")); infoFields.Add(new Field("Numer karty pojazdu", splitted[59], "")); infoFields.Add(new Field("?", splitted[60], "Kod ITS")); infoFields.Add(new Field("?", splitted[61], "?")); infoFields.Add(new Field("?", splitted[62], "?")); infoFields.Add(new Field("?", splitted[63], "?")); infoFields.Add(new Field("?", splitted[64], "?")); infoFields.Add(new Field("?", splitted[65], "?")); return infoFields; } } public class Field { public string Name { get; set; } public string Value { get; set; } public string Description { get; set; } public Field(string name, string value, string description) { Name = name; Value = value; Description = description; } } }

Program.cs

/*  * C#.NET decoder of Aztec 2D used on Polish Vehicle Registration Documents  * - by http://haker.info A.D. 2019  * Based on original UCL library written by:  * Markus F.X.J. Oberhumer <[email protected]>  * http://www.oberhumer.com/opensource/ucl/  *  * This program is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License as published by  * the Free Software Foundation, either version 3 of the License, or  * (at your option) any later version.  * This program is distributed in the hope that it will be useful,  * but WITHOUT ANY WARRANTY; without even the implied warranty of  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  * GNU General Public License for more details. */ using System; using System.Collections.Generic; using System.Text; namespace FreeAztecVehicleDocDecoder { class Program { static void Main(string[] args) { //Przykładowy ciąg Aztec 2D string test1 = "BgQAANtYAAJDAPkxAHwAQXIw7zcGNN4ANiox+w81HrUGOP8eUABSAEUA+1oAWQBEDv9OAFQAIABN3wAuClMAvlQPV/eKUhq9Wg5X7k58UtcWSVq9TF5J79pBZ+5PAEsG12bTSm5GVQBM/ntSAEH7L1dj+0MAS1vvMvovewo3Ut4wDi39HjEAN6Pbl0FNe3YgPt5Q3kv3IlSevVnX1z9FMmuCShL2WgBaG9umKADvSAApJnx75k+itwZMAEx9X0rvbkSOTXtOOF/DRy0WOW53fPYLFoMzLr0xAi3DGnevLQOCfJ/vQZ5TcBZrN0oa9k4AfA82Q4QaDzj3q8deN6sN7zIE/1x8lbMnQdwBQi5ZT86jL2tqNAr2MwAw34xSH+uPSVPYFxZThBMzON8AMJM5wQA3MwRcMX7bNcET2jInwyedE01HZ4dlM94qKy0DL38fNgAqeBszSxOvNIeKfHM7fCLxNQAwVkMtdzl7Xiw/YMyrFzxQACBWw+Hza7c3C93/NWuHg1OWRquPQ5KP02K9IBZT4QZC9oNZU7aXFiOX83U4ADJFC7ADhrNVCyOW8w9qMbEnZhdHbHxjdjIT7E4DW0M3OQuGaxYmCSSSSSr/"; VehicleDocumentAztecDecoder dec = new VehicleDocumentAztecDecoder(); string result = dec.Decode(test1); Console.OutputEncoding = Encoding.Unicode; //Wyświetl rozkodowane dane na konsoli tekstowej Console.WriteLine(result); //Wczytaj dane do listy z opisem poszczególnych pól VehicleRegInfoParser parser = new VehicleRegInfoParser(); List<Field> info = parser.ParseAztecDecodedText(result); //Wyświetl dane wraz z opisami foreach (var item in info) Console.WriteLine(item.Description + " (" + item.Name + ")" + " : \t" + item.Value); #if DEBUG Console.ReadLine(); #endif } } }

3.2. C++ — kod źródłowy (License)


/* * C++ implementation of NRV2E decompression algorithm * which was used in this project to decode * Aztec 2D from Polish Vehicle Registration Documents * - by http://haker.info A.D. 2019 * * Based on original UCL library written by: * Markus F.X.J. Oberhumer <[email protected]> * http://www.oberhumer.com/opensource/ucl/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include <iostream> #include <string> #include <vector> const int START_OFFSET = 4; std::vector<unsigned char> src; int ilen = START_OFFSET; int currentByte; int currentBit; static unsigned char GetBit() { if (ilen >= src.size()) throw std::invalid_argument("Przesunięcie jest poza zakresem."); if (currentBit == 0) { currentByte = src[ilen++]; currentBit = 8; } return (unsigned char)(((unsigned int)currentByte >> --currentBit) & 1); } static std::vector<unsigned char> DecompressNRV2E(std::vector<unsigned char> sourceData) { src = sourceData; int destSize = src[0] | (int)src[1] << 8 | (int)src[2] << 16 | (int)src[3] << 24; std::vector<unsigned char> dst(destSize); unsigned int olen = 0, last_m_off = 1; while (ilen < src.size()) { unsigned int m_off, m_len; while (GetBit() == 1) { dst[olen++] = src[ilen++]; } m_off = 1; while (true) { m_off = m_off * 2 + GetBit(); if (GetBit() == 1) break; m_off = (m_off - 1) * 2 + GetBit(); } if (m_off == 2) { m_off = last_m_off; m_len = GetBit(); } else { m_off = (m_off - 3) * 256 + src[ilen++]; if (m_off == 0xffffffff) break; m_len = (m_off ^ 0xffffffff) & 1; m_off >>= 1; last_m_off = ++m_off; } if (m_len > 0) m_len = (unsigned int)1 + GetBit(); else if (GetBit() == 1) m_len = (unsigned int)3 + GetBit(); else { m_len++; do { m_len = m_len * 2 + GetBit(); } while (GetBit() == 0); m_len += 3; } m_len += (unsigned int)(m_off > 0x500 ? 1 : 0); unsigned int m_pos; m_pos = olen - m_off; dst[olen++] = dst[m_pos++]; do dst[olen++] = dst[m_pos++]; while (--m_len > 0); } return dst; } static std::string base64_decode(const std::string &in) { std::string out; std::vector<int> T(256, -1); for (int i = 0; i < 64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; int val = 0, valb = -8; for (unsigned char c : in) { if (T[c] == -1) break; val = (val << 6) + T[c]; valb += 6; if (valb >= 0) { out.push_back(char((val >> valb) & 0xFF)); valb -= 8; } } return out; } int main(int argc, char *argv[]) { std::string test1 = "BgQAANtYAAJDAPkxAHwAQXIw7zcGNN4ANiox+w81HrUGOP8eUABSAEUA+1oAWQBEDv9OAFQAIABN3wAuClMAvlQPV/eKUhq9Wg5X7k58UtcWSVq9TF5J79pBZ+5PAEsG12bTSm5GVQBM/ntSAEH7L1dj+0MAS1vvMvovewo3Ut4wDi39HjEAN6Pbl0FNe3YgPt5Q3kv3IlSevVnX1z9FMmuCShL2WgBaG9umKADvSAApJnx75k+itwZMAEx9X0rvbkSOTXtOOF/DRy0WOW53fPYLFoMzLr0xAi3DGnevLQOCfJ/vQZ5TcBZrN0oa9k4AfA82Q4QaDzj3q8deN6sN7zIE/1x8lbMnQdwBQi5ZT86jL2tqNAr2MwAw34xSH+uPSVPYFxZThBMzON8AMJM5wQA3MwRcMX7bNcET2jInwyedE01HZ4dlM94qKy0DL38fNgAqeBszSxOvNIeKfHM7fCLxNQAwVkMtdzl7Xiw/YMyrFzxQACBWw+Hza7c3C93/NWuHg1OWRquPQ5KP02K9IBZT4QZC9oNZU7aXFiOX83U4ADJFC7ADhrNVCyOW8w9qMbEnZhdHbHxjdjIT7E4DW0M3OQuGaxYmCSSSSSr/"; if (test1.length() % 2 == 1) { test1[test1.length() - 1] = '\0'; } std::string decoded = base64_decode(test1); std::vector<unsigned char> decodedVec = std::vector<unsigned char>(decoded.begin(), decoded.end()); std::vector<unsigned char> decompressed = DecompressNRV2E(decodedVec); std::wstring plainData(decompressed.begin(), decompressed.end()); std::wcout << plainData << std::endl; #if _DEBUG getchar(); #endif return EXIT_SUCCESS; }

4. Opis poszczególnych pól z danymi

Przykładowe rozkodowane dane w stanie surowym przedstawiono na rysunku 4.1 poniżej.

Przykładowe rozkodowane dane
Rysunek 4.1. Przykład rozkodowania kodu Aztec 2D z dow. rej. - dane surowe

W przypadku pisania własnego oprogramowania bazującego na powyższym kodzie, na pewno będzie przydatny opis poszczególnych pól.

Pozycja (indeks) Opis
0Wersja protokołu
1Seria i numer dowodu
2Kod terytorialny urzędu rejestrującego
3Organ wydający - Nazwa
4Organ wydający - Gmina
5Organ wydający - Ulica i nr
6Organ wydający - Kod i miejscowość
7Numer rejestracyjny pojazdu
8Marka
9Typ homologacji
10Wariant homologacji
11Wersja homologacji
12Model
13Numer VIN
14Data wydania dowodu rejestracyjnego
15Okres ważności dowodu
16Nazwa właściciela
17Imiona
18Nazwisko
19?
20PESEL
21Kod pocztowy
22Gmina
23Miejscowość
24Ulica
25Nr domu
26Nr mieszkania
27Nazwa właściciela
28Imiona
29Nazwisko
30?
31PESEL
32Kod pocztowy
33Gmina
34Miejscowość
35Ulica
36Nr domu
37Nr mieszkania
38Maksymalna masa całkowita [kg]
39Maksymalna masa całkowita pojazdu [kg]
40Maksymalna masa całkowita zespołu pojazdów [kg]
41Masa własna
42Kategoria pojazdu
43Numer świadectwa homologacji typu pojazdu
44Liczba osi
45Maksymalna masa całkowita przyczepy z hamulcem
46Maksymalna mas całkowita przyczepy bez hamulca
47Stosunek mocy do masy (w KW/kg)
48Pojemność silnika [cm3]
49Moc silnika [kW]
50Rodzaj paliwa
51Data pierwszej rejestracji pojazdu
52Liczba miejsc siedzących
53Liczba miejsc stojących
54Rodzaj pojazdu
55Przeznaczenie
56Rok produkcji
57Dopuszczalna ładowność
58Największy dopuszczalny nacisk osi
59Numer karty pojazdu
60Kod ITS
61?
62?
63?
64?
65?

5. Podsumowanie

Dzięki za przeczytanie. Jeśli są jakieś pytania lub sugestie dot. tego wpisu to proszę pisać.

Dawid Farbaniec


Tagi:  reverse-engineering  visual-cpp  c-sharp 

Komentarze czytających

Czytelnik napisał:

Rewelacja, super robota. Mam pytanie, jak wykonać to w drugą stronę tzn. aby otrzymać kod Aztec obrazkowy z posiadanych danych.

 ponad 8 miesięcy temu (13 lutego 2019 godz. 18:55)
Dawid (haker.info) napisał:

Ja próbowałbym wykonać poszczególne kroki dekodowania w odwrotnej kolejności 😃

  1. Zapisać dane tekstem w kodowaniu UTF-16LE oddzielone znakiem pionowej linii | (0x7C) w odpowiedniej kolejności
  2. Skompresować algorytmem NRV2E (wariant 8-bitowy)
  3. Zakodować za pomocą algorytmu Base64
  4. No i o ten ostatni krok pewnie jest pytanie. Trzeba znaleźć coś co wygeneruje Aztec 2D. Nie pomogę w wyborze, bo nigdy takich bibliotek nie szukałem. Ale powinno być coś takiego, gdyż ten cały Aztec 2D to dość popularny barcode.

Dla wielu programistów problemem było to, że do niedawna algorytm nie był jawny dla wszystkich. Nie było wiadomo, że to kompresja NRV2E w wariancie 8-bitowym. Teraz wiadomo czego należy użyć i pozostaje tylko odpowiednie zaprogramowanie tego.

Pozdrawiam!

 ponad 8 miesięcy temu (13 lutego 2019 godz. 20:02)
Czytelnik napisał:

No właśnie z kompresją algorytmem NRV2E mam problem.

 ponad 8 miesięcy temu (13 lutego 2019 godz. 20:24)
Dawid (haker.info) napisał:

Włączyłem wyszukiwanie w źródłach biblioteki UCL i oprócz dekompresji są tam też funkcje kompresujące np. ucl_nrv2e_99_compress (nazwa pliku n2_99.ch. Trzeba eksperymentować. Ja bibliotekę UCL używałem tylko na potrzeby tego artykułu. I to tylko funkcję dekompresującą.

 ponad 8 miesięcy temu (13 lutego 2019 godz. 21:03)
Krzysiek napisał:

Wszystko super opisane. Mam pytanie dotyczące implementacji w C++.
Jak rozwiązać problem polskich znaków.
Zamiast litery Ł otrzymuje dwuznak A? (ten znak zapytania jest w takim jakby kwadracie).
Czy mógłbyś mi napisać w jaki sposób wyświetlić te polskie znaki?

 ponad 2 miesiące temu (26 lipca 2019 godz. 13:42)
Dawid (haker.info) napisał:

Myślę, że w napisie (zmiennej) znaki są poprawne, a jedynie konsola tekstowa Windows je źle wyświetla.
Dodaj na początku funkcji głównej main wywołanie: setlocale(LC_ALL, "Polish");
Powinno to wyglądać:

//(...)
int main(int argc, char *argv[])
{
    setlocale(LC_ALL, "Polish");
    //(...)

Dziękuję za zainteresowanie artykułem 😃

 ponad 2 miesiące temu (26 lipca 2019 godz. 23:05)
Krzysiek napisał:

Po dodaniu setlocale(LC_ALL, "Polish"); litera Ó wyświetla się poprawnie ale litera Ł w dalszym ciągu wyświetla się błędnie (nawet przy zapisie do pliku).
Czy dysponujesz może jakimś opisem algorytmu NRV2E?

 ponad 2 miesiące temu (27 lipca 2019 godz. 13:15)
Dawid (haker.info) napisał:

Uruchomiłem teraz jeszcze raz kod źródłowy w C# i tam polskie znaki diakrytyczne wyświetlają się poprawnie. Coś widocznie jest nie tak z kodowaniem znaków w przykładzie dla C++. Nie znalazłem dokumentów, które by opisywały algorytm NRV2E. Przeglądałem tylko jego oryginalną implementację jako kod z biblioteki UCL.

 ponad 2 miesiące temu (28 lipca 2019 godz. 01:20)
Jacek napisał:

"Aby zeskanować kod Aztec 2D z polskiego dowodu rejestracyjnego pojazdu można użyć gotowych skanerów kodów Aztec/2D. Po zeskanowaniu otrzymamy ciąg znaków."

znasz jakąś bibliotekę która pozwala np w xamarin zeskanować taki kod ? Oczywiście nie pytam o bibliotekę od peloka.

 ponad 3 tygodnie temu (18 września 2019 godz. 14:53)
Dawid (haker.info) napisał:

Jest dużo profesjonalnych bibliotek, ale płatnych. Warto pomyśleć czy nie kupić profesjonalnego SDK, który odczyta kod obrazkowy (zaoszczędzi to czas na własne rozpoznawanie obrazów), a resztą aplikacji zająć się samodzielnie skoro algorytm jest już jawny.

Można też spróbować skorzystać z darmowej biblioteki ZXing:
https://github.com/micjahn/ZXing.Net

 ponad 3 tygodnie temu (19 września 2019 godz. 19:48)


Wszystkie treści umieszczone na tej witrynie są chronione prawem autorskim. Surowo zabronione jest kopiowanie i rozpowszechnianie zawartości tej witryny bez zgody autora. Wszelkie opublikowane tutaj treści (w tym kody źródłowe i inne) służą wyłącznie celom informacyjnym oraz edukacyjnym. Właściciele tej witryny nie ponoszą odpowiedzialności za ewentualne niezgodne z prawem wykorzystanie zasobów dostępnych w witrynie. Użytkownik tej witryny oświadcza, że z zamieszczonych tutaj danych korzysta na własną odpowiedzialność. Wszelkie znaki towarowe i nazwy zastrzeżone zostały użyte jedynie w celach informacyjnych i należą wyłącznie do ich prawnych właścicieli. Korzystając z zasobów witryny haker.info oświadczasz, że akceptujesz powyższe warunki.