في الآونة الأخيرة، واجهت مشكلة مع تثبيت Fedora Linux الخاص بي والذي يعرض الرموز التعبيرية في واجهة مستخدم نظام التشغيل والمتصفحات. قادتني هذه المشكلة إلى التحقيق قليلاً حول مشروع تكوين الخطوط، ولكن لاختبار التكوينات والخطوط الخاصة بي، كنت بحاجة إلى إنتاج رموز تعبيرية من جميع إصدارات Unicode، مما دفعني في النهاية إلى كتابة "برنامج نصي" لـ Golang لطباعة جميع الرموز التعبيرية وبعضها معلومات عن دواخلهم.
طوال هذه الرحلة، تعمقت في التفاصيل الداخلية للرموز التعبيرية، وتمثيلاتها الثنائية، وبعض القرارات الغريبة/اللطيفة التي اتخذتها معايير Unicode فيما يتعلق بالرموز التعبيرية.
ولكن أولاً، دعونا نرجع خطوة سريعة إلى الوراء ونلخص بعض المسرد.
يمكننا وصف التشفير بأنه "تعيين" أو "ترجمة" بين حرف من اللغة والتمثيل الثنائي لهذا الحرف. على سبيل المثال، يقوم ترميز ASCII التقليدي بتعيين الحرف a إلى 0x61 سداسي عشري (0b01100001 ثنائي). ومن أمثلة الترميزات صفحات التعليمات البرمجية ذات 8 بت لـ Microsoft (Windows 125x) أو ISO (ISO/IEC 8859).
في صفحات الرموز الثابتة هذه ذات 8 بت، الحد الأدنى "لمقدار" المعلومات المستخدمة هو 8 بت (1 بايت)، مما يعني أنها يمكن أن تحتوي على 256 حرفًا/حرفًا مختلفًا. تم إنشاء صفحات رموز مختلفة عن طريق إعادة استخدام 256 رمزًا ثنائيًا لدعم العديد من اللغات. لذلك، فإن وجود ملف نصي مكتوب عليه هذه البايتات الثلاثة [0xD0، 0xE5، 0xF2] يُقرأ كـ "Πες" باستخدام ISO 8859-7 اليوناني، أو "Ðåò" باستخدام ISO 8859-7 الغربي (نفس البايتات، يتم تفسيرها بشكل مختلف بناءً على صفحة الرموز).
في مرحلة ما، لم يكن وجود العديد من صفحات الأكواد المختلفة يتسع بشكل جيد مع تقدم التكنولوجيا. لذلك، كنا بحاجة إلى شيء يمكن أن يناسب جميع اللغات (وأكثر) ويكون موحدًا عبر الأنظمة.
[تقدم سريعًا، مع ترك الكثير من التاريخ والمعايير حتى الوقت الحاضر]
تم تصميم معيار Unicode لدعم كافة أنظمة الكتابة في العالم التي يمكن رقمنتها. لذلك، باستخدام المثال أعلاه، في معايير Unicode، يحتوي الحرف اليوناني "Π" على الرمز 0x03A0 بينما يحتوي الحرف اللاتيني الكبير eth "Ð" على الرمز 0x00D0 ولم يعد يتعارض. لدى Unicode Standard إصدارات، وفي وقت كتابة هذا التقرير، كان الإصدار الأحدث هو 16.0 (المواصفات).
ولكن انتظر لحظة، ما هي "نقطة الرمز" هذه؟
في معيار Unicode، كل "حرف" وحرف تحكم ورمز تعبيري وكل عنصر محدد بشكل عام له قيمة ثنائية فريدة تسمى "نقطة الرمز". يحدد المعيار جميع نقاط الرمز، وتحتوي كل نقطة رمز على معلومات رمزية/ثنائية خالصة. عادةً ما تتم كتابة التنسيق السداسي العشري لكل نقطة رمز ببادئة U. على سبيل المثال، نقطة ترميز الحرف اليوناني الصغير أوميغا (ω) هي U 03C9.
إذن من الذي نقوم بتشفير نقاط الكود هذه بالفعل؟
الجزء الأول من تشفير نقاط الكود إلى بايت هو Encoding Fomrs. وفقا للمعيار:
تحدد نماذج الترميز كيفية التعبير عن كل عدد صحيح (نقطة رمز) لحرف Unicode كتسلسل لواحدة أو أكثر من وحدات التعليمات البرمجية.
تستخدم نماذج الترميز مصطلح "وحدة الكود" للإشارة إلى أصغر وحدة من البيانات المستخدمة لتمثيل نقطة ترميز Unicode ضمن ترميز معين.
يحدد معيار Unicode ثلاثة نماذج تشفير مختلفة:
وهذا يعني أن نقطة رمز واحدة أو سلسلة من نقاط الرمز قد يتم تشفيرها بشكل مختلف اعتمادًا على نموذج التشفير المستخدم.
الطبقة التي تهتم بالتسلسل الثنائي الفعلي في Unicode تسمى أنظمة التشفير وتعتني بجميع التفاصيل ذات المستوى المنخفض (مثل endianness). الجدول 2-4 من مواصفات Unicode:
|Encoding Scheme| Endian Order | BOM Allowed? | | ------------- | ----------------------------| ------------ | | UTF-8 | N/A | yes | | UTF-16 | Big-endian or little-endian | yes | | UTF-16BE | Big-endian | no | | UTF-16LE | Little-endian | no | | UTF-32 | Big-endian or little-endian | yes | | UTF-32BE | Big-endian | no | | UTF-32LE | Little-endian | no |
ملاحظة: تستخدم جميع لغات البرمجة وأنظمة التشغيل وأنظمة الملفات الحديثة تقريبًا Unicode (مع أحد أنظمة التشفير الخاصة بها) باعتباره الترميز الأصلي لها. تستخدم Java و.NET UTF-16، بينما يستخدم Golang UTF-8 كتشفر سلسلة داخلية (وهذا يعني أنه عندما نقوم بإنشاء أي سلسلة في الذاكرة، يتم ترميزها في Unicode بنموذج التشفير المذكور)
يحدد معيار Unicode أيضًا نقاط التعليمات البرمجية للرموز التعبيرية (الكثير منها)، و(بعد بعض الخلط مع رقم الإصدار)، يتقدم إصدار Emoji "المعياري" بالتوازي مع معيار Unicode. في وقت كتابة هذا التقرير، كان لدينا الرموز التعبيرية "16.0" ومعيار Unicode "16.0".
أمثلة:
⛄ رجل ثلج بلا ثلج (U 26C4)
؟ وجه مبتسم بعيون مبتسمة وثلاثة قلوب (U 1F970)
يحدد Unicode المعدلات التي يمكن أن تتبع نقطة الرمز الأساسية للرموز التعبيرية، مثل الاختلاف ولون البشرة (لن نستكشف جزء الاختلاف).
لدينا ستة معدلات للون البشرة (تتبع مقياس فيتزباتريك) تسمى EMOJI MODIFIER FITZPATRICK TYPE-X (حيث x من 1 إلى 6)، وهي تؤثر على جميع الرموز التعبيرية البشرية.
لون بشرة فاتح (نوع فيتزباتريك-1-2) (U 1F3FB)
لون بشرة فاتح ومتوسط (نوع فيتزباتريك -3) (U 1F3FC)
لون بشرة متوسط (نوع فيتزباتريك -4) (U 1F3FD)
لون بشرة متوسط-داكن (نوع فيتزباتريك -5) (U 1F3FE)
لون البشرة الداكن (نوع فيتزباتريك -6) (U 1F3FF)
إذن، على سبيل المثال، مثل جميع الرموز التعبيرية البشرية، فإن الرموز التعبيرية للأطفال؟ (U 1F476)، عندما لا يتبعه مُعدِّل للجلد، يظهر باللون الأصفر المحايد. في المقابل، عندما يتبعه معدّل لون البشرة، فإنه يتغير وفقًا لذلك.
؟ ش 1F476
؟؟ يو 1F476 يو 1F3FF
؟؟ يو 1F476 يو 1F3FE
؟؟ يو 1F476 يو 1F3FD
؟؟ يو 1F476 يو 1F3FC
؟؟ يو 1F476 يو 1F3FB
القرار الأكثر غرابة ولكن اللطيف لمعيار Emoji/Unicode هو أنه تم تعريف بعض الرموز التعبيرية من خلال ضم الآخرين معًا باستخدام Zero Width Joiner بدون نقطة رمز مستقلة.
لذلك، على سبيل المثال، عندما ندمج:
العلم الأبيض ️ (U 1F3F3 U FE0F)
نجار ذو عرض صفر (U 200D)
قوس قزح ؟ (ش 1F308)
يظهر كعلم قوس قزح ⁉️؟ (يو 1F3F3 يو FE0F يو 200 دي يو 1F308)
أو، ؟؟ ؟ => ???
أو حتى،؟؟ ❤️ ؟ ؟؟ => ??❤️???
يشبه الأمر ضغط الرموز التعبيرية معًا، ثم يظهر رمز تعبيري جديد. كم هو لطيف؟
أردت إنشاء جدول Markdown بجميع الرموز التعبيرية، وجداول تسلسل الرموز التعبيرية Unicode هي مصدر الحقيقة لذلك.
https://unicode.org/Public/emoji/16.0/emoji-sequences.txt
https://unicode.org/Public/emoji/16.0/emoji-zwj-sequences.txt
لذلك قمت بإنشاء محلل Golang (هنا) الذي يجلب ملفات التسلسل هذه ويوزعها، ويولد كل رمز تعبيري عندما يتم وصف نطاق في ملف التسلسل، ويطبع جدول تخفيض السعر مع بعض المعلومات الداخلية لكل واحد (مثل ]الأجزاء في حالة انضمامها، أو القاعدة لون البشرة، وما إلى ذلك).
يمكنك العثور على جدول تخفيض السعر هنا.
يوجد العمود الأخير في هذا الجدول بهذا التنسيق
str := "⌚" len([]rune(str)) // 1 len([]byte(str)) // 3
كما ناقشنا، ترميز سلسلة Golang الداخلية هو UTF-8، مما يعني أنه، على سبيل المثال، بالنسبة للرموز التعبيرية على مدار الساعة ⌚ طول البايت هو 3 (لأن UTF-8 ينتج 3 بايت "لكتابة" نقطة الرمز هذه)، وطول نقطة الكود هو 1.
رون جولانج == نقطة ترميز Unicode
ولكن في حالة الرموز التعبيرية المنضمة - حتى لو "ظهرت" كرمز واحد - فلدينا العديد من نقاط الرمز (الرونية) والمزيد من وحدات البايت.
str := "??❤️???" len([]rune(str)) // 10 len([]byte(str)) // 35
والسبب هو:
??❤️??? : ?? ZWJ ❤️ ZWJ ? ZWJ ?? ?? : 1F469 1F3FC // ? skin tone modifier [2 code points] ZWJ : 200D // [1 code points] * 3 ❤️ : 2764 FE0F // ❤ VS16 for emoji-style [2 code points] ? : 1F48B // [1 code point] ?? : 1F468 1F3FE // ? skin tone modifier [2 code points]
؟
من الجدير بالذكر أن الطريقة التي نرى بها الرموز التعبيرية تعتمد على خط نظامنا وإصدارات الرموز التعبيرية التي يدعمها هذا الخط.
لا أعرف التفاصيل الداخلية الدقيقة لعرض الخطوط وكيف يمكن عرض الخطوط المرتبطة بشكل صحيح. ربما سيكون منشورا في المستقبل.
حتى ذلك الحين، هتاف؟
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3