/**
* PhoneImageEngine - 手机图片排版引擎
*
* 将文章HTML内容自动分页并渲染为手机尺寸图片
* 依赖: jQuery, html2canvas (项目已有)
*
* 渲染宽度540px, html2canvas scale=2 输出1080px
* 小红书: 540x720 (输出1080x1440)
* 抖音: 540x960 (输出1080x1920)
*/
var PhoneImageEngine = (function () {
// ===== 配置 =====
var config = {
template: 'minimal',
size: 'xiaohongshu',
font: 'source-han-sans',
fontSize: 14,
watermark: '',
// 尺寸映射 (渲染尺寸, 实际输出是2倍)
sizes: {
xiaohongshu: { width: 540, height: 720 },
douyin: { width: 540, height: 960 }
},
// 字体映射
fonts: {
'source-han-sans': "'Source Han Sans', 'SourceHanSans-Normal', sans-serif",
'alibaba-puhuiti': "'AlibabaPuHuiTi', sans-serif",
'lxgw-wenkai': "'LXGWWenKai', cursive"
},
// 页面内容边距
contentPadding: 20
};
// ===== 文章数据 =====
var postData = {
id: 0,
title: '',
desc: '',
content_html: '',
poster: '',
author_name: '',
create_time: '',
category_name: ''
};
// ===== 分页结果 =====
var pages = [];
// ===== DOM引用 =====
var $container = null;
/**
* 初始化引擎
* @param {Object} options - {postId, title, desc, contentHtml, poster, authorName, createTime, categoryName}
* @param {Object} userConfig - {template, size, font, fontSize, watermark}
*/
function init(options, userConfig) {
postData.id = options.postId || 0;
postData.title = options.title || '';
postData.desc = options.desc || '';
postData.content_html = options.contentHtml || '';
postData.poster = options.poster || '';
postData.author_name = options.authorName || '';
postData.create_time = options.createTime || '';
postData.category_name = options.categoryName || '';
if (userConfig) {
$.extend(config, userConfig);
}
$container = $('#phone-image-container');
}
/**
* 渲染排版预览 - 生成分页后的HTML
* @returns {Array} 页面数组,每项是 { type, html, pageNum }
*/
function render() {
pages = [];
var sizeConfig = config.sizes[config.size] || config.sizes.xiaohongshu;
var pageHeight = sizeConfig.height;
var contentAreaHeight = pageHeight - (config.contentPadding * 2);
// 内容预处理
var cleanHtml = preprocessContent(postData.content_html);
// 解析为块级元素
var blocks = parseHtmlToBlocks(cleanHtml);
// 生成封面页
pages.push(generateCoverPage(sizeConfig));
// 内容分页
var contentPages = paginateContent(blocks, contentAreaHeight, sizeConfig);
pages = pages.concat(contentPages);
// 生成尾页
pages.push(generateSummaryPage(sizeConfig, pages.length));
// 渲染到DOM
renderToDOM(sizeConfig);
return pages;
}
/**
* 预处理HTML内容
*/
function preprocessContent(html) {
if (!html) return '';
html = html.replace(/