Skip to content

Scala API 参考

PDF Oxide 提供地道的 Scala 3 绑定,它是建立在成熟的 fyi.oxide:pdf-oxide Java 绑定之上的一层纯 JVM 薄封装,而后者则拥有唯一的 JNI 原生桥接(pdf_oxide_jni crate)。Scala 模块不引入任何原生代码:它直接使用 Java 类型(PdfPdfDocumentPdfPageDocumentEditorAutoExtractorPdfSignerPdfValidator,以及几何/文本/表格/搜索值类型),并在其上叠加了一组 Scala 3 extension 方法,把 java.util.Optional[T] 转换为 Option[T]、把 java.util.List[T] 转换为 Seq[T]scala.util.Using 可以直接配合任何 AutoCloseable 句柄使用,无需额外适配。

// build.sbt — cross-versioned for Scala 3 (resolves pdf-oxide-scala_3)
libraryDependencies += "fyi.oxide" %% "pdf-oxide-scala" % "0.3.69"

JNI 原生库(libpdf_oxide_jni)不会被一并打包。请通过把它放到 java.library.path 中并调用 System.loadLibrary("pdf_oxide_jni") 使其可被加载,或者用 -Dfyi.oxide.pdf.lib.path=<path> 让 Java 的 NativeLoader 直接指向它。

import fyi.oxide.pdf.{Pdf, PdfDocument, producerOption, wordsSeq}
import scala.util.Using

Using.resource(Pdf.fromMarkdown("# Hello\n\nbody\n")): pdf =>
  Using.resource(PdfDocument.open(pdf.save())): doc =>
    println(doc.pageCount())
    println(doc.extractText(0))
    println(doc.toMarkdown())
    println(doc.page(0).wordsSeq.map(_.text))       // List -> Seq
    println(doc.producerOption.getOrElse("(none)")) // Optional -> Option

关于 Java API,参见 Java API 参考;关于 Rust API,参见 Rust API 参考;关于类型详情,参见 类型与枚举


Pdf

fyi.oxide.pdf.Pdf —— 从源格式创建新的 PDF,并拆分已有的 PDF。实现了 AutoCloseable

工厂方法

def fromMarkdown(markdown: String): Pdf

从 Markdown 内容创建 PDF。

def fromHtml(html: String): Pdf

从 HTML 内容创建 PDF。

def fromImages(images: java.util.List[Array[Byte]]): Pdf

创建一个多页 PDF,每张图片(JPEG/PNG 字节)对应一页。

拆分

def planSplitByBookmarks(opts: SplitByBookmarksOptions): java.util.List[BookmarkSegment]

在不生成实际字节的情况下规划一次基于书签的拆分;返回将会创建的各个片段。

def splitByBookmarks(opts: SplitByBookmarksOptions): java.util.List[Array[Byte]]

在书签边界处拆分文档,每个片段返回一个 PDF 字节数组。

def planSplitByBookmarksCount(sourcePdf: Array[Byte], level: Int): Int

静态辅助方法:统计在给定大纲层级下进行书签拆分将产生的片段数量。

def splitByBookmarksFromBytes(sourcePdf: Array[Byte], level: Int): Array[Array[Byte]]

静态辅助方法:在给定大纲层级处拆分源 PDF 字节,得到逐片段的字节数组。

保存与生命周期

def save(): Array[Byte]
def saveTo(out: java.nio.file.Path): Unit
def isOpen(): Boolean
def close(): Unit

将 PDF 序列化为字节或写入文件路径,检查原生句柄是否仍然存活,以及释放该句柄。


PdfDocument

fyi.oxide.pdf.PdfDocument —— 打开、提取、渲染并查询一个 PDF。实现了 AutoCloseable

打开

def open(path: java.nio.file.Path): PdfDocument
def open(path: String): PdfDocument
def open(bytes: Array[Byte]): PdfDocument
def open(path: java.nio.file.Path, password: String): PdfDocument
def open(path: String, password: String): PdfDocument
def open(bytes: Array[Byte], password: String): PdfDocument
def open(stream: java.io.InputStream): PdfDocument

