npm package: @mattuy/text-frame
Description: a javascript tool to split long text into pages, with typesetting prohibition processed and unicode full support.
一个将长文本分成若干页的js工具，处理排版禁则，支持Unicode
Author: mattuy
Latest version: 0.0.6

# TextFrame
一个通过canvas将长文本分成若干页渲染的js工具，处理排版禁则，支持辅助平面Unicode字符。目前仅工作于浏览器环境。

[中文版](https://github.com/mattuylee/text-frame) | [English](https://github.com/mattuylee/text-frame/blob/master/docs/en/README-EN.md)

## 项目介绍
在浏览器中，尽管css有分栏布局，但却很难实现“分页”。浏览器没有提供计算一个盒子可以显示多少文本的API，只有Canvas的上下文提供了一个`measureText()`方法，但它只能简单的计算一段文本的渲染宽度，当文本不止一行时就无法计算了。由于不同字体不同字符有不同的宽度，再加上各浏览器对排版禁则的处理不一致，基本杜绝了计算浏览器原生DOM元素能够显示多少文本的可能。

于是我参考W3C对中文排版的[草案](https://www.w3.org/TR/2020/WD-clreq-20201001/#prohibition_rules_for_line_start_end)中的排版禁则，通过[CanvasRenderingContext2D.measureText()](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/measureText)方法计算字符宽度，对文本进行分页。

### 行首禁则
|  名称 |  符号  |
| ---- | ------ |
| 点号           |   、,，.．。:：;；!！?？  |
| 结束引号        |  '"」』”’              |
| 结束括号        | )]}）】〗〕］｝         |
| 结束乙式书名号   | 》〉                   |
| 连接号         | –~～—                  |
| 间隔号         | ·．‧•・                 |
| 分隔号         | /／                    |

### 行尾禁则
|  名称 |  符号  |
| ---- | ------ |
| 开始引号        |  「『“‘                 |
| 开始括号        |  ([{（【〖〔［〔         |
| 开始单双书名号   | 《〈                    |
| 分隔号         | /／                     |

### 符号分离禁则
|  名称  |  符号  |
| ----  | ------ |
| 破折号 | ──     |
| 省略号 | ……     |

本项目默认按照上述禁则处理排版禁则，但可以通过[选项](https://github.com/mattuylee/text-frame/blob/master/docs/zh/options.md)控制排版禁则。
草案中规定，当碰到行首为（行首禁则中的）标点时，应遵守「先挤进，后推出」原则，即先尝试压缩当前行的标点，无法挤压再取前一行的最后一个字至下一行。考虑到标点符号压缩的复杂性，本项目暂未实现该特性，而是直接尝试取上一行的最后一字到下一行，如果直到上一行首都没找到允许出现在行首的字符，则采取不处理的方式。

## 功能
本项目定义了文本片段（text fragment）的概念。通过在选项中指定一个文本片段数组，可以对每一个片段设置不同的样式，如字体、颜色、对齐方式等，具体请参考[配置项](https://github.com/mattuylee/text-frame/blob/master/docs/zh/options.md)。一般来说，一个文本片段即一个文本段落，虽然文本片段内也支持换行，但不会有段间距和行缩进。在本项目文档中文本片段和文本段落等价，都是指`FragmentOptions`定义的文本片段。

本项目支持以下特性：
* 每个段落单独设置字体、缩进、边距、字体颜色等；
* 支持两端对齐；
* 支持自定义排版禁则；
* 可配置段落之间边距是否折叠；
* 支持rtl模式；

这是一个[在线例子](https://mattuylee.github.io/text-frame/zh/example.html)。

## 使用
本项目仅支持浏览器环境，且依赖于Canvas 2D上下文，请参考[canvas兼容性](https://caniuse.com/?search=canvas)。

### API
```typescript
// 计算分页
function computeTextFrames(options: FrameOptions): TextFrame[];
// 渲染分页到canvas
function renderFrame(context: CanvasRenderingContext2D, frame: TextFrame, clear: boolean): void;
```

### npm
`npm install @mattuy/text-frame --save`
### ES2015 & CommonJS
```javascript
// cjs
// const { computeTextFrames, renderFrame } = require('@mattuy/text-frame');
// or esm
import { computeTextFrames, renderFrame } from '@mattuy/text-frame/esm';

const frames = computeTextFrames({
    viewWidth: 320,
    viewHeight: 640,
    fontSize: 16,
    margin: 8,
    color: '#000',
    fragments: [
        {
            color: 'green',
            fontSize: 20,
            margin: 32,
            textAlign: 'center',
            text: "标题"
        },
        {
            textIndent: 32,
            fontFamily: 'serif',
            margin: { bottom: 32 },
            marginCollapse: true,
            textAlign: 'justify',
            text: "这是一个多行文本。"
        }
    ]
});
const canvas = document.createElement('canvas');
canvas.style.width = '320px';
canvas.style.height = '640px';
document.body.append(canvas);
renderFrame(canvas.getContext('2d'), frames[0]);
```

### 全局引入
```html
<script src="text-frame/dist/umd/text-frame-min.js"></script>
<script>
    const frames = TextFrame.computeTextFrames({
        // ...options
    });
    console.log(frames)
    // ...render
</script>
```

配置说明请参考[配置项](https://github.com/mattuylee/text-frame/blob/master/docs/zh/options.md)。


## 源码编译
* 克隆仓库
`git clone https://github.com/mattuylee/text-frame.git`

* 安装依赖
`cd text-frame`  
`npm install`

### 发布
`npm run dist`

### 启动demo
`npm run example`

### 调试代码
执行`npm run debug`，然后rollup会监听src下文件的变化自动重新编译。可浏览器直接打开`example/index.html`测试。


## Licence
MIT