PDF files exported from the notebook have no bookmarks, It's not convenient. Is there any way to add bookmarks for each sections in the exported pdf files? The table of content is also not supported.
-
$\begingroup$ What have you tried? Did you read the details in the Export PDF format? Please do share minimum due diligence. There is always the option of understanding the internal file format and appending extra information, however, that is very cumbersome. The main problem I see is that Wolfram Mathematica doesn't attempt to create files directly but trusts external libraries, which greatly reduces flexibility. $\endgroup$– rhermansMay 6, 2022 at 14:12
2 Answers
PDF files exported from the notebook have no bookmarks
I am assuming by bookmarks you mean table of content and sections, subsections links?
If so, this is what I would do. This needs slight manual steps, but it took me 60 seconds to do.
The idea is to covert the notebook to latex. Edit the latex and add \usepackage{hyperref}
and \tableofcontents
and remove the *
added by Mathematica to all sections and subsection names. For some reason Mathematica adds *
to these names which prevent them from showing in table of content and add \today
to the \date
command as it was left empty.
This can be done using the editor in one command by global replace.
Here is the original notebook
Below is the PDF, with table of content. All active links, backlinks, etc.. all in the PDF.
For some reason exporting to latex does not handle chapters. Only sections as Mathematica makes the document an article class instead of a book class. There might be a way to fix this. I have not looked at all the options. So for now, do not use chapters in your notebook, start with sections only.
Here is a zip file that includes the notebook, latex generated after editing, and the PDF file. This was converted to pdf using lualatex.
I have been using Mathemtica to write notes. It's really convenient to use notebook files because we can write program, formular, figures and text in one file. However, not everyone uses Mathematica, If we want to share our notes with others, exporting nb files into pdf format is necessary.
In default, the generated pdf by Mathematica is relatively simple, and there is no TOC or bookmarks in the generated pdf files. It will be very inconvenient to read if the notes are very long. I have searched on stack exchange, but it seems that there is no existing good solution. According to the question, may be we can save the nb files into tex
format and then compile it into pdf, but (according to my experience) problem exists when the nb file is long and the formular is complicated.
Finally, I myself try to add bookmarks mannually. I use python to process the generated pdf files, what I want to realize can be summarized as follows:
- Using Mathematica: For a long notebook file, we directly generate the pdf files (
pdf file 1
) - Using Mathematica: Then I will extract the chapter/section information and generate a pdf file (
pdf file 2
) containing the table of contents (with suitable pdf page numbers) - Using Python:Finally, I will use python to merge the two pdfs into
pdf file 3
and add bookmarks into this merged pdf (pdf file 3
).
It seems that the above process is cumbersome, but it can be automated by the program, and the operation is very simple. I will show an example:
Assuming that the notebook file to be processed is test.nb
, then in Mathematica
(1) we just need run the following program to generate the helping files (modefied from the question)
(*The notebook directory*)
fileDir =
"./";
(*The notebook file*)
fileNb = "test.nb";
GenTOCtoPDFTZH[fileDir, fileNb];
NOTES:
By runing above Mathematica program, two pdf files will be generated (
pdf file 1
,pdf file 2
). Extra txt file namestest.txt
is also generated. In this txt file, information about the TOC are saved.
(2)then we use the corresponding python program (see appendix II) to generate the final pdf files.
If you don't want to install python, I have also written a program with simple GUI and compiled it into Exe format. This can be directly run on windows. Original code can be found on this
the final pdf files will have toc and bookmarks:
However, by using python, the original links in PDF will be lost, so you can't jump into certain page by clicking the links, this is the problem of the python package PyPDF2
.
Appendix
the Mathematica function
modefied from the question
*helper file for creating cell*)
createCell[text_,tag_,level_]:=Cell[BoxData[TagBox[GridBox[{{"",text,CounterBox["Page",{bookUrl,tag}]}},GridBoxAlignment->{"Columns"->{Left,Left,Right}},GridBoxItemSize->{"Columns"->{2 level-1,35-2 level,5}}],"Grid"]],"Text",FontColor->Blue];
(*helper file for creating cell version II*)
createCellPy[text_,tag_,level_]:=Cell[BoxData[TagBox[GridBox[{{"",text,";",level,";",CounterBox["Page",{bookUrl,tag}],";"}},GridBoxAlignment->{"Columns"->{Left,Left,Left,Left,Right}},GridBoxItemSize->{"Columns"->{2 level-1,35-2 level,5}}],"Grid"]],"Text"];
GenTOCtoPDFTZH[fileDir_,fileNb_]:=Block[{typeList,filepdf,filepdfToc,filetxt,bookUrl,tocPDF,tocPy,book,fexp},
typeList={"Section","Subsection"};
filepdf=StringReplace[fileNb,".nb"->".pdf"];
filepdfToc=StringReplace[fileNb,".nb"->"Toc.pdf"];
filetxt=StringReplace[fileNb,".nb"->".txt"];
bookUrl=FileNameJoin[{fileDir,fileNb}];
SetDirectory[fileDir];
tocPDF=CreateDocument[];
SelectionMove[tocPDF,Before,Notebook];
NotebookWrite[tocPDF,Cell["目录","Text",20,Blue]];
tocPy=CreateDocument[];
book=NotebookOpen[bookUrl];
SetOptions[book,ShowPageBreaks->True];
(*iterate over cells to set tags and write lines to TOC*)
Scan[(counter[#]=0)&,typeList];
SelectionMove[book,Before,Notebook];
SelectionMove[book,Next,Cell];
While[(cell=NotebookRead[book])=!={},If[Length[cell]>=2,type=cell[[2]];
If[MemberQ[typeList,type],counter[type]+=1;
tag=type<>ToString[counter[type]];
SetOptions[NotebookSelection[book],CellTags->Union[Flatten[{Options[NotebookSelection[book],CellTags][[1,2]],tag}]]];
SelectionMove[book,All,CellContents];
NotebookWrite[tocPDF,createCell[NotebookRead[book],tag,Position[typeList,type][[1,1]]]];
NotebookWrite[tocPy,createCellPy[NotebookRead[book],tag,Position[typeList,type][[1,1]]]]];];
SelectionMove[book,Next,Cell]];
(*存储为txt文件*)
fexp=FileNameJoin[{fileDir,filetxt}];
FrontEndExecute[FrontEndToken[tocPy,"Save",{fexp,"Text"}]];
(*存储笔记本文件为pdf文件供Python使用*)
fexp=FileNameJoin[{fileDir,filepdf}];
Export[fexp,book];
(*存储目录文件为pdf文件供Python使用*)
fexp=FileNameJoin[{fileDir,filepdfToc}];
Export[fexp,tocPDF];
(*关闭所有*)
NotebookClose[book];
NotebookClose[tocPDF];
NotebookClose[tocPy];]
(*(*复制目录到目标笔记本*)
SelectionMove[tocPDF,All,Notebook];
data=NotebookRead[tocPDF];
SelectionMove[book,Before,Notebook];
NotebookWrite[book,Cell["目录","Text",20,Blue]];
NotebookWrite[book,NotebookRead[tocPDF]];*)
(*存储为txt文件供Python使用*)
Python program to generate TOC
# we set the file name to be processed
dir='./'
fileName='test'
dele=0 # 0 means delete,1 means keep
from PyPDF2 import PdfReader, PdfWriter
import os
import sys
#sys.path.insert(1, '') #
os.chdir(dir)
filetxt=fileName+'.txt'
filepdf=fileName+'.pdf'
filepdfTOC=fileName+'Toc.pdf'
filepdfout=fileName+'WithToc.pdf'
# read lines from txt
with open(filetxt,'r',encoding='utf-8') as f:
lines = f.read().split(";")
numlines=int(len(lines)/3)
tit_list=[]
level_list=[]
page_list=[]
for l in range(numlines):
tit_list.append(lines[3*l].replace("\t", "").replace("\n", ""))
level_list.append(lines[3*l+1].replace("\t", "").replace("\n", ""))
page_list.append(lines[3*l+2].replace("\t", "").replace("\n", ""))
#print(tit_list)
#print(level_list)
#print(page_list)
# generate bookmarks
reader_Main = PdfReader(filepdf) # open input
reader_Toc = PdfReader(filepdfTOC) # open input
writer = PdfWriter() # open output
n_Main = len(reader_Main.pages)
n_Toc = len(reader_Toc.pages)
for i in range(n_Toc):
writer.add_page(reader_Toc.pages[i]) # insert page of TOC
for i in range(n_Main):
writer.add_page(reader_Main.pages[i]) # insert page of Main
for l in range(numlines):
if level_list[l]=="1":
par=writer.add_outline_item(tit_list[l], int(page_list[l]), parent=None)
elif level_list[l]=="2":
writer.add_outline_item(tit_list[l], int(page_list[l]), parent=par)
with open(filepdfout, "wb") as fp: # creating result pdf JCT
writer.write(fp) # writing to result pdf JCT
if dele==0:
# Delete the mid files
os.remove(filepdf)
os.remove(filepdfTOC)
os.remove(filetxt)