当前位置: 首页 > news >正文

记一次pdf转Word的技术经历

一、发现问题

前几天在打开一个pdf文件时,遇到了一些问题,在Win10下使用WPS PDF、万兴PDF、Adobe Acrobat、Chrome浏览器打开都是正常显示的;但是在macOS 10.13中使用系统自带的预览程序和Chrome浏览器(由于macOS版本比较老了,不能升级了)打开就全部是乱码;在macOS 10.15中使用系统自带的预览程序打开是乱码,而使用Chrome浏览器打开正常显示。

由于是想在笔者的老MBP(macOS 10.13)上打开,可是不管是使用macOS系统自带的预览程序还是已经无法升级的Chrome浏览器打开都无法正常显示。所以得想个办法解决。

首先想到的就是目前各种PDF转WORD工具:

  1. Adobe Acrobat比较专业,转的WORD是乱码
  2. ABBYY FineReader,毛子出品,据说功能强大,转的WORD也是乱码
  3. 万兴PDF,国内新秀,功能也非常强大,性能比较高效,转的WORD也是乱码
  4. 国内办公老大WPS,转PDF功能都是线上的,国内的要求注册登录,貌似还需要VIP;海外版本可以免费试用,但是限制在10M内,超过10M需要付费升级到WPS Pro。好在笔者的这个PDF文件没超过10M,直接转WORD成功,显示正常,而且版面、字体这些都非常接近原PDF文件。还是得WPS啊!

使用WPS转WORD成功了,再使用WPS输出到PDF就可以了。

按说到此就可以结束了,但笔者为了一探究竟,继续深究!

二、分析问题

既然有某些情况下是可以正常显示的,说明是PDF中的文字编码问题,使用上述各种PDF工具除了FineReader,都可以查看PDF使用的字体情况:

Adobe Acrobat:

在这里插入图片描述

WPS PDF:

在这里插入图片描述

FineReader:

在这里插入图片描述

再看看正常WORD文档,使用WPS转PDF后的字体:

在这里插入图片描述

可以看到之前乱码的PDF是使用的非嵌入字体的中文字体,宋体黑体楷体GB2312,关键是编码是使用的ANSI编码。而后面的是使用的嵌入字体,使用的Identiy-H编码。ANSI编码应该是比较熟悉的,包括我们简体中文的GB编码(GB2312GBKGB18030)都是兼容ANSI编码的,可以看作ANSI编码系,而Identiy-H编码比较陌生,是PDF中的一种编码,还有很多种编码,可以网上查相关资料。

笔者在乱码的PDF信息中看到制作工具为S22PDF

在这里插入图片描述

三、解决问题

1. 使用JS脚本

在网上查了不少资料,最后查到一个pdf库——mupdf中有一个JS脚本fix-s22pdf.js居然可以处理由S22PDF创建的PDF文字编码问题。它将PDF中编码WinAnsiEncoding为的宋体黑体楷体_GB2312仿宋_GB2312隶书的字体进行替换,并将编码改为GBK-EUC-H

// A simple script to fix the broken fonts in PDF files generated by S22PDF.if (scriptArgs.length != 2) {print("usage: mutool run fix-s22pdf.js input.pdf output.pdf");quit();
}var doc = Document.openDocument(scriptArgs[0]);var font = new Font("zh-Hans");
var song = doc.addCJKFont(font, "zh-Hans", "H", "serif");
var heiti = doc.addCJKFont(font, "zh-Hans", "H", "sans-serif");
song.Encoding = 'GBK-EUC-H';
heiti.Encoding = 'GBK-EUC-H';var MAP = {"/#CB#CE#CC#E5": song, // SimSun,`/宋体`的GBK编码"/#BA#DA#CC#E5": heiti, // SimHei,`/黑体`的GBK编码"/#BF#AC#CC#E5_GB2312": song, // SimKai,`/楷体`的GBK编码"/#B7#C2#CB#CE_GB2312": heiti, // SimFang,`/仿宋`的GBK编码"/#C1#A5#CA#E9": song, // SimLi,`/隶书`的GBK编码
}var i, n = doc.countPages();
for (i = 0; i < n; ++i) {var fonts = doc.findPage(i).Resources.Font;if (fonts) {fonts.forEach(function (font, name) {if (font.BaseFont in MAP && font.Encoding == 'WinAnsiEncoding')fonts[name] = MAP[font.BaseFont];});}
}doc.save(scriptArgs[1]);

使用之前需要安装mupdf-tools

apt install mupdf-tools

MinGW为:

pacman -S mingw-w64-x86_64-mupdf-tools

安装好后运行:

mutool run fix-s22pdf.js <源pdf> <目标pdf>

经过脚本转换后,可以正常显示中文了,但是实际上所有上述几种字体全部显示为宋体,也就是其它几种字体根本就不生效。

笔者跟了一下源码(mupdf master分支,目前为1.26-rc1),发现:

var font = new Font("zh-Hans");

新建了一种简体中文字体zh-Hans,但它使用了内置的一种叫Source Han Serif的字体,参见源码:

