picsi/sideolclient

Sideol Engine 的 PHP 客户端

1.0.4 2023-02-20 15:47 UTC

This package is auto-updated.

Last update: 2024-09-20 20:05:56 UTC


README

Build And Testing codecov

Sideol PHP 客户端

用于与 Sideol Engine 交互的 PHP 客户端

此包是 Sideol 的 PHP 客户端,API 用于与 Chromium 和 LibreOffice 等强大的工具交互,可以将多种文档格式(HTML、Markdown、Word、Excel 等)转换为 PDF 文件,等等!

快速示例

可以将目标 URL 转换为 PDF 并将其保存到指定的目录

use Picsi\Sideolclient\Sideol;

// Converts a target URL to PDF and saves it to a given directory.
$filename = Sideol::save(
    Sideol::chromium($apiUrl)->url('https://my.url'), 
    $pathToSavingDirectory
);

还可以将 Office 文档进行转换和合并

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

// Converts Office documents to PDF and merges them.
$response = Sideol::send(
    Sideol::libreOffice($apiUrl)
        ->merge()
        ->convert(
            Stream::path($pathToDocx),
            Stream::path($pathToXlsx)
        )
);

要求

此包需要Sideol,一个用于 PDF 文件的基于 Docker 的无状态 API

安装

此包不能使用 Composer 安装,它是一个私有仓库。一旦您下载了仓库,您可以使用以下设置使用 Composer 安装它。

{
    "repositories": [
        {
            "type" : "path",
            "url" : "./sidoelclient"
        }   
    ],
    "require": {
        "picsi/sideolclient" : "*"
    },
}

我使用 PSR-7 HTTP 消息接口(即 RequestInterfaceResponseInterface)以及 PSR-18 HTTP 客户端接口(即 ClientInterface)。对于后者,您可能需要一个适配器才能使用您喜欢的客户端库。查看可用的适配器

如果您不确定应该使用哪个适配器,请考虑使用 php-http/guzzle7-adapter

composer require php-http/guzzle7-adapter

用法

向 API 发送请求

在创建了 HTTP 请求(见下文)后,您有两个选项

  1. 从 API 获取响应并根据您的需求处理它。
  2. 将结果文件保存到指定的目录。

在以下示例中,我们假设 Sideol Engine API 可在 https://:3000 上找到。

获取响应

您可以使用任何能够处理 PSR-7 RequestInterface 的 HTTP 客户端调用 API

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium('https://:3000')
    ->url('https://my.url');
    
$response = $client->sendRequest($request);

如果您有一个兼容 PSR-18 的 HTTP 客户端(见 安装),您也可以使用 Sideol::send

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium('https://:3000')
    ->url('https://my.url');

try {
    $response = Sideol::send($request);
    return $response;
} catch (SideolApiError $e) {
    // $e->getResponse();
}

此助手将解析响应,如果它不是 2xx,则抛出异常。如果您希望直接将响应返回给浏览器,这特别有用。

您还可以显式设置 HTTP 客户端

use  Picsi\Sideolclient\Sideol;

$response = Sideol::send($request, $client);

保存结果文件

如果您有一个兼容 PSR-18 的 HTTP 客户端(见 安装),您可以使用 Sideol::save

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium('https://:3000')
    ->url('https://my.url');
    
$filename = Sideol::save($request, '/path/to/saving/directory');

它返回结果文件的名称。默认情况下,Sideol 创建一个 UUID 文件名(例如,36a1558b-02a5-4a45-a950-2f8f8fd7f644),具有 .zip.pdf 文件扩展名。

您还可以显式设置 HTTP 客户端

use Picsi\Sideolclient\Sideol;

$response = Sideol::save($request, $pathToSavingDirectory, $client);

文件名

您可以使用以下方法覆盖输出文件名

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium('https://:3000')
    ->outputFilename('my_file')
    ->url('https://my.url');

Sideol 将自动添加正确的文件扩展名。

跟踪或请求 ID

默认情况下,Sideol 创建一个 UUID 跟踪,用于在其日志中标识一个请求。您可以使用以下方法覆盖其值

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium('https://:3000')
    ->trace('debug')
    ->url('https://my.url');

它将使用您的值设置 Sideol-Trace 标头。您还可以覆盖默认的标头名称

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium('https://:3000')
    ->trace('debug', 'Request-Id')
    ->url('https://my.url');

请注意,它应该与由 Sideol 的 --api-trace-header 属性定义的值相同。

