git.iko.soy/rowtype-yoga/yoga-r3f
on main help sign in
133 lines 5.0 kB history raw
1# bidi-js
3This is a pure JavaScript implementation of the [Unicode Bidirectional Algorithm](https://www.unicode.org/reports/tr9/) version 13.0.0. Its goals, in no particular order, are to be:
5* Correct
6* Small
7* Fast
10## Conformance
12This implementation currently conforms to section [UAX-C1](https://unicode.org/reports/tr9/#C1) of the bidi spec, as verified by running all the provided [conformance tests](https://unicode.org/reports/tr9/#Bidi_Conformance_Testing).
14## Compatibility
16It has no external dependencies and therefore should run just fine in any relatively capable web browser, Node.js, etc. The provided distribution `.js` files are valid ES5.
18## Usage
20Install it from npm:
22```shell
23npm install bidi-js
24```
26[![NPM](https://nodei.co/npm/bidi-js.png?compact=true)](https://npmjs.org/package/bidi-js)
28Import and initialize:
30```js
31import bidiFactory from 'bidi-js'
32// or: const bidiFactory = require('bidi-js')
34const bidi = bidiFactory()
35```
37The `bidi-js` package's only export is a factory function which you _must invoke_ to return a `bidi` object; that object exposes the methods for bidi processing.
39(_Why a factory function?_ The main reason is to ensure the entire module's code is wrapped within a single self-contained function with no closure dependencies. This enables that function to be stringified and passed into a web worker, for example.)
41Now that you have the `bidi` object, you can:
43### Calculate bidi embedding levels
45```js
46const embeddingLevels = bidi.getEmbeddingLevels(
47 text, //the input string containing mixed-direction text
48 explicitDirection //"ltr" or "rtl" if you don't want to auto-detect it
51const { levels, paragraphs } = embeddingLevels
52```
54The result object `embeddingLevels` will usually be passed to other functions described below. Its contents, should you need to inspect them individually, are:
56* `levels` is a `Uint8Array` holding the calculated [bidi embedding levels](https://unicode.org/reports/tr9/#BD2) for each character in the string. The most important thing to know about these levels is that any given character is in a right-to-left scope if its embedding level is an odd number, and left-to-right if it's an even number.
58* `paragraphs` is an array of `{start, end, level}` objects, one for each paragraph in the text (paragraphs are separated by explicit breaking characters, not soft line wrapping). The `start` and `end` indices are inclusive, and `level` is the resolved base embedding level of that paragraph.
60### Calculate character reorderings
62```js
63const flips = bidi.getReorderSegments(
64 text, //the full input string
65 embeddingLevels //the full result object from getEmbeddingLevels
68// Process all reversal sequences, in order:
69flips.forEach(range => {
70 const [start, end] = range
71 // Reverse this sequence of characters from start to end, inclusive
72 for (let i = start; i <= end; i++) {
73 //...
74 }
75})
76```
78Each "flip" is a range that should be reversed in place; they must all be applied in order.
80Sometimes you don't want to process the whole string at once, but just a particular substring. A common example would be if you've applied line wrapping, in which case you need to process each line individually (in particular this does some special handling for trailing whitespace for each line). For this you can pass the extra `start` and `end` parameters:
82```js
83yourWrappedLines.forEach(([lineStart, lineEnd]) => {
84 const flips = bidi.getReorderSegments(
85 text,
86 embeddingLevels,
87 lineStart,
88 lineEnd //inclusive
89 )
90 // ...process flips for this line
91})
92```
94### Handle right-to-left mirrored characters
96Some characters that resolve to right-to-left need to be swapped with their "mirrored" characters. Examples of this are opening/closing parentheses. You can determine all the characters that need to be mirrored like so:
98```js
99const mirrored = bidi.getMirroredCharactersMap(
100 text,
101 embeddingLevels
103```
105This returns a `Map` of numeric character indices to replacement characters.
107You can also process just a substring with extra `start` and `end` parameters:
109```js
110const mirrored = bidi.getMirroredCharactersMap(
111 text,
112 embeddingLevels,
113 start,
114 end //inclusive
116```
118If you'd rather process mirrored characters individually, you can use the single `getMirroredCharacter` function, just make sure you only do it for right-to-left characters (those whose embedding level is an odd number.) It will return `null` if the character doesn't support mirroring.
120```js
121const mirroredChar = (embeddingLevels.levels[charIndex] & 1) //odd number means RTL
122 ? bidi.getMirroredCharacter(text[charIndex])
123 : null
124```
126### Get a character's bidi type
128This is used internally, but you can also ask for the ["bidi character type"](https://unicode.org/reports/tr9/#BD1) of any character, should you need it:
130```js
131const bidiType = bidi.getBidiCharTypeName(string[charIndex])
132// e.g. "L", "R", "AL", "NSM", ...
133```