从文件路径、内存中的字节或输入流打开一个 PDF。带密码参数的重载可以一步完成对加密文档的解密。

静态提取

def extractText(path: String): String
def extractText(path: java.nio.file.Path): String

一次性便捷方法:打开文件、提取全部文本,然后关闭它。

文档信息与鉴权

def pageCount(): Int
def authenticate(password: String): Boolean
def authenticate(password: Array[Byte]): Boolean
def isOpen(): Boolean
def close(): Unit

获取页数、对加密 PDF 在打开后进行鉴权(字符串或原始字节)、检查句柄是否存活,以及释放资源。

元数据

def producer(): java.util.Optional[String]
def creator(): java.util.Optional[String]

文档的生产者 / 创建者字符串。可使用封装方法 producerOption / creatorOption 得到符合习惯的 Option[String]

文本提取

def extractText(pageIndex: Int): String
def extractTextAuto(pageIndex: Int): String
def extractStructured(page: Int): String

提取某一页的纯文本、带自动 OCR 兜底的文本(用于扫描页),或者结构化的 JSON 表示。页码从零开始计数。

转换

def toMarkdown(): String
def toMarkdown(pageIndex: Int): String
def toHtml(): String
def toHtml(pageIndex: Int): String

把整个文档或单个页面转换为 Markdown 或 HTML。

渲染

def render(pageIndex: Int): Array[Byte]
def render(pageIndex: Int, dpi: Int): Array[Byte]

把一个页面光栅化为 PNG 字节,可选择指定 DPI。

页面与搜索

def page(index: Int): PdfPage
def pages(): java.util.List[PdfPage]
def pagesStream(): java.util.stream.Stream[PdfPage]
def formFields(): java.util.List[FormField]
def search(query: String): java.util.List[SearchMatch]
def search(query: String, caseInsensitive: Boolean, regex: Boolean, maxResults: Int): java.util.List[SearchMatch]

获取单个页面句柄、全部页面(以列表或流形式)、所有 AcroForm 表单字段,以及在整篇文档中的搜索匹配项。maxResults = 0 表示不限制数量。可使用封装方法 pagesSeqformFieldsSeqsearchSeq 获得 Scala 的 Seq 结果。


PdfPage

fyi.oxide.pdf.PdfPage —— 由 PdfDocument.page / pages 返回的单页句柄。

几何信息

def parent(): PdfDocument
def index(): Int
def mediaBox(): BBox
def cropBox(): BBox
def width(): Double
def height(): Double
def rotation(): Int

所属文档、从零开始的索引、MediaBox / CropBox 矩形、以磅为单位的页面尺寸,以及以度为单位的旋转角度。

内容提取

def text(): String
def text(region: BBox): String
def words(): java.util.List[TextWord]
def lines(): java.util.List[TextLine]
def chars(): java.util.List[TextChar]
def images(): java.util.List[ExtractedImage]
def tables(): java.util.List[Table]
def annotations(): java.util.List[Annotation]

提取整页文本或限定区域的文本,以及结构化的单词、行、字符、图片、表格和注释。可使用封装方法 wordsSeqlinesSeqcharsSeqimagesSeqtablesSeqannotationsSeq 获得 Scala 的 Seq


DocumentEditor

fyi.oxide.pdf.DocumentEditor —— 填写表单、执行编校、清除元数据,并保存(增量或完全重写)。实现了 AutoCloseable。所有 setter 方法都是链式的,并返回 this

打开

def open(path: java.nio.file.Path): DocumentEditor
def open(path: String): DocumentEditor
def open(bytes: Array[Byte]): DocumentEditor

表单

def setFormField(name: String, value: String): DocumentEditor
def setFormField(name: String, checked: Boolean): DocumentEditor

设置文本/选项字段的值,或切换复选框/单选字段的状态。

编校(Redaction)

def addRedaction(pageIndex: Int, region: BBox): DocumentEditor
def redactionCount(pageIndex: Int): Int
def redactionCount(): Int
def applyRedactionsDestructive(): RedactResult

排队一个编校矩形区域,统计某一页或全部待处理的编校数量,并以破坏性方式(彻底移除底层内容)应用这些编校,返回一个 RedactResult