Sideol 的响应还将包含跟踪标头。如果发生错误,则 Sideol::sendSideol::save 方法都会抛出 SideolApiError 异常,该异常提供了以下方法来检索跟踪

use Picsi\Sideolclient\Exceptions\SideolApiError;
use Picsi\Sideolclient\Sideol;

try {
    $response = Sideol::send(
        Sideol::chromium('https://:3000')
            ->url('https://my.url')
    );
} catch (SideolApiError $e) {
    $trace = $e->getSideolTrace();
    // Or if you override the header name:
    $trace = $e->getSideolTrace('Request-Id');
}

Chromium

Chromium模块与Chromium浏览器交互,用于将HTML文档转换为PDF。

将目标URL转换为PDF

请参阅https://khairu-aqsara.net/sideol/docs/modules/chromium#url

将目标URL转换为PDF非常简单

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->url('https://my.url');

由于提供了$extraLinkTags和$extraScriptTags参数,您可以使用它们注入<link><script> HTML元素

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Modules\ChromiumExtraLinkTag;
use Picsi\Sideolclient\Modules\ChromiumExtraScriptTag;

$request = Sideol::chromium($apiUrl)->url('https://my.url',
        [
            new ChromiumExtraLinkTag('https://my.css'),
        ],
        [
            new ChromiumExtraScriptTag('https://my.js'),
        ],
    );

请注意,Sideol将根据参数的顺序添加<link><script>元素。

将HTML文档转换为PDF

请参阅https://khairu-aqsara.net/sideol/docs/modules/chromium#html

您可以使用以下方式转换HTML文档

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::chromium($apiUrl)
    ->html(Stream::path('/path/to/file.html'));

或使用HTML字符串

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::chromium($apiUrl)
    ->html(Stream::string('my.html', $someHtml));

请注意,它将自动将文件名设置为index.html,这是Sideol的要求,无论您使用的是Stream类的哪个值。

您还可以发送其他文件,如图像、字体、样式表等。唯一的要求是它们在HTML DOM中的路径位于根级别。

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::chromium($apiUrl)
    ->assets(
        Stream::path('/path/to/my.css'),
        Stream::path('/path/to/my.js')
    )
    ->html(Stream::path('/path/to/file.html'));

将一个或多个Markdown文件转换为PDF

请参阅https://khairu-aqsara.net/sideol/docs/modules/chromium#markdown

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::chromium($apiUrl)
    ->markdown(
        Stream::path('/path/to/my_wrapper.html'),
        Stream::path('/path/to/file.md')
    );

第一个参数是一个包含HTML内容的Stream,例如

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>My PDF</title>
  </head>
  <body>
    {{ toHTML "file.md" }}
  </body>
</html>

这里有一个Go模板函数toHTML。Sideol将使用它将Markdown文件的内容转换为HTML。

就像HTML转换一样,您也可以发送其他文件,如图像、字体、样式表等。唯一的要求是它们在HTML DOM中的路径位于根级别。

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::chromium($apiUrl)
    ->assets(
        Stream::path('/path/to/my.css'),
        Stream::path('/path/to/my.js')
    )
    ->markdown(
        Stream::path('/path/to/file.html'),
        Stream::path('/path/to/my.md'),
        Stream::path('/path/to/my2.md')
    );

纸张大小

您可以使用以下方式覆盖默认纸张大小

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->paperSize($width, $height)
    ->url('https://my.url');

纸张大小示例(宽度 x 高度,以英寸为单位)

  • Letter - 8.5 x 11(默认)
  • Legal - 8.5 x 14
  • Tabloid - 11 x 17
  • Ledger - 17 x 11
  • A0 - 33.1 x 46.8
  • A1 - 23.4 x 33.1
  • A2 - 16.54 x 23.4
  • A3 - 11.7 x 16.54
  • A4 - 8.27 x 11.7
  • A5 - 5.83 x 8.27
  • A6 - 4.13 x 5.83

边距

您可以覆盖默认边距(即0.39英寸)

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->margins($top, $bottom, $left, $right)
    ->url('https://my.url');

优先CSS页面大小

您可以将页面大小强制为CSS中定义的大小

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->preferCssPageSize()
    ->url('https://my.url');

打印背景图形

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->printBackground()
    ->url('https://my.url');

您还可以隐藏默认的白色背景,并使用以下方式生成具有透明度的PDF

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->printBackground()
    ->omitBackground()
    ->url('https://my.url');

