我需要在我的angularjs网络应用中自动填写pdf表单。pdf表单是在应用程序外部生成的,因此我可以根据需要进行配置。
在我的应用程序中,我只需要加载pdf,修改表单字段并展平文件,以使其不再看起来像表单。
你知道有什么办法吗?
编辑:我找到了iText,但它是一个Java库,不适用于我的项目(该应用程序在平板电脑上运行,因此我正在寻找100%HTML5的东西)
我找到了一个解决方案…不是完美的,但它应该适合大多数要求。它不使用任何服务器(完全符合隐私要求)或库!首先,PDF必须为1.5版(Acrobat6.0或更高版本)。原始pdf可能是另一个版本,但是在创建字段时,必须将其保存为与Acrobat 6.0或更高版本兼容。
假设我有“ myform.pdf”文件(没有表单字段);我使用Acrobat Pro打开了它(我有Acrobat Pro 11,但它应该与其他版本一起使用)。我添加字段并用“代码”(唯一的文本字符串)预填充字段的值(而不是字段名称!)。此代码将由下面的javascript函数查找/替换为所需的字符串,因此可以说’%address%’(您可以添加多个字段,但可以使用不同的代码来区分字段)。如果要使字段外观平坦,请将字段设置为只读。要保存它,请转到“文件”->“另存为其他…”->“优化的PDF”,然后在“与…兼容”(弹出式窗口右上方)下选择“ Acrobat 6.0及更高版本”。
保存文件后,可以通过在文本编辑器中打开格式并查找代码(在我的情况下为’%address%’)来检查格式是否正确。计算发生次数,应该出现3次。
以下功能可做三件事:-更改字段内容-重新计算内容的长度-修复交叉引用表
现在,函数(查看最后的pdf blob的结尾):
@param证书:您的pdf表格(此变量的格式必须与FileReader兼容)
@param更改:字段更改,[{查找:’%address%’,替换为:‘2386 5th Street,纽约,美国’},…]
// Works only for PDF 1.5 (Acrobat 6.0 and later) var fillCertificate = function (certificate, changes) { // replace a a substring at a specific position String.prototype.replaceBetween = function(start, end, what) { return this.substring(0, start) + what + this.substring(end); }; // format number with zeros at the beginning (n is the number and length is the total length) var addLeadingZeros = function (n, length) { var str = (n > 0 ? n : -n) + ""; var zeros = ""; for (var i = length - str.length; i > 0; i--) zeros += "0"; zeros += str; return n >= 0 ? zeros : "-" + zeros; } // Create the reader first and read the file (call after the onload method) var reader = new FileReader(); // To change the content of a field, three things must be done; - change the text of the field, - change the length of the content field, - change the cross table reference reader.onload = function(aEvent) { var string = aEvent.target.result; // Let's first change the content and the content's length var arrayDiff = []; var char; for(var foo = 0; foo < changes.length; foo++) { // Divide the string into a table of character for finding indices char = new Array(string.length); for (var int = 0; int < string.length; int++) { char[int] = string.charAt(int); } // Let's find the content's field to change and change it everywhere var find = changes[foo].find; var replace = changes[foo].replace; var lengthDiff = replace.length - find.length; var search = new RegExp(find, "g"); var match; var lastElements = []; var int = 0; var objectLenPos; var objectLenEnd; // Each time you change the content, compute the offset difference (number of characters). We'll add it later for the cross tables while (match = search.exec(string)) { arrayDiff.push({index: match.index, diff: lengthDiff}); lastElements.push({index: match.index, diff: lengthDiff}); // Find length object if(int == 0){ var length = 0; var index; while(char[match.index - length] != '\r'){ index = match.index - length; length++; } objectLenPos = index + 10; length = 0; while(char[objectLenPos + length] != ' '){ length++; objectLenEnd = objectLenPos + length; } } int++; } var lengthObject = string.slice(objectLenPos, objectLenEnd) + ' 0 obj'; var objectPositionStart = string.search(new RegExp('\\D' + lengthObject, 'g')) + lengthObject.toString().length + 2; var length = 0; var objectPositionEnd; while(char[objectPositionStart + length] != '\r'){ length++; objectPositionEnd = objectPositionStart + length; } // Change the length of the content's field var lengthString = new RegExp('Length ', "g"); var fieldLength; var newLength; string = string.replace(lengthString, function (match, int) { // The length is between the two positions calculated above if (int > objectPositionStart && int < objectPositionEnd) { var length = 0; var end; while (char[int + 7 + length] != '/') { length++; end = int + 7 + length; } fieldLength = string.slice(end - length, end); newLength = parseInt(fieldLength) + lengthDiff; if (fieldLength.length != newLength.toString().length) { arrayDiff.push({index: int, diff: (newLength.toString().length - fieldLength.length)}); } // Let's modify the length so it's easy to find and replace what interests us; the length number itself return "Length%"; } return match; }); // Replace the length with the new one based on the length difference string = string.replace('Length%' + fieldLength, 'Length ' + (newLength).toString()); string = string.replace(new RegExp(find, 'g'), replace); } // FIND xref and repair cross tables // Rebuild the table of character var char = new Array(string.length); for (var int = 0; int < string.length; int++) { char[int] = string.charAt(int); }; // Find XRefStm (cross reference streams) var regex = /XRefStm/g, result, indices = []; while ( (result = regex.exec(string)) ) { indices.push(result.index); } // Get the position of the stream var xrefstmPositions = []; for(var int = 0; int < indices.length; int++){ var start; var length = 0; while(char[indices[int] - 2 - length] != ' '){ start = indices[int] - 2 - length; length++; } var index = parseInt(string.slice(start, start + length)); var tempIndex = parseInt(string.slice(start, start + length)); // Add the offset (consequence of the content changes) to the index for(var num = 0; num < arrayDiff.length; num++){ if(index > arrayDiff[num].index){ index = index + arrayDiff[num].diff; } } string = string.replaceBetween(start, start + length, index); // If there is a difference in the string length then update what needs to be updated if(tempIndex.toString().length != index.toString().length){ arrayDiff.push({index: start, diff: (index.toString().length - tempIndex.toString().length)}); char = new Array(string.length); for (var int = 0; int < string.length; int++) { char[int] = string.charAt(int); }; } xrefstmPositions.push(index); } // Do the same for non-stream var regex = /startxref/g, result, indices = []; while ( (result = regex.exec(string)) ) { indices.push(result.index); } for(var int = 0; int < indices.length; int++){ var end; var length = 0; while(char[indices[int] + 11 + length] != '\r'){ length++; end = indices[int] + 11 + length; } var index = parseInt(string.slice(end - length, end)); var tempIndex = parseInt(string.slice(end - length, end)); for(var num = 0; num < arrayDiff.length; num++){ if(index > arrayDiff[num].index){ index = index + arrayDiff[num].diff; } } string = string.replaceBetween(end - length, end, index); if(tempIndex.toString().length != index.toString().length){ arrayDiff.push({index: end - length, diff: (index.toString().length - tempIndex.toString().length)}); char = new Array(string.length); for (var int = 0; int < string.length; int++) { char[int] = string.charAt(int); }; } xrefstmPositions.push(index); } xrefstmPositions.reverse(); var firstObject, objectLength, end; var offset; // Updated the cross tables for(var int = 0; int < xrefstmPositions.length; int++) { var length = 0; var end; if(char[xrefstmPositions[int]] == 'x'){ offset = 6; } else{ offset = 0; } // Get first object index (read pdf documentation) while(char[xrefstmPositions[int] + offset + length] != ' '){ length++; end = xrefstmPositions[int] + offset + length; } firstObject = string.slice(end - length, end); // Get length of objects (read pdf documentation) length = 0; while(char[xrefstmPositions[int] + offset + 1 + firstObject.length + length] != '\r'){ length++; end = xrefstmPositions[int] + offset + 1 + firstObject.length + length; } objectLength = string.slice(end - length, end); // Replace the offset by adding the differences from the content's field for(var num = 0; num < objectLength; num++){ if(char[xrefstmPositions[int]] == 'x'){ offset = 9; } else{ offset = 3; } // Check if it's an available object if (char[xrefstmPositions[int] + 17 + offset + firstObject.length + objectLength.length + (num * 20)] == 'n') { var objectCall = (parseInt(firstObject) + num).toString() + " 0 obj"; var regexp = new RegExp('\\D' + objectCall, "g"); var m; var lastIndexOf; // Get the last index in case an object is created more than once. (not very accurate and can be improved) while (m = regexp.exec(string)) { lastIndexOf = m.index; } string = string.replaceBetween(xrefstmPositions[int] + offset + firstObject.length + objectLength.length + (num * 20), xrefstmPositions[int] + 10 + offset + firstObject.length + objectLength.length + (num * 20), addLeadingZeros(lastIndexOf + 1, 10)); } if(num == objectLength - 1){ if (char[xrefstmPositions[int] + offset + firstObject.length + objectLength.length + ((num + 1) * 20)] != 't'){ xrefstmPositions.push(xrefstmPositions[int] + offset + firstObject.length + objectLength.length + ((num + 1) * 20)); } } } } // create a blob from the string var byteNumbers = new Array(string.length); for (var int = 0; int < string.length; int++) { byteNumbers[int] = string.charCodeAt(int); } var byteArray = new Uint8Array(byteNumbers); var blob = new Blob([byteArray], {type : 'application/pdf'}); // Do whatever you want with the blob here }; reader.readAsBinaryString(certificate); }
所以代码根本不是干净的,但是可以工作:)
有任何问题请与我联络