元数据与保存

def scrubMetadata(): DocumentEditor
def save(): Array[Byte]
def saveTo(out: java.nio.file.Path): Unit
def saveIncremental(): Array[Byte]
def saveIncrementalTo(out: java.nio.file.Path): Unit
def isOpen(): Boolean
def close(): Unit

清除可识别身份的元数据,然后把完全重写或增量追加的 PDF 保存为字节或写入磁盘。


AutoExtractor

fyi.oxide.pdf.AutoExtractor —— 智能提取功能,会对页面进行分类,并在需要时回退到 OCR。

构造

def of(doc: PdfDocument): AutoExtractor
def of(doc: PdfDocument, config: AutoExtractConfig): AutoExtractor
def fast(doc: PdfDocument): AutoExtractor
def balanced(doc: PdfDocument): AutoExtractor
def highFidelity(doc: PdfDocument): AutoExtractor

使用默认配置、自定义配置,或预设配置(fast / balanced / high-fidelity)构建一个提取器。

提取

def extractText(): String
def extractTextForPage(pageIndex: Int): String
def extractDocument(): AutoResult
def extractPage(pageIndex: Int): AutoResult
def extractAutoDocument(): AutoResult
def extractAutoPage(pageIndex: Int): AutoResult
def extractDocumentJson(): String
def extractPageJson(pageIndex: Int): String

为整个文档或单个页面提取纯文本,或者更丰富的 AutoResult 记录(包含文本、可选的 Markdown/HTML、置信度、是否使用了 OCR、各区域信息),以及对应的 JSON 变体。

分类与访问器

def classifyDocument(): ClassifyResult
def classifyPage(pageIndex: Int): ClassifyResult
def document(): PdfDocument
def config(): AutoExtractConfig

在不实际提取内容的情况下对页面进行分类(文本层 / 扫描 / 混合,以及需要 OCR/含图表/已加密的页面列表),并读取回源文档和配置。


MarkdownConverter

fyi.oxide.pdf.MarkdownConverter —— 静态的 Markdown/HTML 转换辅助工具。

def toMarkdown(doc: PdfDocument): String
def toMarkdown(doc: PdfDocument, pageIndex: Int): String
def toHtml(doc: PdfDocument): String
def toHtml(doc: PdfDocument, pageIndex: Int): String

把整个文档或单个页面转换为 Markdown 或 HTML。


PdfSigner

fyi.oxide.pdf.PdfSigner —— PAdES / CMS 数字签名与验证。

def fromPkcs12(keystore: java.nio.file.Path, password: String): PdfSigner
def fromPkcs12(keystoreBytes: Array[Byte], password: String): PdfSigner
def sign(pdf: Array[Byte], opts: SignOptions): Array[Byte]
def verify(pdf: Array[Byte]): Boolean
def classifyLevel(pdf: Array[Byte]): SignatureLevel

从 PKCS#12 密钥库(文件或字节)加载签名者,使用给定的 SignOptions 对 PDF 签名,验证已有签名,并以静态方式判定已签名 PDF 的 PAdES 等级。


PdfValidator

fyi.oxide.pdf.PdfValidator —— PDF/A、PDF/X 与 PDF/UA 合规性检查。

def isPdfA(doc: PdfDocument, level: PdfALevel): Boolean
def isPdfUa(doc: PdfDocument, level: PdfUaLevel): Boolean
def validatePdfA(doc: PdfDocument, level: PdfALevel): ValidationResult
def validatePdfX(doc: PdfDocument, level: PdfXLevel): ValidationResult
def validatePdfUa(doc: PdfDocument, level: PdfUaLevel): ValidationResult

针对 PDF/A、PDF/X 与 PDF/UA,提供布尔型的快速检查,以及包含逐条违规项的完整 ValidationResult 报告。


PdfPolicy

fyi.oxide.pdf.PdfPolicy —— 全局密码学策略模式。

def current(): PolicyMode
def set(mode: PolicyMode): Unit
def compat(): PolicyMode
def strict(): PolicyMode
def fipsStrict(): PolicyMode

