# ratex-react-native

React Native 原生 LaTeX 数学公式渲染库——无 WebView，无 JavaScript 数学引擎。公式在 Rust 中完成解析和排版（编译为原生库），直接使用 KaTeX 字体绘制到原生 Canvas 上。

> English documentation: [README.md](./README.md)

## 特性

- 在 iOS、Android 与 macOS（[React Native macOS](https://github.com/microsoft/react-native-macos)）上原生渲染 LaTeX 数学公式
- 基于**新架构**（Fabric / JSI / TurboModules）构建 —— React Native ≥ 0.84 仅支持新架构
- 测量渲染内容尺寸，便于滚动视图和动态布局
- 提供解析失败的错误回调
- 内置所有 KaTeX 字体，无需额外配置
- 通过 `alignSelf: 'baseline'` 在 flex 行和 `<Text>` 内实现基线对齐
- 同步公式度量 API（`getTexMetrics`），供自定义文本引擎使用
- `InlineTeX` 组件支持文字与 `$...$` 公式混排

## 环境要求

| 依赖 | 版本 |
|-----|------|
| React Native | ≥ 0.84 |
| React | ≥ 19.2 |
| iOS | ≥ 14.0 |
| macOS | ≥ 13.0（使用 React Native macOS 时） |
| Android | minSdk 21（Android 5.0+）|

## 安装

```sh
npm install ratex-react-native
```

### iOS — pod install

```sh
cd ios && pod install
```

### macOS（React Native macOS）

在应用的 `macos/` 目录执行 `pod install`，然后 `npx react-native run-macos`。本 pod 自带的 `RaTeX.xcframework` 需包含 **macOS** 切片（见 RaTeX 仓库中的 `./scripts/build-apple-xcframework.sh`）。

### Android

无需额外操作，原生 `.so` 库会自动打包。

## 使用方法

### 块级公式

```tsx
import { RaTeXView } from 'ratex-react-native';

function MathFormula() {
  return (
    <RaTeXView
      latex="\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}"
      fontSize={24}
      color="#1E88E5"
      onError={(e) => console.warn('LaTeX 错误:', e.nativeEvent.error)}
    />
  );
}
```

### 内联公式（文字与 LaTeX 混排）

```tsx
import { InlineTeX } from 'ratex-react-native';

function Paragraph() {
  return (
    <InlineTeX
      content="质能等价关系 $E = mc^2$ 是狭义相对论的核心结论。"
      fontSize={16}
      textStyle={{ color: '#333' }}
    />
  );
}
```

在 `content` 字符串中用 `$...$` 标记公式，支持一段文字中包含多个公式。

### 共享默认颜色

```tsx
import { RaTeXProvider, InlineTeX, RaTeXView } from 'ratex-react-native';

function Screen() {
  return (
    <RaTeXProvider color="#1E88E5">
      <RaTeXView latex="x + y" />
      <InlineTeX content="内联公式：$E = mc^2$" />
    </RaTeXProvider>
  );
}
```

## API

### `<RaTeXView />`

| 属性 | 类型 | 默认值 | 说明 |
|-----|------|--------|------|
| `latex` | `string` | — | 要渲染的 LaTeX 数学字符串（必填） |
| `fontSize` | `number` | `24` | 字体大小，单位为 **dp**（密度无关像素）。公式整体等比缩放。 |
| `displayMode` | `boolean` | `true` | `true` = 独立块样式（`$$...$$`）；`false` = 行内样式（`$...$`）。 |
| `color` | `ColorValue` | — | 默认公式颜色。显式 LaTeX 颜色仍然优先。 |
| `style` | `StyleProp<ViewStyle>` | — | 标准 React Native 样式。宽高会自动从测量结果设置，也可手动覆盖。 |
| `onError` | `(e: { nativeEvent: { error: string } }) => void` | — | LaTeX 字符串解析失败时调用。 |
| `onContentSizeChange` | `(e: { nativeEvent: { width: number; height: number } }) => void` | — | 排版完成后回调，携带公式的**固有内容尺寸（未缩放）**（dp）。适用于滚动视图或动态容器。 |

### 内容尺寸自适应

`RaTeXView` 会自动将 `onContentSizeChange` 返回的 `width` 和 `height` 应用到自身 `style`，实现类似 `wrap_content` 的自适应布局，无需手动指定尺寸：

```tsx
<ScrollView horizontal>
  <RaTeXView latex="\sum_{n=1}^{\infty} \frac{1}{n^2} = \frac{\pi^2}{6}" fontSize={28} />
</ScrollView>
```

#### 显式指定宽高时的行为

如果你在 `style` 中显式指定了 `width` 和/或 `height`，`RaTeXView` **不会**再用测量结果覆盖这些值；原生视图会在绘制阶段把公式**按比例缩小（不会放大）**以适配给定布局尺寸，并在必要时按边界裁剪。

### 基线对齐

`RaTeXView` 可以像普通字符一样落在文字基线上——既支持 flex 行，也支持 `<Text>` 内嵌：

```tsx
<View style={{ flexDirection: 'row', alignItems: 'baseline' }}>
  <Text>f(x) =</Text>
  <RaTeXView latex={'\\frac{a}{b}'} fontSize={16} displayMode={false} />
</View>

<Text>
  compare y with{' '}
  <RaTeXView latex="y" fontSize={16} displayMode={false}
             style={{ alignSelf: 'baseline' }} />{' '}
  mid-sentence
</Text>
```

### `<InlineTeX />`

将包含 `$...$` 标记的混合字符串渲染为原生内联文本流。公式在 iOS/macOS 上通过 `NSTextAttachment` 嵌入，在 Android 上通过 `ReplacementSpan` 嵌入，因此换行、断词和基线对齐都交给平台文本排版引擎处理。

**渲染流程：**

1. 将 `content` 解析成文字和公式片段。转义美元符号（`\$`）保留为普通文本，未闭合或空的 `$` 分隔符会回退为普通文本。
2. 公式片段使用原生 text attachment/span 参与内联排版，并上报测量后的内容高度用于动态布局。

| 属性 | 类型 | 默认值 | 说明 |
|-----|------|--------|------|
| `content` | `string` | — | 包含 `$...$` 标记的文字字符串（必填）。 |
| `fontSize` | `number` | `16` | 传给每个公式渲染器的字体大小（dp）。 |
| `color` | `ColorValue` | — | 传给每个行内公式的默认颜色。显式 LaTeX 颜色仍然优先。 |
| `textStyle` | `StyleProp<TextStyle>` | — | 普通文字样式来源。支持字段：`color`、`fontSize`、`fontFamily`、`fontStyle: 'italic'`，以及包含 `underline` / `line-through` 的 `textDecorationLine`。 |
| `style` | `StyleProp<ViewStyle>` | — | 原生内联容器的标准 React Native 样式。若未显式指定高度，会自动使用测量高度。 |

> `InlineTeX` 会自动对所有公式传入 `displayMode={false}`——`$...$` 始终使用行内样式。

### `<RaTeXProvider />`

为后代 `RaTeXView` 和 `InlineTeX` 提供默认公式颜色。若组件自身传入 `color`，则会覆盖继承值。

### `getTexMetrics()`

同步获取公式的墨迹度量——即 TeX 盒模型中的 *depth*：KaTeX 在 Web 端以 `vertical-align: -depth` 输出，MathML Core 中称为 *ink line-descent*。适用于需要以数值形式拿到基线偏移的自定义文本引擎（TextKit / Spannable、markdown 渲染器等）。度量与 measure/render 共享同一解析缓存，屏幕上已有的公式不会重复解析。

```tsx
import { getTexMetrics } from 'ratex-react-native';

// 任意公式的自然（未缩放）度量——无需挂载视图：
const m = getTexMetrics('\\frac{a}{b}', 16, false);
// { width, height, depth } | null —— 单位 dp；基线位于 height - depth 处

// 已挂载视图的实际绘制度量（已应用适配缩放与居中）：
const d = ref.current?.getTexMetrics(); // ref: RaTeXViewRef
// { depth, scale, width, height } | null —— depth 可直接使用
```

两者都可在 `useLayoutEffect` 中安全调用。

## 架构支持

仅支持**新架构**（Fabric / Codegen / TurboModules），与 [React Native 官方支持版本](https://reactnative.dev/versions)（≥ 0.84）保持一致，新架构在这些版本中默认开启。不再支持旧架构（Bridge/Paper）。

## fontSize 说明

`fontSize` 单位为 **dp（密度无关像素）**，而非 CSS `pt` 或物理像素。在 3× 屏幕密度的设备上，`fontSize={24}` 的公式渲染高度为 72 物理像素，与 React Native 的标准布局单位一致。

## 许可证

MIT
