تخطَّ إلى المقال

الطباعة والخطوط نُشِر زمن القراءة 11 د

العربية بلا ارتفاع إكس: قياس size-adjust من مخطّطات المحارف

في CSS رافعة واحدة صادقة تجعل خطًّا عربيًّا يجلس بالحجم البصري نفسه الذي يجلس به خطٌّ لاتيني، وهي تطلب رقمًا لن تحسبه لك أي أداة في المنصّة. هنا من أين جاء ذلك الرقم في هذا الموقع، ولماذا تبيّن أن قيمة واحدة لا تكفي، ولماذا تعجز الخاصيّة المصنوعة لهذه المهمّة عن أدائها.

باختصار

يبدو النص العربي أصغر من اللاتيني عند font-size نفسه لأن font-size يضبط صندوق الـ em، والكتابتان تملآن ذلك الصندوق على نحوين مختلفين: يرسم Quicksand حرف x الصغير عند 0.516 من صندوقه، بينما يرسم Zain سنّته — وهي الارتفاع الذي يقرأ عنده القارئ العربي النثر — عند 0.4475. ولـ CSS رافعة واحدة صادقة لهذا الأمر، هي واصف size-adjust داخل @font-face، وهو يطلب رقمًا عليك أن تقيسه بنفسك من مخطّطات المحارف (outlines). في هذا الموقع أعطى ذلك القياس رقمين لا رقمًا واحدًا: 112 بالمئة لعائلة النثر، مطابَقةً عند ارتفاع السنّة، و103 بالمئة لعائلة العناوين (display)، مطابَقةً عند ارتفاع الألف. ونسبة السنّة إلى الألف تخصّ كل تصميم على حدة لا الكتابة العربية نفسها، فقيمة واحدة مشتركة كانت ستترك أحد الدورين خارج الهدف بما بين 7.8 و9.0 بالمئة.

ما تخرج به

  1. لا ارتفاع إكس في العربية، لكن فيها مقاييس قابلة للقياس: السنّة للنثر والألف للعناوين، وكلاهما يُقرأ كنسبة من صندوق em الخاصّ بالخط نفسه.
  2. في Cairo رُسمت السنّة والحلقة والعين وحرف x اللاتيني الصغير كلّها عند 0.5000em بالضبط — المصمّم عامل السنّة أصلًا معاملة ارتفاع الإكس، والمخطّطات تثبت ذلك.
  3. نسبة السنّة إلى الألف تخصّ التصميم لا الكتابة، ولهذا احتاج استبدال خطٍّ عربي بآخر إلى قيمتَي size-adjust اثنتين، 112 بالمئة و103.
  4. لا تستطيع font-size-adjust أداء هذه المهمّة: كل مقياس تعرضه معرَّف على محرف لاتيني أو صيني-ياباني-كوري، والمواصفة تجعلها تُلغي واصف size-adjust، وحقل OS/2 الذي تقرؤه يخطئ بنسبة 30.4 بالمئة في الخط المنشور.
  5. size-adjust يضبط الصعود والنزول أيضًا، فيحرّك صناديق سطورك — وأي تجاوز (override) يُكتب بجانبه كنسبة حبر خام يتخطّى الهدف بمقدار التعديل نفسه بالضبط.
  6. مطابقة السنّة مطابقة رأسية لا أكثر: بالقياس مع التشكيل السياقي، يأتي الخط الجديد أعرض بنحو 25 بالمئة، فتلتفّ الفقرات أبكر وترتفع أكثر.

src/styles/tokens/type.css:43

لماذا يبدو النص العربي أصغر من الإنجليزي عند font-size نفسه؟

font-size لا يضبط حجم أي شيء يراه القارئ. إنه يضبط صندوق الـ em، أما مقدار ما يملؤه الحبر من ذلك الصندوق فقرارٌ يعود إلى مصمّم الخط — ولهذا قد يختلف خطّان عند الحجم المعلن نفسه بعُشر ارتفاعهما الظاهر دون أن يكون في الأمر خطأ في أي موضع. وبين خطٍّ لاتيني غير مذيَّل وخطٍّ عربي، هذه الفجوة هي القاعدة لا الاستثناء.

يضبط هذا الموقع نصّ المتن عند 17px على سطح المكتب العريض، وتأخذ الوثائق العربية 0.9 من ذلك عبر مُعامِل خاصّ بكل لغة، أي 15.3px. وعند هذا الحجم كان الخط الذي نشره الموقع سابقًا، Cairo، يرسم سنّته — وهي العمود القصير الذي تبدأ به الباء، والارتفاع الذي يُقرأ عنده النثر العربي — بحبر مقداره 7.650px. أما Zain، حين وُضع مكانه بلا تعديل، فيرسم الملمح نفسه عند 6.847px. لم يتغيّر أي سطر CSS؛ فقدت الفقرة ببساطة عُشر الحبر الذي يحكم به القارئ على حجمها.