读取或设置进程范围内的策略模式,并访问 COMPAT / STRICT / FIPS_STRICT 这几个预设值。


结果与值类型

AutoResult

def text(): String
def markdown(): java.util.Optional[String]   // facade: markdownOption
def html(): java.util.Optional[String]       // facade: htmlOption
def reason(): ExtractReason
def confidence(): Double
def ocrUsed(): Boolean
def regions(): java.util.List[RegionResult]
def pagesNeedingOcr(): java.util.List[Integer]  // facade: pagesNeedingOcrSeq

RegionResult

def pageIndex(): Int
def bbox(): BBox
def text(): String
def reason(): ExtractReason
def confidence(): Double
def ocrUsed(): Boolean
def table(): java.util.Optional[Table]

ClassifyResult

def pages(): java.util.List[PageClass]
def pagesNeedingOcr(): java.util.List[Integer]
def pagesWithChart(): java.util.List[Integer]
def pagesEncrypted(): java.util.List[Integer]

AutoExtractConfig

不可变配置,通过 AutoExtractConfig.builder()(或 .toBuilder())构建。各访问器均返回 Optional

def builder(): AutoExtractConfig.Builder
def toBuilder(): AutoExtractConfig.Builder
def mode(): java.util.Optional[ExtractMode]
def forceOcrPages(): java.util.Optional[java.util.List[Integer]]
def minOcrConfidence(): java.util.Optional[Double]
def ocrLanguages(): java.util.Optional[java.util.List[String]]
def passwords(): java.util.Optional[java.util.List[String]]
def topMarginFraction(): java.util.Optional[Double]
def bottomMarginFraction(): java.util.Optional[Double]
def allowSingleColumnTables(): java.util.Optional[Boolean]
def ocrInlineImages(): java.util.Optional[Boolean]
def cancelToken(): java.util.Optional[String]

AutoExtractConfig.Builder

def withMode(m: ExtractMode): Builder
def withForceOcrPages(p: java.util.List[Integer]): Builder
def withMinOcrConfidence(c: Double): Builder
def withOcrLanguages(l: java.util.List[String]): Builder
def withOcrLanguages(l: String*): Builder
def withPasswords(p: java.util.List[String]): Builder
def withPasswords(p: String*): Builder
def withTopMarginFraction(f: Double): Builder
def withBottomMarginFraction(f: Double): Builder
def withAllowSingleColumnTables(b: Boolean): Builder
def withOcrInlineImages(b: Boolean): Builder
def withCancelToken(t: String): Builder
def build(): AutoExtractConfig

ValidationResult / ValidationViolation

// ValidationResult
def valid(): Boolean
def violations(): java.util.List[ValidationViolation]

// ValidationViolation
def ruleId(): String
def description(): String
def pageIndex(): java.util.Optional[Integer]  // facade: pageIndexOption

RedactResult

def regionsApplied(): Int
def oracleVerified(): Boolean

SearchResult / SearchMatch / SearchOptions

// SearchResult
def query(): String
def matches(): java.util.List[SearchMatch]
def count(): Int
def isEmpty(): Boolean

// SearchMatch
def pageIndex(): Int
def bbox(): BBox
def text(): String

// SearchOptions (builder via SearchOptions.builder())
def caseSensitive(): Boolean
def wholeWord(): Boolean
def regex(): Boolean
def maxResults(): java.util.Optional[Integer]

SearchOptions.BuilderwithCaseSensitive(b: Boolean)withWholeWord(b: Boolean)withRegex(b: Boolean)withMaxResults(m: Int)build()

FormField

def name(): String
def type(): FormFieldType
def value(): java.util.Optional[String]   // facade: valueOption
def bbox(): java.util.Optional[BBox]       // facade: bboxOption
def pageIndex(): Int

Annotation

def `type`(): AnnotationType
def pageIndex(): Int
def bbox(): BBox
def contents(): java.util.Optional[String]  // facade: contentsOption
def uri(): java.util.Optional[String]        // facade: uriOption

ExtractedImage

def bytes(): Array[Byte]
def format(): ImageFormat
def bbox(): BBox
def width(): Int
def height(): Int

Table / TableCell