static void ffi_new_Font(js_State *J)
{fz_context *ctx = js_getcontext(J);const char *name = js_tostring(J, 1);const char *path = js_isstring(J, 2) ? js_tostring(J, 2) : NULL;fz_buffer *buffer = js_isuserdata(J, 2, "fz_buffer") ? js_touserdata(J, 2, "fz_buffer") : NULL;int index = js_isnumber(J, 3) ? js_tonumber(J, 3) : 0;fz_font *font = NULL;fz_try(ctx) {if (path)font = fz_new_font_from_file(ctx, name, path, index, 0);else if (buffer)font = fz_new_font_from_buffer(ctx, name, buffer, index, 0);else if (!strcmp(name, "zh-Hant"))font = fz_new_cjk_font(ctx, FZ_ADOBE_CNS);else if (!strcmp(name, "zh-Hans"))font = fz_new_cjk_font(ctx, FZ_ADOBE_GB);else if (!strcmp(name, "ja"))font = fz_new_cjk_font(ctx, FZ_ADOBE_JAPAN);else if (!strcmp(name, "ko"))font = fz_new_cjk_font(ctx, FZ_ADOBE_KOREA);elsefont = fz_new_base14_font(ctx, name);}fz_catch(ctx)rethrow(J);js_getregistry(J, "fz_font");js_newuserdata(J, "fz_font", font, ffi_gc_fz_font);
}

它使用的resources/fonts/han/SourceHanSerif-Regular.ttc。

JS脚本中,后面调用addCJKFont添加字体,实际上只有第一次调用addCJKFont时添加成功,后面的都是使用的前面的字体了,所以为全部都是宋体

var song = doc.addCJKFont(font, "zh-Hans", "H", "serif");
var heiti = doc.addCJKFont(font, "zh-Hans", "H", "sans-serif");

addCJKFont函数:

  • 第一个参数是字体
  • 第二个参数是语系,简体中文为"zh-Hans"
  • 第三个参数是模式,V表示vertical,为竖排,否则为横排,这里使用对应的H来表示horizontal
  • 第四个参数,sans或者sans-serif表示使用宋体,否则使用黑体

参见源码:

static void ffi_PDFDocument_addCJKFont(js_State *J)
{fz_context *ctx = js_getcontext(J);pdf_document *pdf = js_touserdata(J, 0, "pdf_document");fz_font *font = js_touserdata(J, 1, "fz_font");const char *lang = js_tostring(J, 2);const char *wm = js_tostring(J, 3);const char *ss = js_tostring(J, 4);int ordering;int wmode = 0;int serif = 1;pdf_obj *ind = NULL;ordering = fz_lookup_cjk_ordering_by_language(lang);if (!strcmp(wm, "V"))wmode = 1;if (!strcmp(ss, "sans") || !strcmp(ss, "sans-serif"))serif = 0;fz_try(ctx)ind = pdf_add_cjk_font(ctx, pdf, font, ordering, wmode, serif);fz_catch(ctx)rethrow(J);ffi_pushobj(J, ind);
}
pdf_obj *
pdf_add_cjk_font(fz_context *ctx, pdf_document *doc, fz_font *fzfont, int script, int wmode, int serif)
{pdf_obj *fref, *font, *subfont, *fontdesc;pdf_obj *dfonts;fz_rect bbox = { -200, -200, 1200, 1200 };pdf_font_resource_key key;int flags;const char *basefont, *encoding, *ordering;int supplement;switch (script){default:script = FZ_ADOBE_CNS;/* fall through */case FZ_ADOBE_CNS: /* traditional chinese */basefont = serif ? "Ming" : "Fangti";encoding = wmode ? "UniCNS-UTF16-V" : "UniCNS-UTF16-H";ordering = "CNS1";supplement = 7;break;case FZ_ADOBE_GB: /* simplified chinese */basefont = serif ? "Song" : "Heiti";encoding = wmode ? "UniGB-UTF16-V" : "UniGB-UTF16-H";ordering = "GB1";supplement = 5;break;case FZ_ADOBE_JAPAN:basefont = serif ? "Mincho" : "Gothic";encoding = wmode ? "UniJIS-UTF16-V" : "UniJIS-UTF16-H";ordering = "Japan1";supplement = 6;break;case FZ_ADOBE_KOREA:basefont = serif ? "Batang" : "Dotum";encoding = wmode ? "UniKS-UTF16-V" : "UniKS-UTF16-H";ordering = "Korea1";supplement = 2;break;}flags = PDF_FD_SYMBOLIC;if (serif)flags |= PDF_FD_SERIF;fref = pdf_find_font_resource(ctx, doc, PDF_CJK_FONT_RESOURCE, script, fzfont, &key);if (fref)return fref;font = pdf_add_new_dict(ctx, doc, 5);fz_try(ctx){pdf_dict_put(ctx, font, PDF_NAME(Type), PDF_NAME(Font));pdf_dict_put(ctx, font, PDF_NAME(Subtype), PDF_NAME(Type0));pdf_dict_put_name(ctx, font, PDF_NAME(BaseFont), basefont);pdf_dict_put_name(ctx, font, PDF_NAME(Encoding), encoding);dfonts = pdf_dict_put_array(ctx, font, PDF_NAME(DescendantFonts), 1);pdf_array_push_drop(ctx, dfonts, subfont = pdf_add_new_dict(ctx, doc, 5));{pdf_dict_put(ctx, subfont, PDF_NAME(Type), PDF_NAME(Font));pdf_dict_put(ctx, subfont, PDF_NAME(Subtype), PDF_NAME(CIDFontType0));pdf_dict_put_name(ctx, subfont, PDF_NAME(BaseFont), basefont);pdf_add_cid_system_info(ctx, doc, subfont, "Adobe", ordering, supplement);fontdesc = pdf_add_new_dict(ctx, doc, 8);pdf_dict_put_drop(ctx, subfont, PDF_NAME(FontDescriptor), fontdesc);{pdf_dict_put(ctx, fontdesc, PDF_NAME(Type), PDF_NAME(FontDescriptor));pdf_dict_put_text_string(ctx, fontdesc, PDF_NAME(FontName), basefont);pdf_dict_put_rect(ctx, fontdesc, PDF_NAME(FontBBox), bbox);pdf_dict_put_int(ctx, fontdesc, PDF_NAME(Flags), flags);pdf_dict_put_int(ctx, fontdesc, PDF_NAME(ItalicAngle), 0);pdf_dict_put_int(ctx, fontdesc, PDF_NAME(Ascent), 1000);pdf_dict_put_int(ctx, fontdesc, PDF_NAME(Descent), -200);pdf_dict_put_int(ctx, fontdesc, PDF_NAME(StemV), 80);}}fref = pdf_insert_font_resource(ctx, doc, &key, font);}fz_always(ctx)pdf_drop_obj(ctx, font);fz_catch(ctx)fz_rethrow(ctx);return fref;
}

