docs(collabora): organize deployment guides and fix proxy chain

This commit is contained in:
wren
2026-05-11 17:54:39 +08:00
parent dcc0f3c30d
commit f788149ca7
16 changed files with 2959 additions and 15 deletions
@@ -0,0 +1,244 @@
## canvas 绘制(dist\bundle.js
```plain
┌─────────────────────────────────────────────────────────────┐
│ 1. 用户打开 cool.html │
│ 参数: WOPISrc=..., access_token=... │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. global.js 初始化 │
│ - 检测浏览器类型 (BrowserProperties) │
│ - 创建 WebSocket 连接 │
│ - 发送 "load url=..." 命令 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. bundle.js 加载并处理 "status:" 消息 │
│ status: { type: "text", parts: 5, width: 21000, ... } │
│ → 创建 L.WriterTileLayer │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. TileManager 请求瓦片 │
│ → "tilecombine part=0 tileposx=0,256,512 ..." │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. 服务器返回瓦片数据 │
│ textMsg: "tile: nviewid=0 part=0 width=256 height=256" │
│ img: ImageBitmap (二进制图像数据) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 6. TileManager.onTileMsg() 处理 │
│ - 解析 tileMsgObj │
│ - 存储到 _tiles Map │
│ - 标记需要重绘 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 7. CanvasSectionContainer 绘制 │
│ ctx.drawImage(tile.image, sx, sy, sw, sh, dx, dy, dw, dh)│
│ → 用户看到完整的文档页面 │
└─────────────────────────────────────────────────────────────┘
```
+ **文档类型检测**: `_docType` 属性标识文档类型 ("text", "spreadsheet", "presentation", "drawing")
+ **消息处理**: `_onMessage(textMsg, img)` 是核心消息处理函数
+ **WriterTileLayer**: 专门用于处理 Word 文档 (docx) 的图层类
+ **瓦片消息**: 通过 WebSocket 接收 "tile:" 和 "tilecombine:" 消息
+ **Canvas 渲染**: 使用 `canvas.drawImage()` 将瓦片图像绘制到 Canvas 上
### 核心架构
### 1. 文档类型层次结构
```javascript
L.Layer (基类)
L.CanvasTileLayer (瓦片图层基类)
L.WriterTileLayer (Word 文档 - docx)
L.CalcTileLayer (电子表格 - xlsx)
L.ImpressTileLayer (演示文稿 - pptx/odp)
```
### 2. DOCX 文档数据对象结构
#### **TileMsgObj** (瓦片消息对象)
```javascript
{
id: number, // 瓦片唯一标识
width: number, // 瓦片宽度 (像素)
height: number, // 瓦片高度 (像素)
part: number, // 文档部分 (页码)
mode: number, // 渲染模式 (默认 0)
nviewid: number, // 视图ID
wireId: number, // 传输ID (用于增量更新)
zoom: number // 缩放级别
}
```
#### **文档层对象** (L.WriterTileLayer)
```javascript
{
_docType: "text", // 文档类型标识
_selectedPart: number, // 当前选中的页码
_selectedMode: number, // 当前模式
_viewId: number, // 视图ID
_debug: { // 调试信息
tileInvalidationsOn: boolean,
tileOverlaysOn: boolean
},
_onMessage: Function // 核心消息处理函数
}
```
### 渲染流程
### 第一阶段: 初始化与连接
```javascript
1. cool.html 加载
2. global.js 初始化浏览器属性和配置
3. 创建 WebSocket 连接
websocketURI = "ws://collabora-server/cool/{docURL}"
4. 发送 load 命令
"load url={encodedDocURL} lang=zh-CN accessibilityState=..."
```
### 第二阶段: 文档加载
```javascript
1. 服务器返回 "status:" 消息
包含文档类型尺寸页数等元数据
2. 根据文档类型创建对应的 TileLayer
if (command.type === "text")
docLayer = new L.WriterTileLayer(options)
3. 设置文档属性
docLayer._docType = "text"
docLayer.options = {
tileWidthTwips: 3840,
tileHeightTwips: 3840,
tileSize: 256 // 默认瓦片大小 256x256 像素
}
```
### 第三阶段: 瓦片渲染
#### **瓦片请求**
```javascript
// 客户端发送瓦片组合请求
"tilecombine nviewid=0 part=0 width=256 height=256
tileposx=0,256,512 tileposy=0,0,0 tilewidth=3840 tileheight=3840"
```
#### **瓦片接收与处理**
```javascript
TileManager.onTileMsg(textMsg, img) {
// 1. 解析瓦片消息
var tileMsgObj = parseServerCmd(textMsg);
// textMsg 格式: "tile: nviewid=0 part=0 width=256 height=256 ..."
// 2. 转换为坐标
var coords = tileMsgToCoords(tileMsgObj);
// coords = { x, y, z (zoom), part, mode }
// 3. 获取或创建瓦片对象
var tile = this.get(coords);
tile.viewId = tileMsgObj.nviewid;
tile.wireId = tileMsgObj.wireId;
tile.image = img; // ImageBitmap 或 Image 对象
// 4. 标记需要重绘
this.rehydrateTile(tile, true);
}
```
### 第四阶段: Canvas 绘制
#### **CanvasSectionContainer** (Canvas 容器管理)
```javascript
{
canvas: HTMLCanvasElement, // 主 Canvas 元素
context: CanvasRenderingContext2D, // 2D 渲染上下文
width: number, // Canvas 宽度
height: number, // Canvas 高度
clearColor: string // 背景色
}
```
#### **瓦片绘制到 Canvas**
```javascript
// 核心绘制逻辑
function paintTile(tile, canvas) {
// 1. 获取 2D 渲染上下文
var ctx = canvas.getContext("2d");
// 2. 计算源矩形 (瓦片图像中的区域)
var sx = 0, sy = 0;
var sWidth = tile.image.width;
var sHeight = tile.image.height;
// 3. 计算目标矩形 (Canvas 中的位置)
var dx = tile.coords.x * 256; // 目标 X 坐标
var dy = tile.coords.y * 256; // 目标 Y 坐标
var dWidth = 256; // 目标宽度
var dHeight = 256; // 目标高度
// 4. 绘制瓦片
if (tile.image) {
canvas.drawImage(
tile.image, // 源图像
sx, sy, // 源起点
sWidth, sHeight, // 源尺寸
dx, dy, // 目标起点
dWidth, dHeight // 目标尺寸
);
}
}
```
### 第五阶段: 增量更新 (Delta)
```javascript
// 服务器发送增量更新而非完整瓦片
if (textMsg.startsWith("delta:")) {
var deltaObj = parseServerCmd(textMsg);
// 应用增量到现有瓦片
applyDelta(tile, deltaObj);
}
```
### 关键特性
### 1. **瓦片缓存机制**
```javascript
TileManager._tiles = new Map(); // 缓存所有已加载的瓦片
// Key: "z:x:y:part:mode"
// Value: { image, wireId, viewId, coords }
```
### 2. **视口可见性优化**
```javascript
// 仅请求当前视口内的瓦片
clientvisiblearea = "0;0;{width};{height}"
// 优先级: 视口中心 > 视口边缘 > 视口外
```
### 3. **消息队列**
```javascript
global.queueMsg = []; // 在文档层准备好之前缓存消息
socket.onmessage = function(event) {
if (typeof socket._onMessage === "function") {
socket._emptyQueue();
socket._onMessage(event);
} else {
queueMsg.push(event.data); // 延迟处理
}
}
```
### 4. **多用户协作**
```javascript
// 每个用户视图有独立的 viewId
tileMsgObj.nviewid = 0; // 当前用户
// 其他用户的光标、选择通过单独的消息同步
```