ox-moderncv.el 8.31 KB
Newer Older
1
;;; ox-moderncv.el --- LaTeX moderncv Back-End for Org Export Engine -*- lexical-binding: t; -*-
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

;; Copyright (C) 2018 Free Software Foundation, Inc.

;; Author: Oscar Najera <hi AT oscarnajera.com DOT com>
;; Keywords: org, wp, tex

;; This file is not part of GNU Emacs.

;; 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, 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 GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary:
;;
;; This library implements a LaTeX moderncv back-end, derived from the
;; LaTeX one.

;;; Code:
31 32
(require 'cl-lib)
(require 'ox-latex)
Óscar Nájera's avatar
Óscar Nájera committed
33
(require 'org-cv-utils)
34

35
;; Install a default set-up for moderncv export.
36
(unless (assoc "moderncv" org-latex-classes)
37
  (add-to-list 'org-latex-classes
Óscar Nájera's avatar
Óscar Nájera committed
38
               '("moderncv"
39
                 "\\documentclass{moderncv}"
Óscar Nájera's avatar
Óscar Nájera committed
40 41 42
                 ("\\section{%s}" . "\\section*{%s}")
                 ("\\subsection{%s}" . "\\subsection*{%s}")
                 ("\\subsubsection{%s}" . "\\subsubsection*{%s}"))))
43 44 45 46 47 48 49 50 51


;;; User-Configurable Variables

(defgroup org-export-cv nil
  "Options specific for using the moderncv class in LaTeX export."
  :tag "Org moderncv"
  :group 'org-export
  :version "25.3")
Óscar Nájera's avatar
Óscar Nájera committed
52 53

;;; Define Back-End
54
(org-export-define-derived-backend 'moderncv 'latex
55
  :options-alist
Óscar Nájera's avatar
Óscar Nájera committed
56
  '((:latex-class "LATEX_CLASS" nil "moderncv" t)
57
    (:cvstyle "CVSTYLE" nil "classic" t)
58
    (:cvcolor "CVCOLOR" nil nil t)
59
    (:mobile "MOBILE" nil nil parse)
Óscar Nájera's avatar
Óscar Nájera committed
60
    (:homepage "HOMEPAGE" nil nil parse)
61
    (:address "ADDRESS" nil nil newline)
Óscar Nájera's avatar
Óscar Nájera committed
62
    (:photo "PHOTO" nil nil parse)
Óscar Nájera's avatar
Óscar Nájera committed
63 64 65
    (:gitlab "GITLAB" nil nil parse)
    (:github "GITHUB" nil nil parse)
    (:linkedin "LINKEDIN" nil nil parse)
Óscar Nájera's avatar
Óscar Nájera committed
66
    (:with-email nil "email" t t)
Óscar Nájera's avatar
Óscar Nájera committed
67
    )
68
  :translate-alist '((template . org-moderncv-template)
Óscar Nájera's avatar
Óscar Nájera committed
69
                     (headline . org-moderncv-headline)))
70

71