الرافعة التي تقدّمها CSS هي واصف size-adjust داخل @font-face: مُعامِل يُطبَّق على الخط كلّه، مخطّطاتٍ ومقاييسَ معًا، فيمكن جعل خطّين يتّفقان عند font-size معلن واحد. يسجّله MDN بوصفه متاحًا على نطاق واسع منذ سبتمبر 2023، ويضعه caniuse عند 93.58 بالمئة من المستخدمين. وما لا يعطيه لك أيٌّ منهما هو الرقم الذي تضعه فيه.

  • 7.650 px حبر سنّة Cairo الارتفاع المطلوب مطابقته، عند 15.3px
  • 6.847 px سنّة Zain، بلا تعديل حبر أقل من Cairo بنسبة 10.5 بالمئة
  • 7.668 px سنّة Zain عند 112 بالمئة 0.24 بالمئة فوق الهدف
  • 8.263 px حبر ارتفاع إكس Quicksand العمود اللاتيني، عند 17px
ارتفاع الحبر عند أحجام سطح المكتب المنشورة، مشتقًّا من المخطّطات المقيسة ومن قيم الرموز في src/styles/tokens/type.css. حسابٌ لا قراءة من متصفّح.

كل رقم من هذه الأرقام الأربعة حسابٌ على صندوق محيط لمحرف وعلى قيمة رمز، لا لقطة شاشة، وهذا هو بيت القصيد في هذه الطريقة: تعمل قبل أن يُنشَر الخط، وتعمل مرّة أخرى يوم يتغيّر الخط.

المخطّطات نفسها تحدّد ارتفاع السطر الذي تجلس عليه تلك الفقرات، وذلك الرقم مقيسٌ لا مختار — حساب الأرضية الحقيقية.

fontTools 4.60.2 · five faces

ما الذي يحلّ محلّ ارتفاع الإكس حين لا يكون في الكتابة إكس؟

ليس في العربية إكس ولا حروف كبيرة، والمرجع المعياري صريح في رفض استعارة المصطلحين رغم ذلك: لا تستعمل ارتفاع الإكس ولا ارتفاع الحرف الكبير، تكتب عزّة علم الدين، لسبب بسيط هو أنه لا إكس ولا حروف كبيرة في العربية. وما في العربية بدلًا من ذلك سنّة، وهي العمود القصير الذي تبدأ به الباء والتاء؛ وحلقة، وهي بطن الهاء والواو؛ وعين، وهي رأس حرف العين؛ والألف، وهو عمود بسيط يرسم خط الصعود.

قياس هذه الملامح عبر الخطوط العربية الخمسة التي نشرها هذا الموقع أظهر شيئًا لا تسجّله ورقة الأنماط. في Cairo رُسمت السنّة والحلقة والعين وحرف x اللاتيني الصغير كلّها عند الخط نفسه تمامًا — 0.5000em في Regular و0.5010em في SemiBold، بوحدة التصميم. كان مصمّم عائلة ثنائية الكتابة قد كتب المعادلة داخل المخطّطات أصلًا. وليس في CSS أي خاصيّة تستطيع قراءتها منها.

قراءة ارتفاع سنّة من ملفّ خط. السطران الأخيران هما ما يستحقّ تشغيله على أي خط توشك أن تثق به.
from fontTools.ttLib import TTFont
from fontTools.pens.boundsPen import BoundsPen
 
f    = TTFont('zain/regular.woff2')
upm  = f['head'].unitsPerEm       # 800 لـ Zain، و1000 لـ Cairo
gs   = f.getGlyphSet()
name = f.getBestCmap()[0x0628]    # الباء — السنّة
 
pen = BoundsPen(gs)
gs[name].draw(pen)
print(pen.bounds[3] / upm)        # 0.4475
 
# وما لا ينبغي الوثوق به في هذا الخط:
print(f['OS/2'].sxHeight   / upm) # 0.600 — قمّة x المرسومة عند 0.460
print(f['OS/2'].sCapHeight / upm) # 0.874 — قمّة H المرسومة عند 0.695
قيمة ymax للصندوق المحيط بالمحرف كنسبة من صندوق em لكل خط، مقروءة بـ fontTools 4.60.2. صفّ Cairo هو الاكتشاف: السنّة والحلقة والإكس اللاتينية رقم واحد في القطعتين.
الخط tooth ب loop و الإكس اللاتينية
Cairo Regular 0.5000 0.5000 0.5000
Cairo SemiBold 0.5010 0.5010 0.5010
Noto Kufi Bold 0.4950 0.4950 0.5460
Zain Regular 0.4475 0.4713 0.4600
Zain Bold 0.4575 0.4800 0.4700

