I can successfully fill PDF forms,but it looks to be breaking when there are Kids sections in them.
E.g. this is the file I'm working with:
$ pdftk test.pdf dump_data_fields | grep FieldName
FieldName: Text36.0
FieldName: Text36.1
FieldName: Text37.1
FieldName: Text37.0.0
FieldName: Text37.0.1.0
FieldName: Text37.0.1.1
FieldName: Text38
FieldName: Text39.0
...
FieldName: Text41.5.23.1.19.5
FieldName: Text41.5.23.1.19.6
FieldName: Text41.5.23.1.19.7
FieldName: Text41
$ pdftk test.pdf dump_data_fields | grep FieldName | wc -l
402
This works:
<?php
require_once 'fpdm-2.9.2/fpdm.php';
$fields = [
'Text38' => 'Joe'
];
$pdf = new FPDM('test.pdf', 'test.fdf', true);
$pdf->Load($fields);
$pdf->Merge();
$pdf->Output('F', 'test_out.pdf');
Output:
PDF parse
Starting to parse 14336 entries
PDF parse retrieved 1094 refs
Merge info:
26 Field entry values found for 1 field values to fill
Output
Write file test_out.pdf
This does not (same code as above, only different field name):
$fields = [
'Text36.0' => 'Joe'
];
FPDF-Merge Error: field Text36.0 not found
Snip from the FDF file:
/T (Text41)
>>
<<
/Kids [
<<
/V ()
/T (1)
>>
<<
/V ()
/T (0)
>>]
/T (Text36)
>>
<<
/Kids [
<<
/V ()
/T (1)
>>
<<
/Kids [
<<
/Kids [
<<
/V ()
/T (1)
>>
<<
/V ()
/T (0)
>>]
/T (1)
>>
<<
/V ()
/T (0)
>>]
/T (0)
>>]
/T (Text37)
>>
<<
/V ()
/T (Text38)
>>
<<
/Kids [
<<
/V ()
/T (19)
>>
<<
/V ()
/T (18)
>>
So I'm trying to figure out if I'm sending the wrong data in, or if this is a limitation of the FPDM library.
Related
could you help me with this problem?
I am trying to create and download a signed PDF.
Once the generated PDF file exists, I can't sign in because of this error.
"Call to a member function set_signature_certificate() on bool"
in browser and
"Error info at /Users/martinkravec/git/projectname/vendor/ddn/sapp/src/PDFUtilFnc.php:377: PDF version string not found"
in stderr.log.
I am running the following code outside CLI in Laravel 9 with PHP 8.1.9.
config/app.php
in provider:
Barryvdh\DomPDF\ServiceProvider::class,
In aliases:
'PDF' => Barryvdh\DomPDF\Facade::class,
Controller
use PDF;
use ddn\sapp\PDFDoc;
if (!defined('STDERR')) define('STDERR', fopen(__DIR__ . '/../../../storage/logs/stderr.log', 'wb'));
require_once(__DIR__ . '/../../../vendor/autoload.php');
class ClientController extends Controller
{
public function index()
{
$pdf = PDF::loadView('pdf'); // temlate in resources/views/pdf.blade.php
$fileContent = $pdf->stream();
$obj = PDFDoc::from_string($fileContent);
$certificatePath = __DIR__ . '/../../../storage/app/test_certificate.pfx';
$password = '';
$obj->set_signature_certificate($certificatePath, $password);
$signedPdf = $obj->to_pdf_file_s();
return response()->streamDownload(function () use ($signedPdf) {
echo $signedPdf;
}, 'export.pdf');
}
}
When I do
cat ./file.pdf
I get file content starting with:
HTTP/1.0 200 OK
Cache-Control: no-cache, private
Content-Disposition: inline; filename="document.pdf"
Content-Type: application/pdf
Date: Sun, 28 Aug 2022 09:37:03 GMT
%PDF-1.7
1 0 obj
<< /Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R >>
endobj
2 0 obj
<< /Type /Outlines /Count 0 >>
endobj
3 0 obj
<< /Type /Pages
/Kids [6 0 R
19 0 R
]
/Count 2
/Resources <<
/ProcSet 4 0 R
/Font <<
/F1 8 0 R
/F2 13 0 R
>>
/XObject <<
/I1 18 0 R
>>
>>
/MediaBox [0.000 0.000 595.280 841.890]
>>
endobj
4 0 obj
[/PDF /Text /ImageC ]
endobj
5 0 obj
<<
/Producer (���d�o�m�p�d�f� �2�.�0�.�0� �+� �C�P�D�F)
/CreationDate (D:20220828113702+02'00')
/ModDate (D:20220828113702+02'00')
/Title (���D���v�k�y�.�P�D�F)
>>
endobj
6 0 obj
<< /Type /Page
/MediaBox [0.000 0.000 595.280 841.890]
/Parent 3 0 R
/Contents 7 0 R
>>
endobj
7 0 obj
<< /Filter /FlateDecode
/Length 919 >>
stream
I have found the problem.
Instead of reading the PDF file content using
$pdf->stream()
we need to save the file using
$pdf->save($fileName);
which does not generate the first 6 lines of a file and probably some more things causing an error with the version.
Thanks for helping #Peppermintology anyway.
command line (works fine)
$ sudo chPermissions.sh
trying to do this from within a Qt program, using QProcess and have tried the following without success
code:
QString program = "/bin/sh /usr/bin/chPermissions.sh";
m_process->start(program);
m_process->waitForFinished();
qDebug() << m_process->exitCode();
result: 0 (the script ran but without sudo rights & didn't work!)
code:
QString program = "sudo /bin/sh /usr/bin/chPermissions.sh";
m_process->start(program);
m_process->waitForFinished();
qDebug() << m_process->exitCode();
result: 1
code:
QString program = "/usr/bin/sudo /bin/sh /usr/bin/chPermissions.sh";
m_process->start(program);
m_process->waitForFinished();
qDebug() << m_process->exitCode();
result: 1
code:
QString program = "/bin/sh";
QStringList arguments;
arguments << "/usr/bin/chPermissions.sh";
m_process->start(program, arguments);
m_process->waitForFinished();
qDebug() << m_process->exitCode();
result: 0 (the script ran! no sudo rights)
code:
QString program = "/bin/sh";
QStringList arguments;
arguments << sudo << "/usr/bin/chPermissions.sh";
m_process->start(program, arguments);
m_process->waitForFinished();
qDebug() << m_process->exitCode();
result: 127 (command not found)
code:
QString program = "/bin/sh";
QStringList arguments;
arguments << /usr/bin/sudo << "/usr/bin/chPermissions.sh";
m_process->start(program, arguments);
m_process->waitForFinished();
qDebug() << m_process->exitCode();
result: 2 (Misuse of shell builtins)
code:
QString program = "/bin/bash";
QStringList arguments;
arguments << "-c" << "\"/usr/bin/sudo /usr/bin/chPermissions.sh"\";
m_process->start(program, arguments);
m_process->waitForFinished();
qDebug() << m_process->exitCode();
result: 127
code:
QString shellCommandLine = "/usr/bin/chPermissions.sh";
QStringList arguments;
arguments << "-c" << shellCommandLine;
m_process->start("/bin/sh", arguments);
m_process->waitForFinished();
qDebug() << "Exit Code: " << m_process->exitCode();
result: 0 (the script ran!)
code:
QString shellCommandLine = "sudo /usr/bin/chPermissions.sh";
QStringList arguments;
arguments << "-c" << shellCommandLine;
m_process->start("/bin/sh", arguments);
m_process->waitForFinished();
qDebug() << "Exit Code: " << m_process->exitCode();
result: 1
code:
QString shellCommandLine = "/usr/bin/sudo /usr/bin/chPermissions.sh";
QStringList arguments;
arguments << "-c" << shellCommandLine;
m_process->start("/bin/sh", arguments);
m_process->waitForFinished();
qDebug() << "Exit Code: " << m_process->exitCode();
result: 1
code:
QString shellCommandLine = "/usr/bin/chPermissions.sh";
QStringList arguments;
arguments << "-c" << shellCommandLine;
m_process->start("/bin/sh", arguments);
m_process->waitForFinished();
qDebug() << "Exit Code: " << m_process->exitCode();
result: 0 (script ran without sudo rights)
Anyone able to let me know the secret of how to do this? (or put me out of my misery if it's not possible)
TIA
Andy
Not solved as such.
I got round this by
using one of the options that actually runs the script (without su rights), and
granting permission in sudoers, to user, to run the command in the script without password prompt.
hope that makes sense.
not happy, but it works.
This question already has answers here:
Regular expression is being too greedy
(5 answers)
Non-greedy match is still too greedy
(2 answers)
Closed 3 years ago.
I have this text:
<< /T (D3K_indicatif[0]) /V () >> << /T (D3P_pays[0]) /V () >> << /T (D1H_homme[0]) /V / >> << /T (D3K_indicatif[0]) /V () >>
I would like to catch << /T (D3P_pays[0]) /V () >>
I did this regex
/<< \/T\s.{0,}\(D3P_pays\[0\]\) \/V \(\) >>/gm but this regex catch << /T (D3K_indicatif[0]) /V () >> << /T (D3P_pays[0]) /V () >>
I don't understand why I have << /T (D3K_indicatif[0]) /V () >> in my match.
I am signing PDF files server side with PHP and I want Adobe Reader to display this banner on my resulting PDF saying that the file has been successfuly signed :
I am using code from the TCPDF library to achieve this (I had to modify some code to fit my needs).
I based my work on these two documents from Adobe official documentation : doc1 and doc2.
What I do :
Add the signature tags to the PDF file :
// The ID of the last object of the PDF + 1
$sigobjid = preg_match_all("/([0-9]+) 0 obj/", $pdfdoc, $output_array);
$sigobjid = end($output_array[1]) + 1;
// Write the signature tags where needed
$index_to_write = strrpos($pdfdoc,"endobj") + 6;
$signature_tag = PHP_EOL . $sigobjid . ' 0 obj '. PHP_EOL . '<< /Type /Sig /Filter /Adobe.PPKLite /SubFilter /adbe.pkcs7.detached '. TCPDF_STATIC::$byterange_string . ' /Contents<'.str_repeat('0', $tcpdf->get_signature_max_length()).'> >>' . PHP_EOL . 'endobj';
$pdfdoc = substr_replace($pdfdoc, $signature_tag, $index_to_write, 0);
Compute and replace the ByteRange
Compute the hash of the file like this :
$hash_result = hash('sha256', $pdfdoc);
Sign the resulting hash client side with forge.js : I use a PFX file that I parse and then create a PKCS7 using the data contained in the PFX.
Send the hash to server.
Add the signature to the PDF in the Content tag.
EDIT : Thanks to #mkl comment I also tried to reference my Sig object with the AcroForm with the following lines into my PDF file :
11 0 obj
<< /Type /Sig /Filter /Adobe.PPKLite /SubFilter /adbe.pkcs7.detached /ByteRange[0 2846 14590 507] /Contents< ...
endobj
12 0 obj
<</AcroForm 11 0 R >>
endobj
It is not working either. How should I fill the AcroForm field ?
The resulting PDF is readable by Adobe Reader but the blue banner does not appear, why ?
Okay I solved it myself so for these who were wondering, you need those fields :
1 0 obj
<<
/Type /Catalog /AcroForm << /Fields [12 0 R 13 0 R] /NeedAppearances false /SigFlags 3 /DR << /Font << /F1 14 0 R >> >> /DA (/F1 0 Tf 0 g) /Q 0 >> /Perms << /DocMDP 11 0 R >>
>>
endobj
4 0 obj
<<
/Type /Page
...
endobj
11 0 obj
<< /Type /Sig /Filter /Adobe.PPKLite /SubFilter /adbe.pkcs7.detached /ByteRange[0 3153 14897 922] /Contents<...> /Reference [ << /Type /SigRef /DigestMethod /SHA256 /TransformMethod /DocMDP /TransformParams << /Type /TransformParams /P 2 /V /1.2 >> >> ] >>
endobj
12 0 obj
<< /Type /Annot /Subtype /Widget /Rect [510.236220 572.598661 552.755906 615.118346] /P 4 0 R /F 4 /FT /Sig /T (Signer Name) /Ff 0 >>
endobj
13 0 obj
<< /Type /Annot /Subtype /Widget /Rect [510.236220 572.598661 552.755906 615.118346] /P 4 0 R /F 4 /FT /Sig /T (Signer Name) /Ff 0 /V 11 0 R >>
endobj
14 0 obj
<</Type /Font /Subtype /Type1 /BaseFont /Helvetica /Name /F1 /Encoding /WinAnsiEncoding >>
endobj
You basically need :
1 Font object (obj 14 in this example)
1 Sig object (obj 11) containing the signature in the 'contents' field
1 Page object (obj 4)
2 Annot object (obj 12 and 13) referencing the first page object with the P flag and the Sig object with the V flag
1 AcroForm field inside your main Catalog (obj 1) referencing your 2 Annot object in the Fields array, referencing your font in the F1 field, and your signature in the DocMDP field.
EDIT : The DocMDP field is not mendatory and only one Annot is needed. No Font required either.
I'm trying to send a Json array over using an AJAX call to a controller function which processes the data using a FPDF library which is already implemented into Codeigniter. I can see the PDF data being returned, but how would I go about opening this PDF data in a new window so the PDF can be saved. It doesn't matter if the PDF cant be viewed i just need to save the generated file.
Here's the code I have so far:
Jquery Code
<script defer="defer" type="text/javascript">
$(document).ready(function() {
$('a#export').click(function(exportRecord) {
var postData = {
'record_type' : 1,
'title' : 'Some Title Here',
'content' : 'Some content here',
};
$.ajax({
url: "<?php echo base_url().'admin/pdf/createPDF';?>",
type:'POST',
data: postData,
dataType: 'json',
success: function(data){
window.open(
'data:application/pdf,'+encodeURIComponent(data),
'Batch Print',
'width=600,height=600,location=_newtab'
);
} // End of success function of ajax form
}); // End of ajax call
return false;
});
});
</script>
The Controller Function
<?php
function createPDF(){
$content = $this->input->post('content');
$font_directory = './assets/fpdf_fonts/';
set_realpath($font_directory);
define('FPDF_FONTPATH',$font_directory);
$data = $this->fpdf->Open();
$data = $this->fpdf->AddPage();
$data = $this->fpdf->SetFont('Arial','',8);
$data = $this->fpdf->Cell(80);
$data = $this->fpdf->Cell(0,0,$content,0,1,'R');
$data = $this->fpdf->Output();
}
The JSON Response
%PDF-1.3
3 0 obj
<</Type /Page
/Parent 1 0 R
/Resources 2 0 R
/Contents 4 0 R>>
endobj
4 0 obj
<</Filter /FlateDecode /Length 72>>
stream
x�3R��2�35W(�r
Q�w3T��30PISp
��陛)X��(��(hx����+���i*�d�����F
endstream
endobj
1 0 obj
<</Type /Pages
/Kids [3 0 R ]
/Count 1
/MediaBox [0 0 595.28 841.89]
>>
endobj
5 0 obj
<</Type /Font
/BaseFont /Helvetica
/Subtype /Type1
/Encoding /WinAnsiEncoding
>>
endobj
2 0 obj
<<
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
/Font <<
/F1 5 0 R
>>
/XObject <<
>>
>>
endobj
6 0 obj
<<
/Producer (FPDF 1.7)
/CreationDate (D:20120309201958)
>>
endobj
7 0 obj
<<
/Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000228 00000 n
0000000411 00000 n
0000000009 00000 n
0000000087 00000 n
0000000315 00000 n
0000000515 00000 n
0000000590 00000 n
trailer
<<
/Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
639
%%EOF
""
Maybe a bit late, there is my answer I got the system working.
For a reason that I don't understand it doesn't work with ajax call.
This post helped has done the magic :
JavaScript post request like a form submit
HTML (here the call the PDF generator in PHP with POST parameter ):
post('GenerateStockLabelsWPDF.php', { CSVTable: JSON.stringify(encodeURIComponent(CSVtable))});
Then PHP (For the sample it's a fixed text, you can adapt it by getting variable via _post):
<?php
include "php/fpdf/fpdf.php";
$pdf = new FPDF();
$pdf->Open();
$pdf->SetMargins(0, 0);
$pdf->SetAutoPageBreak(false);
$pdf->AddPage();
$content = 'thisis a test';
$pdf->SetXY(20, 20);
$pdf->SetFont('Helvetica', 'B', 10);
$pdf->MultiCell(150, 5, $content, 0, 'L');
$pdf->Output('Labels.pdf', 'D');
exit;
?>
Save the generated file where? On the users computer? On the server?
Looks like $data in the createPFD method is the PDF.
For the former, check http://codeigniter.com/user_guide/helpers/download_helper.html
force_download('coolPDF.pdf', $data);
For the latter, check http://codeigniter.com/user_guide/helpers/file_helper.html
write_file('/path/to/these/PDFs/fileName.pdf', $data);