[cl-pdf-devel] Named destinations proposal and small amendments
Dmitri Ivanov
divanov at aha.ru
Wed Oct 27 08:03:23 UTC 2004
Hello,
Below are:
1) Sketch of code implementing named destinations,
2) Suggestion of the single add-link function replacing multiple
add-<whatever>-link functions,
3) An example.
;=================================================
(defun pdf-name (obj &optional (prefix #\/))
;;; Helper (akin to pdf-string) to escape non-alphanumeric characters in
PDF names
;; by writing 2-digit hexadecimal code, preceded by the number sign
character (#).
;; CAUTION: PDF names are case-sensitive!
(let ((string (if (stringp obj)
(if (and prefix (char= (schar obj 0) prefix))
(return-from pdf-name obj) ; PDF-ready
obj)
(princ-to-string obj))))
(with-output-to-string (stream nil #-cmu :element-type #-cmu
(array-element-type string))
(when prefix
(write-char prefix stream))
(dotimes (i (length string))
(let ((char (schar string i)))
(if (or (alphanumericp char)
(find char "-_." :test #'char=)) ; often used regular
chars
(write-char char stream)
(format stream "#~2,'0x" (char-code char))))))))
(defmacro enforce-/ (&rest names)
;;; Verify and prefix each name by / unless it is PDF-ready.
`(setf ,@(loop for name in names collect name collect `(pdf-name ,name))))
(defun register-destination (destination &optional (display "/Fit") &rest
args)
;;; Register named destination as named object or string.
(cond ((symbolp destination)
;; PDF 1.1: Include into the Dest entry of in the document's
Catalog dictionary.
;; Limited by maxumim dictionary size.
(let ((entry (pdf-name destination))
(value (apply #'vector *page* display args))
(dests (get-dict-value (content (catalog *document*))
"/Dests")))
(if dests
(add-dict-value dests entry value)
(add-dict-value (content (catalog *document*)) "/Dests"
(setf dests (make-instance 'dictionary
:dict-values
`((,entry .
,value))))))))
((stringp destination)
;; PDF 1.2: Dests entry in the document's Names dictionary, located
via
;; the Names entry in the document's Catalog dictionary.
(error "Name trees are not implemeted!"))))
(defun add-link (x y dx dy &key reference destination uri filename
(border #(0 0 0)) (display "/Fit") args)
;;; "Generic" link adder.
;; Args: reference Named reference inside the current document
;; destinaion Named destination in the current document or the remote filename:
;; - either symbol designating named object;
;; - or string through the name tree (not implemented);
;; - or integer interpreted as a page number (for remote only?)
;; Other types of links are welcome.
(let ((annotation (make-instance 'annotation
:rect (vector x y (+ x dx) (+ y dy))
:type "/Link" :border border))
(action (cond (uri
(make-instance 'dictionary :dict-values '(("/S" .
"/URI"))))
(filename
(make-instance 'dictionary :dict-values '(("/S" .
"/GoToR")))))))
(if action
(add-dict-value (content annotation) "/A" action)
;; Named reference or destination in the current document
(push (cons "/Dest" (cond (reference
(get-named-reference reference))
((symbolp destination)
(pdf-name destination))
(t (error "not implemeted"))))
(dict-values (content annotation))))
(cond (uri
(add-dict-value action "/URI" (pdf-string uri)))
(filename
(add-dict-value action "/F" (pdf-string filename))
(add-dict-value action "/D"
(cond ((numberp destination) ; page number
(apply #'vector destination display args))
((symbolp destination)
(pdf-name destination))
(t (error "not implemeted"))))) )
annotation))
;===================== Example ========================
(defun ex-dest (&optional (file #P"/tmp/ex-dest.pdf"))
(pdf:with-document ()
(let* ((helvetica (pdf:get-font "Helvetica")))
(loop for i from 1 to 7
do
(pdf:with-page ()
(pdf:with-outline-level ((format nil "Page ~d"
i)(pdf:register-page-reference))
(pdf::register-destination (make-symbol (format nil
"top-of-page~d" i))
"/FitH" 810)
(pdf:in-text-mode
(pdf:set-font helvetica 10)
(pdf:move-text 50 800)
(pdf:draw-text (format nil "go to next page ~d" (1+ i))))
(pdf:add-link 50 800 80 10
:destination (make-symbol (format nil "top-of-page~d" (1+
i))))
(pdf:in-text-mode
(pdf:set-font helvetica 12)
(pdf:move-text 400 800)
(pdf:draw-text "cl-pdf: Named destiantions"))
(pdf:in-text-mode
(pdf:set-font helvetica 36)
(pdf:move-text 100 600)
(pdf:draw-text (format nil "Hello world - page ~d" i)))
(pdf:in-text-mode
(pdf:set-font helvetica 10)
(pdf:move-text 50 50)
(pdf:draw-text (format nil "go to previous page ~d" (1-
i))))
(pdf:add-link 50 50 80 10
:destination (make-symbol (format nil "bottom-of-page~d"
(1- i))))
(pdf::register-destination (make-symbol (format nil
"bottom-of-page~d" i))
)))
))
(pdf:write-document file)))
(ex-dest (current-pathname "ex-dest.pdf"))
;======================= End of Example =====================
Named destinations could benefit when using Acrobat Interapplication
Communication.
For example, in LWW, you can automate AcroReader via DDE as follows:
(setq fullpath "E:\\Projects\\cl-pdf\\examples\\ex-dest.pdf")
(win32:dde-execute-command* 'acroview 'control "DocOpen"
`(,fullpath))
;; The first page is numbered 0.
(win32:dde-execute-command* 'acroview 'control "DocGoTo"
`(,fullpath 0))
(win32:dde-execute-command* 'acroview 'control "DocGoToNameDest"
`(,fullpath "top-of-page3"))
(win32:dde-execute-command* 'acroview 'control "DocGoToNameDest"
`(,fullpath "bottom-of-page6"))
(win32:dde-execute-command* 'acroview 'control "DocClose"
`(,fullpath))
--
Sincerely,
Dmitri Ivanov
lisp.ystok.ru
More information about the cl-pdf-devel
mailing list