وNoto Kufi هو المثال المضاد. سنّته عند 0.4950 وإكسه اللاتينية عند 0.5460، أي بفارق 10.3 بالمئة، فكلمة عربية وأخرى لاتينية بجانبها لم تكونا يومًا على خط الوسط نفسه. أما Zain فيقع بين الاثنين: إكسه اللاتينية أعلى من سنّته بـ 2.8 بالمئة، وهو قريب من التناغم بين اللاتيني والعربي الذي يدّعيه ملفّ README الخاص به، وليس هو التطابق التامّ الذي في Cairo.

src/styles/locale/arabic.css:41-56

لماذا يحتاج تصميم عربي واحد إلى قيمتَي size-adjust؟

كان Cairo وNoto Kufi تصميمين اثنين، فكان بإمكان عنوان بارز والفقرة تحته أن يختلفا في الخط. أما Zain فتصميم واحد، وجمع عائلتين في واحدة لا يترك سوى الوزن والمقاييس رافعتين — وهنا تكفّ نسبة السنّة إلى الألف عن كونها تفصيلًا عارضًا وتصير هي القيد.

تلك النسبة خاصّية التصميم لا خاصّية الكتابة. لا يتحرّك Cairo إلا من 0.697 إلى 0.702 بين قطعتيه، أي سبعة أجزاء في الألف، فداخل Cairo كان مُعامِل واحد ليكفي الدورين معًا. أما نسبتا Zain فهما 0.644 و0.658. واستبدال تصميم تختلف نسبته هو ما يفرض قيمتين، ولا قدر من العناية في CSS يستطيع ردّهما إلى واحدة.

السنّة والألف كنسبتين من صندوق em، مسجّلتين في src/styles/locale/arabic.css:41-46 ومُعاد إنتاجهما لهذا المقال بشكل مستقل من ملفات .woff2. كل خانة تتّفق.
الخط tooth ب alef ا السنّة إلى الألف
Cairo Regular 50.000% 71.700% 0.697
Cairo SemiBold 50.100% 71.400% 0.702
Zain Regular 44.750% 69.500% 0.644
Zain Bold 45.750% 69.500% 0.658
Noto Kufi Bold 49.500% 76.000% 0.651

النثر يُقرأ عند ارتفاع السنّة، كما يُقرأ النثر اللاتيني عند ارتفاع الإكس، فتطابق عائلة النثر سنّة Zain بسنّة Cairo: 0.500 مقسومة على 0.4475 تساوي 1.117، والتصريح يقول 112 بالمئة. أما العناوين فتُقرأ عند ارتفاع الألف — فكل كلمة بارزة على هذا الموقع كلمة من أعمدة رأسية — فتطابق عائلة العناوين ألف Zain بألف Cairo SemiBold: 0.714 مقسومة على 0.695 تساوي 1.027، والتصريح يقول 103 بالمئة.

الجوابان يفصل بينهما 8.76 بالمئة، وهذا الفارق هو كل الحجّة. استعمل 112 بالمئة للعناوين أيضًا فتُعرَض كلمة عربية بارزة أطول بنسبة 9.02 بالمئة من ألف Cairo التي طوبقت بها. واستعمل 103 بالمئة للنثر أيضًا فتخرج السنّة أقصر بنسبة 7.82 بالمئة. اختيار قيمة واحدة ليس تبسيطًا؛ إنه اختيار أيّ الدورين نخطئ فيه.

وهناك أيضًا عُرف منافس في المقياس الذي يُطابَق عنده أصلًا. فـ Edo Smitshuijzen، وهو يكتب عن نِسَب الخطوط ثنائية الكتابة، يطابق ارتفاع الإكس اللاتينية بارتفاع الحلقة لا بالسنّة، ويطلب من المصمّم تضييق الفجوة بين الاثنين. في Cairo لا معنى للنقاش، لأن السنّة والحلقة والإكس اللاتينية كلّها 0.500em. أما في Zain فتفترق القاعدتان: السنّة تعطي 112 بالمئة، وحلقة الواو تعطي 106. واختار هذا الموقع فرع السنّة لأن الخط الذي كان يستنسخه قد ساوى بين الثلاثة أصلًا.

