-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.html
3082 lines (2930 loc) · 168 KB
/
index.html
1
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta name="google-site-verification" content="5_8DTmIiEq4gFvNAfxAD6TsGOgrMAjp8lQFQvfA6zZc" />
<title>/var/</title>
<meta name="author" content="Serafeim Papastefanos">
<!-- http://t.co/dKP3o1e -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="http://spapas.github.io/favicon.png" rel="icon">
<link href="http://spapas.github.io/theme/css/main.css" media="screen, projection"
rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic"
rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic"
rel="stylesheet" type="text/css">
</head>
<body>
<header role="banner"><hgroup>
<h1><a href="http://spapas.github.io/">/var/</a></h1>
<h2>Various programming stuff</h2>
</hgroup></header>
<nav role="navigation"><ul class="subscription" data-subscription="rss">
</ul>
<ul class="main-navigation">
<li >
<a href="http://spapas.github.io/category/css.html">Css</a>
</li>
<li >
<a href="http://spapas.github.io/category/django.html">Django</a>
</li>
<li >
<a href="http://spapas.github.io/category/flask.html">Flask</a>
</li>
<li >
<a href="http://spapas.github.io/category/git.html">Git</a>
</li>
<li >
<a href="http://spapas.github.io/category/javascript.html">Javascript</a>
</li>
<li >
<a href="http://spapas.github.io/category/pelican.html">Pelican</a>
</li>
<li >
<a href="http://spapas.github.io/category/python.html">Python</a>
</li>
<li >
<a href="http://spapas.github.io/category/spring.html">Spring</a>
</li>
<li >
<a href="http://spapas.github.io/category/wagtail.html">Wagtail</a>
</li>
</ul></nav>
<div id="main">
<div id="content">
<div class="blog-index">
<article>
<header>
<h1 class="entry-title">
<a href="http://spapas.github.io/2015/12/22/ajax-with-react-fixed-data-table/">Ajax data with Fixed Data Table for React</a>
</h1>
<p class="meta">
<time datetime="2015-12-22T15:20:00+02:00" pubdate>Τρι 22 Δεκέμβριος 2015</time> </p>
</header>
<div class="entry-content"><div class="contents topic" id="contents">
<p class="topic-title first">Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#introduction" id="id1">Introduction</a></li>
<li><a class="reference internal" href="#our-project" id="id2">Our project</a></li>
<li><a class="reference internal" href="#how-fixeddatatable-gets-its-data" id="id3">How FixedDataTable gets its data</a></li>
<li><a class="reference internal" href="#ajaxcell-non-working-version-1" id="id4">AjaxCell: Non working version 1</a></li>
<li><a class="reference internal" href="#ajaxcell-non-working-version-2" id="id5">AjaxCell: Non working version 2</a></li>
<li><a class="reference internal" href="#ajaxcell-non-working-version-3" id="id6">AjaxCell: Non working version 3</a></li>
<li><a class="reference internal" href="#ajaxcell-final-version" id="id7">AjaxCell: Final version</a></li>
<li><a class="reference internal" href="#the-table-container-component" id="id8">The Table container component</a></li>
<li><a class="reference internal" href="#some-enchancements" id="id9">Some enchancements</a></li>
<li><a class="reference internal" href="#conslusion" id="id10">Conslusion</a></li>
</ul>
</div>
<div class="section" id="introduction">
<h2><a class="toc-backref" href="#id1">Introduction</a></h2>
<p><a class="reference external" href="https://facebook.github.io/fixed-data-table/">FixedDataTable</a> is a nice React component from Facebook that is used to render tabular data.
Its main characteristic is that it can handle many rows without sacrificing performance, however
probably due to this fact I was not able to find any examples for loading data using Ajax - all
examples I was able to find had the data already loaded (or created on the fly).</p>
<p>So, although FixedDataTable is able to handle many rows, I am against transfering all of them to the user
whenever our page loads since at most one page of data will be shown on screen (30 rows or so ?) -
any other actions (filtering, sorting, aggregate calculations etc) should be done on the server.</p>
<p>In the following I will present a simple, react-only example with a FixedDataTable that can be
used with server-side, asynchronous, paginated data.</p>
</div>
<div class="section" id="our-project">
<h2><a class="toc-backref" href="#id2">Our project</a></h2>
<p>Let’s see an example of what we’ll build:</p>
<img alt="Our project" src="/images/ajax_fixed_data_tables.gif" style="width: 600px;" />
<p>As a source of the data I’ve used the <a class="reference external" href="http://swapi.co/">Star Wars <span class="caps">API</span></a> and specifically its <a class="reference external" href="http://swapi.co/documentation#people">People <span class="caps">API</span></a> by issuing
requests to <a class="reference external" href="http://swapi.co/api/people/?format=json">http://swapi.co/api/people/?format=json</a>. This will return an array of people from the
star wars universe in <span class="caps">JSON</span> format - the results are paginated with a page size of 10 (and we can switch
to another page using the extra <tt class="docutils literal">page=</tt> request parameter).</p>
<p>I will use es6 with the object spread operator (as described in <a class="reference external" href="http://spapas.github.io/2015/11/16/using-browserify-es6/">a previous article</a>)
to write the code, using a single main.js as a source which will be transpiled to <tt class="docutils literal">dist/bundle.js</tt>.</p>
<p>The placeholder <span class="caps">HTML</span> for our application is:</p>
<pre class="code literal-block">
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello Ajax Fixed Data Table!</title>
<link rel="stylesheet" href='https://cdnjs.cloudflare.com/ajax/libs/fixed-data-table/0.6.0/fixed-data-table.min.css'>
</head>
<body>
<div id="main"></div>
<script type="text/javascript" src='dist/bundle.js' ></script>
</body>
</html>
</pre>
<p>The versions that are used are react 14.3 and fixed-data-table 0.6.0. You can find this project in github @
<a class="reference external" href="https://github.com/spapas/react-tables">https://github.com/spapas/react-tables</a> — tag name <tt class="docutils literal"><span class="pre">fixed-data-table-ajax</span></tt>.</p>
</div>
<div class="section" id="how-fixeddatatable-gets-its-data">
<h2><a class="toc-backref" href="#id3">How FixedDataTable gets its data</a></h2>
<p>The data of a <tt class="docutils literal">FixedDataTable</tt> component is defined through a number of <tt class="docutils literal">Column</tt> components
and specifically, the <tt class="docutils literal">cell</tt> attribute of that component which sould either return be a static string
to be displayed in that column or a React component (usually a <tt class="docutils literal">Cell</tt>) that will be displayed in
that column or even a function that returns a string or a react component. The function (or component)
passed to <tt class="docutils literal">cell</tt> will have an object with a <tt class="docutils literal">rowIndex</tt> attribute (among others) as a parameter,
for example the following</p>
<pre class="code literal-block">
<Column
header={<Cell>Url</Cell>}
cell={props => props.rowIndex}
width={200}
/>
</pre>
<p>will just display the rowIndex for each cell.</p>
<p>So we can see that for each cell that FixedDataTable wants to display it will use its corresponding
<tt class="docutils literal">cell</tt> attribute.
So, a general though if we wanted to support asynchronous, paginated data
is to check the rowIndex and retrieve the correct page asynchronously.
This would lead to a difficulty: What should the <tt class="docutils literal">cell</tt> function return? We’ll try to resolve this
using an <tt class="docutils literal">AjaxCell</tt> function that should return a react componet to put in the cell:</p>
</div>
<div class="section" id="ajaxcell-non-working-version-1">
<h2><a class="toc-backref" href="#id4">AjaxCell: Non working version 1</a></h2>
<p>A first sketch of an <tt class="docutils literal">`AjaxCell</tt> component could be something like this:</p>
<pre class="code literal-block">
// Careful - not working
const AjaxCell1 = ({rowIndex, col, ...props}) => {
let page = 1;
let idx = rowIndex;
if(rowIndex>=pageSize) {
page = Math.floor(rowIndex / pageSize) + 1;
idx = rowIndex % pageSize;
}
fetch('http://swapi.co/api/people/?format=json&page='+page).then(function(response) {
return response.json();
}).then(function(j) {
// Here we have the result ! But where will it go ?
});
return /// what ?
}
</pre>
<p>The col attribute will later be used to select the attribute we want to display in this cell.
The <tt class="docutils literal">page</tt> and <tt class="docutils literal">idx</tt> will have the correct page number and idx inside that page (for example,
if <tt class="docutils literal">rowIndex</tt> is 33, <tt class="docutils literal">page</tt> will be 4 and idx will be 3. The problem with the above is that
the fetch function will be called <em>asynchronously</em> so when the AjaxCell returns it will <em>not</em> have
the results (yet)! So we won’t be able to render anything :(</p>
</div>
<div class="section" id="ajaxcell-non-working-version-2">
<h2><a class="toc-backref" href="#id5">AjaxCell: Non working version 2</a></h2>
<p>To be able to render <em>something</em>, we could put the results of fetching a page to a <tt class="docutils literal">cache</tt> dictionary — and only
fetch that page if it is not inside the <tt class="docutils literal">cache</tt> - if it is inside the cache then we’ll just return
the correct value:</p>
<pre class="code literal-block">
// Careful - not working correctly
const cache = {}
const AjaxCell2 = ({rowIndex, col, forceUpdate, ...props}) => {
let page = 1;
let idx = rowIndex;
if(rowIndex>=pageSize) {
page = Math.floor(rowIndex / pageSize) + 1;
idx = rowIndex % pageSize;
}
if (cache[page]) {
return <Cell>{cache[page][idx][col]}</Cell>
} else {
console.log("Loading page " + page);
fetch('http://swapi.co/api/people/?format=json&page='+page).then(function(response) {
return response.json();
}).then(function(j) {
cache[page] = j['results'];
});
}
return <Cell>-</Cell>;
}
</pre>
<p>The above will work, since it will return an empty (<tt class="docutils literal"><span class="pre"><Cell>-</Cell></span></tt>) initially but when the
<tt class="docutils literal">fetch</tt> returns it will set the <tt class="docutils literal">cache</tt> for that page and return the correct value (<tt class="docutils literal"><span class="pre"><Cell>{cache[page][idx][col]}</Cell></span></tt>).</p>
<p>However, as can be understood, when the page first loads it will call <tt class="docutils literal">AjaxCell2</tt> for all visible cells —
because fetch is asynchronous and takes time until it returns (and sets the cache for that page), so the
<tt class="docutils literal">fetch</tt> will be called for <em>all</em> cells!</p>
</div>
<div class="section" id="ajaxcell-non-working-version-3">
<h2><a class="toc-backref" href="#id6">AjaxCell: Non working version 3</a></h2>
<p><tt class="docutils literal">fetch</tt> ing each page multiple times is of course not acceptable, so we’ll add a <tt class="docutils literal">loading</tt> flag
and <tt class="docutils literal">fetch</tt> will be called only when this flag is <tt class="docutils literal">false</tt>, like this:</p>
<pre class="code literal-block">
// Not ready yet
const cache = {};
let loading = false;
const AjaxCell2 = ({rowIndex, col, ...props}) => {
let page = 1;
let idx = rowIndex;
if(rowIndex>=pageSize) {
page = Math.floor(rowIndex / pageSize) + 1;
idx = rowIndex % pageSize;
}
if (cache[page]) {
return <Cell>{cache[page][idx][col]}</Cell>
} else if(!loading) {
console.log("Loading page " + page);
loading = true;
fetch('http://swapi.co/api/people/?format=json&page='+page).then(function(response) {
return response.json();
}).then(function(j) {
cache[page] = j['results'];
loading = false;
});
}
return <Cell>-</Cell>;
}
</pre>
<p>This works much better - the cells are rendered correctly and each page is loaded only once. However, if for example
I tried to move to the end of the table quickly, I would see some cells that are always loading (they never get their correct value). This is because
there is no way to know that the fetch function has actually completed in order to update with the latest (correct) value of that
cell and will contain the stale placeholder (<tt class="docutils literal"><span class="pre"><Cell>-</Cell></span></tt>) value.</p>
</div>
<div class="section" id="ajaxcell-final-version">
<h2><a class="toc-backref" href="#id7">AjaxCell: Final version</a></h2>
<p>To clear the stale data we need to do an update to the table data
when each fetch is finished — this should be done by a callback that will be passed to the <tt class="docutils literal">AjaxCell</tt>, like this:</p>
<pre class="code literal-block">
const cache = {};
let loading = false;
const AjaxCell = ({rowIndex, col, forceUpdate, ...props}) => {
let page = 1;
let idx = rowIndex;
if(rowIndex>=pageSize) {
page = Math.floor(rowIndex / pageSize) + 1;
idx = rowIndex % pageSize;
}
if (cache[page]) {
return <Cell>{cache[page][idx][col]}</Cell>
} else if(!loading) {
console.log("Loading page " + page);
loading = true;
fetch('http://swapi.co/api/people/?format=json&page='+page).then(function(response) {
return response.json();
}).then(function(j) {
cache[page] = j['results'];
loading = false;
forceUpdate();
});
}
return loadingCell;
}
</pre>
<p>So we pass a forceUpdate callback as a property which is called when a fetch is finished. This may
result to some not needed updates to the table (since we would do a fetch + forceUpdate for non-displayed
data) but we can now be positive that when the data is loaded the table will be updated to dispaly it.</p>
</div>
<div class="section" id="the-table-container-component">
<h2><a class="toc-backref" href="#id8">The Table container component</a></h2>
<p>Finally, the component that contains the table is the following:</p>
<pre class="code literal-block">
class TableContainer extends React.Component {
render() {
return <Table
rowHeight={30} rowsCount={87} width={600} height={200} headerHeight={30}>
<Column
header={<Cell>Name</Cell>}
cell={ <AjaxCell col='name' forceUpdate={this.forceUpdate.bind(this)} /> }
width={200}
/>
<Column
header={<Cell>Birth Year</Cell>}
cell={ <AjaxCell col='birth_year' forceUpdate={this.forceUpdate.bind(this)} /> }
width={200}
/>
<Column
header={<Cell>Url</Cell>}
cell={ <AjaxCell col='url' forceUpdate={this.forceUpdate.bind(this)} /> }
width={200}
/>
</Table>
}
}
</pre>
<p>I’ve made it a component in order to be able to bind the <tt class="docutils literal">forceUpdate</tt> method of the component to and
<tt class="docutils literal">this</tt> and pass it to the <tt class="docutils literal">forceUpdate</tt> parameter to the <tt class="docutils literal">AjaxCell</tt> component. I’ve hard-coded
the rowsCount value — instead we should have done an initial fetch to the first page of the <span class="caps">API</span> to get
the total number of rows and only after that fetch had returned display the <tt class="docutils literal"><Table></tt> component (left
as an exercise to the reader).</p>
</div>
<div class="section" id="some-enchancements">
<h2><a class="toc-backref" href="#id9">Some enchancements</a></h2>
<p>Instead of displaying <tt class="docutils literal"><span class="pre"><Cell>-</Cell></span></tt> (or <tt class="docutils literal"></Cell></tt>) when the page loads, I propose to define a
cell with an embedded spinner, like</p>
<pre class="code literal-block">
const loadingCell = <Cell>
<img width="16" height="16" alt="star" src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==" />
</Cell>
</pre>
<p>and return this instead.</p>
<p>Also, if your <span class="caps">REST</span> <span class="caps">API</span> returns too fast and you’d like to see what would happen if the server request took too long to return, you could
change fetch like this</p>
<pre class="code literal-block">
fetch('http://swapi.co/api/people/?format=json&page='+page).then(function(response) {
return response.json();
}).then(function(j) {
setTimeout( () => {
cache[page] = j['results'];
loading = false;
forceUpdate();
}, 1000);
});
</pre>
<p>to add a 1 second delay.</p>
</div>
<div class="section" id="conslusion">
<h2><a class="toc-backref" href="#id10">Conslusion</a></h2>
<p>The above is a just a proof of concept of using FixedDataTable with asynchronously loaded server-side data.
This of course could be used for small projects (I am already using it for an internal project) but I recommend
using the <a class="reference external" href="http://spapas.github.io/2015/07/02/comprehensive-react-flux-tutorial-2/">flux architecture</a> for more complex projects. What this more or
less means is that a store component
should be developed that will actually keep the data for each row, and a <tt class="docutils literal">fetchCompleted</tt> action should be
dispatched when the <tt class="docutils literal">fetch</tt> is finished instead of calling <tt class="docutils literal">forceUpdate</tt> directly.</p>
</div>
</div>
</article>
<article>
<header>
<h1 class="entry-title">
<a href="http://spapas.github.io/2015/11/27/pdf-in-django/">PDFs in Django: The essential guide</a>
</h1>
<p class="meta">
<time datetime="2015-11-27T10:20:00+02:00" pubdate>Παρ 27 Νοέμβριος 2015</time> </p>
</header>
<div class="entry-content"><div class="contents topic" id="contents">
<p class="topic-title first">Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#introduction" id="id4">Introduction</a></li>
<li><a class="reference internal" href="#the-players" id="id5">The players</a><ul>
<li><a class="reference internal" href="#reportlab" id="id6">ReportLab</a></li>
<li><a class="reference internal" href="#xhtml2pdf" id="id7">xhtml2pdf</a></li>
<li><a class="reference internal" href="#pypdf2" id="id8">PyPDF2</a></li>
</ul>
</li>
<li><a class="reference internal" href="#django-integration" id="id9">Django integration</a><ul>
<li><a class="reference internal" href="#using-a-plain-old-view" id="id10">Using a plain old view</a></li>
<li><a class="reference internal" href="#using-a-cbv" id="id11">Using a <span class="caps">CBV</span></a></li>
<li><a class="reference internal" href="#how-does-django-xhtml2pdf-loads-resources" id="id12">How does django-xhtml2pdf loads resources</a></li>
<li><a class="reference internal" href="#using-a-common-style-for-your-pdfs" id="id13">Using a common style for your PDFs</a></li>
<li><a class="reference internal" href="#changing-the-font-to-a-unicode-enabled-one" id="id14">Changing the font (to a Unicode enabled one)</a></li>
<li><a class="reference internal" href="#configure-django-for-debugging-pdf-creation" id="id15">Configure Django for debugging <span class="caps">PDF</span> creation</a></li>
<li><a class="reference internal" href="#concatenating-pdfs-in-django" id="id16">Concatenating PDFs in Django</a></li>
</ul>
</li>
<li><a class="reference internal" href="#more-advanced-xhtml2pdf-features" id="id17">More advanced xhtml2pdf features</a><ul>
<li><a class="reference internal" href="#laying-out" id="id18">Laying out</a></li>
<li><a class="reference internal" href="#extra-stuff" id="id19">Extra stuff</a></li>
</ul>
</li>
<li><a class="reference internal" href="#conclusion" id="id20">Conclusion</a></li>
</ul>
</div>
<div class="section" id="introduction">
<h2><a class="toc-backref" href="#id4">Introduction</a></h2>
<p>I’ve noticed that although it is easy to create PDFs with
Python, there’s no complete guide on how to
integrate these tools with Django and resolve the problems
that you’ll encounter when trying to actually create PDFs
from your Django web application.</p>
<p>In this article I will present the solution I use for
creating PDFs with Django, along with various tips on how to
solve most of your common requirements. Specifically, here
are some things that we’ll cover:</p>
<ul class="simple">
<li>Learn how to create PDFs "by hand"</li>
<li>Create PDFs with Django using a normal Djang Template (similar to an <span class="caps">HTML</span> page)</li>
<li>Change the fonts of your PDFs</li>
<li>Use styling in your output</li>
<li>Create layouts</li>
<li>Embed images to your PDFs</li>
<li>Add page numbers</li>
<li>Merge (concatenate) PDFs</li>
</ul>
</div>
<div class="section" id="the-players">
<h2><a class="toc-backref" href="#id5">The players</a></h2>
<p>We are going to use the following main tools:</p>
<ul class="simple">
<li><a class="reference external" href="https://bitbucket.org/rptlab/reportlab">ReportLab</a> is an open source python library for creating PDFs. It uses a low-level <span class="caps">API</span> that allows "drawing" strings on specific coordinates on the <span class="caps">PDF</span> - for people familiar with creating PDFs in Java it is more or less <a class="reference external" href="http://itextpdf.com/">iText</a> for python.</li>
<li><a class="reference external" href="https://github.com/chrisglass/xhtml2pdf">xhtml2pdf</a> (formerly named <em>pisa</em>) is an open source library that can convert <span class="caps">HTML</span>/<span class="caps">CSS</span> pages to <span class="caps">PDF</span> using ReportLab.</li>
<li><a class="reference external" href="https://github.com/chrisglass/django-xhtml2pdf">django-xhtml2pdf</a> is a wrapper around xhtml2pdf that makes integration with Django easier.</li>
<li><a class="reference external" href="https://github.com/mstamy2/PyPDF2">PyPDF2</a> is an open source tool of that can split, merge and transform pages of <span class="caps">PDF</span> files.</li>
</ul>
<p>I’ve created a <a class="reference external" href="https://github.com/spapas/django-pdf-guide">django project</a> <a class="reference external" href="https://github.com/spapas/django-pdf-guide">https://github.com/spapas/django-pdf-guide</a> with everything covered here. Please clone it,
install its requirements and play with it to see how everything works !</p>
<p>Before integrating the above tools to a Django project, I’d like to describe them individually a bit more. Any files
I mention below will be included in this project.</p>
<div class="section" id="reportlab">
<h3><a class="toc-backref" href="#id6">ReportLab</a></h3>
<p>ReportLab offers a really low <span class="caps">API</span> for creating PDFs. It is something like having a <tt class="docutils literal">canvas.drawString()</tt> method (for
people familiar with drawing APIs) for your <span class="caps">PDF</span> page. Let’s take a look at an example, creating a <span class="caps">PDF</span> with a simple string:</p>
<pre class="code literal-block">
from reportlab.pdfgen import canvas
import reportlab.rl_config
if __name__ == '__main__':
reportlab.rl_config.warnOnMissingFontGlyphs = 0
c = canvas.Canvas("./hello1.pdf",)
c.drawString(100, 100, "Hello World")
c.showPage()
c.save()
</pre>
<p>Save the above in a file named testreportlab1.py. If you run python testreportlab1.py (in an environment that has
reportlab of cours) you should see no errors and a pdf named <tt class="docutils literal">hello1.pdf</tt> created. If you open it in your <span class="caps">PDF</span>
reader you’ll see a blank page with "Hello World" written in its lower right corner.</p>
<p>If you try to add a unicode text, for example "Καλημέρα ελλάδα", you should see something like the following:</p>
<img alt="Hello PDF" src="/images/hellopdf2.png" style="width: 280px;" />
<p>It seems that the default font that ReportLab uses does not have a good support for accented greek characters
since they are missing (and probably for various other characters).</p>
<p>To resolve this, we could try changing the font to one that contains the missing symbols. You can find free
fonts on the internet (for example the <cite>DejaVu</cite> font), or even grab one from your system fonts (in windows,
check out <tt class="docutils literal"><span class="pre">c:\windows\fonts\</span></tt>). In any case, just copy the ttf file of your font inside the folder of
your project and crate a file named testreportlab2.py with the following (I am using the DejaVuSans font):</p>
<pre class="code literal-block">
# -*- coding: utf-8 -*-
import reportlab.rl_config
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
if __name__ == '__main__':
c = canvas.Canvas("./hello2.pdf",)
reportlab.rl_config.warnOnMissingFontGlyphs = 0
pdfmetrics.registerFont(TTFont('DejaVuSans', 'DejaVuSans.ttf'))
c.setFont('DejaVuSans', 22)
c.drawString(100, 100, u"Καλημέρα ελλάδα.")
c.showPage()
c.save()
</pre>
<p>The above was just a scratch on the surface of ReportLab, mainly to be confident that
everything <em>will</em> work fine for non-english speaking people! To find out more, you should check the <a class="reference external" href="http://www.reportlab.com/docs/reportlab-userguide.pdf">ReportLab open-source User Guide</a>.</p>
<p>I also have to mention that
<a class="reference external" href="http://reportlab.com/">the company behind ReportLab</a> offers some great commercial solutions based on ReportLab for creating PDFs (similar to <a class="reference external" href="http://community.jaspersoft.com/project/jasperreports-library">JasperReports</a>) - check it out
if you need support or advanced capabilities.</p>
</div>
<div class="section" id="xhtml2pdf">
<h3><a class="toc-backref" href="#id7">xhtml2pdf</a></h3>
<p>The xhtml2pdf is a really great library that allows you to use html files as a template
to a <span class="caps">PDF</span>. Of course, an html cannot always be converted to a <span class="caps">PDF</span> since,
unfortunately, PDFs <em>do</em> have pages.</p>
<p>xhtml2pdf has a nice executable script that can be used to test its capabilities. After
you install it (either globally or to a virtual environment) you should be able to find
out the executable <tt class="docutils literal">$<span class="caps">PYTHON</span>/scripts/xhtml2pdf</tt> (or <tt class="docutils literal">xhtml2pdf.exe</tt> if you are in
Windows) and a corresponding python script @ <tt class="docutils literal"><span class="pre">$<span class="caps">PYTHON</span>/scripts/xhtml2pdf-script.py</span></tt>.</p>
<p>Let’s try to use xhtml2pdf to explore some of its capabilities. Create a file named
testxhtml2pdf.html with the following contents and run <tt class="docutils literal">xhtml2pdf testxhtml2pdf.html</tt>:</p>
<pre class="code literal-block">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>Testing xhtml2pdf </h1>
<ul>
<li><b>Hello, world!</b></li>
<li><i>Hello, italics</i></li>
<li>Καλημέρα Ελλάδα!</li>
</ul>
<hr />
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus nulla erat, porttitor ut venenatis eget,
tempor et purus. Nullam nec erat vel enim euismod auctor et at nisl. Integer posuere bibendum condimentum. Ut
euismod velit ut porttitor condimentum. In ullamcorper nulla at lectus fermentum aliquam. Nunc elementum commodo
dui, id pulvinar ex viverra id. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos
himenaeos.</p>
<p>Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed aliquam vitae lectus sit amet accumsan. Morbi
nibh urna, condimentum nec volutpat at, lobortis sit amet odio. Etiam quis neque interdum sapien cursus ornare. Cras
commodo lacinia sapien nec porta. Suspendisse potenti. Nulla hendrerit dolor et rutrum consectetur.</p>
<hr />
<img width="26" height="20" src="data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/
rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0C
cguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7" >
<hr />
<table>
<tr>
<th>header0</th><th>header1</th><th>header2</th><th>header3</th><th>header4</th><th>header5</th>
</tr>
<tr>
<td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td>
</tr>
<tr>
<td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td>
</tr>
<tr>
<td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td>
</tr>
<tr>
<td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td><td>Hello World!!!</td>
</tr>
</table>
</body>
</html>
</pre>
<p>Please notice the <tt class="docutils literal"><meta <span class="pre">http-equiv="Content-Type"</span> <span class="pre">content="text/html;</span> <span class="pre">charset=utf-8"</span> /></tt> in the above <span class="caps">HTML</span> — also it is saved as
Unicode (Encoding - Covert to <span class="caps">UTF</span>-8 in Notepad++). The result (<tt class="docutils literal">testxhtml2pdf.pdf</tt>) should have:</p>
<ul class="simple">
<li>A nice header (h1)</li>
<li>Paragraphs</li>
<li>Horizontal lines</li>
<li>No support for greek characters (same problem as with reportlab)</li>
<li>Images (I am inlining it as a base 64 image)</li>
<li>A list</li>
<li>A table</li>
</ul>
<p>Before moving on, I’d like to fix the problem with the greek characters. You should
set the font to one supporting greek characters, just like you did with ReportLab before.
This can be done with the help of the <tt class="docutils literal"><span class="pre">@font-face</span></tt> <a class="reference external" href="https://github.com/xhtml2pdf/xhtml2pdf/blob/master/doc/usage.rst#fonts">css directive</a>. So, let’s create
a file named <tt class="docutils literal">testxhtml2pdf2.html</tt> with the following contents:</p>
<pre class="code literal-block">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
@font-face {
font-family: DejaVuSans;
src: url("c:/progr/py/django-pdf-guide/django_pdf_guide/DejaVuSans.ttf");
}
body {
font-family: DejaVuSans;
}
</style>
</head>
<body>
<h1>Δοκιμή του xhtml2pdf </h1>
<ul>
<li>Καλημέρα Ελλάδα!</li>
</ul>
</body>
</html>
</pre>
<p>Before running <tt class="docutils literal">xhtml2pdf testxhtml2pdf2.html</tt>, please make
sure to change the url of the font file above to the absolute path of that font in your
local system . As a result, after running xhhtml2pdf
you
should see the unicode characters without problems.</p>
<p>I have to mention here that I wasn’t able to use the font from a relative path, that’s
why I used the absolute one. In case something is not right, try
running it with the <tt class="docutils literal"><span class="pre">-d</span></tt> option to output debugging information (something like
<tt class="docutils literal">xhtml2pdf <span class="pre">-d</span> testxhtml2pdf2.html</tt>). You must see a line like this one:</p>
<pre class="code literal-block">
DEBUG [xhtml2pdf] C:\progr\py\django-pdf-guide\venv\lib\site-packages\xhtml2pdf\context.py line 857: Load font 'c:\\progr\\py\\django-pdf-guide\\django_pdf_guide\\DejaVuSans.ttf'
</pre>
<p>to make sure that the font is actually loaded!</p>
</div>
<div class="section" id="pypdf2">
<h3><a class="toc-backref" href="#id8">PyPDF2</a></h3>
<p>The PyPDF2 library can be used to extract pages from a <span class="caps">PDF</span> to a new one
or combine pages from different PDFs to a a new one. A common requirement is
to have the first and page of a report as static PDFs, create the contents
of this report through your app as a <span class="caps">PDF</span> and combine all three PDFs (front page,
content and back page) to the resulting <span class="caps">PDF</span>.</p>
<p>Let’s see a quick example of combining two PDFs:</p>
<pre class="code literal-block">
import sys
from PyPDF2 import PdfFileMerger
if __name__ == '__main__':
pdfs = sys.argv[1:]
if not pdfs or len(pdfs) < 2:
exit("Please enter at least two pdfs for merging!")
merger = PdfFileMerger()
for pdf in pdfs:
merger.append(fileobj=open(pdf, "rb"))
output = open("output.pdf", "wb")
merger.write(output)
</pre>
<p>The above will try to open all input parameters (as files) and append them to a the output.pdf.</p>
</div>
</div>
<div class="section" id="django-integration">
<h2><a class="toc-backref" href="#id9">Django integration</a></h2>
<p>To integrate the <span class="caps">PDF</span> creation process with django we’ll use a simple app with only one model about books. We are
going to use the django-xhtml2pdf library — I recommend installing the latest version (from github
using something like <tt class="docutils literal">pip install <span class="pre">-e</span> <span class="pre">git+https://github.com/chrisglass/django-xhtml2pdf.git#egg=django-xhtml2pdf</span></tt>
) since the pip package has not been updated in a long time!</p>
<div class="section" id="using-a-plain-old-view">
<h3><a class="toc-backref" href="#id10">Using a plain old view</a></h3>
<p>The simplest case is to just create plain old view to display the <span class="caps">PDF</span>. We’ll use django-xhtml2pdf along with the
followig django template:</p>
<pre class="code literal-block">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>Books</h1>
<table>
<tr>
<th>ID</th><th>Title</th>
</tr>
{% for book in books %}
<tr>
<td>{{ book.id }}</td><td>{{ book.title }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
</pre>
<p>Name it as <tt class="docutils literal">books_plain_old_view.html</tt> and put it on <tt class="docutils literal">books/templates</tt> directory. The view that
returns the above template as <span class="caps">PDF</span> is the following:</p>
<pre class="code literal-block">
from django.http import HttpResponse
from django_xhtml2pdf.utils import generate_pdf
def books_plain_old_view(request):
resp = HttpResponse(content_type='application/pdf')
context = {
'books': Book.objects.all()
}
result = generate_pdf('books_plain_old_view.html', file_object=resp, context=context)
return result
</pre>
<p>We just use the <tt class="docutils literal">generate_pdf</tt> method of django-xhtml2pdf to help us generate the <span class="caps">PDF</span>, passing it
our response object and a context dictionary (containing all books).</p>
<p>Instead of the simple <span class="caps">HTTP</span> response above, we could add a ‘Content Disposition’ <span class="caps">HTTP</span> header to
our response
(or use the django-xhtml2pdf method <tt class="docutils literal">render_to_pdf_response</tt> instead of <tt class="docutils literal">generate_pdf</tt>)
to suggest a default filename for the file to be saved by adding the line</p>
<pre class="code literal-block">
resp['Content-Disposition'] = 'attachment; filename="output.pdf"'
</pre>
<p>after the definition of <tt class="docutils literal">resp</tt>.</p>
<p>This will have the extra effect, at least in Chrome and Firefox to show the "Save File" dialog
when clicking on the link instead of retrieving the <span class="caps">PDF</span> and displaying it inside* the browser window.</p>
</div>
<div class="section" id="using-a-cbv">
<h3><a class="toc-backref" href="#id11">Using a <span class="caps">CBV</span></a></h3>
<p>I don’t really recommend using plain old Django views - instead I propose to always use Class Based Views
for their DRYness. The best approach is to create a mixin that would allow any kind of <span class="caps">CBV</span> (at least any
kind of <span class="caps">CBV</span> that uses a template) to be rendered in <span class="caps">PDF</span>. Here’s how we could implement a <tt class="docutils literal">PdfResponseMixin</tt>:</p>
<pre class="code literal-block">
class PdfResponseMixin(object, ):
def render_to_response(self, context, **response_kwargs):
context=self.get_context_data()
template=self.get_template_names()[0]
resp = HttpResponse(content_type='application/pdf')
result = generate_pdf(template, file_object=resp, context=context)
return result
</pre>
<p>Now, we could use this mixin to create <span class="caps">PDF</span> outputting views from any other view! For example, here’s how
we could create a book list in pdf:</p>
<pre class="code literal-block">
class BookPdfListView(PdfResponseMixin, ListView):
context_object_name = 'books'
model = Book
</pre>
<p>To display it, you could use the same template as <tt class="docutils literal">books_plain_old_view.html</tt> (so either add a <tt class="docutils literal"><span class="pre">template_name='books_plain_old_view.html'</span></tt>
property to the class or copy <tt class="docutils literal">books_plain_old_view.html</tt> to <tt class="docutils literal">books/book_list.html</tt>).</p>
<p>Also, as another example, here’s a <tt class="docutils literal">BookPdfDetailView</tt> that outputs <span class="caps">PDF</span>:</p>
<pre class="code literal-block">
class BookPdfDetailView(PdfResponseMixin, DetailView):
context_object_name = 'book'
model = Book
</pre>
<p>and a corresponding template (name it <tt class="docutils literal">books/book_detail.html</tt>):</p>
<pre class="code literal-block">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<h1>Book Detail</h1>
<b>ID</b>: {{ book.id }} <br />
<b>Title</b>: {{ book.title }} <br />
</body>
</html>
</pre>
<p>To add the content-disposition header and a name for your <span class="caps">PDF</span>, you can use the following mixin:</p>
<pre class="code literal-block">
class PdfResponseMixin(object, ):
pdf_name = "output"
def get_pdf_name(self):
return self.pdf_name
def render_to_response(self, context, **response_kwargs):
context=self.get_context_data()
template=self.get_template_names()[0]
resp = HttpResponse(content_type='application/pdf')
resp['Content-Disposition'] = 'attachment; filename="{0}.pdf"'.format(self.get_pdf_name())
result = generate_pdf(template, file_object=resp, context=context)
return result
</pre>
<p>You see that, in order to havea configurable output name for our <span class="caps">PDF</span> and be consistent with the other
django CBVs, a <tt class="docutils literal">pdf_name</tt> class attribute and a <tt class="docutils literal">get_pdf_name</tt> instance method are added. When
using the above mixin in your classes you can either provide a value for <tt class="docutils literal">pdf_name</tt> (to use the same for all
your instances), or override <tt class="docutils literal">get_pdf_name</tt> to have a dynamic value!</p>
</div>
<div class="section" id="how-does-django-xhtml2pdf-loads-resources">
<h3><a class="toc-backref" href="#id12">How does django-xhtml2pdf loads resources</a></h3>
<p>Before doing more advanced things, we need to understand how <tt class="docutils literal"><span class="pre">django-xhtml2pdf</span></tt> works and specifically
how we can refer to things like css, images, fonts etc from our <span class="caps">PDF</span> templates.
If you check the <a class="reference external" href="https://github.com/chrisglass/django-xhtml2pdf/blob/master/django_xhtml2pdf/utils.py">utils.py of django-xhtml2pdf</a> you’ll see that it uses a function named <tt class="docutils literal">fetch_resources</tt>
for loading these resources. This function checks to see if the resource starts with <tt class="docutils literal">/MEDIA_URL</tt> or
<tt class="docutils literal">/STATIC_URL</tt> and converts it to a local (filesystem) path. For example, if you refer to a font like
<tt class="docutils literal">/static/font1.ttf</tt> in your <span class="caps">PDF</span> template, <tt class="docutils literal">xhtml2pdf</tt> will try to load the file <tt class="docutils literal">STATIC_ROOT + /font1.ttf</tt>
(and if it does not find the file you want to refer to there it will check all <tt class="docutils literal">STATICFILES_DIRS</tt> entries).</p>
<p>Thus, you can just put your resources into your <tt class="docutils literal">STATIC_ROOT</tt> directory and use the <tt class="docutils literal">{% static %}</tt>
template tag to create <span class="caps">URL</span> paths for them — django-xhtml2pdf will convert these to local paths and
everything will work fine.</p>
<p><strong>Please notice that you *need* to have configured “STATIC_ROOT“ for this to work</strong> — if <tt class="docutils literal">STATIC_ROOT</tt> is
empty (and, for example you use <tt class="docutils literal">static</tt> directories in your apps) then the described substitution
mechanism will <em>not</em> work. Also, notice that the <tt class="docutils literal">/static</tt> directory inside your apps <em>cannot be used</em>
for fetch resources like this
(due to how <tt class="docutils literal">fetch_resources</tt> is implemented it only checks if the static resource is contained inside
the <tt class="docutils literal">STATIC_ROOT</tt> or in one of the the <tt class="docutils literal">STATICFILES_DIRS</tt>) so be careful to either put the static files
you need to load from PDFs (fonts, styles and possibly images) to either the <tt class="docutils literal">STATIC_ROOT</tt> or the
one of the <tt class="docutils literal">STATICFILES_DIRS</tt>.</p>
</div>
<div class="section" id="using-a-common-style-for-your-pdfs">
<h3><a class="toc-backref" href="#id13">Using a common style for your PDFs</a></h3>
<p>If you need to create a lot of similar PDFs then you’ll probably want to
use a bunch of common styles for them (same fonts, headers etc). This could be done using
the <tt class="docutils literal">{% static %}</tt> trick we saw on the previous section. However, if we include the
styling css as a static file then we won’t be able to use the static-file-uri-to-local-path
mechanism described above (since the <tt class="docutils literal">{% static %}</tt> template tag won’t work in static files).</p>
<p>Thankfully, not everything is lost — Django comes to the rescue!!! We can create a single <span class="caps">CSS</span> file
that would be used by all our <span class="caps">PDF</span> templates and <em>include</em> it in the templates using the <tt class="docutils literal">{% include %}</tt> Django
template tag! Django will think that this will be a normal template and paste its contents where we wanted and
also execute the templates tags!</p>
<p>We’ll see an example of all this in the next section.</p>
</div>
<div class="section" id="changing-the-font-to-a-unicode-enabled-one">
<h3><a class="toc-backref" href="#id14">Changing the font (to a Unicode enabled one)</a></h3>
<p>The time has finally arrived to change the font! It’s easy if you know exactly what to do. First of all
configure your <tt class="docutils literal">STATIC_ROOT</tt> and <tt class="docutils literal">STATIC_URL</tt> setting, for example <tt class="docutils literal">STATIC_ROOT = <span class="pre">os.path.join(BASE_DIR,'static')</span></tt>
and <tt class="docutils literal">STATIC_URL = '/static/'</tt>.</p>
<p>Then, add a template-css file for your fonts in one of your templates directories. I am naming the
file <tt class="docutils literal">pdfstylefonts.css</tt> and I’ve put it to <tt class="docutils literal">books/templates</tt>:</p>
<pre class="code literal-block">
{% load static %}
@font-face {
font-family: "Calibri";
src: url({% static "fonts/calibri.ttf" %});
}
@font-face {
font-family: "Calibri";
src: url({% static "fonts/calibrib.ttf" %});
font-weight: bold;
}
@font-face {
font-family: "Calibri";
src: url({% static "fonts/calibrii.ttf" %});
font-style: italic, oblique;
}
@font-face {
font-family: "Calibri";
src: url({% static "fonts/calibriz.ttf" %});
font-weight: bold;
font-style: italic, oblique;
}
</pre>
<p>I am using Calibri family of fonts (copied from <tt class="docutils literal"><span class="pre">c:\windows\fonts</span></tt>) for this — I’ve also configured
all styles (bold, italic, bold-italic) of this font family to use the correct ttf files. All the
ttf files have been copied to the directory <tt class="docutils literal">static/fonts/</tt>.</p>
<p>Now, add another css file that will be your global <span class="caps">PDF</span> styles. This should be put to the <tt class="docutils literal">static</tt> directory
and could be named <tt class="docutils literal">pdfstyle.css</tt>:</p>
<pre class="code literal-block">
h1 {
color: blue;
}
*, html {
font-family: "Calibri";
font-size:11pt;
color: red;
}
</pre>
<p>Next, here’s a template that lists all books (and contain some greek characters — the title of the books also contain
greek characters) — I’ve named it <tt class="docutils literal">book_list_ex.html</tt>:</p>
<pre class="code literal-block">
{% load static %}
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
{% include "pdfstylefonts.css" %}
</style>
<link rel='stylesheet' href='{% static "pdfstyle.css" %}'/>
</head>
<body>
<h1>Λίστα βιβλίων</h1>
<img src='{% static "pony.png" %}' />
<table>
<tr>
<th>ID</th><th>Title</th><th>Cover</th>
</tr>
{% for book in books %}
<tr>
<td>{{ book.id }}</td><td>{{ book.title }}</td><td><img src='{{ book.cover.url }}' /></td>
</tr>
{% endfor %}
</table>
</body>
</html>
</pre>
<p>You’ll see that the <tt class="docutils literal">pdfstylefonts.css</tt> is included as a Django template (so that <tt class="docutils literal">{% static %}</tt> will
work in that file) while <tt class="docutils literal">pdfstyle.css</tt> is included using <tt class="docutils literal">{% static %}</tt>.
Als, notice that I’ve also added a static image (using the <tt class="docutils literal">{% static %}</tt> tag) and a dynamic (media)
file to show off how great the url-to-local-path mechanism works. Please notice that for the
media files to work fine in your development environment you need to configure the
<tt class="docutils literal">MEDIA_URL</tt> and <tt class="docutils literal">MEDIA_ROOT</tt> settigns (similar to <tt class="docutils literal">STATIC_URL</tt> and <tt class="docutils literal">STATIC_ROOT</tt>) and follow the
<a class="reference external" href="https://docs.djangoproject.com/en/1.8/howto/static-files/#serving-files-uploaded-by-a-user-during-development">serve files uploaded by a user during development</a> tutorial on Django docs.</p>
<p>Finally, if you configure a PdfResponseMixin ListView like this:</p>
<pre class="code literal-block">
class BookExPdfListView(PdfResponseMixin, ListView):
context_object_name = 'books'
model = Book
template_name = 'books/book_list_ex.html'
</pre>
<p>you should see be able to see the correct (calibri) font (defined in <tt class="docutils literal">pdfstylefonts.css</tt>), with unicode characters without problems
including both the static and user uploaded images and with the styles defined in the pdf stylesheet (<tt class="docutils literal">pdfstyle.css</tt>).</p>
</div>
<div class="section" id="configure-django-for-debugging-pdf-creation">
<h3><a class="toc-backref" href="#id15">Configure Django for debugging <span class="caps">PDF</span> creation</a></h3>
<p>If you experience any problems, you can configure xhtml2pdf to output <span class="caps">DEBUG</span> information. To do this,
you may change your django logging configuration like this:</p>
<pre class="code literal-block">
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'xhtml2pdf': {
'handlers': ['console'],
'level': 'DEBUG',
}
}
}
</pre>
<p>This configuration will keep existing loggers (<tt class="docutils literal">'disable_existing_loggers': False,</tt>) and will configure
<tt class="docutils literal">xhtml2pdf</tt> to log its output to the console, helping us find out why some things won’t be working.</p>
</div>
<div class="section" id="concatenating-pdfs-in-django">
<h3><a class="toc-backref" href="#id16">Concatenating PDFs in Django</a></h3>
<p>The final section of the <span class="caps">PDF</span>-Django-integration is to explain how we can concatenate PDFs in django using PyPDF2. There may be
some other requirements like extracting pages from PDFs however the most common one as explained before is to just append
the pages of one <span class="caps">PDF</span> after the other — after all using PyPDF2 is really easy after you get the hang of it.</p>
<p>To be more <span class="caps">DRY</span>, I will create a <tt class="docutils literal">CoverPdfResponseMixin</tt> that will output a <span class="caps">PDF</span> <em>with</em> a cover. To be <em>even more</em> <span class="caps">DRY</span>,
I will refactor <tt class="docutils literal">PdfResponseMixin</tt> to put some common code in an extra method so that <tt class="docutils literal">CoverPdfResponseMixin</tt> could inherit from it:</p>
<pre class="code literal-block">
class PdfResponseMixin(object, ):
def write_pdf(self, file_object, ):
context = self.get_context_data()
template = self.get_template_names()[0]
generate_pdf(template, file_object=file_object, context=context)
def render_to_response(self, context, **response_kwargs):
resp = HttpResponse(content_type='application/pdf')
self.write_pdf(resp)
return resp
class CoverPdfResponseMixin(PdfResponseMixin, ):
cover_pdf = None
def render_to_response(self, context, **response_kwargs):
merger = PdfFileMerger()
merger.append(open(self.cover_pdf, "rb"))
pdf_fo = StringIO.StringIO()
self.write_pdf(pdf_fo)
merger.append(pdf_fo)
resp = HttpResponse(content_type='application/pdf')
merger.write(resp)
return resp
</pre>
<p>So, <tt class="docutils literal">PdfResponseMixin</tt> now has a <tt class="docutils literal">write_pdf</tt> method that gets a file-like object and outputs the <span class="caps">PDF</span> there.
The new mixin, <tt class="docutils literal">CoverPdfResponseMixin</tt> has a <tt class="docutils literal">cover_pdf</tt> attribute that should be configured with the filesystem
path of the cover file. The <tt class="docutils literal">render_to_response</tt> method now will create a <tt class="docutils literal">PdfFileMerger</tt> (which is empty
initially) to which it appends the contents <tt class="docutils literal">cover_pdf</tt>. After that, it creates a file-stream (using StreamIO)
and uses <tt class="docutils literal">write_pdf</tt> to create the <span class="caps">PDF</span> there and appends that file-stream to the merger. Finally, it writes
the merger contents to the <tt class="docutils literal">HttpResponse</tt>.</p>
<p>One thing that I’ve seen is that if you want to concatenate many PDFs with many pages sometimes you’ll get
a strange an error when using <tt class="docutils literal">PdfFileMerger</tt>. I was able to overcome this by reading and appending the pages of each
<span class="caps">PDF</span> to-be-appended one by one using the <tt class="docutils literal">PdfFileReader</tt> and <tt class="docutils literal">PdfFileWriter</tt> objects. Here’s a small snippet of how
this could be done:</p>
<pre class="code literal-block">
pdfs = [] # List of pdfs to be concatenated
writer = PdfFileWriter()
for pdf in pdfs:
reader = PdfFileReader(open(pdf, "rb"))
for i in range(reader.getNumPages()):
writer.addPage(reader.getPage(i))
resp = HttpResponse(content_type='application/pdf')
writer.write(resp)
return resp
</pre>
</div>
</div>
<div class="section" id="more-advanced-xhtml2pdf-features">
<h2><a class="toc-backref" href="#id17">More advanced xhtml2pdf features</a></h2>
<p>In this section I will present some information on how to use various xhtml2pdf features
to create the most common required printed document features, for example adding page
numbers, adding headers and footers etc.</p>
<div class="section" id="laying-out">
<h3><a class="toc-backref" href="#id18">Laying out</a></h3>
<p>To find out how you can create your pages you should read the
<a class="reference external" href="https://github.com/xhtml2pdf/xhtml2pdf/blob/master/doc/usage.rst#defining-page-layouts">defining page layouts</a> section of the xhtml2pdf manual. There
you’ll see that the basic components of laying out in xhtml2pdf is
@page to create pages and @frames to create rectangular components
inside these pages. So each page will have a number of frames
inside it. These frames are seperated to static and dynamic. Staticsome
should be used for things like headers
and footers (so they’ll be the same across all pages) and dynamic will
contain the main content of the report.</p>
<p>For pages you can use any page size you want, not just the specified ones. For example, in one of my
projects I wanted to create a <span class="caps">PDF</span> print on a normal plastic card, so I’d used the
following <tt class="docutils literal">size: 8.56cm 5.398cm;</tt>. Also, page templates can be named
and you can use different ones in the same <span class="caps">PDF</span> (so you could create
a cover page with a different page template, use it first and then continue
with the normal pages). To name a tempalte you just use @page template_name {}
and to change the template use the combination of the following two xhtml2pdf tags:</p>
<pre class="code literal-block">
<pdf:nexttemplate name="back_page" />
<pdf:nextpage />
</pre>
<p>Now, one thing I’ve noticed is that you are not able to use a named template for the first
page of your <span class="caps">PDF</span>. So, what I’ve done is that I create an anonymous (default) page for the
first page of the report. If I want to <em>reuse</em> it in another page, I copy it and name it
accordingly. I will give an example shortly.</p>
<p>Now, for frames, I recommend using the <tt class="docutils literal"><span class="pre">-pdf-frame-border:</span> 1;</tt> command for debugging where
they are actually printed. Also, I recommend using a normal ruler and measuring completely
where you want them to be. For example, for the following frame:</p>
<pre class="code literal-block">
@frame photo_frame {
-pdf-frame-border: 1;
top: 2.4cm;
left: 6.2cm;
width: 1.9cm;
height: 2.2cm;
}
</pre>
<p>I’d used a ruler to find out that I want it to start 2.4 cm from the top of my page (actually a credit card)
and 6.2 cm from the left and have a width and height of 1.9 and 2.2 cm.</p>
<p>I recommend naming all frames to be able to distinguish them however I don’t think that their name plays