Skip to content

Commit

Permalink
Merge pull request #1 from omaralalwi/support-store-pdf-files-to-s3
Browse files Browse the repository at this point in the history
support store pdf files to s3 and local storage
  • Loading branch information
omaralalwi authored May 31, 2024
2 parents 6597bfd + dd8740f commit c6312a3
Show file tree
Hide file tree
Showing 18 changed files with 544 additions and 57 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/.idea
/.vagrant
/vendor/
composer.lock
48 changes: 42 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# [Gpdf](https://github.com/omaralalwi/Gpdf) : HTML to PDF Converter for PHP & Laravel

Open Source PHP Package for converting HTML to PDF in PHP & Laravel applications, with out-of-the-box support for Arabic content and other languages. Extends [dompdf](https://github.com/dompdf/dompdf) to add new features and solve issues like Arabic language support.
Open Source PHP Package for converting HTML to PDF in PHP & Laravel applications, support store to s3, with out-of-the-box support for Arabic content and other languages. Extends [dompdf](https://github.com/dompdf/dompdf) to add new features and solve issues like Arabic language support.

## Requirements

Expand Down Expand Up @@ -85,10 +85,12 @@ header('Content-Type: application/pdf');
echo $pdfContent;
```

### Storing Generated PDF Files
### Storing Generated PDF Files ( Local || S3 )

Save a PDF to a specific location using `generateWithStore`:

**Note** By default it store files to local driver.

```php
require_once __DIR__ . '/vendor/autoload.php';

Expand All @@ -107,6 +109,15 @@ $pdfContent = $gpdf->generateWithStore($content, __DIR__ . '/storage/downloads/'
header('Content-Type: application/pdf');
echo $pdfContent;
```
#### generateWithStore params

| Parameter | Type | Description |
|-------------------------------------|--------|-----------------------------------------------------------------------------------------------|
| `html file` | string | The HTML content to be stored. |
| `store path or bucket name with s3` | string | The path where the file will be stored, with S3 store this should bucket name. |
| `file name` | string | The name of the file. |
| `with stream` | bool | If you need to stream the file to the browser after storing, set this to `true`. |
| `sslVerify` | bool | If `with stream` is set to `true`, you should set this to `true` in production to verify SSL. |

### [Demo Native PHP App](https://github.com/omaralalwi/Gpdf-Native-PHP-Demo)
please see this Demo Native PHP app contain more detailed examples and cases like pass dynamic parameters for html file & pass inline configs , .. and another cases.
Expand Down Expand Up @@ -146,6 +157,7 @@ public function generateSecondWayPdf(Gpdf $gpdf)
Stream a PDF directly to the browser using `generateWithStream`:

```php
// by default it store files to local driver (path should in public path).
public function generateAndStream()
{
$html = view('pdf.example-2')->render();
Expand All @@ -157,17 +169,39 @@ public function generateAndStream()

### Storing Generated PDF Files

#### Store Files To local

Save a PDF to storage using `generateWithStore`:

**Note** By default it store files to local driver (ensure that: the store path is access able for read and write).

please see [generateWithStore params](#generateWithStore-params) .
```php
public function generateAndStore()
{
$html = view('pdf.example-2')->render();
$gpdf = app(Gpdf::class);
$storePath = storage_path('app/downloads/users/');
$gpdf->generateWithStore($html, $storePath, 'test-stored-pdf-file');
return response(null, 200, ['Content-Type' => 'application/pdf']);
$gpdf->generateWithStore($html, $storePath, 'test-stored-pdf-file', true, false); // ssl verify should be true in production .
return $file['ObjectURL']; // return file url as string , to store in db or do any action
}
// may be you will face problems with stream in local, so you can disable ssl verify in local, but should enable it in production.
```

#### Store Files To S3
same to store in local, just replace local path with bucket name, and replace `generateWithStore` with `generateWithStoreToS3` .

**Note** Ensure you setup s3 configs in config file.
```php
public function generateAndStoreToS3()
{
$data = $this->getDynamicParams();
$html = view('pdf.example-2',$data)->render();
$gpdf = app(Gpdf::class);
$bucketName = 'your_s3_bucket_name'; // should be read abel and write able .
$file = $gpdf->generateWithStoreToS3($html, $bucketName, 'test-store-pdf-fle', true, true); // with s36 the ssl verify will work in local or production (always secure).
return $file['ObjectURL']; // return file url as string , to store in db or do any action
}
```

### [Demo Laravel App](https://github.com/omaralalwi/Gpdf-Laravel-Demo)
Expand Down Expand Up @@ -224,6 +258,8 @@ php vendor/omaralalwi/gpdf/scripts/install_font.php "tajawal" ./resources/fonts/
## Features

- Compatibility with any Standard PHP application, Or framework.
- store pdf files to S3 or local storage directly.
- stream pdf files from urls (local or s3).
- Supports 17 fonts by default, including 7 that support Arabic.
- Allows for the installation of custom fonts.
- Provides easy integration with Laravel applications.
Expand All @@ -235,8 +271,8 @@ php vendor/omaralalwi/gpdf/scripts/install_font.php "tajawal" ./resources/fonts/
---

## Thanks
- ### [dompdf](https://github.com/dompdf/dompdf) .
- ### [Ar-PHP](https://github.com/khaled-alshamaa/ar-php).
- ### [dompdf](https://github.com/dompdf/dompdf)
- ### [Ar-PHP](https://github.com/khaled-alshamaa/ar-php)


## Testing
Expand Down
7 changes: 6 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
"arabic",
"arabicpdf",
"Gpdf",
"dompdf",
"PDF",
"Arabic",
"Arabic PDF",
"PDF PHP",
"PGPDF",
"PDF Package",
"PDF to s3",
"s3 and pdf",
"store pdf to s3",
"PHP PDF Package",
"PDF Generation",
"PDF Generation Library",
Expand Down Expand Up @@ -47,7 +51,7 @@
"homepage": "https://github.com/omaralalwi/gpdf",
"license": "MIT",
"type": "library",
"version": "1.0.0",
"version": "1.0.1",
"authors": [
{
"name": "omar alalwi",
Expand All @@ -57,6 +61,7 @@
],
"require": {
"php": "^8.1|^8.2|^8.3",
"aws/aws-sdk-php": "^3.308",
"dompdf/dompdf": "^3.0",
"khaled.alshamaa/ar-php": "^6.3"
},
Expand Down
18 changes: 11 additions & 7 deletions config/gpdf.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use Omaralalwi\Gpdf\Enums\{GpdfDefaultSettings as GpdfDefault, GpdfSettingKeys as GpdfSet, GpdfDefaultSupportedFonts};
use Omaralalwi\Gpdf\Enums\{GpdfDefaultSettings as GpdfDefault, GpdfSettingKeys as GpdfSet,
GpdfStorageDrivers, GpdfDefaultSupportedFonts};

/**
* Configuration file for the Gpdf package.
Expand Down Expand Up @@ -54,6 +55,15 @@
*/
GpdfSet::CHROOT => GpdfDefault::CHROOT,

GpdfSet::STORAGE_PATH => GpdfDefault::STORAGE_PATH,

GpdfSet::AWS_BUCKET => '',

GpdfSet::AWS_REGION => '',

GpdfSet::AWS_KEY => '',

GpdfSet::AWS_SECRET => '',
/**
* Enable or disable entity conversion.
* @var bool
Expand All @@ -78,12 +88,6 @@
*/
GpdfSet::LOG_OUTPUT_FILE => GpdfDefault::LOG_OUTPUT_FILE,

/**
* Storage path for generated PDFs.
* @var string
*/
GpdfSet::STORAGE_PATH => GpdfDefault::STORAGE_PAT,

/**
* Default media type for the generated PDFs.
* @var string
Expand Down
74 changes: 42 additions & 32 deletions src/Builders/PdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@

use ArPHP\I18N\Arabic;
use Dompdf\Dompdf;
use Aws\Exception\AwsException;
use Omaralalwi\Gpdf\Clients\S3Client;
use Omaralalwi\Gpdf\Services\{S3Service, LocalFileService};
use Omaralalwi\Gpdf\Enums\GpdfSettingKeys;
use Omaralalwi\Gpdf\Traits\HasGpdfLog;
use Omaralalwi\Gpdf\Traits\HasFile;

class PdfBuilder
{
protected $dompdf;
use HasGpdfLog, HasFile;

protected Dompdf $dompdf;

public function __construct(Dompdf $dompdf)
{
Expand Down Expand Up @@ -99,47 +107,30 @@ public function buildAndStream(string $htmlContent, string $fileName, bool $atta
}
}

/**
* Save the generated PDF to a specified file path.
*
* @param string $htmlContent
* @param string $filePath
* @param string $fileName
* @return void
*/
public function buildAndStore(string $htmlContent, string $filePath, string $fileName): void
public function buildAndStore(S3Service|LocalFileService $storageService, string $htmlContent, string $filePath, string $fileName, bool $withStream = false, bool $verifySsl=true)
{
try {
$formattedContent = $this->formatArabic($htmlContent);
$pdf = $this->dompdf;
$pdf->loadHtml($formattedContent);
$pdf->render();

$this->checkStoraPath($filePath);
$fullFilePath = $this->getFullFilePath($filePath, $fileName);
$this->preparePdf($htmlContent);
$pdfContent = $this->dompdf->output();
$generatedFile = $this->storeFile($storageService, $pdfContent, $filePath, $fileName);
$formattedGeneratedFile = $this->appendObjectURLToGeneratedFile($storageService, $generatedFile);

// Check if the file exists and delete it if it does
if (file_exists($fullFilePath)) {
unlink($fullFilePath);
if($withStream) {
$storageService->streamFromUrl($formattedGeneratedFile['ObjectURL'], $verifySsl);
}

// Create a new file with the PDF content
file_put_contents($fullFilePath, $pdf->output());
return $formattedGeneratedFile;
// stream from url not available with local storage driver.
} catch (\Exception $e) {
echo 'An error occurred while saving the PDF: '. $e->getMessage();
}
}

public function checkStoraPath($storePath)
{
if (!is_dir($storePath)) {
mkdir($storePath, 0755, true);
echo 'An error occurred while saving the PDF: ' . $e->getMessage();
}
}

public function getFullFilePath($filePath, $fileName)
public function appendObjectURLToGeneratedFile($storageService, $generatedFile)
{
return $filePath.'/'.$fileName;
$fileUrl = $storageService->getFileUrl($generatedFile);
$generatedFile['ObjectURL'] = $fileUrl;
return $generatedFile;
}

/**
Expand Down Expand Up @@ -184,4 +175,23 @@ protected function convertEntities(string $subject): string
}
return $subject;
}

protected function storeFile(S3Service|LocalFileService $storageService, $pdfFile, $filePath, $fileName)
{
try {
return $storageService->store($pdfFile, $filePath, $fileName);
} catch (\Exception $e) {
echo $e->getMessage() . "\n";
}
}

protected function streamFromUrl(S3Service|LocalFileService $storageService, $fileUrl)
{
try {
return $storageService->streamFromUrl($fileUrl);
} catch (\Exception $e) {
echo $e->getMessage() . "\n";
}
}

}
32 changes: 32 additions & 0 deletions src/Clients/S3Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Omaralalwi\Gpdf\Clients;

use Omaralalwi\Gpdf\Enums\GpdfSettingKeys;
use Omaralalwi\Gpdf\GpdfConfig;

class S3Client
{
protected $gpdfConfig;

public function __construct(GpdfConfig $gpdfConfig)
{
$this->gpdfConfig = $gpdfConfig;
}

public function initilizeClient()
{
return new \Aws\S3\S3Client([
'region' => $this->gpdfConfig->get(GpdfSettingKeys::AWS_REGION),
'credentials' => [
'key' => $this->gpdfConfig->get(GpdfSettingKeys::AWS_KEY),
'secret' => $this->gpdfConfig->get(GpdfSettingKeys::AWS_SECRET),
],
]);
}

public function getBucket()
{
return $this->gpdfConfig->get(GpdfSettingKeys::AWS_BUCKET);
}
}
10 changes: 10 additions & 0 deletions src/Css/CustomBoxShadow.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Omaralalwi\Gpdf\Css;

use dompdf\Css\Property\AbstractProperty;

class CustomBoxShadow
{

}
2 changes: 1 addition & 1 deletion src/Enums/GpdfDefaultSettings.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class GpdfDefaultSettings
const ALLOWED_PROTOCOLS = ['http', 'https'];
const ARTIFACT_PATH_VALIDATION = true;
const LOG_OUTPUT_FILE = null;
const STORAGE_PAT = __DIR__ . '/../storage/pdfs/';
const STORAGE_PATH = '/public/downloads/pdfs/';
const DEFAULT_MEDIA_TYPE = 'application/pdf';
const DEFAULT_PAPER_SIZE = 'A4';
const DEFAULT_PAPER_ORIENTATION = 'portrait';
Expand Down
4 changes: 4 additions & 0 deletions src/Enums/GpdfSettingKeys.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ class GpdfSettingKeys
const FONT_CACHE = 'fontCache';
const CHROOT = 'chroot';
const STORAGE_PATH = 'storage_path';
const AWS_BUCKET = 'aws_storage_bucket';
const AWS_REGION = 'aws_storage_region';
const AWS_KEY = 'aws_storage_key';
const AWS_SECRET = 'aws_storage_secret';
const CONVERT_ENTITIES = 'convert_entities';
const ALLOWED_PROTOCOLS = 'allowedProtocols';
const ARTIFACT_PATH_VALIDATION = 'artifactPathValidation';
Expand Down
10 changes: 10 additions & 0 deletions src/Enums/GpdfStorageDrivers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Omaralalwi\Gpdf\Enums;

class GpdfStorageDrivers
{
const LOCAL = 'local';
const S3 = 's3';

}
22 changes: 22 additions & 0 deletions src/Factories/StorageServiceFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Omaralalwi\Gpdf\Factories;

use Omaralalwi\Gpdf\Enums\GpdfSettingKeys;
use Omaralalwi\Gpdf\Enums\GpdfStorageDrivers;
use Omaralalwi\Gpdf\Services\{S3Service, LocalFileService};
use Omaralalwi\Gpdf\Clients\S3Client;

class StorageServiceFactory
{
public function getStorageService($driver, $gpdfConfig)
{
switch ($driver) {
case GpdfStorageDrivers::S3:
return new S3Service(new S3Client($gpdfConfig));
case GpdfStorageDrivers::LOCAL:
default:
return new LocalFileService();
}
}
}
Loading

0 comments on commit c6312a3

Please sign in to comment.