استبدال عائلتين عربيّتين بعائلة واحدة غيّر أيضًا ما يحدث للمقاطع اللاتينية داخل صفحة عربية — نطاق unicode-range الذي يمنع خطًّا من التقاط اللاتيني.

OS/2 sxHeight 480 · upm 800

لماذا لا نستعمل font-size-adjust، وهي الخاصيّة المصنوعة لهذا؟

في CSS خاصيّة لهذه المشكلة بالذات. تُسوّي font-size-adjust خطًّا بآخر عبر نسبة مقياس مسمّى، والنصيحة السائدة في كيفية اختيار قيمتها هي أن تضع حرف x صغيرًا في خطّين وتحرّك الرقم حتى يتطابقا. تلك نصيحة لاتينية بالكامل، والخاصيّة عاجزة عن هذه المهمّة لثلاثة أسباب مستقلّة — يكفي أيٌّ منها وحده.

ابدأ بأسهلها ظهورًا. اختيار المقياس هو اختيار الجواب، والخاصيّة لا تتيح أي طريقة لطلب الصفّ الذي يحتاجه خط عربي.

حلقة الهاء 87%
الحرف الكبير H اللاتيني 99%
الألف — المنشورة للعناوين 103%
حلقة الواو والعين 106%
الإكس اللاتينية 109%
السنّة — المنشورة للنثر 112%
طابِق Zain بخط الوسط 0.500em في Cairo Regular على مقياس مختلف تحصل على مُعامِل مختلف. زوج خطوط واحد، وستة مقاييس، وفارق 25 نقطة.الأعمدة مرسومة بمقياس النسبة إلى أكبرها، 112 بالمئة.
لا وجود لمقياس عربي
المقياس الافتراضي هو ex-height، وتعرّف مواصفة CSS Values وحدة ex بأنها ارتفاع الإكس المستعمل لأول خط متاح، وسمّيت كذلك لأنها كثيرًا ما تساوي ارتفاع حرف x الصغير. وتضيف الصيغة ذات القيمتين cap-height وch-width وic-width وic-height. والخمسة جميعًا معرَّفة على محرف مرجعي لاتيني أو صيني-ياباني-كوري، ولا خيار عربي بينها.
الحقل خاطئ في الخط المنشور
يسجّل Zain في OS/2 قيمة sxHeight 480 وقيمة sCapHeight 699 داخل صندوق em من 800 وحدة — أي 0.600em و0.874em — بينما تبلغ قمّة x المرسومة فيه 0.460em وقمّة H المرسومة 0.695em، بفارق 30.4 و25.7 بالمئة. وقراءة from-font على هذا الخط ستعيد 0.600: لا السنّة، ولا الحلقة، ولا حتى ارتفاع الإكس اللاتينية الخاص بالخط نفسه. تبدو الأرقام كأنها كُتبت في صندوق em من 1000 وحدة ولم يُعَد قياسها قط، لكن هذا افتراض عن السبب؛ والمقيس هو التفاوت وحده.
الاثنان لا يتركّبان
تنصّ CSS Fonts 5 على أن font-size-adjust تُطبَّق بعد واصف size-adjust، ونتيجة ذلك أن size-adjust يبدو بلا أي أثر. هما بديلان لا زوج تحسين تدريجي، وورقة أنماط تضبط الاثنين تكون قد ألقت بأحدهما في صمت.
ثلاثة أسباب تجعل font-size-adjust عاجزة عن التعبير عن الأرقام أعلاه.

الدعم أهون المشكلات الثلاث، والمرجعان اللذان تعود إليهما مختلفان. يعطي caniuse لـ font-size-adjust نسبة 88.02 بالمئة عالميًّا مع دعم كامل من Chrome وEdge 127؛ ويفصل MDN بين الصيغتين فيضع صيغة الكلمة المفتاحية للمقياس عند Chrome وEdge 129، وFirefox 129، وSafari 18، ويسجّل الخاصيّة بوصفها Baseline 2024. وقُرئ الاثنان في 26 أغسطس 2026. أما الواصف فمتاح على نطاق واسع منذ ثلاث سنوات.

إذن فالخاصيّة المصمَّمة لمطابقة الأحجام بين الخطوط تعتمد على حقل معرَّف مقابل حرف لا وجود له في العربية، وفي الخط الذي ينشره هذا الموقع فعلًا يخطئ ذلك الحقل بما يقارب الثلث. والمُعامِل الفظّ لكل خط هو الأداة الوحيدة في المنصّة القادرة على حمل قياس ليس لـ CSS مفردات تعبّر عنه.

hhea 1303/-571 against 869/-459

لماذا لا تحمل الخطوط العربية أي ascent-override

