Browse Source

pdf图层完善

master
review512jwy@163.com 1 month ago
parent
commit
0b807095cf
  1. 37
      build.gradle
  2. 109
      src/main/java/co/jp/techsor/pdf/PdfMemoLayerService.java
  3. 93
      src/main/java/co/jp/techsor/pdf/TestLayerFix.java
  4. 117
      src/main/java/co/jp/techsor/pdf/TestLayerFixDetailed.java

37
build.gradle

@ -3,6 +3,25 @@ plugins {
id 'application' id 'application'
} }
// - UTF-8
//
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
tasks.withType(Javadoc) {
options.encoding = 'UTF-8'
}
tasks.withType(Test) {
jvmArgs '-Dfile.encoding=UTF-8'
}
run {
jvmArgs '-Dfile.encoding=UTF-8'
}
group = 'co.jp.techsor.pdf' group = 'co.jp.techsor.pdf'
version = '1.0.0' version = '1.0.0'
sourceCompatibility = '17' sourceCompatibility = '17'
@ -15,7 +34,7 @@ dependencies {
implementation 'com.itextpdf:kernel:8.0.2' implementation 'com.itextpdf:kernel:8.0.2'
implementation 'com.itextpdf:layout:8.0.2' implementation 'com.itextpdf:layout:8.0.2'
implementation 'commons-cli:commons-cli:1.5.0' implementation 'commons-cli:commons-cli:1.5.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
} }
@ -26,7 +45,7 @@ application {
jar { jar {
manifest { manifest {
attributes( attributes(
'Main-Class': 'co.jp.techsor.pdf.App' 'Main-Class': 'co.jp.techsor.pdf.App'
) )
} }
from { from {
@ -58,4 +77,18 @@ task probeJar(type: Jar) {
// JARを作成するタスク // JARを作成するタスク
task allJars { task allJars {
dependsOn fatJar, probeJar dependsOn fatJar, probeJar
}
//
task runLayerTest(type: JavaExec) {
mainClass = 'co.jp.techsor.pdf.TestLayerFix'
classpath = sourceSets.main.runtimeClasspath
jvmArgs '-Dfile.encoding=UTF-8'
}
//
task runLayerTestDetailed(type: JavaExec) {
mainClass = 'co.jp.techsor.pdf.TestLayerFixDetailed'
classpath = sourceSets.main.runtimeClasspath
jvmArgs '-Dfile.encoding=UTF-8'
} }

109
src/main/java/co/jp/techsor/pdf/PdfMemoLayerService.java

@ -19,6 +19,95 @@ import java.util.List;
*/ */
public class PdfMemoLayerService { public class PdfMemoLayerService {
/**
* 指定された名前のレイヤーを取得または作成する
*
* @param pdf PDFドキュメント
* @param layerName レイヤー名
* @param initialVisibility 初期表示状態
* @return 既存または新しく作成されたレイヤー
*/
private PdfLayer getOrCreateLayer(PdfDocument pdf, String layerName, boolean initialVisibility) {
// 既存レイヤーを検索
PdfDictionary catalog = pdf.getCatalog().getPdfObject();
// 统一使用new PdfName()创建名称对象,确保iText 8.0.2兼容性
PdfDictionary ocProps = catalog.getAsDictionary(new PdfName("OCProperties"));
System.out.println("レイヤー検索開始: " + layerName);
System.out.println("Catalog OCProperties: " + (ocProps != null ? "存在" : "不存在"));
if (ocProps != null) {
// 检查OCGs数组
PdfArray ocgs = ocProps.getAsArray(new PdfName("OCGs"));
System.out.println("OCGs数组: " + (ocgs != null ? "存在 (size: " + ocgs.size() + ")" : "不存在"));
if (ocgs != null) {
for (int i = 0; i < ocgs.size(); i++) {
PdfObject ocgObj = ocgs.get(i);
System.out.println("OCG[" + i + "] タイプ: " + ocgObj.getClass().getSimpleName());
if (ocgObj instanceof PdfDictionary ocgDict) {
PdfString name = ocgDict.getAsString(new PdfName("Name"));
if (name != null) {
// エンコーディング問題を回避するためにtoUnicodeString()を使用
String foundName = name.toUnicodeString();
System.out.println("OCG[" + i + "] 名前: " + foundName);
// 使用equalsIgnoreCase进行不区分大小写的比较,提高兼容性
if (layerName.equalsIgnoreCase(foundName)) {
// 既存レイヤーを見つけた場合
System.out.println("既存レイヤーを使用します: " + layerName + " (見つかった名前: " + foundName + ")");
PdfLayer existingLayer = new PdfLayer(ocgDict);
existingLayer.setOn(initialVisibility); // 初期表示状態を更新
return existingLayer;
}
}
} else if (ocgObj instanceof PdfIndirectReference ref) {
// 处理间接引用
PdfObject directObj = ref.getRefersTo();
if (directObj instanceof PdfDictionary ocgDict) {
PdfString name = ocgDict.getAsString(new PdfName("Name"));
if (name != null) {
// エンコーディング問題を回避するためにtoUnicodeString()を使用
String foundName = name.toUnicodeString();
System.out.println("OCG[" + i + "] (间接引用) 名前: " + foundName);
if (layerName.equalsIgnoreCase(foundName)) {
System.out.println("既存レイヤーを使用します: " + layerName + " (見つかった名前: " + foundName + ")");
PdfLayer existingLayer = new PdfLayer(ocgDict);
existingLayer.setOn(initialVisibility);
return existingLayer;
}
}
}
}
}
}
// 检查可选内容配置(可能在其他位置)
PdfDictionary ocConfig = ocProps.getAsDictionary(new PdfName("OCConfig"));
if (ocConfig != null) {
PdfArray onArray = ocConfig.getAsArray(new PdfName("On"));
if (onArray != null) {
System.out.println("OCConfig On数组存在 (size: " + onArray.size() + ")");
}
PdfArray offArray = ocConfig.getAsArray(new PdfName("Off"));
if (offArray != null) {
System.out.println("OCConfig Off数组存在 (size: " + offArray.size() + ")");
}
}
}
// レイヤーが存在しない場合、新しく作成
System.out.println("新しいレイヤーを作成します: " + layerName);
// PdfLayerコンストラクタは自動的にOCGs配列に追加します
PdfLayer newLayer = new PdfLayer(layerName, pdf);
newLayer.setOn(initialVisibility);
return newLayer;
}
/** /**
* PDFにメモ用レイヤーを2つ追加しオプションで描画を行う * PDFにメモ用レイヤーを2つ追加しオプションで描画を行う
* *
@ -72,12 +161,9 @@ public class PdfMemoLayerService {
try (PdfDocument pdf = new PdfDocument(new PdfReader(inputPath, readerProps), try (PdfDocument pdf = new PdfDocument(new PdfReader(inputPath, readerProps),
new PdfWriter(outputPath, writerProps))) { new PdfWriter(outputPath, writerProps))) {
// レイヤー(OCG)の作成 // レイヤー(OCG)の作成または既存レイヤーの取得
PdfLayer memoLayer1 = new PdfLayer(layer1Name, pdf); PdfLayer memoLayer1 = getOrCreateLayer(pdf, layer1Name, layer1On);
memoLayer1.setOn(layer1On); PdfLayer memoLayer2 = getOrCreateLayer(pdf, layer2Name, layer2On);
PdfLayer memoLayer2 = new PdfLayer(layer2Name, pdf);
memoLayer2.setOn(layer2On);
System.out.println("レイヤーを作成しました:"); System.out.println("レイヤーを作成しました:");
System.out.println(" - " + layer1Name + " (初期状態: " + (layer1On ? "ON" : "OFF") + ")"); System.out.println(" - " + layer1Name + " (初期状態: " + (layer1On ? "ON" : "OFF") + ")");
@ -239,7 +325,12 @@ public class PdfMemoLayerService {
* @return 対象レイヤー * @return 対象レイヤー
*/ */
private PdfLayer determineTargetLayer(PdfAnnotation annotation, PdfLayer layer1, PdfLayer layer2, int defaultLayer) { private PdfLayer determineTargetLayer(PdfAnnotation annotation, PdfLayer layer1, PdfLayer layer2, int defaultLayer) {
// 注釈タイプに基づく自動判定ロジック(カスタマイズ可能) // 如果用户明确指定了图层(不等于默认值1),则忽略注释类型,使用指定的图层
if (defaultLayer != 1) {
return (defaultLayer == 1) ? layer1 : layer2;
}
// デフォルトの自動判定ロジック(ユーザーがレイヤーを指定しない場合のみ使用)
PdfName subtype = annotation.getSubtype(); PdfName subtype = annotation.getSubtype();
if (subtype != null) { if (subtype != null) {
@ -260,11 +351,11 @@ public class PdfMemoLayerService {
default: default:
// デフォルトレイヤーを使用 // デフォルトレイヤーを使用
return (defaultLayer == 1) ? layer1 : layer2; return layer1;
} }
} }
// サブタイプが不明な場合はデフォルトレイヤーを使用 // サブタイプが不明な場合はデフォルトレイヤーを使用
return (defaultLayer == 1) ? layer1 : layer2; return layer1;
} }
} }

93
src/main/java/co/jp/techsor/pdf/TestLayerFix.java

@ -0,0 +1,93 @@
package co.jp.techsor.pdf;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Test program for layer duplicate creation fix
*/
public class TestLayerFix {
public static void main(String[] args) {
try {
// Set test file paths
String inputPdf = "test-input.pdf";
String outputPdf1 = "test-output-1.pdf";
String outputPdf2 = "test-output-2.pdf";
System.out.println("=== Layer Duplicate Fix Test Start ===");
// 1. Create test PDF
createTestPdf(inputPdf);
System.out.println("1. Test PDF created: " + inputPdf);
// 2. First run - add layers and content
List<LayerDrawingOptions> drawingOptions1 = new ArrayList<>();
drawingOptions1.add(LayerDrawingOptions.text(2, 100, 700, "First content (layer 2)"));
PdfMemoLayerService service = new PdfMemoLayerService();
service.addMemoLayers(inputPdf, outputPdf1, "Memo1", "Memo2",
true, true, drawingOptions1, false, 1);
System.out.println("2. First processing completed: " + outputPdf1);
// 3. Second run - add more content to the same file
List<LayerDrawingOptions> drawingOptions2 = new ArrayList<>();
drawingOptions2.add(LayerDrawingOptions.text(2, 150, 650, "Second content (layer 2)"));
service.addMemoLayers(outputPdf1, outputPdf2, "Memo1", "Memo2",
true, true, drawingOptions2, false, 1);
System.out.println("3. Second processing completed: " + outputPdf2);
// 4. Check layer count in final PDF
int layerCount = countLayers(outputPdf2);
System.out.println("\n=== Test Results ===");
System.out.println("Final PDF layer count: " + layerCount);
if (layerCount == 2) {
System.out.println("✅ Test passed: Layer count remains 2, no duplicates!");
} else {
System.out.println("❌ Test failed: Layer count is " + layerCount + ", expected 2");
}
} catch (Exception e) {
System.err.println("Error during test: " + e.getMessage());
e.printStackTrace();
}
}
/**
* Create a simple test PDF
*/
private static void createTestPdf(String filePath) throws IOException {
PdfDocument pdf = new PdfDocument(new PdfWriter(filePath));
Document document = new Document(pdf);
document.add(new Paragraph("Test PDF Document - Layer Fix Verification"));
document.add(new Paragraph("This is a test page for PDF layer functionality."));
document.close();
}
/**
* Count layers in PDF
*/
private static int countLayers(String filePath) throws IOException {
try (PdfDocument pdf = new PdfDocument(new PdfReader(filePath))) {
PdfDictionary catalog = pdf.getCatalog().getPdfObject();
PdfDictionary ocProps = catalog.getAsDictionary(new PdfName("OCProperties"));
if (ocProps != null) {
PdfArray ocgs = ocProps.getAsArray(new PdfName("OCGs"));
if (ocgs != null) {
return ocgs.size();
}
}
return 0; // No layers
}
}
}

117
src/main/java/co/jp/techsor/pdf/TestLayerFixDetailed.java

@ -0,0 +1,117 @@
package co.jp.techsor.pdf;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 详细测试程序模拟用户实际使用场景
*/
public class TestLayerFixDetailed {
public static void main(String[] args) {
try {
// 模拟用户实际使用的图层名称
String layer1Name = "メモ1";
String layer2Name = "メモ2";
// 设置测试文件路径
String testDir = "layer-test-results/";
new File(testDir).mkdirs();
String inputPdf = testDir + "original.pdf";
String outputPdf1 = testDir + "output-1.pdf";
String outputPdf2 = testDir + "output-2.pdf";
System.out.println("=== 图层重复创建修复详细测试 ===");
System.out.println("测试图层名称: " + layer1Name + " 和 " + layer2Name);
// 1. 创建测试PDF
createTestPdf(inputPdf);
System.out.println("\n1. 原始PDF创建完成: " + inputPdf);
System.out.println(" 初始图层数量: " + countLayers(inputPdf));
// 2. 第一次运行程序 - 添加图层和内容
System.out.println("\n2. 第一次运行程序添加图层和内容...");
List<LayerDrawingOptions> drawingOptions1 = new ArrayList<>();
drawingOptions1.add(LayerDrawingOptions.text(2, 100, 700, "第一次添加的内容(图层2)"));
PdfMemoLayerService service = new PdfMemoLayerService();
service.addMemoLayers(inputPdf, outputPdf1, layer1Name, layer2Name,
true, true, drawingOptions1, false, 1);
int layerCount1 = countLayers(outputPdf1);
System.out.println(" 第一次处理后图层数量: " + layerCount1);
System.out.println(" 预期图层数量: 2");
// 3. 第二次运行程序 - 在同一文件上添加更多内容
System.out.println("\n3. 第二次运行程序添加更多内容...");
List<LayerDrawingOptions> drawingOptions2 = new ArrayList<>();
drawingOptions2.add(LayerDrawingOptions.text(2, 150, 650, "第二次添加的内容(图层2)"));
drawingOptions2.add(LayerDrawingOptions.text(1, 200, 600, "第二次添加的内容(图层1)"));
service.addMemoLayers(outputPdf1, outputPdf2, layer1Name, layer2Name,
true, true, drawingOptions2, false, 1);
int layerCount2 = countLayers(outputPdf2);
System.out.println(" 第二次处理后图层数量: " + layerCount2);
System.out.println(" 预期图层数量: 2");
// 4. 检查最终结果
System.out.println("\n=== 测试结果汇总 ===");
System.out.println("原始PDF图层数: " + countLayers(inputPdf));
System.out.println("第一次处理后图层数: " + layerCount1);
System.out.println("第二次处理后图层数: " + layerCount2);
if (layerCount1 == 2 && layerCount2 == 2) {
System.out.println("✅ 测试通过:图层数量始终保持为2个,没有重复创建!");
System.out.println(" 最终文件:" + outputPdf2);
} else {
System.out.println("❌ 测试失败:图层数量不符合预期!");
System.out.println(" 第一次处理后预期2个,实际" + layerCount1 + "个");
System.out.println(" 第二次处理后预期2个,实际" + layerCount2 + "个");
}
} catch (Exception e) {
System.err.println("\n测试过程中发生错误: " + e.getMessage());
e.printStackTrace();
}
}
/**
* 创建一个简单的测试PDF
*/
private static void createTestPdf(String filePath) throws IOException {
PdfDocument pdf = new PdfDocument(new PdfWriter(filePath));
Document document = new Document(pdf);
document.add(new Paragraph("原始PDF文档"));
document.add(new Paragraph("这是一个用于测试图层重复创建修复的文档。"));
document.add(new Paragraph("将通过程序两次添加图层和内容,检查是否会重复创建图层。"));
document.close();
}
/**
* 统计PDF中的图层数量
*/
private static int countLayers(String filePath) throws IOException {
try (PdfDocument pdf = new PdfDocument(new PdfReader(filePath))) {
PdfDictionary catalog = pdf.getCatalog().getPdfObject();
PdfDictionary ocProps = catalog.getAsDictionary(new PdfName("OCProperties"));
if (ocProps != null) {
PdfArray ocgs = ocProps.getAsArray(new PdfName("OCGs"));
if (ocgs != null) {
return ocgs.size();
}
}
return 0; // No layers
}
}
}
Loading…
Cancel
Save