跳转到内容
getnextpdf.com

渲染与 I/O 错误

这些条目涵盖在以下过程中抛出的渲染与输入/输出(I/O)异常: HTML 流水线对内容进行布局、分页媒体解析器分配页面几何、文本整形器处理复杂脚本、排版阶段进行断行、写入器序列化一个文档、读取器解析一个已有的 PDF,以及元数据阶段读取一个 Extensible Metadata Platform(XMP)数据包。

下文出现了两套基类层级,而二者的差异决定了你在一次 catch 之后能读取哪些诊断数据:

  • NextPdfException 实现了 ContextAwareExceptionInterface::getContext(): array。基类实现返回一个空数组;只有当子类重写 getContext() 时,它才携带结构化的键。那些不重写它的子类仍然通过 public readonly 属性暴露其数据。
  • 此处有几个类直接继承 PHP 的 RuntimeException。它们不是上下文感知的,也没有 getContext() 方法;请改为读取它们的 getMessage() 和任何公开属性。

每个条目都会指明确切的类、触发条件、它携带的上下文键或公开属性,以及恢复路径。

  • When it is thrown. 当被标记为 break-inside: avoid 的内容(一个其断行约束为 Avoid 的表格单元格)的测得高度超过单个页面的可用高度时,HTML 布局引擎会抛出它。引擎无法同时满足 avoid-break 约束和页面边界,因此它会失败,而非静默溢出。
  • Carried data. 继承 NextPdfException,但不重写 getContext(),因此 getContext() 返回一个空数组。诊断数据位于 public readonly 属性上:gridRow(int)、gridCol(int)、 contentHeight(float,磅)和 pageHeight(float,磅)。消息会指明单元格坐标和两个高度。
  • Recovery. 移除出问题单元格上的 break-inside: avoid 约束、 减少该单元格的内容以使它能容纳进一个页面,或增大页面尺寸或减小其边距,以使可用高度能容纳该内容。
  • When it is thrown. 当四个资源预算层级之一(定义于架构决策记录 ADR-020)被突破,且调用方选择了硬失败而非软回退时,retained 模式的布局原语会抛出它。默认路径不会抛出:ContainerLayout::acceptChild() 返回 false,调用方回退到块布局,并发出一条警告。该异常保留给配置时的验证,以及给那些断言确切突破元组的测试。这些层级是 per-child(一个被捕获的子流超出其上限)、per-container(Tier 1 的节点数量预算)、per-document(布局遍数或嵌套深度预算), 以及 global(SDK 级别的 256 MB 峰值常驻集大小上限)。
  • Carried data. 重写 getContext(),它返回一个被应用性能监控 (APM)工具消费的稳定八键形态: budgetTierexceededValuebudgetLimitcontainerTypephasebreachOrigincaptureSizeprocessedItemCount。前四个键是最初的 v1.0.0 子集,始终会被填充;后四个则在不带它们调用构造函数时默认为 null0getCausalWarningCode() 会把(层级、容器类型)元组映射到软回退路径本会发出的那个 WarningCode
  • Recovery. 对于配置突破,把所请求的值降回已记录的范围内(例如,retained 节点预算通过 Config::withRetainedNodeBudget() 接受 5,000100,000)。对于内容突破, 减少容器嵌套或节点数量,或依赖默认的软回退到块布局,而非选择那个硬失败表面。
  • When it is thrown. 当一个文档声明了一条具名的 @page <ident> { … } 规则(通过 page: <ident> 属性绑定到内容)时,分页媒体阶段会失败即关闭地抛出它。来自 CSS Paged Media Level 3 §3.4 和 Level 4 §3.2 的具名页面——包括 :first:left:right:blank 伪类以及具名的 size:rotate: 覆盖——会被解析,但没有生产布局路径会消费它们。引擎会拒绝,而非发出丢弃该规则本会产生的那种静默错误的默认分页。
  • Carried data. 重写 getContext(),它返回 page_names (触发该失败的各个不同标识,按源代码顺序)、 has_size_override(bool)、has_rotate_override(bool),以及 has_pseudo_classes(bool)。同样的值也暴露在 pageNameshasSizeOverridehasRotateOverridehasPseudoClasses 公开属性上。
  • Recovery. 移除那些具名的 @page <ident> 规则和任何 page: <ident> 绑定,并通过受支持的未具名 @page { … } 规则及其伪类形式来表达预期的几何。或者,固定到一个将落地完整具名页面布局支持的未来版本。
  • When it is thrown. 当文本分段需要 International Components for Unicode(ICU)断行迭代器,但 require-ICU 策略处于激活状态 (NEXTPDF_REQUIRE_ICU=1),而 ext-intl 扩展和 IntlBreakIterator 不可用时,文本分段会抛出它。
  • Carried data. 直接继承 RuntimeException,因此它不是上下文感知的,也没有 getContext()。它是同一代码路径先前抛出的通用异常的一个严格细化,因此现有的 catch (\RuntimeException) 处理器会继续工作。
  • Recovery. 安装并启用 ext-intl 以使 ICU 断行迭代器可用,或在 require-ICU 策略并非强制的场合,取消设置 NEXTPDF_REQUIRE_ICU 以回退到非 ICU 的分段器。
  • When it is thrown. 这是脚本整形服务提供者接口 (SPI)的基异常。它今天不会被直接抛出;而是会抛出具体的子类型。捕获这个类型以在一处统一处理任何整形失败。
  • Carried data. 直接继承 RuntimeException;不是上下文感知的,没有 getContext()
  • Recovery. 在具体子类型上进行分支。关于当前版本中唯一提供的子类型,参见下文的 NotYetImplementedException
  • When it is thrown. 对于那些具体整形被推迟的脚本(蒙古文和藏文),每个占位脚本整形器都会从其 shape() 体中抛出它。 整形 SPI 接缝在架构上已就绪,但真正的整形仍待一个经母语者验证的夹具。抛出一个异常而非一个静默的空操作,会在运行时浮现意外的生产接线,而非把未整形的文本发出到一个宣称带标记无障碍的 PDF 中。
  • Carried data. 继承 ScriptShaperException(因此也继承 RuntimeException),所以它不是上下文感知的,也没有 getContext()。 诊断数据位于它的 public readonly 属性上:bcp47LanguageTag (该运行的 BCP-47 标签,例如 mn-Mongbo-Tibt)和 missingCapability(该实现所缺少的具体能力)。 消息会包含两者。
  • Recovery. 不要在生产中把未实现脚本的运行经由整形器路由。在上游检测语言标签,并要么回退到一个不同的渲染路径,要么固定到一个将落地受影响脚本整形的未来版本。
  • When it is thrown. 当一个文档包含一个在 PDF 1.4 输出配置文件(ISO 19005-1:2005 / PDF/A-1)下被禁止的特性时,写入器会抛出它,该配置文件禁止那些在更高 PDF 版本中引入的构造。
  • Carried data. 继承 NextPdfException,但不重写 getContext(),因此 getContext() 返回一个空数组。诊断数据位于它的 public readonly 属性上:feature(被拒绝的特性名称)、 reason(它为何被禁止)和 isoClause(ISO 条款引用)。 消息会组合这三者。
  • Recovery. 移除被拒绝的特性,或用一个 PDF 1.4 兼容的等价物替换它,或瞄准一个允许该特性的更高输出配置文件。
  • When it is thrown. 当一个文档包含一个在严格 PDF 2.0 输出配置文件下被禁止的特性时,写入器会抛出它。ISO 32000-2:2020 弃用了 PDF 1.7 仍然允许的构造——最显著的是 Standard 14 Type 1 字体(§9.6.2),它们在一个一致的 PDF 2.0 文档中必须被嵌入。
  • Carried data.Pdf14FeatureRejectedException 形态相同:继承 NextPdfException,不重写 getContext()(返回一个空数组), 并把 featurereasonisoClause 暴露为 public readonly 属性。
  • Recovery. 修复被拒绝的特性——例如,嵌入那 base 14 字体——或者在存在已记录逃生舱的地方采用它(对于非嵌入的 base 14 字体,Document::allowNonEmbeddedBase14())。
  • When it is thrown. 当文档的 encryptionModepubkey(一个公钥接收者列表)时,PdfWriter::build() 会在入口点抛出它,此时写入器端的公钥流体加密分派尚未接线。提前拒绝可以防止静默地发出一个调用方误以为已加密的未加密 PDF。
  • Carried data. 直接继承 RuntimeException,因此它不是上下文感知的,也没有 getContext()。它是同一站点先前抛出的通用异常的一个严格细化,因此现有的 catch (\RuntimeException) 处理器会继续工作。
  • Recovery. 改用一个受支持的加密模式(基于密码的加密), 而非公钥接收者列表,或固定到一个将落地公钥加密支持的版本。当它被抛出时,不要把输出当作已加密来处理。
  • When it is thrown. 当一个输入 PDF 落在其受支持的范围之外时, 对象图读取器会失败即关闭地抛出它。该读取器支持经典的交叉引用表(ISO 32000-2:2020 §7.5.4)、交叉引用流 (§7.5.8)、对象流压缩的对象(§7.5.7)、多修订版的 /Prev 链(§7.5.6),以及经由 /XRefStm 的混合引用文件(§7.5.8.4)。 那个范围之外的任何内容都会浮现这个异常,而非一个部分或猜测的解析。具名构造函数映射到这些原因案例: encrypted()damagedCrossReference()cyclicReferenceChain()nonConformantObjectStream()irresolvableObjectCollision()truncatedFile(),以及 crossReferenceOffsetOutOfBounds()
  • Carried data. 直接继承 RuntimeException,因此它不是上下文感知的,也没有 getContext()。它暴露一个 public readonly reason 属性,其类型为 UnsupportedPdfStructureReason(一个枚举),以便调用方在精确的类别上分支,而无需解析消息;一个可选的 detail 字符串和一个 previous throwable 可以添加有界的、不敏感的上下文。默认消息是该原因的不泄漏摘要。
  • Recovery.reason 上分支。对于 EncryptedDocument,在读取之前运行一个解密步骤,因为解密在读取器的范围之外。对于 DamagedCrossReferenceTruncatedFileCrossReferenceOffsetOutOfBounds,把该文件当作格式错误或不完整来处理,并重新获取或修复该源。对于 CyclicReferenceChainNonConformantObjectStreamIrresolvableObjectCollision,该输入违反了结构模型,无法按原样读取。
  • When it is thrown. 当一个被嵌入的 XMP 数据包超出已配置的字节上限时,流式 XMP 元数据读取器会抛出它。它是一个针对实体扩展和二次方爆炸式输入的防御性守卫(一个 128 MB 峰值上限,针对吉字节规模的被嵌入 XMP)。
  • Carried data. 继承 NextPdfException,但不重写 getContext(),因此 getContext() 返回一个空数组。诊断数据位于它的 public readonly 属性上:byteCount(观测到的字节数) 和 cap(已配置的上限,以字节为单位)。消息会报告两者。
  • Recovery. 把这份超大的元数据当作恶意或格式错误来拒绝或跳过。如果一个合法的文档确实需要一个更大的数据包,请有意地提高已配置的上限,权衡那个守卫本就是为了防止的内存耗尽风险。