Skip to content

feat(text): Add Unicode paragraph base direction detection - #3291

Open
OmarAglan wants to merge 4 commits into
TheSuperHackers:mainfrom
OmarAglan:feature/unicode-paragraph-direction
Open

OmarAglan wants to merge 4 commits into
TheSuperHackers:mainfrom
OmarAglan:feature/unicode-paragraph-direction

Conversation

@OmarAglan

@OmarAglan OmarAglan commented Sep 14, 2026 •

Copy link
Copy Markdown

Split and replace #3231.

Adds a portable helper that determines a paragraph initial direction from its first strong character, following UAX #9 rules P2/P3.

The helper supplies the initial paragraph level for Uniscribe itemization in #3292, which #3293 integrates into single-line UI text. For example, a paragraph beginning with Arabic or Hebrew selects RTL, while one beginning with Latin selects LTR. Leading numbers and punctuation do not determine the direction.

The helper:

  • Returns level 0 for LTR or 1 for RTL, defaulting to LTR when no strong character is found.
  • Skips nested directional isolate contents and stops at the first paragraph boundary.
  • Decodes UTF-16 surrogate pairs and supports scalar values on platforms with 32-bit wchar_t.
  • Ignores malformed surrogate sequences and invalid scalar values.

Both BMP and supplementary characters use generated Unicode 17.0.0 data. This removes the Windows API dependency and enables building the helper on every platform. The generator validates the source version and checksum; the Unicode license is retained alongside the generated table.

Usp10 remains required by the renderer in #3292 for bidirectional analysis, shaping, measurement, and visual ordering. This helper only supplies the initial paragraph level.

Validation

  • VC6 Release builds of Generals and Zero Hour passed.
  • 30 paragraph-direction regression cases passed with both 16-bit and 32-bit wchar_t.
  • All 1,112,064 Unicode scalar values checked against the pinned Unicode data with LTR and RTL suffixes on both character widths.
  • Linux target compilation of the helper passed.
  • Table regeneration check and git diff --check passed.

Implementation and local verification developed with AI assistance.

@OmarAglan
OmarAglan marked this pull request as ready for review September 14, 2026 10:04
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add Unicode-aware paragraph direction detection

✨ Enhancement 🕐 20-40 Minutes

Grey Divider

AI Description

• Detects paragraph direction from first strong UTF-16 character while excluding isolate contents.
• Classifies BMP text through Windows and supplementary code points through Unicode 17 data.
• Adds reproducible table generation with checksum validation and Unicode licensing.
Diagram

graph TD
  U["Unicode 17 Data"] --> G["Table Generator"] --> T["Bidi Ranges"] --> H["Direction Helper"] --> P["Paragraph Level"]
  W["Windows API"] --> H
  X["UTF-16 Text"] --> H
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use a Unicode bidi library
2. Generate classification for all Unicode planes
  • ➕ Produces consistent behavior across Windows versions
  • ➕ Uses one Unicode version for BMP and supplementary characters
  • ➖ Requires a larger generated table
  • ➖ Duplicates BMP classification already available through Windows

Recommendation: Keep the PR's hybrid approach: Windows classification minimizes BMP data and preserves legacy compatibility, while generated Unicode 17 ranges address unreliable supplementary classification. A full bidi library would be preferable only if later shaping work requires substantially more of UAX #9; automated tests should accompany that future integration.

Files changed (5) +444 / -0

Enhancement (2) +349 / -0
supplementarybidi.inlAdd generated supplementary bidi ranges +265/-0

Add generated supplementary bidi ranges

• Adds Unicode 17.0.0 ranges for supplementary right-to-left and neutral code points. Omitted ranges default to left-to-right and are suitable for binary-search lookup.

Core/Libraries/Source/WWVegas/WW3D2/supplementarybidi.inl

unicodebidi.hImplement first-strong paragraph direction detection +84/-0

Implement first-strong paragraph direction detection

• Adds UTF-16 paragraph scanning that ignores directional-isolate contents, safely handles malformed surrogates, and decodes valid supplementary code points. BMP characters use GetStringTypeW, while supplementary characters use the generated range table.

Core/Libraries/Source/WWVegas/WW3D2/unicodebidi.h

Documentation (1) +39 / -0
unicode-license.txtInclude the Unicode data license +39/-0

Include the Unicode data license

• Documents the license governing the Unicode data used to generate supplementary bidi classifications.

