Skip to content

Commit

Permalink
Merge pull request #30 from erikn69/patch-8
Browse files Browse the repository at this point in the history
Support Attachments
  • Loading branch information
alphp authored May 2, 2024
2 parents 6ea4dd0 + 8df38dc commit ff4e378
Show file tree
Hide file tree
Showing 15 changed files with 285 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ fpdf/** eol=crlf
/scripts/*/ex*.pdf export-ignore
/scripts/*/ex.php export-ignore
/scripts/*/info.htm export-ignore
/scripts/*/*.txt export-ignore
/scripts/*/*.png export-ignore
/scripts/*/*.jpg export-ignore
/scripts/*/*.json export-ignore
Expand Down
123 changes: 123 additions & 0 deletions scripts/Attachments/AttachmentsTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php
declare(strict_types=1);

namespace FPDF\Scripts\Attachments;
//http://www.fpdf.org/en/script/script95.php

trait AttachmentsTrait {

protected $files = array();
protected $n_files;
protected $open_attachment_pane = false;

/**
* Add a attachment
*
* @param string $file path to the file to attach.
* @param string $name the name under which the file will be attached, the default value is taken from file.
* @param string $desc an optional description.
* @return void
*/
public function Attach($file, $name='', $desc='')
{
if($name=='')
{
$p = strrpos($file,'/');
if($p===false)
$p = strrpos($file,'\\');
if($p!==false)
$name = substr($file,$p+1);
else
$name = $file;
}
$this->files[] = array('file'=>$file, 'name'=>$name, 'desc'=>$desc);
}

/**
* Force the PDF viewer to open the attachment pane when the document is loaded
*
* @return void
*/
public function OpenAttachmentPane()
{
$this->open_attachment_pane = true;
}

protected function _putfiles()
{
if(empty($this->files)) return;

foreach($this->files as $i=>&$info)
{
$file = $info['file'];
$name = $info['name'];
$desc = $info['desc'];

$fc = file_get_contents($file);
if($fc===false)
$this->Error('Cannot open file: '.$file);
$size = strlen($fc);
$date = @date('YmdHisO', filemtime($file));
$md = 'D:'.substr($date,0,-2)."'".substr($date,-2)."'";;

$this->_newobj();
$info['n'] = $this->n;
$this->_put('<<');
$this->_put('/Type /Filespec');
$this->_put('/F ('.$this->_escape($name).')');
$this->_put('/UF '.$this->_textstring($name));
$this->_put('/EF <</F '.($this->n+1).' 0 R>>');
if($desc)
$this->_put('/Desc '.$this->_textstring($desc));
$this->_put('/AFRelationship /Unspecified');
$this->_put('>>');
$this->_put('endobj');

$this->_newobj();
$this->_put('<<');
$this->_put('/Type /EmbeddedFile');
$this->_put('/Subtype /application#2Foctet-stream');
$this->_put('/Length '.$size);
$this->_put('/Params <</Size '.$size.' /ModDate '.$this->_textstring($md).'>>');
$this->_put('>>');
$this->_putstream($fc);
$this->_put('endobj');
}
unset($info);

$this->_newobj();
$this->n_files = $this->n;
$a = array();
foreach($this->files as $i=>$info)
$a[] = $this->_textstring(sprintf('%03d',$i)).' '.$info['n'].' 0 R';
$this->_put('<<');
$this->_put('/Names ['.implode(' ',$a).']');
$this->_put('>>');
$this->_put('endobj');
}

protected function _putresources()
{
parent::_putresources();
$this->_putfiles();
}

protected function _putfilescatalog()
{
if(empty($this->files)) return;

$this->_put('/Names <</EmbeddedFiles '.$this->n_files.' 0 R>>');
$a = array();
foreach($this->files as $info)
$a[] = $info['n'].' 0 R';
$this->_put('/AF ['.implode(' ',$a).']');
if($this->open_attachment_pane)
$this->_put('/PageMode /UseAttachments');
}

protected function _putcatalog()
{
parent::_putcatalog();
$this->_putfilescatalog();
}
}
47 changes: 47 additions & 0 deletions scripts/Attachments/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# AttachmentsTrait
![GitHub license](https://img.shields.io/badge/license-FPDF-green)
[![Author](https://img.shields.io/badge/author-Olivier-blue)](mailto:[email protected]?subject=Bookmarks)

This script allows to attach files to the PDF.

## Usage
The method to attach a file is:

```php
/**
* Add a attachment
*
* @param string $file path to the file to attach.
* @param string $name the name under which the file will be attached. The default value is taken from file.
* @param string $desc an optional description.
* @return void
*/
AttachmentsTrait::Attach(string file [, string name [, string desc]]);
```

The `OpenAttachmentPane()` method is also provided to force the PDF viewer to open the attachment pane when the document is loaded.

## Example

```php
<?php
declare(strict_types=1);

require dirname(dirname(__DIR__)) . '/fpdf/fpdf.php';
require __DIR__ . '/AttachmentsTrait.php';

use FPDF\Scripts\Attachments\AttachmentsTrait;

$pdf = new class extends FPDF {
use AttachmentsTrait;
};

$pdf->Attach('attached.txt');
$pdf->OpenAttachmentPane();
$pdf->AddPage();
$pdf->SetFont('Arial','',14);
$pdf->Write(5,'This PDF contains an attached file.');

$pdf->Output('F', __DIR__ . '/example.pdf');
```
[Result](ex.pdf)
1 change: 1 addition & 0 deletions scripts/Attachments/attached.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Attached file.
Binary file added scripts/Attachments/ex.pdf
Binary file not shown.
19 changes: 19 additions & 0 deletions scripts/Attachments/ex.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);

require dirname(dirname(__DIR__)) . '/fpdf/fpdf.php';
require __DIR__ . '/AttachmentsTrait.php';

use FPDF\Scripts\Attachments\AttachmentsTrait;

$pdf = new class extends FPDF {
use AttachmentsTrait;
};

$pdf->Attach('attached.txt');
$pdf->OpenAttachmentPane();
$pdf->AddPage();
$pdf->SetFont('Arial','',14);
$pdf->Write(5,'This PDF contains an attached file.');

$pdf->Output('F', __DIR__ . '/example.pdf');
31 changes: 31 additions & 0 deletions scripts/Attachments/info.htm
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Attachments</title>
<style type="text/css">
body {font-family:"Times New Roman",serif}
h1 {font:bold 135% Arial,sans-serif; color:#4000A0; margin-bottom:0.9em}
h2 {font:bold 95% Arial,sans-serif; color:#900000; margin-top:1.5em; margin-bottom:1em}
</style>
</head>
<body>
<h1>Attachments</h1>
<h2>Informations</h2>
Author: <a href="mailto:[email protected]?subject=Attachments">Olivier</a><br>
License: FPDF
<h2>Description</h2>
This script allows to attach files to the PDF. The method to attach a file is:<br>
<br>
<tt>Attach(<b>string</b> file [, <b>string</b> name [, <b>string</b> desc]])</tt><br>
<br>
<tt><u>file</u></tt>: path to the file to attach.<br>
<tt><u>name</u></tt>: the name under which the file will be attached. The default value is taken from <tt>file</tt>.<br>
<tt><u>desc</u></tt>: an optional description.<br>
<br>
The <code>OpenAttachmentPane()</code> method is also provided to force the PDF viewer to open the attachment
pane when the document is loaded.<br>
<br>
<strong>Note:</strong> this feature is supported by Adobe Reader but not by all alternative readers.
</body>
</html>
15 changes: 10 additions & 5 deletions scripts/PDFBookmark/PDFBookmarkTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,22 @@ protected function _putbookmarks()

protected function _putresources()
{
parent::_putresources();
$this->_putbookmarks();
parent::_putresources();
$this->_putbookmarks();
}

protected function _putcatalog()
{
parent::_putcatalog();
protected function _putbookmarkscatalog()
{
if(count($this->outlines)>0)
{
$this->_put('/Outlines '.$this->outlineRoot.' 0 R');
$this->_put('/PageMode /UseOutlines');
}
}

protected function _putcatalog()
{
parent::_putcatalog();
$this->_putbookmarkscatalog();
}
}
1 change: 1 addition & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ $pdf = new class extends FPDF {
45. [PDFMemImage](PDFMemImage) by **Olivier** (2004-04-05)
69. [PDFDraw](PDFDraw) by **David Hernández Sanz** (2005-01-16)
88. [PDFCode128](PDFCode128) by **Roland Gautier** (2016-01-31)
95. [Attachments](Attachments) by **Olivier** (2012-04-29)
18 changes: 17 additions & 1 deletion src/FawnoFPDF.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Fawno\FPDF\Traits\PDFMacroableTrait;
use Fawno\FPDF\PDFWrapper;
use Fawno\FPDF\Traits\FontsTrait;
use FPDF\Scripts\Attachments\AttachmentsTrait;
use FPDF\Scripts\PDFBookmark\PDFBookmarkTrait;
use FPDF\Scripts\PDFCode128\PDFCode128Trait;
use FPDF\Scripts\PDFDraw\PDFDrawTrait;
Expand All @@ -19,7 +20,14 @@
class FawnoFPDF extends PDFWrapper {
use FontsTrait;
use PDFMacroableTrait;
use PDFBookmarkTrait { PDFBookmarkTrait::_putresources as PDFBookmark_putresources; }
use AttachmentsTrait {
AttachmentsTrait::_putresources as Attachments_putresources;
AttachmentsTrait::_putcatalog as Attachments_putcatalog;
}
use PDFBookmarkTrait {
PDFBookmarkTrait::_putresources as PDFBookmark_putresources;
PDFBookmarkTrait::_putcatalog as PDFBookmark_putcatalog;
}
use PDFProtectionTrait { PDFProtectionTrait::_putresources as PDFProtection_putresources; }
use PDFRotateTrait;
use CMYKTrait;
Expand All @@ -33,5 +41,13 @@ protected function _putresources () {
parent::_putresources();
$this->_putbookmarks();
$this->_encrypresources();
$this->_putfiles();
}

protected function _putcatalog()
{
parent::_putcatalog();
$this->_putbookmarkscatalog();
$this->_putfilescatalog();
}
}
26 changes: 26 additions & 0 deletions tests/Scripts/AttachmentsTraitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);

namespace Fawno\FPDF\Tests\Scripts;

use FPDF;
use FPDF\Scripts\Attachments\AttachmentsTrait;
use Fawno\FPDF\Tests\TestCase;

class AttachmentsTraitTest extends TestCase {
public function testAttachmentsTrait () {
$pdf = new class extends FPDF {
use AttachmentsTrait;
};

$pdf->Attach(__DIR__ . '/../../scripts/Attachments/attached.txt');
$pdf->OpenAttachmentPane();
$pdf->AddPage();
$pdf->SetFont('Arial','',14);
$pdf->Write(5,'This PDF contains an attached file.');

$this->assertFileCanBeCreated($pdf);

$this->assertPdfIsOk($pdf);
}
}
2 changes: 1 addition & 1 deletion tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function assertPdfAreEquals (string $expected, string $actual) : void {
$diff = [];
foreach ($differences as $oid => $obj) {
$keys = (is_a($obj->get_value(), PDFValue::class) ? $obj->get_keys() : false) ?: ['OID_' . $obj->get_oid()];
$diff = array_merge($diff, array_diff($keys, ['Producer', 'CreationDate', 'Title', 'O', 'U']));
$diff = array_merge($diff, array_diff($keys, ['Producer', 'CreationDate', 'ModDate', 'Title', 'O', 'U', 'UF', 'Params', 'Names']));
}

$this->assertEquals([], $diff, 'The PDFs contents have differences.');
Expand Down
Binary file modified tests/examples/example.pdf
Binary file not shown.
Binary file added tests/examples/exampleAttachmentsTraitTest.pdf
Binary file not shown.
8 changes: 8 additions & 0 deletions tests/test.php
Original file line number Diff line number Diff line change
Expand Up @@ -279,4 +279,12 @@
$pdf->SetXY(50, 195);
$pdf->Write(5, 'ABC sets combined: "' . $code . '"');

//Attachments
$pdf->AddPage();
$pdf->Bookmark('Attachments', false);
$pdf->Attach(__DIR__ . '/../scripts/Attachments/attached.txt');
$pdf->OpenAttachmentPane();
$pdf->SetFont('Arial','',14);
$pdf->Write(5,'This PDF contains an attached file.');

$pdf->Output('F', __DIR__ . '/example.pdf');

0 comments on commit ff4e378

Please sign in to comment.