file-type

GetUnicode:一款汉字转Unicode编码的必备工具

ZIP文件

5星 · 超过95%的资源 | 下载需积分: 9 | 15KB | 更新于2025-09-15 | 140 浏览量 | 16 下载量 举报 收藏
download 立即下载
GetUnicode 是一个简洁但功能强大的命令行工具,主要用于将输入的汉字字符串转换为其对应的 Unicode 编码。这一工具的核心功能围绕字符编码转换展开,涉及多个 IT 技术领域的基础知识,包括字符集、编码规范、命令行工具设计、以及嵌入式开发中的应用场景。 ### 一、Unicode 编码概述 Unicode 是一种国际化的字符编码标准,旨在为世界上所有的字符提供一个唯一的数字(即代码点),无论其语言、平台或程序如何。Unicode 编码的出现,解决了传统 ASCII 和各种区域性编码(如 GBK、Shift_JIS、EUC-KR 等)之间无法兼容的问题。其基本单位是代码点(Code Point),通常表示为 U+XXXX 的形式,其中 X 是十六进制数字。例如,“汉”字在 Unicode 中的代码点是 U+6C49。 Unicode 编码可以通过多种方式进行存储和传输,最常见的是 UTF-8、UTF-16 和 UTF-32。UTF-8 是一种变长编码方式,广泛应用于互联网和现代操作系统中,尤其适合英文字符,同时也能高效处理其他语言的字符。UTF-16 则在 Windows 系统和 Java 编程语言中使用较多,采用 2 字节或 4 字节表示字符。UTF-32 则是固定长度的 4 字节编码方式,虽然处理效率高,但由于占用空间较大,应用相对较少。 GetUnicode 工具的主要功能就是将用户输入的汉字字符串转换为对应的 Unicode 代码点或 UTF-16、UTF-8 编码形式,便于开发者在程序中使用或调试。 ### 二、命令行工具的设计与作用 GetUnicode 作为一个命令行程序(CLI,Command Line Interface),其设计简洁高效,适合集成到自动化脚本或开发流程中。命令行工具相比图形界面程序有以下优势: 1. **轻量化**:不需要图形界面资源,启动速度快,占用内存小。 2. **易于集成**:可方便地被批处理脚本、Makefile 或其他程序调用。 3. **跨平台兼容性好**:多数命令行工具可以在 Windows、Linux、macOS 等操作系统上运行,尤其是使用 C/C++ 或 Go 编写的程序。 4. **适合开发人员使用**:命令行工具往往提供更灵活的参数选项,便于高级用户进行定制化操作。 GetUnicode 的典型使用方式可能是这样的:用户在命令行中输入类似 `GetUnicode.exe 汉字` 的命令,程序将输出“汉字”这两个字符的 Unicode 编码,例如: ``` 字符:汉 → Unicode:U+6C49 → UTF-8:E6 B1 89 → UTF-16:6C49 字符:字 → Unicode:U+5B57 → UTF-8:E5 AD 97 → UTF-16:5B57 ``` 这种输出格式有助于开发者在不同编码格式之间进行调试和转换。 ### 三、汉字编码转换的意义 在多语言支持和国际化(i18n)开发中,字符编码转换是一个基础但至关重要的环节。汉字作为世界上使用人数最多的文字系统之一,其编码处理尤为复杂。早期的汉字编码标准包括 GB2312、GBK、GB18030 等,这些编码方式通常采用双字节表示一个汉字,且只适用于中文环境。 随着互联网和全球化的发展,Unicode 成为了处理多语言文本的标准。开发者在处理中文字符时,经常需要将本地编码(如 GBK)转换为 Unicode,或反过来进行转换。例如: - 在网页开发中,HTML 页面默认使用 UTF-8 编码,服务器端可能需要将接收到的 GBK 编码的表单数据转换为 UTF-8; - 在数据库操作中,不同的数据库系统可能使用不同的字符集,Unicode 编码成为中间桥梁; - 在嵌入式系统中,资源受限的设备往往需要将常用汉字预处理为 Unicode 编码,以节省内存和提高显示效率。 GetUnicode 工具正是为了解决这类编码转换问题而存在。它可以帮助开发者快速确认某个汉字的 Unicode 表示,避免手动查表的繁琐,提升开发效率。 ### 四、嵌入式开发中的应用 标题中提到“嵌入式开发必备”,说明 GetUnicode 在嵌入式系统开发中具有重要价值。嵌入式系统通常资源受限,对内存、处理速度和代码体积有严格要求。在这种环境下,汉字显示往往需要将字符预处理为特定格式(如 Unicode),然后通过字库进行渲染。 在嵌入式设备中使用汉字显示时,常见的做法是: 1. **将汉字转换为 Unicode**:用于标识字符; 2. **使用字库文件(如点阵字库或矢量字体)**:根据 Unicode 代码点查找对应字形; 3. **通过显示驱动程序进行渲染**:将字形数据发送到 LCD 或 OLED 屏幕上。 例如,在一个基于 STM32 的嵌入式项目中,如果需要显示“你好”两个字,开发者可能需要知道这两个字的 Unicode 编码(U+4F60 和 U+597D),然后将其转换为特定的字库索引,再通过 SPI 或 I2C 接口发送到显示屏。 因此,GetUnicode 可以帮助开发者快速获取这些 Unicode 编码,避免手动查找或出错,是嵌入式开发中非常实用的辅助工具。 ### 五、程序结构与实现思路(假设) 虽然无法查看 GetUnicode.exe 的源代码,但从其功能可以推测其内部实现的大致逻辑: 1. **命令行参数解析**:程序启动时接收用户输入的字符串参数; 2. **字符编码识别**:判断输入字符串的编码格式(如 ANSI、UTF-8 等); 3. **字符转换**:将输入字符转换为 Unicode 编码; 4. **输出格式化**:以用户可读的方式输出 Unicode 代码点、UTF-8 或 UTF-16 编码; 5. **错误处理**:对非法字符或编码错误进行提示。 在 Windows 平台上,这一过程可能使用了 Win32 API 中的 MultiByteToWideChar 函数进行多字节编码到 Unicode 的转换;在 Linux 上则可能使用 iconv 或 glib 提供的编码转换函数。 ### 六、相关技术扩展 除了 Unicode 编码转换之外,GetUnicode 还可以引申出一系列相关的技术知识点,包括但不限于: - **字符集与编码的发展历史**:从 ASCII 到 Unicode 的演变; - **字库与字体渲染技术**:如点阵字体、TrueType、FreeType 等; - **跨平台编码处理**:Python、Java 等语言如何处理 Unicode; - **网络传输中的编码协商**:HTTP 协议中的 Content-Type 和 charset; - **正则表达式中的 Unicode 支持**:如 Unicode 属性转义; - **国际化与本地化开发**:i18n、l10n 的实践与工具链。 这些技术点构成了现代软件开发中关于字符处理的核心知识体系,而 GetUnicode 正是其中的一个实用工具,帮助开发者理解和掌握这些技术。 ### 七、总结 综上所述,GetUnicode 虽然只是一个命令行小程序,但其背后涉及的知识点非常广泛。它不仅是一个汉字 Unicode 编码转换工具,更是字符编码处理、命令行开发、嵌入式系统调试等多个领域的交汇点。对于开发者而言,掌握类似工具的使用和原理,有助于提升在多语言开发、嵌入式系统、网络通信等方面的技术能力。

