;;; Copyright (c) 2023 Teddy Wing
;;;
;;; This file is part of XFDF.
;;;
;;; XFDF is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation, either version 3 of the License, or
;;; (at your option) any later version.
;;;
;;; XFDF is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with XFDF. If not, see .
(in-package :xfdf)
(defun write-xfdf (output-stream fields)
"Write an XFDF document to `output-stream` using cons cells in the `fields`
list."
(format output-stream "~
")
(loop for (name . value) in fields
do
(format output-stream "~A" (field-xfdf name value)))
(format output-stream "
")
output-stream)
(defconstant +field-base-indentation+ 2)
(defun field-xfdf (name value)
"Build an XFDF XML string for a field."
(field-xfdf* name value 0))
(defun field-xfdf* (name value nesting-level)
"Build an XFDF XML string for a field. The `nesting-level` specifies the
amount of indentation to use relative to the +field-base-indentation+."
(let ((indent (+ +field-base-indentation+ nesting-level)))
(if (consp value)
(build-xfdf-nested-field name value nesting-level indent)
(let ((value (pdf-checkbox-value value)))
(xfdf-field-string name value indent)))))
(defun build-xfdf-nested-field (name value nesting-level indent)
"Build the XFDF XML for a field containing other fields."
(let ((inner-fields
(loop for field in value
collect
(let ((subname (if (listp field)
(first field)
field))
(subfield (if (listp field)
(rest field)
field)))
(field-xfdf* subname subfield (1+ nesting-level))))))
(xfdf-outer-field-string name inner-fields indent)))
(defun pdf-checkbox-value (value)
"If `value` is T or NIL, convert it to the default PDF checkbox values
'Yes' and 'Off' respectively.
If `value` is anything else, return its identity."
(cond ((eq value t) "Yes")
((eq value nil) "Off")
(t value)))
(defun xfdf-outer-field-string (name inner-fields-string indent)
"Build the XFDF XML string for a field containing other fields."
(format nil "~
~v{~A~:*~}
~{~A~}~v{~A~:*~}
"
indent
'(" ")
name
inner-fields-string
indent
'(" ")))
(defun xfdf-field-string (name value indent)
"Build the XFDF XML for a single field."
(format nil "~
~v{~A~:*~}
~v{~A~:*~} ~A
~v{~A~:*~}
"
indent
'(" ")
name
indent
'(" ")
value
indent
'(" ")))