72 73 74
;;;; Template
;;
;; Template used is similar to the one used in `latex' back-end,
75
;; excepted for the table of contents and moderncv themes.
76

77
(defun org-moderncv-template (contents info)
78 79 80 81
  "Return complete document string after LaTeX conversion.
CONTENTS is the transcoded contents string.  INFO is a plist
holding export options."
  (let ((title (org-export-data (plist-get info :title) info))
Óscar Nájera's avatar
Óscar Nájera committed
82
        (spec (org-latex--format-spec info)))
83 84 85
    (concat
     ;; Time-stamp.
     (and (plist-get info :time-stamp-file)
Óscar Nájera's avatar
Óscar Nájera committed
86
          (format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
87
     ;; LaTeX compiler.
88
     (org-latex--insert-compiler info)
89 90
     ;; Document class and packages.
     (org-latex-make-preamble info)
91 92 93 94 95
     ;; cvstyle
     (let ((cvstyle (org-export-data (plist-get info :cvstyle) info)))
       (when cvstyle (format "\\moderncvstyle{%s}\n" cvstyle)))
     ;; cvcolor
     (let ((cvcolor (org-export-data (plist-get info :cvcolor) info)))
96
       (when (not (string-empty-p cvcolor)) (format "\\moderncvcolor{%s}\n" cvcolor)))
97 98 99
     ;; Possibly limit depth for headline numbering.
     (let ((sec-num (plist-get info :section-numbers)))
       (when (integerp sec-num)
Óscar Nájera's avatar
Óscar Nájera committed
100
         (format "\\setcounter{secnumdepth}{%d}\n" sec-num)))
101 102
     ;; Author.
     (let ((author (and (plist-get info :with-author)
Óscar Nájera's avatar
Óscar Nájera committed
103 104
                        (let ((auth (plist-get info :author)))
                          (and auth (org-export-data auth info))))))
105
       (format "\\name{%s}{}\n" author))
Óscar Nájera's avatar
Óscar Nájera committed
106 107
     ;; photo
     (let ((photo (org-export-data (plist-get info :photo) info)))
108 109
       (when (org-string-nw-p photo)
         (format "\\photo{%s}\n" photo)))
110
     ;; email
111
     (let ((email (and (plist-get info :with-email)
Óscar Nájera's avatar
Óscar Nájera committed
112
                       (org-export-data (plist-get info :email) info))))
113 114
       (when (org-string-nw-p email)
         (format "\\email{%s}\n" email)))
115 116
     ;; phone
     (let ((mobile (org-export-data (plist-get info :mobile) info)))
117 118
       (when (org-string-nw-p mobile)
         (format "\\phone[mobile]{%s}\n" mobile)))
119 120
     ;; homepage
     (let ((homepage (org-export-data (plist-get info :homepage) info)))
121 122
       (when (org-string-nw-p homepage)
         (format "\\homepage{%s}\n" homepage)))
123 124
     ;; address
     (let ((address (org-export-data (plist-get info :address) info)))
125 126 127 128
       (when (org-string-nw-p address)
         (format "\\address%s\n" (mapconcat (lambda (line)
                                              (format "{%s}" line))
                                            (split-string address "\n") ""))))
129
     (mapconcat (lambda (social-network)
130 131 132 133 134
                  (let ((network (org-export-data
                                  (plist-get info (car social-network)) info)))
                    (when (org-string-nw-p network)
                      (format "\\social[%s]{%s}\n"
                              (nth 1 social-network) network))))
Óscar Nájera's avatar
Óscar Nájera committed
135 136 137 138
                '((:github "github")
                  (:gitlab "gitlab")
                  (:linkedin "linkedin"))
                "")
139

140 141 142
     ;; Date.
     (let ((date (and (plist-get info :with-date) (org-export-get-date info))))
       (format "\\date{%s}\n" (org-export-data date info)))
143

144 145
     ;; Title and subtitle.
     (let* ((subtitle (plist-get info :subtitle))
Óscar Nájera's avatar
Óscar Nájera committed
146 147 148 149 150
            (formatted-subtitle
             (when subtitle
               (format (plist-get info :latex-subtitle-format)
                       (org-export-data subtitle info))))
            (separate (plist-get info :latex-subtitle-separate)))
151
       (concat
Óscar Nájera's avatar
Óscar Nájera committed
152 153 154 155
        (format "\\title{%s%s}\n" title
                (if separate "" (or formatted-subtitle "")))
        (when (and separate subtitle)
          (concat formatted-subtitle "\n"))))
156 157 158 159 160 161 162 163 164 165 166
     ;; Hyperref options.
     (let ((template (plist-get info :latex-hyperref-template)))
       (and (stringp template)
            (format-spec template spec)))
     ;; Document start.
     "\\begin{document}\n\n"
     ;; Title command.
     (let* ((title-command (plist-get info :latex-title-command))
            (command (and (stringp title-command)
                          (format-spec title-command spec))))
       (org-element-normalize-string
Óscar Nájera's avatar
Óscar Nájera committed
167 168 169 170 171 172
        (cond ((not (plist-get info :with-title)) nil)
              ((string= "" title) nil)
              ((not (stringp command)) nil)
              ((string-match "\\(?:[^%]\\|^\\)%s" command)
               (format command title))
              (t command))))
173 174 175 176
     ;; Document's body.
     contents
     ;; Creator.
     (and (plist-get info :with-creator)
Óscar Nájera's avatar
Óscar Nájera committed
177
          (concat (plist-get info :creator) "\n"))
178 179
     ;; Document end.
     "\\end{document}")))
180

Óscar Nájera's avatar
Óscar Nájera committed
181

182 183 184 185
(defun org-moderncv--format-cventry (headline contents info)
  "Format HEADLINE as as cventry.
CONTENTS holds the contents of the headline.  INFO is a plist used
as a communication channel."
186 187 188 189 190 191
  (let* ((title (org-export-data (org-element-property :title headline) info))
         (from-date (or (org-element-property :FROM headline) (error "No FROM property provided for cventry %s" title)))
         (to-date (org-element-property :TO headline))
         (employer (org-element-property :EMPLOYER headline))
         (location (or (org-element-property :LOCATION headline) ""))
         (note (or (org-element-property :NOTE headline) "")))
Óscar Nájera's avatar
Óscar Nájera committed
192
    (format "\\cventry{\\textbf{%s}}{%s}{%s}{%s}{%s}{%s}\n"
Óscar Nájera's avatar
Óscar Nájera committed
193
            (org-cv-utils--format-time-window from-date to-date)
Óscar Nájera's avatar
Óscar Nájera committed
194
            title employer location note contents)))
195 196 197


;;;; Headline
198 199
(defun org-moderncv-headline (headline contents info)
  "Transcode HEADLINE element into moderncv code.
200 201 202
CONTENTS is the contents of the headline.  INFO is a plist used
as a communication channel."
  (unless (org-element-property :footnote-section-p headline)
203
    (let ((environment (let ((env (org-element-property :CV_ENV headline)))
Óscar Nájera's avatar
Óscar Nájera committed
204
                         (or (org-string-nw-p env) "block"))))
205 206 207
      (cond
       ;; is a cv entry
       ((equal environment "cventry")
208
        (org-moderncv--format-cventry headline contents info))
209
       ((org-export-with-backend 'latex headline contents info))))))
210 211 212

(provide 'ox-moderncv)
;;; ox-moderncv ends here