// Table
def bbox(): BBox
def rows(): Int
def cols(): Int
def cells(): java.util.List[TableCell]

// TableCell
def text(): String
def bbox(): BBox
def row(): Int
def col(): Int
def rowSpan(): Int
def colSpan(): Int

文本类型

// TextWord
def text(): String
def bbox(): BBox
def confidence(): Float

// TextLine
def text(): String
def bbox(): BBox
def words(): java.util.List[TextWord]

// TextChar
def codepoint(): Int
def bbox(): BBox
def confidence(): Float
def asString(): String

// TextSpan
def text(): String
def bbox(): BBox
def style(): TextStyle

// TextStyle
def size(): Double
def color(): Color
def bold(): Boolean
def italic(): Boolean

几何类型

// BBox(x0, y0, x1, y1)
def x0(): Double
def y0(): Double
def x1(): Double
def y1(): Double
def width(): Double
def height(): Double

// Color(r, g, b[, a])
def r(): Int
def g(): Int
def b(): Int
def a(): Int

// Point(x, y)
def x(): Double
def y(): Double

// Rect(x, y, width, height)
def x(): Double
def y(): Double
def width(): Double
def height(): Double
def toBBox(): BBox

元数据

// DocumentInfo — all accessors return Optional[String]
def title(); def author(); def subject(); def keywords()
def creator(); def producer(); def creationDate(); def modificationDate()

// XmpMetadata(xml: String)
def xml(): String
def isEmpty(): Boolean

签名与拆分选项

// SignOptions (builder via SignOptions.builder())
def level(): SignatureLevel
def reason(): java.util.Optional[String]
def location(): java.util.Optional[String]
def contactInfo(): java.util.Optional[String]
def tsaUrl(): java.util.Optional[String]
// Builder: withLevel, withReason, withLocation, withContactInfo, withTsaUrl, build

// SplitByBookmarksOptions (builder via SplitByBookmarksOptions.builder())
def level(): Int
def filenamePrefix(): java.util.Optional[String]
// Builder: withLevel(l: Int), withFilenamePrefix(p: String), build

// BookmarkSegment
def title(): String
def firstPage(): Int
def lastPage(): Int
def filename(): String

SecurityPolicy

def mode(): PolicyMode
def additionalAllow(): java.util.List[String]
def additionalDeny(): java.util.List[String]
def builder(): SecurityPolicy.Builder
// Builder: withMode(m: PolicyMode), allow(algId: String), deny(algId: String), build

枚举