相关推荐

filetype

@Override public void run(String... args) throws Exception { // 1. 初始化 Flink 环境 StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); // env.getConfig().setAutoWatermarkInterval(1000); // 1秒生成一次水位线 // env.enableCheckpointing(5000); // 1. 从Kafka读取数据流 DataStream<String> input = env.addSource(new FlinkKafkaConsumer<>(TOPIC, new SimpleStringSchema(), getKafkaProperties())); // 解析JSON并配对事件 DataStream<EventPair> pairedStream = input .map(FlinkMonitoringJob::parseJsonToEvent) .assignTimestampsAndWatermarks( // 为原始事件分配时间戳 WatermarkStrategy.<Event>forBoundedOutOfOrderness(Duration.ofSeconds(5)) .withTimestampAssigner((event, ts) -> event.getStartTime()) ) .keyBy(Event::getUniCode) .process(new EventPairingProcessFunction()) .assignTimestampsAndWatermarks( // 为EventPair分配时间戳 WatermarkStrategy.<EventPair>forBoundedOutOfOrderness(Duration.ofSeconds(1)) .withTimestampAssigner((pair, ts) -> pair.getEndTime()) .withIdleness(Duration.ofSeconds(5)) // 空闲5秒后推进水位线 ); // 窗口计算 DataStream<WindowResult> windowResults = pairedStream .keyBy(EventPair::getUniCode) .window(new DynamicWindowAssigner()) .trigger(EventTimeTrigger.create()) .aggregate(new CountAggregator(), new WindowResultFunction()); // 交替输出处理 windowResults .keyBy(WindowResult::getUniCode) .process(new AlternatingOutputFunction()) .print(); // 4. 启动 Flink 作业(异步执行) env.executeAsync("spring-flink-monitor"); } // 交替输出处理函数(最终版) private static class AlternatingOutputFunction extends KeyedProcessFunction<String, WindowResult, Tuple3<String, String, String>> { private ValueState<Boolean> expectCompliantState; @Override public void open(Configuration parameters) { // 初始状态设置为期待符合 expectCompliantState = getRuntimeContext().getState( new ValueStateDescriptor<>("expectCompliant", Boolean.class, true)); } @Override public void processElement(WindowResult result, Context ctx, Collector<Tuple3<String, String, String>> out) throws Exception { Boolean expectCompliant = Optional.ofNullable(expectCompliantState.value()).orElse(true); boolean isMet = result.isMet(); System.out.printf("当前状态: 期待%s | 实际%s | 窗口[%s - %s]%n", expectCompliant ? "符合" : "不符合", isMet ? "符合" : "不符合", formatTime(result.getStartTime()), formatTime(result.getEndTime())); if (isMet == expectCompliant) { String status = isMet ? "COMPLIANT" : "NON-COMPLIANT"; String timeRange = formatTime(result.getStartTime()) + " ~ " + formatTime(result.getEndTime()); System.out.println("输出结果: " + result.getUniCode() + " | " + status + " | " + timeRange); out.collect(Tuple3.of( result.getUniCode(), status, timeRange )); // 切换期待状态 expectCompliantState.update(!expectCompliant); } } } private static String formatTime(long timestamp) { return Instant.ofEpochMilli(timestamp).atZone(ZoneId.of("UTC")) .format(formatter); } // 动态窗口分配器 public static class DynamicWindowAssigner extends WindowAssigner<Object, TimeWindow> { @Override public Collection<TimeWindow> assignWindows(Object element, long timestamp, WindowAssignerContext context) { EventPair pair = (EventPair) element; long delayMillis = pair.getDisposition().getDelay() * 1000L; long endTime = pair.getEndTime(); // 计算对齐后的窗口结束时间 long windowEnd = ((endTime / delayMillis) * delayMillis) + delayMillis; // 对齐到delay的整数倍,生成固定间隔窗口 // long windowEnd = ((endTime + delayMillis - 1) / delayMillis) * delayMillis; long windowStart = windowEnd - delayMillis; System.out.println("分配窗口: " + pair.getUniCode() + " " + formatTime(windowStart) + " - " + formatTime(windowEnd)); return Collections.singletonList(new TimeWindow(windowStart, windowEnd)); } @Override public Trigger<Object, TimeWindow> getDefaultTrigger( StreamExecutionEnvironment env) { return EventTimeTrigger.create(); } @Override public TypeSerializer<TimeWindow> getWindowSerializer( ExecutionConfig executionConfig) { return new TimeWindow.Serializer(); } @Override public boolean isEventTime() { return true; } } // 窗口结果处理 public static class WindowResultFunction extends ProcessWindowFunction<Tuple2<Integer, Disposition>, WindowResult, String, TimeWindow> { // 存储当前水位线状态 private transient ValueState<Long> lastWatermarkState; @Override public void open(Configuration parameters) { // 初始化状态存储 ValueStateDescriptor<Long> watermarkDesc = new ValueStateDescriptor<>("lastWatermark", Long.class); lastWatermarkState = getRuntimeContext().getState(watermarkDesc); } @Override public void process(String key, Context ctx, Iterable<Tuple2<Integer, Disposition>> elements, Collector<WindowResult> out) throws IOException { // 1. 获取当前水位线 long currentWatermark = ctx.currentWatermark(); System.out.println("窗口 [" + ctx.window() + "] 水位线: " + currentWatermark); // 2. 检查水位线是否变化 Long lastWatermark = lastWatermarkState.value(); if (lastWatermark == null || currentWatermark != lastWatermark) { System.out.println("水位线变化: " + (lastWatermark != null ? lastWatermark : "null") + " → " + currentWatermark); lastWatermarkState.update(currentWatermark); } Tuple2<Integer, Disposition> result = elements.iterator().next(); Disposition disp = result.f1; if (disp == null) { System.out.println("无处置参数,不处理"); return; // 无处置参数,不处理 } int actualCount = result.f0; boolean isMet = actualCount >= disp.getFrequency(); System.out.println("窗口触发: " + key + " " + formatTime(ctx.window().getStart()) + " - " + formatTime(ctx.window().getEnd()) + " | 计数: " + actualCount + "/" + disp.getFrequency() + " | 结果: " + (isMet ? "符合" : "不符合")); out.collect(new WindowResult( key, ctx.window().getStart(), ctx.window().getEnd(), isMet, actualCount, disp.getFrequency() )); } } // 自定义聚合函数 public static class CountAggregator implements AggregateFunction<EventPair, Tuple2<Integer, Disposition>, Tuple2<Integer, Disposition>> { @Override public Tuple2<Integer, Disposition> createAccumulator() { return Tuple2.of(0, null); } @Override public Tuple2<Integer, Disposition> add(EventPair pair, Tuple2<Integer, Disposition> acc) { return Tuple2.of(acc.f0 + 1, pair.getDisposition()); // 传递 disposition } @Override public Tuple2<Integer, Disposition> getResult(Tuple2<Integer, Disposition> acc) { return acc; } @Override public Tuple2<Integer, Disposition> merge(Tuple2<Integer, Disposition> a, Tuple2<Integer, Disposition> b) { // 合并时保留最新的非空 disposition Disposition mergedDisp = (b.f1 != null) ? b.f1 : a.f1; return Tuple2.of(a.f0 + b.f0, mergedDisp); // return Tuple2.of(a.f0 + b.f0, a.f1 != null ? a.f1 : b.f1); // 合并时保留非空 disposition } } // 事件配对处理函数 private static class EventPairingProcessFunction extends KeyedProcessFunction<String, Event, EventPair> { private ValueState<Event> lastEventState; private ValueState<Disposition> dispositionState; @Override public void open(Configuration parameters) { lastEventState = getRuntimeContext().getState( new ValueStateDescriptor<>("lastEvent", Event.class)); dispositionState = getRuntimeContext().getState( new ValueStateDescriptor<>("disposition", Disposition.class)); } @Override public void processElement(Event event, Context ctx, Collector<EventPair> out) throws Exception { // 更新处置参数 dispositionState.update(event.getDisposition()); Event lastEvent = lastEventState.value(); if (lastEvent != null) { if (!"0".equals(lastEvent.getLeval()) && "0".equals(event.getLeval())) { long timeDiff = event.getStartTime() - lastEvent.getStartTime(); Disposition disp = dispositionState.value(); System.out.println("生成事件对Generated pair: " + event.getUniCode() + " " + Instant.ofEpochMilli(lastEvent.getStartTime()).atZone(ZoneId.of("UTC")).format(formatter) + "->" + Instant.ofEpochMilli(event.getStartTime()).atZone(ZoneId.of("UTC")).format(formatter) + " delay:" + disp.getDelay()); if (timeDiff <= disp.getDelay() * 1000L) { out.collect(new EventPair( lastEvent.getUniCode(), lastEvent.getDataCode(), lastEvent.getDevAlarmId(), lastEvent.getDevId(), lastEvent.getLeval(), lastEvent.getMac(), lastEvent.getSource(), lastEvent.getStartTimeStr(), lastEvent.getStartTime(), event.getStartTimeStr(), event.getStartTime(), lastEvent.getState(), lastEvent.getValue(), lastEvent.getMark(), lastEvent.getDisplay(), lastEvent.getAlarmLb(), lastEvent.getAlarmLx(), lastEvent.getBoxInfo(), lastEvent.getCameraIp(), lastEvent.getDetail(), lastEvent.getDevCode(), lastEvent.getIsConfirm(), lastEvent.getPic(), lastEvent.getStatus(), lastEvent.getType(), lastEvent.getVal(), lastEvent.getRecoverVal(), disp.getDelay(), disp.getFrequency(), disp )); } lastEventState.clear(); } else { lastEventState.update(event); } } else { if (!"0".equals(event.getLeval())) { lastEventState.update(event); } } } } @Data @AllArgsConstructor public static class WindowResult { private String uniCode; private long startTime; private long endTime; private boolean isMet; private int actualCount; private int requiredFrequency; } // 数据结构 @Data public static class Event { private String uniCode; private String dataCode; private String devAlarmId; private String devId; private String leval; private String mac; private String source; private String startTimeStr; private long startTime; private String endTimeStr; private long endTime; private String state; private String value; private String mark; private String display; private String alarmLb; private String alarmLx; private String boxInfo; private String cameraIp; private String detail; private String devCode; private String isConfirm; private String pic; private String status; private String type; private String val; private String recoverVal; private Disposition disposition; } @Data public static class Disposition { private int delay; private int frequency; } @Data @AllArgsConstructor public static class EventPair { private String uniCode; private String dataCode; private String devAlarmId; private String devId; private String leval; private String mac; private String source; private String startTimeStr; private long startTime; private String endTimeStr; private long endTime; private String state; private String value; private String mark; private String display; private String alarmLb; private String alarmLx; private String boxInfo; private String cameraIp; private String detail; private String devCode; private String isConfirm; private String pic; private String status; private String type; private String val; private String recoverVal; private int delay; private int frequency; private Disposition disposition; } // JSON解析方法(示例) private static Event parseJsonToEvent(String json) { cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(json); Event event = new Event(); event.setDataCode(jsonObject.getStr("dataCode")); event.setDevAlarmId(jsonObject.getStr("devAlarmId")); event.setDevId(jsonObject.getStr("devId")); event.setLeval(jsonObject.getStr("leval")); event.setMac(jsonObject.getStr("mac")); event.setSource(jsonObject.getStr("source")); event.setStartTimeStr(jsonObject.getStr("startTime")); long startTime = LocalDateTime.parse(jsonObject.getStr("startTime"), formatter).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); event.setStartTime(startTime); event.setState(jsonObject.getStr("state")); event.setValue(jsonObject.getStr("value")); event.setAlarmLb(jsonObject.getStr("alarmLb")); event.setAlarmLx(jsonObject.getStr("alarmLx")); event.setBoxInfo(jsonObject.getStr("boxInfo")); event.setCameraIp(jsonObject.getStr("cameraIp")); event.setDetail(jsonObject.getStr("detail")); event.setDevCode(jsonObject.getStr("devCode")); event.setIsConfirm(jsonObject.getStr("isConfirm")); event.setPic(jsonObject.getStr("pic")); event.setStatus(jsonObject.getStr("status")); event.setType(jsonObject.getStr("type")); event.setVal(jsonObject.getStr("val")); event.setUniCode(jsonObject.getStr("uniCode")); JSONObject jsonObject1 = jsonObject.getJSONObject("disposition"); Disposition disposition = new Disposition(); disposition.setFrequency(jsonObject1.getInt("frequency")); disposition.setDelay(jsonObject1.getInt("delay")); event.setDisposition(disposition); return event; }上述代码,比如有一个窗口17:00-17:03,有符合数据1条,时间到17:03后,没有后续数据,窗口未触发结束,窗口需要触发结束

filetype

import com.jhlabs.image.GaussianFilter; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.text.PDFTextStripper; import org.apache.pdfbox.text.TextPosition; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageOutputStream; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class bjbdtest { private static final Pattern BARCODE_PATTERN = Pattern.compile("(条形码|条\\s*码)[::]?\\s*(\\d{10,20})"); // 测试示例 public static void main(String[] args) { String path = "D:\\集中打印\\pdf"; // 替换为实际路径,例如 "C:/Users/YourName/Documents" try { List<File> pdfFiles = findPDFFiles(path); System.out.println("找到 " + pdfFiles.size() + " 个 PDF 文件:"); for (File pdf : pdfFiles) { System.out.println(pdf.getAbsolutePath()); } // 2. 处理每个PDF文件 // for (File pdfFile : pdfFiles) { // System.out.println("\n处理文件: " + pdfFile.getName()); // System.out.println("文件路径: " + pdfFile.getAbsolutePath()); // // // 3. 提取文本内容 // String content = extractTextFromPDF(pdfFile); // // // 4. 输出前200个字符作为预览 // int previewLength = Math.min(200, content.length()); // String preview = content.substring(0, previewLength).replaceAll("\\s+", " "); // System.out.println("内容预览: " + preview + (content.length() > 200 ? "..." : "")); // // // 5. 可选:保存完整内容到文件 // // saveContentToFile(pdfFile, content); // } System.out.println("\n处理文件: " + pdfFiles.get(0).getName()); System.out.println("文件路径: " + pdfFiles.get(0).getAbsolutePath()); // 3. 提取文本内容 String content = extractTextFromPDF(pdfFiles.get(0)); String barcode = extractBarcode(content)+"03"; //pdf 转 jpg File jpgFile = pdfToHighQualityImage( pdfFiles.get(0), "D:\\集中打印\\pdf\\" + barcode ); // 4. 输出前200个字符作为预览 System.out.println(barcode); int previewLength = Math.min(30, content.length()); String preview = content.substring(0, previewLength).replaceAll("\\s+", " "); System.out.println("内容预览: " + preview + (content.length() > 30 ? "..." : "")); } catch (IllegalArgumentException e) { System.err.println("错误: " + e.getMessage()); } catch (Exception e) { throw new RuntimeException(e); } } public static File pdfToHighQualityImage(File pdfFile, String outputPath) throws IOException { // 系统级字体渲染优化 System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider"); System.setProperty("org.apache.pdfbox.rendering.UsePureJavaCMYK", "true"); File outputFile = new File(outputPath + ".jpg"); try (PDDocument document = PDDocument.load(pdfFile)) { PDFRenderer renderer = new OptimizedPDFRenderer(document); // 仅处理第一页(如需多页可修改循环) BufferedImage image = renderer.renderImageWithDPI(0, 350, ImageType.RGB); // 高级图像后处理 BufferedImage optimizedImage = applyAdvancedProcessing(image); // 保存为高质量JPEG saveAsJpeg(optimizedImage, outputFile, 0.92f); // 92%质量 } return outputFile; } // 自定义渲染器优化字体处理 private static class OptimizedPDFRenderer extends PDFRenderer { public OptimizedPDFRenderer(PDDocument document) { super(document); setSubsamplingAllowed(true); } } // 高级图像处理流程 private static BufferedImage applyAdvancedProcessing(BufferedImage source) { BufferedImage result = new BufferedImage( source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g2d = result.createGraphics(); // 精细化的抗锯齿设置 g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); // 轻微减轻字体权重 g2d.setComposite(AlphaComposite.SrcOver.derive(0.98f)); g2d.drawImage(source, 0, 0, null); // 边缘锐化处理 result = applyUnsharpMask(result, 0.7f); g2d.dispose(); return result; } // 高质量JPEG保存 private static void saveAsJpeg(BufferedImage image, File output, float quality) throws IOException { Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg"); ImageWriter writer = writers.next(); try (ImageOutputStream ios = ImageIO.createImageOutputStream(output)) { writer.setOutput(ios); ImageWriteParam param = writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(quality); writer.write(null, new IIOImage(image, null, null), param); } writer.dispose(); } // 反锐化掩模算法(减轻加粗效果) private static BufferedImage applyUnsharpMask(BufferedImage image, float amount) { BufferedImage blurred = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); // 创建轻微模糊版本 Graphics2D g = blurred.createGraphics(); g.drawImage(image, 0, 0, null); g.dispose(); blurred = new GaussianFilter(1.2f).filter(blurred, null); // 应用反锐化掩模 BufferedImage result = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); for (int y = 0; y < image.getHeight(); y++) { for (int x = 0; x < image.getWidth(); x++) { int original = image.getRGB(x, y); int blur = blurred.getRGB(x, y); // 计算差值并混合 int deltaR = ((original >> 16) & 0xFF) - ((blur >> 16) & 0xFF); int deltaG = ((original >> 8) & 0xFF) - ((blur >> 8) & 0xFF); int deltaB = (original & 0xFF) - (blur & 0xFF); int newR = clamp((int)(((original >> 16) & 0xFF) + amount * deltaR)); int newG = clamp((int)(((original >> 8) & 0xFF) + amount * deltaG)); int newB = clamp((int)((original & 0xFF) + amount * deltaB)); result.setRGB(x, y, (newR << 16) | (newG << 8) | newB); } } return result; } private static int clamp(int value) { return Math.max(0, Math.min(255, value)); } // 同上文的extractBarcode方法 public static String extractBarcode(String text) { Pattern pattern = Pattern.compile(String.valueOf(BARCODE_PATTERN)); Matcher matcher = pattern.matcher(text); return matcher.find() ? matcher.group(2) : null; } /** * 提取PDF文件中的文本内容 * @param pdfFile PDF文件 * @return 提取的文本内容 */ public static String extractTextFromPDF(File pdfFile) { try (PDDocument document = PDDocument.load(pdfFile)) { // 1. 配置文本提取器 PDFTextStripper stripper = new PDFTextStripper() { @Override protected void processTextPosition(TextPosition text) { // 直接提取Unicode字符,避免字体问题 try { String unicode = text.getUnicode(); if (!unicode.isEmpty() && !unicode.matches("\\s")) { writeString(unicode); } } catch (IOException e) { throw new RuntimeException("文本提取失败", e); } } }; // 2. 设置提取参数 stripper.setSortByPosition(true); // 按物理位置排序 stripper.setShouldSeparateByBeads(false); stripper.setAddMoreFormatting(false); // 禁用自动格式化 // 3. 提取并清理结果 String rawText = stripper.getText(document); return cleanExtractedText(rawText); } catch (Exception e) { System.err.println("PDF处理错误: " + e.getMessage()); return ""; } } /** * 清理提取的文本(合并空格、移除无效字符) */ private static String cleanExtractedText(String text) { return text.replaceAll("[\\r\\n]+", "\n") // 合并换行 .replaceAll("[\\s]{2,}", " ") // 合并空格 .replaceAll("[\\x00-\\x1F]", ""); // 移除控制字符 } /** * 获取指定目录下所有 PDF 文件(包括子文件夹) * @param directoryPath 文件夹路径(例如:"C:/Documents") * @return PDF 文件列表 * @throws IllegalArgumentException 如果路径无效 */ public static List<File> findPDFFiles(String directoryPath) { File rootDir = new File(directoryPath); // 检查路径有效性 if (!rootDir.exists()) { throw new IllegalArgumentException("路径不存在: " + directoryPath); } if (!rootDir.isDirectory()) { throw new IllegalArgumentException("路径不是文件夹: " + directoryPath); } List<File> pdfFiles = new ArrayList<>(); LinkedList<File> queue = new LinkedList<>(); queue.add(rootDir); // 广度优先遍历文件夹(参考引用[2]方法) while (!queue.isEmpty()) { File currentDir = queue.removeFirst(); File[] files = currentDir.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { queue.add(file); // 添加子文件夹到队列 } else if (file.isFile() && isPDFFile(file)) { pdfFiles.add(file); // 收集 PDF 文件 } } } } return pdfFiles; } // 检查文件是否为 PDF(基于扩展名) private static boolean isPDFFile(File file) { String fileName = file.getName().toLowerCase(); return fileName.endsWith(".pdf"); } } 转换成图片后没有加粗的字体变的加粗了 字体有可能改变了

filetype
gaveny
  • 粉丝: 0
上传资源 快速赚钱