Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Attachments #30

Merged
merged 2 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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');
Loading