使用javascript填写pdf表单(仅限客户端)

时间:2014-07-24 21:35:16

标签: javascript angularjs pdf

我需要在angularjs webapp中自动填写pdf表格。 pdf表单是在应用程序外部生成的,因此我可以根据需要对其进行配置。

在我的应用中,我只需要加载pdf,修改表单字段并展平文件,使其不再像表单一样。

你知道怎么办?

编辑: 我找到了iText,但它是一个不能为我的项目工作的java库(应用程序在平板电脑上运行,因此我正在寻找100%HTML5的东西)

2 个答案:

答案 0 :(得分:14)

我找到了一个解决方案......不完美但它应该符合大多数要求。它不使用任何服务器(完全符合隐私要求)或库!首先,PDF必须是1.5版(Acrobat 6.0或更高版本)。原始pdf可以是另一个版本,但是当您创建字段时,必须将其保存为与Acrobat 6.0或更高版本兼容。如果您想确保格式正确,可以查看there

所以,让我们说我有myform.pdf'文件(没有表单字段);我用Acrobat Pro打开它(我有Acrobat Pro 11,但它应该与其他版本一起使用)。我添加字段并使用'代码'预先填写字段的值(不是字段名称!)。 (唯一文本字符串)。这段代码将通过下面的javascript函数找到/替换为您想要的字符串,所以请说“'%address%' (您可以添加多个字段,但使用不同的代码来区分字段)。如果要使字段外观平整,请将字段设置为只读。要保存它,请转到文件 - >另存为... - >优化PDF并选择" Acrobat 6.0及更高版本"在Make兼容下(弹出窗口的右上角)。

保存文件后,您可以通过在文本编辑器中打开并查找代码(在我的情况下'%地址%')来检查格式是否正确。计算出现次数,应该出现三次。

以下功能有三个作用: - 更改字段内容 - 重新计算内容的长度 - 修复交叉引用表

现在这个函数(看看最后的pdf blob的结尾):

@param证书:您的pdf表格(此变量的格式必须与FileReader兼容)

@param更改:字段更改,[{find:'%address%&#39 ;,替换:' 2386第五街,纽约,美国'},...]

// 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);

    }

所以代码根本不干净但它有效:)

如果您有任何疑问,请告诉我

答案 1 :(得分:2)

据我所知,平板电脑上没有客户端应用程序可以做到这一点。

这意味着您需要服务器端支持,iText确实是其中的产品之一。另一个是Appligent的FDFMerge,它填充并可以设置为展平。