Core/Libraries/Source/WWVegas/WW3D2/unicode-license.txt

Other (2) +56 / -0
CMakeLists.txtRegister Unicode bidi sources in WW3D2 +2/-0

Register Unicode bidi sources in WW3D2

• Adds the paragraph-direction header and generated supplementary table to the WW3D2 source list.

Core/Libraries/Source/WWVegas/WW3D2/CMakeLists.txt

generate_supplementary_bidi.pyGenerate and verify supplementary bidi data +54/-0

Generate and verify supplementary bidi data

• Adds a reproducible Unicode 17.0.0 table generator with source-version and SHA-256 validation. It applies explicit and @missing classes, emits compact non-LTR ranges, and supports an out-of-date check mode.

scripts/generate_supplementary_bidi.py

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can ask Qodo to dismiss a finding you disagree with, with your reason on record

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@greptile-apps

greptile-apps Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Adds Unicode text direction detection library.

The PR appears safe to merge based on the reviewed changes.

Summary

The PR adds a portable Unicode 17.0.0 first-strong paragraph-direction helper and connects it to the shared build. Since the previous review, the only change removes the helper library’s archive-name override. The previously reported paragraph-boundary issue is fixed: scanning stops at the separator.

Reviews (9) · Last reviewed commit: "chore(text): Use the default Unicode bid..."

@stephanmeesters

Copy link
Copy Markdown

Why do we need the unicode license and copyright headers?


