由于项目中用到,所以网上找了很多,但或多或少有些问题,我整理更新了一下。
Itext架包下载地址:
http://download.csdn.net/detail/qm4050/5531349
PDF生成代码如下:
import java.io.*;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
/**
* PDF生成
*/
public class PDFBuild {
public static void buidPDF(String pdfFile, String imageFile,
String waterMarkName, int permission) {
try {
File file = File.createTempFile("tempFile", ".pdf"); // 创建临时文件
// 生成PDF
if (createPDFFile(file)) {
waterMark(file.getPath(), imageFile, pdfFile, waterMarkName,
permission); // 添加水印
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 创建PDF文件
*
* @param file
* 临时文件
* @return 成功/失败
*/
public static boolean createPDFFile(File file) {
Rectangle rect = new Rectangle(PageSize.A4);
Document doc = new Document(rect, 50.0F, 50.0F, 50.0F, 50.0F);
try {
PdfWriter.getInstance(doc, new FileOutputStream(file));
doc.open();
BaseFont bf = BaseFont.createFont("C:/WINDOWS/Fonts/SIMSUN.TTC,1",
"Identity-H", true);// 使用系统字体
Font font = new Font(bf, 14.0F, 0);
font.setStyle(37); // 设置样式
font.setFamily("宋体"); // 设置字体
Paragraph p = new Paragraph("付 款 通 知 书\r\n", font);
p.setAlignment(1);
doc.add(p);
doc.close();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static void waterMark(String inputFile, String imageFile,
String outputFile, String waterMarkName, int permission) {
try {
PdfReader reader = new PdfReader(inputFile);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(
outputFile));
BaseFont base = BaseFont.createFont(
"C:/WINDOWS/Fonts/SIMSUN.TTC,1", "Identity-H", true);// 使用系统字体
int total = reader.getNumberOfPages() + 1;
Image image = Image.getInstance(imageFile);
// 图片位置
image.setAbsolutePosition(400, 480);
PdfContentByte under;
int j = waterMarkName.length();
char c = 0;
int rise = 0;
for (int i = 1; i < total; i++) {
rise = 400;
under = stamper.getUnderContent(i);
under.beginText();
under.setFontAndSize(base, 30);
if (j >= 15) {
under.setTextMatrix(200, 120);
for (int k = 0; k < j; k++) {
under.setTextRise(rise);
c = waterMarkName.charAt(k);
under.showText(c + "");
}
} else {
under.setTextMatrix(240, 100);
for (int k = 0; k < j; k++) {
under.setTextRise(rise);
c = waterMarkName.charAt(k);
under.showText(c + "");
rise -= 18;
}
}
// 添加水印文字
under.endText();
// 添加水印图片
under.addImage(image);
// 画个圈
under.ellipse(250, 450, 350, 550);
under.setLineWidth(1f);
under.stroke();
}
stamper.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String imageFilePath = "D:/itext2.png"; // 水印图片路径
String pdfFilePath = "D:/itext.pdf"; // 文件生成路径
buidPDF(pdfFilePath, imageFilePath, "正版授权", 16);
}
}这节讲进程控制块中的标识进程之间亲属关系的那些域。
系统创建的进程具有父子关系。所有的进程都是PID为1的init进程的后代(ps -e命令可以看到系统中进程相关的一些信息)。内核在系统启动的最后阶段启动init进程,该进程读取系统的初始化脚本并执行其他的相关程序,最终完成系统启动的整个过程。随后所有创建的进程都是从该进程中创建的子进程。系统中每个进程必有一个子进程,相应的,每个进程也可以拥有一个或多个子进程。拥有统一个父进程的所有进程被称为兄弟。我们在进程PCB中定义了描述这种继承关系的几个域,linux内核(3.9版)这样定义这几个域:
struct task_struct {
// 1> 状态信息——描述进程的动态变化,如就绪态、等待态、僵死态等。
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
// 2> 亲属关系——描述进程的亲属关系,如祖父进程、父进程、养父进程、子进程、兄进程、孙进程等。
/*
* pointers to (original) parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
* p->real_parent->pid)
*/
struct task_struct __rcu *real_parent; /* real parent process */
struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
/*
* children/sibling forms the list of my natural children
*/
struct list_head children; /* list of my children */
struct list_head sibling; /* linkage in my parent's children list */
struct task_struct *group_leader; /* threadgroup leader */
// 3> 各种标识符——如进程标识符、用户标识符等等用来标识一个进程的数字。
// 4> 进程间通信信息——描述多个进程在同一任务上协作工作,如管道、消息队列、共享内存、套接字等。
// 5> 时间和定时器信息——描述进程在生存周期内使用CPU时间的统计、计费等信息。
// 6> 调度信息——描述进程的优先级、调度策略等信息,如静态优先级、动态优先级、时间片轮转、高优先级以及多级反馈队列等的调度策略。
// 7> 文件系统信息——对进程使用文件情况进行记录,如文件描述符、系统打开文件表、用户打开文件表等。
// 8> 虚拟内存信息——描述每个进程拥有的地址空间,也就是进程编译连接后形成的空间,这里肯定用到前边提到的分页机制。
// 9> 处理器环境信息——描述进程的执行环境(处理器的各种寄存器及堆栈等),这是体现进程动态变化的最主要的场景。
};
这里说明一点,一个进程可能有两个父亲,一个为亲生父亲(real_parent),一个为养父(parent),因为父进程有可能在子进程之前销毁,就得给子进程重新找个养父,但大多数情况下,生父和养父是相同的,即real_parent和parent指向同一个位置。
我们可以通过下面的代码获得指向其父进程的进程控制块:
struct task_struct *my_parent = current->parent;
可以按以下方式依次访问子进程:
struct task_struct *task;
struct list_head *list;
list_for_each(list, &currnet->children) {
task = list_entry(list, struct tast_struct, sibling);
/*task现在指向当前的某个子进程*/
}
init进程的进程控制块是作为init_task静态分配的。下面的代码可以很好地演示所有进程之间的关系:
struct task_struct *task; for (task = current; task != init_task; task = task->parent) ; /*task 现在指向init*/
实际上,可以通过这种继承体系从系统的任何一个进程出发找到任意指定的其他进程。但大都数时候,只需呀简单的重复方式就可以遍历系统中所有的子进程,因为我们是通过双向链表组织所有进程的,<linux/sched.h>中给出了获取一个进程的先一个进程的宏(3.9版内核):
#define next_task(p) \
list_entry_rcu((p)->tasks.next, struct task_struct, tasks)
内核也定义了一个遍历整个任务(一个进程也叫一个任务)队列的宏:
#define for_each_process(p) \ for (p = &init_task ; (p = next_task(p)) != &init_task ; )
我们要注意的是,在一个拥有大量进程的系统中通过重复来遍历所有进程的代价是很大的。
1. C语言实现服务器软件时的常用编程模型
熟悉服务器软件开发的开发人员都知道使用Producer-Consumer模型加上线程池隔离消息的接收和消息的处理,但到消息的具体处理时一般是构建一个实例状态和消息类型的二维矩阵,矩阵的内容是函数指针。本文默认认为你熟悉State Design Pattern,主要介绍用State Design Pattern来替换这个方法。
2. State模式的应用
使用State设计模式可以将所有的消息的处理放到具体的State对象中,这样将实例状态这个维度分解为不同的消息对象,而将消息类型这个维度封装到具体的State对象中,就可以使用面向对象的思想来实现服务器软件。
3. State模式的增强
a. 一般在设计状态对象的时候,状态对象里面不放任何属性,完全把状态对象做成一个方法的集合。
b. 如果a成立的话,可以进一步把每个状态对象作为Singleton和Immutable来实现。
采用以上方法的好处是,整个系统中所有的session就可以共享这些状态对象,避免了需要不停的创建和销毁状态对象,极大的提升性能。
4. 关于Servlet Container实现的思考
a. 其实我们考虑HttpServlet的实现,其实我们自己在写HttpServlet的时候最好不要增加字段,否则就会出现同步的问题。
b. 虽然HttpServlet没有使用Singleton模式,但是一般的Servlet Container只会创建一个HttpServlet。
c. HttpServlet就是就是一个方法的集合,有doGet, doPost, doMethod...
d. XML其实就是就应和了State这个维度
综合以上思考,其实Servlet Container实现就是应用了State设计模式