يصرّح الجانب اللاتيني من هذا الموقع بـ size-adjust وascent-override وdescent-override على كل خط — ثلاث عائلات اصطناعية فوق أربعة ملفات فعلية، واثنتان من تلك العائلات تشيران إلى ملفّ .woff2 نفسه بينما تعلنان مقاييس مختلفة. أما الورقة العربية فلا تصرّح إلا بـ size-adjust. وهذا هو الموضع الوحيد الذي يختلف فيه الملفّان عن قصد.

التجاوزات اللاتينية موجودة كي لا يعيد تبديل Quicksand تدفّق النص مقابل Arial. والعربية لا تستطيع شراء الشيء نفسه، لأن لا Quicksand ولا Arial يحمل أي عربية أصلًا: فالبديل الحقيقي هو أي خط نسخ تصادف أن توفّره المنصّة، وليس هناك شيء ثابت نثبّت عليه. ثم يقول الحساب إن التجاوزات ما كانت لتشتري شيئًا يُذكر على أي حال.

  • 1.87400 صندوق محتوى Cairo، em صعود 1303، ونزول -571، وupm 1000
  • 1.85920 Zain عند 112 بالمئة، em 869 و-459 في صندوق em من 800 وحدة
  • 0.79% الفجوة بينهما ما كان التجاوز ليصحّحه
صناديق المحتوى من جدول hhea لكل عائلة، مقروءة من ثنائيات الخطوط. العائلتان تضبطان USE_TYPO_METRICS بقيم sTypo مطابقة لقيم hhea، فيقرأ المتصفّح الزوج نفسه في الحالين.

وما كانت التجاوزات ستشتريه — صندوق سطر كانت ارتفاعات الأسطر القائمة مضبوطة عليه أصلًا — يسلّمه size-adjust بنفسه، لأنه يضبط صعود الخط ونزوله مع المخطّطات.

الخطة التي انطلقت منها هذه المرحلة كتبت تلك التجاوزات كنسب حبر خام بجانب size-adjust، وكانت ستخطئ بـ 12 بالمئة بالضبط. تسجّل ورقة الأنماط ذلك الرقم وتعطي له الآلية الخاطئة: تقول إن التجاوز ما كان ليُضبَط، لأن التعديل يُطبَّق أولًا والتجاوز يستبدل النتيجة. وتقول CSS Fonts 5 العكس — كل المقاييس المرتبطة بهذا الخط، بما فيها التجاوزات التي توفّرها واصفات @font-face، تُضرب بالنسبة المعطاة.

والـ 12 بالمئة تنجو من التصحيح، لسبب معكوس. فـ ascent-override بنسبة 130.3 بالمئة وdescent-override بنسبة 57.1 بالمئة، وهما صندوق Cairo مذكورًا كحبر، مضروبين في size-adjust بنسبة 112 بالمئة، يعطيان 2.0989em حيث كان المقصود 1.874em. طريقان مستقلّان يبلغان الرقم نفسه، فالقرار كان صحيحًا في الحالين — لكن السبب الذي سيأخذه قارئ لاحق من ذلك الملف لن يكون صحيحًا.

GSUB advances · four strings

هل تُبقي مطابقة السنّة الفقرات ملتفّة حيث كانت تلتفّ؟

تقول ورقة الأنماط إنها تفعل، وتقولها وثيقة التسليم مرّتين: كل فقرة عربية تحتفظ بعرض السطر ونقاط الالتفاف وعدد الأسطر التي لديها اليوم. وإعادة القياس لهذا المقال تشير إلى العكس، والادّعاء لا يسنده أي شيء مسجّل في المستودع.

مطابقة سنّة بسنّة عملية رأسية. أما العرض المضبوط فكمّية منفصلة لا تتحكّم فيها أي مطابقة رأسية، وZain تصميم أعرض من Cairo. وحلّ الأشكال السياقية عبر بدائل GSUB المفردة الأوّلية والوسطية والنهائية والمنفصلة في كل خط — 91 استبدالًا في الفقرة الاختبارية، وصفر محارف مفقودة في أيٍّ من الخطّين — ثم جمع التقدّمات يعطي الأرقام أدناه.

عروض التقدّم بعد التشكيل، بوحدة em، لأربع سلاسل ينشرها هذا الموقع. بدائل مفردة فقط: لا بحث عن رِباطات، ولا ضبط للتقارب (kerning)، ولا وضع للعلامات، ولا HarfBuzz، فهذه مجاميع تقدّم سياقية لا أطوال أسطر نهائية معروضة.
السلسلة Cairo Regular Zain عند 112% مقابل Cairo
فقرة متن المكدّس، 143 محرفًا 63.3040 79.0832 +24.93%
عنوان بطاقة الرحلة 04، 38 محرفًا 16.6920 20.8656 +25.00%
عنوان المشاريع، 13 محرفًا 5.8590 7.3878 +26.09%
عنوان البرمجة والتصميم، 14 محرفًا 6.0130 7.4872 +24.52%

