专注收集记录技术开发学习笔记、技术难点、解决方案
网站信息搜索 >> 请输入关键词:
您当前的位置: 首页 > PowerDesigner

运用Emacs Org制作PDF文档

发布时间:2011-06-20 20:21:29 文章来源:www.iduyao.cn 采编人员:星星草
使用Emacs Org制作PDF文档

Emacs Org 是什么,这里就不介绍了。

记得前不久我还用muse做笔记的,而现在就要用org,还是相当的纠结的。制作PDF最烦的就是中文显示了,周六用一整天折腾,终于搞定,现将过程记录下来。(仅在windows下测试通过,linux还未测试。)

本文只关注于使用Emacs Org生成PDF文档(尤其是中文显示问题),并假设读者对Emacs以及Emacs Org有一定的使用经历。

有任何问题,欢迎交流。邮件:wuyao721@163.com 微博:weibo.com/wuyao721 QQ:289811390 技术博客:blog.csdn.net/wuyao721 

1 工具包

1.1 Linux(Emacs + texlive + gbk2uni)

除了Emacs之外,还要安装texlive,它是Latex在Linux下的一个发行版,我们要用到它的一个程序pdflatex,用它将tex文件转成pdf。在ubuntu下这样安装:

sudo apt-get install emacs23 texlive-latex-recommended latex-cjk-chinese

1.2 Windows(Emacs + MinGW + CTeX + gbk2uni)

CTeX是Latex在Windows下的一个发行版,同样的,它提供了程序pdflatex。

MinGW是GNU在Winodws下的一个发行版,我们要用到它的一个程序iconv。如果字符编码是GBK,就不用iconv了。

2 把org给黑了

既然org-mode没有考虑中文问题,那么只能黑它一下了。1

2.1 添加新的latex文档类型

latex默认的文档类型有article, report, book和beamer,我们这里要新建一个支持中文的article,叫cjk-article,这个类型里,我们不使用默认的latex package(也就是org-export-latex-default-packages-alist),而使用自己定义的latex package(也就是org-export-latex-packages-alist)。

(add-to-list 'org-export-latex-classes
                  '("cjk-article"
                    "\\documentclass{article}
 [NO-DEFAULT-PACKAGES]
 [PACKAGES]
 [EXTRA]"
         ("\\section{%s}" . "\\section*{%s}")
         ("\\subsection{%s}" . "\\subsection*{%s}")
         ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
         ("\\paragraph{%s}" . "\\paragraph*{%s}")
         ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))

2.2 自定义latex package

上面说到,要用自定义latex package,那么它是怎样的呢,请看:

(setq org-export-latex-packages-alist '(
    (""   "CJK"   t)
    (""     "indentfirst"  nil)
    ("pdftex"     "graphicx"  t)
    (""     "fancyhdr" nil)
    ("CJKbookmarks=true"     "hyperref"  nil)
"%% Define a museincludegraphics command, which is
%%   able to handle escaped special characters in image filenames.
\\def\\museincludegraphics{%
  \\begingroup
  \\catcode`\\\|=0
  \\catcode`\\\\=12
  \\catcode`\\\#=12
  \\includegraphics[width=0.75\\textwidth]
}"))

考察上面的配置,我们关注两个要点:latex包CJK提供了中文支持,CJKbookmarks=true让中文书签得以显示。这样生成的tex文件就大概是下面的样子:

\usepackage{CJK}
\usepackage{indentfirst}
\usepackage[pdftex]{graphicx}
\usepackage{fancyhdr}
\usepackage[CJKbookmarks=true]{hyperref}

2.3 重定义生成pdf的命令

用pdflatex生成的pdf书签会有乱码问题,使用gbk2uni能解决这个问题。流程大概是这样的:

  1. pdflatex生成乱码书签的pdf
  2. iconv将utf-8格式的书签文本(也就是.out文件)转成gbk格式
  3. gbk2uni将书签文本转成指定格式
  4. pdflatex使用正确的书签文本生成没有乱码书签的pdf

如果org文件的格式是GBK,那么就不用第二步了。

(setq org-latex-to-pdf-process
  '("pdflatex -interaction nonstopmode -output-directory %o %f"
    "iconv -f utf-8 -t gbk %b.out > %b.out.bak"
    "mv %b.out.bak %b.out"
    "gbk2uni %b.out"
    "pdflatex -interaction nonstopmode -output-directory %o %f"
    "rm %b.out.bak %b.tex"))

2.4 其它的小小改动

到目前为止,生成的tex文件头部分已经没有问题了,下面就要解决正文的问题。生成的tex文件正文必须是这样的:

\begin{document}
\begin{CJK*}{UTF8}{gbsn}
.....
\end{CJK*}
\end{document}

参考函数org-export-latex-make-header和org-export-as-latex,我们可以看到要插入=\begin{CJK*}{UTF8}{gbsn}=这样的tex代码的正确的位置,还是比较麻烦的。2 所以,我们要对这两个函数做小小的修改。(修改的el配置在后面贴出)

2.5 配置汇总(org-hack.el)

(require 'org)
(require 'org-latex)

(add-to-list 'org-export-latex-classes
                  '("cjk-article"
                    "\\documentclass{article}
 [NO-DEFAULT-PACKAGES]
 [PACKAGES]
 [EXTRA]"
         ("\\section{%s}" . "\\section*{%s}")
         ("\\subsection{%s}" . "\\subsection*{%s}")
         ("\\subsubsection{%s}" . "\\subsubsection*{%s}")
         ("\\paragraph{%s}" . "\\paragraph*{%s}")
         ("\\subparagraph{%s}" . "\\subparagraph*{%s}")))

(setq org-export-latex-packages-alist '(
    (""   "CJK"   t)
    (""     "indentfirst"  nil)
    ("pdftex"     "graphicx"  t)
    (""     "fancyhdr" nil)
    ("CJKbookmarks=true"     "hyperref"  nil)
"%% Define a museincludegraphics command, which is
%%   able to handle escaped special characters in image filenames.
\\def\\museincludegraphics{%
  \\begingroup
  \\catcode`\\\|=0
  \\catcode`\\\\=12
  \\catcode`\\\#=12
  \\includegraphics[width=0.75\\textwidth]
}"))

(setq org-latex-to-pdf-process
  '("pdflatex -interaction nonstopmode -output-directory %o %f"
    "iconv -f utf-8 -t gbk %b.out > %b.out.bak"
    "mv %b.out.bak %b.out"
    "gbk2uni %b.out"
    "pdflatex -interaction nonstopmode -output-directory %o %f"
    "rm %b.out.bak %b.tex"))