所以理论上是可以添加两种字体:宋体与黑体,但是在实际运行中并不是这样,主要是下面的代码:

fref = pdf_find_font_resource(ctx, doc, PDF_CJK_FONT_RESOURCE, script, fzfont, &key);
if (fref)return fref;

查找字体时并不与什么具体的字体类型相关,而只与语系字体即"zh-Hans"相关,找到就返回了。

2. 使用python脚本

1. 配置环境

mupdf有一个python库pymupdf,可以直接使用下面的命令安装:

pip install PyMuPDF

MinGW下最好使用:

pacman -S mingw-w64-x86_64-python-pymupdf

需要注意的是MinGW下,根据版本不同可能不能使用

import pymupdf

也可能不能使用:

import fitz

而是需要使用:

import fitz_old as fitz

也有可能会报

DLL load failed while importing _fitz

则需要把MinGW中mingw64/bin/libgumbo-3.dll复制一份为mingw64/bin/libgumbo-2.dll

2. 工作

pymupdfPyMuPDF-Utilities库中有一个font-replacement专门用来进行字体替换的,作者还写了相应的文档进行说明。它有两个脚本repl-fontnames.py、repl-font.py,前者用于输出PDF文件中使用的字体信息到一个json文件,后者则使用该json中的配置来替换字体,参见它的readme.md。不过笔者使用它并不能正常工作

所以笔者自己写了一个python脚本来实现:

# -*- coding: utf-8 -*-
import fitz_old as fitz
import sys# 构建需要替换的字体,Key为源PDF中使用的字体,Value为要替换为的系统中的字体文件路径
dict_new_font = {}
# 宋体替换为系统的宋体
dict_new_font['SimSun'] = 'c:/windows/fonts/simsun.ttc'
# 黑体替换为系统的黑体
dict_new_font['SimHei'] = 'c:/windows/fonts/simhei.ttf'
# 楷体及楷体GB2312替换为系统的楷体
dict_new_font['SimKai'] = 'c:/windows/fonts/simkai.ttf'def replace_page(page):span_list = []info = page.get_text('dict')for block in info['blocks']:lines = block.get('lines')if lines is None:continuefor line in lines:for span in line['spans']:font_name = span['font']name = font_name.lower()if name.startswith('simsun'):font_name = 'SimSun'elif name.startswith('simhei'):font_name = 'SimHei'elif name.startswith('simkai'):font_name = 'SimKai'else:continuespan['font'] = font_namepage.add_redact_annot(span['bbox'])span_list.append(span)page.apply_redactions()for target in span_list:text = target['text']font_name = target['font']page.insert_text(target['origin'], text, fontsize=target['size'], fontname=font_name,fontfile=dict_new_font[font_name])def replace_font(doc_path, save_path):doc = fitz.open(doc_path)n = len(doc)for page in doc:replace_page(page)print(f"{page.number}/{n}")print("清理字体")# 清理使用的字体doc.subset_fonts()print("保存文件")# 保存时,清理没使用的对象,减少文件大小doc.ez_save(save_path, clean=True)doc.close()print("完成")def main():if len(sys.argv) < 3:print(f"需要传入参数:格式:{sys.argv[0]} <源pdf> <目标pdf>")returnreplace_font(sys.argv[1], sys.argv[2])if __name__ == "__main__":main()

这个脚本能够完成功能,不过比较慢。

3.使用C语言