تطول الأسطر، وتتحرّك نقاط الالتفاف، وترتفع الفقرات. القرار البصري ينجو من التصحيح — فمطابقة السنّة تبقى الطريقة الصحيحة لضبط حجم النثر العربي مقابل خط لاتيني — أما النتيجة التي ادُّعيت له فلا تنجو، وإعادة بناء تضع ميزانية تخطيطها العربي على نقاط التفاف لم تتغيّر كانت ستُفاجأ متأخّرة.

وللقياس حدود يجب ذكرها بصدق. الفقرة الاختبارية لا تحوي أي زوج لام-ألف، وهذا يزيل أكبر تشويش تسبّبه الرِّباطات في تلك السلسلة الواحدة؛ ولم يُعرَض شيء هنا بمتصفّح. هو fontTools يقرأ جداول الاستبدال الخاصة بكل خط، وهذا يكفي لتحديد الاتجاه والمقدار التقريبي، ولا يكفي للتنبّؤ بمكان انكسار سطر بعينه.

السطر دالّة على مقاييس خطٍّ محلولة، ولهذا أيضًا لا يستطيع مُقسِّم الحركة أن يعمل قبل وصول الخطوط — تقسيم النص في كتابة متّصلة.

src/styles/locale/arabic.css:286-326

ما الذي يجرّه size-adjust خلفه

مُعامِل المقاييس ليس محليًّا أبدًا. ولأن size-adjust يضبط صعود الخط ونزوله مع المخطّطات، يصير صندوق محتوى Zain عند 112 بالمئة 1.859em — فأي عنصر مضبوط على line-height بقيمة 1 يكون نصف فراغه السطري سالبًا، ويتدلّى حبره خارج صندوق سطره في الاتجاهين.

وحركة الالتفاف في أزرار هذا الموقع تقصّ نصّها إلى نافذة مقطوعة عند 94 بالمئة من ذلك الصندوق، وهي مريحة لـ Quicksand وغير مريحة لـ Zain. وبالقياس على مخطّطات Zain Bold، تنزل الياء النهائية إلى -0.46625em والعين إلى -0.41380em؛ وعند 112 بالمئة يصير ذلك -0.5222em و-0.4635em تحت خط الأساس في صندوق مقداره 1em. أخذت النافذة نقاط الكلمة من أسفلها، فعرضت النسخة الذهبية رابط التواصل العربي بحرف مختلف — خطأ إملائي في كلمات صاحب الموقع نفسه، في نصّ رابط، في الإنتاج.

ثلاثة أرقام يجب أن تتحرّك معًا، تحت ثابت واحد: التوأم تحت النافذة، والنافذة تحت الحبر.
/* src/styles/locale/arabic.css، مختصرًا */
html[lang='ar'] :is(.btn-text, .text-eyebrow) {
  --ar-label-lh: 1.55;   /* كانت 1، و1 كانت تقصّ */
  --text-offset: 2em;    /* حيث يبدأ حبر التوأم */
}
 
html[lang='ar'] .text-clip-w {
  /* اللاتيني يُقطَع عند 94%؛ نزول Zain يحتاج 114% */
  clip-path: polygon(0 -2%, 0 114%, 100% 114%, 100% -2%);
}
 
/* النافذة عند 114% من صندوق 1.55em تبلغ 1.767em،
   ويبدأ حبر التوأم عند 1.845em. */

والمنظومة الاختبارية في المتصفّح التي تحرس المسارات العربية لا تستطيع رؤية هذا الصنف من العيوب. هي تقارن الصناديق المحيطة لعناصر أسطر متتالية عند أربعة عروض شاشة، وسطران عربيان يصطدمان حبرًا بحبر لا صندوقًا بصندوق — فاشتقاق fontTools هو الاختبار الأقوى والمنظومة هي الأضعف. وقول ذلك على النحو المعكوس يقلب كل الغاية من قياس المخطّطات.

والنزول المقصوص واحد من العيوب التي لا تبلغها الخصائص المنطقية وحدها — سبعة عيوب في الاتجاه من اليمين إلى اليسار.

@layer utilities · order.json:88

كيف تشتقّ قيمة size-adjust تستطيع الدفاع عنها