关于printBackgroundomitBackground表单字段的规则如下

  • 如果将printBackground设置为false,则不打印背景。
  • 如果将printBackground设置为true
    • 如果HTML文档有背景,则使用该背景。
    • 如果没有
      • 如果将omitBackground设置为true,则默认背景是透明的。
      • 如果没有,则使用默认的白色背景。

横向排版

您可以使用以下方式覆盖默认的纵向排版

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->landscape()
    ->url('https://my.url');

缩放

您可以使用以下方式覆盖页面渲染的默认缩放(即1.0

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->scale(2.0)
    ->url('https://my.url');

页面范围

您可以设置要打印的页面范围,例如1-5, 8, 11-13。空值表示所有页面。

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->nativePageRanges('1-2')
    ->url('https://my.url');

页眉和页脚

您可以为PDF的每一页添加页眉和/或页脚

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::chromium($apiUrl)
    ->header(Stream::path('/path/to/my_header.html'))
    ->footer(Stream::path('/path/to/my_footer.html'))
    ->margins(1, 1, 0.39, 0.39)
    ->url('https://my.url');

请注意,它将自动将文件名设置为header.htmlfooter.html,这是Sideol的要求,无论您使用的是Stream类的哪个值。每个都必须是一个完整的HTML文档

<html>
<head>
    <style>
    body {
        font-size: 8rem;
        margin: 4rem auto;
    }
    </style>
</head>
<body>
<p><span class="pageNumber"></span> of <span class="totalPages"></span></p>
</body>
</html>

以下类允许您注入打印值

  • date - 格式化的打印日期。
  • title - 文档标题。
  • url - 文档位置。
  • pageNumber - 当前页码。
  • totalPages - 文档的总页数。

⚠️ 确保以下条件:

  1. 顶部和底部的边距足够大(即,->margins(1, 1, 0.39, 0.39)
  2. 字体大小足够大。

⚠️ 存在一些限制

  • 没有JavaScript。
  • CSS属性与HTML文档中的属性独立。
  • 页脚CSS属性会覆盖标题中的属性;
  • 仅加载Docker镜像中安装的字体 - 请参阅字体章节
  • 图像仅使用base64编码的源工作 - 即,data:image/png;base64, iVBORw0K....
  • background-color 和颜色 CSS 属性需要一个额外的 -webkit-print-color-adjust: exact CSS属性才能工作。
  • 不加载资源(例如,CSS文件、脚本、字体等)。
  • 背景表单字段不适用。

等待延迟

当页面依赖于JavaScript进行渲染,并且您无法访问页面的代码时,您可能需要等待一段时间(例如,1s2ms等),以确保Chromium已完全渲染您试图生成的页面。

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->waitDelay('3s')
    ->url('https://my.url');

等待表达式

您还可以等待直到指定的JavaScript表达式返回true

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->waitForExpression("window.status === 'ready'")
    ->url('https://my.url');

用户代理

您可以使用Sideol覆盖默认的User-Agent

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->userAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1")
    ->url('https://my.url');

额外的HTTP头

您可以在加载HTML文档时添加Chromium将发送的HTTP头

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->extraHttpHeaders([
        'My-Header-1' => 'My value',
        'My-Header-2' => 'My value'
    ])
    ->url('https://my.url');

在控制台异常时失败

如果Chromium控制台中存在异常,您可以将Sideol强制返回409 Conflict响应

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->failOnConsoleExceptions()
    ->url('https://my.url');

模拟媒体类型

一些网站有针对打印的专用CSS规则。使用screen允许您强制使用“标准”CSS规则

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->emulateScreenMediaType()
    ->url('https://my.url');

您还可以使用以下方法强制使用print媒体类型:

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->emulatePrintMediaType()
    ->url('https://my.url');

PDF格式

请参阅https://khairu-aqsara.net/sidoel/docs/modules/pdf-engines#engines

您可以使用以下方法设置结果的PDF格式:

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->pdfFormat('PDF/A-1a')
    ->url('https://my.url');

LibreOffice

LibreOffice模块通过unoconvLibreOffice交互,将文档转换为PDF。

将文档转换为PDF

请参阅https://khairu-aqsara.net/sideol/docs/modules/libreoffice#route。将文档转换为PDF非常简单,只需

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::libreOffice($apiUrl)
    ->convert(Stream::path('/path/to/my.docx'));

如果您发送许多文档,Sideol将返回包含PDF的ZIP存档

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::libreOffice($apiUrl)
    ->outputFilename('archive')
    ->convert(
        Stream::path('/path/to/my.docx'),
        Stream::path('/path/to/my.xlsx')
    );

// $filename = archive.zip
$filename = Sideol::save($request, $pathToSavingDirectory);

您也可以将它们合并为一个唯一的PDF

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::libreOffice($apiUrl)
    ->merge()
    ->outputFilename('merged')
    ->convert(
        Stream::path('/path/to/my.docx'),
        Stream::path('/path/to/my.xlsx')
    );

// $filename = merged.pdf
$filename = Sideol::save($request, $pathToSavingDirectory);

请注意,合并顺序由参数的顺序确定。

横向排版

您可以使用以下方式覆盖默认的纵向排版

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::libreOffice($apiUrl)
    ->landscape()
    ->convert(Stream::path('/path/to/my.docx'));

页面范围

您可以设置要打印的页面范围,例如,1-4。为空表示所有页面。

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::libreOffice($apiUrl)
    ->nativePageRanges('1-2')
    ->convert(Stream::path('/path/to/my.docx'));

⚠️ 页面范围独立应用于所有文件。

PDF格式

请参阅https://khairu-aqsara.net/sideol/docs/modules/pdf-engines#engines

您可以使用以下方法设置结果的PDF(s)格式:

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::libreOffice($apiUrl)
    ->pdfFormat('PDF/A-1a')
    ->convert(Stream::path('/path/to/my.docx'));

您还可以明确告诉Sideol使用unoconv将结果的PDF(s)转换为PDF格式

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::libreOffice($apiUrl)
    ->nativePdfFormat('PDF/A-1a')
    ->convert(Stream::path('/path/to/my.docx'));

⚠️ 您不能设置两个属性,否则Sideol将返回400 Bad Request响应。

PDF 引擎

PDF引擎模块收集了所有可以操作PDF文件的工具。

合并PDF

请参阅https://khairu-aqsara.net/sideol/docs/modules/pdf-engines#merge

合并PDF非常简单,只需

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::pdfEngines($apiUrl)
    ->merge(
        Stream::path('/path/to/my.pdf'),
        Stream::path('/path/to/my2.pdf')
    );

请注意,合并顺序由参数的顺序确定。

您也可以使用以下方法设置结果的PDF(s)格式:

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::pdfEngines($apiUrl)
    ->pdfFormat('PDF/A-1a')
    ->merge(
        Stream::path('/path/to/my.pdf'),
        Stream::path('/path/to/my2.pdf'),
        Stream::path('/path/to/my3.pdf')
    );

转换为特定的PDF格式

请参阅https://khairu-aqsara.net/sideol/docs/modules/pdf-engines#convert

您可以使用以下方式将PDF转换为特定的PDF格式

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::pdfEngines($apiUrl)
    ->convert(
        'PDF/A-1a'
        Stream::path('/path/to/my.pdf')
    );

如果您发送许多PDF文件,Sideol将返回一个包含PDF文件的ZIP存档

use Picsi\Sideolclient\Sideol;
use Picsi\Sideolclient\Stream;

$request = Sideol::pdfEngines($apiUrl)
    ->outputFilename('archive')
    ->convert(
        'PDF/A-1a',
        Stream::path('/path/to/my.pdf'),
        Stream::path('/path/to/my2.pdf'),
        Stream::path('/path/to/my3.pdf')
    );

// $filename = archive.zip
$filename = Sideol::save($request, $pathToSavingDirectory);

Webhook

Webhook模块是Sideol中间件,用于将API响应发送到回调。

⚠️ 如果您使用Webhook功能,则不能使用Sideol::save方法。例如

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->webhook('https://my.webhook.url', 'https://my.webhook.error.url')
    ->url('https://my.url'); 

您还可以覆盖Sideol用于调用webhook的默认HTTP方法(POST

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->webhook('https://my.webhook.url', 'https://my.webhook.error.url')
    ->webhookMethod('PATCH')
    ->webhookErrorMethod('PUT')
    ->url('https://my.url');

您还可以指示Sideol在请求webhook时添加额外的HTTP头信息

use Picsi\Sideolclient\Sideol;

$request = Sideol::chromium($apiUrl)
    ->webhook('https://my.webhook.url', 'https://my.webhook.error.url')
    ->webhookExtraHttpHeaders([
        'My-Header-1' => 'My value',
        'My-Header-2' => 'My value'    
    ])
    ->url('https://my.url');