枚举 常量
AnnotationType HIGHLIGHT, TEXT, LINK, STAMP, UNDERLINE, STRIKEOUT, SQUIGGLY, FREE_TEXT, LINE, SQUARE, CIRCLE, FILE_ATTACHMENT
FormFieldType TEXT, CHECKBOX, RADIO, CHOICE, SIGNATURE
ImageFormat JPEG, PNG, JBIG2, JPEG2000, CCITT, RAW
ExtractMode TEXT_ONLY, AUTO
ExtractReason OK, SCANNED_NO_TEXT_LAYER, GLYPH_MAPPING_MISSING, ENCRYPTED_NO_EXTRACT_PERMISSION, IMAGE_TABLE_NO_STRUCTURE, CHART_NOT_TRANSCRIBED, OCR_REQUESTED_BUT_UNAVAILABLE, OCR_LOW_CONFIDENCE, EMPTY
PageClass TEXT_LAYER, SCANNED, MIXED
PdfALevel A_1B, A_1A, A_2B, A_2A, A_2U, A_3B, A_3A, A_3U, A_4, A_4E
PdfUaLevel UA_1, UA_2(各自暴露 code(): Int
PdfXLevel X_1A_2001, X_1A_2003, X_3_2002, X_3_2003, X_4, X_4P, X_5G, X_5N, X_5PG, X_6, X_6P
PolicyMode COMPAT, STRICT, FIPS_STRICT
PixelFormat RGBA_8888, RGB_888, GRAY_8
SignatureLevel B_B, B_T, B_LT

Scala 封装扩展

Scala 模块在 Java 类型之上叠加了以下 extension 方法,让调用方得到 Option/Seq,而不是 java.util.Optional/java.util.List。请从 fyi.oxide.pdf 中按需导入。

// Generic — any java.util.Optional[T]
extension [T](o: java.util.Optional[T]) def toOption: Option[T]

// PdfDocument
extension (doc: PdfDocument)
  def producerOption: Option[String]
  def creatorOption: Option[String]
  def formFieldsSeq: Seq[FormField]
  def pagesSeq: Seq[PdfPage]
  def searchSeq(query: String): Seq[SearchMatch]

// PdfPage
extension (page: PdfPage)
  def wordsSeq: Seq[TextWord]
  def linesSeq: Seq[TextLine]
  def charsSeq: Seq[TextChar]
  def tablesSeq: Seq[Table]
  def imagesSeq: Seq[ExtractedImage]
  def annotationsSeq: Seq[Annotation]

// FormField
extension (f: FormField)
  def valueOption: Option[String]
  def bboxOption: Option[BBox]

// Annotation
extension (a: Annotation)
  def contentsOption: Option[String]
  def uriOption: Option[String]

// AutoResult
extension (r: AutoResult)
  def markdownOption: Option[String]
  def htmlOption: Option[String]
  def pagesNeedingOcrSeq: Seq[Int]

// ValidationViolation
extension (v: ValidationViolation)
  def pageIndexOption: Option[Int]

异常

所有错误都派生自 fyi.oxide.pdf.exception.PdfException

异常 触发原因
PdfException 所有 PDF Oxide 错误的基类
PdfParseException PDF 格式错误或已损坏
PdfEncryptedException 在未提供密码的情况下打开了加密 PDF
PdfPermissionException 操作不被文档的加密标志所允许
PdfInvalidStateException 在已关闭或无效的句柄上执行操作
PdfIoException 底层 I/O 失败
PdfUnsupportedException 不支持的功能或格式
PdfOcrUnavailableException 请求了 OCR,但未启用 ocr 功能特性
PdfSignatureException 签名或签名验证失败

PdfErrorKind 是一个枚举,用于对 PdfException 所携带的具体错误类别进行分类。


完整示例

import fyi.oxide.pdf.*
import fyi.oxide.pdf.auto.AutoExtractConfig
import scala.util.Using

// --- Creation ---
Using.resource(Pdf.fromMarkdown("# Report\n\nGenerated by PDF Oxide.")): pdf =>
  pdf.saveTo(java.nio.file.Path.of("report.pdf"))

// --- Extraction ---
Using.resource(PdfDocument.open("report.pdf")): doc =>
  println(s"Pages: ${doc.pageCount()}")

  val page = doc.page(0)
  println(s"${page.width()} x ${page.height()}")
  page.wordsSeq.foreach(w => println(s"${w.text} @ ${w.bbox.x0}"))

  // Search (facade -> Seq)
  doc.searchSeq("Report").foreach(m => println(s"p${m.pageIndex}: ${m.text}"))

  // Metadata (facade -> Option)
  println(doc.producerOption.getOrElse("(none)"))

  // Auto extraction with OCR fallback
  val cfg = AutoExtractConfig.builder().withMinOcrConfidence(0.6).build()
  val result = AutoExtractor.of(doc, cfg).extractDocument()
  println(s"confidence=${result.confidence} ocr=${result.ocrUsed}")
  result.markdownOption.foreach(println)

// --- Editing: fill a form, redact, scrub ---
Using.resource(DocumentEditor.open("form.pdf")): ed =>
  ed.setFormField("name", "Jane Doe")
    .setFormField("agree", true)
    .addRedaction(0, BBox(72, 700, 272, 720))
  val redacted = ed.applyRedactionsDestructive()
  println(s"redacted ${redacted.regionsApplied} regions")
  ed.scrubMetadata().saveTo(java.nio.file.Path.of("output.pdf"))

Other Language Bindings

PDF Oxide 为所有主流生态系统提供原生绑定:Rust, Python, Node.js, WASM, C#, Golang, Java, PHP, Ruby, C++, Swift, Kotlin, Dart, R, Julia, Zig, Clojure, Objective-C, Elixir

后续步骤