4
$\begingroup$

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.

$\endgroup$
1
  • $\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$
    – rhermans
    May 6, 2022 at 14:12

2 Answers 2

6
$\begingroup$

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

Mathematica graphics

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.

enter image description here

Here is a zip file that includes the notebook, latex generated after editing, and the PDF file. This was converted to pdf using lualatex.

$\endgroup$
1
$\begingroup$

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 names test.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)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.