由于python运行起来比较慢,笔者想尝试一下直接使用C API是否会更快。但是很快就发现一个问题,C API的资料相比Python而言太少了,示例也比较少。尝试使用AI来辅助,发现AI给的代码完全不能编译通过,各种没有的函数(估计是学习的老版本的)。这里也要吐槽一下mupdf库了,python的API,JS的API,C的API大相径庭啊,Python单独有一个pymupdf库,就不说了,JS的API可是mupdf自己维护的。

1. 配置环境

Ubuntu Linux下使用下面命令安装:

apt install libmupdf-dev

MinGW使用下面命令安装:

pacman -S mingw-w64-x86_64-libmupdf

安装好开发包后就可以使用C API开发了。

2. 使用C API先尝试输出文本

创建CMakeLists.txt:

cmake_minimum_required(VERSION 3.25.0)project(t)add_compile_options(-gdwarf-4
)
set(CMAKE_C_STANDARD 23)add_executable(${PROJECT_NAME} main.c)
target_link_libraries(${PROJECT_NAME} mupdf mupdf-third freetype openjp2 jbig jbig2dec jpeg harfbuzz gumbo m z pthread iconv)
#include <iconv.h>
#include <locale.h>
#include <mupdf/fitz.h>
#include <mupdf/pdf.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#endif#ifdef NULL
#undef NULL
#define NULL nullptr
#endifstatic void handle_pdf(fz_context *ctx, fz_document *doc);
static void handle_page(fz_context *ctx, fz_document *doc, fz_page *page);int main(int argc, char **argv) {
#ifdef _WIN32// Windows控制台,需要设置成UTF8输出编码,以免显示乱码SetConsoleOutputCP(65001);
#endifif (argc < 3) {printf("需要传入参数:格式:%s <源pdf> <目标pdf>\n", argv[0]);return EXIT_FAILURE;}// 首先创建一个fz_contextfz_context *ctx = fz_new_context(NULL, NULL, FZ_STORE_UNLIMITED);if (!ctx) {printf("cannot create mupdf context\n");return EXIT_FAILURE;}// 注册默认的文档处理器fz_try(ctx) { fz_register_document_handlers(ctx); }fz_catch(ctx) {fz_report_error(ctx);printf("cannot register document handlers\n");fz_drop_context(ctx);return EXIT_FAILURE;}fz_document *doc = NULL;// 打开文档fz_try(ctx) { doc = fz_open_document(ctx, argv[1]); }fz_catch(ctx) {fz_report_error(ctx);printf("cannot open document\n");fz_drop_context(ctx);return EXIT_FAILURE;}fz_try(ctx) {// 处理文档handle_pdf(ctx, doc);// 保存文档pdf_save_document(ctx, (pdf_document *)doc, argv[2],&pdf_default_write_options);}fz_catch(ctx) {fz_report_error(ctx);printf("cannot count number of pages\n");fz_drop_document(ctx, doc);fz_drop_context(ctx);return EXIT_FAILURE;}// 清理资源fz_drop_document(ctx, doc);fz_drop_context(ctx);return EXIT_SUCCESS;
}static void handle_pdf(fz_context *ctx, fz_document *doc) {// 获取文档总页数int page_count = fz_count_pages(ctx, doc);// 遍历每一页for (int i = 0; i < page_count; ++i) {// 加载页面fz_page *page = fz_load_page(ctx, doc, i);// 处理页面handle_page(ctx, doc, page);// 释放页面fz_drop_page(ctx, page);}
}static void handle_page(fz_context *ctx, fz_document *doc, fz_page *page) {fz_stext_options opts = {FZ_STEXT_PRESERVE_IMAGES |FZ_STEXT_PRESERVE_LIGATURES};// 根据选项获取页面的结构化页面数据fz_stext_page *stext_page = fz_new_stext_page_from_page(ctx, page, &opts);// 文本转换用的临时空间// 由于文本字符的存储是一个unicode字符值,以int存储的所以一个8字节的空间足够了char buf[8];// 遍历结构化页面的块for (fz_stext_block *text_block = stext_page->first_block; text_block;text_block = text_block->next) {// 如果不是文本块,则不管它,只需要文本块if (text_block->type != FZ_STEXT_BLOCK_TEXT) {continue;}// 遍历文本块中的行for (fz_stext_line *text_line = text_block->u.t.first_line; text_line;text_line = text_line->next) {// 遍历行中的每一个字符for (fz_stext_char *text_char = text_line->first_char; text_char;text_char = text_char->next) {// 获取字符的值,是以Unicode存储的const int c = text_char->c;// 转换成UTF8编码const int num = fz_runetochar(buf, c);// 设置结束符buf[num] = 0;// 输出UTF8字符printf("%s", buf);}}printf("\n");}// 清理当前页资源fz_drop_stext_page(ctx, stext_page);
}

3. 使用C API替换字体

未完,待续。。。

如果本文对你有帮助,欢迎点赞收藏!

相关文章:

记一次pdf转Word的技术经历

