首页 > 代码库 > pdf文件格式多余内容的移除,解除llPDFLib5.2的限制
pdf文件格式多余内容的移除,解除llPDFLib5.2的限制
使用报表导出pdf时,因为使用的是llPDFLib 3.6这个插件,对于大多数的报表输出而言,是没有排版差异的,可是对于较多文本内容时,以pdf来预览时,出现了偏差。后来,把插件更改为 5.2版本时,发现排版非常一致。
只是llpdfLib 5.2已经商业化,需要400美元,穷尽google、网盘搜索之力,均无找到任何非试用版。一度放弃使用llpdfLib插件,尝试了一些诸如eDocEngine VCL,pdftoolkit,PowerPDF,Synpdf,VersyPDF等插件,均不理想。甚至采用打印的方式,输出到虚拟的pdf打印机,弄来了PDF Factory Pro 5.11,实际试用,在win7上也能满足要求,但是在windows 2003上输出的PDF文件,其中的阿拉伯数字采用了非正常的字体,也不满意。
再次回到llpdfLib 5.2试用版,导出的文件,有两个部分的版权保护:打开文件时弹出对话框,并且每页加了水印类的注释。开始时并不知道是注释,总以为是加的图像水印,后来打印机输出了效果,发现打印时却没有水印出来,是完整的内容。
于是,想办法把这两个部分在文件中去除,即可满足我的要求。对于水印类的注释,还是找来cpdf文档,找到了-remove--annotations这个参数,于是运行命令: cpdf -remove-annotations 1.pdf -o 2.pdf,果然,那个水印类图章没有了。最关键的是剩下弹出的对话框,但是google都不知道怎么找,最后打开pdf文件编码,查找到弹出对话框的内容,有一段javascript,就是alert一个提示框。其中有一个条件app.viewerVersion>=5时弹出对话框,我就将>=5,改为<-5,让条件永不满足,即可避免弹窗,保存之后果然灵了。这样,更改pdf编码,并且使用cpdf去除注释,基本上可以弄掉不要的信息。
接着,再google了pdf文件格式,并且参照pdf编码,看到那个注释其实就是以 obj <</Type /Annot ... endobj 部分的内容,再用EmEditor打开,将这部分的内容删除,保存再预览pdf效果,果然也没有注释了。如法炮制,对于弹出对话框部分,找到有类似 JavaScript 部分的 obj/endobj对,也将其删除,果然也没有弹窗了。这样,不用改条件,不用运行cpdf,直接将编码里的多余部分清除掉即可。
代码看图:
因为在不同系统中运行,并且有7x24小时的需求,经常运行并不稳定,所以对于去掉多余代码有些问题,后来的替代办法,对于弹出式的JavaScript脚本,只是更改条件,原为:app.viewerVersion>=5,改为:app.viewerVersion<-5,使得条件永不成立。并且改前与改后的字节数没变化。至于移除注释,使用命令 cpdf -remove-annotations 来实现。
1 ...... 2 uses shellAPI; 3 4 const 5 StrJSText : AnsiString = ‘\(app.viewerVersion>=5\)‘; // 查找这部分的JavaScript脚本特征 6 ...... 7 function RunDosCommand(const CommandLine: string): boolean; 8 var 9 HRead,HWrite:THandle;10 StartInfo:TStartupInfo;11 ProceInfo:TProcessInformation;12 sa:TSecurityAttributes;13 begin14 FillChar(sa,sizeof(sa),0);15 sa.nLength := sizeof(sa);16 sa.bInheritHandle := True;17 sa.lpSecurityDescriptor := nil;18 CreatePipe(HRead,HWrite,@sa,0);19 20 FillChar(StartInfo,SizeOf(StartInfo),0);21 StartInfo.cb := SizeOf(StartInfo);22 StartInfo.wShowWindow := SW_HIDE;23 StartInfo.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;24 StartInfo.hStdError := HWrite;25 StartInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE);26 StartInfo.hStdOutput := HWrite;27 try28 CreateProcess(nil,//lpApplicationName: PChar29 PChar(CommandLine), //lpCommandLine: PChar30 nil, //lpProcessAttributes: PSecurityAttributes31 nil, //lpThreadAttributes: PSecurityAttributes32 True, //bInheritHandles: BOOL33 CREATE_NEW_CONSOLE,34 nil,35 nil,36 StartInfo,37 ProceInfo );38 WaitForSingleObject(ProceInfo.hProcess,INFINITE);39 except40 end;41 CloseHandle(HRead);42 CloseHandle(HWrite);43 Result := True;44 end;45 46 { 更改JavaScript中IF条件的值,由>=5改为<-5,使条件永不成立。 }47 procedure AlterJSIFValue(var srcPt: PAnsiChar; srcLen:Integer);48 var jsPt : PAnsiChar;49 i,j,jsLen : Integer;50 begin51 jsPt := PAnsiChar(StrJSText);52 jsLen := Length(StrJSText);53 for i := 0 to srcLen-jsLen do54 begin55 for j := 0 to jsLen-1 do56 if srcPt[i+j]<>jsPt[j] then break;57 if j=jsLen then58 begin59 srcPt[i+j-5] := ‘<‘;60 srcPt[i+j-4] := ‘-‘;61 break; // 目前发现只有一处JavaScript,如果有多处,可注释此行。62 end;63 end;64 end;65 66 ......67 68 if FPdf.FileName <> ‘‘ then69 begin70 dir := ExtractFilePath(FPdf.FileName);71 fn := ExtractFileName(FPdf.FileName);72 Delete(fn,Pos(ExtractFileExt(fn),fn),Length(fn)); // 去掉扩展名73 74 stream := TMemoryStream.Create;75 stream.LoadFromFile(FPdf.FileName);76 try77 m := stream.Size;78 pt := PAnsiChar(stream.Memory);79 AlterJSIFValue(pt,m);80 stream.Position := 0;81 stream.SaveToFile(FPdf.FileName);82 FreeAndNil(stream);83 84 cpdf := ‘cpdf.exe‘;85 begin86 dir := ExtractFilePath(FPdf.FileName);87 ChgDosName(dir);88 ChDir(dir);89 cpdf := cpdf + ‘ -remove-annotations ‘ + fn + ‘.pdf -o ‘ + fn + ‘.pdf‘;90 RunDosCommand(cpdf);91 end;92 except // 有异常,则将原始的及错误的,保存一份以便查原因。93 if FileExists(FPdf.FileName) then94 RenameFile(FPdf.FileName, dir + fn + ‘_bak.pdf‘);95 stream.Position := 0;96 stream.SaveToFile(dir + fn + ‘_err.pdf‘);97 end;98 end;
提醒:以上代码如果不加变通,粗暴复制是无法测试的!
pdf文件格式多余内容的移除,解除llPDFLib5.2的限制