لا ينتقل أي رقم من الأرقام أعلاه. هي Zain مقابل Cairo، عند أحجام موقع واحد، لدورين يصادف أن ذلك الموقع لديه. الذي ينتقل هو الطريقة، وطولها سبع خطوات — الثلاث الأولى هي هذا المقال، والأربع الأخيرة كلّفت هذا المستودع عيبًا لكلٍّ منها.

  1. سمِّ الدور ومحرفه قرِّر بصوت مسموع عند أي ملمح يُقرأ كل دور: النثر عند السنّة، والعناوين عند الألف. واكتب السبب داخل ورقة الأنماط، فهو الجزء الوحيد من هذا كلّه الذي لا يستطيع قارئ لاحق استرجاعه من التصريحات نفسها.
  2. قِس ymax، لا جدول OS/2 اقرأ الصندوق المحيط بالمحرف في الخط الخارج وفي الخط الداخل، واقسم كلًّا منهما على unitsPerEm الخاصّ به. لا تفترض 1000 أبدًا: صندوق em في Zain 800 وحدة، وكل نسبة هنا تخطئ بالربع إن قسمت على الرقم الخاطئ.
  3. اقسم ارتفاع الخط الخارج على ارتفاع الداخل ذلك الخارج هو قيمة size-adjust. 0.500 على 0.4475 تساوي 1.117، وتُنشَر 112 بالمئة. ولا تقرّب خارجًا مقيسًا إلى رقم أكثر استدارة قبل أن تحسب ما تكلّفه تلك التقريبة من حبر.
  4. عائلة اصطناعية واحدة لكل دور صرّح بعائلة منفصلة لكل قيمة size-adjust، حتى حين تشير اثنتان منها إلى الملف نفسه. عائلتان فوق ملفّ .woff2 واحد ليستا تكرارًا — الواصفات هي ما يجعل صندوق السطر بالارتفاع الذي هو عليه، ودمجهما يغيّر أحد الدورين في صمت.
  5. اترك التجاوزات خارجًا، أو اذكرها بمقاييس الخط الداخل أي ascent-override يُكتب كنسبة الحبر التي تريدها يُضرب في size-adjust، فيتخطّى الهدف بذلك المُعامِل بالضبط. وإن كان الخط الداخل يقع أصلًا قريبًا من صندوق الخط الخارج بعد التعديل، فالتصرّف الصادق ألّا تصرّح بأي تجاوز إطلاقًا.
  6. أعِد اشتقاق كل ارتفاع سطر بعدها التعديل حرّك الصعود والنزول، فتحرّكت معه كل أرضية ضُبط الحرف عليها. وكل ما يقصّ أو يُقنّع أو يثبّت ارتفاعًا يجب أن يُعاد قياسه في المرور نفسه، وإلا انتهى نزولٌ خارج نافذته.
  7. ضع ورقة اللغة في طبقة تتالٍ متأخّرة الطبقة تغلب الخصوصية، فلا تستطيع ورقة أنماط قسمٍ أن تتفوّق على قاعدة لغة مهما ضاق نطاقها. ونشر الاستثناء في ملف القسم بدلًا من ذلك هو كيف عرض هذا الموقع كتلة عنوان عند 130.5 بكسل حيث كان المقصود 112.5.
الإجراء، بترتيب وقوع الأخطاء.

وحدود الطريقة تستحقّ أن تُذكر بالوضوح نفسه الذي تُذكر به الطريقة. هي تصلح الحجم الرأسي ولا شيء غيره: العرض المضبوط يتحرّك، ونقاط الالتفاف تتحرّك، ولا خاصيّة CSS واحدة تمسك الاثنين معًا. وهي ثابت لكل خط على حدة، فتُعاد عملية الاشتقاق كلّها يوم يتغيّر الخط — وهو ما حدث في هذا الموقع فعلًا.

الخطوة السادسة قياس قائم بذاته، وأرضيات هذا الموقع منشورة مع حسابها — أرضية ارتفاع السطر العربي.

وإن كنت تخطّط لبناء بأكثر من كتابة وتفضّل أن تُحسم الطباعة قبل التخطيط، ابدأ محادثة.

أسئلة

هل النص العربي أصغر فعلًا من اللاتيني عند font-size نفسه، أم أنه يبدو كذلك فحسب؟

هو أصغر بالقياس في الجزء من الحرف الذي تحكم به العين على الحجم. font-size يضبط صندوق الـ em، والكتابتان تملآنه على نحوين مختلفين. وعند حجم متن هذا الموقع يرسم Quicksand حرف x الصغير عند 0.516 من صندوقه، بينما يرسم Zain سنّته عند 0.4475. أي حبر أقل بنحو ثمانية بالمئة عند الحجم المعلن نفسه، قبل أي متغيّر آخر. ليس وهمًا وليس خللًا في العرض؛ هو مصمّما خطٍّ اتّخذا قرارين مختلفين داخل الصندوق نفسه.