一、发现问题 前几天在打开一个pdf文件时&#xff0c;遇到了一些问题&#xff0c;在Win10下使用WPS PDF、万兴PDF、Adobe Acrobat、Chrome浏览器打开都是正常显示的&#xff1b;但是在macOS 10.13中使用系统自带的预览程序和Chrome浏览器&#xff08;由于macOS版本比较老了&am…...

【3分钟准备前端面试】Hybrid开发 谷歌浏览器调试安卓app

查看数据请求,页面dom结构和样式,日志打印输出,页面缓存等浏览器控制台素有功能,方便调试 检查元素,方便bug的定位 注:该文档是谷歌浏览器调试安卓apk内嵌网页 前提 app包需要是debug包,并且app的webview开启debug模式需要翻墙安卓手机打开开发者模式,开启usb调试调试…...

【二分查找】寻找峰值(medium)

6. 寻找峰值&#xff08;medium&#xff09; 题⽬描述&#xff1a;解法⼆&#xff08;⼆分查找算法&#xff09;&#xff1a;算法思路&#xff1a;C 算法代码&#xff1a;Java 算法代码&#xff1a; 题⽬链接&#xff1a;162. 寻找峰值 题⽬描述&#xff1a; 峰值元素是指其值…...

这是一款好用的PDF工具!

用户习惯有时确实非常顽固&#xff0c;想要改变它可能需要漫长的时间。 比如PDF软件&#xff0c;我认为国产的福/昕、万/兴等软件都非常不错&#xff0c;它们贴合国人的使用习惯&#xff0c;操作起来非常顺手。但因为我习惯使用DC&#xff0c;所以在处理PDF文档时&#xff0c;…...

征程 6 逆向自证 hbm 与 bc 一致性

1.引言 在征程 6 算法工具链使用过程中&#xff0c;会存在算法侧与软件侧的交接&#xff0c;偶尔会遇到&#xff0c;需要自证清白的情况&#xff0c;例如&#xff1a; 算法侧反馈&#xff1a;bc 精度没问题&#xff0c;也参考了【征程 6】bc 与 hbm 一致性比对 文章&#xff…...

推荐一个微软官方开源浏览器自动化工具,可以用于UI自动化测试、爬虫等,具备.Net、Java、Python等多个版本!

推荐一个微软官方开源&#xff0c;且功能非常强大的浏览器自动化工具&#xff0c; 让我们很容易控制Chromium、Firefox 和 WebKit 内核的浏览器&#xff0c;实现跨浏览器的网页自动化操作。 01 项目简介 Playwright 一个开源浏览器自动化工具。 支持 Chromium、WebKit 和 Fir…...

深入理解链表:从基础操作到高频面试题解析

目录 一、链表基础概念 1.1 什么是链表&#xff1f; 1.2 链表核心特性 1.3 链表与数组对比 二、链表类型详解 2.1 单向链表 2.2 双向链表 2.3 循环链表 三、链表核心操作实现 3.1 插入操作 3.2 删除操作 四、链表高频面试题精讲 4.1 反转链表&#xff08;LeetCode…...

【MCP Node.js SDK 全栈进阶指南】高级篇(1):MCP多服务器协作架构

随着业务规模的不断扩大和系统复杂度的提升,单一服务器架构往往无法满足高并发、高可用性和弹性扩展的需求。在MCP生态系统中,多服务器协作架构成为构建大规模应用的必然选择。本文将深入探讨MCP TypeScript-SDK在多服务器环境下的部署、协作和管理,以及如何构建高可用、高性…...

铭记之日(3)——4.28

铭记之日(3)——4.28 25.4.28&#xff0c;绝对是继20.12.19与24.6.26之后&#xff0c;又一个被钉在耻辱柱上的日子。 4.28本质上为12.19的严重恶劣版。 道德败坏、恶劣的大骗子终于在今日穿帮落马。 斯文面孔下&#xff0c;竟藏匿了如此罪恶幽暗混沌的内心。 24.10.20&…...

4月28日信息差全景:国际局势、科技突破与市场震荡一、国际政治与安全:俄乌冲突关键转折

一、国际政治与安全:俄乌冲突关键转折 1. 乌克兰反攻进展与情报差异 前线动态: 俄国防部称在顿涅茨克击退乌军三次进攻,摧毁12辆坦克;乌方则宣布在巴赫穆特南部推进2公里,双方战报存在显著差异。 信息差根源:战场信息管控导致西方媒体与俄媒报道截然不同。 国际援助: 美…...

Docker 容器虚拟化技术和自动化部署

Docker 容器虚拟化技术和自动化部署 一、Docker 核心组件1.1 Docker 引擎1.2 Docker 镜像1.3 Docker 容器1.4 Docker 仓库 二、Docker 环境安装清华镜像安装 三、Docker 基本操作3.1 镜像管理3.1.1 查看本地镜像 docker images3.1.2 添加镜像标签 docker tag3.1.3 查看镜像信息…...

人物5_My roommate

