6
6
import java .nio .charset .StandardCharsets ;
7
7
import java .util .HashMap ;
8
8
import java .util .List ;
9
+ import java .util .regex .Matcher ;
10
+ import java .util .regex .Pattern ;
9
11
import org .apache .commons .lang3 .StringUtils ;
10
12
import org .slf4j .Logger ;
11
13
import org .slf4j .LoggerFactory ;
@@ -42,6 +44,9 @@ public class ExportServicePDF extends ServiceBase {
42
44
private ExportRequest req ;
43
45
private int baseSlashCount = 0 ;
44
46
47
+ private HashMap <String , TreeNode > treeItemsByNodeName = new HashMap <>();
48
+ private int figNumStart = 1 ;
49
+
45
50
/*
46
51
* Exports the node specified in the req. If the node specified is "/", or the repository root, then
47
52
* we don't expect a filename, because we will generate a timestamped one.
@@ -70,6 +75,8 @@ private void exportNodeToFile(String nodeId) {
70
75
TreeNode rootNode = req .isThreadAsPDF () ? svc_mongoRead .getThreadGraphTree (nodeId ) : //
71
76
svc_mongoRead .getSubGraphTree (nodeId , null , null , null );
72
77
78
+ prePocessTree (rootNode );
79
+
73
80
SubNode exportNode = rootNode .node ;
74
81
String fileName = svc_snUtil .getExportFileName (req .getFileName (), exportNode );
75
82
shortFileName = fileName + ".pdf" ;
@@ -106,10 +113,33 @@ private void exportNodeToFile(String nodeId) {
106
113
}
107
114
}
108
115
116
+ private void prePocessTree (TreeNode root ) {
117
+ if (root .node .getAttachments () != null && root .node .getAttachments ().size () > 0 ) {
118
+ root .figNumStart = figNumStart ;
119
+ figNumStart += root .node .getAttachments ().size ();
120
+ }
121
+
122
+ String nodeName = root .node .getName ();
123
+ if (nodeName != null ) {
124
+ if (treeItemsByNodeName .containsKey (nodeName )) {
125
+ log .warn ("Duplicate node name: " + nodeName + " on nodeId " + root .node .getIdStr ());
126
+ } else {
127
+ treeItemsByNodeName .put (nodeName , root );
128
+ }
129
+ }
130
+
131
+ if (root .children == null )
132
+ return ;
133
+
134
+ for (TreeNode c : root .children ) {
135
+ prePocessTree (c );
136
+ }
137
+ }
138
+
109
139
private void recurseNode (TreeNode tn , int level ) {
110
140
if (tn .node == null )
111
141
return ;
112
- processNode (tn . node );
142
+ processNode (tn );
113
143
114
144
if (level == 0 && req .isIncludeToc ()) {
115
145
markdown .append ("[TOC]" );
@@ -128,12 +158,12 @@ private void recurseNode(TreeNode tn, int level) {
128
158
}
129
159
}
130
160
131
- private void processNode (SubNode node ) {
161
+ private void processNode (TreeNode tn ) {
132
162
String nodeMarkdown = req .isDividerLine () ? "\n ----\n " : "\n " ;
133
163
134
- String id = req .isIncludeIDs () ? (" (id:" + node .getIdStr () + ")" ) : "" ;
164
+ String id = req .isIncludeIDs () ? (" (id:" + tn . node .getIdStr () + ")" ) : "" ;
135
165
if (req .isIncludeOwners ()) {
136
- AccountNode accntNode = svc_user .getAccountNode (node .getOwner ());
166
+ AccountNode accntNode = svc_user .getAccountNode (tn . node .getOwner ());
137
167
if (accntNode != null ) {
138
168
nodeMarkdown += "Owner: " + accntNode .getStr (NodeProp .USER ) + id + "\n " ;
139
169
}
@@ -143,27 +173,49 @@ private void processNode(SubNode node) {
143
173
}
144
174
nodeMarkdown += "\n " ;
145
175
146
- String content = node .getContent ();
147
- TypeBase plugin = svc_typeMgr .getPluginByType (node .getType ());
176
+ String content = tn . node .getContent ();
177
+ TypeBase plugin = svc_typeMgr .getPluginByType (tn . node .getType ());
148
178
if (plugin != null ) {
149
- content = plugin .formatExportText ("pdf" , node );
179
+ content = plugin .formatExportText ("pdf" , tn . node );
150
180
}
151
181
152
182
if (content != null && req .isUpdateHeadings ()) {
153
183
content = content .trim ();
154
- int slashCount = StringUtils .countMatches (node .getPath (), "/" );
184
+ int slashCount = StringUtils .countMatches (tn . node .getPath (), "/" );
155
185
int lev = slashCount - baseSlashCount ; // top level node comes here with lev=0
156
186
if (lev > 6 )
157
187
lev = 6 ;
158
188
content = svc_edit .translateHeadingsForLevel (content , lev );
159
189
}
160
190
161
- content = insertPropertySubstitutions (content , node );
191
+ content = insertPropertySubstitutions (content , tn .node );
192
+ content = injectFigureLinks (content );
162
193
nodeMarkdown += content + "\n " ;
163
- nodeMarkdown = writeImages (node , nodeMarkdown );
194
+ nodeMarkdown = writeImages (tn , nodeMarkdown );
164
195
markdown .append (nodeMarkdown );
165
196
}
166
197
198
+ private String injectFigureLinks (String content ) {
199
+ // using regex we find the pattern {{figure:[node_name]}} and iterate over all of them where the
200
+ // [node_name]
201
+ // is a variable that we need to have during iteration
202
+ String regex = "\\ {\\ {figure:([^\\ }]+)\\ }\\ }" ;
203
+ Pattern pattern = Pattern .compile (regex );
204
+ Matcher matcher = pattern .matcher (content );
205
+ while (matcher .find ()) {
206
+ String nodeName = matcher .group (1 );
207
+ log .debug ("FIGURE: " + nodeName );
208
+ TreeNode tn = treeItemsByNodeName .get (nodeName );
209
+ if (tn == null ) {
210
+ // needs to be a reported error that makes it's way to the screen.
211
+ log .warn ("Figure node not found: " + nodeName );
212
+ continue ;
213
+ }
214
+ content = content .replace ("{{figure:" + nodeName + "}}" , "Fig. " + tn .figNumStart );
215
+ }
216
+ return content ;
217
+ }
218
+
167
219
private String insertPropertySubstitutions (String content , SubNode node ) {
168
220
HashMap <String , Object > propMap = node .getProps ();
169
221
if (propMap != null && propMap .keySet () != null ) {
@@ -177,13 +229,15 @@ private String insertPropertySubstitutions(String content, SubNode node) {
177
229
return content ;
178
230
}
179
231
180
- private String writeImages (SubNode node , String content ) {
181
- List <Attachment > atts = node .getOrderedAttachments ();
232
+ private String writeImages (TreeNode tn , String content ) {
233
+ List <Attachment > atts = tn . node .getOrderedAttachments ();
182
234
if (atts == null )
183
235
return content ;
184
236
237
+ int figNum = tn .figNumStart - 1 ;
185
238
// process all attachments specifically to embed the image ones
186
239
for (Attachment att : atts ) {
240
+ figNum ++;
187
241
// Since GIFs are really only ever used for animated GIFs nowadays, and since PDF files cannot
188
242
// render them we just always ignore GIF files when generating PDFs.
189
243
if (att .getFileName () != null && att .getFileName ().toLowerCase ().endsWith (".gif" )) {
@@ -218,7 +272,7 @@ private String writeImages(SubNode node, String content) {
218
272
219
273
String src = null ;
220
274
if (bin != null ) {
221
- String path = AppController .API_PATH + "/bin/" + bin + "?nodeId=" + node .getIdStr () + "&token="
275
+ String path = AppController .API_PATH + "/bin/" + bin + "?nodeId=" + tn . node .getIdStr () + "&token="
222
276
+ URLEncoder .encode (TL .getSC ().getUserToken (), StandardCharsets .UTF_8 );
223
277
src = svc_prop .getProtocolHostAndPort () + path ;
224
278
} //
@@ -230,6 +284,10 @@ else if (url != null) {
230
284
231
285
String imgHtml = "\n <img src='" + src + "' " + style + "/>\n " ;
232
286
287
+ if (figNum > 0 ) {
288
+ imgHtml = "<figure>\n " + imgHtml + "<figcaption>Fig. " + figNum + "</figcaption>\n </figure>\n " ;
289
+ }
290
+
233
291
if ("ft" .equals (att .getPosition ())) {
234
292
content = content .replace ("{{" + att .getFileName () + "}}" , imgHtml );
235
293
} else {
0 commit comments