لماذا لا نرفع حجم الخط العربي 10 بالمئة وننتهي من الأمر؟

لأن font-size يحمل التخطيط معه، وsize-adjust لا يفعل. كل رمز من رموز الحرف الأحد عشر هنا هو clamp() مضروبة في مُعامِل خاص بكل لغة، فتحريك ذلك المُعامِل يحرّك الأحد عشر دفعةً واحدة ويُبطل كل قياس عربي مسجّل في المستودع. أما size-adjust فنطاقه @font-face واحد، وهذا ما يتيح لعائلة النثر أن تأخذ 112 بالمئة ولعائلة العناوين 103 عند الحجم المعلن نفسه. ورفع font-size لا يستطيع التعبير عن هذا التمييز أصلًا.

هل أستطيع استعمال font-size-adjust بدلًا من ذلك؟

ليس للعربية. مقياسها الافتراضي هو ex-height، وكلٌّ من CSS وOpenType يعرّف ارتفاع الإكس مقابل حرف x اللاتيني الصغير، وهو حرف لا وجود له في العربية؛ والصيغة ذات القيمتين تضيف cap-height وch-width وic-width وic-height، وكلّها محارف مرجعية لاتينية أو صينية-يابانية-كورية. وفي الخط الذي ينشره هذا الموقع يخطئ الحقل الأساسي على أي حال: يسجّل Zain في OS/2 قيمة sxHeight 480 داخل صندوق em من 800 وحدة، أي 0.600em، بينما تبلغ قمّة x المرسومة فيه 0.460em. كما تجعل المواصفة الاثنين متنافيين، لأن font-size-adjust تُطبَّق بعد واصف size-adjust.

هل أحتاج ascent-override وdescent-override أيضًا؟

فقط إن كان لديك خط بديل ثابت يستحقّ المطابقة. الخطوط اللاتينية هنا تحملهما كي لا يعيد تبديل خط الويب تدفّق النص مقابل Arial. والعربية لا بديل ثابت لها، لأن لا Quicksand ولا Arial يحمل أي عربية، فالبديل الحقيقي هو ما توفّره المنصّة. وإن أضفتهما، فلا تذكرهما كنسب حبر خام بجانب size-adjust: المواصفة تضبط تجاوزات @font-face مع كل شيء آخر، وهنا كان ذلك الخطأ سيخرج أطول بـ 12 بالمئة.

المصادر

مقيس في هذا المستودع

  • src/styles/locale/arabic.css جدول السنّة والألف للقطع الخمس كلّها، واشتقاق 112 و103 بالمئة، والعائلتان الاصطناعيتان، وإصلاح نافذة القصّ.
  • src/assets/fonts/zain/ القطع الثلاث المنشورة التي أُخذ منها كل قياس مخطّطات في هذا المقال، مع رخصة SIL Open Font Licence الخاصة بها.
  • src/styles/base/fonts.css الجانب اللاتيني من الجهاز نفسه: ثلاث عائلات اصطناعية فوق أربعة ملفات فعلية، لكلٍّ منها قيمة size-adjust خاصّة بها وتجاوزات مقاييس.
  • src/styles/tokens/type.css سلّم الحرف ذو الأحد عشر رمزًا، والمُعامِلان البصريان لكل لغة اللذان يمرّ بهما كل حجم عربي.

مُراجَع مقابل

Alaa Abbod

بقلم

علاء عبّود

مطوّر إبداعي — هيرنه، ألمانيا

مصمّم ومطوّر يبني مواقع ويب سهلة الوصول وتطبيقات جوال ومتاجر إلكترونية وهويات بصرية كعملٍ واحد، وبيديه. هذا الموقع منشور بالإنجليزية والألمانية والعربية من مصدر واحد، ومن هناك جاء معظم هذه الأسئلة.

Alaa Abbod

أتختار خطًّا لكتابتين في وقت واحد؟

المقاييس تقرّر أكثر من المظهر: هي التي تحدّد صناديق الأسطر ونوافذ القصّ ونقاط الالتفاف، وحسمها أرخص ما يكون قبل أن يُبنى تخطيط فوقها. وإن كان لديك بناء ثنائي اللغة قادم، فتلك محادثة قصيرة تستحقّ أن تُجرى مبكرًا.

من فضلك أدر جهازك،
هذا الموقع مبني عموديًا.