221 lines
7.8 KiB
EmacsLisp
221 lines
7.8 KiB
EmacsLisp
;;; org-krita.el --- Krita support for Org Mode -*- lexical-binding: t; -*-
|
|
|
|
;; Copyright (c) 2020 Abhinav Tushar
|
|
|
|
;; Author: Abhinav Tushar <abhinav@lepisma.xyz>
|
|
;; Forked by: Emmet (LibrePhoenix) <emmet@librephoenix.com>
|
|
;; Version: 0.2.1
|
|
;; Package-Requires: ((emacs "26") (f "0.20.0") (org "9.3"))
|
|
;; URL: https://github.com/librephoenix/org-krita
|
|
|
|
;;; Commentary:
|
|
|
|
;; Krita support for Org Mode
|
|
;; This file is not a part of GNU Emacs.
|
|
|
|
;;; License:
|
|
|
|
;; This program 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.
|
|
|
|
;; This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Code:
|
|
|
|
(require 'arc-mode)
|
|
(require 'filenotify)
|
|
(require 'f)
|
|
(require 'cl-lib)
|
|
(require 'org)
|
|
|
|
(org-link-set-parameters "krita" :follow #'org-krita-edit :export #'org-krita-export)
|
|
|
|
;; NOTE: Only single reference for a file supported as of now.
|
|
|
|
(defgroup org-krita nil
|
|
"Org-krita customization."
|
|
:group 'org
|
|
:package-version '(org-krita . "0.2.0"))
|
|
|
|
(defcustom org-krita-executable "krita"
|
|
"The command of krita or full path to executable."
|
|
:type 'string
|
|
:safe #'stringp
|
|
:group 'org-krita
|
|
:package-version '(org-krita . "0.2.1"))
|
|
|
|
(defcustom org-krita-append-ext-kra t
|
|
"Append automatically .kra extension."
|
|
:group 'org-krita
|
|
:type 'boolean
|
|
:package-version '(org-krita . "0.2.0"))
|
|
|
|
(defcustom org-krita-get-new-filepath (lambda () (read-file-name "New krita file: "))
|
|
"Function returning filepath of new created image."
|
|
:group 'org-krita
|
|
:type 'function
|
|
:package-version '(org-krita . "0.2.0"))
|
|
|
|
(defcustom org-krita-get-new-desc (lambda () (read-string "Description: "))
|
|
"Function returning description of new created image."
|
|
:group 'org-krita
|
|
:type 'function
|
|
:package-version '(org-krita . "0.2.0"))
|
|
|
|
(defvar-local org-krita-watchers nil
|
|
"A-list mapping file names to change watcher descriptors.")
|
|
|
|
(defvar-local org-krita-overlays nil
|
|
"A-list mapping file names to overlay.")
|
|
|
|
(defconst org-krita-dir (file-name-directory load-file-name)
|
|
"Base directory for package.")
|
|
|
|
(defvar org-krita-extract-filename "mergedimage.png"
|
|
"Filename to extract from krita file for image preview.
|
|
Set to \"mergedimage.png\" for highest fidelity or \"preview.png\"
|
|
for better performance (esp. for large images).")
|
|
|
|
(defvar org-krita-scale 1
|
|
"Scale factor to apply to krita image previews from org-krita.")
|
|
|
|
(defun org-krita-resource (file)
|
|
"Return full path of a resource FILE."
|
|
(expand-file-name file (file-name-as-directory (concat org-krita-dir "resources"))))
|
|
|
|
(defun org-krita-export (_path _desc _backend)
|
|
"Export krita canvas _PATH from Org files.
|
|
Argument _DESC refers to link description.
|
|
Argument _BACKEND refers to export backend."
|
|
(let ((png-path (f-swap-ext _path "png")))
|
|
(cl-case _backend
|
|
(html (format "<img src=\"%s\">"
|
|
(prog1 png-path
|
|
(org-krita-save-image _path png-path))))
|
|
(ascii (format "%s (%s)" (or _desc _path) _path))
|
|
(latex (format "\\includegraphics[width=\\textheight,height=\\textwidth,keepaspectratio]{%s}"
|
|
(prog1 png-path
|
|
(org-krita-save-image _path png-path)))))))
|
|
|
|
(defun org-krita-save-image (kra-path png-path)
|
|
"Extract from KRA-PATH a .png and write it to PNG-PATH."
|
|
(let ((image (create-image (org-krita-extract-png kra-path) 'png t)))
|
|
(with-temp-buffer
|
|
(insert (plist-get (cdr image) :data))
|
|
(write-region (point-min) (point-max) png-path))))
|
|
|
|
(defun org-krita-make-new-image (output-kra-path &optional width height)
|
|
"Create a new image based on a template at OUTPUT-KRA-PATH."
|
|
(let ((template (org-krita-resource "template.kra")))
|
|
;; TODO: Change image width and height based on provided argument
|
|
(f-copy template output-kra-path)))
|
|
|
|
(defun org-krita-extract-png (kra-path)
|
|
"Extract png from given KRA-PATH and return data."
|
|
(with-temp-buffer
|
|
(set-buffer-multibyte nil)
|
|
(archive-zip-extract (expand-file-name kra-path) org-krita-extract-filename)
|
|
(buffer-string)))
|
|
|
|
(defun org-krita-get-links ()
|
|
(org-element-map (org-element-parse-buffer) 'link
|
|
(lambda (link)
|
|
(when (string-equal (org-element-property :type link) "krita")
|
|
link))))
|
|
|
|
(defun org-krita-event-file-path (event)
|
|
(if (eq (nth 1 event) 'renamed)
|
|
(nth 3 event)
|
|
(nth 2 event)))
|
|
|
|
(defun org-krita-watcher-callback (event)
|
|
"Callback that runs after krita files are modified."
|
|
(let* ((kra-path (org-krita-event-file-path event))
|
|
(links (org-krita-get-links))
|
|
(paths (mapcar (lambda (it) (expand-file-name (org-element-property :path it))) links))
|
|
(idx (cl-position kra-path paths :test #'string-equal)))
|
|
(when idx (org-krita-show-link (nth idx links)))))
|
|
|
|
(defun org-krita-add-watcher (kra-path)
|
|
"Setup auto-refreshing watcher for given krita LINK."
|
|
(let ((desc (file-notify-add-watch kra-path '(change) #'org-krita-watcher-callback)))
|
|
(unless (alist-get kra-path org-krita-watchers nil nil #'string-equal)
|
|
(push (cons kra-path desc) org-krita-watchers))))
|
|
|
|
(defun org-krita-edit (path &optional canvasonly-mode)
|
|
"Edit given PATH in krita.
|
|
|
|
If CANVASONLY-MODE is not null, run krita in canvasonly mode."
|
|
(let ((kra-path (expand-file-name path)))
|
|
(when (f-exists-p kra-path)
|
|
(if canvasonly-mode
|
|
(call-process org-krita-executable nil 0 nil "--canvasonly" "--nosplash" kra-path)
|
|
(call-process org-krita-executable nil 0 nil "--nosplash" kra-path))
|
|
(org-krita-add-watcher kra-path))))
|
|
|
|
(defun org-krita-hide-link (link)
|
|
(let ((overlay (alist-get (org-element-property :path link) org-krita-overlays nil nil #'string-equal)))
|
|
(when overlay (delete-overlay overlay))))
|
|
|
|
(defun org-krita-show-link (link)
|
|
(org-krita-hide-link link)
|
|
(let* ((start (org-element-property :begin link))
|
|
(end (org-element-property :end link))
|
|
(overlay (make-overlay start end))
|
|
(kra-path (org-element-property :path link)))
|
|
(overlay-put overlay 'display (create-image (org-krita-extract-png kra-path) 'png t :scale org-krita-scale))
|
|
(push (cons kra-path overlay) org-krita-overlays)))
|
|
|
|
(defun org-krita-hide-all ()
|
|
(dolist (link (org-krita-get-links))
|
|
(org-krita-hide-link link)))
|
|
|
|
(defun org-krita-enable ()
|
|
(dolist (link (org-krita-get-links))
|
|
(org-krita-show-link link)))
|
|
|
|
(defun org-krita-disable ()
|
|
"Disable watchers and hide krita images."
|
|
(dolist (watcher org-krita-watchers)
|
|
(file-notify-rm-watch (cdr watcher)))
|
|
(setq org-krita-watchers nil)
|
|
(org-krita-hide-all))
|
|
|
|
(defun org-krita-validate-path (path)
|
|
"Validate the file PATH as a krita path."
|
|
(if (f-ext-p path "kra")
|
|
path
|
|
(if org-krita-append-ext-kra
|
|
(concat path ".kra")
|
|
path)))
|
|
|
|
;;;###autoload
|
|
(defun org-krita-insert-new-image (output-kra-path desc)
|
|
"Insert new image in current buffer."
|
|
(interactive
|
|
(let ((output-kra-path (funcall org-krita-get-new-filepath))
|
|
(desc (funcall org-krita-get-new-desc)))
|
|
(list (org-krita-validate-path output-kra-path) desc)))
|
|
(org-krita-make-new-image output-kra-path)
|
|
(org-insert-link nil (concat "krita:" output-kra-path) desc)
|
|
;; TODO: Enable only the new image
|
|
(org-krita-enable))
|
|
|
|
;;;###autoload
|
|
(define-minor-mode org-krita-mode
|
|
"Mode for displaying editable krita images within Org file."
|
|
:init-value nil
|
|
(if org-krita-mode (org-krita-enable) (org-krita-disable)))
|
|
|
|
(provide 'org-krita)
|
|
|
|
;;; org-krita.el ends here
|