Direction direction = Neutral;
if (ch >= 0xD800 && ch <= 0xDBFF) {
if (index + 1 < length && text[index + 1] >= 0xDC00 && text[index + 1] <= 0xDFFF) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you understand everything that's going on here or is it left to reviewers to figure out?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, the goal here is specifically paragraph base direction detection according to the first strong character, rather than implementing the complete bidi algorithm, so the loop do this at the moment:

  • it skips the contents of directional isolates for the paragraph direction decision, and safely decodes valid UTF-16 surrogate pairs
  • it ignores malformed surrogate sequences rather than treating them as directional characters, and uses GetStringTypeW(CT_CTYPE2) for BMP characters

it then uses the generated Unicode bidi class ranges for supplementary code points because GetStringTypeW does not reliably classify those, and returns level 0 or 1 when the first strong L or R or AL character is found.

my reason is this generated table is intentionally limited to what this helper needs rather than trying to duplicate make the whole the complete Unicode bidi algorithm (more code more complix, and it will take a lot of time)

well, i suppose we can improve the loop or i can add more comments if needed.

@OmarAglan

Copy link
Copy Markdown
Author

Why do we need the unicode license and copyright headers?

hmm this is the supplementary bidi table and it is generated from Unicode's DerivedBidiClass.txt, i included the unicode license because we are distributing generated data derived from the unicode data files.

if you read the license of unicode v3 , you would see that it requires the copyright and permission notice to be included with any copies of the data software or it can be included in associated documentation also the short copyright and license header on the generated .inl is there to make the to be more clear and it points to the full license

i suppose, if it will do no harm in anyway, i can remove it if needed

@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: d98457fc-e1e7-4f80-ab74-89e110f425eb

📥 Commits

Reviewing files that changed from the base of the PR and between 1e735f5 and 5802fb2.

📒 Files selected for processing (1)
  • Core/Libraries/Source/WWVegas/WW3D2/unicodebidi.h

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


Walkthrough

The change adds Unicode 17.0.0 supplementary bidi data and a generator for that data. It also adds C++ functions that classify supplementary code points and detect paragraph direction in UTF-16 text.

Changes

Unicode bidirectional classification

Layer / File(s) Summary
Generate supplementary bidi data
scripts/generate_supplementary_bidi.py, Core/Libraries/Source/WWVegas/WW3D2/supplementarybidi.inl, Core/Libraries/Source/WWVegas/WW3D2/unicode-license.txt
The generator validates the Unicode 17.0.0 source header and checksum, then emits supplementary ranges for non-left-to-right classes. The generated table includes neutral and right-to-left ranges. The license notice is added.
Classify paragraph direction
Core/Libraries/Source/WWVegas/WW3D2/unicodebidi.h, Core/Libraries/Source/WWVegas/WW3D2/supplementarybidi.inl, Core/Libraries/Source/WWVegas/WW3D2/CMakeLists.txt
Adds direction values, supplementary code-point lookup, and UTF-16 paragraph-level detection. The build source list includes the new header and range table.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Text
  participant Get_Paragraph_Level
  participant Get_Supplementary_Direction
  participant supplementarybidi.inl
  participant GetStringTypeW
  Text->>Get_Paragraph_Level: provide UTF-16 text
  Get_Paragraph_Level->>Get_Supplementary_Direction: classify valid surrogate pairs
  Get_Supplementary_Direction->>supplementarybidi.inl: search supplementary ranges
  Get_Paragraph_Level->>GetStringTypeW: classify non-surrogate BMP characters
  Get_Paragraph_Level-->>Text: return paragraph level
Loading

Suggested reviewers: stephanmeesters

Merge Risk: 🔵 Low · up to 5802f

Direction detection may assign an empty paragraph the direction of text in the next paragraph. This is a bounded issue, but paragraph separation should be clarified or fixed before the helper is used for rendering.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 5802f

The new direction helper is not connected to a rendering path in this PR, so no new active security exposure was identified. Its input and generated-data contracts matter when a later change begins using it.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — Current in-repository reach is limited to the new Core helper and its internal lookup. Exposure through future or external consumers remains unestablished.

Trust Boundaries and Controls

  • inferred — No attacker-controlled route to the helper was identified in this PR. A future caller would own the boundary between its input and the pointer-and-length contract.

Resilience and Maintainability Implications

  • observed — Checksum validation and deterministic table generation help prevent an unintended source-data change from silently altering supplementary-character classification.

Hardening Proposals

  • proposed — When a rendering caller is introduced, verify that it passes a valid bounded UTF-16 span and assess any attacker-controlled text path at that integration boundary.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: adding Unicode paragraph base direction detection.
Description check ✅ Passed The description explains the helper, its Unicode data, intended use, and validation. It is directly related to the changeset.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: e349eecd-f1d5-4445-9ec2-2dcf71941e61

📥 Commits

Reviewing files that changed from the base of the PR and between 722dbea and 79e4789.

📒 Files selected for processing (5)
  • Core/Libraries/Source/WWVegas/WW3D2/CMakeLists.txt
  • Core/Libraries/Source/WWVegas/WW3D2/supplementarybidi.inl
  • Core/Libraries/Source/WWVegas/WW3D2/unicode-license.txt
  • Core/Libraries/Source/WWVegas/WW3D2/unicodebidi.h
  • scripts/generate_supplementary_bidi.py

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread Dependencies/UnicodeBidi/unicodebidi.cpp
@OmarAglan
OmarAglan force-pushed the feature/unicode-paragraph-direction branch from 79e4789 to 1e735f5 Compare September 24, 2026 21:50

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the use case for this?

Comment thread Dependencies/UnicodeBidi/unicode-license.txt
Comment thread Dependencies/UnicodeBidi/unicodebidi.cpp
Comment thread Dependencies/UnicodeBidi/generate_bidi.py
Comment thread Core/Libraries/Source/WWVegas/WW3D2/unicodebidi.h Outdated
@OmarAglan
OmarAglan force-pushed the feature/unicode-paragraph-direction branch from 1e735f5 to 5802fb2 Compare September 26, 2026 11:44
Comment thread Dependencies/UnicodeBidi/unicodebidi.cpp Outdated
@OmarAglan OmarAglan changed the title feat(text): Add Unicode paragraph direction detection feat(text): Add Unicode paragraph base direction detection Sep 26, 2026
@OmarAglan

Copy link
Copy Markdown
Author

What is the use case for this?

this is a helper, and is needed for #3292, well if use complex text path it uses Uniscribe to shape normally authored Arabic or Hebrew and mixed direction ui text

this is mainly used befor itemization, the itemization needs the paragraph initial bidi level (we use 0 for LTR or 1 for RTL), this donot implement the full bidi algorithm, this is more simpler approach in figuring out initial bidi level, i dont want to make a whole bidi algorithm, (about 5000 code of line to get it fully setup +_+).

@OmarAglan
OmarAglan force-pushed the feature/unicode-paragraph-direction branch from 5802fb2 to 51b90cf Compare September 26, 2026 13:38
Comment thread Dependencies/UnicodeBidi/unicodebidi.h Outdated
#pragma once

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be obsolete after #3393

Comment thread Dependencies/UnicodeBidi/unicodebidi.h Outdated
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need windows?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the algorithm it self do not require Windows, as you see the current implementation uses GetStringTypeW for bmp classification and Windows types in the API

i suppose i can replace that classification with generated Unicode data too and use portable types, That would remove the need for windows.h and duplicate macro mentioned in #3393 and the windows only CMake condition once and for all

i can do that if agreed upon!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wide char type currently is wchar_t. Becomes unichar after #3321

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wide char type currently is wchar_t. Becomes unichar after #3321

so what i need to do is what exactly? cant figure it out!

do i make the helper portable using the project’s current wchar_t type, and remove the Windows include and macro?!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wide char type currently is wchar_t. Becomes unichar after #3321

i have these changes localy, if we agree upon i will push it:

  • now Windows dependencies is removed so there is no windows.h or WCHAR or WORD or GetStringTypeW or WIN32_LEAN_AND_MEAN
  • i used wchar_t
  • made the classification portable and now we have generated Unicode 17 table that now covers both bmp and supplementary characters
  • now CMake build the library on every platform

note that the algorithm still finds the first strong direction in the first paragraph and skips isolate contents and handles surrogate pairs

the shaping and visual reordering remain the renderer responsibility

there is a tradeoff here (as a maintainer maybe we need to consider the larger generated table in exchange for consistent classification across operating systems)
bmp results now follow Unicode 17 rather than the host Windows version, making build on multiple platforms as possible

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes right now we just use wchar_t

bmp results now follow Unicode 17 rather than the host Windows version

What does this mean in practice? Any complications?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in practice, paragraph base direction would use the same Unicode data on every platform.

i did compared it with GetStringTypeW locally, i did find (for example) that Windows reports U+061C (arabic letter mark) as having no direction (why???!), while Unicode 17 classifies it as strong RTL, and a paragraph starting with that mark followed by Latin text would get RTL base direction.

also U+0600 (arabic number sign) is weak under Unicode 17, so it would no longer determine paragraph direction by itself

i assume That the main compatibility concern is that an older Uniscribe implementation may use different classifications internally, that helper only supplies the initial paragraph level, it does not add Unicode 17 shaping or font support

the full rendering still needs to be tested and to be validated in the pr #3292.

this table is also larger and requires regeneration for future Unicode updates (when needed of course), i did some local direction checks VC6 game builds and they passed

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if I understand this correctly the presented solution here will not work reliably, is that right? If so, is there an alternative superior implementation that works reliably?

I still do not understand what the goal of this change is, because the explanations have not been clear to me.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry if my explanation is unclear, I'm having a hard time putting my words Clearly

the goal here is to choose the initial paragraph direction before Uniscribe itemization in #3292, lets take for example, 123 مرحبا should start with RTL paragraph level because the numbers are weak characters and Arabic supplies the first strong character

Hello مرحبا should start with LTR level. This supplies the starting direction for mixed direction UI text.

the helper simply implements the Unicode 17 first strong rule using the same data on every platform, i did some classification checks and paragraph regression cases and they pass.

Usp10 still performs the remaining bidirectional analysis, shaping and rendering.

the changes localy is pushed, now Windows include and macro and API types are removed

the API uses wchar_t and and CMake builds the helper on every platform (i only tested linux and windows).

Comment thread CMakeLists.txt Outdated
@OmarAglan
OmarAglan force-pushed the feature/unicode-paragraph-direction branch from 51b90cf to 6d0f511 Compare October 2, 2026 12:33
Comment thread Dependencies/UnicodeBidi/unicodebidi.cpp Outdated
Comment thread Dependencies/UnicodeBidi/unicodebidi.cpp Outdated
Comment thread Dependencies/UnicodeBidi/CMakeLists.txt Outdated
Comment thread Dependencies/UnicodeBidi/CMakeLists.txt Outdated
@xezon
xezon requested review from OmniBlade and xezon October 3, 2026 10:28
Comment thread Dependencies/UnicodeBidi/CMakeLists.txt Outdated

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand what is going on unicodebidi.cpp but it looks like c++ code so good enough.

I would like to wait for @OmniBlade review before merging.

@OmarAglan

Copy link
Copy Markdown
Author

I do not understand what is going on unicodebidi.cpp but it looks like c++ code so good enough.

I would like to wait for @OmniBlade review before merging.

cool with me, take your time, and thanks for your effort

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants