C++ web框架 Paozhu 1.14.0 发布,加入html生成pdf文件,目前全球几乎唯一C++语言开源功能,速度非常快,自带images处理,生成二维码模块为内置,不需要第三方,因为paozhu已经有了自己原生的图像处理类,目前支持jpg png图片,足够常规web开发使用
paozhu子项目webpdf,是html svg images生成pdf功能,灵感来自fpdf.php,然后重构为c++代码,并且支持ttf otf字体,并优化了程序。
增加excel xlst读写程序,可以实现excel读写功能,目前测试支持office2006 macos number libreoffice生成的xlsx格式
html 转 pdf 文件例子
#include "httppeer.h"
#include "serverconfig.h"
#include "server_localvar.h"
#include "test_webpdf.h"
#include "func.h"
#include <memory>
#include <string>
#ifdef ENABLE_PDF
#include "webpdf.h"
#endif// ENABLE_PDF
namespace http
{
namespace fs = std::filesystem;
//@urlpath(null,test_webpdf)
std::string test_webpdf(std::shared_ptr<httppeer> peer)
{
httppeer &client = peer->get_peer();
#ifdef ENABLE_PDF
server_loaclvar &static_server_var = get_server_global_var();
if (static_server_var.config_path.size() < 5)
{
client << "<p> static_server_var.config_path empty </p>";
return "";
}
std::string file_conf = dir_name(static_server_var.config_path);
if (file_conf.size() > 0 && file_conf.back() != '/')
{
file_conf.push_back('/');
}
file_conf.append("docs/");
if(peer->pathinfos.size() == 2 && peer->pathinfos[1] == "cn")
{
try
{
std::string html_file = file_conf + "webpdf_tutorial.html";
std::string pdf_file = file_conf + "webpdf_tutorial.pdf";
// OTF (bare CID-keyed CFF) subsetting path. content_otf.html uses
// the family name "SourceHanSerifSC-Light" directly.
std::string font_file = "AlibabaPuHuiTi-Light.otf";
std::string font_name = "AlibabaPuHuiTi-Light";
fs::path html_path = fs::absolute(html_file);
fs::path pdf_path = fs::absolute(pdf_file);
fs::path font_dir = fs::absolute(file_conf + "font");
std::ifstream f(html_file, std::ios::binary);
if (!f.is_open())
{
client << "Cannot open file: " << html_file;
return "";
}
// 获取文件大小
f.seekg(0, std::ios::end);
std::streamsize size = f.tellg();
f.seekg(0, std::ios::beg);
std::string html;
html.resize(static_cast<size_t>(size));
if (size > 0 && !f.read(html.data(), size)) {
client << "Failed to read file: " << html_file;
return "";
}
auto pdf = std::make_unique<pz::webpdf>();
pdf->setImagesPath(file_conf);
pdf->AddPage();
pdf->AddFont(font_name,"",font_file,font_dir.string());
pdf->SetFont(font_name,"",14);
fs::current_path(fs::absolute(html_file).parent_path());
pdf->WriteHTML(html);
peer->type("application/pdf");
pdf->Output("F", pdf_path.string());
peer->output = pdf->Output("D", pdf_path.string());
//peer->output = pdf->move_result();
} catch (const std::exception& e) {
client << "Error: " << e.what();
return "";
}
}
else
{
try
{
std::string html_file = file_conf + "webpdf_tutorial_en.html";
std::string pdf_file = file_conf + "webpdf_tutorial_en.pdf";
std::string font_file = "AlibabaSans-Regular.otf";
std::string font_name = "AlibabaSans-Regular";
// Resolve everything to absolute paths up front, before we change the
// working directory to the HTML's location for relative asset lookups.
fs::path html_path = fs::absolute(html_file);
fs::path pdf_path = fs::absolute(pdf_file);
fs::path font_dir = fs::absolute(file_conf + "font");
std::ifstream f(html_file, std::ios::binary);
if (!f.is_open())
{
client << "Cannot open file: " << html_file;
return "";
}
// 获取文件大小
f.seekg(0, std::ios::end);
std::streamsize size = f.tellg();
f.seekg(0, std::ios::beg);
std::string html;
html.resize(static_cast<size_t>(size));
if (size > 0 && !f.read(html.data(), size)) {
client << "Failed to read file: " << html_file;
return "";
}
auto pdf = std::make_unique<pz::webpdf>();
pdf->setImagesPath(file_conf);
pdf->AddFont(font_name, "", font_file, font_dir.string());
pdf->SetFont(font_name, "", 14);
// Resources (images, etc.) in the HTML are relative to the HTML file.
//fs::current_path(html_path.parent_path());
pdf->WriteHTML(html);
peer->type("application/pdf");
pdf->Output("F", pdf_path.string());
peer->output = pdf->Output("D", pdf_path.string());
//peer->output = pdf->move_result();
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return "";
}
}
#else
client << "<p>Please: cmake .. -DENABLE_PDF=ON </p>";
#endif// ENABLE_PDF
return "";
}
//@urlpath(null,test_ttfpdf)
std::string test_otfpdf(std::shared_ptr<httppeer> peer)
{
httppeer &client = peer->get_peer();
#ifdef ENABLE_PDF
server_loaclvar &static_server_var = get_server_global_var();
if (static_server_var.config_path.size() < 5)
{
client << "<p> static_server_var.config_path empty </p>";
return "";
}
std::string file_conf = dir_name(static_server_var.config_path);
if (file_conf.size() > 0 && file_conf.back() != '/')
{
file_conf.push_back('/');
}
file_conf.append("docs/");
if(peer->pathinfos.size() == 2 && peer->pathinfos[1] == "cn")
{
try
{
std::string html_file = file_conf + "webpdf_tutorial.html";
std::string pdf_file = file_conf + "webpdf_tutorial_ttf.pdf";
// OTF (bare CID-keyed CFF) subsetting path. content_otf.html uses
// the family name "SourceHanSerifSC-Light" directly.
std::string font_file = "AlibabaPuHuiTi-Light.ttf";
std::string font_name = "AlibabaPuHuiTi-Light";
fs::path html_path = fs::absolute(html_file);
fs::path pdf_path = fs::absolute(pdf_file);
fs::path font_dir = fs::absolute(file_conf + "font");
std::ifstream f(html_file, std::ios::binary);
if (!f.is_open())
{
client << "Cannot open file: " << html_file;
return "";
}
// 获取文件大小
f.seekg(0, std::ios::end);
std::streamsize size = f.tellg();
f.seekg(0, std::ios::beg);
std::string html;
html.resize(static_cast<size_t>(size));
if (size > 0 && !f.read(html.data(), size)) {
client << "Failed to read file: " << html_file;
return "";
}
auto pdf = std::make_unique<pz::webpdf>();
pdf->setImagesPath(file_conf);
pdf->AddPage();
pdf->AddFont(font_name, "", font_file, font_dir.string());
pdf->SetFont(font_name, "", 14);
// Resources (images, etc.) in the HTML are relative to the HTML file.
fs::current_path(html_path.parent_path());
pdf->WriteHTML(html);
peer->type("application/pdf");
pdf->Output("F", pdf_path.string());
peer->output = pdf->Output("D", pdf_path.string());
//peer->output = pdf->move_result();
} catch (const std::exception& e) {
client << "Error: " << e.what();
return "";
}
}
else
{
try
{
std::string html_file = file_conf + "webpdf_tutorial_en.html";
std::string pdf_file = file_conf + "webpdf_tutorial_en_ttf.pdf";
std::string font_file = "AlibabaSans-Regular.ttf";
std::string font_name = "AlibabaSans-Regular";
// Resolve everything to absolute paths up front, before we change the
// working directory to the HTML's location for relative asset lookups.
fs::path html_path = fs::absolute(html_file);
fs::path pdf_path = fs::absolute(pdf_file);
fs::path font_dir = fs::absolute(file_conf + "font");
std::ifstream f(html_file, std::ios::binary);
if (!f.is_open())
{
client << "Cannot open file: " << html_file;
return "";
}
// 获取文件大小
f.seekg(0, std::ios::end);
std::streamsize size = f.tellg();
f.seekg(0, std::ios::beg);
std::string html;
html.resize(static_cast<size_t>(size));
if (size > 0 && !f.read(html.data(), size)) {
client << "Failed to read file: " << html_file;
return "";
}
auto pdf = std::make_unique<pz::webpdf>();
pdf->setImagesPath(file_conf);
pdf->AddFont(font_name, "", font_file, font_dir.string());
pdf->SetFont(font_name, "", 14);
// Resources (images, etc.) in the HTML are relative to the HTML file.
fs::current_path(html_path.parent_path());
pdf->WriteHTML(html);
peer->type("application/pdf");
pdf->Output("F", pdf_path.string());
peer->output = pdf->Output("D", pdf_path.string());
//peer->output = pdf->move_result();
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return "";
}
}
#else
client << "<p>Please: cmake .. -DENABLE_PDF=ON </p>";
#endif// ENABLE_PDF
return "";
}
}// namespace http
excel xlsx读写程序例子
#include "httppeer.h"
#include "serverconfig.h"
#include "server_localvar.h"
#include "test_pzexcel.h"
#include "func.h"
#include <memory>
#include <string>
#ifdef ENABLE_EXCEL
#include "pzexcel.h"
#endif// ENABLE_EXCEL
namespace http
{
//@urlpath(null,test_pzexcel)
std::string test_pzexcel(std::shared_ptr<httppeer> peer)
{
httppeer &client = peer->get_peer();
#ifdef ENABLE_EXCEL
server_loaclvar &static_server_var = get_server_global_var();
if (static_server_var.config_path.size() < 5)
{
client << "<p> static_server_var.config_path empty </p>";
return "";
}
std::string file_conf = dir_name(static_server_var.config_path);
if (file_conf.size() > 0 && file_conf.back() != '/')
{
file_conf.push_back('/');
}
file_conf.append("docs/");
std::string zipfile;
zipfile = file_conf + "tt.xlsx";
client << "<p>=== Reading " << zipfile << " ===</p></p>";
pz::excel pza;
if (!pza.read(zipfile))
{
client << "<p>Read error: " << pza.error_msg << "</p>";
return "";
}
client << "<p>\n---------------------\n</p>";
client << "<p>pz_excel shared_strings size:" << pza.shared_strings.size() << "</p>";
client << "<p>\n---------------------\n</p>";
for (unsigned int i = 0; i < pza.shared_strings.size(); i++)
client << i << ":" << pza.shared_strings[i] << "</p>";
client << "<p>\n---------------------\n</p>";
client << "<p>pz_excel sheets size:" << pza.sheets.size() << "</p>";
client << "<p>\n---------------------\n</p><pre>";
for (unsigned int i = 0; i < pza.sheets.size(); i++)
{
client << i << ":" << pza.sheets[i].name;
client << i << ":" << pza.sheets[i].sheet_index;
client << i << "max_row:" << pza.sheets[i].rows;
client << i << "max_col:" << pza.sheets[i].cols;
}
client << "</pre><p>\n---------------------\n</p>";
for (auto &[first, second] : pza.workbook_mapfiles)
client << first << " " << second;
pza.setActiveSheet(1);
client << "<p>A1: " << pza.getCell<std::string>("A1") << "</p>";
client << "<p>B2: " << pza.getCell<std::string>("B2") << "</p>";
client << "<p>B4: " << pza.getCell<std::string>("B4") << "</p>";
client << "<p>C4: " << pza.getCell<std::string>("C4") << "</p>";
client << "<p>A5: " << pza.getCell<double>("A5") << "</p>";
client << "<p>B5: " << pza.getCell<double>("B5") << "</p>";
client << "<p>\n=== Writing qq.xlsx ===</p>";
pz::excel pzw;
pzw.setCell("A1", std::string("姓名"));
pzw.setCell("B1", std::string("年龄"));
pzw.setCell("C1", std::string("成绩"));
pzw.setCell("A2", std::string("张三"));
pzw.setCell("B2", 25);
pzw.setCell("C2", 95.5);
pzw.setCell("A3", std::string("李四"));
pzw.setCell("B3", 30);
pzw.setCell("C3", 88.75);
pzw.setCell("A4", std::string("王五"));
pzw.setCell("B4", 28);
pzw.setCell("C4", 92.0);
pzw.addSheet("Sheet2");
pzw.setActiveSheet(1);
pzw.setCell("A1", std::string("第二页数据"));
pzw.setCell("B2", 12345);
pzw.setCell("C3", 3.14159);
std::string qqfile = file_conf + "qq.xlsx";
if (pzw.write(qqfile))
{
client << "<p>Successfully wrote qq.xlsx</p>";
}
else
{
client << "<p>Write error: " << pzw.error_msg << "</p>";
return "";
}
client << "<p>\n=== Verifying qq.xlsx by reading back ===</p>";
pz::excel pzv;
if (pzv.read(qqfile))
{
client << "<p>Sheets: " << pzv.sheets.size() << "</p>";
for (unsigned int i = 0; i < pzv.sheets.size(); i++)
{
client << "<p>Sheet " << i + 1 << ": " << pzv.sheets[i].name << ", rows=" << pzv.sheets[i].rows << ", cols=" << pzv.sheets[i].cols << "</p>";
}
pzv.setActiveSheet(0);
client << "<p>A1: " << pzv.getCell<std::string>("A1") << "</p>";
client << "<p>B2: " << pzv.getCell<int>("B2") << "</p>";
client << "<p>C2: " << pzv.getCell<double>("C2") << "</p>";
pzv.setActiveSheet(1);
client << "<p>Sheet2 A1: " << pzv.getCell<std::string>("A1") << "</p>";
client << "<p>Sheet2 B2: " << pzv.getCell<int>("B2") << "</p>";
}
else
{
client << "<p>Verify error: " << pzv.error_msg << "</p>";
}
client << "<p>\n=== Reading mm.xlsx and writing mmm.xlsx with setCellColor ===</p>";
pz::excel pzm;
std::string mmfile = file_conf + "mm.xlsx";
if (pzm.read(mmfile))
{
client << "<p>Successfully read mm.xlsx" << "</p>";
client << "<p>Sheets: " << pzm.sheets.size() << "</p>";
client << "<p>Shared strings: " << pzm.shared_strings.size() << "</p>";
client << "<p>Fonts count: " << pzm.fonts.size() << "</p>";
client << "<p>Styles count: " << pzm.styles.size() << "</p>";
pzm.setActiveSheet(0);
client << "<p>A1: " << pzm.getCell<std::string>("A1") << "</p>";
client << "<p>B2: " << pzm.getCell<std::string>("B2") << "</p>";
pzm.setCellColor("A1", "FFFF0000");
pzm.setCellColor("B2", "FF00FF00");
pzm.setCellColor("B4", "FF0000FF");
pzm.setCellColor("C4", "FFFF2278");
std::string mmmfile = file_conf + "mmm.xlsx";
if (pzm.write(mmmfile))
{
client << "<p>Successfully wrote mmm.xlsx" << "</p>";
pz::excel pzverify;
if (pzverify.read(mmmfile))
{
client << "<p>Verified mmm.xlsx read back ok</p>";
client << "<p>Fonts count: " << pzverify.fonts.size() << "</p>";
client << "<p>Styles count: " << pzverify.styles.size() << "</p>";
}
}
else
{
client << "<p>Write mmm.xlsx error: " << pzm.error_msg << "</p>";
}
}
else
{
client << "<p>Read mm.xlsx error: " << pzm.error_msg << "</p>";
}
client << "<p>\n=== Reading kk.xlsx (LibreOffice) and writing kk_out.xlsx ===</p>";
{
pz::excel pzk;
std::string kkfile = file_conf + "kk.xlsx";
if (pzk.read(kkfile))
{
client << "<p>Read kk.xlsx OK, shared strings: " << pzk.shared_strings.size() << "</p>";
client << "<p>A1: " << pzk.getCell<std::string>("A1") << "</p>";
client << "<p>B2: " << pzk.getCell<std::string>("B2") << "</p>";
client << "<p>B4: " << pzk.getCell<std::string>("B4") << "</p>";
client << "<p>C4: " << pzk.getCell<std::string>("C4") << "</p>";
client << "<p>A5: " << pzk.getCell<double>("A5") << "</p>";
client << "<p>B5: " << pzk.getCell<double>("B5") << "</p>";
pzk.setCellColor("A1", "FF0000");
pzk.setCellColor("B4", "0000FF");
std::string kkoutfile = file_conf + "kk_out.xlsx";
if (pzk.write(kkoutfile))
{
client << "<p>Wrote kk_out.xlsx OK</p>";
pz::excel v;
if (v.read(kkoutfile))
{
client << "<p>Read back kk_out.xlsx OK</p>";
client << "<p>A1: " << v.getCell<std::string>("A1") << "</p>";
client << "<p>B4: " << v.getCell<std::string>("B4") << "</p>";
client << "<p>Fonts with color: </p>";
for (size_t i = 0; i < v.fonts.size(); i++)
{
if (v.fonts[i].color_type == 10)
client << "<p>[" << v.fonts[i].color << "] </p>";
}
}
}
}
else
{
client << "<p>Read kk.xlsx error: " << pzk.error_msg << "</p>";
}
}
#else
client << "<p>Please: cmake .. -DENABLE_EXCEL=ON </p>";
#endif// ENABLE_EXCEL
return "";
}
}// namespace http
二维码生成例子
#include <chrono>
#include <thread>
#include "httppeer.h"
#include <filesystem>
#ifdef ENABLE_IMAGE
#include "qrcode.h"
#include "pzpng.h"
#endif
#include "testqrcode.h"
namespace http
{
//@urlpath(null,testqrcode)
std::string testqrcode(std::shared_ptr<httppeer> peer)
{
httppeer &client = peer->get_peer();
std::string atxt = client.post["qrcontent"].to_string();
if (atxt.size() == 0)
{
atxt = "qrsting hello world !";
}
client << "<p>" << atxt << "</p>";
#ifdef ENABLE_IMAGE
namespace fs = std::filesystem;
qr::qrcode q;
q.text(atxt, qr::Ecc::M, 1);
image::png img;
unsigned char scale = 10;
img.create(q.width()*scale + 40, q.height()*scale + 40, 6, 8);
img.fillColor({255, 255, 255, 255});
img.qrdata(q.data,q.width(),q.height(),scale, 20 ,20);
std::string wwwpath;
wwwpath.append(client.get_sitepath());
wwwpath.append("/upload");
fs::path paths = wwwpath;
bool is_success = fs::create_directories(paths);
if (is_success)
{
fs::permissions(paths,
fs::perms::owner_all | fs::perms::group_all | fs::perms::others_read,
fs::perm_options::add);
}
wwwpath.append("/test.png");
// std::string wwwurl = client.get_hosturl();
img.save(wwwpath);
client << "文字 : " << atxt << '\n';
client << "版本 : " << q.version << '\n';
client << "黑点大小 : " << q.scale << '\n';
client << "尺寸 : " << q.width() << " x " << q.height() << '\n';
client << "q.data 长度: " << q.data.size() << " (= width*height)\n\n";
//client.output = img.imshow();
client << "<img src=\"" << "/upload/test.png" << "?token=123456\">";
#endif
client << "<form method=\"post\" action=\"/testqrcode\"><p><textarea name=\"qrcontent\"></textarea></p><p><input type=\"submit\" name=\"submit2\" value=\"Submit\"></p></form>";
return "";
}
}// namespace http
1.特性🔥🔥🔥🔥🔥
✅ 1. 自带json编解码不用第三方库,标准json支持
✅ 2. 支持多域名网站
✅ 3. 支持多域名ssl 服务端
✅ 4. 支持http/1.1、http/2协议
✅ 5. 支持WebSocket服务端和客户端,双工收发
✅ 6. 框架自带WebSocket推送,支持定时推送到WebSocket客户端
✅ 7. 支持httpclient get post,同步异步、协程模式,数据采集
✅ 8. 框架自带ORM,使用链接池方式,目前支持MySQL,支持协程和同步模式
✅ 9. 框架自带线程池,和用户代码运行的线程池
✅10. 框架使用asio自带的协程
✅11. 框架特色是I/O 使用协程池 用户代码运行使用线程池,类似GO那种调度,只是针对http请求调度
✅12. 框架支持普通文件gzip、br,并支持缓存到磁盘,下次不用cpu再压缩
✅13. URL、POST和上传文件,解析结果client.get[] client.post[] client.files[]方式获取内容
✅14. 自带sendmail类库
✅15. 生成二维码(qrcode),需要gd、qrencode库
✅16. 插件化编程,热动态更新,使用动态库方式
✅17. 框架内置通用数据缓存模块,ORM结果缓存,提高并发能力
✅18. 框架controller目录注解功能,方便添加URL路由映射,降低入门心智
✅19. 结构和类注解JSON功能,使用json_encode json_decode操作复杂C++结构体和JSON互转,可以参考Wiki
✅20. 提供一个完整admin后台管理框架(见后面图片), 访问URL为 /admin/main
✅21. 支持PHP-FPM fastcgi运行模式,代替Apache做PHP前端,让PHP程序员平稳过渡到 C++ 开发模式
✅22. 内置微信小程序支付功能
✅23. 支持Socket服务端和客户端,支持Socket ssl连接
✅24. 支持RPC服务端和客户端,使用http注册的URL函数
✅25. 框架支持流量限制,60秒内平滑移动平均线二段式流量限制,配置参数在server.conf
✅26. 框架内置ACME协议,自动续订SSL证书并生效
✅27. 整合服务器端OCSP证书装订
✅28. 简单的Excel文件xlsx读写支持
✅29. 内置图片生成和处理,支持jpg png两个类
✅30. 内置HTML生成pdf模块webpdf(本项目子项目)
https://github.com/hggq/paozhu