dealfonso / sapp

Simple and Agnostic PDF Document Parser in PHP - sign PDF docs using PHP
GNU Lesser General Public License v3.0
110 stars 29 forks source link

[enhancement] Make it macroable #36

Closed parallels999 closed 1 year ago

parallels999 commented 1 year ago

Support Closure Macros for common tasks

It could be really helpful

Examples: https://github.com/spatie/macroable/blob/main/src/Macroable.php https://github.com/illuminate/macroable/blob/master/Traits/Macroable.php https://github.com/fawno/FPDF/blob/master/src/Traits/PDFMacroableTrait.php https://github.com/mike42/escpos-php/commit/dd16a6d583dc7ceecf54610f933865750d68eb7d

Code:

declare(strict_types=1);

use BadMethodCallException;
use Closure;

namespace ddn\sapp;

trait MacroableTrait {
    /**
     * The registered string macros.
     *
     * @var array
     */
    protected static $macros = [];

    /**
     * Register a custom macro.
     *
     * @param  string  $name
     * @param  callable  $macro
     * @return void
     */
    public static function macro($name, $macro)
    {
        static::$macros[$name] = $macro;
    }

    /**
     * Dynamically handle calls to the class.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     *
     * @throws \BadMethodCallException
     */
    public function __call($method, $parameters)
    {
        if (! isset(static::$macros[$name])) {
            throw new BadMethodCallException(sprintf(
                'Method %s::%s does not exist.', static::class, $method
            ));
        }

        $macro = static::$macros[$method];

        if ($macro instanceof Closure) {
            $macro = $macro->bindTo($this, static::class);
        }

        return $macro(...$parameters);
    }
}

Now we can customize class adding our code, for example:

PDFDoc::macro('set_signature_appearance_calculating', function($image, $page = 0) {
    $position = [ ];
    $image = $argv[2];
    $imagesize = @getimagesize($image);
    if ($imagesize === false) {
        fwrite(STDERR, "failed to open the image $image");
        return;
    }
    $pagesize = $this->get_page_size($page);
    if ($pagesize === false)
        return p_error("failed to get page size");

    $pagesize = explode(" ", $pagesize[0]->val());
    // Calculate the position of the image according to its size and the size of the page;
    //   the idea is to keep the aspect ratio and center the image in the page with a size
    //   of 1/3 of the size of the page.
    $p_x = intval("". $pagesize[0]);
    $p_y = intval("". $pagesize[1]);
    $p_w = intval("". $pagesize[2]) - $p_x;
    $p_h = intval("". $pagesize[3]) - $p_y;
    $i_w = $imagesize[0];
    $i_h = $imagesize[1];

    $ratio_x = $p_w / $i_w;
    $ratio_y = $p_h / $i_h;
    $ratio = min($ratio_x, $ratio_y);

    $i_w = ($i_w * $ratio) / 3;
    $i_h = ($i_h * $ratio) / 3;
    $p_x = $p_w / 3;
    $p_y = $p_h / 3;

    // Set the image appearance
    $this->set_signature_appearance($page, [ $p_x, $p_y, $p_x + $i_w, $p_y + $i_h ], $image);
});

Finally, we can reuse set_signature_appearance_calculating anywhere

$obj = PDFDoc::from_string($pdfPath);
$image= file_get_contents($imagePath);
// Set the image appearance and the certificate file
$obj->set_signature_appearance_calculating($image, $obj->get_page_count()-1);
$obj->set_signature_certificate($cert, $password);
$docsigned = $obj->to_pdf_file_s();
if ($docsigned)
    echo $docsigned;
dealfonso commented 1 year ago

Hi,

sorry but I do not have used Closures. Why not simply create a subclass of the PDFDoc class?

(e.g.)

class MyPDFDoc extends ddn\sapp\PDFDoc {
 function set_signature_appearance_calculating($image, $page = 0) {
    $position = [ ];
    $image = $argv[2];
    $imagesize = @getimagesize($image);
    if ($imagesize === false) {
        fwrite(STDERR, "failed to open the image $image");
        return;
    }
    $pagesize = $this->get_page_size($page);
    if ($pagesize === false)
        return p_error("failed to get page size");

    $pagesize = explode(" ", $pagesize[0]->val());
    // Calculate the position of the image according to its size and the size of the page;
    //   the idea is to keep the aspect ratio and center the image in the page with a size
    //   of 1/3 of the size of the page.
    $p_x = intval("". $pagesize[0]);
    $p_y = intval("". $pagesize[1]);
    $p_w = intval("". $pagesize[2]) - $p_x;
    $p_h = intval("". $pagesize[3]) - $p_y;
    $i_w = $imagesize[0];
    $i_h = $imagesize[1];

    $ratio_x = $p_w / $i_w;
    $ratio_y = $p_h / $i_h;
    $ratio = min($ratio_x, $ratio_y);

    $i_w = ($i_w * $ratio) / 3;
    $i_h = ($i_h * $ratio) / 3;
    $p_x = $p_w / 3;
    $p_y = $p_h / 3;

    // Set the image appearance
    $this->set_signature_appearance($page, [ $p_x, $p_y, $p_x + $i_w, $p_y + $i_h ], $image);
  }
}
parallels999 commented 1 year ago

Extend is exactly what I'm trying to avoid, i'm using laravel it is more readable to use a ServiceProvider for macro register than creating a new custom class instead of using the same default one ddn\sapp\PDFDoc

This is laravel https://github.com/illuminate/macroable/blob/master/Traits/Macroable.php