(defun org-export-latex-make-header (title opt-plist)
  "Make the LaTeX header and return it as a string.
TITLE is the current title from the buffer or region.
OPT-PLIST is the options plist for current buffer."
  (let ((toc (plist-get opt-plist :table-of-contents))
        (author (org-export-apply-macros-in-string
                 (plist-get opt-plist :author)))
        (email (replace-regexp-in-string
                "_" "\\\\_"
                (org-export-apply-macros-in-string
                 (plist-get opt-plist :email)))))
    (concat
     (if (plist-get opt-plist :time-stamp-file)
         (format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
     ;; insert LaTeX custom header and packages from the list
     (org-splice-latex-header
      (org-export-apply-macros-in-string org-export-latex-header)
      org-export-latex-default-packages-alist
      org-export-latex-packages-alist nil
      (org-export-apply-macros-in-string
       (plist-get opt-plist :latex-header-extra)))
     ;; append another special variable
     (org-export-apply-macros-in-string org-export-latex-append-header)
     ;; define alert if not yet defined
     "\n\\providecommand{\\alert}[1]{\\textbf{#1}}"

     ;; changed by wuyao721@163.com
     ;; beginning of the document
     "\n\\begin{document}\n\n"
     "\n\\begin{CJK*}{UTF8}{gbsn}\n\n"
     ;; insert the title
     (format
      "\n\n\\title{%s}\n"
      (org-export-latex-fontify-headline title))
     ;; insert author info
     (if (plist-get opt-plist :author-info)
         (format "\\author{%s%s}\n"
                 (org-export-latex-fontify-headline (or author user-full-name))
                 (if (and (plist-get opt-plist :email-info) email
                          (string-match "\\S-" email))
                     (format "\\thanks{%s}" email)
                   ""))
       (format "%%\\author{%s}\n"
               (org-export-latex-fontify-headline (or author user-full-name))))
     ;; insert the date
     (format "\\date{%s}\n"
             (format-time-string
              (or (plist-get opt-plist :date)
                  org-export-latex-date-format)))
     ;; insert the title command
     (when (string-match "\\S-" title)
       (if (string-match "%s" org-export-latex-title-command)
           (format org-export-latex-title-command title)
         org-export-latex-title-command))
     "\n\n"
     ;; table of contents
     (when (and org-export-with-toc
                (plist-get opt-plist :section-numbers))
       (funcall org-export-latex-format-toc-function
                (cond ((numberp toc)
                       (min toc (plist-get opt-plist :headline-levels)))
                      (toc  (plist-get opt-plist :headline-levels))))))))

(defun org-export-as-latex (arg &optional hidden ext-plist
                                to-buffer body-only pub-dir)
  "Export current buffer to a LaTeX file.
If there is an active region, export only the region.  The prefix
ARG specifies how many levels of the outline should become
headlines.  The default is 3.  Lower levels will be exported
depending on `org-export-latex-low-levels'.  The default is to
convert them as description lists.
HIDDEN is obsolete and does nothing.
EXT-PLIST is a property list with
external parameters overriding org-mode's default settings, but
still inferior to file-local settings.  When TO-BUFFER is
non-nil, create a buffer with that name and export to that
buffer.  If TO-BUFFER is the symbol `string', don't leave any
buffer behind but just return the resulting LaTeX as a string.
When BODY-ONLY is set, don't produce the file header and footer,
simply return the content of \\begin{document}...\\end{document},
without even the \\begin{document} and \\end{document} commands.
when PUB-DIR is set, use this as the publishing directory."
  (interactive "P")
  (when (and (not body-only) arg (listp arg)) (setq body-only t))
  (run-hooks 'org-export-first-hook)

  ;; Make sure we have a file name when we need it.
  (when (and (not (or to-buffer body-only))
             (not buffer-file-name))
    (if (buffer-base-buffer)
        (org-set-local 'buffer-file-name
                       (with-current-buffer (buffer-base-buffer)
                         buffer-file-name))
      (error "Need a file name to be able to export")))

  (message "Exporting to LaTeX...")
  (org-unmodified
   (let ((inhibit-read-only t))
     (remove-text-properties (point-min) (point-max)
                             '(:org-license-to-kill nil))))
  (org-update-radio-target-regexp)
  (org-export-latex-set-initial-vars ext-plist arg)
  (setq org-export-opt-plist org-export-latex-options-plist
        org-export-footnotes-data (org-footnote-all-labels 'with-defs)
        org-export-footnotes-seen nil
        org-export-latex-footmark-seen nil)
  (org-install-letbind)
  (run-hooks 'org-export-latex-after-initial-vars-hook)
  (let* ((wcf (current-window-configuration))
         (opt-plist
          (org-export-process-option-filters org-export-latex-options-plist))
         (region-p (org-region-active-p))
         (rbeg (and region-p (region-beginning)))
         (rend (and region-p (region-end)))
         (subtree-p
          (if (plist-get opt-plist :ignore-subtree-p)
              nil
            (when region-p
              (save-excursion
                (goto-char rbeg)
                (and (org-at-heading-p)
                     (>= (org-end-of-subtree t t) rend))))))
         (opt-plist (setq org-export-opt-plist
                          (if subtree-p
                              (org-export-add-subtree-options opt-plist rbeg)
                            opt-plist)))
         ;; Make sure the variable contains the updated values.
         (org-export-latex-options-plist (setq org-export-opt-plist opt-plist))
         ;; The following two are dynamically scoped into other
         ;; routines below.
         (org-current-export-dir
          (or pub-dir (org-export-directory :html opt-plist)))
         (org-current-export-file buffer-file-name)
         (title (or (and subtree-p (org-export-get-title-from-subtree))
                    (plist-get opt-plist :title)
                    (and (not
                          (plist-get opt-plist :skip-before-1st-heading))
                         (org-export-grab-title-from-buffer))
                    (and buffer-file-name
                         (file-name-sans-extension
                          (file-name-nondirectory buffer-file-name)))
                    "No Title"))
         (filename
          (and (not to-buffer)
               (concat
                (file-name-as-directory
                 (or pub-dir
                     (org-export-directory :LaTeX ext-plist)))
                (file-name-sans-extension
                 (or (and subtree-p
                          (org-entry-get rbeg "EXPORT_FILE_NAME" t))
                     (file-name-nondirectory ;sans-extension
                      (or buffer-file-name
                          (error "Don't know which export file to use")))))
                ".tex")))
         (filename
          (and filename
               (if (equal (file-truename filename)
                          (file-truename (or buffer-file-name "dummy.org")))
                   (concat filename ".tex")
                 filename)))
         (buffer (if to-buffer
                     (cond
                      ((eq to-buffer 'string) (get-buffer-create
                                               "*Org LaTeX Export*"))
                      (t (get-buffer-create to-buffer)))
                   (find-file-noselect filename)))
         (odd org-odd-levels-only)
         (header (org-export-latex-make-header title opt-plist))
         (skip (cond (subtree-p nil)
                     (region-p nil)
                     (t (plist-get opt-plist :skip-before-1st-heading))))
         (text (plist-get opt-plist :text))
         (org-export-preprocess-hook
          (cons
           `(lambda () (org-set-local 'org-complex-heading-regexp
                                      org-export-latex-complex-heading-re))
           org-export-preprocess-hook))
         (first-lines (if skip "" (org-export-latex-first-lines
                                   opt-plist
                                   (if subtree-p
                                       (save-excursion
                                         (goto-char rbeg)
                                         (point-at-bol 2))
                                     rbeg)
                                   (if region-p rend))))
         (coding-system (and (boundp 'buffer-file-coding-system)
                             buffer-file-coding-system))
         (coding-system-for-write (or org-export-latex-coding-system
                                      coding-system))
         (save-buffer-coding-system (or org-export-latex-coding-system
                                        coding-system))
         (region (buffer-substring
                  (if region-p (region-beginning) (point-min))
                  (if region-p (region-end) (point-max))))
         (text
          (and text (string-match "\\S-" text)
               (org-export-preprocess-string
                text
                :emph-multiline t
                :for-backend 'latex
                :comments nil
                :tags (plist-get opt-plist :tags)
                :priority (plist-get opt-plist :priority)
                :footnotes (plist-get opt-plist :footnotes)
                :drawers (plist-get opt-plist :drawers)
                :timestamps (plist-get opt-plist :timestamps)
                :todo-keywords (plist-get opt-plist :todo-keywords)
                :tasks (plist-get opt-plist :tasks)
                :add-text nil
                :skip-before-1st-heading skip
                :select-tags nil
                :exclude-tags nil
                :LaTeX-fragments nil)))
         (string-for-export
          (org-export-preprocess-string
           region
           :emph-multiline t
           :for-backend 'latex
           :comments nil
           :tags (plist-get opt-plist :tags)
           :priority (plist-get opt-plist :priority)
           :footnotes (plist-get opt-plist :footnotes)
           :drawers (plist-get opt-plist :drawers)
           :timestamps (plist-get opt-plist :timestamps)
           :todo-keywords (plist-get opt-plist :todo-keywords)
           :tasks (plist-get opt-plist :tasks)
           :add-text (if (eq to-buffer 'string) nil text)
           :skip-before-1st-heading skip
           :select-tags (plist-get opt-plist :select-tags)
           :exclude-tags (plist-get opt-plist :exclude-tags)
           :LaTeX-fragments nil)))

    (set-buffer buffer)
    (erase-buffer)
    (org-install-letbind)

    (and (fboundp 'set-buffer-file-coding-system)
         (set-buffer-file-coding-system coding-system-for-write))

    ;; insert the header and initial document commands
    (unless (or (eq to-buffer 'string) body-only)
      (insert header))

    ;; insert text found in #+TEXT
    (when (and text (not (eq to-buffer 'string)))
      (insert (org-export-latex-content
               text '(lists tables fixed-width keywords))
               "\n\n"))

    ;; insert lines before the first headline
    (unless (or skip (string-match "^\\*" first-lines))
      (insert first-lines))

    ;; export the content of headlines
    (org-export-latex-global
     (with-temp-buffer
       (insert string-for-export)
       (goto-char (point-min))
       (when (re-search-forward "^\\(\\*+\\) " nil t)
         (let* ((asters (length (match-string 1)))
                (level (if odd (- asters 2) (- asters 1))))
           (setq org-export-latex-add-level
                 (if odd (1- (/ (1+ asters) 2)) (1- asters)))
           (org-export-latex-parse-global level odd)))))

    ;; changed by wuyao721@163.com
    ;; finalization
    ;;(unless body-only (insert "\n\\end{document}"))
    (unless body-only (insert "\n\\end{CJK*}")(insert "\n\\end{document}"))

    ;; Attach description terms to the \item macro
    (goto-char (point-min))
    (while (re-search-forward "^[ \t]*\\\\item\\([ \t]+\\)\\[" nil t)
      (delete-region (match-beginning 1) (match-end 1)))

    ;; Relocate the table of contents
    (goto-char (point-min))
    (when (re-search-forward "\\[TABLE-OF-CONTENTS\\]" nil t)
      (goto-char (point-min))
      (while (re-search-forward "\\\\tableofcontents\\>[ \t]*\n?" nil t)
        (replace-match ""))
      (goto-char (point-min))
      (and (re-search-forward "\\[TABLE-OF-CONTENTS\\]" nil t)
           (replace-match "\\tableofcontents" t t)))

    ;; Cleanup forced line ends in items where they are not needed
    (goto-char (point-min))
    (while (re-search-forward
            "^[ \t]*\\\\item\\>.*\\(\\\\\\\\\\)[ \t]*\\(\n\\\\label.*\\)*\n\\\\begin"
            nil t)
      (delete-region (match-beginning 1) (match-end 1)))
    (goto-char (point-min))
    (while (re-search-forward
            "^[ \t]*\\\\item\\>.*\\(\\\\\\\\\\)[ \t]*\\(\n\\\\label.*\\)*"
            nil t)
      (if (looking-at "[\n \t]+")
          (replace-match "\n")))

    (run-hooks 'org-export-latex-final-hook)
    (if to-buffer
        (unless (eq major-mode 'latex-mode) (latex-mode))
      (save-buffer))
    (org-export-latex-fix-inputenc)
    (run-hooks 'org-export-latex-after-save-hook)
    (goto-char (point-min))
    (or (org-export-push-to-kill-ring "LaTeX")
        (message "Exporting to LaTeX...done"))
    (prog1
        (if (eq to-buffer 'string)
            (prog1 (buffer-substring (point-min) (point-max))
              (kill-buffer (current-buffer)))
          (current-buffer))
      (set-window-configuration wcf))))

3 注意要点

如果你怎么整都是乱码的pdf,那么考虑以下几点

3.1 字符编码是否一致

看看是否把org文件保存成了GBK格式,而配置里却用了UTF-8。

3.2 分步实现

试试先生成tex文件,再用命令pdflatex生成pdf。这样能帮你发现问题所在。

3.3 emacs能否调用pdflatex,mv等命令

设置好环境变量,确保emacs能调用到这些命令

4 未解问题

有一些未解问题,请高手指点

4.1 示例超出pdf界限

以下的示例生成的pdf超出界限,怎么办呢?

apt-get install texlive-latex-base texlive-latex-recommended latex-cjk-chinese latex-cjk-chinese latex-cjk-chinese 

4.2 pdf超链接有红色框框

pdf超链接有红色框框,这样的显示效果不怎么好。怎么办?

5 参考资料

  • http://orgmode.org/manual/LaTeX-and-PDF-export.html
  • http://orgmode.org/worg/org-tutorials/org-latex-export.html
  • http://orgmode.org/worg/org-contrib/babel/examples/article-class.html
  • http://blog.csdn.net/ywj1225/article/details/7407316
  • http://comments.gmane.org/gmane.emacs.orgmode/52201

Footnotes:

1 网上好像是说xelatex可以解决utf-8的问题,但是我真的不知道该怎么整。

2 参考http://blog.csdn.net/ywj1225/article/details/7407316 和 http://comments.gmane.org/gmane.emacs.orgmode/52201

Date: 2012-06-17 02:07:17 中国标准时间

Author: 吴遥

Org version 7.7 with Emacs version 24

Validate XHTML 1.0
1楼mazhaojuan前天 10:05
高手呀!
友情提示:
信息收集于互联网,如果您发现错误或造成侵权,请及时通知本站更正或删除,具体联系方式见页面底部联系我们,谢谢。

其他相似内容:

热门推荐: