利用浏览器优化排版算法的方案

问题分析

无论是犁书项目之前的将社交网络的简单文本与图片列表信息整理排版,还是将富文本内容排版成册,
其本质是两件事:

  • 将原有的数据按照我们的样式规则进行排列,如果是带有样式的富文本,那么将原有样式与模板样式相结合
  • 根据DOM元素的高度除以页的高度进行断页(break page)

那么对于排版算法,尤其是富文本的排版,其最大的难点与工作量在于:

  • 解析各种html标签与样式,计算出dom元素的高度,以便在合适的地方断页
  • 递归dom树形结构,将其转换为以树为元素的线性集合

浏览器计算元素高度

对于解析html标签、样式来说,如果不拘泥于现在的用java代码的Font库逐步计算每个字符的宽高、何时换行这种造轮子的方式,
很容易想到,最简单直接的方法,就是利用浏览器的渲染引擎以及javascript代码来获取dom元素的高度。

那么问题就在于,谁来打开这个浏览器,谁来输入这样的url进行访问,谁来向服务器报告dom元素的高度?
很显然,不可能是用户,用户的浏览器千差万别,把数据交由一个情况不明的用户浏览器来创造肯定是不符合常理的。

于是希望尝试使用“服务端无图形界面虚拟浏览器”————phantomjs来担此重任。

排版步骤

流程图

  1. 服务器获取内容数据
  2. 提供一个查询内容数据的Rest API接口
  3. 服务器启动一个phantomjs的子线程,打开对应url的网页,载入内容数据
  4. 在网页中,使用javascript代码完成实际排版

    1. 将html文本解析为虚拟dom树,并准备活动页dom树
    2. 递归遍历dom树元素,并准备两个集合作为主要操作对象:
      • dom元素类型栈:保存由根元素向当前被操作的叶元素的类型,注意ol.li是第几个以设置新的ol.start
      • 活动原子队列:根据排版逻辑确定不可分割的最小原子元素,如单个字符、单张图片、一条标题,将已经开始排列的原子元素逐一向网页中添加
    3. 监听实际dom树的元素增加事件,获取页面已有元素高度,判断是否达到书页底部
    4. 监听断页事件,将活动页dom元素回滚一个版本后,保存至实际dom树列表
    5. 排版完成后,将dom元素情况提交服务器
  5. 将排版情况数据发送到服务器进行保存
  6. 网页利用事件通知phantomjs结束线程

模型设计

ER图

  • TableOfContents

    保存目录结构

  • PageLayout

    保存单页的排版数据

    • isValid() 判断是否有效,如果无效,客户端必须等待
    • 当work内容发生变化时,必须将isValid()设置为false