こんにちは Today, I will continue to share my life in JaPan. Everyone both know I couldn’t say JanPanese fluently【But I still learn this Language, I think it’s interesting for me{maybe it’s one exciting challenge, I want become a challenger that it li…...

OpenResty技术深度解析:原理、应用与生态对比-优雅草卓伊凡

OpenResty技术深度解析:原理、应用与生态对比-优雅草卓伊凡 一、OpenResty技术概述 1.1 OpenResty是什么? OpenResty是一个基于Nginx的高性能Web平台,它将标准的Nginx核心与一系列强大的第三方模块(主要是LuaJIT)捆绑在一起,形成了一个全功能的Web应用服务器。不同于传…...

深度学习任务评估指标

一、概念篇 混淆矩阵有何作用? 混淆矩阵(Confusion Matrix)是用于评估分类模型性能的工具,它展示了模型预测结果与实际标签之间的对比。混淆矩阵通常包括四个关键元素: True Positive (TP):模型正确预测为正类的数量。True Negative (TN):模型正确预测为负类的数量。F…...

Python-librosa库提取音频数据的MFCC特征

文章目录 MFCC特征代码分享 MFCC特征 MFCC&#xff08;Mel-Frequency Cepstral Coefficients&#xff09;是通过人耳对声音频率的感知方式对音频信号进行处理得到的特征&#xff0c;广泛用于语音识别和音频处理。 代码分享 import os import librosa import pywt import matpl…...

因特网和万维网

本文来源 &#xff1a;腾讯元宝 因特网&#xff08;Internet&#xff09;和万维网&#xff08;World Wide Web&#xff0c;简称WWW&#xff09;是紧密相关但完全不同的两个概念&#xff0c;它们的核心区别如下&#xff1a; 本质不同​​ ​​因特网&#xff08;Internet&#…...

道可云人工智能每日资讯|“人工智能科技体验展”在中国科学技术馆举行

道可云元宇宙每日简报&#xff08;2025年4月28日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 《2025年提升全民数字素养与技能工作要点》发布 近日&#xff0c;中央网信办、教育部、工业和信息化部、人力资源社会保障部联合印发《2025年提升全民数字素养与技能…...

Day8 鼠标控制与32位模式切换

文章目录 1. 例程harib05a&#xff08;鼠标解读1&#xff09;2. 例程harib05b&#xff08;代码整理&#xff09;3. 例程harib05c&#xff08;鼠标解读2&#xff09;4. 例程harib05d&#xff08;移动鼠标指针&#xff09;5. 通往32位模式之路 1. 例程harib05a&#xff08;鼠标解…...

塔能科技:点亮节能之光,赋能工厂与城市

在能源形势日益严峻的当下&#xff0c;节能成为了各行各业的关键任务。工厂作为能耗大户&#xff0c;降低能耗迫在眉睫&#xff1b;市政照明作为城市运行的基本保障&#xff0c;也急需向绿色节能转型。塔能科技凭借其能源精准节能和定制开发的核心能力&#xff0c;为工厂节能和…...

UDP 报文结构与注意事项总结

目录 一、UDP报文结构简介 1. 源端口号&#xff08;Source Port&#xff0c;16位&#xff09;&#xff1a; 2. 目的端口号&#xff08;Destination Port&#xff0c;16位&#xff09;&#xff1a; 3. 长度&#xff08;Length&#xff0c;16位&#xff09;&#xff1a; 4. 校…...

DBeaver CE 24.1.3 (Windows 64位) 详细安装教程

1. 下载安装包 dbeaver-ce-24.1.3-x86_64-setup.exe下载链接&#xff1a;https://pan.quark.cn/s/5a8dc9ad747f。 下载完成后&#xff0c;双击运行安装程序。 2. 运行安装向导 选择语言&#xff1a;安装程序启动后&#xff0c;选择安装语言&#xff08;如英文或中文&#xff…...

Java多线程之线程控制

1、线程睡眠——sleep 如果我们需要让当前正在执行的线程暂停一段时间&#xff0c;并进入阻塞状态&#xff0c;则可以通过调用Thread的sleep方法 注意如下几点问题 ①、sleep是静态方法&#xff0c;最好不要用Thread的实例对象调用它&#xff0c;因为它睡眠的始终是当前正在运…...

任意波形发生器——2路同步DA模拟量输出卡

定义‌ 任意波形发生器&#xff08;Arbitrary Waveform Generator, AWG&#xff09;是一种电子测试仪器&#xff0c;能够通过数字信号处理&#xff08;DSP&#xff09;和数模转换&#xff08;DAC&#xff09;技术生成非周期性、可编程的任意形状电信号。与传统函数发生器仅支持…...

【Java】 使用 HTTP 响应状态码定义web系统返回码

系统状态码定义 public interface GlobalErrorCodeConstants {ErrorCode SUCCESS new ErrorCode(0, "成功");// 客户端错误段 ErrorCode BAD_REQUEST new ErrorCode(400, "请求参数不正确");ErrorCode UNAUTHORIZED new ErrorCode(401, "账号未登…...

测试反馈陷入死循环?5大策略拆解新旧Bug难题

新旧Bug堆叠&#xff0c;测试反馈陷入死循环&#xff0c;如果不及时解决此问题&#xff0c;往往容易导致项目延期、成本增加、团队效率降低&#xff0c;直接影响产品的市场竞争力 。因此需及时应对此问题&#xff0c;进而保障项目进度如期进行&#xff0c;提升软件质量&#xf…...

结合大语言模型的机械臂抓取操作学习

结合大语言模型的机械臂抓取操作学习&#xff08;完整ppt和代码&#xff09;无视频 代码能正常运行时不负责答疑! 电子产品&#xff0c;一经出售&#xff0c;概不退换 算法设计、毕业设计、期刊专利&#xff01;感兴趣可以联系我。 &#x1f3c6;代码获取方式1&#xff1a; 私信…...

待验证---Oracle 19c 在 CentOS 7 上的快速安装部署指南

Oracle 19c 在 CentOS 7 上的快速安装部署指南 Oracle Database 19c 是一个功能强大的企业级数据库系统&#xff0c;下面我将为您提供在 CentOS 7 上快速安装部署 Oracle 19c 的详细步骤。 一、准备工作 1. 系统要求 CentOS 7 (64位)最小内存: 2GB (推荐 8GB 以上)最小磁盘…...

风力发电领域canopen转Profinet网关的应用

在风力发电领域&#xff0c;开疆canopen转Profinet网关KJ-PNG-205的应用案例通常涉及将风力涡轮机内部的CANopen网络与外部的Profinet工业以太网连接起来。这种转换网关允许风力发电场的控制系统通过Profinet协议收集和监控涡轮机的状态信息&#xff0c;同时发送控制命令。 风力…...

vr全景相机如何选择?

VR全景相机&#xff0c;作为虚拟现实技术的核心设备之一&#xff0c;能够拍摄360度全景照片和视频&#xff0c;使用户通过虚拟现实设备身临其境地体验拍摄场景。 这种技术的快速发展&#xff0c;得益于传感器、图像处理和计算机视觉技术的不断进步。 选择一台合适的VR全景相机…...

在 Conda 中,包的安装路径在电脑的哪里

在 Conda 中&#xff0c;包的安装路径取决于你的 Conda 安装方式 和 环境类型&#xff08;base 或其他虚拟环境&#xff09;。以下是不同情况下的详细说明&#xff1a; &#x1f4cc; 1. Conda 包的默认安装路径 Conda 将所有包存储在 pkgs 目录 下&#xff0c;并在各个环境中…...

phpstorm用php连接数据库报错

项目场景&#xff1a; phpstorm用php连接数据库 问题描述 用php使用mysql_connect 的时候报错了&#xff0c;没有这个函数 原因分析&#xff1a; php解释器问题&#xff0c;后来查资料得知mysql_connct只适用于php5.5以下解释器。一开始用的7&#xff0c;改成5.3以后还是报…...

今日行情明日机会——20250428

指数依然在震荡区间&#xff0c;等待方向选择~ 2025年4月28日涨停主要行业方向分析 一、核心主线方向 一季报增长&#xff08;业绩驱动资金避险&#xff09; • 涨停家数&#xff1a;10家&#xff08;最强方向&#xff09;。 • 代表标的&#xff1a; ◦ 珀莱雅&#xff08;2…...

Spring Boot 3与JDK 8环境下的单元测试实践指南

一、引言 在Java后端开发中&#xff0c;单元测试是保障代码质量的核心手段。Spring Boot作为主流框架&#xff0c;其单元测试体系随着版本迭代不断优化。本文聚焦于JDK 8与Spring Boot 3的组合场景&#xff0c;深入解析单元测试的多种实现方式&#xff0c;对比不同测试策略的异…...

微分与积分(前言)

导数 导数是一个非常重要的概念&#xff0c;先来看一个引例&#xff1a;速度问题。历史上速度问题与倒数概念的形成有着密切的关系。 平均速度 v s t v\frac{s}{t} vts​那么如何表示瞬时速度呢&#xff1f; 瞬时经过路程&#xff1a; Δ s s ( t 0 Δ t ) − s ( t 0 ) Δ…...

61. Java 类和对象 - 使用 this 关键字

文章目录 61. Java 类和对象 - 使用 this 关键字1. 在方法或构造函数中引用对象成员1.1 区分同名变量1.2 在普通方法中引用字段或调用方法 2. 在构造函数中调用另一个构造函数示例&#xff1a;构造函数重载 3. 其他用法&#xff1a;返回当前对象4. 注意事项5. 总结 61. Java 类…...

安达发|高效智能塑料切割数控系统 - 全自动化软件解决方案

在当今的制造业中&#xff0c;塑料作为一种轻便、耐用且成本效益高的材料&#xff0c;被广泛应用于各个领域。随着科技的进步和市场需求的变化&#xff0c;塑料加工行业正面临着前所未有的挑战和机遇。为了提高生产效率&#xff0c;降低成本&#xff0c;并满足日益严格的质量标…...

20250428-AI Agent:智能体的演进与未来

目录 一、AI Agent的定义与演进 1.1 传统AI Agent的发展历程 1.2 现代AI Agent的技术突破 二、AI Agent的核心组件 2.1 大模型动态推理规划 2.2 工具系统的演进 2.3 记忆模块的设计 三、AI Agent的工作流程 3.1 感知阶段 3.2 推理阶段 3.3 决策阶段 3.4 执行阶段 …...

微信小程序分页和下拉刷新

在page.json中配置下拉刷新和底部距顶部的距离&#xff1a; {"path": "school-detail","style": {"navigationStyle": "custom","navigationBarTextStyle": "black","enablePullDownRefresh&quo…...

文献阅读(一)植物应对干旱的生理学反应 | The physiology of plant responses to drought

分享一篇Science上的综述文章&#xff0c;主要探讨了植物应对干旱的生理机制&#xff0c;强调通过调控激素信号提升植物耐旱性、保障粮食安全的重要性。 摘要 干旱每年致使农作物产量的损失&#xff0c;比所有病原体造成损失的总和还要多。为适应土壤中的湿度梯度变化&#x…...

开源CMS系统的SEO优化功能主要依赖哪些插件?

在当今互联网时代&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;是网站获取流量的核心手段之一。开源内容管理系统&#xff08;CMS&#xff09;因其灵活性和丰富的插件生态&#xff0c;成为许多开发者和企业的首选。本文将以主流开源CMS为例&#xff0c;深入解析其SEO优…...

YUM/DNF管理工具

YUM (Yellow dog Updater&#xff0c; Modified) &#xff0c; RHEL8 中默认使用的软件批量管理工具由原版本的 yum 换成了速度更快的 dnf &#xff08; DNF Dandified YUM &#xff09;&#xff0c;原有的 yum 命令仅为 dnf 的软链接&#xff0c;当然依旧可以使用。 [root…...

Deepseek 生成新玩法:从文本到可下载 Word 文档?思路与实践

大家好&#xff01;最近有朋友问到&#xff0c;能不能用 Deepseek 这类强大的 AI 模型&#xff0c;根据一个包含具体格式要求&#xff08;比如字体、字号、行距、对齐方式等&#xff09;的提示词&#xff0c;直接生成一篇论文&#xff0c;并且输出一个能直接下载的 Word (.docx…...

【OSG学习笔记】Day 13: 事件处理——响应键盘与鼠标

在OpenSceneGraph (OSG) 中&#xff0c;事件处理是实现用户交互功能的重要部分。 通过自定义按键事件&#xff08;如 WASD 键控制模型移动&#xff09;&#xff0c;可以让用户与场景进行互动。 osgGA::GUIEventHandler osgGA::GUIEventHandler 是 OpenSceneGraph (OSG) 中用…...

element-ui carousel 组件源码分享

carousel 走马灯源码简单分享&#xff0c;主要从以下几个方面&#xff1a; 1、carousel 组件页面结构。 2、carousel 组件属性。 3、carousel 组件事件。 4、carousel 组件方法。 5、carousel-item 组件属性。 一、组件页面结构。 二、组件属性。 2.1 height 走马灯的高…...

在视图中交互 闪退问题

程序闪退 //void mouseEventOccurred(const pcl::visualization::MouseEvent &event, // void* viewer_void) //{ // boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer *static_cast<boost::shared_ptr<pcl::visualization::PCLVisualizer> …...

python 线程池顺序执行

在Python中&#xff0c;线程池&#xff08;ThreadPoolExecutor&#xff09;默认是并发执行任务的&#xff0c;但若需要实现任务的顺序执行&#xff08;按提交顺序执行或按结果顺序处理&#xff09;&#xff0c;可以通过以下方案实现&#xff1a; 方案一&#xff1a;强制单线程&…...

DeepSeek智能时空数据分析(六):大模型NL2SQL绘制城市之间连线

序言&#xff1a;时空数据分析很有用&#xff0c;但是GIS/时空数据库技术门槛太高 时空数据分析在优化业务运营中至关重要&#xff0c;然而&#xff0c;三大挑战仍制约其发展&#xff1a;技术门槛高&#xff0c;需融合GIS理论、SQL开发与时空数据库等多领域知识&#xff1b;空…...

[250428] Nginx 1.28.0 发布:性能优化、安全增强及新特性

目录 Nginx 1.28.0 稳定版发布主要亮点包括&#xff1a;功能增强&#xff1a;安全性改进&#xff1a;其他&#xff1a; Nginx 1.28.0 稳定版发布 Nginx 官方于 4 月 24 日发布了最新的 1.28.0 稳定版本。此版本基于之前的 1.27.x 主线分支&#xff0c;整合了多项新功能、性能优…...

第二章:Agent System

Chapter 2: Agent System 从用户界面到代理系统&#xff1a;背后的“大脑”如何运作&#xff1f; 在上一章的用户界面抽象中&#xff0c;我们已经能通过命令行与AI简单对话了。但你有没有好奇过&#xff1a;输入的问题是如何变成“北京今天晴&#xff0c;气温25C”这样的回答的…...

自动驾驶L4级技术落地:特斯拉、Waymo与华为的路线之争

自动驾驶L4级技术落地&#xff1a;特斯拉、Waymo与华为的路线之争 摘要 随着自动驾驶技术进入L4级&#xff08;高度自动化驾驶&#xff09;商业化探索的关键阶段&#xff0c;全球头部企业围绕技术路线与商业模式展开激烈竞争。特斯拉、Waymo与华为分别代表视觉优先、全栈自研…...