Gravity PDF - Version 2.1.0

Version Description

  • Feature Product table can now be accessed directly through custom templates by running GFPDFEntryDetail::product_table($form, $lead);. See documentation for more details.
  • Feature Update screen will ask you if you want to deploy new template files, instead of overriding your modified versions.
  • Feature Product subtotal, shipping and total have been added to $form_data['field'] array to make it easier to work with product details in the custom template.
  • Feature Added two new default template files. One displays field and name in two rows (like you see when viewing an entry in the admin area) and the other removes all styling. See documentation on use.
  • Security Tightened PDF template security so that custom templates couldnt be automatically generated by just anyone. Now only logged in users with the correct privileges and the user who submitted the form (matched against IP) can auto generate a PDF. See documentation on usage.
  • Depreciated Removed form data that was added directly to the $form_data array instead of $form_data['field'] array. Users upgrading will need to update their custom templates if not using field data from the $form_data[field] array. If using $form_data['field'] in your custom template this wont affect you.
  • Bug Fixed problem with default template not showing and displaying a timeout error. Removed table tags and replaced with divs that are styled appropriately.
  • Bug The new plugin theme folder will successfully create when upgrading. You wont have to deactivate and reactivate to get it working.
  • Bug some installs had plugins that included the function mb_string which is also included in DOMPDF. DOMPDF will now check if the function exists before creating it.
  • Bug Remove empty signature field from the default template.
  • Bug fixed problem with redirecting to login screen even when logged in while accessing template file through the browser window directly.
  • Bug fixed error where sample template would reimport itself automatically even after deleting it. Will now only reimport if any important changes to template need to be viewed straight after an update.
  • Bug Moved render_to_pdf.php constants to pdf.php so we can use the constants in the core files. Was previously generating an error.
  • Housekeeping Cleaned up core template files, moved functions into classes and added more in-file documentation.
  • Housekeeping moved install/upgrade code from pdf.php to installation-update-manager.php
  • Housekeeping changed pdf-entry-detail.php class name from GFEntryDetail to GFPDFEntryDetail to remove compatibility problems with Gravity Forms.
  • Housekeeping created pdf-settings.php file to house the settings page code.
Download this release

Release Info

Developer Blue Liquid Designs
Plugin Icon 128x128 Gravity PDF
Version 2.1.0
Comparing to
See all releases

Code changes from version 3.6.0 to 2.1.0

Files changed (92) hide show
  1. LICENSE +0 -339
  2. README.txt +44 -384
  3. depreciated.php +0 -24
  4. dompdf/CONTRIBUTING.md +73 -0
  5. dompdf/LICENSE.LGPL +456 -0
  6. dompdf/README.md +33 -0
  7. dompdf/changelog.txt +213 -0
  8. dompdf/composer.json +23 -0
  9. dompdf/docblox.dist.xml +20 -0
  10. dompdf/dompdf.php +285 -0
  11. dompdf/dompdf_config.custom.inc.php +29 -0
  12. dompdf/dompdf_config.inc.php +401 -0
  13. dompdf/include/_notes/dwsync.xml +73 -0
  14. dompdf/include/absolute_positioner.cls.php +124 -0
  15. dompdf/include/abstract_renderer.cls.php +753 -0
  16. dompdf/include/attribute_translator.cls.php +592 -0
  17. dompdf/include/autoload.inc.php +86 -0
  18. dompdf/include/block_frame_decorator.cls.php +234 -0
  19. dompdf/include/block_frame_reflower.cls.php +805 -0
  20. dompdf/include/block_positioner.cls.php +57 -0
  21. dompdf/include/block_renderer.cls.php +218 -0
  22. dompdf/include/cached_pdf_decorator.cls.php +164 -0
  23. dompdf/include/canvas.cls.php +385 -0
  24. dompdf/include/canvas_factory.cls.php +63 -0
  25. dompdf/include/cellmap.cls.php +790 -0
  26. dompdf/include/cpdf_adapter.cls.php +869 -0
  27. dompdf/include/css_color.cls.php +274 -0
  28. dompdf/include/dompdf.cls.php +1064 -0
  29. dompdf/include/dompdf_exception.cls.php +26 -0
  30. dompdf/include/dompdf_image_exception.cls.php +26 -0
  31. dompdf/include/file.skel +8 -0
  32. dompdf/include/fixed_positioner.cls.php +88 -0
  33. dompdf/include/font_metrics.cls.php +351 -0
  34. dompdf/include/frame.cls.php +1184 -0
  35. dompdf/include/frame_decorator.cls.php +696 -0
  36. dompdf/include/frame_factory.cls.php +246 -0
  37. dompdf/include/frame_reflower.cls.php +449 -0
  38. dompdf/include/frame_tree.cls.php +239 -0
  39. dompdf/include/functions.inc.php +1050 -0
  40. dompdf/include/gd_adapter.cls.php +840 -0
  41. dompdf/include/image_cache.cls.php +198 -0
  42. dompdf/include/image_frame_decorator.cls.php +80 -0
  43. dompdf/include/image_frame_reflower.cls.php +186 -0
  44. dompdf/include/image_renderer.cls.php +119 -0
  45. dompdf/include/inline_frame_decorator.cls.php +74 -0
  46. dompdf/include/inline_frame_reflower.cls.php +66 -0
  47. dompdf/include/inline_positioner.cls.php +70 -0
  48. dompdf/include/inline_renderer.cls.php +190 -0
  49. dompdf/include/javascript_embedder.cls.php +37 -0
  50. dompdf/include/line_box.cls.php +252 -0
  51. dompdf/include/list_bullet_frame_decorator.cls.php +65 -0
  52. dompdf/include/list_bullet_frame_reflower.cls.php +33 -0
  53. dompdf/include/list_bullet_image_frame_decorator.cls.php +143 -0
  54. dompdf/include/list_bullet_positioner.cls.php +73 -0
  55. dompdf/include/list_bullet_renderer.cls.php +225 -0
  56. dompdf/include/null_frame_decorator.cls.php +26 -0
  57. dompdf/include/null_frame_reflower.cls.php +21 -0
  58. dompdf/include/null_positioner.cls.php +23 -0
  59. dompdf/include/page_cache.cls.php +126 -0
  60. dompdf/include/page_frame_decorator.cls.php +603 -0
  61. dompdf/include/page_frame_reflower.cls.php +186 -0
  62. dompdf/include/pdflib_adapter.cls.php +1085 -0
  63. dompdf/include/php_evaluator.cls.php +48 -0
  64. dompdf/include/positioner.cls.php +51 -0
  65. dompdf/include/renderer.cls.php +290 -0
  66. dompdf/include/style.cls.php +2435 -0
  67. dompdf/include/stylesheet.cls.php +1434 -0
  68. dompdf/include/table_cell_frame_decorator.cls.php +102 -0
  69. dompdf/include/table_cell_frame_reflower.cls.php +119 -0
  70. dompdf/include/table_cell_positioner.cls.php +28 -0
  71. dompdf/include/table_cell_renderer.cls.php +155 -0
  72. dompdf/include/table_frame_decorator.cls.php +334 -0
  73. dompdf/include/table_frame_reflower.cls.php +578 -0
  74. dompdf/include/table_row_frame_decorator.cls.php +48 -0
  75. dompdf/include/table_row_frame_reflower.cls.php +61 -0
  76. dompdf/include/table_row_group_frame_decorator.cls.php +66 -0
  77. dompdf/include/table_row_group_frame_reflower.cls.php +59 -0
  78. dompdf/include/table_row_group_renderer.cls.php +40 -0
  79. dompdf/include/table_row_positioner.cls.php +35 -0
  80. dompdf/include/tcpdf_adapter.cls.php +628 -0
  81. dompdf/include/text_frame_decorator.cls.php +173 -0
  82. dompdf/include/text_frame_reflower.cls.php +441 -0
  83. dompdf/include/text_renderer.cls.php +152 -0
  84. dompdf/index.php +1 -0
  85. dompdf/lib/_notes/dwsync.xml +4 -0
  86. dompdf/lib/class.pdf.php +4590 -0
  87. dompdf/lib/fonts/Courier-Bold.afm +344 -0
  88. dompdf/lib/fonts/Courier-BoldOblique.afm +344 -0
  89. dompdf/lib/fonts/Courier-Oblique.afm +344 -0
  90. dompdf/lib/fonts/Courier.afm +344 -0
  91. {mPDF/ttfonts → dompdf/lib/fonts}/DejaVuSans-Bold.ttf +0 -0
  92. dompdf/lib/fonts/DejaVuSans-Bold.ufm +522 -0
LICENSE DELETED
@@ -1,339 +0,0 @@
1
- GNU GENERAL PUBLIC LICENSE
2
- Version 2, June 1991
3
-
4
- Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
5
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
- Everyone is permitted to copy and distribute verbatim copies
7
- of this license document, but changing it is not allowed.
8
-
9
- Preamble
10
-
11
- The licenses for most software are designed to take away your
12
- freedom to share and change it. By contrast, the GNU General Public
13
- License is intended to guarantee your freedom to share and change free
14
- software--to make sure the software is free for all its users. This
15
- General Public License applies to most of the Free Software
16
- Foundation's software and to any other program whose authors commit to
17
- using it. (Some other Free Software Foundation software is covered by
18
- the GNU Lesser General Public License instead.) You can apply it to
19
- your programs, too.
20
-
21
- When we speak of free software, we are referring to freedom, not
22
- price. Our General Public Licenses are designed to make sure that you
23
- have the freedom to distribute copies of free software (and charge for
24
- this service if you wish), that you receive source code or can get it
25
- if you want it, that you can change the software or use pieces of it
26
- in new free programs; and that you know you can do these things.
27
-
28
- To protect your rights, we need to make restrictions that forbid
29
- anyone to deny you these rights or to ask you to surrender the rights.
30
- These restrictions translate to certain responsibilities for you if you
31
- distribute copies of the software, or if you modify it.
32
-
33
- For example, if you distribute copies of such a program, whether
34
- gratis or for a fee, you must give the recipients all the rights that
35
- you have. You must make sure that they, too, receive or can get the
36
- source code. And you must show them these terms so they know their
37
- rights.
38
-
39
- We protect your rights with two steps: (1) copyright the software, and
40
- (2) offer you this license which gives you legal permission to copy,
41
- distribute and/or modify the software.
42
-
43
- Also, for each author's protection and ours, we want to make certain
44
- that everyone understands that there is no warranty for this free
45
- software. If the software is modified by someone else and passed on, we
46
- want its recipients to know that what they have is not the original, so
47
- that any problems introduced by others will not reflect on the original
48
- authors' reputations.
49
-
50
- Finally, any free program is threatened constantly by software
51
- patents. We wish to avoid the danger that redistributors of a free
52
- program will individually obtain patent licenses, in effect making the
53
- program proprietary. To prevent this, we have made it clear that any
54
- patent must be licensed for everyone's free use or not licensed at all.
55
-
56
- The precise terms and conditions for copying, distribution and
57
- modification follow.
58
-
59
- GNU GENERAL PUBLIC LICENSE
60
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
-
62
- 0. This License applies to any program or other work which contains
63
- a notice placed by the copyright holder saying it may be distributed
64
- under the terms of this General Public License. The "Program", below,
65
- refers to any such program or work, and a "work based on the Program"
66
- means either the Program or any derivative work under copyright law:
67
- that is to say, a work containing the Program or a portion of it,
68
- either verbatim or with modifications and/or translated into another
69
- language. (Hereinafter, translation is included without limitation in
70
- the term "modification".) Each licensee is addressed as "you".
71
-
72
- Activities other than copying, distribution and modification are not
73
- covered by this License; they are outside its scope. The act of
74
- running the Program is not restricted, and the output from the Program
75
- is covered only if its contents constitute a work based on the
76
- Program (independent of having been made by running the Program).
77
- Whether that is true depends on what the Program does.
78
-
79
- 1. You may copy and distribute verbatim copies of the Program's
80
- source code as you receive it, in any medium, provided that you
81
- conspicuously and appropriately publish on each copy an appropriate
82
- copyright notice and disclaimer of warranty; keep intact all the
83
- notices that refer to this License and to the absence of any warranty;
84
- and give any other recipients of the Program a copy of this License
85
- along with the Program.
86
-
87
- You may charge a fee for the physical act of transferring a copy, and
88
- you may at your option offer warranty protection in exchange for a fee.
89
-
90
- 2. You may modify your copy or copies of the Program or any portion
91
- of it, thus forming a work based on the Program, and copy and
92
- distribute such modifications or work under the terms of Section 1
93
- above, provided that you also meet all of these conditions:
94
-
95
- a) You must cause the modified files to carry prominent notices
96
- stating that you changed the files and the date of any change.
97
-
98
- b) You must cause any work that you distribute or publish, that in
99
- whole or in part contains or is derived from the Program or any
100
- part thereof, to be licensed as a whole at no charge to all third
101
- parties under the terms of this License.
102
-
103
- c) If the modified program normally reads commands interactively
104
- when run, you must cause it, when started running for such
105
- interactive use in the most ordinary way, to print or display an
106
- announcement including an appropriate copyright notice and a
107
- notice that there is no warranty (or else, saying that you provide
108
- a warranty) and that users may redistribute the program under
109
- these conditions, and telling the user how to view a copy of this
110
- License. (Exception: if the Program itself is interactive but
111
- does not normally print such an announcement, your work based on
112
- the Program is not required to print an announcement.)
113
-
114
- These requirements apply to the modified work as a whole. If
115
- identifiable sections of that work are not derived from the Program,
116
- and can be reasonably considered independent and separate works in
117
- themselves, then this License, and its terms, do not apply to those
118
- sections when you distribute them as separate works. But when you
119
- distribute the same sections as part of a whole which is a work based
120
- on the Program, the distribution of the whole must be on the terms of
121
- this License, whose permissions for other licensees extend to the
122
- entire whole, and thus to each and every part regardless of who wrote it.
123
-
124
- Thus, it is not the intent of this section to claim rights or contest
125
- your rights to work written entirely by you; rather, the intent is to
126
- exercise the right to control the distribution of derivative or
127
- collective works based on the Program.
128
-
129
- In addition, mere aggregation of another work not based on the Program
130
- with the Program (or with a work based on the Program) on a volume of
131
- a storage or distribution medium does not bring the other work under
132
- the scope of this License.
133
-
134
- 3. You may copy and distribute the Program (or a work based on it,
135
- under Section 2) in object code or executable form under the terms of
136
- Sections 1 and 2 above provided that you also do one of the following:
137
-
138
- a) Accompany it with the complete corresponding machine-readable
139
- source code, which must be distributed under the terms of Sections
140
- 1 and 2 above on a medium customarily used for software interchange; or,
141
-
142
- b) Accompany it with a written offer, valid for at least three
143
- years, to give any third party, for a charge no more than your
144
- cost of physically performing source distribution, a complete
145
- machine-readable copy of the corresponding source code, to be
146
- distributed under the terms of Sections 1 and 2 above on a medium
147
- customarily used for software interchange; or,
148
-
149
- c) Accompany it with the information you received as to the offer
150
- to distribute corresponding source code. (This alternative is
151
- allowed only for noncommercial distribution and only if you
152
- received the program in object code or executable form with such
153
- an offer, in accord with Subsection b above.)
154
-
155
- The source code for a work means the preferred form of the work for
156
- making modifications to it. For an executable work, complete source
157
- code means all the source code for all modules it contains, plus any
158
- associated interface definition files, plus the scripts used to
159
- control compilation and installation of the executable. However, as a
160
- special exception, the source code distributed need not include
161
- anything that is normally distributed (in either source or binary
162
- form) with the major components (compiler, kernel, and so on) of the
163
- operating system on which the executable runs, unless that component
164
- itself accompanies the executable.
165
-
166
- If distribution of executable or object code is made by offering
167
- access to copy from a designated place, then offering equivalent
168
- access to copy the source code from the same place counts as
169
- distribution of the source code, even though third parties are not
170
- compelled to copy the source along with the object code.
171
-
172
- 4. You may not copy, modify, sublicense, or distribute the Program
173
- except as expressly provided under this License. Any attempt
174
- otherwise to copy, modify, sublicense or distribute the Program is
175
- void, and will automatically terminate your rights under this License.
176
- However, parties who have received copies, or rights, from you under
177
- this License will not have their licenses terminated so long as such
178
- parties remain in full compliance.
179
-
180
- 5. You are not required to accept this License, since you have not
181
- signed it. However, nothing else grants you permission to modify or
182
- distribute the Program or its derivative works. These actions are
183
- prohibited by law if you do not accept this License. Therefore, by
184
- modifying or distributing the Program (or any work based on the
185
- Program), you indicate your acceptance of this License to do so, and
186
- all its terms and conditions for copying, distributing or modifying
187
- the Program or works based on it.
188
-
189
- 6. Each time you redistribute the Program (or any work based on the
190
- Program), the recipient automatically receives a license from the
191
- original licensor to copy, distribute or modify the Program subject to
192
- these terms and conditions. You may not impose any further
193
- restrictions on the recipients' exercise of the rights granted herein.
194
- You are not responsible for enforcing compliance by third parties to
195
- this License.
196
-
197
- 7. If, as a consequence of a court judgment or allegation of patent
198
- infringement or for any other reason (not limited to patent issues),
199
- conditions are imposed on you (whether by court order, agreement or
200
- otherwise) that contradict the conditions of this License, they do not
201
- excuse you from the conditions of this License. If you cannot
202
- distribute so as to satisfy simultaneously your obligations under this
203
- License and any other pertinent obligations, then as a consequence you
204
- may not distribute the Program at all. For example, if a patent
205
- license would not permit royalty-free redistribution of the Program by
206
- all those who receive copies directly or indirectly through you, then
207
- the only way you could satisfy both it and this License would be to
208
- refrain entirely from distribution of the Program.
209
-
210
- If any portion of this section is held invalid or unenforceable under
211
- any particular circumstance, the balance of the section is intended to
212
- apply and the section as a whole is intended to apply in other
213
- circumstances.
214
-
215
- It is not the purpose of this section to induce you to infringe any
216
- patents or other property right claims or to contest validity of any
217
- such claims; this section has the sole purpose of protecting the
218
- integrity of the free software distribution system, which is
219
- implemented by public license practices. Many people have made
220
- generous contributions to the wide range of software distributed
221
- through that system in reliance on consistent application of that
222
- system; it is up to the author/donor to decide if he or she is willing
223
- to distribute software through any other system and a licensee cannot
224
- impose that choice.
225
-
226
- This section is intended to make thoroughly clear what is believed to
227
- be a consequence of the rest of this License.
228
-
229
- 8. If the distribution and/or use of the Program is restricted in
230
- certain countries either by patents or by copyrighted interfaces, the
231
- original copyright holder who places the Program under this License
232
- may add an explicit geographical distribution limitation excluding
233
- those countries, so that distribution is permitted only in or among
234
- countries not thus excluded. In such case, this License incorporates
235
- the limitation as if written in the body of this License.
236
-
237
- 9. The Free Software Foundation may publish revised and/or new versions
238
- of the General Public License from time to time. Such new versions will
239
- be similar in spirit to the present version, but may differ in detail to
240
- address new problems or concerns.
241
-
242
- Each version is given a distinguishing version number. If the Program
243
- specifies a version number of this License which applies to it and "any
244
- later version", you have the option of following the terms and conditions
245
- either of that version or of any later version published by the Free
246
- Software Foundation. If the Program does not specify a version number of
247
- this License, you may choose any version ever published by the Free Software
248
- Foundation.
249
-
250
- 10. If you wish to incorporate parts of the Program into other free
251
- programs whose distribution conditions are different, write to the author
252
- to ask for permission. For software which is copyrighted by the Free
253
- Software Foundation, write to the Free Software Foundation; we sometimes
254
- make exceptions for this. Our decision will be guided by the two goals
255
- of preserving the free status of all derivatives of our free software and
256
- of promoting the sharing and reuse of software generally.
257
-
258
- NO WARRANTY
259
-
260
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
- FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
- OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
- PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
- OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
- TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
- PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
- REPAIR OR CORRECTION.
269
-
270
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
- WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
- REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
- INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
- OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
- TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
- YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
- PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
- POSSIBILITY OF SUCH DAMAGES.
279
-
280
- END OF TERMS AND CONDITIONS
281
-
282
- How to Apply These Terms to Your New Programs
283
-
284
- If you develop a new program, and you want it to be of the greatest
285
- possible use to the public, the best way to achieve this is to make it
286
- free software which everyone can redistribute and change under these terms.
287
-
288
- To do so, attach the following notices to the program. It is safest
289
- to attach them to the start of each source file to most effectively
290
- convey the exclusion of warranty; and each file should have at least
291
- the "copyright" line and a pointer to where the full notice is found.
292
-
293
- {description}
294
- Copyright (C) {year} {fullname}
295
-
296
- This program is free software; you can redistribute it and/or modify
297
- it under the terms of the GNU General Public License as published by
298
- the Free Software Foundation; either version 2 of the License, or
299
- (at your option) any later version.
300
-
301
- This program is distributed in the hope that it will be useful,
302
- but WITHOUT ANY WARRANTY; without even the implied warranty of
303
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
- GNU General Public License for more details.
305
-
306
- You should have received a copy of the GNU General Public License along
307
- with this program; if not, write to the Free Software Foundation, Inc.,
308
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
-
310
- Also add information on how to contact you by electronic and paper mail.
311
-
312
- If the program is interactive, make it output a short notice like this
313
- when it starts in an interactive mode:
314
-
315
- Gnomovision version 69, Copyright (C) year name of author
316
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
- This is free software, and you are welcome to redistribute it
318
- under certain conditions; type `show c' for details.
319
-
320
- The hypothetical commands `show w' and `show c' should show the appropriate
321
- parts of the General Public License. Of course, the commands you use may
322
- be called something other than `show w' and `show c'; they could even be
323
- mouse-clicks or menu items--whatever suits your program.
324
-
325
- You should also get your employer (if you work as a programmer) or your
326
- school, if any, to sign a "copyright disclaimer" for the program, if
327
- necessary. Here is a sample; alter the names:
328
-
329
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
-
332
- {signature of Ty Coon}, 1 April 1989
333
- Ty Coon, President of Vice
334
-
335
- This General Public License does not permit incorporating your program into
336
- proprietary programs. If your program is a subroutine library, you may
337
- consider it more useful to permit linking proprietary applications with the
338
- library. If this is what you want to do, use the GNU Lesser General
339
- Public License instead of this License.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.txt CHANGED
@@ -1,428 +1,88 @@
1
  === Plugin Name ===
2
  Contributors: blueliquiddesigns
3
- Donate link: https://gravitypdf.com
4
  Tags: gravity, forms, pdf, automation, attachment
5
- Requires at least: 3.5
6
- Tested up to: 4.1
7
- Stable tag: 3.6.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
- Gravity PDF allows you to save/view/download a PDF from the front- and back-end, and automate PDF creation on form submission.
12
 
13
  == Description ==
14
 
15
- Gravity PDF is a powerful developer tool for creating PDF documents using form data captured from Gravity Forms. While the software is targeted at web developers, we've attempted to make it user friendly for hobbyists and DIY business owners. The basic setup can be done in minutes, and there is a huge array of options to configure the PDF as you see fit.
16
 
17
- **Gravity Form Features**
 
 
18
 
19
  * Save PDF File on user submission of a Gravity Form so it can be attached to a notification
20
  * Customise the PDF template without affecting the core Gravity Form Plugin
21
  * Multiple PDF Templates
22
  * Custom PDF Name
23
  * Output individual form fields in the template - like MERGETAGS.
24
- * View and download a PDF via the administrator interface or after a user submits their form
 
25
  * Works with Gravity Forms Signature Add-On
26
-
27
- **PDF Features**
28
-
29
- Along with the above, the PDF software includes powerful feature such as:
30
-
31
- * Language Support - almost all languages are supported including RTL (right to left) languages like Arabic, Hebrew and CJK languages - Chinese, Japanese and Korean.
32
- * HTML Page Numbering
33
- * Odd and even paging with mirrored margins (most commonly used in printing).
34
- * Nested Tables
35
- * Text-justification and hyphenation
36
- * Table of Contents
37
- * Index
38
- * Bookmarks
39
- * Watermarks
40
- * Password protection
41
- * UTF-8 encoded HTML
42
- * Better system resource handling
43
 
44
  **Server Requirements**
45
 
46
- 1. PHP 5+
47
- 2. MB String
48
- 3. GD Library
49
- 4. RAM: Recommended: 128MB. Minimum: 64MB.
50
-
51
- *Note:* We've had clients report slow PDF generation times and problems meeting the RAM requirements on cheap shared web hosting. If you experience these problems [we recommend you look into WP Engine's managed hosting platform](http://www.shareasale.com/r.cfm?B=398776&U=955815&M=41388&urllink=) as our software works correctly out of the box.
52
 
53
  **Software Requirements**
54
 
55
  1. [Purchase and install Gravity Forms](https://www.e-junkie.com/ecom/gb.php?cl=54585&c=ib&aff=235154)
56
- 2. Wordpress 3.5+
57
- 3. Gravity Forms 1.7+
58
 
59
  **Documentation and Support**
60
 
61
- To view the Development Documentation head to [https://developer.gravitypdf.com](https://developer.gravitypdf.com). If you need support with the plugin please post a topic in our [support forums](https://support.gravitypdf.com).
62
 
63
  == Installation ==
64
 
65
  1. Upload this plugin to your website and activate it
66
- 2. Head to Forms -> Settings -> PDF to initialise the plugin.
67
- 3. Create a form in Gravity Forms and configure notifications
68
- 4. Get the Form ID and follow the steps below in [the configuration section](http://developer.gravitypdf.com/documentation/getting-started-with-gravity-pdf-configuration/)
69
- 5. Modify the PDF template file ([see the advanced templating section in the documentation](http://developer.gravitypdf.com/documentation/custom-templates-introduction/)) inside your active theme's PDF_EXTENDED_TEMPLATES/ folder.
70
 
71
 
72
  == Frequently Asked Questions ==
73
 
74
- All FAQs can be [viewed on the Gravity PDF website](https://gravitypdf.com/#faqs).
75
 
76
  == Screenshots ==
77
 
78
- 1. The View PDF button is avaliable for each Gravity Form entry
79
- 2. Multiple PDFs can be assigned to a form and is also avaliable on the detailed entry view.
80
- 3. The configuration.php file allows you to easily assign PDFs to Gravity Forms
81
 
82
  == Changelog ==
83
 
84
- = 3.6.0 =
85
- * Feature - Added support for Gravity Form's sub-field middle name (1.9Beta)
86
- * Feature - Patch mPDF with full :nth-child support on TD and TR table cells
87
- * Feature - Added $form_data[products_totals][subtotal] key (total price without shipping costs added)
88
- * Feature - Added formated money to all product fields in the $form_data array
89
- * Feature - Default templates: only show fields who's conditional logic is true. Perfect when used with default-show-html
90
- * Housekeeping - Move PDF_EXTENDED_TEMPLATES folder to the /wp-content/upload/ directory. Get more info about the move (see http://developer.gravitypdf.com/news/migrating-template-directory-means/)
91
- * Housekeeping - Refined when admin resources are loaded
92
- * Housekeeping - Fixed typo during initial initialisation
93
- * Housekeeping - Switched icons back to FontAwesome which is shipped by default with Gravity Forms
94
- * Housekeeping - Display full path to mPDF tmp directory when there are issues writing to it
95
- * Housekeeping - Modified font installation message.
96
- * Housekeeping - Update example-header-and-footer_06.php and example-advanced-headers_07.php to better reflect current mPDF features
97
- * Bug - Fixed issue pulling the correct configuration when multiple nodes were assigned to multiple forms
98
- * Bug - Fixed number field formatting issue which always rounded to two decimal places
99
- * Bug - Fixed JS namespace issue with WordPress Leads plugin
100
- * Bug - Fixed error initialising fonts / backing up PDF_EXTENDED_TEMPLATES directory when using the glob() function
101
- * Bug - Fix issue with PHP 5.0 and 5.1 array_replace_recursive function when used with an array inside the $gf_pdf_config array
102
- * Bug - Fixed fatal error when logged in user attempts to view PDF they don't have access to
103
- * Bug - Fixed issue in $form_data array where single-column list items where being returned as an array and not a HTML list.
104
- * Bug - Prevent unauthorised users auto-initialising the software or migrating the templates folder
105
- * Bug - Fixed up incorrect formatting issue when using custom font name
106
- * Bug - Fixed issue displaying Times New Roman in PDF templates
107
-
108
- = 3.5.11.1 =
109
- * Bug - Fix issue saving and sending blank PDFs due to security fix
110
-
111
- = 3.5.11 =
112
- * Bug - Fix security issue which gave unauthorised users access to Gravity Form entires
113
-
114
- = 3.5.10 =
115
- * Housekeeping - Include individual scoring for Gravity Form Survey Likert field in the $form_data['survey'] array
116
- * Bug - Fix fatal error when Gravity Forms isn't activated, but Gravity PDF is.
117
-
118
- = 3.5.9 =
119
- * Bug - Rollback recent changes that introduced the GFAPI as introduces errors for older versions of Gravity Forms. Will reintroduce in next major release and increase the minimum Gravity Forms version.
120
-
121
- = 3.5.8 =
122
- * Bug - Fixed issue affected some users where a depreciated function was causing a fatal error
123
-
124
- = 3.5.7 =
125
- * Bug - Fixed issue where the PDF settings page was blank for some users
126
-
127
- = 3.5.6 =
128
- * Bug - Fixed issue with last release that affected checks to see if Gravity Forms has submitting
129
- * Bug - Fixed fatal error with servers using PHP5.2 or lower
130
- * Bug - Fixed E_NOTICE for replacement array_replace_recursive() function in PHP5.2 or lower
131
- * Bug - Fixed issue with AJAX spinner showing when submitting support request
132
-
133
- = 3.5.5 =
134
- * Housekeeping - Include French translation (thanks to Marie-Aude Koiransky-Ballouk)
135
- * Housekeeping - Wrap 'Initialise Fonts' text in translation ready _e() function
136
- * Housekeeping - Tidy up System Status CSS styles to accomidate translation text lengths
137
- * Housekeeping - Fix E_NOTICE when viewing entry details page when form has no PDF configuration
138
- * Bug - Fixed load_plugin_textdomain which was incorrectly called.
139
- * Bug - Correctly check if the plugin is loaded correctly before letting the PDF class fully load
140
-
141
- = 3.5.4 =
142
- * Bug - Fixed issue with incorrect PDF name showing on the entry details page
143
- * Bug - Fixed issue with custom fonts being inaccessible without manually reinstalling after upgrading.
144
- * Housekeeping - Added in two new filters to modify the $mpdf object. 'gfpdfe_mpdf_class' and 'gfpdfe_mpdf_class_pre_render' (replaces the gfpdfe_pre_render_pdf filter).
145
-
146
- = 3.5.3 =
147
- * Bug - Mergetags braces ({}) were being encoded before conversion
148
- * Bug - Fixed issue with empty string being passed to array filter
149
- * Housekeeping - Enabled mergetag usage in the pdf_password and pdf_master_password configuration options
150
- * Housekeeping - Correctly call $wpdb->prepare so the variables in are in the second argument
151
-
152
- = 3.5.2 =
153
- * Bug - Initialisation folder .htaccess file was preventing template.css from being loaded by the default templates.
154
-
155
- = 3.5.1 =
156
- * Bug - Fixed issue with core fonts Arial/Helvetica, Times/Times New Roman and Courier not displaying in the PDF.
157
- * Bug - Fixed display issues for multiple PDFs on the details admin entry page
158
- * Housekeeping - Made the details entry page PDF view consistent for single or multiple PDFs
159
- * Housekeeping - Ensured all javascript files are minified and are correctly being used
160
- * Housekeeping - Remove legacy notices from mPDF package
161
-
162
- = 3.5.0 =
163
- * Feature - No longer need to reinitialise every time the software is updated.
164
- * Feature - Add auto-initialiser on initial installation for sites that have direct write access to their server files
165
- * Feature - Add auto-initialiser on initial installation across entire multisite network for sites who have direct write access to their server files.
166
- * Feature - Add auto-PDF_EXTENDED_TEMPLATES theme syncer for sites that have direct write access to their server files
167
- * Feature - Correctly added language support. The .PO file is located in the /language/ folder if anyone would like to do a translation.
168
-
169
- * Housekeeping - Restrict initialisation process to 64MB or greater to counter problems with users reporting a 'white screen' when running in a low-RAM environment.
170
- * Housekeeping - Refractor the admin notices code
171
- * Housekeeping - Create responsive PDF settings page
172
- * Housekeeping - Minify CSS and Javascript files
173
- * Housekeeping - Remove FontAwesome fonts from package and use Wordpress' build-in 'dashicons'
174
- * Housekeeping - Refine action and error messages
175
- * Housekeeping - Update initialisation tab copy for both pre- and post- initialisation
176
- * Housekeeping - Use Gravity Forms get_ip() function instead of custom function
177
- * Housekeeping - The in-built support form uses SSL once again (disabled in the past due to some servers being unable to verify the certificate).
178
-
179
- * Bug - When testing write permissions, file_exist() is throwing false positives for some users which would generate a warning when unlink() is called. Hide warning using '@'.
180
-
181
- = 3.4.1 =
182
- * Bug - Fix typo that effected sites running PHP5.2 or below.
183
-
184
- = 3.4.0.3 =
185
- * Bug - Define array_replace_recursive() if it doesn't exist, as it is PHP 5.3 only.
186
-
187
- = 3.4.0.2 =
188
- * Housekeeping - Wrapped the View PDF and Download buttons in correct language functions - _e()
189
- * Bug - Fix problem displaying the signature field
190
- * Bug - Fix notice errors with new 'save' PDF hook
191
-
192
- = 3.4.0.1 =
193
- * Housekeeping - Add commas on the last line of every config node in the configuration.php file
194
- * Housekeeping - Fix up initialisation error messages
195
- * Bug - Fix up mPDF bugs - soft hyphens, watermarks over SVG images, inline CSS bug
196
-
197
- = 3.4.0 =
198
- * Feature - Added auto-print prompt ability when you add &print=1 to the PDF URL (see https://developer.gravitypdf.com/documentation/display-pdf-in-browser/ for details)
199
- * Feature - Added ability to rotate absolute positioned text 180 degrees (previously only 90 and -90). Note: feature in beta
200
- * Feature - Backup all template files that are overridden when initialising to a folder inside PDF_EXTENDED_TEMPLATES
201
- * Feature - Added SSH initialisation support
202
- * Feature - Allow MERGETAGS to be used in all PDF templates, including default template (but only in the HTML field).
203
- * Feature - Updated mPDF to 3.7.1
204
- * Feature - Enable text/image watermark support. Added new example template example-watermark09.php showing off its usage (see http://gravitypdf.com/documentation-v3-x-x/templates/watermarks/)
205
- * Feature - Added full survey, poll and quiz support to both the default template and $form_data (see https://developer.gravitypdf.com/documentation/accessing-survey-poll-quiz-data/)
206
- * Feature - Shortcodes will now be processed in all templates, including default template (but only in the HTML field).
207
- * Feature - Added 'save' configuration option so PDFs are saved to the local disk when 'notifications' aren't enabled.
208
- * Feature - Added 'dpi' configuration option to modify the PDF image DPI. Default 96dpi. Use 300dpi for printing.
209
- * Feature - Added PDF/A1-b compliance option. Enable with 'pdfa1b' => true. See http://mpdf1.com/manual/index.php?tid=420&searchstring=pdf/a1-b for more details.
210
- * Feature - Added PDF/X1-a compliance option. Enable with 'pdfx1a' => true. See http://mpdf1.com/manual/index.php?tid=481&searchstring=pdf/x-1a for more details.
211
- * Feature - Added new constant option 'PDF_REPACK_FONT' which when enabled may improve function with some PostScript printers (disabled by default). Existing sites will need to add define('PDF_REPACK_FONT', true); to the bottom of their configuration.php file.
212
- * Feature - Added a sleuth of new hooks and filters for developers. See https://developer.gravitypdf.com/documentation/filters-and-hooks/ for examples.
213
- * Feature - Added $form_data['form_description'] key to $form_data array
214
- * Feature - Update $form_data['products'] array key to field ID
215
- * Feature - Added survey Likert output function for custom templates (much like the product table function). It can be used with the following command 'echo GFPDFEntryDetails::get_likert($form, $lead, $field_id);' where $field_id is substituted for the form field ID.
216
- * Feature - Added field descriptions to the $form_data array under the $form_data['field_descriptions'] key.
217
- * Feature - Added pre and post PDF generation filters and actions to pdf-render.php. These include gfpdfe_pre_render_pdf, gfpdfe_pdf_output_type, gfpdfe_pdf_filename and gfpdf_post_pdf_save.
218
- * Feature: $form_data['signature'] et al. keys now contain the signature width and height attributes
219
-
220
- * Housekeeping - Ensure the form and lead IDs are correctly passed throughout the render functions.
221
- * Housekeeping - Update settings page link to match new Gravity Forms URL structure
222
- * Housekeeping - Check if $lead['gfsurvey_score'] exists before assigning to $form_data array
223
- * Housekeeping - Removed table and font checksum debugging from mPDF when WP_DEBUG enabled as they produced inaccurate results.
224
- * Housekeeping - Fixed up mPDF logging location when WP_DEBUG enabled. Files now stored in wp-content/themes/Active_Theme_Folder/PDF_EXTENDED_TEMPLATES/output/ folder.
225
- * Housekeeping - Removed API logging locally when WP_DEBUG is enabled.
226
- * Housekeeping - Increase API timeout interval as some overseas users reported timeout issues
227
- * Housekeeping - Modified mPDF functions Image() and purify_utf8_text() to validate the input data so we don't have to do it every time through the template.
228
- * Housekeeping - Added ability to not re-deploy every update (not enabled this release as template files were all updated)
229
- * Housekeeping - Additional checks on load to see if any of the required file/folder structure is missing. If so, re-initilise.
230
- * Housekeeping - Save resources and turn off automatic rtl identification. Users must set the RTL option when configuring form
231
- * Housekeeping - Turn off mPDFs packTableData setting, decreasing processing time when working with large tables.
232
- * Housekeeping - $gf_pdf_default_configuration options now merge down into existing PDF nodes, instead of applying to only unassigned forms. $gf_pdf_config settings override any in $gf_pdf_default_configuration
233
- * Housekeeping - Center aligned Survey Likery field results
234
- * Housekeeping - Partially refactored the pdf-entry-detail.php code
235
- * Housekeeping - All default and example templates have been tidied. This won't affect custom templates.
236
- * Housekeeping - Set the gform_notification order number to 100 which will prevent other functions (example snippets from Gravity Forms, for instance) from overridding the attached PDF.
237
- * Housekeeping - Fix spelling mistake on initialising fonts
238
- * Housekeeping - Remove wpautop() function from Gravity Form HTML output, which was applied before rendering and was messing up the HTML markup.
239
- * Housekeeping - Remove empty list rows from the $form_data['list'] array in single and multi-column lists.
240
- * Housekeeping - Apply same CSS styles (padding, border and line height) to HTML fields as done to form values in default templates
241
- * Housekeeping - Replaced arbitrary wrapper IDs in the default templates with the actual field ID
242
-
243
- * Bug - Fixed signature rendering issue when custom signature size was being used
244
- * Bug - Fixed static error types in helper/install-update-manager.php file.
245
- * Bug - Fixed redeployment error message which wasn't showing correctly
246
- * Bug - Fixed issue with PDF not attaching to notification using Paypal's delayed notification feature
247
- * Bug - Fixed strict standard warning about calling GFPDF_Settings::settings_page();
248
- * Bug - Fixed strict standard warning about calling GFPDFEntryDetail::pdf_get_lead_field_display();
249
- * Bug - Fixed issue with Gravity Form Post Category field causing fatal error generating PDF
250
- * Bug - Fixed number field formatting issue when displaying on PDF.
251
- * Bug - Do additional check for PHP's MB_String regex functions before initialising ti prevent errors after initialising
252
- * Bug - Fixed problem with multiple nodes assigned to a form using the same template
253
- * Bug - Fixed path to fallback templates when not found
254
- * Bug - Fixed problem with master password setting to user password
255
-
256
-
257
- = 3.3.4 =
258
- * Bug - Fixed issue linking to PDF from front end
259
- * Housekeeping - Removed autoredirect to initialisation page
260
-
261
- = 3.3.3 =
262
- * Bug - Correctly call javascript to control admin area 'View PDFs' drop down
263
- * Bug - Some users still reported incorrect RAM. Convert MB/KB/GB values to M/K/G as per the PHP documentation.
264
- * Housekeeping - Show initilisation prompt on all admin area pages instead of only on the Gravity Forms pages
265
-
266
- = 3.3.2.1 =
267
- * Bug - Incorrectly showing assigned RAM to website
268
-
269
- = 3.3.2 =
270
- * Bug - Some hosts reported SSL certificate errors when using the support API. Disabled HTTPS for further investigation. Using hash-based verification for authentication.
271
- * Housekeeping - Forgot to disable API debug feature after completing beta
272
-
273
- = 3.3.1 =
274
- * Bug - $form_data['list'] was mapped using an incremental key instead of via the field ID
275
-
276
- = 3.3.0 =
277
- * Feature - Overhauled the initialisation process so that the software better reviews the host for potential problems before initialisation. This should help debug issues and make users aware there could be a problem before they begin using the software.
278
- * Feature - Overhauled the settings page to make it easier to access features of the software
279
- * Feature - Added a Support tab to the settings page which allows users to securely (over HTTPS) submit a support ticket to the Gravity PDF support desk
280
- * Feature - Changed select, multiselect and radio fields so that the default templates use the name rather than the value. $form_data now also includes the name and values for all these fields.
281
- * Feature - $form_data now includes all miscellaneous lead information in the $form_data['misc'] array.
282
- * Feature - $form_data now contains 24 and 12 hour time of entry submission.
283
- * Feature - Added localisation support
284
- * Compatibility - Added new multi-upload support which was added in Gravity Forms 1.8.
285
- * Bug - Added 'aid' parametre to the PDF url when multiple configuration nodes present on a single form
286
- * Bug - Fixed issue when Gravity Forms in No Conflict Mode
287
- * Bug - Font config.php's array keys now in lower case
288
- * Housekeeping - Moved all initialisation files to a folder called 'initialisation'.
289
- * Housekeeping - Renamed the configuration.php file in the plugin folder to configuration.php.example to alleviate confusion for developers who unwittingly modify the plugin configuration file instead of the file in their active theme's PDF_EXTENDED_TEMPLATES folder.
290
- * Housekeeping - Updated the plugin file system to a more MVC-style approach, with model and view folders.
291
- * Housekeeping - Removed ability to directly access default and example template files.
292
- * Housekeeping - Fixed PHP notices in default templates related to the default template-only configuration options
293
- * Housekeeping - Update core styles to match Wordpress 3.8/Gravity Forms 1.8.
294
- * Housekeeping - Updated header/footer examples to use @page in example.
295
-
296
- = 3.2.0 =
297
- * Feature - Can now view multiple PDFs assigned to a single form via the admin area. Note: You must provide a unique 'filename' parameter in configuration.php for multiple PDFs assigned to a single form.
298
- * Feature - You can exclude a field from the default templates using the class name 'exclude'. See our [FAQ topic](https://gravitypdf.com/#faqs) for more details.
299
- * Bug - Fixed issue viewing own PDF entry when logged in as anything lower than editor.
300
- * Bug - Fixed data return bug in pdf-entry-details.php that was preventing all data returning correctly.
301
- * Bug - Fixed PHP Warning when using products with no options
302
- * Bug - Fixed issue with invalid characters being added to the PDF filename. Most notably the date mergetag.
303
- * Bug - Limit filename length to 150 characters which should work on the majority of web servers.
304
- * Bug - Fixed problem sending duplicate PDF when using mass resend notification feature
305
- * Depreciated - Removed GF_FORM_ID and GF_LEAD_ID constants which were used in v2.x.x of the software. Ensure you follow [v2.x.x upgrade guide](https://developer.gravitypdf.com/news/version-2-3-migration-guide/) to your templates before upgrading.
306
-
307
- = 3.1.4 =
308
- * Bug - Fixed issue with plugin breaking website's when the Gravity Forms plugin wasn't activated.
309
- * Housekeeping - The plugin now only supports Gravity Forms 1.7 or higher and Wordpress 3.5 or higher.
310
- * Housekeeping - PDF template files can no longer be accessed directly. Instead, add &amp;html=1 to the end of your URL when viewing a PDF.
311
- * Extension - Added additional filters to allow the lead ID and notifications to be overridden.
312
-
313
- = 3.1.3 =
314
- * Feature - Added signature_details_id to $form_data array which maps a signatures field ID to the array.
315
- * Extension - Added pre-PDF generator filter for use with extensions.
316
- * Bug - Fixed issue with quotes in entry data breaking custom templates.
317
- * Bug - Fixed issue with the plugin not correctly using the new default configuration template, if set.
318
- * Bug - Fixed issue with signature not being removed correctly when only testing with file_exists(). Added second is_dir() test.
319
- * Bug - Fixed issue with empty signature field not displaying when option 'default-show-empty' is set.
320
- * Bug - Fixed initialisation prompt issue when the MPDF package wasn't unpacked.
321
-
322
- = 3.1.2 =
323
- * Feature - Added list array, file path, form ID and lead ID to $form_data array in custom templates
324
- * Bug - Fixed initialisation prompt issue when updating plugin
325
- * Bug - Fixed window.open issue which prevented a new window from opening when viewing a PDF in the admin area
326
- * Bug - Fixed issue with product dropdown and radio button data showing the value instead of the name field.
327
- * Bug - Fixed incorrect URL pointing to signature in $form_data
328
-
329
- = 3.1.1 =
330
- * Bug - Users whose server only supports FTP file manipulation using the WP_Filesystem API moved the files into the wrong directory due to FTP usually being rooted to the Wordpress home directory. To fix this the plugin attempts to determine the FTP directory, otherwise assumes it is the WP base directory.
331
- * Bug - Initialisation error message was being called but the success message was also showing.
332
-
333
- = 3.1.0 =
334
- * Feature - Added defaults to configuration.php which allows users to define the default PDF settings for all Gravity Forms. See the [installation and configuration documentation](https://developer.gravitypdf.com/documentation/getting-started-with-gravity-pdf-configuration/) for more details.
335
- * Feature - Added three new configuration options 'default-show-html', 'default-show-empty' and 'default-show-page-names' which allow different display options to the three default templates. See the [installation and configuration documentation](http://gravitypdf.com/documentation-v3-x-x/installation-and-configuration/#default-template) for more details.
336
- * Feature - Added filter hooks 'gfpdfe_pdf_name' and 'gfpdfe_template' which allows developers to further modify a PDF name and template file, respectively, outside of the configuration.php. This is useful if you have a special case naming convention based on user input. See [https://developer.gravitypdf.com/documentation/filters-and-hooks/](https://developer.gravitypdf.com/documentation/filters-and-hooks/) for more details about using these filters.
337
- * Feature - Custom font support. Any .ttf font file added to the PDF_EXTENDED_TEMPLATES/fonts/ folder will be automatically installed once the plugin has been initialised. Users also have the option to just initialise the fonts via the settings page. See the [font/language documentation ](https://developer.gravitypdf.com/documentation/language-support/#install-custom-fonts) for details.
338
- * Compatability - Use Gravity Forms get_upload_root() and get_upload_url_root() instead of hard coding the signature upload directory in pdf-entry-detail.php
339
- * Compatability - Changed depreciated functions get_themes() and get_theme() to wp_get_theme() (added in Wordpress v3.4).
340
- * Compatability - The plugin now needs to be initialised on fresh installation and upgrade. This allows us to use the WP_Filesystem API for file manipulation.
341
- * Compatability - Automatic copying of PDF_EXTENDED_TEMPLATES folder on a theme change was removed in favour of a user prompt. This allows us to take advantage of the WP_Filesystem API.
342
- * Compatability - Added Wordpress compatibility checker (minimum now 3.4 or higher).
343
- * Bug - Removed ZipArchive in favour of Wordpress's WP_Filesystem API unzip_file() command. Some users reported the plugin would stop their entire website working if this extension wasn't installed.
344
- * Bug - Fixed Gravity Forms compatibility checker which wouldn't return the correct response.
345
- * Bug - Fixed minor bug in pdf.php when using static call 'self' in add_filter hook. Changed to class name.
346
- * Bug - Removed PHP notice about $even variable not being defined in pdf-entry-detail.php
347
- * Bug - Prevent code from continuing to excecute after sending header redirect.
348
-
349
- = 3.0.2 =
350
- * Backwards Compatibility - While PHP 5.3 has was released a number of years ago it seems a number of hosts do not currently offer this version to their clients. In the interest of backwards compatibility we've re-written the plugin to again work with PHP 5+.
351
- * Signature / Image Display Bug - All URLs have been converted to a path so images should now display correctly in PDF.
352
-
353
- = 3.0.1 =
354
- * Bug - Fixed issue that caused website to become unresponsive when Gravity Forms was disabled or upgraded
355
- * Bug - New HTML fields weren't being displayed in $form_data array
356
- * Feature - Options for default templates to disable HTML fields or empty fields (or both)
357
-
358
- = 3.0.0 =
359
- As of Gravity PDF v3.0.0 we have removed the DOMPDF package from our plugin and integrated the more advanced mPDF system. Along with a new HTML to PDF generator, we've rewritten the entire plugin's base code to make it more user friendly to both hobbyists and rock star web developers. Configuration time is cut in half and advanced features like adding security features is now accessible to users who have little experience with PHP.
360
-
361
- New Features include:
362
-
363
- * Language Support - almost all languages are supported including RTL (right to left) languages like Arabic and Hebrew and CJK languages - Chinese, Japanese and Korean.
364
- * HTML Page Numbering
365
- * Odd and even paging with mirrored margins (most commonly used in printing).
366
- * Nested Tables
367
- * Text-justification and hyphenation
368
- * Table of Contents
369
- * Index
370
- * Bookmarks
371
- * Watermarks
372
- * Password protection
373
- * UTF-8 encoded HTML
374
- * Better system resource handling
375
-
376
- A new HTML to PDF package wasn't the only change to this edition of the software. We have rewritten the entire configuration system and made it super easy to get the software up and running.
377
-
378
- Users will no longer place code in their active theme's functions.php file. Instead, configuration will happen in a new file called configuration.php, inside the PDF_EXTENDED_TEMPLATES folder (in your active theme).
379
-
380
- Other changes include
381
- * Improved security - further restrictions were placed on non-administrators viewing template files.
382
- * $form_data array tidied up - images won't be wrapped in anchor tags.
383
-
384
- For more details [view the 3.x.x online documentation](https://developer.gravitypdf.com/).
385
-
386
- = 2.2.3 =
387
- * Bug - Fixed mb_string error in the updated DOMPDF package.
388
-
389
- = 2.2.2 =
390
- * DOMPDF - We updated to the latest version of DOMPDF - DOMPDF 0.6.0 beta 3.
391
- * DOMPDF - We've enabled font subsetting by default which should help limit the increased PDF size when using DejaVu Sans (or any other font).
392
-
393
- = 2.2.1 =
394
- * Bug - Fixed HTML error which caused list items to distort on PDF
395
-
396
- = 2.2.0 =
397
- * Compatibility - Ensure compatibility with Gravity Forms 1.7. We've updated the functions.php code and remove gform_user_notification_attachments and gform_admin_notification_attachments hooks which are now depreciated. Functions gform_pdf_create and gform_add_attachment have been removed and replaced with gfpdfe_create_and_attach_pdf(). See upgrade documentation for details.
398
- * Enhancement - Added deployment code switch so the template redeployment feature can be turned on and off. This release doesn't require redeployment.
399
- * Enhancement - PDF_Generator() variables were getting long and complex so the third variable is now an array which will pass all the optional arguments. The new 1.7 compatible functions.php code includes this method by default. For backwards compatibility the function will still work with the variable structure prior to 2.2.0.
400
- * Bug - Fixed error generated by legacy code in the function PDF_processing() which is located in render_to_pdf.php.
401
- * Bug - Images and stylesheets will now try and be accessed with a local path instead of a URL. It fixes problem where some hosts were preventing read access from a URL. No template changes are required.
402
-
403
- = 2.1.1 =
404
- * Bug - Signatures stopped displaying after 2.1.0 update. Fixed issue.
405
- * Bug - First time install code now won't execute if already have configuration variables in database
406
-
407
  = 2.1.0 =
408
 
409
- * Feature - Product table can now be accessed directly through custom templates by running GFPDFEntryDetail::product_table($form, $lead);. See documentation for more details.
410
- * Feature - Update screen will ask you if you want to deploy new template files, instead of overriding your modified versions.
411
- * Feature - Product subtotal, shipping and total have been added to $form_data['field'] array to make it easier to work with product details in the custom template.
412
- * Feature - Added two new default template files. One displays field and name in two rows (like you see when viewing an entry in the admin area) and the other removes all styling. See documentation on use.
413
- * Security - Tightened PDF template security so that custom templates couldn't be automatically generated by just anyone. Now only logged in users with the correct privileges and the user who submitted the form (matched against IP) can auto generate a PDF. See documentation on usage.
414
- * Depreciated - Removed form data that was added directly to the $form_data array instead of $form_data['field'] array. Users upgrading will need to update their custom templates if not using field data from the $form_data[�field'] array. If using $form_data['field'] in your custom template this won't affect you.
415
- * Bug - Fixed problem with default template not showing and displaying a timeout error. Removed table tags and replaced with divs that are styled appropriately.
416
- * Bug - The new plugin theme folder will successfully create when upgrading. You won't have to deactivate and reactivate to get it working.
417
- * Bug - some installs had plugins that included the function mb_string which is also included in DOMPDF. DOMPDF will now check if the function exists before creating it.
418
- * Bug - Remove empty signature field from the default template.
419
- * Bug - fixed problem with redirecting to login screen even when logged in while accessing template file through the browser window directly.
420
- * Bug - fixed error where sample template would reimport itself automatically even after deleting it. Will now only reimport if any important changes to template need to be viewed straight after an update.
421
- * Bug - Moved render_to_pdf.php constants to pdf.php so we can use the constants in the core files. Was previously generating an error.
422
- * Housekeeping - Cleaned up core template files, moved functions into classes and added more in-file documentation.
423
- * Housekeeping - moved install/upgrade code from pdf.php to installation-update-manager.php
424
- * Housekeeping - changed pdf-entry-detail.php class name from GFEntryDetail to GFPDFEntryDetail to remove compatibility problems with Gravity Forms.
425
- * Housekeeping - created pdf-settings.php file to house the settings page code.
426
 
427
  = 2.0.1 =
428
  * Fixed Signature bug when checking if image file exists using URL instead of filesystem path
@@ -465,5 +125,5 @@ For more details [view the 3.x.x online documentation](https://developer.gravity
465
 
466
  == Upgrade Notice ==
467
 
468
- = 3.6.0 =
469
- WARNING: Minimum WordPress and Gravity Forms versions have been bumped to WordPress 3.9+ and Gravity Forms 1.8+. If you aren't running either version of this software DO NOT UPGRADE.
1
  === Plugin Name ===
2
  Contributors: blueliquiddesigns
3
+ Donate link: http://www.gravityformspdfextended.com
4
  Tags: gravity, forms, pdf, automation, attachment
5
+ Requires at least: 3.4.1
6
+ Tested up to: 3.5
7
+ Stable tag: 2.1.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
11
+ Gravity Forms PDF Extended allows you to save/view/download a PDF from the front- and back-end, and automate PDF creation on form submission.
12
 
13
  == Description ==
14
 
15
+ Gravity Forms PDF Extended is a plugin for Wordpress and Gravity Forms that allows your web server to create PDFs when a user submits a Gravity Form. Not only that, you can then easily attach the document to an email.
16
 
17
+ At its core the plugin uses the power of the DOMPDF library to convert HTML and CSS into PDFs.
18
+
19
+ **Features**
20
 
21
  * Save PDF File on user submission of a Gravity Form so it can be attached to a notification
22
  * Customise the PDF template without affecting the core Gravity Form Plugin
23
  * Multiple PDF Templates
24
  * Custom PDF Name
25
  * Output individual form fields in the template - like MERGETAGS.
26
+ * View and download a PDF via the administrator interface
27
+ * Simple function to output PDF via template / plugin
28
  * Works with Gravity Forms Signature Add-On
29
+ * Installs a sample form using the new MERGETAGS-style template to help customisation
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  **Server Requirements**
32
 
33
+ 1. PHP 5.0+ (5.3 recommended)
34
+ 2. MBString extension
35
+ 3. DOM extension (bundled with PHP 5)
36
+ 4. If you want images in your PDF you'll also need the GD Library
 
 
37
 
38
  **Software Requirements**
39
 
40
  1. [Purchase and install Gravity Forms](https://www.e-junkie.com/ecom/gb.php?cl=54585&c=ib&aff=235154)
41
+ 2. Wordpress 3.0+
42
+ 3. Gravity Forms 1.6.9+
43
 
44
  **Documentation and Support**
45
 
46
+ To view the Development Documentation head to [http://www.gravityformspdfextended.com/documentation/](http://www.gravityformspdfextended.com/documentation/). If you need support with the plugin please post a topic in our [support forums](http://gravityformspdfextended.com/support/gravity-forms-pdf-extended/).
47
 
48
  == Installation ==
49
 
50
  1. Upload this plugin to your website and activate it
51
+ 2. Create a form in Gravity Forms and configure notifications
52
+ 3. Get the Form ID and follow the steps below in [the configuration section](http://gravityformspdfextended.com/documentation/installation/configuration/)
53
+ 4. Modify the PDF template file ([see the advanced templating section in the documentation](http://gravityformspdfextended.com/documentation/advanced-configuration/)), pdf-print-entry.php or example-template.php, inside your active theme's PDF_EXTENDED_TEMPLATES/ folder.
 
54
 
55
 
56
  == Frequently Asked Questions ==
57
 
58
+ All FAQs can be [viewed on the Gravity Forms PDF Extended website](http://gravityformspdfextended.com/faq/category/developers/).
59
 
60
  == Screenshots ==
61
 
62
+ 1. View PDF from the Gravity Forms entries list.
63
+ 2. View or download the PDF from a Gravity Forms entry.
 
64
 
65
  == Changelog ==
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  = 2.1.0 =
68
 
69
+ * Feature � Product table can now be accessed directly through custom templates by running GFPDFEntryDetail::product_table($form, $lead);. See documentation for more details.
70
+ * Feature � Update screen will ask you if you want to deploy new template files, instead of overriding your modified versions.
71
+ * Feature � Product subtotal, shipping and total have been added to $form_data['field'] array to make it easier to work with product details in the custom template.
72
+ * Feature � Added two new default template files. One displays field and name in two rows (like you see when viewing an entry in the admin area) and the other removes all styling. See documentation on use.
73
+ * Security � Tightened PDF template security so that custom templates couldn�t be automatically generated by just anyone. Now only logged in users with the correct privileges and the user who submitted the form (matched against IP) can auto generate a PDF. See documentation on usage.
74
+ * Depreciated � Removed form data that was added directly to the $form_data array instead of $form_data['field'] array. Users upgrading will need to update their custom templates if not using field data from the $form_data[�field�] array. If using $form_data['field'] in your custom template this won�t affect you.
75
+ * Bug � Fixed problem with default template not showing and displaying a timeout error. Removed table tags and replaced with divs that are styled appropriately.
76
+ * Bug � The new plugin theme folder will successfully create when upgrading. You won�t have to deactivate and reactivate to get it working.
77
+ * Bug � some installs had plugins that included the function mb_string which is also included in DOMPDF. DOMPDF will now check if the function exists before creating it.
78
+ * Bug � Remove empty signature field from the default template.
79
+ * Bug � fixed problem with redirecting to login screen even when logged in while accessing template file through the browser window directly.
80
+ * Bug � fixed error where sample template would reimport itself automatically even after deleting it. Will now only reimport if any important changes to template need to be viewed straight after an update.
81
+ * Bug � Moved render_to_pdf.php constants to pdf.php so we can use the constants in the core files. Was previously generating an error.
82
+ * Housekeeping � Cleaned up core template files, moved functions into classes and added more in-file documentation.
83
+ * Housekeeping � moved install/upgrade code from pdf.php to installation-update-manager.php
84
+ * Housekeeping � changed pdf-entry-detail.php class name from GFEntryDetail to GFPDFEntryDetail to remove compatibility problems with Gravity Forms.
85
+ * Housekeeping � created pdf-settings.php file to house the settings page code.
86
 
87
  = 2.0.1 =
88
  * Fixed Signature bug when checking if image file exists using URL instead of filesystem path
125
 
126
  == Upgrade Notice ==
127
 
128
+ = 2.0.0 =
129
+ New Features: Added custom PDF names and moved templates to active theme's folder (no longer overridden after updating). Also fixed a number of bugs in the problem. Remember to backup your custom templates before upgrading!
depreciated.php DELETED
@@ -1,24 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Filename: depreciated.php
5
- * This file contains any depreciated functionality to help preserve backwards compatibility
6
- * with either Gravity Forms, or our template files.
7
- */
8
-
9
-
10
- /*
11
- * Since v3.6
12
- * Moved to the $gfpdfe_data class.
13
- * Can now be accessed using the following:
14
- *
15
- * $gfpdfe_data->template_location
16
- * $gfpdfe_data->template_site_location
17
- * $gfpdfe_data->template_save_location
18
- * $gfpdfe_data->template_font_location
19
- *
20
- */
21
- define('PDF_SAVE_LOCATION', $gfpdfe_data->template_save_location);
22
- define('PDF_FONT_LOCATION', $gfpdfe_data->template_font_location);
23
- define('PDF_TEMPLATE_LOCATION', $gfpdfe_data->template_site_location);
24
- define('PDF_TEMPLATE_URL_LOCATION', $gfpdfe_data->template_site_location_url);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dompdf/CONTRIBUTING.md ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # How to contribute
2
+
3
+ - [Getting help](#getting-help)
4
+ - [Submitting bug reports](#submitting-bug-reports)
5
+ - [Contributing code](#contributing-code)
6
+
7
+ ## Getting help
8
+
9
+ Community discussion, questions, and informal bug reporting is done on the
10
+ [dompdf Google group](http://groups.google.com/group/dompdf).
11
+
12
+ ## Submitting bug reports
13
+
14
+ The preferred way to report bugs is to use the
15
+ [GitHub issue tracker](http://github.com/dompdf/dompdf/issues). Before
16
+ reporting a bug, read these pointers.
17
+
18
+ **Please search inside the bug tracker to see if the bug you found is not already reported.**
19
+
20
+ **Note:** The issue tracker is for *bugs* and *feature requests*, not requests for help.
21
+ Questions should be asked on the
22
+ [dompdf Google group](http://groups.google.com/group/dompdf) instead.
23
+
24
+ ### Reporting bugs effectively
25
+
26
+ - dompdf is maintained by volunteers. They don't owe you anything, so be
27
+ polite. Reports with an indignant or belligerent tone tend to be moved to the
28
+ bottom of the pile.
29
+
30
+ - Include information about **the PHP version on which the problem occurred**. Even
31
+ if you tested several PHP version on different servers, and the problem occurred
32
+ in all of them, mention this fact in the bug report.
33
+ Also include the operating system it's installed on. PHP configuration can also help,
34
+ and server error logs (like Apache logs)
35
+
36
+ - Mention which release of dompdf you're using (the zip, the master branch, etc).
37
+ Preferably, try also with the current development snapshot, to ensure the
38
+ problem has not already been fixed.
39
+
40
+ - Mention very precisely what went wrong. "X is broken" is not a good bug
41
+ report. What did you expect to happen? What happened instead? Describe the
42
+ exact steps a maintainer has to take to make the problem occur. We can not
43
+ fix something that we can not observe.
44
+
45
+ - If the problem can not be reproduced in any of the demos included in the
46
+ dompdf distribution, please provide an HTML document that demonstrates
47
+ the problem. There are a few options to show us your code:
48
+ - [JS Fiddle](http://jsfiddle.net/)
49
+ - [dompdf debug helper](http://eclecticgeek.com/dompdf/debug.php) (provided by @bsweeney)
50
+ - Include the HTML/CSS inside the bug report, with
51
+ [code highlighting](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#wiki-code).
52
+
53
+ ## Contributing code
54
+
55
+ - Make sure you have a [GitHub Account](https://github.com/signup/free)
56
+ - Fork [dompdf](https://github.com/dompdf/dompdf/)
57
+ ([how to fork a repo](https://help.github.com/articles/fork-a-repo))
58
+ - Make your changes
59
+ - Add a simple test file in `www/test/`, with a comprehensive name.
60
+ - Submit a pull request
61
+ ([how to create a pull request](https://help.github.com/articles/fork-a-repo))
62
+
63
+ ### Coding standards
64
+
65
+ - 2 spaces per indentation level, no tabs.
66
+ - spaces inside `if` like this:
67
+ ```php
68
+ if ( $foo == "bar" ) {
69
+ //
70
+ }
71
+ ```
72
+ - booleans in lowercase
73
+ - opening braces *always* on the same line
dompdf/LICENSE.LGPL ADDED
@@ -0,0 +1,456 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 2.1, February 1999
3
+
4
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
5
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ [This is the first released version of the Lesser GPL. It also counts
10
+ as the successor of the GNU Library Public License, version 2, hence
11
+ the version number 2.1.]
12
+
13
+ Preamble
14
+
15
+ The licenses for most software are designed to take away your
16
+ freedom to share and change it. By contrast, the GNU General Public
17
+ Licenses are intended to guarantee your freedom to share and change
18
+ free software--to make sure the software is free for all its users.
19
+
20
+ This license, the Lesser General Public License, applies to some
21
+ specially designated software packages--typically libraries--of the
22
+ Free Software Foundation and other authors who decide to use it. You
23
+ can use it too, but we suggest you first think carefully about whether
24
+ this license or the ordinary General Public License is the better
25
+ strategy to use in any particular case, based on the explanations below.
26
+
27
+ When we speak of free software, we are referring to freedom of use,
28
+ not price. Our General Public Licenses are designed to make sure that
29
+ you have the freedom to distribute copies of free software (and charge
30
+ for this service if you wish); that you receive source code or can get
31
+ it if you want it; that you can change the software and use pieces of
32
+ it in new free programs; and that you are informed that you can do
33
+ these things.
34
+
35
+ To protect your rights, we need to make restrictions that forbid
36
+ distributors to deny you these rights or to ask you to surrender these
37
+ rights. These restrictions translate to certain responsibilities for
38
+ you if you distribute copies of the library or if you modify it.
39
+
40
+ For example, if you distribute copies of the library, whether gratis
41
+ or for a fee, you must give the recipients all the rights that we gave
42
+ you. You must make sure that they, too, receive or can get the source
43
+ code. If you link other code with the library, you must provide
44
+ complete object files to the recipients, so that they can relink them
45
+ with the library after making changes to the library and recompiling
46
+ it. And you must show them these terms so they know their rights.
47
+
48
+ We protect your rights with a two-step method: (1) we copyright the
49
+ library, and (2) we offer you this license, which gives you legal
50
+ permission to copy, distribute and/or modify the library.
51
+
52
+ To protect each distributor, we want to make it very clear that
53
+ there is no warranty for the free library. Also, if the library is
54
+ modified by someone else and passed on, the recipients should know
55
+ that what they have is not the original version, so that the original
56
+ author's reputation will not be affected by problems that might be
57
+ introduced by others.
58
+
59
+ Finally, software patents pose a constant threat to the existence of
60
+ any free program. We wish to make sure that a company cannot
61
+ effectively restrict the users of a free program by obtaining a
62
+ restrictive license from a patent holder. Therefore, we insist that
63
+ any patent license obtained for a version of the library must be
64
+ consistent with the full freedom of use specified in this license.
65
+
66
+ Most GNU software, including some libraries, is covered by the
67
+ ordinary GNU General Public License. This license, the GNU Lesser
68
+ General Public License, applies to certain designated libraries, and
69
+ is quite different from the ordinary General Public License. We use
70
+ this license for certain libraries in order to permit linking those
71
+ libraries into non-free programs.
72
+
73
+ When a program is linked with a library, whether statically or using
74
+ a shared library, the combination of the two is legally speaking a
75
+ combined work, a derivative of the original library. The ordinary
76
+ General Public License therefore permits such linking only if the
77
+ entire combination fits its criteria of freedom. The Lesser General
78
+ Public License permits more lax criteria for linking other code with
79
+ the library.
80
+
81
+ We call this license the "Lesser" General Public License because it
82
+ does Less to protect the user's freedom than the ordinary General
83
+ Public License. It also provides other free software developers Less
84
+ of an advantage over competing non-free programs. These disadvantages
85
+ are the reason we use the ordinary General Public License for many
86
+ libraries. However, the Lesser license provides advantages in certain
87
+ special circumstances.
88
+
89
+ For example, on rare occasions, there may be a special need to
90
+ encourage the widest possible use of a certain library, so that it becomes
91
+ a de-facto standard. To achieve this, non-free programs must be
92
+ allowed to use the library. A more frequent case is that a free
93
+ library does the same job as widely used non-free libraries. In this
94
+ case, there is little to gain by limiting the free library to free
95
+ software only, so we use the Lesser General Public License.
96
+
97
+ In other cases, permission to use a particular library in non-free
98
+ programs enables a greater number of people to use a large body of
99
+ free software. For example, permission to use the GNU C Library in
100
+ non-free programs enables many more people to use the whole GNU
101
+ operating system, as well as its variant, the GNU/Linux operating
102
+ system.
103
+
104
+ Although the Lesser General Public License is Less protective of the
105
+ users' freedom, it does ensure that the user of a program that is
106
+ linked with the Library has the freedom and the wherewithal to run
107
+ that program using a modified version of the Library.
108
+
109
+ The precise terms and conditions for copying, distribution and
110
+ modification follow. Pay close attention to the difference between a
111
+ "work based on the library" and a "work that uses the library". The
112
+ former contains code derived from the library, whereas the latter must
113
+ be combined with the library in order to run.
114
+
115
+ GNU LESSER GENERAL PUBLIC LICENSE
116
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
117
+
118
+ 0. This License Agreement applies to any software library or other
119
+ program which contains a notice placed by the copyright holder or
120
+ other authorized party saying it may be distributed under the terms of
121
+ this Lesser General Public License (also called "this License").
122
+ Each licensee is addressed as "you".
123
+
124
+ A "library" means a collection of software functions and/or data
125
+ prepared so as to be conveniently linked with application programs
126
+ (which use some of those functions and data) to form executables.
127
+
128
+ The "Library", below, refers to any such software library or work
129
+ which has been distributed under these terms. A "work based on the
130
+ Library" means either the Library or any derivative work under
131
+ copyright law: that is to say, a work containing the Library or a
132
+ portion of it, either verbatim or with modifications and/or translated
133
+ straightforwardly into another language. (Hereinafter, translation is
134
+ included without limitation in the term "modification".)
135
+
136
+ "Source code" for a work means the preferred form of the work for
137
+ making modifications to it. For a library, complete source code means
138
+ all the source code for all modules it contains, plus any associated
139
+ interface definition files, plus the scripts used to control compilation
140
+ and installation of the library.
141
+
142
+ Activities other than copying, distribution and modification are not
143
+ covered by this License; they are outside its scope. The act of
144
+ running a program using the Library is not restricted, and output from
145
+ such a program is covered only if its contents constitute a work based
146
+ on the Library (independent of the use of the Library in a tool for
147
+ writing it). Whether that is true depends on what the Library does
148
+ and what the program that uses the Library does.
149
+
150
+ 1. You may copy and distribute verbatim copies of the Library's
151
+ complete source code as you receive it, in any medium, provided that
152
+ you conspicuously and appropriately publish on each copy an
153
+ appropriate copyright notice and disclaimer of warranty; keep intact
154
+ all the notices that refer to this License and to the absence of any
155
+ warranty; and distribute a copy of this License along with the
156
+ Library.
157
+
158
+ You may charge a fee for the physical act of transferring a copy,
159
+ and you may at your option offer warranty protection in exchange for a
160
+ fee.
161
+
162
+ 2. You may modify your copy or copies of the Library or any portion
163
+ of it, thus forming a work based on the Library, and copy and
164
+ distribute such modifications or work under the terms of Section 1
165
+ above, provided that you also meet all of these conditions:
166
+
167
+ a) The modified work must itself be a software library.
168
+
169
+ b) You must cause the files modified to carry prominent notices
170
+ stating that you changed the files and the date of any change.
171
+
172
+ c) You must cause the whole of the work to be licensed at no
173
+ charge to all third parties under the terms of this License.
174
+
175
+ d) If a facility in the modified Library refers to a function or a
176
+ table of data to be supplied by an application program that uses
177
+ the facility, other than as an argument passed when the facility
178
+ is invoked, then you must make a good faith effort to ensure that,
179
+ in the event an application does not supply such function or
180
+ table, the facility still operates, and performs whatever part of
181
+ its purpose remains meaningful.
182
+
183
+ (For example, a function in a library to compute square roots has
184
+ a purpose that is entirely well-defined independent of the
185
+ application. Therefore, Subsection 2d requires that any
186
+ application-supplied function or table used by this function must
187
+ be optional: if the application does not supply it, the square
188
+ root function must still compute square roots.)
189
+
190
+ These requirements apply to the modified work as a whole. If
191
+ identifiable sections of that work are not derived from the Library,
192
+ and can be reasonably considered independent and separate works in
193
+ themselves, then this License, and its terms, do not apply to those
194
+ sections when you distribute them as separate works. But when you
195
+ distribute the same sections as part of a whole which is a work based
196
+ on the Library, the distribution of the whole must be on the terms of
197
+ this License, whose permissions for other licensees extend to the
198
+ entire whole, and thus to each and every part regardless of who wrote
199
+ it.
200
+
201
+ Thus, it is not the intent of this section to claim rights or contest
202
+ your rights to work written entirely by you; rather, the intent is to
203
+ exercise the right to control the distribution of derivative or
204
+ collective works based on the Library.
205
+
206
+ In addition, mere aggregation of another work not based on the Library
207
+ with the Library (or with a work based on the Library) on a volume of
208
+ a storage or distribution medium does not bring the other work under
209
+ the scope of this License.
210
+
211
+ 3. You may opt to apply the terms of the ordinary GNU General Public
212
+ License instead of this License to a given copy of the Library. To do
213
+ this, you must alter all the notices that refer to this License, so
214
+ that they refer to the ordinary GNU General Public License, version 2,
215
+ instead of to this License. (If a newer version than version 2 of the
216
+ ordinary GNU General Public License has appeared, then you can specify
217
+ that version instead if you wish.) Do not make any other change in
218
+ these notices.
219
+
220
+ Once this change is made in a given copy, it is irreversible for
221
+ that copy, so the ordinary GNU General Public License applies to all
222
+ subsequent copies and derivative works made from that copy.
223
+
224
+ This option is useful when you wish to copy part of the code of
225
+ the Library into a program that is not a library.
226
+
227
+ 4. You may copy and distribute the Library (or a portion or
228
+ derivative of it, under Section 2) in object code or executable form
229
+ under the terms of Sections 1 and 2 above provided that you accompany
230
+ it with the complete corresponding machine-readable source code, which
231
+ must be distributed under the terms of Sections 1 and 2 above on a
232
+ medium customarily used for software interchange.
233
+
234
+ If distribution of object code is made by offering access to copy
235
+ from a designated place, then offering equivalent access to copy the
236
+ source code from the same place satisfies the requirement to
237
+ distribute the source code, even though third parties are not
238
+ compelled to copy the source along with the object code.
239
+
240
+ 5. A program that contains no derivative of any portion of the
241
+ Library, but is designed to work with the Library by being compiled or
242
+ linked with it, is called a "work that uses the Library". Such a
243
+ work, in isolation, is not a derivative work of the Library, and
244
+ therefore falls outside the scope of this License.
245
+
246
+ However, linking a "work that uses the Library" with the Library
247
+ creates an executable that is a derivative of the Library (because it
248
+ contains portions of the Library), rather than a "work that uses the
249
+ library". The executable is therefore covered by this License.
250
+ Section 6 states terms for distribution of such executables.
251
+
252
+ When a "work that uses the Library" uses material from a header file
253
+ that is part of the Library, the object code for the work may be a
254
+ derivative work of the Library even though the source code is not.
255
+ Whether this is true is especially significant if the work can be
256
+ linked without the Library, or if the work is itself a library. The
257
+ threshold for this to be true is not precisely defined by law.
258
+
259
+ If such an object file uses only numerical parameters, data
260
+ structure layouts and accessors, and small macros and small inline
261
+ functions (ten lines or less in length), then the use of the object
262
+ file is unrestricted, regardless of whether it is legally a derivative
263
+ work. (Executables containing this object code plus portions of the
264
+ Library will still fall under Section 6.)
265
+
266
+ Otherwise, if the work is a derivative of the Library, you may
267
+ distribute the object code for the work under the terms of Section 6.
268
+ Any executables containing that work also fall under Section 6,
269
+ whether or not they are linked directly with the Library itself.
270
+
271
+ 6. As an exception to the Sections above, you may also combine or
272
+ link a "work that uses the Library" with the Library to produce a
273
+ work containing portions of the Library, and distribute that work
274
+ under terms of your choice, provided that the terms permit
275
+ modification of the work for the customer's own use and reverse
276
+ engineering for debugging such modifications.
277
+
278
+ You must give prominent notice with each copy of the work that the
279
+ Library is used in it and that the Library and its use are covered by
280
+ this License. You must supply a copy of this License. If the work
281
+ during execution displays copyright notices, you must include the
282
+ copyright notice for the Library among them, as well as a reference
283
+ directing the user to the copy of this License. Also, you must do one
284
+ of these things:
285
+
286
+ a) Accompany the work with the complete corresponding
287
+ machine-readable source code for the Library including whatever
288
+ changes were used in the work (which must be distributed under
289
+ Sections 1 and 2 above); and, if the work is an executable linked
290
+ with the Library, with the complete machine-readable "work that
291
+ uses the Library", as object code and/or source code, so that the
292
+ user can modify the Library and then relink to produce a modified
293
+ executable containing the modified Library. (It is understood
294
+ that the user who changes the contents of definitions files in the
295
+ Library will not necessarily be able to recompile the application
296
+ to use the modified definitions.)
297
+
298
+ b) Use a suitable shared library mechanism for linking with the
299
+ Library. A suitable mechanism is one that (1) uses at run time a
300
+ copy of the library already present on the user's computer system,
301
+ rather than copying library functions into the executable, and (2)
302
+ will operate properly with a modified version of the library, if
303
+ the user installs one, as long as the modified version is
304
+ interface-compatible with the version that the work was made with.
305
+
306
+ c) Accompany the work with a written offer, valid for at
307
+ least three years, to give the same user the materials
308
+ specified in Subsection 6a, above, for a charge no more
309
+ than the cost of performing this distribution.
310
+
311
+ d) If distribution of the work is made by offering access to copy
312
+ from a designated place, offer equivalent access to copy the above
313
+ specified materials from the same place.
314
+
315
+ e) Verify that the user has already received a copy of these
316
+ materials or that you have already sent this user a copy.
317
+
318
+ For an executable, the required form of the "work that uses the
319
+ Library" must include any data and utility programs needed for
320
+ reproducing the executable from it. However, as a special exception,
321
+ the materials to be distributed need not include anything that is
322
+ normally distributed (in either source or binary form) with the major
323
+ components (compiler, kernel, and so on) of the operating system on
324
+ which the executable runs, unless that component itself accompanies
325
+ the executable.
326
+
327
+ It may happen that this requirement contradicts the license
328
+ restrictions of other proprietary libraries that do not normally
329
+ accompany the operating system. Such a contradiction means you cannot
330
+ use both them and the Library together in an executable that you
331
+ distribute.
332
+
333
+ 7. You may place library facilities that are a work based on the
334
+ Library side-by-side in a single library together with other library
335
+ facilities not covered by this License, and distribute such a combined
336
+ library, provided that the separate distribution of the work based on
337
+ the Library and of the other library facilities is otherwise
338
+ permitted, and provided that you do these two things:
339
+
340
+ a) Accompany the combined library with a copy of the same work
341
+ based on the Library, uncombined with any other library
342
+ facilities. This must be distributed under the terms of the
343
+ Sections above.
344
+
345
+ b) Give prominent notice with the combined library of the fact
346
+ that part of it is a work based on the Library, and explaining
347
+ where to find the accompanying uncombined form of the same work.
348
+
349
+ 8. You may not copy, modify, sublicense, link with, or distribute
350
+ the Library except as expressly provided under this License. Any
351
+ attempt otherwise to copy, modify, sublicense, link with, or
352
+ distribute the Library is void, and will automatically terminate your
353
+ rights under this License. However, parties who have received copies,
354
+ or rights, from you under this License will not have their licenses
355
+ terminated so long as such parties remain in full compliance.
356
+
357
+ 9. You are not required to accept this License, since you have not
358
+ signed it. However, nothing else grants you permission to modify or
359
+ distribute the Library or its derivative works. These actions are
360
+ prohibited by law if you do not accept this License. Therefore, by
361
+ modifying or distributing the Library (or any work based on the
362
+ Library), you indicate your acceptance of this License to do so, and
363
+ all its terms and conditions for copying, distributing or modifying
364
+ the Library or works based on it.
365
+
366
+ 10. Each time you redistribute the Library (or any work based on the
367
+ Library), the recipient automatically receives a license from the
368
+ original licensor to copy, distribute, link with or modify the Library
369
+ subject to these terms and conditions. You may not impose any further
370
+ restrictions on the recipients' exercise of the rights granted herein.
371
+ You are not responsible for enforcing compliance by third parties with
372
+ this License.
373
+
374
+ 11. If, as a consequence of a court judgment or allegation of patent
375
+ infringement or for any other reason (not limited to patent issues),
376
+ conditions are imposed on you (whether by court order, agreement or
377
+ otherwise) that contradict the conditions of this License, they do not
378
+ excuse you from the conditions of this License. If you cannot
379
+ distribute so as to satisfy simultaneously your obligations under this
380
+ License and any other pertinent obligations, then as a consequence you
381
+ may not distribute the Library at all. For example, if a patent
382
+ license would not permit royalty-free redistribution of the Library by
383
+ all those who receive copies directly or indirectly through you, then
384
+ the only way you could satisfy both it and this License would be to
385
+ refrain entirely from distribution of the Library.
386
+
387
+ If any portion of this section is held invalid or unenforceable under any
388
+ particular circumstance, the balance of the section is intended to apply,
389
+ and the section as a whole is intended to apply in other circumstances.
390
+
391
+ It is not the purpose of this section to induce you to infringe any
392
+ patents or other property right claims or to contest validity of any
393
+ such claims; this section has the sole purpose of protecting the
394
+ integrity of the free software distribution system which is
395
+ implemented by public license practices. Many people have made
396
+ generous contributions to the wide range of software distributed
397
+ through that system in reliance on consistent application of that
398
+ system; it is up to the author/donor to decide if he or she is willing
399
+ to distribute software through any other system and a licensee cannot
400
+ impose that choice.
401
+
402
+ This section is intended to make thoroughly clear what is believed to
403
+ be a consequence of the rest of this License.
404
+
405
+ 12. If the distribution and/or use of the Library is restricted in
406
+ certain countries either by patents or by copyrighted interfaces, the
407
+ original copyright holder who places the Library under this License may add
408
+ an explicit geographical distribution limitation excluding those countries,
409
+ so that distribution is permitted only in or among countries not thus
410
+ excluded. In such case, this License incorporates the limitation as if
411
+ written in the body of this License.
412
+
413
+ 13. The Free Software Foundation may publish revised and/or new
414
+ versions of the Lesser General Public License from time to time.
415
+ Such new versions will be similar in spirit to the present version,
416
+ but may differ in detail to address new problems or concerns.
417
+
418
+ Each version is given a distinguishing version number. If the Library
419
+ specifies a version number of this License which applies to it and
420
+ "any later version", you have the option of following the terms and
421
+ conditions either of that version or of any later version published by
422
+ the Free Software Foundation. If the Library does not specify a
423
+ license version number, you may choose any version ever published by
424
+ the Free Software Foundation.
425
+
426
+ 14. If you wish to incorporate parts of the Library into other free
427
+ programs whose distribution conditions are incompatible with these,
428
+ write to the author to ask for permission. For software which is
429
+ copyrighted by the Free Software Foundation, write to the Free
430
+ Software Foundation; we sometimes make exceptions for this. Our
431
+ decision will be guided by the two goals of preserving the free status
432
+ of all derivatives of our free software and of promoting the sharing
433
+ and reuse of software generally.
434
+
435
+ NO WARRANTY
436
+
437
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
438
+ WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
439
+ EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
440
+ OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
441
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
442
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
443
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
444
+ LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
445
+ THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
446
+
447
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
448
+ WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
449
+ AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
450
+ FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
451
+ CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
452
+ LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
453
+ RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
454
+ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
455
+ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
456
+ DAMAGES.
dompdf/README.md ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ **dompdf is an HTML to PDF converter**. At its heart, dompdf is (mostly) [CSS 2.1](https://github.com/HNygard/dompdf/tree/master/dompdf/lib) compliant HTML layout and rendering engine written in PHP. It is a style-driven renderer: it will download and read external stylesheets, inline style tags, and the style attributes of individual HTML elements. It also supports most presentational HTML attributes.
2
+
3
+ PDF rendering is currently provided either by [PDFLib](http://www.pdflib.com/) or by a bundled version the R&OS CPDF class written by Wayne Munro. (Some important changes have been made to the R&OS class, however). In order to use PDFLib with dompdf, the [PDFLib PECL extension](http://pecl.php.net/package/pdflib) is required. Using PDFLib improves performance and reduces the memory requirements of dompdf somewhat, while the R&OS CPDF class, though slightly slower, eliminates any dependencies on external PDF libraries.
4
+
5
+ [![Follow us on Twitter](http://twitter-badges.s3.amazonaws.com/twitter-a.png)](http://www.twitter.com/dompdf)
6
+ [![Follow us on Google+](https://ssl.gstatic.com/images/icons/gplus-32.png)](https://plus.google.com/108710008521858993320?prsrc=3)
7
+
8
+ Features
9
+ ========
10
+ * handles most CSS 2.1 and a few CSS3 properties, including @import, @media & @page rules
11
+ * supports most presentational HTML 4.0 attributes
12
+ * supports external stylesheets, either local or through http/ftp (via fopen-wrappers)
13
+ * supports complex tables, including row & column spans, separate & collapsed border models, individual cell styling
14
+ * image support (gif, png (8, 24 and 32 bit with alpha channel), bmp & jpeg)
15
+ * no dependencies on external PDF libraries, thanks to the R&OS PDF class
16
+ * inline PHP support
17
+
18
+ Requirements
19
+ ============
20
+ * PHP 5.0+ (5.3 recommended)
21
+ * MBString extension
22
+ * DOM extension (bundled with PHP 5)
23
+ * Some fonts. PDFs internally support Helvetica, Times-Roman, Courier & Zapf-Dingbats, but if you wish to use other fonts you will need to install some fonts. dompdf supports the same fonts as the underlying R&OS PDF class: Type 1 (.pfb with the corresponding .afm) and TrueType (.ttf). At the minimum, you should probably have the Microsoft core fonts. See the font installation instructions.
24
+
25
+ Limitations (Known Issues)
26
+ ==========================
27
+ * not particularly tolerant to poorly-formed HTML input (using Tidy first may help).
28
+ * large files or large tables can take a while to render
29
+ * CSS float is not supported (but is in the works).
30
+ * If you find this project useful, please consider making a donation.
31
+
32
+ (Any funds donated will be used to help further development on this project.)
33
+ [![Donate button](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](http://goo.gl/DSvWf)
dompdf/changelog.txt ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ DOMPDF 0.6.0 beta 3 Release Notes
3
+
4
+
5
+ New Features
6
+
7
+
8
+ HTML/CSS/Images support
9
+
10
+ * Limited support for CSS float (disabled by default). See
11
+ DOMPDF_ENABLE_CSS_FLOAT
12
+ <http://code.google.com/p/dompdf/source/browse/tags/dompdf_0-6-0_beta3/dompdf/dompdf_config.inc.php#301>(r407,
13
+ r408, r415, r438, r457, r459, r471)
14
+ * Support for nth-child selectors (r407, r419)
15
+ * Support for @font-face (r407, r413)
16
+ * Font sub-setting now available (disabled by default). See
17
+ DOMPDF_ENABLE_FONTSUBSETTING
18
+ <http://code.google.com/p/dompdf/source/browse/tags/dompdf_0-6-0_beta3/dompdf/dompdf_config.inc.php#134>
19
+ (r466, r468, r469)
20
+ * Added an HTML5 Parser to enable improved document parsing/correction
21
+ (disabled by default). See DOMPDF_ENABLE_HTML5PARSER
22
+ <http://code.google.com/p/dompdf/source/browse/tags/dompdf_0-6-0_beta3/dompdf/dompdf_config.inc.php#316>
23
+ (r429, r430, r431, r441)
24
+ * Added support for ID in anchors tags (r373)
25
+ * Added a message for broken images and updated the broken_image.png
26
+ file to
27
+ something less aggressive (r377)
28
+ * Added support for transparent PNG in background-image and improved
29
+ background-image handling (r380, r404, r450, r453)
30
+ * Improved absolute positioning (r387, r409, r459, r460)
31
+ * Added support for the "rem" CSS unit (r389)
32
+ * Improved support for the "ex" CSS unit (r390)
33
+ * When parsing tables, TR elements not contained by TBODY, TFOOT, or
34
+ THEAD are automatically encapsulated by TBODY (r390)
35
+ * Added support for the CSS declaration word-wrap: break-word (r391)
36
+ * Added support for @page :left, :right, :first, :odd, and :even (r393)
37
+ * Added support for CSS visibility and empty-cells properties (r393)
38
+ * Type selectors (e.g. h1) are now case insensitive (r417)
39
+ * Image type detection is now based on file header instead of filename
40
+ extension (r422)
41
+ * Added support for HTML5-style charset metatag (<meta
42
+ charset=?utf-8?>) (r430)
43
+ * Added support for nested CSS counters (r438)
44
+ * Replaced TTF2UFM with php-font-lib and remove all dependencies on
45
+ TTF2UFM (r447)
46
+ * Table columns widths are now consistent across pages (r462)
47
+ * Added limited support for table captions (r456)
48
+ * Reduced rendering time by using caches (r469)
49
+
50
+
51
+ Installation / configuration / debugging
52
+
53
+ * Added frame (i.e. discreet document element) count to the sample
54
+ website debugger output (r399)
55
+ * DOMPDF_ENABLE_REMOTE is no longer needed for stylesheet references
56
+ that use a URL with domain component (r407)
57
+ * Added a ready-to-use web-based font installer to the sample website
58
+ (www/fonts.php) (r417, r418)
59
+ * Added the Unicode-compatible DejaVu Fonts <http://dejavu-fonts.org>
60
+ as part of the base installation (r388)
61
+
62
+
63
+ Major bug fixes
64
+
65
+ * Fixes compatibility with the Symfony framework autoloader (disabled
66
+ by default). See DOMPDF_AUTOLOAD_PREPEND
67
+ <http://code.google.com/p/dompdf/source/browse/tags/dompdf_0-6-0_beta3/dompdf/dompdf_config.inc.php#310>
68
+ (r374)
69
+ * Fixes errors in how margins were collapsed between siblings (r375)
70
+ * Improves the way lines are aligned vertically (see the
71
+ css_baseline example) (r375)
72
+ * Corrects the bounding box used for drawing backgrounds (r377)
73
+ * Fixes the z-index rendering process (r377, r378, r379, r393)
74
+ * Adds support for color styling inheritance (r390)
75
+ * Fixes bugs with nested tables and HTML attributes (r393)
76
+ * Fixes handling of URLs with non-ascii chars in the CPDF adapter (r394)
77
+ * Fixes a rgb()-style color parsing bug (r402)
78
+ * Fixes RLE4 compressed bitmap image support (r405)
79
+ * Fixes bug that caused generated content to occasionally display
80
+ multiple times (r406)
81
+ * Improves background image clipping (r417)
82
+ * Fixes table layout bug caused by zero-height rows (r464)
83
+ * Fixes layout bug caused by 100% width tables centered with margin
84
+ auto (r465)
85
+
86
+ For a full list of modifications since DOMPDF 0.6.0 beta 2 see the
87
+ changes listed on this page of the repository changelog
88
+ <http://code.google.com/p/dompdf/source/list?num=88&start=472&path=/trunk>.
89
+
90
+
91
+ Known Issues
92
+
93
+ * Table cells cannot be split over multiple pages
94
+ * CSS float support is not yet perfected
95
+
96
+ For a full list of known issues, see the issue tracker
97
+ <http://code.google.com/p/dompdf/issues/list>.
98
+
99
+
100
+ Installation Notes
101
+
102
+ * Starting with DOMPDF 0.6.0 the dompdf.php script will no longer
103
+ allow conversion of HTML document on the local file system that are
104
+ located outside of the path specified by DOMPDF_CHROOT
105
+ <http://code.google.com/p/dompdf/source/browse/tags/dompdf_0-6-0_beta3/dompdf/dompdf_config.inc.php#109>
106
+ * If you are installing DOMPDF on top of an existing installation you
107
+ should remove any existing font metrics. This can be done manually
108
+ or through the sample website (www/fonts.php).
109
+ * When upgrading to a new version of DOMPDF you must replace
110
+ dompdf_config.inc.php with the new one. To simplify the upgrade
111
+ process you can store your configuration settings in
112
+ dompdf_config.custom.inc.php (which does not need to be overwritten).
113
+
114
+ ------------------------------------------------------------------------
115
+
116
+
117
+ DOMPDF 0.6.0 beta 2
118
+
119
+
120
+ New Features
121
+
122
+
123
+ HTML/CSS/Images support
124
+
125
+ * CSS3: opacity, 2D transforms
126
+ * CSS2: outline, letter-spacing, z-index, position: relative,
127
+ overflow: hidden
128
+ * CSS Pseudo elements :before and :after with generated content
129
+ * CSS2 pseudo-selectors (last-child, disabled, checked, enabled)
130
+ * CSS3 attribute selectors (ends-width, starts-width, contains)
131
+ * Improves absolute positioning
132
+ * Adds fixed positioning
133
+ * CMYK colors and CMYK Jpeg images
134
+ * 32bit PNG with alpha channel (Cpdf backend)
135
+ * BMP images (8, 24 and 32 bit)
136
+ * Adds support for image embedding via ?data? URI
137
+ * Adds support for ordered list
138
+ * Adds support for embedding PDF JavaScript
139
+ * Uses the HTML document title element and certain meta tags to
140
+ populate the PDF?s meta information (title, author, keywords and
141
+ subject)
142
+ * Uses the ?alt? attribute of an image when the image is inaccessible
143
+ * Supports loading system fonts
144
+
145
+
146
+ Installation / configuration
147
+
148
+ * The demo page now shows the HTML file and the PDF document in an iframe
149
+ * Adds a setup/configuration tool that provides information about the
150
+ server configuration, dompdf parameters, and installed fonts.
151
+ * The font metrics cache files can now be cleared using the
152
+ setup/config tool
153
+ * Adds a debug tool that shows side-by-side the HTML file, the
154
+ rendered PDF, and a console showing memory consumption, rendering
155
+ time, warning, and debug messages
156
+ * Adds examples showing new features
157
+ * Moves ttf2ufm out of the DOMPDF code repository and into an external
158
+ project <http://code.google.com/p/ttf2ufm/>
159
+ * Disables inline PHP support by default
160
+ * Disables direct input in the examples page for non-localhost access
161
+ * Adds configuration option to help debugging (see DEBUG_LAYOUT) which
162
+ draws rectangles around the different types of blocks and frames
163
+
164
+
165
+ Major bug fixes
166
+
167
+ * Addresses memory leaks from running eval() on the font metrics cache
168
+ * Reduces memory consumption caused by the font metrics (when using
169
+ the Cpdf backend)
170
+ * Updates text wrapping to prevent splitting text into more lines than
171
+ needed (issue 198
172
+ <http://code.google.com/p/dompdf/issues/detail?id=198>)
173
+ * Implements a check against an infinite loop caused by table cells
174
+ larger than a page
175
+ * Improves text height and width calculations as well as placement
176
+ (improves, for example, justified text rendering for text that is
177
+ not iso-8859-1)
178
+ * Updates the fallback MBString functions
179
+ * Supports PHP 5.3 and includes improved compatibility with older
180
+ versions of PHP 5
181
+ * Improves image placement
182
+ * Addresses problems with table flow caused by empty table cells
183
+ * Addresses warning/errors caused by unrecognized CSS rules or selectors
184
+
185
+ For a full list of modifications since DOMPDF 0.6.0 beta 1 see the
186
+ repository changelog
187
+ <http://code.google.com/p/dompdf/source/list?path=/tags/dompdf_0-6-0_beta2&num=104>
188
+
189
+
190
+ Known Issues
191
+
192
+ * Table cells cannot be split over multiple pages
193
+ * Column widths of tables that span more than one page may not be
194
+ consistent across pages
195
+
196
+
197
+ Installation Notes
198
+
199
+ * Starting with dompdf 0.6.0 dompdf.php will no longer allow
200
+ conversion of HTML document on the local file system that are
201
+ located outside of the path specified by DOMPDF_CHROOT
202
+ * The format of the font metrics cache has changed as of this release.
203
+ You should manually remove any existing font metrics prior to
204
+ upgrading or use the setup/configuration tool to do so immediately
205
+ after.
206
+ * Inline PHP is *disabled* by default now (see DOMPDF_ENABLE_PHP)
207
+ * Because additional configuration options have been added you will
208
+ need to replace your dompdf_config.inc.php file with the new one.
209
+ You may modify this file or copy your configuration settings to
210
+ dompdf_config.custom.inc.php.
211
+
212
+
213
+
dompdf/composer.json ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "dompdf/dompdf",
3
+ "type": "library",
4
+ "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
5
+ "homepage": "https://github.com/dompdf/dompdf",
6
+ "license": "LGPL",
7
+ "authors": [
8
+ {
9
+ "name": "Fabien Ménager",
10
+ "email": "fabien.menager@gmail.com"
11
+ },
12
+ {
13
+ "name": "Brian Sweeney",
14
+ "email": "eclecticgeek@gmail.com"
15
+ }
16
+ ],
17
+ "autoload": {
18
+ "classmap": ["include/"]
19
+ },
20
+ "require": {
21
+ "phenx/php-font-lib": "0.1.*"
22
+ }
23
+ }
dompdf/docblox.dist.xml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <docblox>
3
+ <parser>
4
+ <target>docs</target>
5
+ </parser>
6
+ <transformer>
7
+ <target>docs</target>
8
+ </transformer>
9
+ <files>
10
+ <directory>.</directory>
11
+ <ignore>*/lib/fonts/*</ignore>
12
+ <ignore>*/lib/html5lib/*</ignore>
13
+ <ignore>*/lib/php-font-lib/*</ignore>
14
+ <ignore>*www/*</ignore>
15
+ <ignore>*.svn*</ignore>
16
+ </files>
17
+ <transformations>
18
+ <template name="new_black" />
19
+ </transformations>
20
+ </docblox>
dompdf/dompdf.php ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Command line utility to use dompdf.
4
+ * Can also be used with HTTP GET parameters
5
+ *
6
+ * @package dompdf
7
+ * @link http://www.dompdf.com/
8
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
9
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
10
+ */
11
+
12
+ /**
13
+ * Display command line usage
14
+ */
15
+ function dompdf_usage() {
16
+ $default_paper_size = DOMPDF_DEFAULT_PAPER_SIZE;
17
+
18
+ echo <<<EOD
19
+
20
+ Usage: {$_SERVER["argv"][0]} [options] html_file
21
+
22
+ html_file can be a filename, a url if fopen_wrappers are enabled, or the '-' character to read from standard input.
23
+
24
+ Options:
25
+ -h Show this message
26
+ -l List available paper sizes
27
+ -p size Paper size; something like 'letter', 'A4', 'legal', etc.
28
+ The default is '$default_paper_size'
29
+ -o orientation Either 'portrait' or 'landscape'. Default is 'portrait'
30
+ -b path Set the 'document root' of the html_file.
31
+ Relative urls (for stylesheets) are resolved using this directory.
32
+ Default is the directory of html_file.
33
+ -f file The output filename. Default is the input [html_file].pdf
34
+ -v Verbose: display html parsing warnings and file not found errors.
35
+ -d Very verbose: display oodles of debugging output: every frame
36
+ in the tree printed to stdout.
37
+ -t Comma separated list of debugging types (page-break,reflow,split)
38
+
39
+ EOD;
40
+ exit;
41
+ }
42
+
43
+ /**
44
+ * Parses command line options
45
+ *
46
+ * @return array The command line options
47
+ */
48
+ function getoptions() {
49
+
50
+ $opts = array();
51
+
52
+ if ( $_SERVER["argc"] == 1 )
53
+ return $opts;
54
+
55
+ $i = 1;
56
+ while ($i < $_SERVER["argc"]) {
57
+
58
+ switch ($_SERVER["argv"][$i]) {
59
+
60
+ case "--help":
61
+ case "-h":
62
+ $opts["h"] = true;
63
+ $i++;
64
+ break;
65
+
66
+ case "-l":
67
+ $opts["l"] = true;
68
+ $i++;
69
+ break;
70
+
71
+ case "-p":
72
+ if ( !isset($_SERVER["argv"][$i+1]) )
73
+ die("-p switch requires a size parameter\n");
74
+ $opts["p"] = $_SERVER["argv"][$i+1];
75
+ $i += 2;
76
+ break;
77
+
78
+ case "-o":
79
+ if ( !isset($_SERVER["argv"][$i+1]) )
80
+ die("-o switch requires an orientation parameter\n");
81
+ $opts["o"] = $_SERVER["argv"][$i+1];
82
+ $i += 2;
83
+ break;
84
+
85
+ case "-b":
86
+ if ( !isset($_SERVER["argv"][$i+1]) )
87
+ die("-b switch requires a path parameter\n");
88
+ $opts["b"] = $_SERVER["argv"][$i+1];
89
+ $i += 2;
90
+ break;
91
+
92
+ case "-f":
93
+ if ( !isset($_SERVER["argv"][$i+1]) )
94
+ die("-f switch requires a filename parameter\n");
95
+ $opts["f"] = $_SERVER["argv"][$i+1];
96
+ $i += 2;
97
+ break;
98
+
99
+ case "-v":
100
+ $opts["v"] = true;
101
+ $i++;
102
+ break;
103
+
104
+ case "-d":
105
+ $opts["d"] = true;
106
+ $i++;
107
+ break;
108
+
109
+ case "-t":
110
+ if ( !isset($_SERVER['argv'][$i + 1]) )
111
+ die("-t switch requires a comma separated list of types\n");
112
+ $opts["t"] = $_SERVER['argv'][$i+1];
113
+ $i += 2;
114
+ break;
115
+
116
+ default:
117
+ $opts["filename"] = $_SERVER["argv"][$i];
118
+ $i++;
119
+ break;
120
+ }
121
+
122
+ }
123
+ return $opts;
124
+ }
125
+
126
+ require_once("dompdf_config.inc.php");
127
+ global $_dompdf_show_warnings, $_dompdf_debug, $_DOMPDF_DEBUG_TYPES;
128
+
129
+ $sapi = php_sapi_name();
130
+ $options = array();
131
+
132
+ switch ( $sapi ) {
133
+
134
+ case "cli":
135
+
136
+ $opts = getoptions();
137
+
138
+ if ( isset($opts["h"]) || (!isset($opts["filename"]) && !isset($opts["l"])) ) {
139
+ dompdf_usage();
140
+ exit;
141
+ }
142
+
143
+ if ( isset($opts["l"]) ) {
144
+ echo "\nUnderstood paper sizes:\n";
145
+
146
+ foreach (array_keys(CPDF_Adapter::$PAPER_SIZES) as $size)
147
+ echo " " . mb_strtoupper($size) . "\n";
148
+ exit;
149
+ }
150
+ $file = $opts["filename"];
151
+
152
+ if ( isset($opts["p"]) )
153
+ $paper = $opts["p"];
154
+ else
155
+ $paper = DOMPDF_DEFAULT_PAPER_SIZE;
156
+
157
+ if ( isset($opts["o"]) )
158
+ $orientation = $opts["o"];
159
+ else
160
+ $orientation = "portrait";
161
+
162
+ if ( isset($opts["b"]) )
163
+ $base_path = $opts["b"];
164
+
165
+ if ( isset($opts["f"]) )
166
+ $outfile = $opts["f"];
167
+ else {
168
+ if ( $file === "-" )
169
+ $outfile = "dompdf_out.pdf";
170
+ else
171
+ $outfile = str_ireplace(array(".html", ".htm", ".php"), "", $file) . ".pdf";
172
+ }
173
+
174
+ if ( isset($opts["v"]) )
175
+ $_dompdf_show_warnings = true;
176
+
177
+ if ( isset($opts["d"]) ) {
178
+ $_dompdf_show_warnings = true;
179
+ $_dompdf_debug = true;
180
+ }
181
+
182
+ if ( isset($opts['t']) ) {
183
+ $arr = split(',',$opts['t']);
184
+ $types = array();
185
+ foreach ($arr as $type)
186
+ $types[ trim($type) ] = 1;
187
+ $_DOMPDF_DEBUG_TYPES = $types;
188
+ }
189
+
190
+ $save_file = true;
191
+
192
+ break;
193
+
194
+ default:
195
+
196
+ if ( isset($_GET["input_file"]) )
197
+ $file = rawurldecode($_GET["input_file"]);
198
+ else
199
+ throw new DOMPDF_Exception("An input file is required (i.e. input_file _GET variable).");
200
+
201
+ if ( isset($_GET["paper"]) )
202
+ $paper = rawurldecode($_GET["paper"]);
203
+ else
204
+ $paper = DOMPDF_DEFAULT_PAPER_SIZE;
205
+
206
+ if ( isset($_GET["orientation"]) )
207
+ $orientation = rawurldecode($_GET["orientation"]);
208
+ else
209
+ $orientation = "portrait";
210
+
211
+ if ( isset($_GET["base_path"]) ) {
212
+ $base_path = rawurldecode($_GET["base_path"]);
213
+ $file = $base_path . $file; # Set the input file
214
+ }
215
+
216
+ if ( isset($_GET["options"]) ) {
217
+ $options = $_GET["options"];
218
+ }
219
+
220
+ $file_parts = explode_url($file);
221
+
222
+ /* Check to see if the input file is local and, if so, that the base path falls within that specified by DOMDPF_CHROOT */
223
+ if(($file_parts['protocol'] == '' || $file_parts['protocol'] === 'file://')) {
224
+ $file = realpath($file);
225
+ if ( strpos($file, DOMPDF_CHROOT) !== 0 ) {
226
+ throw new DOMPDF_Exception("Permission denied on $file. The file could not be found under the directory specified by DOMPDF_CHROOT.");
227
+ }
228
+ }
229
+
230
+ $outfile = "dompdf_out.pdf"; # Don't allow them to set the output file
231
+ $save_file = false; # Don't save the file
232
+
233
+ break;
234
+ }
235
+
236
+ $dompdf = new DOMPDF();
237
+
238
+ if ( $file === "-" ) {
239
+ $str = "";
240
+ while ( !feof(STDIN) )
241
+ $str .= fread(STDIN, 4096);
242
+
243
+ $dompdf->load_html($str);
244
+
245
+ } else
246
+ $dompdf->load_html_file($file);
247
+
248
+ if ( isset($base_path) ) {
249
+ $dompdf->set_base_path($base_path);
250
+ }
251
+
252
+ $dompdf->set_paper($paper, $orientation);
253
+
254
+ $dompdf->render();
255
+
256
+ if ( $_dompdf_show_warnings ) {
257
+ global $_dompdf_warnings;
258
+ foreach ($_dompdf_warnings as $msg)
259
+ echo $msg . "\n";
260
+ echo $dompdf->get_canvas()->get_cpdf()->messages;
261
+ flush();
262
+ }
263
+
264
+ if ( $save_file ) {
265
+ // if ( !is_writable($outfile) )
266
+ // throw new DOMPDF_Exception("'$outfile' is not writable.");
267
+ if ( strtolower(DOMPDF_PDF_BACKEND) === "gd" )
268
+ $outfile = str_replace(".pdf", ".png", $outfile);
269
+
270
+ list($proto, $host, $path, $file) = explode_url($outfile);
271
+ if ( $proto != "" ) // i.e. not file://
272
+ $outfile = $file; // just save it locally, FIXME? could save it like wget: ./host/basepath/file
273
+
274
+ $outfile = realpath(dirname($outfile)) . DIRECTORY_SEPARATOR . basename($outfile);
275
+
276
+ if ( strpos($outfile, DOMPDF_CHROOT) !== 0 )
277
+ throw new DOMPDF_Exception("Permission denied.");
278
+
279
+ file_put_contents($outfile, $dompdf->output( array("compress" => 0) ));
280
+ exit(0);
281
+ }
282
+
283
+ if ( !headers_sent() ) {
284
+ $dompdf->stream($outfile, $options);
285
+ }
dompdf/dompdf_config.custom.inc.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //define("DOMPDF_TEMP_DIR", "/tmp");
3
+ //define("DOMPDF_CHROOT", DOMPDF_DIR);
4
+ //define("DOMPDF_UNICODE_ENABLED", false);
5
+ //define("DOMPDF_PDF_BACKEND", "PDFLib");
6
+ //define("DOMPDF_DEFAULT_MEDIA_TYPE", "print");
7
+ //define("DOMPDF_DEFAULT_PAPER_SIZE", "letter");
8
+ //define("DOMPDF_DEFAULT_FONT", "serif");
9
+ //define("DOMPDF_DPI", 72);
10
+ //define("DOMPDF_ENABLE_PHP", true);
11
+ //define("DOMPDF_ENABLE_REMOTE", true);
12
+ //define("DOMPDF_ENABLE_CSS_FLOAT", true);
13
+ //define("DOMPDF_ENABLE_JAVASCRIPT", false);
14
+ //define("DEBUGPNG", true);
15
+ //define("DEBUGKEEPTEMP", true);
16
+ //define("DEBUGCSS", true);
17
+ //define("DEBUG_LAYOUT", true);
18
+ //define("DEBUG_LAYOUT_LINES", false);
19
+ //define("DEBUG_LAYOUT_BLOCKS", false);
20
+ //define("DEBUG_LAYOUT_INLINE", false);
21
+ //define("DOMPDF_FONT_HEIGHT_RATIO", 1.0);
22
+ //define("DEBUG_LAYOUT_PADDINGBOX", false);
23
+ //define("DOMPDF_LOG_OUTPUT_FILE", DOMPDF_FONT_DIR."log.htm");
24
+ //define("DOMPDF_ENABLE_HTML5PARSER", true);
25
+ //define("DOMPDF_ENABLE_FONTSUBSETTING", true);
26
+
27
+ // DOMPDF authentication
28
+ //define("DOMPDF_ADMIN_USERNAME", "user");
29
+ //define("DOMPDF_ADMIN_PASSWORD", "password");
dompdf/dompdf_config.inc.php ADDED
@@ -0,0 +1,401 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ //error_reporting(E_STRICT | E_ALL | E_DEPRECATED);
12
+ //ini_set("display_errors", 1);
13
+
14
+ PHP_VERSION >= 5.0 or die("DOMPDF requires PHP 5.0+");
15
+
16
+ /**
17
+ * The root of your DOMPDF installation
18
+ */
19
+ define("DOMPDF_DIR", str_replace(DIRECTORY_SEPARATOR, '/', realpath(dirname(__FILE__))));
20
+
21
+ /**
22
+ * The location of the DOMPDF include directory
23
+ */
24
+ define("DOMPDF_INC_DIR", DOMPDF_DIR . "/include");
25
+
26
+ /**
27
+ * The location of the DOMPDF lib directory
28
+ */
29
+ define("DOMPDF_LIB_DIR", DOMPDF_DIR . "/lib");
30
+
31
+ /**
32
+ * Some installations don't have $_SERVER['DOCUMENT_ROOT']
33
+ * http://fyneworks.blogspot.com/2007/08/php-documentroot-in-iis-windows-servers.html
34
+ */
35
+ if( !isset($_SERVER['DOCUMENT_ROOT']) ) {
36
+ $path = "";
37
+
38
+ if ( isset($_SERVER['SCRIPT_FILENAME']) )
39
+ $path = $_SERVER['SCRIPT_FILENAME'];
40
+ elseif ( isset($_SERVER['PATH_TRANSLATED']) )
41
+ $path = str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']);
42
+
43
+ $_SERVER['DOCUMENT_ROOT'] = str_replace( '\\', '/', substr($path, 0, 0-strlen($_SERVER['PHP_SELF'])));
44
+ }
45
+
46
+ /** Include the custom config file if it exists */
47
+ if ( file_exists(DOMPDF_DIR . "/dompdf_config.custom.inc.php") ){
48
+ require_once(DOMPDF_DIR . "/dompdf_config.custom.inc.php");
49
+ }
50
+
51
+ //FIXME: Some function definitions rely on the constants defined by DOMPDF. However, might this location prove problematic?
52
+ require_once(DOMPDF_INC_DIR . "/functions.inc.php");
53
+
54
+ /**
55
+ * Username and password used by the configuration utility in www/
56
+ */
57
+ def("DOMPDF_ADMIN_USERNAME", "user");
58
+ def("DOMPDF_ADMIN_PASSWORD", "password");
59
+
60
+ /**
61
+ * The location of the DOMPDF font directory
62
+ *
63
+ * If DOMPDF_FONT_DIR identical to DOMPDF_FONT_CACHE or user executing DOMPDF from the CLI,
64
+ * this directory must be writable by the webserver process ().
65
+ * *Please note the trailing slash.*
66
+ *
67
+ * Notes regarding fonts:
68
+ * Additional .afm font metrics can be added by executing load_font.php from command line.
69
+ *
70
+ * Only the original "Base 14 fonts" are present on all pdf viewers. Additional fonts must
71
+ * be embedded in the pdf file or the PDF may not display correctly. This can significantly
72
+ * increase file size and could violate copyright provisions of a font. Font subsetting is
73
+ * not currently supported.
74
+ *
75
+ * Any font specification in the source HTML is translated to the closest font available
76
+ * in the font directory.
77
+ *
78
+ * The pdf standard "Base 14 fonts" are:
79
+ * Courier, Courier-Bold, Courier-BoldOblique, Courier-Oblique,
80
+ * Helvetica, Helvetica-Bold, Helvetica-BoldOblique, Helvetica-Oblique,
81
+ * Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
82
+ * Symbol,
83
+ * ZapfDingbats,
84
+ *
85
+ * *Please note the trailing slash.*
86
+ */
87
+ def("DOMPDF_FONT_DIR", DOMPDF_DIR . "/lib/fonts/");
88
+
89
+ /**
90
+ * The location of the DOMPDF font cache directory
91
+ *
92
+ * Note this directory must be writable by the webserver process
93
+ * This folder must already exist!
94
+ * It contains the .afm files, on demand parsed, converted to php syntax and cached
95
+ * This folder can be the same as DOMPDF_FONT_DIR
96
+ */
97
+ def("DOMPDF_FONT_CACHE", DOMPDF_FONT_DIR);
98
+
99
+ /**
100
+ * The location of a temporary directory.
101
+ *
102
+ * The directory specified must be writeable by the webserver process.
103
+ * The temporary directory is required to download remote images and when
104
+ * using the PFDLib back end.
105
+ */
106
+ def("DOMPDF_TEMP_DIR", DOMPDF_DIR . "/temp/");
107
+
108
+ /**
109
+ * ==== IMPORTANT ====
110
+ *
111
+ * dompdf's "chroot": Prevents dompdf from accessing system files or other
112
+ * files on the webserver. All local files opened by dompdf must be in a
113
+ * subdirectory of this directory. DO NOT set it to '/' since this could
114
+ * allow an attacker to use dompdf to read any files on the server. This
115
+ * should be an absolute path.
116
+ * This is only checked on command line call by dompdf.php, but not by
117
+ * direct class use like:
118
+ * $dompdf = new DOMPDF(); $dompdf->load_html($htmldata); $dompdf->render(); $pdfdata = $dompdf->output();
119
+ */
120
+ def("DOMPDF_CHROOT", realpath(DOMPDF_DIR));
121
+
122
+ /**
123
+ * Whether to use Unicode fonts or not.
124
+ *
125
+ * When set to true the PDF backend must be set to "CPDF" and fonts must be
126
+ * loaded via load_font.php.
127
+ *
128
+ * When enabled, dompdf can support all Unicode glyphs. Any glyphs used in a
129
+ * document must be present in your fonts, however.
130
+ */
131
+ def("DOMPDF_UNICODE_ENABLED", true);
132
+
133
+ /**
134
+ * Whether to make font subsetting or not.
135
+ */
136
+ def("DOMPDF_ENABLE_FONTSUBSETTING", false);
137
+
138
+ /**
139
+ * The PDF rendering backend to use
140
+ *
141
+ * Valid settings are 'PDFLib', 'CPDF' (the bundled R&OS PDF class), 'GD' and
142
+ * 'auto'. 'auto' will look for PDFLib and use it if found, or if not it will
143
+ * fall back on CPDF. 'GD' renders PDFs to graphic files. {@link
144
+ * Canvas_Factory} ultimately determines which rendering class to instantiate
145
+ * based on this setting.
146
+ *
147
+ * Both PDFLib & CPDF rendering backends provide sufficient rendering
148
+ * capabilities for dompdf, however additional features (e.g. object,
149
+ * image and font support, etc.) differ between backends. Please see
150
+ * {@link PDFLib_Adapter} for more information on the PDFLib backend
151
+ * and {@link CPDF_Adapter} and lib/class.pdf.php for more information
152
+ * on CPDF. Also see the documentation for each backend at the links
153
+ * below.
154
+ *
155
+ * The GD rendering backend is a little different than PDFLib and
156
+ * CPDF. Several features of CPDF and PDFLib are not supported or do
157
+ * not make any sense when creating image files. For example,
158
+ * multiple pages are not supported, nor are PDF 'objects'. Have a
159
+ * look at {@link GD_Adapter} for more information. GD support is new
160
+ * and experimental, so use it at your own risk.
161
+ *
162
+ * @link http://www.pdflib.com
163
+ * @link http://www.ros.co.nz/pdf
164
+ * @link http://www.php.net/image
165
+ */
166
+ def("DOMPDF_PDF_BACKEND", "CPDF");
167
+
168
+ /**
169
+ * PDFlib license key
170
+ *
171
+ * If you are using a licensed, commercial version of PDFlib, specify
172
+ * your license key here. If you are using PDFlib-Lite or are evaluating
173
+ * the commercial version of PDFlib, comment out this setting.
174
+ *
175
+ * @link http://www.pdflib.com
176
+ *
177
+ * If pdflib present in web server and auto or selected explicitely above,
178
+ * a real license code must exist!
179
+ */
180
+ //def("DOMPDF_PDFLIB_LICENSE", "your license key here");
181
+
182
+ /**
183
+ * html target media view which should be rendered into pdf.
184
+ * List of types and parsing rules for future extensions:
185
+ * http://www.w3.org/TR/REC-html40/types.html
186
+ * screen, tty, tv, projection, handheld, print, braille, aural, all
187
+ * Note: aural is deprecated in CSS 2.1 because it is replaced by speech in CSS 3.
188
+ * Note, even though the generated pdf file is intended for print output,
189
+ * the desired content might be different (e.g. screen or projection view of html file).
190
+ * Therefore allow specification of content here.
191
+ */
192
+ def("DOMPDF_DEFAULT_MEDIA_TYPE", "screen");
193
+
194
+ /**
195
+ * The default paper size.
196
+ *
197
+ * North America standard is "letter"; other countries generally "a4"
198
+ *
199
+ * @see CPDF_Adapter::PAPER_SIZES for valid sizes
200
+ */
201
+ def("DOMPDF_DEFAULT_PAPER_SIZE", "a4");
202
+
203
+ /**
204
+ * The default font family
205
+ *
206
+ * Used if no suitable fonts can be found. This must exist in the font folder.
207
+ * @var string
208
+ */
209
+ def("DOMPDF_DEFAULT_FONT", "serif");
210
+
211
+ /**
212
+ * Image DPI setting
213
+ *
214
+ * This setting determines the default DPI setting for images and fonts. The
215
+ * DPI may be overridden for inline images by explictly setting the
216
+ * image's width & height style attributes (i.e. if the image's native
217
+ * width is 600 pixels and you specify the image's width as 72 points,
218
+ * the image will have a DPI of 600 in the rendered PDF. The DPI of
219
+ * background images can not be overridden and is controlled entirely
220
+ * via this parameter.
221
+ *
222
+ * For the purposes of DOMPDF, pixels per inch (PPI) = dots per inch (DPI).
223
+ * If a size in html is given as px (or without unit as image size),
224
+ * this tells the corresponding size in pt.
225
+ * This adjusts the relative sizes to be similar to the rendering of the
226
+ * html page in a reference browser.
227
+ *
228
+ * In pdf, always 1 pt = 1/72 inch
229
+ *
230
+ * Rendering resolution of various browsers in px per inch:
231
+ * Windows Firefox and Internet Explorer:
232
+ * SystemControl->Display properties->FontResolution: Default:96, largefonts:120, custom:?
233
+ * Linux Firefox:
234
+ * about:config *resolution: Default:96
235
+ * (xorg screen dimension in mm and Desktop font dpi settings are ignored)
236
+ *
237
+ * Take care about extra font/image zoom factor of browser.
238
+ *
239
+ * In images, <img> size in pixel attribute, img css style, are overriding
240
+ * the real image dimension in px for rendering.
241
+ *
242
+ * @var int
243
+ */
244
+ def("DOMPDF_DPI", 96);
245
+
246
+ /**
247
+ * Enable inline PHP
248
+ *
249
+ * If this setting is set to true then DOMPDF will automatically evaluate
250
+ * inline PHP contained within <script type="text/php"> ... </script> tags.
251
+ *
252
+ * Enabling this for documents you do not trust (e.g. arbitrary remote html
253
+ * pages) is a security risk. Set this option to false if you wish to process
254
+ * untrusted documents.
255
+ *
256
+ * @var bool
257
+ */
258
+ def("DOMPDF_ENABLE_PHP", false);
259
+
260
+ /**
261
+ * Enable inline Javascript
262
+ *
263
+ * If this setting is set to true then DOMPDF will automatically insert
264
+ * JavaScript code contained within <script type="text/javascript"> ... </script> tags.
265
+ *
266
+ * @var bool
267
+ */
268
+ def("DOMPDF_ENABLE_JAVASCRIPT", true);
269
+
270
+ /**
271
+ * Enable remote file access
272
+ *
273
+ * If this setting is set to true, DOMPDF will access remote sites for
274
+ * images and CSS files as required.
275
+ * This is required for part of test case www/test/image_variants.html through www/examples.php
276
+ *
277
+ * Attention!
278
+ * This can be a security risk, in particular in combination with DOMPDF_ENABLE_PHP and
279
+ * allowing remote access to dompdf.php or on allowing remote html code to be passed to
280
+ * $dompdf = new DOMPDF(); $dompdf->load_html(...);
281
+ * This allows anonymous users to download legally doubtful internet content which on
282
+ * tracing back appears to being downloaded by your server, or allows malicious php code
283
+ * in remote html pages to be executed by your server with your account privileges.
284
+ *
285
+ * @var bool
286
+ */
287
+ def("DOMPDF_ENABLE_REMOTE", true);
288
+
289
+ /**
290
+ * The debug output log
291
+ * @var string
292
+ */
293
+ def("DOMPDF_LOG_OUTPUT_FILE", DOMPDF_FONT_DIR."log.htm");
294
+
295
+ /**
296
+ * A ratio applied to the fonts height to be more like browsers' line height
297
+ */
298
+ def("DOMPDF_FONT_HEIGHT_RATIO", 1.1);
299
+
300
+ /**
301
+ * Enable CSS float
302
+ *
303
+ * Allows people to disabled CSS float support
304
+ * @var bool
305
+ */
306
+ def("DOMPDF_ENABLE_CSS_FLOAT", false);
307
+
308
+ /**
309
+ * Prepend the DOMPDF autoload function the spl_autoload stack
310
+ *
311
+ * @var bool
312
+ */
313
+ def("DOMPDF_AUTOLOAD_PREPEND", false);
314
+
315
+ /**
316
+ * Use the more-than-experimental HTML5 Lib parser
317
+ */
318
+ def("DOMPDF_ENABLE_HTML5PARSER", false);
319
+ require_once(DOMPDF_LIB_DIR . "/html5lib/Parser.php");
320
+
321
+ // ### End of user-configurable options ###
322
+
323
+ // is composer running?
324
+ if (!class_exists("ComposerAutoloaderInit")) {
325
+ if (file_exists(DOMPDF_DIR . "/vendor/autoload.php")) {
326
+ // development mode - composer is installed locally
327
+ require_once(DOMPDF_DIR . "/vendor/autoload.php");
328
+ } else {
329
+ // composer is not installed - use our custom autoloader
330
+ require_once(DOMPDF_INC_DIR . "/autoload.inc.php");
331
+ }
332
+ }
333
+
334
+ // check for php-font-lib
335
+ if (!class_exists('Font')) {
336
+ if (file_exists(DOMPDF_LIB_DIR . "/php-font-lib/classes/font.cls.php")) {
337
+ require_once(DOMPDF_LIB_DIR . "/php-font-lib/classes/font.cls.php");
338
+ } else {
339
+ exit("PHP-font-lib must either be installed via composer or copied to lib/php-font-lib\n");
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Ensure that PHP is working with text internally using UTF8 character encoding.
345
+ */
346
+ mb_internal_encoding('UTF-8');
347
+
348
+ /**
349
+ * Global array of warnings generated by DomDocument parser and
350
+ * stylesheet class
351
+ *
352
+ * @var array
353
+ */
354
+ global $_dompdf_warnings;
355
+ $_dompdf_warnings = array();
356
+
357
+ /**
358
+ * If true, $_dompdf_warnings is dumped on script termination when using
359
+ * dompdf/dompdf.php or after rendering when using the DOMPDF class.
360
+ * When using the class, setting this value to true will prevent you from
361
+ * streaming the PDF.
362
+ *
363
+ * @var bool
364
+ */
365
+ global $_dompdf_show_warnings;
366
+ $_dompdf_show_warnings = false;
367
+
368
+ /**
369
+ * If true, the entire tree is dumped to stdout in dompdf.cls.php.
370
+ * Setting this value to true will prevent you from streaming the PDF.
371
+ *
372
+ * @var bool
373
+ */
374
+ global $_dompdf_debug;
375
+ $_dompdf_debug = false;
376
+
377
+ /**
378
+ * Array of enabled debug message types
379
+ *
380
+ * @var array
381
+ */
382
+ global $_DOMPDF_DEBUG_TYPES;
383
+ $_DOMPDF_DEBUG_TYPES = array(); //array("page-break" => 1);
384
+
385
+ /* Optionally enable different classes of debug output before the pdf content.
386
+ * Visible if displaying pdf as text,
387
+ * E.g. on repeated display of same pdf in browser when pdf is not taken out of
388
+ * the browser cache and the premature output prevents setting of the mime type.
389
+ */
390
+ def('DEBUGPNG', false);
391
+ def('DEBUGKEEPTEMP', false);
392
+ def('DEBUGCSS', false);
393
+
394
+ /* Layout debugging. Will display rectangles around different block levels.
395
+ * Visible in the PDF itself.
396
+ */
397
+ def('DEBUG_LAYOUT', false);
398
+ def('DEBUG_LAYOUT_LINES', true);
399
+ def('DEBUG_LAYOUT_BLOCKS', true);
400
+ def('DEBUG_LAYOUT_INLINE', true);
401
+ def('DEBUG_LAYOUT_PADDINGBOX', true);
dompdf/include/_notes/dwsync.xml ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <dwsync>
3
+ <file name="absolute_positioner.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
4
+ <file name="abstract_renderer.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
5
+ <file name="attribute_translator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
6
+ <file name="autoload.inc.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
7
+ <file name="block_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
8
+ <file name="block_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
9
+ <file name="block_positioner.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
10
+ <file name="block_renderer.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
11
+ <file name="cached_pdf_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
12
+ <file name="canvas.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
13
+ <file name="canvas_factory.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
14
+ <file name="cellmap.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
15
+ <file name="cpdf_adapter.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
16
+ <file name="css_color.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
17
+ <file name="dompdf.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
18
+ <file name="dompdf_exception.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
19
+ <file name="dompdf_image_exception.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
20
+ <file name="file.skel" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
21
+ <file name="fixed_positioner.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
22
+ <file name="font_metrics.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
23
+ <file name="frame.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
24
+ <file name="frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
25
+ <file name="frame_factory.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
26
+ <file name="frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
27
+ <file name="frame_tree.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
28
+ <file name="functions.inc.php" server="blueliquiddesigns.com.au" local="130050847200000000" remote="130050847200000000" />
29
+ <file name="gd_adapter.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
30
+ <file name="image_cache.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
31
+ <file name="image_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
32
+ <file name="image_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
33
+ <file name="image_renderer.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
34
+ <file name="inline_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
35
+ <file name="inline_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
36
+ <file name="inline_positioner.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
37
+ <file name="inline_renderer.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
38
+ <file name="javascript_embedder.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
39
+ <file name="line_box.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
40
+ <file name="list_bullet_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
41
+ <file name="list_bullet_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
42
+ <file name="list_bullet_image_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
43
+ <file name="list_bullet_positioner.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
44
+ <file name="list_bullet_renderer.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
45
+ <file name="null_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
46
+ <file name="null_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
47
+ <file name="null_positioner.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
48
+ <file name="page_cache.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
49
+ <file name="page_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
50
+ <file name="page_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
51
+ <file name="pdflib_adapter.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
52
+ <file name="php_evaluator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
53
+ <file name="positioner.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
54
+ <file name="renderer.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
55
+ <file name="style.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
56
+ <file name="stylesheet.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
57
+ <file name="table_cell_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
58
+ <file name="table_cell_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
59
+ <file name="table_cell_positioner.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
60
+ <file name="table_cell_renderer.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
61
+ <file name="table_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
62
+ <file name="table_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
63
+ <file name="table_row_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
64
+ <file name="table_row_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
65
+ <file name="table_row_group_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
66
+ <file name="table_row_group_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
67
+ <file name="table_row_group_renderer.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
68
+ <file name="table_row_positioner.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
69
+ <file name="tcpdf_adapter.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
70
+ <file name="text_frame_decorator.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
71
+ <file name="text_frame_reflower.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
72
+ <file name="text_renderer.cls.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
73
+ </dwsync>
dompdf/include/absolute_positioner.cls.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Positions absolutely positioned frames
11
+ */
12
+ class Absolute_Positioner extends Positioner {
13
+
14
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
15
+
16
+ function position() {
17
+
18
+ $frame = $this->_frame;
19
+ $style = $frame->get_style();
20
+
21
+ $p = $frame->find_positionned_parent();
22
+
23
+ list($x, $y, $w, $h) = $frame->get_containing_block();
24
+
25
+ $top = $style->length_in_pt($style->top, $h);
26
+ $right = $style->length_in_pt($style->right, $w);
27
+ $bottom = $style->length_in_pt($style->bottom, $h);
28
+ $left = $style->length_in_pt($style->left, $w);
29
+
30
+ if ( $p && !($left === "auto" && $right === "auto") ) {
31
+ // Get the parent's padding box (see http://www.w3.org/TR/CSS21/visuren.html#propdef-top)
32
+ list($x, $y, $w, $h) = $p->get_padding_box();
33
+ }
34
+
35
+ list($width, $height) = array($frame->get_margin_width(), $frame->get_margin_height());
36
+
37
+ $orig_style = $this->_frame->get_original_style();
38
+ $orig_width = $orig_style->width;
39
+ $orig_height = $orig_style->height;
40
+
41
+ /****************************
42
+
43
+ Width auto:
44
+ ____________| left=auto | left=fixed |
45
+ right=auto | A | B |
46
+ right=fixed | C | D |
47
+
48
+ Width fixed:
49
+ ____________| left=auto | left=fixed |
50
+ right=auto | E | F |
51
+ right=fixed | G | H |
52
+
53
+ *****************************/
54
+
55
+ if ( $left === "auto" ) {
56
+ if ( $right === "auto" ) {
57
+ // A or E - Keep the frame at the same position
58
+ }
59
+ else {
60
+ if ( $orig_width === "auto" ) {
61
+ // C
62
+ $x += $w - $width - $right;
63
+ }
64
+ else {
65
+ // G
66
+ $x += $w - $width - $right;
67
+ }
68
+ }
69
+ }
70
+ else {
71
+ if ( $right === "auto" ) {
72
+ // B or F
73
+ $x += $left;
74
+ }
75
+ else {
76
+ if ( $orig_width === "auto" ) {
77
+ // D - TODO change width
78
+ $x += $left;
79
+ }
80
+ else {
81
+ // H - Everything is fixed: left + width win
82
+ $x += $left;
83
+ }
84
+ }
85
+ }
86
+
87
+ // The same vertically
88
+ if ( $top === "auto" ) {
89
+ if ( $bottom === "auto" ) {
90
+ // A or E - Keep the frame at the same position
91
+ $y = $frame->get_parent()->get_current_line_box()->y;
92
+ }
93
+ else {
94
+ if ( $orig_height === "auto" ) {
95
+ // C
96
+ $y += $h - $height - $bottom;
97
+ }
98
+ else {
99
+ // G
100
+ $y += $h - $height - $bottom;
101
+ }
102
+ }
103
+ }
104
+ else {
105
+ if ( $bottom === "auto" ) {
106
+ // B or F
107
+ $y += $top;
108
+ }
109
+ else {
110
+ if ( $orig_height === "auto" ) {
111
+ // D - TODO change height
112
+ $y += $top;
113
+ }
114
+ else {
115
+ // H - Everything is fixed: top + height win
116
+ $y += $top;
117
+ }
118
+ }
119
+ }
120
+
121
+ $frame->set_position($x, $y);
122
+
123
+ }
124
+ }
dompdf/include/abstract_renderer.cls.php ADDED
@@ -0,0 +1,753 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * Base renderer class
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ */
17
+ abstract class Abstract_Renderer {
18
+
19
+ /**
20
+ * Rendering backend
21
+ *
22
+ * @var Canvas
23
+ */
24
+ protected $_canvas;
25
+
26
+ /**
27
+ * Current dompdf instance
28
+ *
29
+ * @var DOMPDF
30
+ */
31
+ protected $_dompdf;
32
+
33
+ /**
34
+ * Class constructor
35
+ *
36
+ * @param DOMPDF $dompdf The current dompdf instance
37
+ */
38
+ function __construct(DOMPDF $dompdf) {
39
+ $this->_dompdf = $dompdf;
40
+ $this->_canvas = $dompdf->get_canvas();
41
+ }
42
+
43
+ /**
44
+ * Render a frame.
45
+ *
46
+ * Specialized in child classes
47
+ *
48
+ * @param Frame $frame The frame to render
49
+ */
50
+ abstract function render(Frame $frame);
51
+
52
+ //........................................................................
53
+
54
+ /**
55
+ * Render a background image over a rectangular area
56
+ *
57
+ * @param string $url The background image to load
58
+ * @param float $x The left edge of the rectangular area
59
+ * @param float $y The top edge of the rectangular area
60
+ * @param float $width The width of the rectangular area
61
+ * @param float $height The height of the rectangular area
62
+ * @param Style $style The associated Style object
63
+ */
64
+ protected function _background_image($url, $x, $y, $width, $height, $style) {
65
+ $sheet = $style->get_stylesheet();
66
+
67
+ // Skip degenerate cases
68
+ if ( $width == 0 || $height == 0 ) {
69
+ return;
70
+ }
71
+
72
+ $box_width = $width;
73
+ $box_height = $height;
74
+
75
+ //debugpng
76
+ if (DEBUGPNG) print '[_background_image '.$url.']';
77
+
78
+ list($img, $type, /*$msg*/) = Image_Cache::resolve_url(
79
+ $url,
80
+ $sheet->get_protocol(),
81
+ $sheet->get_host(),
82
+ $sheet->get_base_path(),
83
+ $this->_dompdf
84
+ );
85
+
86
+ // Bail if the image is no good
87
+ if ( Image_Cache::is_broken($img) ) {
88
+ return;
89
+ }
90
+
91
+ //Try to optimize away reading and composing of same background multiple times
92
+ //Postponing read with imagecreatefrom ...()
93
+ //final composition parameters and name not known yet
94
+ //Therefore read dimension directly from file, instead of creating gd object first.
95
+ //$img_w = imagesx($src); $img_h = imagesy($src);
96
+
97
+ list($img_w, $img_h) = dompdf_getimagesize($img);
98
+ if (!isset($img_w) || $img_w == 0 || !isset($img_h) || $img_h == 0) {
99
+ return;
100
+ }
101
+
102
+ $repeat = $style->background_repeat;
103
+ $dpi = $this->_dompdf->get_option("dpi");
104
+
105
+ //Increase background resolution and dependent box size according to image resolution to be placed in
106
+ //Then image can be copied in without resize
107
+ $bg_width = round((float)($width * $dpi) / 72);
108
+ $bg_height = round((float)($height * $dpi) / 72);
109
+
110
+ //Need %bg_x, $bg_y as background pos, where img starts, converted to pixel
111
+
112
+ list($bg_x, $bg_y) = $style->background_position;
113
+
114
+ if ( is_percent($bg_x) ) {
115
+ // The point $bg_x % from the left edge of the image is placed
116
+ // $bg_x % from the left edge of the background rectangle
117
+ $p = ((float)$bg_x)/100.0;
118
+ $x1 = $p * $img_w;
119
+ $x2 = $p * $bg_width;
120
+
121
+ $bg_x = $x2 - $x1;
122
+ }
123
+ else {
124
+ $bg_x = (float)($style->length_in_pt($bg_x)*$dpi) / 72;
125
+ }
126
+
127
+ $bg_x = round($bg_x + $style->length_in_pt($style->border_left_width)*$dpi / 72);
128
+
129
+ if ( is_percent($bg_y) ) {
130
+ // The point $bg_y % from the left edge of the image is placed
131
+ // $bg_y % from the left edge of the background rectangle
132
+ $p = ((float)$bg_y)/100.0;
133
+ $y1 = $p * $img_h;
134
+ $y2 = $p * $bg_height;
135
+
136
+ $bg_y = $y2 - $y1;
137
+ }
138
+ else {
139
+ $bg_y = (float)($style->length_in_pt($bg_y)*$dpi) / 72;
140
+ }
141
+
142
+ $bg_y = round($bg_y + $style->length_in_pt($style->border_top_width)*$dpi / 72);
143
+
144
+ //clip background to the image area on partial repeat. Nothing to do if img off area
145
+ //On repeat, normalize start position to the tile at immediate left/top or 0/0 of area
146
+ //On no repeat with positive offset: move size/start to have offset==0
147
+ //Handle x/y Dimensions separately
148
+
149
+ if ( $repeat !== "repeat" && $repeat !== "repeat-x" ) {
150
+ //No repeat x
151
+ if ($bg_x < 0) {
152
+ $bg_width = $img_w + $bg_x;
153
+ }
154
+ else {
155
+ $x += ($bg_x * 72)/$dpi;
156
+ $bg_width = $bg_width - $bg_x;
157
+ if ($bg_width > $img_w) {
158
+ $bg_width = $img_w;
159
+ }
160
+ $bg_x = 0;
161
+ }
162
+
163
+ if ($bg_width <= 0) {
164
+ return;
165
+ }
166
+
167
+ $width = (float)($bg_width * 72)/$dpi;
168
+ }
169
+ else {
170
+ //repeat x
171
+ if ($bg_x < 0) {
172
+ $bg_x = - ((-$bg_x) % $img_w);
173
+ }
174
+ else {
175
+ $bg_x = $bg_x % $img_w;
176
+ if ($bg_x > 0) {
177
+ $bg_x -= $img_w;
178
+ }
179
+ }
180
+ }
181
+
182
+ if ( $repeat !== "repeat" && $repeat !== "repeat-y" ) {
183
+ //no repeat y
184
+ if ($bg_y < 0) {
185
+ $bg_height = $img_h + $bg_y;
186
+ }
187
+ else {
188
+ $y += ($bg_y * 72)/$dpi;
189
+ $bg_height = $bg_height - $bg_y;
190
+ if ($bg_height > $img_h) {
191
+ $bg_height = $img_h;
192
+ }
193
+ $bg_y = 0;
194
+ }
195
+ if ($bg_height <= 0) {
196
+ return;
197
+ }
198
+ $height = (float)($bg_height * 72)/$dpi;
199
+ }
200
+ else {
201
+ //repeat y
202
+ if ($bg_y < 0) {
203
+ $bg_y = - ((-$bg_y) % $img_h);
204
+ }
205
+ else {
206
+ $bg_y = $bg_y % $img_h;
207
+ if ($bg_y > 0) {
208
+ $bg_y -= $img_h;
209
+ }
210
+ }
211
+ }
212
+
213
+ //Optimization, if repeat has no effect
214
+ if ( $repeat === "repeat" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height ) {
215
+ $repeat = "repeat-x";
216
+ }
217
+
218
+ if ( $repeat === "repeat" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width ) {
219
+ $repeat = "repeat-y";
220
+ }
221
+
222
+ if ( ($repeat === "repeat-x" && $bg_x <= 0 && $img_w+$bg_x >= $bg_width) ||
223
+ ($repeat === "repeat-y" && $bg_y <= 0 && $img_h+$bg_y >= $bg_height) ) {
224
+ $repeat = "no-repeat";
225
+ }
226
+
227
+ //Use filename as indicator only
228
+ //different names for different variants to have different copies in the pdf
229
+ //This is not dependent of background color of box! .'_'.(is_array($bg_color) ? $bg_color["hex"] : $bg_color)
230
+ //Note: Here, bg_* are the start values, not end values after going through the tile loops!
231
+
232
+ $filedummy = $img;
233
+
234
+ $is_png = false;
235
+ $filedummy .= '_'.$bg_width.'_'.$bg_height.'_'.$bg_x.'_'.$bg_y.'_'.$repeat;
236
+
237
+ //Optimization to avoid multiple times rendering the same image.
238
+ //If check functions are existing and identical image already cached,
239
+ //then skip creation of duplicate, because it is not needed by addImagePng
240
+ if ( $this->_canvas instanceof CPDF_Adapter &&
241
+ $this->_canvas->get_cpdf()->image_iscached($filedummy) ) {
242
+ $bg = null;
243
+ }
244
+
245
+ else {
246
+
247
+ // Create a new image to fit over the background rectangle
248
+ $bg = imagecreatetruecolor($bg_width, $bg_height);
249
+
250
+ switch (strtolower($type)) {
251
+ case IMAGETYPE_PNG:
252
+ $is_png = true;
253
+ imagesavealpha($bg, true);
254
+ imagealphablending($bg, false);
255
+ $src = imagecreatefrompng($img);
256
+ break;
257
+
258
+ case IMAGETYPE_JPEG:
259
+ $src = imagecreatefromjpeg($img);
260
+ break;
261
+
262
+ case IMAGETYPE_GIF:
263
+ $src = imagecreatefromgif($img);
264
+ break;
265
+
266
+ case IMAGETYPE_BMP:
267
+ $src = imagecreatefrombmp($img);
268
+ break;
269
+
270
+ default:
271
+ return; // Unsupported image type
272
+ }
273
+
274
+ if ( $src == null ) {
275
+ return;
276
+ }
277
+
278
+ //Background color if box is not relevant here
279
+ //Non transparent image: box clipped to real size. Background non relevant.
280
+ //Transparent image: The image controls the transparency and lets shine through whatever background.
281
+ //However on transparent image preset the composed image with the transparency color,
282
+ //to keep the transparency when copying over the non transparent parts of the tiles.
283
+ $ti = imagecolortransparent($src);
284
+
285
+ if ( $ti >= 0 ) {
286
+ $tc = imagecolorsforindex($src, $ti);
287
+ $ti = imagecolorallocate($bg, $tc['red'], $tc['green'], $tc['blue']);
288
+ imagefill($bg, 0, 0, $ti);
289
+ imagecolortransparent($bg, $ti);
290
+ }
291
+
292
+ //This has only an effect for the non repeatable dimension.
293
+ //compute start of src and dest coordinates of the single copy
294
+ if ( $bg_x < 0 ) {
295
+ $dst_x = 0;
296
+ $src_x = -$bg_x;
297
+ }
298
+ else {
299
+ $src_x = 0;
300
+ $dst_x = $bg_x;
301
+ }
302
+
303
+ if ( $bg_y < 0 ) {
304
+ $dst_y = 0;
305
+ $src_y = -$bg_y;
306
+ }
307
+ else {
308
+ $src_y = 0;
309
+ $dst_y = $bg_y;
310
+ }
311
+
312
+ //For historical reasons exchange meanings of variables:
313
+ //start_* will be the start values, while bg_* will be the temporary start values in the loops
314
+ $start_x = $bg_x;
315
+ $start_y = $bg_y;
316
+
317
+ // Copy regions from the source image to the background
318
+ if ( $repeat === "no-repeat" ) {
319
+
320
+ // Simply place the image on the background
321
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $img_h);
322
+
323
+ }
324
+ else if ( $repeat === "repeat-x" ) {
325
+
326
+ for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
327
+ if ( $bg_x < 0 ) {
328
+ $dst_x = 0;
329
+ $src_x = -$bg_x;
330
+ $w = $img_w + $bg_x;
331
+ }
332
+ else {
333
+ $dst_x = $bg_x;
334
+ $src_x = 0;
335
+ $w = $img_w;
336
+ }
337
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $img_h);
338
+ }
339
+
340
+ }
341
+ else if ( $repeat === "repeat-y" ) {
342
+
343
+ for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
344
+ if ( $bg_y < 0 ) {
345
+ $dst_y = 0;
346
+ $src_y = -$bg_y;
347
+ $h = $img_h + $bg_y;
348
+ }
349
+ else {
350
+ $dst_y = $bg_y;
351
+ $src_y = 0;
352
+ $h = $img_h;
353
+ }
354
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $img_w, $h);
355
+
356
+ }
357
+
358
+ }
359
+ else if ( $repeat === "repeat" ) {
360
+
361
+ for ( $bg_y = $start_y; $bg_y < $bg_height; $bg_y += $img_h ) {
362
+ for ( $bg_x = $start_x; $bg_x < $bg_width; $bg_x += $img_w ) {
363
+
364
+ if ( $bg_x < 0 ) {
365
+ $dst_x = 0;
366
+ $src_x = -$bg_x;
367
+ $w = $img_w + $bg_x;
368
+ }
369
+ else {
370
+ $dst_x = $bg_x;
371
+ $src_x = 0;
372
+ $w = $img_w;
373
+ }
374
+
375
+ if ( $bg_y < 0 ) {
376
+ $dst_y = 0;
377
+ $src_y = -$bg_y;
378
+ $h = $img_h + $bg_y;
379
+ }
380
+ else {
381
+ $dst_y = $bg_y;
382
+ $src_y = 0;
383
+ $h = $img_h;
384
+ }
385
+ imagecopy($bg, $src, $dst_x, $dst_y, $src_x, $src_y, $w, $h);
386
+ }
387
+ }
388
+ }
389
+
390
+ else {
391
+ print 'Unknown repeat!';
392
+ }
393
+
394
+ imagedestroy($src);
395
+
396
+ } /* End optimize away creation of duplicates */
397
+
398
+ $this->_canvas->clipping_rectangle($x, $y, $box_width, $box_height);
399
+
400
+ //img: image url string
401
+ //img_w, img_h: original image size in px
402
+ //width, height: box size in pt
403
+ //bg_width, bg_height: box size in px
404
+ //x, y: left/top edge of box on page in pt
405
+ //start_x, start_y: placement of image relative to pattern
406
+ //$repeat: repeat mode
407
+ //$bg: GD object of result image
408
+ //$src: GD object of original image
409
+ //When using cpdf and optimization to direct png creation from gd object is available,
410
+ //don't create temp file, but place gd object directly into the pdf
411
+ if ( !$is_png && $this->_canvas instanceof CPDF_Adapter ) {
412
+ // Note: CPDF_Adapter image converts y position
413
+ $this->_canvas->get_cpdf()->addImagePng($filedummy, $x, $this->_canvas->get_height() - $y - $height, $width, $height, $bg);
414
+ }
415
+
416
+ else {
417
+ $tmp_dir = $this->_dompdf->get_option("temp_dir");
418
+ $tmp_name = tempnam($tmp_dir, "bg_dompdf_img_");
419
+ @unlink($tmp_name);
420
+ $tmp_file = "$tmp_name.png";
421
+
422
+ //debugpng
423
+ if (DEBUGPNG) print '[_background_image '.$tmp_file.']';
424
+
425
+ imagepng($bg, $tmp_file);
426
+ $this->_canvas->image($tmp_file, $x, $y, $width, $height);
427
+ imagedestroy($bg);
428
+
429
+ //debugpng
430
+ if (DEBUGPNG) print '[_background_image unlink '.$tmp_file.']';
431
+
432
+ if (!DEBUGKEEPTEMP) {
433
+ unlink($tmp_file);
434
+ }
435
+ }
436
+
437
+ $this->_canvas->clipping_end();
438
+ }
439
+
440
+ protected function _get_dash_pattern($style, $width) {
441
+ $pattern = array();
442
+
443
+ switch ($style) {
444
+ default:
445
+ /*case "solid":
446
+ case "double":
447
+ case "groove":
448
+ case "inset":
449
+ case "outset":
450
+ case "ridge":*/
451
+ case "none": break;
452
+
453
+ case "dotted":
454
+ if ( $width <= 1 )
455
+ $pattern = array($width, $width*2);
456
+ else
457
+ $pattern = array($width);
458
+ break;
459
+
460
+ case "dashed":
461
+ $pattern = array(3 * $width);
462
+ break;
463
+ }
464
+
465
+ return $pattern;
466
+ }
467
+
468
+ protected function _border_none($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
469
+ return;
470
+ }
471
+
472
+ protected function _border_hidden($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
473
+ return;
474
+ }
475
+
476
+ // Border rendering functions
477
+
478
+ protected function _border_dotted($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
479
+ $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dotted", $r1, $r2);
480
+ }
481
+
482
+
483
+ protected function _border_dashed($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
484
+ $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "dashed", $r1, $r2);
485
+ }
486
+
487
+
488
+ protected function _border_solid($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
489
+ // TODO: Solve rendering where one corner is beveled (radius == 0), one corner isn't.
490
+ if ( $corner_style !== "bevel" || $r1 > 0 || $r2 > 0 ) {
491
+ // do it the simple way
492
+ $this->_border_line($x, $y, $length, $color, $widths, $side, $corner_style, "solid", $r1, $r2);
493
+ return;
494
+ }
495
+
496
+ list($top, $right, $bottom, $left) = $widths;
497
+
498
+ // All this polygon business is for beveled corners...
499
+ switch ($side) {
500
+ case "top":
501
+ $points = array($x, $y,
502
+ $x + $length, $y,
503
+ $x + $length - $right, $y + $top,
504
+ $x + $left, $y + $top);
505
+ $this->_canvas->polygon($points, $color, null, null, true);
506
+ break;
507
+
508
+ case "bottom":
509
+ $points = array($x, $y,
510
+ $x + $length, $y,
511
+ $x + $length - $right, $y - $bottom,
512
+ $x + $left, $y - $bottom);
513
+ $this->_canvas->polygon($points, $color, null, null, true);
514
+ break;
515
+
516
+ case "left":
517
+ $points = array($x, $y,
518
+ $x, $y + $length,
519
+ $x + $left, $y + $length - $bottom,
520
+ $x + $left, $y + $top);
521
+ $this->_canvas->polygon($points, $color, null, null, true);
522
+ break;
523
+
524
+ case "right":
525
+ $points = array($x, $y,
526
+ $x, $y + $length,
527
+ $x - $right, $y + $length - $bottom,
528
+ $x - $right, $y + $top);
529
+ $this->_canvas->polygon($points, $color, null, null, true);
530
+ break;
531
+
532
+ default:
533
+ return;
534
+ }
535
+ }
536
+
537
+ protected function _apply_ratio($side, $ratio, $top, $right, $bottom, $left, &$x, &$y, &$length, &$r1, &$r2) {
538
+ switch ($side) {
539
+
540
+ case "top":
541
+ $r1 -= $left * $ratio;
542
+ $r2 -= $right * $ratio;
543
+ $x += $left * $ratio;
544
+ $y += $top * $ratio;
545
+ $length -= $left * $ratio + $right * $ratio;
546
+ break;
547
+
548
+ case "bottom":
549
+ $r1 -= $right * $ratio;
550
+ $r2 -= $left * $ratio;
551
+ $x += $left * $ratio;
552
+ $y -= $bottom * $ratio;
553
+ $length -= $left * $ratio + $right * $ratio;
554
+ break;
555
+
556
+ case "left":
557
+ $r1 -= $top * $ratio;
558
+ $r2 -= $bottom * $ratio;
559
+ $x += $left * $ratio;
560
+ $y += $top * $ratio;
561
+ $length -= $top * $ratio + $bottom * $ratio;
562
+ break;
563
+
564
+ case "right":
565
+ $r1 -= $bottom * $ratio;
566
+ $r2 -= $top * $ratio;
567
+ $x -= $right * $ratio;
568
+ $y += $top * $ratio;
569
+ $length -= $top * $ratio + $bottom * $ratio;
570
+ break;
571
+
572
+ default:
573
+ return;
574
+
575
+ }
576
+ }
577
+
578
+ protected function _border_double($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
579
+ list($top, $right, $bottom, $left) = $widths;
580
+
581
+ $third_widths = array($top / 3, $right / 3, $bottom / 3, $left / 3);
582
+
583
+ // draw the outer border
584
+ $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
585
+
586
+ $this->_apply_ratio($side, 2/3, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
587
+
588
+ $this->_border_solid($x, $y, $length, $color, $third_widths, $side, $corner_style, $r1, $r2);
589
+ }
590
+
591
+ protected function _border_groove($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
592
+ list($top, $right, $bottom, $left) = $widths;
593
+
594
+ $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
595
+
596
+ $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
597
+
598
+ $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
599
+
600
+ $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
601
+
602
+ }
603
+
604
+ protected function _border_ridge($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
605
+ list($top, $right, $bottom, $left) = $widths;
606
+
607
+ $half_widths = array($top / 2, $right / 2, $bottom / 2, $left / 2);
608
+
609
+ $this->_border_outset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
610
+
611
+ $this->_apply_ratio($side, 0.5, $top, $right, $bottom, $left, $x, $y, $length, $r1, $r2);
612
+
613
+ $this->_border_inset($x, $y, $length, $color, $half_widths, $side, $corner_style, $r1, $r2);
614
+
615
+ }
616
+
617
+ protected function _tint($c) {
618
+ if ( !is_numeric($c) )
619
+ return $c;
620
+
621
+ return min(1, $c + 0.16);
622
+ }
623
+
624
+ protected function _shade($c) {
625
+ if ( !is_numeric($c) )
626
+ return $c;
627
+
628
+ return max(0, $c - 0.33);
629
+ }
630
+
631
+ protected function _border_inset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
632
+ switch ($side) {
633
+ case "top":
634
+ case "left":
635
+ $shade = array_map(array($this, "_shade"), $color);
636
+ $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
637
+ break;
638
+
639
+ case "bottom":
640
+ case "right":
641
+ $tint = array_map(array($this, "_tint"), $color);
642
+ $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
643
+ break;
644
+
645
+ default:
646
+ return;
647
+ }
648
+ }
649
+
650
+ protected function _border_outset($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $r1 = 0, $r2 = 0) {
651
+ switch ($side) {
652
+ case "top":
653
+ case "left":
654
+ $tint = array_map(array($this, "_tint"), $color);
655
+ $this->_border_solid($x, $y, $length, $tint, $widths, $side, $corner_style, $r1, $r2);
656
+ break;
657
+
658
+ case "bottom":
659
+ case "right":
660
+ $shade = array_map(array($this, "_shade"), $color);
661
+ $this->_border_solid($x, $y, $length, $shade, $widths, $side, $corner_style, $r1, $r2);
662
+ break;
663
+
664
+ default:
665
+ return;
666
+ }
667
+ }
668
+ // Draws a solid, dotted, or dashed line, observing the border radius
669
+ protected function _border_line($x, $y, $length, $color, $widths, $side, $corner_style = "bevel", $pattern_name, $r1 = 0, $r2 = 0) {
670
+ list($top, $right, $bottom, $left) = $widths;
671
+
672
+ $width = $$side;
673
+ $pattern = $this->_get_dash_pattern($pattern_name, $width);
674
+
675
+ $half_width = $width/2;
676
+ $r1 -= $half_width;
677
+ $r2 -= $half_width;
678
+ $adjust = $r1/80;
679
+ $length -= $width;
680
+
681
+ switch ($side) {
682
+ case "top":
683
+ $x += $half_width;
684
+ $y += $half_width;
685
+
686
+ if ( $r1 > 0 ) {
687
+ $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 90-$adjust, 135+$adjust, $color, $width, $pattern);
688
+ }
689
+
690
+ $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
691
+
692
+ if ( $r2 > 0 ) {
693
+ $this->_canvas->arc($x + $length - $r2, $y + $r2, $r2, $r2, 45-$adjust, 90+$adjust, $color, $width, $pattern);
694
+ }
695
+ break;
696
+
697
+ case "bottom":
698
+ $x += $half_width;
699
+ $y -= $half_width;
700
+
701
+ if ( $r1 > 0 ) {
702
+ $this->_canvas->arc($x + $r1, $y - $r1, $r1, $r1, 225-$adjust, 270+$adjust, $color, $width, $pattern);
703
+ }
704
+
705
+ $this->_canvas->line($x + $r1, $y, $x + $length - $r2, $y, $color, $width, $pattern);
706
+
707
+ if ( $r2 > 0 ) {
708
+ $this->_canvas->arc($x + $length - $r2, $y - $r2, $r2, $r2, 270-$adjust, 315+$adjust, $color, $width, $pattern);
709
+ }
710
+ break;
711
+
712
+ case "left":
713
+ $y += $half_width;
714
+ $x += $half_width;
715
+
716
+ if ( $r1 > 0 ) {
717
+ $this->_canvas->arc($x + $r1, $y + $r1, $r1, $r1, 135-$adjust, 180+$adjust, $color, $width, $pattern);
718
+ }
719
+
720
+ $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
721
+
722
+ if ( $r2 > 0 ) {
723
+ $this->_canvas->arc($x + $r2, $y + $length - $r2, $r2, $r2, 180-$adjust, 225+$adjust, $color, $width, $pattern);
724
+ }
725
+ break;
726
+
727
+ case "right":
728
+ $y += $half_width;
729
+ $x -= $half_width;
730
+
731
+ if ( $r1 > 0 ) {
732
+ $this->_canvas->arc($x - $r1, $y + $r1, $r1, $r1, 0-$adjust, 45+$adjust, $color, $width, $pattern);
733
+ }
734
+
735
+ $this->_canvas->line($x, $y + $r1, $x, $y + $length - $r2, $color, $width, $pattern);
736
+
737
+ if ( $r2 > 0 ) {
738
+ $this->_canvas->arc($x - $r2, $y + $length - $r2, $r2, $r2, 315-$adjust, 360+$adjust, $color, $width, $pattern);
739
+ }
740
+ break;
741
+ }
742
+ }
743
+
744
+ protected function _set_opacity($opacity) {
745
+ if ( is_numeric($opacity) && $opacity <= 1.0 && $opacity >= 0.0 ) {
746
+ $this->_canvas->set_opacity( $opacity );
747
+ }
748
+ }
749
+
750
+ protected function _debug_layout($box, $color = "red", $style = array()) {
751
+ $this->_canvas->rectangle($box[0], $box[1], $box[2], $box[3], CSS_Color::parse($color), 0.1, $style);
752
+ }
753
+ }
dompdf/include/attribute_translator.cls.php ADDED
@@ -0,0 +1,592 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Translates HTML 4.0 attributes into CSS rules
12
+ *
13
+ * @package dompdf
14
+ */
15
+ class Attribute_Translator {
16
+ static $_style_attr = "_html_style_attribute";
17
+
18
+ // Munged data originally from
19
+ // http://www.w3.org/TR/REC-html40/index/attributes.html
20
+ // http://www.cs.tut.fi/~jkorpela/html2css.html
21
+ static private $__ATTRIBUTE_LOOKUP = array(
22
+ //'caption' => array ( 'align' => '', ),
23
+ 'img' => array(
24
+ 'align' => array(
25
+ 'bottom' => 'vertical-align: baseline;',
26
+ 'middle' => 'vertical-align: middle;',
27
+ 'top' => 'vertical-align: top;',
28
+ 'left' => 'float: left;',
29
+ 'right' => 'float: right;'
30
+ ),
31
+ 'border' => 'border-width: %0.2F px;',
32
+ 'height' => 'height: %s px;',
33
+ 'hspace' => 'padding-left: %1$0.2F px; padding-right: %1$0.2F px;',
34
+ 'vspace' => 'padding-top: %1$0.2F px; padding-bottom: %1$0.2F px;',
35
+ 'width' => 'width: %s px;',
36
+ ),
37
+ 'table' => array(
38
+ 'align' => array(
39
+ 'left' => 'margin-left: 0; margin-right: auto;',
40
+ 'center' => 'margin-left: auto; margin-right: auto;',
41
+ 'right' => 'margin-left: auto; margin-right: 0;'
42
+ ),
43
+ 'bgcolor' => 'background-color: %s;',
44
+ 'border' => '!set_table_border',
45
+ 'cellpadding' => '!set_table_cellpadding',//'border-spacing: %0.2F; border-collapse: separate;',
46
+ 'cellspacing' => '!set_table_cellspacing',
47
+ 'frame' => array(
48
+ 'void' => 'border-style: none;',
49
+ 'above' => 'border-top-style: solid;',
50
+ 'below' => 'border-bottom-style: solid;',
51
+ 'hsides' => 'border-left-style: solid; border-right-style: solid;',
52
+ 'vsides' => 'border-top-style: solid; border-bottom-style: solid;',
53
+ 'lhs' => 'border-left-style: solid;',
54
+ 'rhs' => 'border-right-style: solid;',
55
+ 'box' => 'border-style: solid;',
56
+ 'border' => 'border-style: solid;'
57
+ ),
58
+ 'rules' => '!set_table_rules',
59
+ 'width' => 'width: %s;',
60
+ ),
61
+ 'hr' => array(
62
+ 'align' => '!set_hr_align', // Need to grab width to set 'left' & 'right' correctly
63
+ 'noshade' => 'border-style: solid;',
64
+ 'size' => '!set_hr_size', //'border-width: %0.2F px;',
65
+ 'width' => 'width: %s;',
66
+ ),
67
+ 'div' => array(
68
+ 'align' => 'text-align: %s;',
69
+ ),
70
+ 'h1' => array(
71
+ 'align' => 'text-align: %s;',
72
+ ),
73
+ 'h2' => array(
74
+ 'align' => 'text-align: %s;',
75
+ ),
76
+ 'h3' => array(
77
+ 'align' => 'text-align: %s;',
78
+ ),
79
+ 'h4' => array(
80
+ 'align' => 'text-align: %s;',
81
+ ),
82
+ 'h5' => array(
83
+ 'align' => 'text-align: %s;',
84
+ ),
85
+ 'h6' => array(
86
+ 'align' => 'text-align: %s;',
87
+ ),
88
+ 'p' => array(
89
+ 'align' => 'text-align: %s;',
90
+ ),
91
+ // 'col' => array(
92
+ // 'align' => '',
93
+ // 'valign' => '',
94
+ // ),
95
+ // 'colgroup' => array(
96
+ // 'align' => '',
97
+ // 'valign' => '',
98
+ // ),
99
+ 'tbody' => array(
100
+ 'align' => '!set_table_row_align',
101
+ 'valign' => '!set_table_row_valign',
102
+ ),
103
+ 'td' => array(
104
+ 'align' => 'text-align: %s;',
105
+ 'bgcolor' => '!set_background_color',
106
+ 'height' => 'height: %s;',
107
+ 'nowrap' => 'white-space: nowrap;',
108
+ 'valign' => 'vertical-align: %s;',
109
+ 'width' => 'width: %s;',
110
+ ),
111
+ 'tfoot' => array(
112
+ 'align' => '!set_table_row_align',
113
+ 'valign' => '!set_table_row_valign',
114
+ ),
115
+ 'th' => array(
116
+ 'align' => 'text-align: %s;',
117
+ 'bgcolor' => '!set_background_color',
118
+ 'height' => 'height: %s;',
119
+ 'nowrap' => 'white-space: nowrap;',
120
+ 'valign' => 'vertical-align: %s;',
121
+ 'width' => 'width: %s;',
122
+ ),
123
+ 'thead' => array(
124
+ 'align' => '!set_table_row_align',
125
+ 'valign' => '!set_table_row_valign',
126
+ ),
127
+ 'tr' => array(
128
+ 'align' => '!set_table_row_align',
129
+ 'bgcolor' => '!set_table_row_bgcolor',
130
+ 'valign' => '!set_table_row_valign',
131
+ ),
132
+ 'body' => array(
133
+ 'background' => 'background-image: url(%s);',
134
+ 'bgcolor' => '!set_background_color',
135
+ 'link' => '!set_body_link',
136
+ 'text' => '!set_color',
137
+ ),
138
+ 'br' => array(
139
+ 'clear' => 'clear: %s;',
140
+ ),
141
+ 'basefont' => array(
142
+ 'color' => '!set_color',
143
+ 'face' => 'font-family: %s;',
144
+ 'size' => '!set_basefont_size',
145
+ ),
146
+ 'font' => array(
147
+ 'color' => '!set_color',
148
+ 'face' => 'font-family: %s;',
149
+ 'size' => '!set_font_size',
150
+ ),
151
+ 'dir' => array(
152
+ 'compact' => 'margin: 0.5em 0;',
153
+ ),
154
+ 'dl' => array(
155
+ 'compact' => 'margin: 0.5em 0;',
156
+ ),
157
+ 'menu' => array(
158
+ 'compact' => 'margin: 0.5em 0;',
159
+ ),
160
+ 'ol' => array(
161
+ 'compact' => 'margin: 0.5em 0;',
162
+ 'start' => 'counter-reset: -dompdf-default-counter %d;',
163
+ 'type' => 'list-style-type: %s;',
164
+ ),
165
+ 'ul' => array(
166
+ 'compact' => 'margin: 0.5em 0;',
167
+ 'type' => 'list-style-type: %s;',
168
+ ),
169
+ 'li' => array(
170
+ 'type' => 'list-style-type: %s;',
171
+ 'value' => 'counter-reset: -dompdf-default-counter %d;',
172
+ ),
173
+ 'pre' => array(
174
+ 'width' => 'width: %s;',
175
+ ),
176
+ );
177
+
178
+ static protected $_last_basefont_size = 3;
179
+ static protected $_font_size_lookup = array(
180
+ // For basefont support
181
+ -3 => "4pt",
182
+ -2 => "5pt",
183
+ -1 => "6pt",
184
+ 0 => "7pt",
185
+
186
+ 1 => "8pt",
187
+ 2 => "10pt",
188
+ 3 => "12pt",
189
+ 4 => "14pt",
190
+ 5 => "18pt",
191
+ 6 => "24pt",
192
+ 7 => "34pt",
193
+
194
+ // For basefont support
195
+ 8 => "48pt",
196
+ 9 => "44pt",
197
+ 10 => "52pt",
198
+ 11 => "60pt",
199
+ );
200
+
201
+ /**
202
+ * @param Frame $frame
203
+ */
204
+ static function translate_attributes(Frame $frame) {
205
+ $node = $frame->get_node();
206
+ $tag = $node->nodeName;
207
+
208
+ if ( !isset(self::$__ATTRIBUTE_LOOKUP[$tag]) ) {
209
+ return;
210
+ }
211
+
212
+ $valid_attrs = self::$__ATTRIBUTE_LOOKUP[$tag];
213
+ $attrs = $node->attributes;
214
+ $style = rtrim($node->getAttribute(self::$_style_attr), "; ");
215
+ if ( $style != "" ) {
216
+ $style .= ";";
217
+ }
218
+
219
+ foreach ($attrs as $attr => $attr_node ) {
220
+ if ( !isset($valid_attrs[$attr]) ) {
221
+ continue;
222
+ }
223
+
224
+ $value = $attr_node->value;
225
+
226
+ $target = $valid_attrs[$attr];
227
+
228
+ // Look up $value in $target, if $target is an array:
229
+ if ( is_array($target) ) {
230
+ if ( isset($target[$value]) ) {
231
+ $style .= " " . self::_resolve_target($node, $target[$value], $value);
232
+ }
233
+ }
234
+ else {
235
+ // otherwise use target directly
236
+ $style .= " " . self::_resolve_target($node, $target, $value);
237
+ }
238
+ }
239
+
240
+ if ( !is_null($style) ) {
241
+ $style = ltrim($style);
242
+ $node->setAttribute(self::$_style_attr, $style);
243
+ }
244
+
245
+ }
246
+
247
+ /**
248
+ * @param DOMNode $node
249
+ * @param string $target
250
+ * @param string $value
251
+ *
252
+ * @return string
253
+ */
254
+ static protected function _resolve_target(DOMNode $node, $target, $value) {
255
+ if ( $target[0] === "!" ) {
256
+ // Function call
257
+ $func = "_" . mb_substr($target, 1);
258
+ return self::$func($node, $value);
259
+ }
260
+
261
+ return $value ? sprintf($target, $value) : "";
262
+ }
263
+
264
+ /**
265
+ * @param DOMElement $node
266
+ * @param string $new_style
267
+ */
268
+ static function append_style(DOMElement $node, $new_style) {
269
+ $style = rtrim($node->getAttribute(self::$_style_attr), ";");
270
+ $style .= $new_style;
271
+ $style = ltrim($style, ";");
272
+ $node->setAttribute(self::$_style_attr, $style);
273
+ }
274
+
275
+ /**
276
+ * @param DOMNode $node
277
+ *
278
+ * @return DOMNodeList|DOMElement[]
279
+ */
280
+ static protected function get_cell_list(DOMNode $node) {
281
+ $xpath = new DOMXpath($node->ownerDocument);
282
+
283
+ switch($node->nodeName) {
284
+ default:
285
+ case "table":
286
+ $query = "tr/td | thead/tr/td | tbody/tr/td | tfoot/tr/td | tr/th | thead/tr/th | tbody/tr/th | tfoot/tr/th";
287
+ break;
288
+
289
+ case "tbody":
290
+ case "tfoot":
291
+ case "thead":
292
+ $query = "tr/td | tr/th";
293
+ break;
294
+
295
+ case "tr":
296
+ $query = "td | th";
297
+ break;
298
+ }
299
+
300
+ return $xpath->query($query, $node);
301
+ }
302
+
303
+ /**
304
+ * @param string $value
305
+ *
306
+ * @return string
307
+ */
308
+ static protected function _get_valid_color($value) {
309
+ if ( preg_match('/^#?([0-9A-F]{6})$/i', $value, $matches) ) {
310
+ $value = "#$matches[1]";
311
+ }
312
+
313
+ return $value;
314
+ }
315
+
316
+ /**
317
+ * @param DOMElement $node
318
+ * @param string $value
319
+ *
320
+ * @return string
321
+ */
322
+ static protected function _set_color(DOMElement $node, $value) {
323
+ $value = self::_get_valid_color($value);
324
+ return "color: $value;";
325
+ }
326
+
327
+ /**
328
+ * @param DOMElement $node
329
+ * @param string $value
330
+ *
331
+ * @return string
332
+ */
333
+ static protected function _set_background_color(DOMElement $node, $value) {
334
+ $value = self::_get_valid_color($value);
335
+ return "background-color: $value;";
336
+ }
337
+
338
+ /**
339
+ * @param DOMElement $node
340
+ * @param string $value
341
+ *
342
+ * @return null
343
+ */
344
+ static protected function _set_table_cellpadding(DOMElement $node, $value) {
345
+ $cell_list = self::get_cell_list($node);
346
+
347
+ foreach ($cell_list as $cell) {
348
+ self::append_style($cell, "; padding: {$value}px;");
349
+ }
350
+
351
+ return null;
352
+ }
353
+
354
+ /**
355
+ * @param DOMElement $node
356
+ * @param string $value
357
+ *
358
+ * @return string
359
+ */
360
+ static protected function _set_table_border(DOMElement $node, $value) {
361
+ $cell_list = self::get_cell_list($node);
362
+
363
+ foreach ($cell_list as $cell) {
364
+ $style = rtrim($cell->getAttribute(self::$_style_attr));
365
+ $style .= "; border-width: " . ($value > 0 ? 1 : 0) . "pt; border-style: inset;";
366
+ $style = ltrim($style, ";");
367
+ $cell->setAttribute(self::$_style_attr, $style);
368
+ }
369
+
370
+ $style = rtrim($node->getAttribute(self::$_style_attr), ";");
371
+ $style .= "; border-width: $value" . "px; ";
372
+ return ltrim($style, "; ");
373
+ }
374
+
375
+ /**
376
+ * @param DOMElement $node
377
+ * @param string $value
378
+ *
379
+ * @return string
380
+ */
381
+ static protected function _set_table_cellspacing(DOMElement $node, $value) {
382
+ $style = rtrim($node->getAttribute(self::$_style_attr), ";");
383
+
384
+ if ( $value == 0 ) {
385
+ $style .= "; border-collapse: collapse;";
386
+ }
387
+ else {
388
+ $style .= "; border-spacing: {$value}px; border-collapse: separate;";
389
+ }
390
+
391
+ return ltrim($style, ";");
392
+ }
393
+
394
+ /**
395
+ * @param DOMElement $node
396
+ * @param string $value
397
+ *
398
+ * @return null|string
399
+ */
400
+ static protected function _set_table_rules(DOMElement $node, $value) {
401
+ $new_style = "; border-collapse: collapse;";
402
+
403
+ switch ($value) {
404
+ case "none":
405
+ $new_style .= "border-style: none;";
406
+ break;
407
+
408
+ case "groups":
409
+ // FIXME: unsupported
410
+ return null;
411
+
412
+ case "rows":
413
+ $new_style .= "border-style: solid none solid none; border-width: 1px; ";
414
+ break;
415
+
416
+ case "cols":
417
+ $new_style .= "border-style: none solid none solid; border-width: 1px; ";
418
+ break;
419
+
420
+ case "all":
421
+ $new_style .= "border-style: solid; border-width: 1px; ";
422
+ break;
423
+
424
+ default:
425
+ // Invalid value
426
+ return null;
427
+ }
428
+
429
+ $cell_list = self::get_cell_list($node);
430
+
431
+ foreach ($cell_list as $cell) {
432
+ $style = $cell->getAttribute(self::$_style_attr);
433
+ $style .= $new_style;
434
+ $cell->setAttribute(self::$_style_attr, $style);
435
+ }
436
+
437
+ $style = rtrim($node->getAttribute(self::$_style_attr), ";");
438
+ $style .= "; border-collapse: collapse; ";
439
+
440
+ return ltrim($style, "; ");
441
+ }
442
+
443
+ /**
444
+ * @param DOMElement $node
445
+ * @param string $value
446
+ *
447
+ * @return string
448
+ */
449
+ static protected function _set_hr_size(DOMElement $node, $value) {
450
+ $style = rtrim($node->getAttribute(self::$_style_attr), ";");
451
+ $style .= "; border-width: ".max(0, $value-2)."; ";
452
+ return ltrim($style, "; ");
453
+ }
454
+
455
+ /**
456
+ * @param DOMElement $node
457
+ * @param string $value
458
+ *
459
+ * @return null|string
460
+ */
461
+ static protected function _set_hr_align(DOMElement $node, $value) {
462
+ $style = rtrim($node->getAttribute(self::$_style_attr),";");
463
+ $width = $node->getAttribute("width");
464
+
465
+ if ( $width == "" ) {
466
+ $width = "100%";
467
+ }
468
+
469
+ $remainder = 100 - (double)rtrim($width, "% ");
470
+
471
+ switch ($value) {
472
+ case "left":
473
+ $style .= "; margin-right: $remainder %;";
474
+ break;
475
+
476
+ case "right":
477
+ $style .= "; margin-left: $remainder %;";
478
+ break;
479
+
480
+ case "center":
481
+ $style .= "; margin-left: auto; margin-right: auto;";
482
+ break;
483
+
484
+ default:
485
+ return null;
486
+ }
487
+
488
+ return ltrim($style, "; ");
489
+ }
490
+
491
+ /**
492
+ * @param DOMElement $node
493
+ * @param string $value
494
+ *
495
+ * @return null
496
+ */
497
+ static protected function _set_table_row_align(DOMElement $node, $value) {
498
+ $cell_list = self::get_cell_list($node);
499
+
500
+ foreach ($cell_list as $cell) {
501
+ self::append_style($cell, "; text-align: $value;");
502
+ }
503
+
504
+ return null;
505
+ }
506
+
507
+ /**
508
+ * @param DOMElement $node
509
+ * @param string $value
510
+ *
511
+ * @return null
512
+ */
513
+ static protected function _set_table_row_valign(DOMElement $node, $value) {
514
+ $cell_list = self::get_cell_list($node);
515
+
516
+ foreach ($cell_list as $cell) {
517
+ self::append_style($cell, "; vertical-align: $value;");
518
+ }
519
+
520
+ return null;
521
+ }
522
+
523
+ /**
524
+ * @param DOMElement $node
525
+ * @param string $value
526
+ *
527
+ * @return null
528
+ */
529
+ static protected function _set_table_row_bgcolor(DOMElement $node, $value) {
530
+ $cell_list = self::get_cell_list($node);
531
+ $value = self::_get_valid_color($value);
532
+
533
+ foreach ($cell_list as $cell) {
534
+ self::append_style($cell, "; background-color: $value;");
535
+ }
536
+
537
+ return null;
538
+ }
539
+
540
+ /**
541
+ * @param DOMElement $node
542
+ * @param string $value
543
+ *
544
+ * @return null
545
+ */
546
+ static protected function _set_body_link(DOMElement $node, $value) {
547
+ $a_list = $node->getElementsByTagName("a");
548
+ $value = self::_get_valid_color($value);
549
+
550
+ foreach ($a_list as $a) {
551
+ self::append_style($a, "; color: $value;");
552
+ }
553
+
554
+ return null;
555
+ }
556
+
557
+ /**
558
+ * @param DOMElement $node
559
+ * @param string $value
560
+ *
561
+ * @return null
562
+ */
563
+ static protected function _set_basefont_size(DOMElement $node, $value) {
564
+ // FIXME: ? we don't actually set the font size of anything here, just
565
+ // the base size for later modification by <font> tags.
566
+ self::$_last_basefont_size = $value;
567
+ return null;
568
+ }
569
+
570
+ /**
571
+ * @param DOMElement $node
572
+ * @param string $value
573
+ *
574
+ * @return string
575
+ */
576
+ static protected function _set_font_size(DOMElement $node, $value) {
577
+ $style = $node->getAttribute(self::$_style_attr);
578
+
579
+ if ( $value[0] === "-" || $value[0] === "+" ) {
580
+ $value = self::$_last_basefont_size + (int)$value;
581
+ }
582
+
583
+ if ( isset(self::$_font_size_lookup[$value]) ) {
584
+ $style .= "; font-size: " . self::$_font_size_lookup[$value] . ";";
585
+ }
586
+ else {
587
+ $style .= "; font-size: $value;";
588
+ }
589
+
590
+ return ltrim($style, "; ");
591
+ }
592
+ }
dompdf/include/autoload.inc.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * DOMPDF autoload function
12
+ *
13
+ * If you have an existing autoload function, add a call to this function
14
+ * from your existing __autoload() implementation.
15
+ *
16
+ * @param string $class
17
+ */
18
+ function DOMPDF_autoload($class) {
19
+ $filename = DOMPDF_INC_DIR . "/" . mb_strtolower($class) . ".cls.php";
20
+
21
+ if ( is_file($filename) ) {
22
+ include_once $filename;
23
+ }
24
+ }
25
+
26
+ // If SPL autoload functions are available (PHP >= 5.1.2)
27
+ if ( function_exists("spl_autoload_register") ) {
28
+ $autoload = "DOMPDF_autoload";
29
+ $funcs = spl_autoload_functions();
30
+
31
+ // No functions currently in the stack.
32
+ if ( !DOMPDF_AUTOLOAD_PREPEND || $funcs === false ) {
33
+ spl_autoload_register($autoload);
34
+ }
35
+
36
+ // If PHP >= 5.3 the $prepend argument is available
37
+ else if ( PHP_VERSION_ID >= 50300 ) {
38
+ spl_autoload_register($autoload, true, true);
39
+ }
40
+
41
+ else {
42
+ // Unregister existing autoloaders...
43
+ $compat = (PHP_VERSION_ID <= 50102 && PHP_VERSION_ID >= 50100);
44
+
45
+ foreach ($funcs as $func) {
46
+ if (is_array($func)) {
47
+ // :TRICKY: There are some compatibility issues and some
48
+ // places where we need to error out
49
+ $reflector = new ReflectionMethod($func[0], $func[1]);
50
+ if (!$reflector->isStatic()) {
51
+ throw new Exception('This function is not compatible with non-static object methods due to PHP Bug #44144.');
52
+ }
53
+
54
+ // Suprisingly, spl_autoload_register supports the
55
+ // Class::staticMethod callback format, although call_user_func doesn't
56
+ if ($compat) $func = implode('::', $func);
57
+ }
58
+
59
+ spl_autoload_unregister($func);
60
+ }
61
+
62
+ // Register the new one, thus putting it at the front of the stack...
63
+ spl_autoload_register($autoload);
64
+
65
+ // Now, go back and re-register all of our old ones.
66
+ foreach ($funcs as $func) {
67
+ spl_autoload_register($func);
68
+ }
69
+
70
+ // Be polite and ensure that userland autoload gets retained
71
+ if ( function_exists("__autoload") ) {
72
+ spl_autoload_register("__autoload");
73
+ }
74
+ }
75
+ }
76
+
77
+ else if ( !function_exists("__autoload") ) {
78
+ /**
79
+ * Default __autoload() function
80
+ *
81
+ * @param string $class
82
+ */
83
+ function __autoload($class) {
84
+ DOMPDF_autoload($class);
85
+ }
86
+ }
dompdf/include/block_frame_decorator.cls.php ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Decorates frames for block layout
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Block_Frame_Decorator extends Frame_Decorator {
16
+ /**
17
+ * Current line index
18
+ *
19
+ * @var int
20
+ */
21
+ protected $_cl;
22
+
23
+ /**
24
+ * The block's line boxes
25
+ *
26
+ * @var Line_Box[]
27
+ */
28
+ protected $_line_boxes;
29
+
30
+ function __construct(Frame $frame, DOMPDF $dompdf) {
31
+ parent::__construct($frame, $dompdf);
32
+
33
+ $this->_line_boxes = array(new Line_Box($this));
34
+ $this->_cl = 0;
35
+ }
36
+
37
+ function reset() {
38
+ parent::reset();
39
+
40
+ $this->_line_boxes = array(new Line_Box($this));
41
+ $this->_cl = 0;
42
+ }
43
+
44
+ /**
45
+ * @return Line_Box
46
+ */
47
+ function get_current_line_box() {
48
+ return $this->_line_boxes[$this->_cl];
49
+ }
50
+
51
+ /**
52
+ * @return integer
53
+ */
54
+ function get_current_line_number() {
55
+ return $this->_cl;
56
+ }
57
+
58
+ /**
59
+ * @return Line_Box[]
60
+ */
61
+ function get_line_boxes() {
62
+ return $this->_line_boxes;
63
+ }
64
+
65
+ /**
66
+ * @param integer $i
67
+ */
68
+ function clear_line($i) {
69
+ if ( isset($this->_line_boxes[$i]) ) {
70
+ unset($this->_line_boxes[$i]);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * @param Frame $frame
76
+ */
77
+ function add_frame_to_line(Frame $frame) {
78
+ if ( !$frame->is_in_flow() ) {
79
+ return;
80
+ }
81
+
82
+ $style = $frame->get_style();
83
+
84
+ $frame->set_containing_line($this->_line_boxes[$this->_cl]);
85
+
86
+ /*
87
+ // Adds a new line after a block, only if certain conditions are met
88
+ if ((($frame instanceof Inline_Frame_Decorator && $frame->get_node()->nodeName !== "br") ||
89
+ $frame instanceof Text_Frame_Decorator && trim($frame->get_text())) &&
90
+ ($frame->get_prev_sibling() && $frame->get_prev_sibling()->get_style()->display === "block" &&
91
+ $this->_line_boxes[$this->_cl]->w > 0 )) {
92
+
93
+ $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
94
+ $this->add_line();
95
+
96
+ // Add each child of the inline frame to the line individually
97
+ foreach ($frame->get_children() as $child)
98
+ $this->add_frame_to_line( $child );
99
+ }
100
+ else*/
101
+
102
+ // Handle inline frames (which are effectively wrappers)
103
+ if ( $frame instanceof Inline_Frame_Decorator ) {
104
+
105
+ // Handle line breaks
106
+ if ( $frame->get_node()->nodeName === "br" ) {
107
+ $this->maximize_line_height( $style->length_in_pt($style->line_height), $frame );
108
+ $this->add_line(true);
109
+ }
110
+
111
+ return;
112
+ }
113
+
114
+ // Trim leading text if this is an empty line. Kinda a hack to put it here,
115
+ // but what can you do...
116
+ if ( $this->get_current_line_box()->w == 0 &&
117
+ $frame->is_text_node() &&
118
+ !$frame->is_pre() ) {
119
+
120
+ $frame->set_text( ltrim($frame->get_text()) );
121
+ $frame->recalculate_width();
122
+ }
123
+
124
+ $w = $frame->get_margin_width();
125
+
126
+ if ( $w == 0 ) {
127
+ return;
128
+ }
129
+
130
+ // Debugging code:
131
+ /*
132
+ pre_r("\n<h3>Adding frame to line:</h3>");
133
+
134
+ // pre_r("Me: " . $this->get_node()->nodeName . " (" . spl_object_hash($this->get_node()) . ")");
135
+ // pre_r("Node: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")");
136
+ if ( $frame->is_text_node() )
137
+ pre_r('"'.$frame->get_node()->nodeValue.'"');
138
+
139
+ pre_r("Line width: " . $this->_line_boxes[$this->_cl]->w);
140
+ pre_r("Frame: " . get_class($frame));
141
+ pre_r("Frame width: " . $w);
142
+ pre_r("Frame height: " . $frame->get_margin_height());
143
+ pre_r("Containing block width: " . $this->get_containing_block("w"));
144
+ */
145
+ // End debugging
146
+
147
+ $line = $this->_line_boxes[$this->_cl];
148
+ if ( $line->left + $line->w + $line->right + $w > $this->get_containing_block("w")) {
149
+ $this->add_line();
150
+ }
151
+
152
+ $frame->position();
153
+
154
+ $current_line = $this->_line_boxes[$this->_cl];
155
+ $current_line->add_frame($frame);
156
+
157
+ if ( $frame->is_text_node() ) {
158
+ $current_line->wc += count(preg_split("/\s+/", trim($frame->get_text())));
159
+ }
160
+
161
+ $this->increase_line_width($w);
162
+
163
+ $this->maximize_line_height($frame->get_margin_height(), $frame);
164
+ }
165
+
166
+ function remove_frames_from_line(Frame $frame) {
167
+ // Search backwards through the lines for $frame
168
+ $i = $this->_cl;
169
+ $j = null;
170
+
171
+ while ($i >= 0) {
172
+ if ( ($j = in_array($frame, $this->_line_boxes[$i]->get_frames(), true)) !== false ) {
173
+ break;
174
+ }
175
+
176
+ $i--;
177
+ }
178
+
179
+ if ( $j === false ) {
180
+ return;
181
+ }
182
+
183
+ // Remove $frame and all frames that follow
184
+ while ($j < count($this->_line_boxes[$i]->get_frames())) {
185
+ $frames = $this->_line_boxes[$i]->get_frames();
186
+ $f = $frames[$j];
187
+ $frames[$j] = null;
188
+ unset($frames[$j]);
189
+ $j++;
190
+ $this->_line_boxes[$i]->w -= $f->get_margin_width();
191
+ }
192
+
193
+ // Recalculate the height of the line
194
+ $h = 0;
195
+ foreach ($this->_line_boxes[$i]->get_frames() as $f) {
196
+ $h = max( $h, $f->get_margin_height() );
197
+ }
198
+
199
+ $this->_line_boxes[$i]->h = $h;
200
+
201
+ // Remove all lines that follow
202
+ while ($this->_cl > $i) {
203
+ $this->_line_boxes[ $this->_cl ] = null;
204
+ unset($this->_line_boxes[ $this->_cl ]);
205
+ $this->_cl--;
206
+ }
207
+ }
208
+
209
+ function increase_line_width($w) {
210
+ $this->_line_boxes[ $this->_cl ]->w += $w;
211
+ }
212
+
213
+ function maximize_line_height($val, Frame $frame) {
214
+ if ( $val > $this->_line_boxes[ $this->_cl ]->h ) {
215
+ $this->_line_boxes[ $this->_cl ]->tallest_frame = $frame;
216
+ $this->_line_boxes[ $this->_cl ]->h = $val;
217
+ }
218
+ }
219
+
220
+ function add_line($br = false) {
221
+
222
+ // if ( $this->_line_boxes[$this->_cl]["h"] == 0 ) //count($this->_line_boxes[$i]["frames"]) == 0 ||
223
+ // return;
224
+
225
+ $this->_line_boxes[$this->_cl]->br = $br;
226
+ $y = $this->_line_boxes[$this->_cl]->y + $this->_line_boxes[$this->_cl]->h;
227
+
228
+ $new_line = new Line_Box($this, $y);
229
+
230
+ $this->_line_boxes[ ++$this->_cl ] = $new_line;
231
+ }
232
+
233
+ //........................................................................
234
+ }
dompdf/include/block_frame_reflower.cls.php ADDED
@@ -0,0 +1,805 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Reflows block frames
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Block_Frame_Reflower extends Frame_Reflower {
17
+ // Minimum line width to justify, as fraction of available width
18
+ const MIN_JUSTIFY_WIDTH = 0.80;
19
+
20
+ /**
21
+ * @var Block_Frame_Decorator
22
+ */
23
+ protected $_frame;
24
+
25
+ function __construct(Block_Frame_Decorator $frame) { parent::__construct($frame); }
26
+
27
+ /**
28
+ * Calculate the ideal used value for the width property as per:
29
+ * http://www.w3.org/TR/CSS21/visudet.html#Computing_widths_and_margins
30
+ *
31
+ * @param float $width
32
+ * @return array
33
+ */
34
+ protected function _calculate_width($width) {
35
+ $frame = $this->_frame;
36
+ $style = $frame->get_style();
37
+ $w = $frame->get_containing_block("w");
38
+
39
+ if ( $style->position === "fixed" ) {
40
+ $w = $frame->get_parent()->get_containing_block("w");
41
+ }
42
+
43
+ $rm = $style->length_in_pt($style->margin_right, $w);
44
+ $lm = $style->length_in_pt($style->margin_left, $w);
45
+
46
+ $left = $style->length_in_pt($style->left, $w);
47
+ $right = $style->length_in_pt($style->right, $w);
48
+
49
+ // Handle 'auto' values
50
+ $dims = array($style->border_left_width,
51
+ $style->border_right_width,
52
+ $style->padding_left,
53
+ $style->padding_right,
54
+ $width !== "auto" ? $width : 0,
55
+ $rm !== "auto" ? $rm : 0,
56
+ $lm !== "auto" ? $lm : 0);
57
+
58
+ // absolutely positioned boxes take the 'left' and 'right' properties into account
59
+ if ( $frame->is_absolute() ) {
60
+ $absolute = true;
61
+ $dims[] = $left !== "auto" ? $left : 0;
62
+ $dims[] = $right !== "auto" ? $right : 0;
63
+ }
64
+ else {
65
+ $absolute = false;
66
+ }
67
+
68
+ $sum = $style->length_in_pt($dims, $w);
69
+
70
+ // Compare to the containing block
71
+ $diff = $w - $sum;
72
+
73
+ if ( $diff > 0 ) {
74
+
75
+ if ( $absolute ) {
76
+
77
+ // resolve auto properties: see
78
+ // http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width
79
+
80
+ if ( $width === "auto" && $left === "auto" && $right === "auto" ) {
81
+
82
+ if ( $lm === "auto" ) $lm = 0;
83
+ if ( $rm === "auto" ) $rm = 0;
84
+
85
+ // Technically, the width should be "shrink-to-fit" i.e. based on the
86
+ // preferred width of the content... a little too costly here as a
87
+ // special case. Just get the width to take up the slack:
88
+ $left = 0;
89
+ $right = 0;
90
+ $width = $diff;
91
+ }
92
+ else if ( $width === "auto" ) {
93
+
94
+ if ( $lm === "auto" ) $lm = 0;
95
+ if ( $rm === "auto" ) $rm = 0;
96
+ if ( $left === "auto" ) $left = 0;
97
+ if ( $right === "auto" ) $right = 0;
98
+
99
+ $width = $diff;
100
+ }
101
+ else if ( $left === "auto" ) {
102
+
103
+ if ( $lm === "auto" ) $lm = 0;
104
+ if ( $rm === "auto" ) $rm = 0;
105
+ if ( $right === "auto" ) $right = 0;
106
+
107
+ $left = $diff;
108
+ }
109
+ else if ( $right === "auto" ) {
110
+
111
+ if ( $lm === "auto" ) $lm = 0;
112
+ if ( $rm === "auto" ) $rm = 0;
113
+
114
+ $right = $diff;
115
+ }
116
+
117
+ }
118
+ else {
119
+
120
+ // Find auto properties and get them to take up the slack
121
+ if ( $width === "auto" ) {
122
+ $width = $diff;
123
+ }
124
+ else if ( $lm === "auto" && $rm === "auto" ) {
125
+ $lm = $rm = round($diff / 2);
126
+ }
127
+ else if ( $lm === "auto" ) {
128
+ $lm = $diff;
129
+ }
130
+ else if ( $rm === "auto" ) {
131
+ $rm = $diff;
132
+ }
133
+ }
134
+
135
+ }
136
+ else if ($diff < 0) {
137
+
138
+ // We are over constrained--set margin-right to the difference
139
+ $rm = $diff;
140
+
141
+ }
142
+
143
+ return array(
144
+ "width" => $width,
145
+ "margin_left" => $lm,
146
+ "margin_right" => $rm,
147
+ "left" => $left,
148
+ "right" => $right,
149
+ );
150
+ }
151
+
152
+ /**
153
+ * Call the above function, but resolve max/min widths
154
+ *
155
+ * @throws DOMPDF_Exception
156
+ * @return array
157
+ */
158
+ protected function _calculate_restricted_width() {
159
+ $frame = $this->_frame;
160
+ $style = $frame->get_style();
161
+ $cb = $frame->get_containing_block();
162
+
163
+ if ( $style->position === "fixed" ) {
164
+ $cb = $frame->get_root()->get_containing_block();
165
+ }
166
+
167
+ //if ( $style->position === "absolute" )
168
+ // $cb = $frame->find_positionned_parent()->get_containing_block();
169
+
170
+ if ( !isset($cb["w"]) ) {
171
+ throw new DOMPDF_Exception("Box property calculation requires containing block width");
172
+ }
173
+
174
+ // Treat width 100% as auto
175
+ if ( $style->width === "100%" ) {
176
+ $width = "auto";
177
+ }
178
+ else {
179
+ $width = $style->length_in_pt($style->width, $cb["w"]);
180
+ }
181
+
182
+ extract($this->_calculate_width($width));
183
+
184
+ // Handle min/max width
185
+ $min_width = $style->length_in_pt($style->min_width, $cb["w"]);
186
+ $max_width = $style->length_in_pt($style->max_width, $cb["w"]);
187
+
188
+ if ( $max_width !== "none" && $min_width > $max_width ) {
189
+ list($max_width, $min_width) = array($min_width, $max_width);
190
+ }
191
+
192
+ if ( $max_width !== "none" && $width > $max_width ) {
193
+ extract($this->_calculate_width($max_width));
194
+ }
195
+
196
+ if ( $width < $min_width ) {
197
+ extract($this->_calculate_width($min_width));
198
+ }
199
+
200
+ return array($width, $margin_left, $margin_right, $left, $right);
201
+ }
202
+
203
+ /**
204
+ * Determine the unrestricted height of content within the block
205
+ * not by adding each line's height, but by getting the last line's position.
206
+ * This because lines could have been pushed lower by a clearing element.
207
+ *
208
+ * @return float
209
+ */
210
+ protected function _calculate_content_height() {
211
+ $lines = $this->_frame->get_line_boxes();
212
+ $height = 0;
213
+
214
+ foreach ($lines as $line) {
215
+ $height += $line->h;
216
+ }
217
+
218
+ /*
219
+ $first_line = reset($lines);
220
+ $last_line = end($lines);
221
+ $height2 = $last_line->y + $last_line->h - $first_line->y;
222
+ */
223
+
224
+ return $height;
225
+ }
226
+
227
+ /**
228
+ * Determine the frame's restricted height
229
+ *
230
+ * @return array
231
+ */
232
+ protected function _calculate_restricted_height() {
233
+ $frame = $this->_frame;
234
+ $style = $frame->get_style();
235
+ $content_height = $this->_calculate_content_height();
236
+ $cb = $frame->get_containing_block();
237
+
238
+ $height = $style->length_in_pt($style->height, $cb["h"]);
239
+
240
+ $top = $style->length_in_pt($style->top, $cb["h"]);
241
+ $bottom = $style->length_in_pt($style->bottom, $cb["h"]);
242
+
243
+ $margin_top = $style->length_in_pt($style->margin_top, $cb["h"]);
244
+ $margin_bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
245
+
246
+ if ( $frame->is_absolute() ) {
247
+
248
+ // see http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-height
249
+
250
+ $dims = array($top !== "auto" ? $top : 0,
251
+ $style->margin_top !== "auto" ? $style->margin_top : 0,
252
+ $style->padding_top,
253
+ $style->border_top_width,
254
+ $height !== "auto" ? $height : 0,
255
+ $style->border_bottom_width,
256
+ $style->padding_bottom,
257
+ $style->margin_bottom !== "auto" ? $style->margin_bottom : 0,
258
+ $bottom !== "auto" ? $bottom : 0);
259
+
260
+ $sum = $style->length_in_pt($dims, $cb["h"]);
261
+
262
+ $diff = $cb["h"] - $sum;
263
+
264
+ if ( $diff > 0 ) {
265
+
266
+ if ( $height === "auto" && $top === "auto" && $bottom === "auto" ) {
267
+
268
+ if ( $margin_top === "auto" ) $margin_top = 0;
269
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
270
+
271
+ $height = $diff;
272
+ }
273
+ else if ( $height === "auto" && $top === "auto" ) {
274
+
275
+ if ( $margin_top === "auto" ) $margin_top = 0;
276
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
277
+
278
+ $height = $content_height;
279
+ $top = $diff - $content_height;
280
+ }
281
+ else if ( $height === "auto" && $bottom === "auto" ) {
282
+
283
+ if ( $margin_top === "auto" ) $margin_top = 0;
284
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
285
+
286
+ $height = $content_height;
287
+ $bottom = $diff - $content_height;
288
+ }
289
+ else if ( $top === "auto" && $bottom === "auto" ) {
290
+
291
+ if ( $margin_top === "auto" ) $margin_top = 0;
292
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
293
+
294
+ $bottom = $diff;
295
+ }
296
+ else if ( $top === "auto" ) {
297
+
298
+ if ( $margin_top === "auto" ) $margin_top = 0;
299
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
300
+
301
+ $top = $diff;
302
+ }
303
+ else if ( $height === "auto" ) {
304
+
305
+ if ( $margin_top === "auto" ) $margin_top = 0;
306
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
307
+
308
+ $height = $diff;
309
+ }
310
+ else if ( $bottom === "auto" ) {
311
+
312
+ if ( $margin_top === "auto" ) $margin_top = 0;
313
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
314
+
315
+ $bottom = $diff;
316
+ }
317
+ else {
318
+
319
+ if ( $style->overflow === "visible" ) {
320
+ // set all autos to zero
321
+ if ( $margin_top === "auto" ) $margin_top = 0;
322
+ if ( $margin_bottom === "auto" ) $margin_bottom = 0;
323
+ if ( $top === "auto" ) $top = 0;
324
+ if ( $bottom === "auto" ) $bottom = 0;
325
+ if ( $height === "auto" ) $height = $content_height;
326
+ }
327
+
328
+ // FIXME: overflow hidden
329
+ }
330
+
331
+ }
332
+
333
+ }
334
+ else {
335
+
336
+ // Expand the height if overflow is visible
337
+ if ( $height === "auto" && $content_height > $height /* && $style->overflow === "visible" */) {
338
+ $height = $content_height;
339
+ }
340
+
341
+ // FIXME: this should probably be moved to a seperate function as per
342
+ // _calculate_restricted_width
343
+
344
+ // Only handle min/max height if the height is independent of the frame's content
345
+ if ( !($style->overflow === "visible" ||
346
+ ($style->overflow === "hidden" && $height === "auto")) ) {
347
+
348
+ $min_height = $style->min_height;
349
+ $max_height = $style->max_height;
350
+
351
+ if ( isset($cb["h"]) ) {
352
+ $min_height = $style->length_in_pt($min_height, $cb["h"]);
353
+ $max_height = $style->length_in_pt($max_height, $cb["h"]);
354
+
355
+ }
356
+ else if ( isset($cb["w"]) ) {
357
+
358
+ if ( mb_strpos($min_height, "%") !== false ) {
359
+ $min_height = 0;
360
+ }
361
+ else {
362
+ $min_height = $style->length_in_pt($min_height, $cb["w"]);
363
+ }
364
+
365
+ if ( mb_strpos($max_height, "%") !== false ) {
366
+ $max_height = "none";
367
+ }
368
+ else {
369
+ $max_height = $style->length_in_pt($max_height, $cb["w"]);
370
+ }
371
+ }
372
+
373
+ if ( $max_height !== "none" && $min_height > $max_height ) {
374
+ // Swap 'em
375
+ list($max_height, $min_height) = array($min_height, $max_height);
376
+ }
377
+
378
+ if ( $max_height !== "none" && $height > $max_height ) {
379
+ $height = $max_height;
380
+ }
381
+
382
+ if ( $height < $min_height ) {
383
+ $height = $min_height;
384
+ }
385
+ }
386
+
387
+ }
388
+
389
+ return array($height, $margin_top, $margin_bottom, $top, $bottom);
390
+
391
+ }
392
+
393
+ /**
394
+ * Adjust the justification of each of our lines.
395
+ * http://www.w3.org/TR/CSS21/text.html#propdef-text-align
396
+ */
397
+ protected function _text_align() {
398
+ $style = $this->_frame->get_style();
399
+ $w = $this->_frame->get_containing_block("w");
400
+ $width = $style->length_in_pt($style->width, $w);
401
+
402
+ switch ($style->text_align) {
403
+ default:
404
+ case "left":
405
+ foreach ($this->_frame->get_line_boxes() as $line) {
406
+ if ( !$line->left ) {
407
+ continue;
408
+ }
409
+
410
+ foreach($line->get_frames() as $frame) {
411
+ if ( $frame instanceof Block_Frame_Decorator) {
412
+ continue;
413
+ }
414
+ $frame->set_position( $frame->get_position("x") + $line->left );
415
+ }
416
+ }
417
+ return;
418
+
419
+ case "right":
420
+ foreach ($this->_frame->get_line_boxes() as $line) {
421
+ // Move each child over by $dx
422
+ $dx = $width - $line->w - $line->right;
423
+
424
+ foreach($line->get_frames() as $frame) {
425
+ // Block frames are not aligned by text-align
426
+ if ($frame instanceof Block_Frame_Decorator) {
427
+ continue;
428
+ }
429
+
430
+ $frame->set_position( $frame->get_position("x") + $dx );
431
+ }
432
+ }
433
+ break;
434
+
435
+
436
+ case "justify":
437
+ // We justify all lines except the last one
438
+ $lines = $this->_frame->get_line_boxes(); // needs to be a variable (strict standards)
439
+ array_pop($lines);
440
+
441
+ foreach($lines as $i => $line) {
442
+ if ( $line->br ) {
443
+ unset($lines[$i]);
444
+ }
445
+ }
446
+
447
+ // One space character's width. Will be used to get a more accurate spacing
448
+ $space_width = Font_Metrics::get_text_width(" ", $style->font_family, $style->font_size);
449
+
450
+ foreach ($lines as $line) {
451
+ if ( $line->left ) {
452
+ foreach ( $line->get_frames() as $frame ) {
453
+ if ( !$frame instanceof Text_Frame_Decorator ) {
454
+ continue;
455
+ }
456
+
457
+ $frame->set_position( $frame->get_position("x") + $line->left );
458
+ }
459
+ }
460
+
461
+ // Only set the spacing if the line is long enough. This is really
462
+ // just an aesthetic choice ;)
463
+ //if ( $line["left"] + $line["w"] + $line["right"] > self::MIN_JUSTIFY_WIDTH * $width ) {
464
+
465
+ // Set the spacing for each child
466
+ if ( $line->wc > 1 ) {
467
+ $spacing = ($width - ($line->left + $line->w + $line->right) + $space_width) / ($line->wc - 1);
468
+ }
469
+ else {
470
+ $spacing = 0;
471
+ }
472
+
473
+ $dx = 0;
474
+ foreach($line->get_frames() as $frame) {
475
+ if ( !$frame instanceof Text_Frame_Decorator ) {
476
+ continue;
477
+ }
478
+
479
+ $text = $frame->get_text();
480
+ $spaces = mb_substr_count($text, " ");
481
+
482
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
483
+ $_spacing = $spacing + $char_spacing;
484
+
485
+ $frame->set_position( $frame->get_position("x") + $dx );
486
+ $frame->set_text_spacing($_spacing);
487
+
488
+ $dx += $spaces * $_spacing;
489
+ }
490
+
491
+ // The line (should) now occupy the entire width
492
+ $line->w = $width;
493
+
494
+ //}
495
+ }
496
+ break;
497
+
498
+ case "center":
499
+ case "centre":
500
+ foreach ($this->_frame->get_line_boxes() as $line) {
501
+ // Centre each line by moving each frame in the line by:
502
+ $dx = ($width + $line->left - $line->w - $line->right ) / 2;
503
+
504
+ foreach ($line->get_frames() as $frame) {
505
+ // Block frames are not aligned by text-align
506
+ if ($frame instanceof Block_Frame_Decorator) {
507
+ continue;
508
+ }
509
+
510
+ $frame->set_position( $frame->get_position("x") + $dx );
511
+ }
512
+ }
513
+ break;
514
+ }
515
+ }
516
+
517
+ /**
518
+ * Align inline children vertically.
519
+ * Aligns each child vertically after each line is reflowed
520
+ */
521
+ function vertical_align() {
522
+
523
+ $canvas = null;
524
+
525
+ foreach ( $this->_frame->get_line_boxes() as $line ) {
526
+
527
+ $height = $line->h;
528
+
529
+ foreach ( $line->get_frames() as $frame ) {
530
+ $style = $frame->get_style();
531
+
532
+ if ( $style->display !== "inline" ) {
533
+ continue;
534
+ }
535
+
536
+ $align = $frame->get_parent()->get_style()->vertical_align;
537
+
538
+ if ( !isset($canvas) ) {
539
+ $canvas = $frame->get_root()->get_dompdf()->get_canvas();
540
+ }
541
+
542
+ $baseline = $canvas->get_font_baseline($style->font_family, $style->font_size);
543
+ $y_offset = 0;
544
+
545
+ switch ($align) {
546
+ case "baseline":
547
+ $y_offset = $height*0.8 - $baseline; // The 0.8 ratio is arbitrary until we find it's meaning
548
+ break;
549
+
550
+ case "middle":
551
+ $y_offset = ($height*0.8 - $baseline) / 2;
552
+ break;
553
+
554
+ case "sub":
555
+ $y_offset = 0.3 * $height;
556
+ break;
557
+
558
+ case "super":
559
+ $y_offset = -0.2 * $height;
560
+ break;
561
+
562
+ case "text-top":
563
+ case "top": // Not strictly accurate, but good enough for now
564
+ break;
565
+
566
+ case "text-bottom":
567
+ case "bottom":
568
+ $y_offset = $height*0.8 - $baseline;
569
+ break;
570
+ }
571
+
572
+ if ( $y_offset ) {
573
+ $frame->move(0, $y_offset);
574
+ }
575
+ }
576
+ }
577
+ }
578
+
579
+ /**
580
+ * @param Frame $child
581
+ */
582
+ function process_clear(Frame $child){
583
+ $enable_css_float = $this->get_dompdf()->get_option("enable_css_float");
584
+ if ( !$enable_css_float ) {
585
+ return;
586
+ }
587
+
588
+ $child_style = $child->get_style();
589
+ $root = $this->_frame->get_root();
590
+
591
+ // Handle "clear"
592
+ if ( $child_style->clear !== "none" ) {
593
+ $lowest_y = $root->get_lowest_float_offset($child);
594
+
595
+ // If a float is still applying, we handle it
596
+ if ( $lowest_y ) {
597
+ if ( $child->is_in_flow() ) {
598
+ $line_box = $this->_frame->get_current_line_box();
599
+ $line_box->y = $lowest_y + $child->get_margin_height();
600
+ $line_box->left = 0;
601
+ $line_box->right = 0;
602
+ }
603
+
604
+ $child->move(0, $lowest_y - $child->get_position("y"));
605
+ }
606
+ }
607
+ }
608
+
609
+ /**
610
+ * @param Frame $child
611
+ * @param float $cb_x
612
+ * @param float $cb_w
613
+ */
614
+ function process_float(Frame $child, $cb_x, $cb_w){
615
+ $enable_css_float = $this->_frame->get_dompdf()->get_option("enable_css_float");
616
+ if ( !$enable_css_float ) {
617
+ return;
618
+ }
619
+
620
+ $child_style = $child->get_style();
621
+ $root = $this->_frame->get_root();
622
+
623
+ // Handle "float"
624
+ if ( $child_style->float !== "none" ) {
625
+ $root->add_floating_frame($child);
626
+
627
+ // Remove next frame's beginning whitespace
628
+ $next = $child->get_next_sibling();
629
+ if ( $next && $next instanceof Text_Frame_Decorator) {
630
+ $next->set_text(ltrim($next->get_text()));
631
+ }
632
+
633
+ $line_box = $this->_frame->get_current_line_box();
634
+ list($old_x, $old_y) = $child->get_position();
635
+
636
+ $float_x = $cb_x;
637
+ $float_y = $old_y;
638
+ $float_w = $child->get_margin_width();
639
+
640
+ if ( $child_style->clear === "none" ) {
641
+ switch( $child_style->float ) {
642
+ case "left":
643
+ $float_x += $line_box->left;
644
+ break;
645
+ case "right":
646
+ $float_x += ($cb_w - $line_box->right - $float_w);
647
+ break;
648
+ }
649
+ }
650
+ else {
651
+ if ( $child_style->float === "right" ) {
652
+ $float_x += ($cb_w - $float_w);
653
+ }
654
+ }
655
+
656
+ if ( $cb_w < $float_x + $float_w - $old_x ) {
657
+ // TODO handle when floating elements don't fit
658
+ }
659
+
660
+ $line_box->get_float_offsets();
661
+
662
+ if ( $child->_float_next_line ) {
663
+ $float_y += $line_box->h;
664
+ }
665
+
666
+ $child->set_position($float_x, $float_y);
667
+ $child->move($float_x - $old_x, $float_y - $old_y, true);
668
+ }
669
+ }
670
+
671
+ /**
672
+ * @param Frame_Decorator $block
673
+ */
674
+ function reflow(Block_Frame_Decorator $block = null) {
675
+
676
+ // Check if a page break is forced
677
+ $page = $this->_frame->get_root();
678
+ $page->check_forced_page_break($this->_frame);
679
+
680
+ // Bail if the page is full
681
+ if ( $page->is_full() ) {
682
+ return;
683
+ }
684
+
685
+ // Generated content
686
+ $this->_set_content();
687
+
688
+ // Collapse margins if required
689
+ $this->_collapse_margins();
690
+
691
+ $style = $this->_frame->get_style();
692
+ $cb = $this->_frame->get_containing_block();
693
+
694
+ if ( $style->position === "fixed" ) {
695
+ $cb = $this->_frame->get_root()->get_containing_block();
696
+ }
697
+
698
+ // Determine the constraints imposed by this frame: calculate the width
699
+ // of the content area:
700
+ list($w, $left_margin, $right_margin, $left, $right) = $this->_calculate_restricted_width();
701
+
702
+ // Store the calculated properties
703
+ $style->width = $w . "pt";
704
+ $style->margin_left = $left_margin."pt";
705
+ $style->margin_right = $right_margin."pt";
706
+ $style->left = $left ."pt";
707
+ $style->right = $right . "pt";
708
+
709
+ // Update the position
710
+ $this->_frame->position();
711
+ list($x, $y) = $this->_frame->get_position();
712
+
713
+ // Adjust the first line based on the text-indent property
714
+ $indent = $style->length_in_pt($style->text_indent, $cb["w"]);
715
+ $this->_frame->increase_line_width($indent);
716
+
717
+ // Determine the content edge
718
+ $top = $style->length_in_pt(array($style->margin_top,
719
+ $style->padding_top,
720
+ $style->border_top_width), $cb["h"]);
721
+
722
+ $bottom = $style->length_in_pt(array($style->border_bottom_width,
723
+ $style->margin_bottom,
724
+ $style->padding_bottom), $cb["h"]);
725
+
726
+ $cb_x = $x + $left_margin + $style->length_in_pt(array($style->border_left_width,
727
+ $style->padding_left), $cb["w"]);
728
+
729
+ $cb_y = $y + $top;
730
+
731
+ $cb_h = ($cb["h"] + $cb["y"]) - $bottom - $cb_y;
732
+
733
+ // Set the y position of the first line in this block
734
+ $line_box = $this->_frame->get_current_line_box();
735
+ $line_box->y = $cb_y;
736
+ $line_box->get_float_offsets();
737
+
738
+ // Set the containing blocks and reflow each child
739
+ foreach ( $this->_frame->get_children() as $child ) {
740
+
741
+ // Bail out if the page is full
742
+ if ( $page->is_full() ) {
743
+ break;
744
+ }
745
+
746
+ $child->set_containing_block($cb_x, $cb_y, $w, $cb_h);
747
+
748
+ $this->process_clear($child);
749
+
750
+ $child->reflow($this->_frame);
751
+
752
+ // Don't add the child to the line if a page break has occurred
753
+ if ( $page->check_page_break($child) ) {
754
+ break;
755
+ }
756
+
757
+ $this->process_float($child, $cb_x, $w);
758
+ }
759
+
760
+ // Determine our height
761
+ list($height, $margin_top, $margin_bottom, $top, $bottom) = $this->_calculate_restricted_height();
762
+ $style->height = $height;
763
+ $style->margin_top = $margin_top;
764
+ $style->margin_bottom = $margin_bottom;
765
+ $style->top = $top;
766
+ $style->bottom = $bottom;
767
+
768
+ $needs_reposition = ($style->position === "absolute" && ($style->right !== "auto" || $style->bottom !== "auto"));
769
+
770
+ // Absolute positioning measurement
771
+ if ( $needs_reposition ) {
772
+ $orig_style = $this->_frame->get_original_style();
773
+ if ( $orig_style->width === "auto" && ($orig_style->left === "auto" || $orig_style->right === "auto") ) {
774
+ $width = 0;
775
+ foreach ($this->_frame->get_line_boxes() as $line) {
776
+ $width = max($line->w, $width);
777
+ }
778
+ $style->width = $width;
779
+ }
780
+
781
+ $style->left = $orig_style->left;
782
+ $style->right = $orig_style->right;
783
+ }
784
+
785
+ $this->_text_align();
786
+ $this->vertical_align();
787
+
788
+ // Absolute positioning
789
+ if ( $needs_reposition ) {
790
+ list($x, $y) = $this->_frame->get_position();
791
+ $this->_frame->position();
792
+ list($new_x, $new_y) = $this->_frame->get_position();
793
+ $this->_frame->move($new_x-$x, $new_y-$y, true);
794
+ }
795
+
796
+ if ( $block && $this->_frame->is_in_flow() ) {
797
+ $block->add_frame_to_line($this->_frame);
798
+
799
+ // May be inline-block
800
+ if ( $style->display === "block" ) {
801
+ $block->add_line();
802
+ }
803
+ }
804
+ }
805
+ }
dompdf/include/block_positioner.cls.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Positions block frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Block_Positioner extends Positioner {
16
+
17
+
18
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
19
+
20
+ //........................................................................
21
+
22
+ function position() {
23
+ $frame = $this->_frame;
24
+ $style = $frame->get_style();
25
+ $cb = $frame->get_containing_block();
26
+ $p = $frame->find_block_parent();
27
+
28
+ if ( $p ) {
29
+ $float = $style->float;
30
+
31
+ $enable_css_float = $frame->get_dompdf()->get_option("enable_css_float");
32
+ if ( !$enable_css_float || !$float || $float === "none" ) {
33
+ $p->add_line(true);
34
+ }
35
+ $y = $p->get_current_line_box()->y;
36
+
37
+ }
38
+ else {
39
+ $y = $cb["y"];
40
+ }
41
+
42
+ $x = $cb["x"];
43
+
44
+ // Relative positionning
45
+ if ( $style->position === "relative" ) {
46
+ $top = $style->length_in_pt($style->top, $cb["h"]);
47
+ //$right = $style->length_in_pt($style->right, $cb["w"]);
48
+ //$bottom = $style->length_in_pt($style->bottom, $cb["h"]);
49
+ $left = $style->length_in_pt($style->left, $cb["w"]);
50
+
51
+ $x += $left;
52
+ $y += $top;
53
+ }
54
+
55
+ $frame->set_position($x, $y);
56
+ }
57
+ }
dompdf/include/block_renderer.cls.php ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Renders block frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Block_Renderer extends Abstract_Renderer {
16
+
17
+ //........................................................................
18
+
19
+ function render(Frame $frame) {
20
+ $style = $frame->get_style();
21
+ list($x, $y, $w, $h) = $frame->get_border_box();
22
+
23
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
24
+
25
+ if ( $frame->get_node()->nodeName === "body" ) {
26
+ $h = $frame->get_containing_block("h") - $style->length_in_pt(array(
27
+ $style->margin_top,
28
+ $style->padding_top,
29
+ $style->border_top_width,
30
+ $style->border_bottom_width,
31
+ $style->padding_bottom,
32
+ $style->margin_bottom),
33
+ $style->width);
34
+ }
35
+
36
+ // Draw our background, border and content
37
+ list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
38
+
39
+ if ( $tl + $tr + $br + $bl > 0 ) {
40
+ $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
41
+ }
42
+
43
+ if ( ($bg = $style->background_color) !== "transparent" ) {
44
+ $this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg );
45
+ }
46
+
47
+ if ( ($url = $style->background_image) && $url !== "none" ) {
48
+ $this->_background_image($url, $x, $y, $w, $h, $style);
49
+ }
50
+
51
+ if ( $tl + $tr + $br + $bl > 0 ) {
52
+ $this->_canvas->clipping_end();
53
+ }
54
+
55
+ $this->_render_border($frame);
56
+ $this->_render_outline($frame);
57
+
58
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
59
+ $this->_debug_layout($frame->get_border_box(), "red");
60
+ if (DEBUG_LAYOUT_PADDINGBOX) {
61
+ $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
62
+ }
63
+ }
64
+
65
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) {
66
+ foreach ($frame->get_decorator()->get_line_boxes() as $line) {
67
+ $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
68
+ }
69
+ }
70
+ }
71
+
72
+ protected function _render_border(Frame_Decorator $frame, $corner_style = "bevel") {
73
+ $style = $frame->get_style();
74
+ $bbox = $frame->get_border_box();
75
+ $bp = $style->get_border_properties();
76
+
77
+ // find the radius
78
+ $radius = $style->get_computed_border_radius($bbox["w"], $bbox["h"]);
79
+
80
+ // Short-cut: If all the borders are "solid" with the same color and style, and no radius, we'd better draw a rectangle
81
+ if (
82
+ in_array($bp["top"]["style"], array("solid", "dashed", "dotted")) &&
83
+ $bp["top"] == $bp["right"] &&
84
+ $bp["right"] == $bp["bottom"] &&
85
+ $bp["bottom"] == $bp["left"] &&
86
+ array_sum($radius) == 0
87
+ ) {
88
+ $props = $bp["top"];
89
+ if ( $props["color"] === "transparent" || $props["width"] <= 0 ) return;
90
+
91
+ list($x, $y, $w, $h) = $bbox;
92
+ $width = $style->length_in_pt($props["width"]);
93
+ $pattern = $this->_get_dash_pattern($props["style"], $width);
94
+ $this->_canvas->rectangle($x + $width / 2, $y + $width / 2, $w - $width, $h - $width, $props["color"], $width, $pattern);
95
+ return;
96
+ }
97
+
98
+ // Do it the long way
99
+ $widths = array($style->length_in_pt($bp["top"]["width"]),
100
+ $style->length_in_pt($bp["right"]["width"]),
101
+ $style->length_in_pt($bp["bottom"]["width"]),
102
+ $style->length_in_pt($bp["left"]["width"]));
103
+
104
+ foreach ($bp as $side => $props) {
105
+ list($x, $y, $w, $h) = $bbox;
106
+ $length = 0;
107
+ $r1 = 0;
108
+ $r2 = 0;
109
+
110
+ if ( !$props["style"] ||
111
+ $props["style"] === "none" ||
112
+ $props["width"] <= 0 ||
113
+ $props["color"] == "transparent" )
114
+ continue;
115
+
116
+ switch($side) {
117
+ case "top":
118
+ $length = $w;
119
+ $r1 = $radius["top-left"];
120
+ $r2 = $radius["top-right"];
121
+ break;
122
+
123
+ case "bottom":
124
+ $length = $w;
125
+ $y += $h;
126
+ $r1 = $radius["bottom-left"];
127
+ $r2 = $radius["bottom-right"];
128
+ break;
129
+
130
+ case "left":
131
+ $length = $h;
132
+ $r1 = $radius["top-left"];
133
+ $r2 = $radius["bottom-left"];
134
+ break;
135
+
136
+ case "right":
137
+ $length = $h;
138
+ $x += $w;
139
+ $r1 = $radius["top-right"];
140
+ $r2 = $radius["bottom-right"];
141
+ break;
142
+ default:
143
+ break;
144
+ }
145
+ $method = "_border_" . $props["style"];
146
+
147
+ // draw rounded corners
148
+ $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style, $r1, $r2);
149
+ }
150
+ }
151
+
152
+ protected function _render_outline(Frame_Decorator $frame, $corner_style = "bevel") {
153
+ $style = $frame->get_style();
154
+
155
+ $props = array(
156
+ "width" => $style->outline_width,
157
+ "style" => $style->outline_style,
158
+ "color" => $style->outline_color,
159
+ );
160
+
161
+ if ( !$props["style"] || $props["style"] === "none" || $props["width"] <= 0 )
162
+ return;
163
+
164
+ $bbox = $frame->get_border_box();
165
+ $offset = $style->length_in_pt($props["width"]);
166
+ $pattern = $this->_get_dash_pattern($props["style"], $offset);
167
+
168
+ // If the outline style is "solid" we'd better draw a rectangle
169
+ if ( in_array($props["style"], array("solid", "dashed", "dotted")) ) {
170
+ $bbox[0] -= $offset / 2;
171
+ $bbox[1] -= $offset / 2;
172
+ $bbox[2] += $offset;
173
+ $bbox[3] += $offset;
174
+
175
+ list($x, $y, $w, $h) = $bbox;
176
+ $this->_canvas->rectangle($x, $y, $w, $h, $props["color"], $offset, $pattern);
177
+ return;
178
+ }
179
+
180
+ $bbox[0] -= $offset;
181
+ $bbox[1] -= $offset;
182
+ $bbox[2] += $offset * 2;
183
+ $bbox[3] += $offset * 2;
184
+
185
+ $method = "_border_" . $props["style"];
186
+ $widths = array_fill(0, 4, $props["width"]);
187
+ $sides = array("top", "right", "left", "bottom");
188
+ $length = 0;
189
+
190
+ foreach ($sides as $side) {
191
+ list($x, $y, $w, $h) = $bbox;
192
+
193
+ switch($side) {
194
+ case "top":
195
+ $length = $w;
196
+ break;
197
+
198
+ case "bottom":
199
+ $length = $w;
200
+ $y += $h;
201
+ break;
202
+
203
+ case "left":
204
+ $length = $h;
205
+ break;
206
+
207
+ case "right":
208
+ $length = $h;
209
+ $x += $w;
210
+ break;
211
+ default:
212
+ break;
213
+ }
214
+
215
+ $this->$method($x, $y, $length, $props["color"], $widths, $side, $corner_style);
216
+ }
217
+ }
218
+ }
dompdf/include/cached_pdf_decorator.cls.php ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Caching canvas implementation
11
+ *
12
+ * Each rendered page is serialized and stored in the {@link Page_Cache}.
13
+ * This is useful for static forms/pages that do not need to be re-rendered
14
+ * all the time.
15
+ *
16
+ * This class decorates normal CPDF_Adapters. It is currently completely
17
+ * experimental.
18
+ *
19
+ * @access private
20
+ * @package dompdf
21
+ */
22
+ class Cached_PDF_Decorator extends CPDF_Adapter implements Canvas {
23
+ /**
24
+ * @var CPDF_Adapter
25
+ */
26
+ protected $_pdf;
27
+ protected $_cache_id;
28
+ protected $_current_page_id;
29
+ protected $_fonts; // fonts used in this document
30
+
31
+ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
32
+ $this->_fonts = array();
33
+ }
34
+
35
+ /**
36
+ * Must be called after constructor
37
+ *
38
+ * @param int $cache_id
39
+ * @param CPDF_Adapter $pdf
40
+ */
41
+ function init($cache_id, CPDF_Adapter $pdf) {
42
+ $this->_cache_id = $cache_id;
43
+ $this->_pdf = $pdf;
44
+ $this->_current_page_id = $this->_pdf->open_object();
45
+ }
46
+
47
+ //........................................................................
48
+
49
+ function get_cpdf() { return $this->_pdf->get_cpdf(); }
50
+
51
+ function open_object() { $this->_pdf->open_object(); }
52
+ function reopen_object($object) { $this->_pdf->reopen_object($object); }
53
+
54
+ function close_object() { $this->_pdf->close_object(); }
55
+
56
+ function add_object($object, $where = 'all') { $this->_pdf->add_object($object, $where); }
57
+
58
+ function serialize_object($id) { $this->_pdf->serialize_object($id); }
59
+
60
+ function reopen_serialized_object($obj) { $this->_pdf->reopen_serialized_object($obj); }
61
+
62
+ //........................................................................
63
+
64
+ function get_width() { return $this->_pdf->get_width(); }
65
+ function get_height() { return $this->_pdf->get_height(); }
66
+ function get_page_number() { return $this->_pdf->get_page_number(); }
67
+ function get_page_count() { return $this->_pdf->get_page_count(); }
68
+
69
+ function set_page_number($num) { $this->_pdf->set_page_number($num); }
70
+ function set_page_count($count) { $this->_pdf->set_page_count($count); }
71
+
72
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) {
73
+ $this->_pdf->line($x1, $y1, $x2, $y2, $color, $width, $style);
74
+ }
75
+
76
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) {
77
+ $this->_pdf->rectangle($x1, $y1, $w, $h, $color, $width, $style);
78
+ }
79
+
80
+ function filled_rectangle($x1, $y1, $w, $h, $color) {
81
+ $this->_pdf->filled_rectangle($x1, $y1, $w, $h, $color);
82
+ }
83
+
84
+ function polygon($points, $color, $width = null, $style = array(), $fill = false) {
85
+ $this->_pdf->polygon($points, $color, $width, $style, $fill);
86
+ }
87
+
88
+ function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) {
89
+ $this->_pdf->circle($x, $y, $r1, $color, $width, $style, $fill);
90
+ }
91
+
92
+ function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
93
+ $this->_pdf->image($img_url, $x, $y, $w, $h, $resolution);
94
+ }
95
+
96
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
97
+ $this->_fonts[$font] = true;
98
+ $this->_pdf->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
99
+ }
100
+
101
+ function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
102
+
103
+ // We want to remove this from cached pages since it may not be correct
104
+ $this->_pdf->close_object();
105
+ $this->_pdf->page_text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
106
+ $this->_pdf->reopen_object($this->_current_page_id);
107
+ }
108
+
109
+ function page_script($script, $type = 'text/php') {
110
+
111
+ // We want to remove this from cached pages since it may not be correct
112
+ $this->_pdf->close_object();
113
+ $this->_pdf->page_script($script, $type);
114
+ $this->_pdf->reopen_object($this->_current_page_id);
115
+ }
116
+
117
+ function new_page() {
118
+ $this->_pdf->close_object();
119
+
120
+ // Add the object to the current page
121
+ $this->_pdf->add_object($this->_current_page_id, "add");
122
+ $this->_pdf->new_page();
123
+
124
+ Page_Cache::store_page($this->_cache_id,
125
+ $this->_pdf->get_page_number() - 1,
126
+ $this->_pdf->serialize_object($this->_current_page_id));
127
+
128
+ $this->_current_page_id = $this->_pdf->open_object();
129
+ return $this->_current_page_id;
130
+ }
131
+
132
+ function stream($filename, $options = null) {
133
+ // Store the last page in the page cache
134
+ if ( !is_null($this->_current_page_id) ) {
135
+ $this->_pdf->close_object();
136
+ $this->_pdf->add_object($this->_current_page_id, "add");
137
+ Page_Cache::store_page($this->_cache_id,
138
+ $this->_pdf->get_page_number(),
139
+ $this->_pdf->serialize_object($this->_current_page_id));
140
+ Page_Cache::store_fonts($this->_cache_id, $this->_fonts);
141
+ $this->_current_page_id = null;
142
+ }
143
+
144
+ $this->_pdf->stream($filename);
145
+
146
+ }
147
+
148
+ function output($options = null) {
149
+ // Store the last page in the page cache
150
+ if ( !is_null($this->_current_page_id) ) {
151
+ $this->_pdf->close_object();
152
+ $this->_pdf->add_object($this->_current_page_id, "add");
153
+ Page_Cache::store_page($this->_cache_id,
154
+ $this->_pdf->get_page_number(),
155
+ $this->_pdf->serialize_object($this->_current_page_id));
156
+ $this->_current_page_id = null;
157
+ }
158
+
159
+ return $this->_pdf->output();
160
+ }
161
+
162
+ function get_messages() { return $this->_pdf->get_messages(); }
163
+
164
+ }
dompdf/include/canvas.cls.php ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Main rendering interface
12
+ *
13
+ * Currently {@link CPDF_Adapter}, {@link PDFLib_Adapter}, {@link TCPDF_Adapter}, and {@link GD_Adapter}
14
+ * implement this interface.
15
+ *
16
+ * Implementations should measure x and y increasing to the left and down,
17
+ * respectively, with the origin in the top left corner. Implementations
18
+ * are free to use a unit other than points for length, but I can't
19
+ * guarantee that the results will look any good.
20
+ *
21
+ * @package dompdf
22
+ */
23
+ interface Canvas {
24
+ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf);
25
+
26
+ /**
27
+ * @return DOMPDF
28
+ */
29
+ function get_dompdf();
30
+
31
+ /**
32
+ * Returns the current page number
33
+ *
34
+ * @return int
35
+ */
36
+ function get_page_number();
37
+
38
+ /**
39
+ * Returns the total number of pages
40
+ *
41
+ * @return int
42
+ */
43
+ function get_page_count();
44
+
45
+ /**
46
+ * Sets the total number of pages
47
+ *
48
+ * @param int $count
49
+ */
50
+ function set_page_count($count);
51
+
52
+ /**
53
+ * Draws a line from x1,y1 to x2,y2
54
+ *
55
+ * See {@link Style::munge_colour()} for the format of the colour array.
56
+ * See {@link Cpdf::setLineStyle()} for a description of the format of the
57
+ * $style parameter (aka dash).
58
+ *
59
+ * @param float $x1
60
+ * @param float $y1
61
+ * @param float $x2
62
+ * @param float $y2
63
+ * @param array $color
64
+ * @param float $width
65
+ * @param array $style
66
+ */
67
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = null);
68
+
69
+ /**
70
+ * Draws a rectangle at x1,y1 with width w and height h
71
+ *
72
+ * See {@link Style::munge_colour()} for the format of the colour array.
73
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
74
+ * parameter (aka dash)
75
+ *
76
+ * @param float $x1
77
+ * @param float $y1
78
+ * @param float $w
79
+ * @param float $h
80
+ * @param array $color
81
+ * @param float $width
82
+ * @param array $style
83
+ */
84
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = null);
85
+
86
+ /**
87
+ * Draws a filled rectangle at x1,y1 with width w and height h
88
+ *
89
+ * See {@link Style::munge_colour()} for the format of the colour array.
90
+ *
91
+ * @param float $x1
92
+ * @param float $y1
93
+ * @param float $w
94
+ * @param float $h
95
+ * @param array $color
96
+ */
97
+ function filled_rectangle($x1, $y1, $w, $h, $color);
98
+
99
+ /**
100
+ * Starts a clipping rectangle at x1,y1 with width w and height h
101
+ *
102
+ * @param float $x1
103
+ * @param float $y1
104
+ * @param float $w
105
+ * @param float $h
106
+ */
107
+ function clipping_rectangle($x1, $y1, $w, $h);
108
+
109
+ /**
110
+ * Starts a rounded clipping rectangle at x1,y1 with width w and height h
111
+ *
112
+ * @param float $x1
113
+ * @param float $y1
114
+ * @param float $w
115
+ * @param float $h
116
+ * @param float $tl
117
+ * @param float $tr
118
+ * @param float $br
119
+ * @param float $bl
120
+ *
121
+ * @return
122
+ */
123
+ function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl);
124
+
125
+ /**
126
+ * Ends the last clipping shape
127
+ */
128
+ function clipping_end();
129
+
130
+ /**
131
+ * Save current state
132
+ */
133
+ function save();
134
+
135
+ /**
136
+ * Restore last state
137
+ */
138
+ function restore();
139
+
140
+ /**
141
+ * Rotate
142
+ */
143
+ function rotate($angle, $x, $y);
144
+
145
+ /**
146
+ * Skew
147
+ */
148
+ function skew($angle_x, $angle_y, $x, $y);
149
+
150
+ /**
151
+ * Scale
152
+ */
153
+ function scale($s_x, $s_y, $x, $y);
154
+
155
+ /**
156
+ * Translate
157
+ */
158
+ function translate($t_x, $t_y);
159
+
160
+ /**
161
+ * Transform
162
+ */
163
+ function transform($a, $b, $c, $d, $e, $f);
164
+
165
+ /**
166
+ * Draws a polygon
167
+ *
168
+ * The polygon is formed by joining all the points stored in the $points
169
+ * array. $points has the following structure:
170
+ * <code>
171
+ * array(0 => x1,
172
+ * 1 => y1,
173
+ * 2 => x2,
174
+ * 3 => y2,
175
+ * ...
176
+ * );
177
+ * </code>
178
+ *
179
+ * See {@link Style::munge_colour()} for the format of the colour array.
180
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
181
+ * parameter (aka dash)
182
+ *
183
+ * @param array $points
184
+ * @param array $color
185
+ * @param float $width
186
+ * @param array $style
187
+ * @param bool $fill Fills the polygon if true
188
+ */
189
+ function polygon($points, $color, $width = null, $style = null, $fill = false);
190
+
191
+ /**
192
+ * Draws a circle at $x,$y with radius $r
193
+ *
194
+ * See {@link Style::munge_colour()} for the format of the colour array.
195
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
196
+ * parameter (aka dash)
197
+ *
198
+ * @param float $x
199
+ * @param float $y
200
+ * @param float $r
201
+ * @param array $color
202
+ * @param float $width
203
+ * @param array $style
204
+ * @param bool $fill Fills the circle if true
205
+ */
206
+ function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false);
207
+
208
+ /**
209
+ * Add an image to the pdf.
210
+ *
211
+ * The image is placed at the specified x and y coordinates with the
212
+ * given width and height.
213
+ *
214
+ * @param string $img_url the path to the image
215
+ * @param float $x x position
216
+ * @param float $y y position
217
+ * @param int $w width (in pixels)
218
+ * @param int $h height (in pixels)
219
+ * @param string $resolution The resolution of the image
220
+ */
221
+ function image($img_url, $x, $y, $w, $h, $resolution = "normal");
222
+
223
+ /**
224
+ * Add an arc to the PDF
225
+ * See {@link Style::munge_colour()} for the format of the colour array.
226
+ *
227
+ * @param float $x X coordinate of the arc
228
+ * @param float $y Y coordinate of the arc
229
+ * @param float $r1 Radius 1
230
+ * @param float $r2 Radius 2
231
+ * @param float $astart Start angle in degrees
232
+ * @param float $aend End angle in degrees
233
+ * @param array $color Color
234
+ * @param float $width
235
+ * @param array $style
236
+ *
237
+ * @return void
238
+ */
239
+ function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array());
240
+
241
+ /**
242
+ * Writes text at the specified x and y coordinates
243
+ * See {@link Style::munge_colour()} for the format of the colour array.
244
+ *
245
+ * @param float $x
246
+ * @param float $y
247
+ * @param string $text the text to write
248
+ * @param string $font the font file to use
249
+ * @param float $size the font size, in points
250
+ * @param array $color
251
+ * @param float $word_space word spacing adjustment
252
+ * @param float $char_space char spacing adjustment
253
+ * @param float $angle angle
254
+ *
255
+ * @return void
256
+ */
257
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0);
258
+
259
+ /**
260
+ * Add a named destination (similar to <a name="foo">...</a> in html)
261
+ *
262
+ * @param string $anchorname The name of the named destination
263
+ */
264
+ function add_named_dest($anchorname);
265
+
266
+ /**
267
+ * Add a link to the pdf
268
+ *
269
+ * @param string $url The url to link to
270
+ * @param float $x The x position of the link
271
+ * @param float $y The y position of the link
272
+ * @param float $width The width of the link
273
+ * @param float $height The height of the link
274
+ *
275
+ * @return void
276
+ */
277
+ function add_link($url, $x, $y, $width, $height);
278
+
279
+ /**
280
+ * Add meta information to the pdf
281
+ *
282
+ * @param string $name Label of the value (Creator, Producer, etc.)
283
+ * @param string $value The text to set
284
+ */
285
+ function add_info($name, $value);
286
+
287
+ /**
288
+ * Calculates text size, in points
289
+ *
290
+ * @param string $text the text to be sized
291
+ * @param string $font the desired font
292
+ * @param float $size the desired font size
293
+ * @param float $word_spacing word spacing, if any
294
+ * @param float $char_spacing
295
+ *
296
+ * @return float
297
+ */
298
+ function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0);
299
+
300
+ /**
301
+ * Calculates font height, in points
302
+ *
303
+ * @param string $font
304
+ * @param float $size
305
+ *
306
+ * @return float
307
+ */
308
+ function get_font_height($font, $size);
309
+
310
+ /**
311
+ * Calculates font baseline, in points
312
+ *
313
+ * @param string $font
314
+ * @param float $size
315
+ *
316
+ * @return float
317
+ */
318
+ function get_font_baseline($font, $size);
319
+
320
+ /**
321
+ * Returns the font x-height, in points
322
+ *
323
+ * @param string $font
324
+ * @param float $size
325
+ *
326
+ * @return float
327
+ */
328
+ //function get_font_x_height($font, $size);
329
+
330
+ /**
331
+ * Sets the opacity
332
+ *
333
+ * @param float $opacity
334
+ * @param string $mode
335
+ */
336
+ function set_opacity($opacity, $mode = "Normal");
337
+
338
+ /**
339
+ * Sets the default view
340
+ *
341
+ * @param string $view
342
+ * 'XYZ' left, top, zoom
343
+ * 'Fit'
344
+ * 'FitH' top
345
+ * 'FitV' left
346
+ * 'FitR' left,bottom,right
347
+ * 'FitB'
348
+ * 'FitBH' top
349
+ * 'FitBV' left
350
+ * @param array $options
351
+ *
352
+ * @return void
353
+ */
354
+ function set_default_view($view, $options = array());
355
+
356
+ /**
357
+ * @param string $script
358
+ *
359
+ * @return void
360
+ */
361
+ function javascript($script);
362
+
363
+ /**
364
+ * Starts a new page
365
+ *
366
+ * Subsequent drawing operations will appear on the new page.
367
+ */
368
+ function new_page();
369
+
370
+ /**
371
+ * Streams the PDF directly to the browser
372
+ *
373
+ * @param string $filename the name of the PDF file
374
+ * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
375
+ */
376
+ function stream($filename, $options = null);
377
+
378
+ /**
379
+ * Returns the PDF as a string
380
+ *
381
+ * @param array $options associative array: 'compress' => 1 or 0
382
+ * @return string
383
+ */
384
+ function output($options = null);
385
+ }
dompdf/include/canvas_factory.cls.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Create canvas instances
11
+ *
12
+ * The canvas factory creates canvas instances based on the
13
+ * availability of rendering backends and config options.
14
+ *
15
+ * @package dompdf
16
+ */
17
+ class Canvas_Factory {
18
+
19
+ /**
20
+ * Constructor is private: this is a static class
21
+ */
22
+ private function __construct() { }
23
+
24
+ /**
25
+ * @param DOMPDF $dompdf
26
+ * @param string|array $paper
27
+ * @param string $orientation
28
+ * @param string $class
29
+ *
30
+ * @return Canvas
31
+ */
32
+ static function get_instance(DOMPDF $dompdf, $paper = null, $orientation = null, $class = null) {
33
+
34
+ $backend = strtolower(DOMPDF_PDF_BACKEND);
35
+
36
+ if ( isset($class) && class_exists($class, false) ) {
37
+ $class .= "_Adapter";
38
+ }
39
+
40
+ else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "pdflib" ) &&
41
+ class_exists("PDFLib", false) ) {
42
+ $class = "PDFLib_Adapter";
43
+ }
44
+
45
+ // FIXME The TCPDF adapter is not ready yet
46
+ //else if ( (DOMPDF_PDF_BACKEND === "auto" || $backend === "cpdf") )
47
+ // $class = "CPDF_Adapter";
48
+
49
+ else if ( $backend === "tcpdf" ) {
50
+ $class = "TCPDF_Adapter";
51
+ }
52
+
53
+ else if ( $backend === "gd" ) {
54
+ $class = "GD_Adapter";
55
+ }
56
+
57
+ else {
58
+ $class = "CPDF_Adapter";
59
+ }
60
+
61
+ return new $class($paper, $orientation, $dompdf);
62
+ }
63
+ }
dompdf/include/cellmap.cls.php ADDED
@@ -0,0 +1,790 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Maps table cells to the table grid.
11
+ *
12
+ * This class resolves borders in tables with collapsed borders and helps
13
+ * place row & column spanned table cells.
14
+ *
15
+ * @access private
16
+ * @package dompdf
17
+ */
18
+ class Cellmap {
19
+
20
+ /**
21
+ * Border style weight lookup for collapsed border resolution.
22
+ *
23
+ * @var array
24
+ */
25
+ static protected $_BORDER_STYLE_SCORE = array(
26
+ "inset" => 1,
27
+ "groove" => 2,
28
+ "outset" => 3,
29
+ "ridge" => 4,
30
+ "dotted" => 5,
31
+ "dashed" => 6,
32
+ "solid" => 7,
33
+ "double" => 8,
34
+ "hidden" => 9,
35
+ "none" => 0,
36
+ );
37
+
38
+ /**
39
+ * The table object this cellmap is attached to.
40
+ *
41
+ * @var Table_Frame_Decorator
42
+ */
43
+ protected $_table;
44
+
45
+ /**
46
+ * The total number of rows in the table
47
+ *
48
+ * @var int
49
+ */
50
+ protected $_num_rows;
51
+
52
+ /**
53
+ * The total number of columns in the table
54
+ *
55
+ * @var int
56
+ */
57
+ protected $_num_cols;
58
+
59
+ /**
60
+ * 2D array mapping <row,column> to frames
61
+ *
62
+ * @var Frame[][]
63
+ */
64
+ protected $_cells;
65
+
66
+ /**
67
+ * 1D array of column dimensions
68
+ *
69
+ * @var array
70
+ */
71
+ protected $_columns;
72
+
73
+ /**
74
+ * 1D array of row dimensions
75
+ *
76
+ * @var array
77
+ */
78
+ protected $_rows;
79
+
80
+ /**
81
+ * 2D array of border specs
82
+ *
83
+ * @var array
84
+ */
85
+ protected $_borders;
86
+
87
+ /**
88
+ * 1D Array mapping frames to (multiple) <row, col> pairs, keyed on frame_id.
89
+ *
90
+ * @var Frame[]
91
+ */
92
+ protected $_frames;
93
+
94
+ /**
95
+ * Current column when adding cells, 0-based
96
+ *
97
+ * @var int
98
+ */
99
+ private $__col;
100
+
101
+ /**
102
+ * Current row when adding cells, 0-based
103
+ *
104
+ * @var int
105
+ */
106
+ private $__row;
107
+
108
+ /**
109
+ * Tells wether the columns' width can be modified
110
+ *
111
+ * @var bool
112
+ */
113
+ private $_columns_locked = false;
114
+
115
+ /**
116
+ * Tells wether the table has table-layout:fixed
117
+ *
118
+ * @var bool
119
+ */
120
+ private $_fixed_layout = false;
121
+
122
+ //........................................................................
123
+
124
+ function __construct(Table_Frame_Decorator $table) {
125
+ $this->_table = $table;
126
+ $this->reset();
127
+ }
128
+
129
+ function __destruct() {
130
+ clear_object($this);
131
+ }
132
+ //........................................................................
133
+
134
+ function reset() {
135
+ $this->_num_rows = 0;
136
+ $this->_num_cols = 0;
137
+
138
+ $this->_cells = array();
139
+ $this->_frames = array();
140
+
141
+ if ( !$this->_columns_locked ) {
142
+ $this->_columns = array();
143
+ }
144
+
145
+ $this->_rows = array();
146
+
147
+ $this->_borders = array();
148
+
149
+ $this->__col = $this->__row = 0;
150
+ }
151
+
152
+ //........................................................................
153
+
154
+ function lock_columns() {
155
+ $this->_columns_locked = true;
156
+ }
157
+
158
+ function is_columns_locked() {
159
+ return $this->_columns_locked;
160
+ }
161
+
162
+ function set_layout_fixed($fixed) {
163
+ $this->_fixed_layout = $fixed;
164
+ }
165
+
166
+ function is_layout_fixed() {
167
+ return $this->_fixed_layout;
168
+ }
169
+
170
+ function get_num_rows() { return $this->_num_rows; }
171
+ function get_num_cols() { return $this->_num_cols; }
172
+
173
+ function &get_columns() {
174
+ return $this->_columns;
175
+ }
176
+
177
+ function set_columns($columns) {
178
+ $this->_columns = $columns;
179
+ }
180
+
181
+ function &get_column($i) {
182
+ if ( !isset($this->_columns[$i]) ) {
183
+ $this->_columns[$i] = array(
184
+ "x" => 0,
185
+ "min-width" => 0,
186
+ "max-width" => 0,
187
+ "used-width" => null,
188
+ "absolute" => 0,
189
+ "percent" => 0,
190
+ "auto" => true,
191
+ );
192
+ }
193
+
194
+ return $this->_columns[$i];
195
+ }
196
+
197
+ function &get_rows() {
198
+ return $this->_rows;
199
+ }
200
+
201
+ function &get_row($j) {
202
+ if ( !isset($this->_rows[$j]) ) {
203
+ $this->_rows[$j] = array(
204
+ "y" => 0,
205
+ "first-column" => 0,
206
+ "height" => null,
207
+ );
208
+ }
209
+
210
+ return $this->_rows[$j];
211
+ }
212
+
213
+ function get_border($i, $j, $h_v, $prop = null) {
214
+ if ( !isset($this->_borders[$i][$j][$h_v]) ) {
215
+ $this->_borders[$i][$j][$h_v] = array(
216
+ "width" => 0,
217
+ "style" => "solid",
218
+ "color" => "black",
219
+ );
220
+ }
221
+
222
+ if ( isset($prop) ) {
223
+ return $this->_borders[$i][$j][$h_v][$prop];
224
+ }
225
+
226
+ return $this->_borders[$i][$j][$h_v];
227
+ }
228
+
229
+ function get_border_properties($i, $j) {
230
+ return array(
231
+ "top" => $this->get_border($i, $j, "horizontal"),
232
+ "right" => $this->get_border($i, $j+1, "vertical"),
233
+ "bottom" => $this->get_border($i+1, $j, "horizontal"),
234
+ "left" => $this->get_border($i, $j, "vertical"),
235
+ );
236
+ }
237
+
238
+ //........................................................................
239
+
240
+ function get_spanned_cells(Frame $frame) {
241
+ $key = $frame->get_id();
242
+
243
+ if ( !isset($this->_frames[$key]) ) {
244
+ throw new DOMPDF_Exception("Frame not found in cellmap");
245
+ }
246
+
247
+ return $this->_frames[$key];
248
+
249
+ }
250
+
251
+ function frame_exists_in_cellmap(Frame $frame) {
252
+ $key = $frame->get_id();
253
+ return isset($this->_frames[$key]);
254
+ }
255
+
256
+ function get_frame_position(Frame $frame) {
257
+ global $_dompdf_warnings;
258
+
259
+ $key = $frame->get_id();
260
+
261
+ if ( !isset($this->_frames[$key]) ) {
262
+ throw new DOMPDF_Exception("Frame not found in cellmap");
263
+ }
264
+
265
+ $col = $this->_frames[$key]["columns"][0];
266
+ $row = $this->_frames[$key]["rows"][0];
267
+
268
+ if ( !isset($this->_columns[$col])) {
269
+ $_dompdf_warnings[] = "Frame not found in columns array. Check your table layout for missing or extra TDs.";
270
+ $x = 0;
271
+ }
272
+ else {
273
+ $x = $this->_columns[$col]["x"];
274
+ }
275
+
276
+ if ( !isset($this->_rows[$row])) {
277
+ $_dompdf_warnings[] = "Frame not found in row array. Check your table layout for missing or extra TDs.";
278
+ $y = 0;
279
+ }
280
+ else {
281
+ $y = $this->_rows[$row]["y"];
282
+ }
283
+
284
+ return array($x, $y, "x" => $x, "y" => $y);
285
+ }
286
+
287
+ function get_frame_width(Frame $frame) {
288
+ $key = $frame->get_id();
289
+
290
+ if ( !isset($this->_frames[$key]) ) {
291
+ throw new DOMPDF_Exception("Frame not found in cellmap");
292
+ }
293
+
294
+ $cols = $this->_frames[$key]["columns"];
295
+ $w = 0;
296
+ foreach ($cols as $i) {
297
+ $w += $this->_columns[$i]["used-width"];
298
+ }
299
+
300
+ return $w;
301
+ }
302
+
303
+ function get_frame_height(Frame $frame) {
304
+ $key = $frame->get_id();
305
+
306
+ if ( !isset($this->_frames[$key]) ) {
307
+ throw new DOMPDF_Exception("Frame not found in cellmap");
308
+ }
309
+
310
+ $rows = $this->_frames[$key]["rows"];
311
+ $h = 0;
312
+ foreach ($rows as $i) {
313
+ if ( !isset($this->_rows[$i]) ) {
314
+ throw new Exception("The row #$i could not be found, please file an issue in the tracker with the HTML code");
315
+ }
316
+
317
+ $h += $this->_rows[$i]["height"];
318
+ }
319
+
320
+ return $h;
321
+ }
322
+
323
+
324
+ //........................................................................
325
+
326
+ function set_column_width($j, $width) {
327
+ if ( $this->_columns_locked ) {
328
+ return;
329
+ }
330
+
331
+ $col =& $this->get_column($j);
332
+ $col["used-width"] = $width;
333
+ $next_col =& $this->get_column($j+1);
334
+ $next_col["x"] = $next_col["x"] + $width;
335
+ }
336
+
337
+ function set_row_height($i, $height) {
338
+ $row =& $this->get_row($i);
339
+
340
+ if ( $row["height"] !== null && $height <= $row["height"] ) {
341
+ return;
342
+ }
343
+
344
+ $row["height"] = $height;
345
+ $next_row =& $this->get_row($i+1);
346
+ $next_row["y"] = $row["y"] + $height;
347
+
348
+ }
349
+
350
+ //........................................................................
351
+
352
+
353
+ protected function _resolve_border($i, $j, $h_v, $border_spec) {
354
+ $n_width = $border_spec["width"];
355
+ $n_style = $border_spec["style"];
356
+
357
+ if ( !isset($this->_borders[$i][$j][$h_v]) ) {
358
+ $this->_borders[$i][$j][$h_v] = $border_spec;
359
+ return $this->_borders[$i][$j][$h_v]["width"];
360
+ }
361
+
362
+ $border = &$this->_borders[$i][$j][$h_v];
363
+
364
+ $o_width = $border["width"];
365
+ $o_style = $border["style"];
366
+
367
+ if ( ($n_style === "hidden" ||
368
+ $n_width > $o_width ||
369
+ $o_style === "none")
370
+
371
+ or
372
+
373
+ ($o_width == $n_width &&
374
+ in_array($n_style, self::$_BORDER_STYLE_SCORE) &&
375
+ self::$_BORDER_STYLE_SCORE[ $n_style ] > self::$_BORDER_STYLE_SCORE[ $o_style ]) ) {
376
+ $border = $border_spec;
377
+ }
378
+
379
+ return $border["width"];
380
+ }
381
+
382
+ //........................................................................
383
+
384
+ function add_frame(Frame $frame) {
385
+
386
+ $style = $frame->get_style();
387
+ $display = $style->display;
388
+
389
+ $collapse = $this->_table->get_style()->border_collapse == "collapse";
390
+
391
+ // Recursively add the frames within tables, table-row-groups and table-rows
392
+ if ( $display === "table-row" ||
393
+ $display === "table" ||
394
+ $display === "inline-table" ||
395
+ in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {
396
+
397
+ $start_row = $this->__row;
398
+ foreach ( $frame->get_children() as $child ) {
399
+ $this->add_frame( $child );
400
+ }
401
+
402
+ if ( $display === "table-row" ) {
403
+ $this->add_row();
404
+ }
405
+
406
+ $num_rows = $this->__row - $start_row - 1;
407
+ $key = $frame->get_id();
408
+
409
+ // Row groups always span across the entire table
410
+ $this->_frames[$key]["columns"] = range(0,max(0,$this->_num_cols-1));
411
+ $this->_frames[$key]["rows"] = range($start_row, max(0, $this->__row - 1));
412
+ $this->_frames[$key]["frame"] = $frame;
413
+
414
+ if ( $display !== "table-row" && $collapse ) {
415
+
416
+ $bp = $style->get_border_properties();
417
+
418
+ // Resolve the borders
419
+ for ( $i = 0; $i < $num_rows+1; $i++) {
420
+ $this->_resolve_border($start_row + $i, 0, "vertical", $bp["left"]);
421
+ $this->_resolve_border($start_row + $i, $this->_num_cols, "vertical", $bp["right"]);
422
+ }
423
+
424
+ for ( $j = 0; $j < $this->_num_cols; $j++) {
425
+ $this->_resolve_border($start_row, $j, "horizontal", $bp["top"]);
426
+ $this->_resolve_border($this->__row, $j, "horizontal", $bp["bottom"]);
427
+ }
428
+ }
429
+
430
+
431
+ return;
432
+ }
433
+
434
+ $node = $frame->get_node();
435
+
436
+ // Determine where this cell is going
437
+ $colspan = $node->getAttribute("colspan");
438
+ $rowspan = $node->getAttribute("rowspan");
439
+
440
+ if ( !$colspan ) {
441
+ $colspan = 1;
442
+ $node->setAttribute("colspan",1);
443
+ }
444
+
445
+ if ( !$rowspan ) {
446
+ $rowspan = 1;
447
+ $node->setAttribute("rowspan",1);
448
+ }
449
+ $key = $frame->get_id();
450
+
451
+ $bp = $style->get_border_properties();
452
+
453
+
454
+ // Add the frame to the cellmap
455
+ $max_left = $max_right = 0;
456
+
457
+ // Find the next available column (fix by Ciro Mondueri)
458
+ $ac = $this->__col;
459
+ while ( isset($this->_cells[$this->__row][$ac]) ) {
460
+ $ac++;
461
+ }
462
+
463
+ $this->__col = $ac;
464
+
465
+ // Rows:
466
+ for ( $i = 0; $i < $rowspan; $i++ ) {
467
+ $row = $this->__row + $i;
468
+
469
+ $this->_frames[$key]["rows"][] = $row;
470
+
471
+ for ( $j = 0; $j < $colspan; $j++) {
472
+ $this->_cells[$row][$this->__col + $j] = $frame;
473
+ }
474
+
475
+ if ( $collapse ) {
476
+ // Resolve vertical borders
477
+ $max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"]));
478
+ $max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]));
479
+ }
480
+ }
481
+
482
+ $max_top = $max_bottom = 0;
483
+
484
+ // Columns:
485
+ for ( $j = 0; $j < $colspan; $j++ ) {
486
+ $col = $this->__col + $j;
487
+ $this->_frames[$key]["columns"][] = $col;
488
+
489
+ if ( $collapse ) {
490
+ // Resolve horizontal borders
491
+ $max_top = max($max_top, $this->_resolve_border($this->__row, $col, "horizontal", $bp["top"]));
492
+ $max_bottom = max($max_bottom, $this->_resolve_border($this->__row + $rowspan, $col, "horizontal", $bp["bottom"]));
493
+ }
494
+ }
495
+
496
+ $this->_frames[$key]["frame"] = $frame;
497
+
498
+ // Handle seperated border model
499
+ if ( !$collapse ) {
500
+ list($h, $v) = $this->_table->get_style()->border_spacing;
501
+
502
+ // Border spacing is effectively a margin between cells
503
+ $v = $style->length_in_pt($v) / 2;
504
+ $h = $style->length_in_pt($h) / 2;
505
+ $style->margin = "$v $h";
506
+
507
+ // The additional 1/2 width gets added to the table proper
508
+ }
509
+ else {
510
+ // Drop the frame's actual border
511
+ $style->border_left_width = $max_left / 2;
512
+ $style->border_right_width = $max_right / 2;
513
+ $style->border_top_width = $max_top / 2;
514
+ $style->border_bottom_width = $max_bottom / 2;
515
+ $style->margin = "none";
516
+ }
517
+
518
+ if ( !$this->_columns_locked ) {
519
+ // Resolve the frame's width
520
+ if ( $this->_fixed_layout ) {
521
+ list($frame_min, $frame_max) = array(0, 10e-10);
522
+ }
523
+ else {
524
+ list($frame_min, $frame_max) = $frame->get_min_max_width();
525
+ }
526
+
527
+ $width = $style->width;
528
+
529
+ $val = null;
530
+ if ( is_percent($width) ) {
531
+ $var = "percent";
532
+ $val = (float)rtrim($width, "% ") / $colspan;
533
+ }
534
+ else if ( $width !== "auto" ) {
535
+ $var = "absolute";
536
+ $val = $style->length_in_pt($frame_min) / $colspan;
537
+ }
538
+
539
+ $min = 0;
540
+ $max = 0;
541
+ for ( $cs = 0; $cs < $colspan; $cs++ ) {
542
+
543
+ // Resolve the frame's width(s) with other cells
544
+ $col =& $this->get_column( $this->__col + $cs );
545
+
546
+ // Note: $var is either 'percent' or 'absolute'. We compare the
547
+ // requested percentage or absolute values with the existing widths
548
+ // and adjust accordingly.
549
+ if ( isset($var) && $val > $col[$var] ) {
550
+ $col[$var] = $val;
551
+ $col["auto"] = false;
552
+ }
553
+
554
+ $min += $col["min-width"];
555
+ $max += $col["max-width"];
556
+ }
557
+
558
+ if ( $frame_min > $min ) {
559
+ // The frame needs more space. Expand each sub-column
560
+ // FIXME try to avoid putting this dummy value when table-layout:fixed
561
+ $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_min - $min) / $colspan);
562
+ for ($c = 0; $c < $colspan; $c++) {
563
+ $col =& $this->get_column($this->__col + $c);
564
+ $col["min-width"] += $inc;
565
+ }
566
+ }
567
+
568
+ if ( $frame_max > $max ) {
569
+ // FIXME try to avoid putting this dummy value when table-layout:fixed
570
+ $inc = ($this->is_layout_fixed() ? 10e-10 : ($frame_max - $max) / $colspan);
571
+ for ($c = 0; $c < $colspan; $c++) {
572
+ $col =& $this->get_column($this->__col + $c);
573
+ $col["max-width"] += $inc;
574
+ }
575
+ }
576
+ }
577
+
578
+ $this->__col += $colspan;
579
+ if ( $this->__col > $this->_num_cols )
580
+ $this->_num_cols = $this->__col;
581
+
582
+ }
583
+
584
+ //........................................................................
585
+
586
+ function add_row() {
587
+
588
+ $this->__row++;
589
+ $this->_num_rows++;
590
+
591
+ // Find the next available column
592
+ $i = 0;
593
+ while ( isset($this->_cells[$this->__row][$i]) ) {
594
+ $i++;
595
+ }
596
+
597
+ $this->__col = $i;
598
+
599
+ }
600
+
601
+ //........................................................................
602
+
603
+ /**
604
+ * Remove a row from the cellmap.
605
+ *
606
+ * @param Frame
607
+ */
608
+ function remove_row(Frame $row) {
609
+
610
+ $key = $row->get_id();
611
+ if ( !isset($this->_frames[$key]) ) {
612
+ return; // Presumably this row has alredy been removed
613
+ }
614
+
615
+ $this->_row = $this->_num_rows--;
616
+
617
+ $rows = $this->_frames[$key]["rows"];
618
+ $columns = $this->_frames[$key]["columns"];
619
+
620
+ // Remove all frames from this row
621
+ foreach ( $rows as $r ) {
622
+ foreach ( $columns as $c ) {
623
+ if ( isset($this->_cells[$r][$c]) ) {
624
+ $id = $this->_cells[$r][$c]->get_id();
625
+
626
+ $this->_frames[$id] = null;
627
+ unset($this->_frames[$id]);
628
+
629
+ $this->_cells[$r][$c] = null;
630
+ unset($this->_cells[$r][$c]);
631
+ }
632
+ }
633
+
634
+ $this->_rows[$r] = null;
635
+ unset($this->_rows[$r]);
636
+ }
637
+
638
+ $this->_frames[$key] = null;
639
+ unset($this->_frames[$key]);
640
+
641
+ }
642
+
643
+ /**
644
+ * Remove a row group from the cellmap.
645
+ *
646
+ * @param Frame $group The group to remove
647
+ */
648
+ function remove_row_group(Frame $group) {
649
+
650
+ $key = $group->get_id();
651
+ if ( !isset($this->_frames[$key]) ) {
652
+ return; // Presumably this row has alredy been removed
653
+ }
654
+
655
+ $iter = $group->get_first_child();
656
+ while ($iter) {
657
+ $this->remove_row($iter);
658
+ $iter = $iter->get_next_sibling();
659
+ }
660
+
661
+ $this->_frames[$key] = null;
662
+ unset($this->_frames[$key]);
663
+ }
664
+
665
+ /**
666
+ * Update a row group after rows have been removed
667
+ *
668
+ * @param Frame $group The group to update
669
+ * @param Frame $last_row The last row in the row group
670
+ */
671
+ function update_row_group(Frame $group, Frame $last_row) {
672
+
673
+ $g_key = $group->get_id();
674
+ $r_key = $last_row->get_id();
675
+
676
+ $r_rows = $this->_frames[$r_key]["rows"];
677
+ $this->_frames[$g_key]["rows"] = range( $this->_frames[$g_key]["rows"][0], end($r_rows) );
678
+
679
+ }
680
+
681
+ //........................................................................
682
+
683
+ function assign_x_positions() {
684
+ // Pre-condition: widths must be resolved and assigned to columns and
685
+ // column[0]["x"] must be set.
686
+
687
+ if ( $this->_columns_locked ) {
688
+ return;
689
+ }
690
+
691
+ $x = $this->_columns[0]["x"];
692
+ foreach ( array_keys($this->_columns) as $j ) {
693
+ $this->_columns[$j]["x"] = $x;
694
+ $x += $this->_columns[$j]["used-width"];
695
+ }
696
+
697
+ }
698
+
699
+ function assign_frame_heights() {
700
+ // Pre-condition: widths and heights of each column & row must be
701
+ // calcluated
702
+
703
+ foreach ( $this->_frames as $arr ) {
704
+ $frame = $arr["frame"];
705
+
706
+ $h = 0;
707
+ foreach( $arr["rows"] as $row ) {
708
+ if ( !isset($this->_rows[$row]) ) {
709
+ // The row has been removed because of a page split, so skip it.
710
+ continue;
711
+ }
712
+
713
+ $h += $this->_rows[$row]["height"];
714
+ }
715
+
716
+ if ( $frame instanceof Table_Cell_Frame_Decorator ) {
717
+ $frame->set_cell_height($h);
718
+ }
719
+ else {
720
+ $frame->get_style()->height = $h;
721
+ }
722
+ }
723
+
724
+ }
725
+
726
+ //........................................................................
727
+
728
+ /**
729
+ * Re-adjust frame height if the table height is larger than its content
730
+ */
731
+ function set_frame_heights($table_height, $content_height) {
732
+
733
+
734
+ // Distribute the increased height proportionally amongst each row
735
+ foreach ( $this->_frames as $arr ) {
736
+ $frame = $arr["frame"];
737
+
738
+ $h = 0;
739
+ foreach ($arr["rows"] as $row ) {
740
+ if ( !isset($this->_rows[$row]) ) {
741
+ continue;
742
+ }
743
+
744
+ $h += $this->_rows[$row]["height"];
745
+ }
746
+
747
+ if ( $content_height > 0 ) {
748
+ $new_height = ($h / $content_height) * $table_height;
749
+ }
750
+ else {
751
+ $new_height = 0;
752
+ }
753
+
754
+ if ( $frame instanceof Table_Cell_Frame_Decorator ) {
755
+ $frame->set_cell_height($new_height);
756
+ }
757
+ else {
758
+ $frame->get_style()->height = $new_height;
759
+ }
760
+ }
761
+
762
+ }
763
+
764
+ //........................................................................
765
+
766
+ // Used for debugging:
767
+ function __toString() {
768
+ $str = "";
769
+ $str .= "Columns:<br/>";
770
+ $str .= pre_r($this->_columns, true);
771
+ $str .= "Rows:<br/>";
772
+ $str .= pre_r($this->_rows, true);
773
+
774
+ $str .= "Frames:<br/>";
775
+ $arr = array();
776
+ foreach ( $this->_frames as $key => $val ) {
777
+ $arr[$key] = array("columns" => $val["columns"], "rows" => $val["rows"]);
778
+ }
779
+
780
+ $str .= pre_r($arr, true);
781
+
782
+ if ( php_sapi_name() == "cli" ) {
783
+ $str = strip_tags(str_replace(array("<br/>","<b>","</b>"),
784
+ array("\n",chr(27)."[01;33m", chr(27)."[0m"),
785
+ $str));
786
+ }
787
+
788
+ return $str;
789
+ }
790
+ }
dompdf/include/cpdf_adapter.cls.php ADDED
@@ -0,0 +1,869 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Orion Richardson <orionr@yahoo.com>
7
+ * @author Helmut Tischer <htischer@weihenstephan.org>
8
+ * @author Fabien Ménager <fabien.menager@gmail.com>
9
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
10
+ */
11
+
12
+ // FIXME: Need to sanity check inputs to this class
13
+ require_once(DOMPDF_LIB_DIR . "/class.pdf.php");
14
+
15
+ /**
16
+ * PDF rendering interface
17
+ *
18
+ * CPDF_Adapter provides a simple stateless interface to the stateful one
19
+ * provided by the Cpdf class.
20
+ *
21
+ * Unless otherwise mentioned, all dimensions are in points (1/72 in). The
22
+ * coordinate origin is in the top left corner, and y values increase
23
+ * downwards.
24
+ *
25
+ * See {@link http://www.ros.co.nz/pdf/} for more complete documentation
26
+ * on the underlying {@link Cpdf} class.
27
+ *
28
+ * @package dompdf
29
+ */
30
+ class CPDF_Adapter implements Canvas {
31
+
32
+ /**
33
+ * Dimensions of paper sizes in points
34
+ *
35
+ * @var array;
36
+ */
37
+ static $PAPER_SIZES = array(
38
+ "4a0" => array(0,0,4767.87,6740.79),
39
+ "2a0" => array(0,0,3370.39,4767.87),
40
+ "a0" => array(0,0,2383.94,3370.39),
41
+ "a1" => array(0,0,1683.78,2383.94),
42
+ "a2" => array(0,0,1190.55,1683.78),
43
+ "a3" => array(0,0,841.89,1190.55),
44
+ "a4" => array(0,0,595.28,841.89),
45
+ "a5" => array(0,0,419.53,595.28),
46
+ "a6" => array(0,0,297.64,419.53),
47
+ "a7" => array(0,0,209.76,297.64),
48
+ "a8" => array(0,0,147.40,209.76),
49
+ "a9" => array(0,0,104.88,147.40),
50
+ "a10" => array(0,0,73.70,104.88),
51
+ "b0" => array(0,0,2834.65,4008.19),
52
+ "b1" => array(0,0,2004.09,2834.65),
53
+ "b2" => array(0,0,1417.32,2004.09),
54
+ "b3" => array(0,0,1000.63,1417.32),
55
+ "b4" => array(0,0,708.66,1000.63),
56
+ "b5" => array(0,0,498.90,708.66),
57
+ "b6" => array(0,0,354.33,498.90),
58
+ "b7" => array(0,0,249.45,354.33),
59
+ "b8" => array(0,0,175.75,249.45),
60
+ "b9" => array(0,0,124.72,175.75),
61
+ "b10" => array(0,0,87.87,124.72),
62
+ "c0" => array(0,0,2599.37,3676.54),
63
+ "c1" => array(0,0,1836.85,2599.37),
64
+ "c2" => array(0,0,1298.27,1836.85),
65
+ "c3" => array(0,0,918.43,1298.27),
66
+ "c4" => array(0,0,649.13,918.43),
67
+ "c5" => array(0,0,459.21,649.13),
68
+ "c6" => array(0,0,323.15,459.21),
69
+ "c7" => array(0,0,229.61,323.15),
70
+ "c8" => array(0,0,161.57,229.61),
71
+ "c9" => array(0,0,113.39,161.57),
72
+ "c10" => array(0,0,79.37,113.39),
73
+ "ra0" => array(0,0,2437.80,3458.27),
74
+ "ra1" => array(0,0,1729.13,2437.80),
75
+ "ra2" => array(0,0,1218.90,1729.13),
76
+ "ra3" => array(0,0,864.57,1218.90),
77
+ "ra4" => array(0,0,609.45,864.57),
78
+ "sra0" => array(0,0,2551.18,3628.35),
79
+ "sra1" => array(0,0,1814.17,2551.18),
80
+ "sra2" => array(0,0,1275.59,1814.17),
81
+ "sra3" => array(0,0,907.09,1275.59),
82
+ "sra4" => array(0,0,637.80,907.09),
83
+ "letter" => array(0,0,612.00,792.00),
84
+ "legal" => array(0,0,612.00,1008.00),
85
+ "ledger" => array(0,0,1224.00, 792.00),
86
+ "tabloid" => array(0,0,792.00, 1224.00),
87
+ "executive" => array(0,0,521.86,756.00),
88
+ "folio" => array(0,0,612.00,936.00),
89
+ "commercial #10 envelope" => array(0,0,684,297),
90
+ "catalog #10 1/2 envelope" => array(0,0,648,864),
91
+ "8.5x11" => array(0,0,612.00,792.00),
92
+ "8.5x14" => array(0,0,612.00,1008.0),
93
+ "11x17" => array(0,0,792.00, 1224.00),
94
+ );
95
+
96
+ /**
97
+ * The DOMPDF object
98
+ *
99
+ * @var DOMPDF
100
+ */
101
+ private $_dompdf;
102
+
103
+ /**
104
+ * Instance of Cpdf class
105
+ *
106
+ * @var Cpdf
107
+ */
108
+ private $_pdf;
109
+
110
+ /**
111
+ * PDF width, in points
112
+ *
113
+ * @var float
114
+ */
115
+ private $_width;
116
+
117
+ /**
118
+ * PDF height, in points
119
+ *
120
+ * @var float;
121
+ */
122
+ private $_height;
123
+
124
+ /**
125
+ * Current page number
126
+ *
127
+ * @var int
128
+ */
129
+ private $_page_number;
130
+
131
+ /**
132
+ * Total number of pages
133
+ *
134
+ * @var int
135
+ */
136
+ private $_page_count;
137
+
138
+ /**
139
+ * Text to display on every page
140
+ *
141
+ * @var array
142
+ */
143
+ private $_page_text;
144
+
145
+ /**
146
+ * Array of pages for accesing after rendering is initially complete
147
+ *
148
+ * @var array
149
+ */
150
+ private $_pages;
151
+
152
+ /**
153
+ * Array of temporary cached images to be deleted when processing is complete
154
+ *
155
+ * @var array
156
+ */
157
+ private $_image_cache;
158
+
159
+ /**
160
+ * Class constructor
161
+ *
162
+ * @param mixed $paper The size of paper to use in this PDF ({@link CPDF_Adapter::$PAPER_SIZES})
163
+ * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
164
+ * @param DOMPDF $dompdf The DOMPDF instance
165
+ */
166
+ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
167
+ if ( is_array($paper) ) {
168
+ $size = $paper;
169
+ }
170
+ else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) {
171
+ $size = self::$PAPER_SIZES[mb_strtolower($paper)];
172
+ }
173
+ else {
174
+ $size = self::$PAPER_SIZES["letter"];
175
+ }
176
+
177
+ if ( mb_strtolower($orientation) === "landscape" ) {
178
+ list($size[2], $size[3]) = array($size[3], $size[2]);
179
+ }
180
+
181
+ $this->_dompdf = $dompdf;
182
+
183
+ $this->_pdf = new Cpdf(
184
+ $size,
185
+ $dompdf->get_option("enable_unicode"),
186
+ $dompdf->get_option("font_cache"),
187
+ $dompdf->get_option("temp_dir")
188
+ );
189
+
190
+ $this->_pdf->addInfo("Creator", "DOMPDF");
191
+ $time = substr_replace(date('YmdHisO'), '\'', -2, 0).'\'';
192
+ $this->_pdf->addInfo("CreationDate", "D:$time");
193
+ $this->_pdf->addInfo("ModDate", "D:$time");
194
+
195
+ $this->_width = $size[2] - $size[0];
196
+ $this->_height= $size[3] - $size[1];
197
+
198
+ $this->_page_number = $this->_page_count = 1;
199
+ $this->_page_text = array();
200
+
201
+ $this->_pages = array($this->_pdf->getFirstPageId());
202
+
203
+ $this->_image_cache = array();
204
+ }
205
+
206
+ function get_dompdf(){
207
+ return $this->_dompdf;
208
+ }
209
+
210
+ /**
211
+ * Class destructor
212
+ *
213
+ * Deletes all temporary image files
214
+ */
215
+ function __destruct() {
216
+ foreach ($this->_image_cache as $img) {
217
+ if (DEBUGPNG) print '[__destruct unlink '.$img.']';
218
+ if (!DEBUGKEEPTEMP) unlink($img);
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Returns the Cpdf instance
224
+ *
225
+ * @return Cpdf
226
+ */
227
+ function get_cpdf() {
228
+ return $this->_pdf;
229
+ }
230
+
231
+ /**
232
+ * Add meta information to the PDF
233
+ *
234
+ * @param string $label label of the value (Creator, Producer, etc.)
235
+ * @param string $value the text to set
236
+ */
237
+ function add_info($label, $value) {
238
+ $this->_pdf->addInfo($label, $value);
239
+ }
240
+
241
+ /**
242
+ * Opens a new 'object'
243
+ *
244
+ * While an object is open, all drawing actions are recored in the object,
245
+ * as opposed to being drawn on the current page. Objects can be added
246
+ * later to a specific page or to several pages.
247
+ *
248
+ * The return value is an integer ID for the new object.
249
+ *
250
+ * @see CPDF_Adapter::close_object()
251
+ * @see CPDF_Adapter::add_object()
252
+ *
253
+ * @return int
254
+ */
255
+ function open_object() {
256
+ $ret = $this->_pdf->openObject();
257
+ $this->_pdf->saveState();
258
+ return $ret;
259
+ }
260
+
261
+ /**
262
+ * Reopens an existing 'object'
263
+ *
264
+ * @see CPDF_Adapter::open_object()
265
+ * @param int $object the ID of a previously opened object
266
+ */
267
+ function reopen_object($object) {
268
+ $this->_pdf->reopenObject($object);
269
+ $this->_pdf->saveState();
270
+ }
271
+
272
+ /**
273
+ * Closes the current 'object'
274
+ *
275
+ * @see CPDF_Adapter::open_object()
276
+ */
277
+ function close_object() {
278
+ $this->_pdf->restoreState();
279
+ $this->_pdf->closeObject();
280
+ }
281
+
282
+ /**
283
+ * Adds a specified 'object' to the document
284
+ *
285
+ * $object int specifying an object created with {@link
286
+ * CPDF_Adapter::open_object()}. $where can be one of:
287
+ * - 'add' add to current page only
288
+ * - 'all' add to every page from the current one onwards
289
+ * - 'odd' add to all odd numbered pages from now on
290
+ * - 'even' add to all even numbered pages from now on
291
+ * - 'next' add the object to the next page only
292
+ * - 'nextodd' add to all odd numbered pages from the next one
293
+ * - 'nexteven' add to all even numbered pages from the next one
294
+ *
295
+ * @see Cpdf::addObject()
296
+ *
297
+ * @param int $object
298
+ * @param string $where
299
+ */
300
+ function add_object($object, $where = 'all') {
301
+ $this->_pdf->addObject($object, $where);
302
+ }
303
+
304
+ /**
305
+ * Stops the specified 'object' from appearing in the document.
306
+ *
307
+ * The object will stop being displayed on the page following the current
308
+ * one.
309
+ *
310
+ * @param int $object
311
+ */
312
+ function stop_object($object) {
313
+ $this->_pdf->stopObject($object);
314
+ }
315
+
316
+ /**
317
+ * @access private
318
+ */
319
+ function serialize_object($id) {
320
+ // Serialize the pdf object's current state for retrieval later
321
+ return $this->_pdf->serializeObject($id);
322
+ }
323
+
324
+ /**
325
+ * @access private
326
+ */
327
+ function reopen_serialized_object($obj) {
328
+ return $this->_pdf->restoreSerializedObject($obj);
329
+ }
330
+
331
+ //........................................................................
332
+
333
+ /**
334
+ * Returns the PDF's width in points
335
+ * @return float
336
+ */
337
+ function get_width() { return $this->_width; }
338
+
339
+ /**
340
+ * Returns the PDF's height in points
341
+ * @return float
342
+ */
343
+ function get_height() { return $this->_height; }
344
+
345
+ /**
346
+ * Returns the current page number
347
+ * @return int
348
+ */
349
+ function get_page_number() { return $this->_page_number; }
350
+
351
+ /**
352
+ * Returns the total number of pages in the document
353
+ * @return int
354
+ */
355
+ function get_page_count() { return $this->_page_count; }
356
+
357
+ /**
358
+ * Sets the current page number
359
+ *
360
+ * @param int $num
361
+ */
362
+ function set_page_number($num) { $this->_page_number = $num; }
363
+
364
+ /**
365
+ * Sets the page count
366
+ *
367
+ * @param int $count
368
+ */
369
+ function set_page_count($count) { $this->_page_count = $count; }
370
+
371
+ /**
372
+ * Sets the stroke colour
373
+ *
374
+ * See {@link Style::set_colour()} for the format of the color array.
375
+ * @param array $color
376
+ */
377
+ protected function _set_stroke_color($color) {
378
+ $this->_pdf->setStrokeColor($color);
379
+ }
380
+
381
+ /**
382
+ * Sets the fill colour
383
+ *
384
+ * See {@link Style::set_colour()} for the format of the colour array.
385
+ * @param array $color
386
+ */
387
+ protected function _set_fill_color($color) {
388
+ $this->_pdf->setColor($color);
389
+ }
390
+
391
+ /**
392
+ * Sets line transparency
393
+ * @see Cpdf::setLineTransparency()
394
+ *
395
+ * Valid blend modes are (case-sensitive):
396
+ *
397
+ * Normal, Multiply, Screen, Overlay, Darken, Lighten,
398
+ * ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
399
+ * Exclusion
400
+ *
401
+ * @param string $mode the blending mode to use
402
+ * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
403
+ */
404
+ protected function _set_line_transparency($mode, $opacity) {
405
+ $this->_pdf->setLineTransparency($mode, $opacity);
406
+ }
407
+
408
+ /**
409
+ * Sets fill transparency
410
+ * @see Cpdf::setFillTransparency()
411
+ *
412
+ * Valid blend modes are (case-sensitive):
413
+ *
414
+ * Normal, Multiply, Screen, Overlay, Darken, Lighten,
415
+ * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
416
+ * Exclusion
417
+ *
418
+ * @param string $mode the blending mode to use
419
+ * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
420
+ */
421
+ protected function _set_fill_transparency($mode, $opacity) {
422
+ $this->_pdf->setFillTransparency($mode, $opacity);
423
+ }
424
+
425
+ /**
426
+ * Sets the line style
427
+ *
428
+ * @see Cpdf::setLineStyle()
429
+ *
430
+ * @param float $width
431
+ * @param string $cap
432
+ * @param string $join
433
+ * @param array $dash
434
+ */
435
+ protected function _set_line_style($width, $cap, $join, $dash) {
436
+ $this->_pdf->setLineStyle($width, $cap, $join, $dash);
437
+ }
438
+
439
+ /**
440
+ * Sets the opacity
441
+ *
442
+ * @param $opacity
443
+ * @param $mode
444
+ */
445
+ function set_opacity($opacity, $mode = "Normal") {
446
+ $this->_set_line_transparency($mode, $opacity);
447
+ $this->_set_fill_transparency($mode, $opacity);
448
+ }
449
+
450
+ function set_default_view($view, $options = array()) {
451
+ array_unshift($options, $view);
452
+ call_user_func_array(array($this->_pdf, "openHere"), $options);
453
+ }
454
+
455
+ /**
456
+ * Remaps y coords from 4th to 1st quadrant
457
+ *
458
+ * @param float $y
459
+ * @return float
460
+ */
461
+ protected function y($y) {
462
+ return $this->_height - $y;
463
+ }
464
+
465
+ // Canvas implementation
466
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = array()) {
467
+ $this->_set_stroke_color($color);
468
+ $this->_set_line_style($width, "butt", "", $style);
469
+
470
+ $this->_pdf->line($x1, $this->y($y1),
471
+ $x2, $this->y($y2));
472
+ }
473
+
474
+ function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
475
+ $this->_set_stroke_color($color);
476
+ $this->_set_line_style($width, "butt", "", $style);
477
+
478
+ $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
479
+ }
480
+
481
+ //........................................................................
482
+
483
+ /**
484
+ * Convert a GIF or BMP image to a PNG image
485
+ *
486
+ * @param string $image_url
487
+ * @param integer $type
488
+ *
489
+ * @throws DOMPDF_Exception
490
+ * @return string The url of the newly converted image
491
+ */
492
+ protected function _convert_gif_bmp_to_png($image_url, $type) {
493
+ $image_type = Image_Cache::type_to_ext($type);
494
+ $func_name = "imagecreatefrom$image_type";
495
+
496
+ if ( !function_exists($func_name) ) {
497
+ throw new DOMPDF_Exception("Function $func_name() not found. Cannot convert $image_type image: $image_url. Please install the image PHP extension.");
498
+ }
499
+
500
+ set_error_handler("record_warnings");
501
+ $im = $func_name($image_url);
502
+
503
+ if ( $im ) {
504
+ imageinterlace($im, false);
505
+
506
+ $tmp_dir = $this->_dompdf->get_option("temp_dir");
507
+ $tmp_name = tempnam($tmp_dir, "{$image_type}dompdf_img_");
508
+ @unlink($tmp_name);
509
+ $filename = "$tmp_name.png";
510
+ $this->_image_cache[] = $filename;
511
+
512
+ imagepng($im, $filename);
513
+ imagedestroy($im);
514
+ }
515
+ else {
516
+ $filename = Image_Cache::$broken_image;
517
+ }
518
+
519
+ restore_error_handler();
520
+
521
+ return $filename;
522
+ }
523
+
524
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = array()) {
525
+ $this->_set_stroke_color($color);
526
+ $this->_set_line_style($width, "butt", "", $style);
527
+ $this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
528
+ }
529
+
530
+ function filled_rectangle($x1, $y1, $w, $h, $color) {
531
+ $this->_set_fill_color($color);
532
+ $this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
533
+ }
534
+
535
+ function clipping_rectangle($x1, $y1, $w, $h) {
536
+ $this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
537
+ }
538
+
539
+ function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
540
+ $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
541
+ }
542
+
543
+ function clipping_end() {
544
+ $this->_pdf->clippingEnd();
545
+ }
546
+
547
+ function save() {
548
+ $this->_pdf->saveState();
549
+ }
550
+
551
+ function restore() {
552
+ $this->_pdf->restoreState();
553
+ }
554
+
555
+ function rotate($angle, $x, $y) {
556
+ $this->_pdf->rotate($angle, $x, $y);
557
+ }
558
+
559
+ function skew($angle_x, $angle_y, $x, $y) {
560
+ $this->_pdf->skew($angle_x, $angle_y, $x, $y);
561
+ }
562
+
563
+ function scale($s_x, $s_y, $x, $y) {
564
+ $this->_pdf->scale($s_x, $s_y, $x, $y);
565
+ }
566
+
567
+ function translate($t_x, $t_y) {
568
+ $this->_pdf->translate($t_x, $t_y);
569
+ }
570
+
571
+ function transform($a, $b, $c, $d, $e, $f) {
572
+ $this->_pdf->transform(array($a, $b, $c, $d, $e, $f));
573
+ }
574
+
575
+ function polygon($points, $color, $width = null, $style = array(), $fill = false) {
576
+ $this->_set_fill_color($color);
577
+ $this->_set_stroke_color($color);
578
+
579
+ // Adjust y values
580
+ for ( $i = 1; $i < count($points); $i += 2) {
581
+ $points[$i] = $this->y($points[$i]);
582
+ }
583
+
584
+ $this->_pdf->polygon($points, count($points) / 2, $fill);
585
+ }
586
+
587
+ function circle($x, $y, $r1, $color, $width = null, $style = null, $fill = false) {
588
+ $this->_set_fill_color($color);
589
+ $this->_set_stroke_color($color);
590
+
591
+ if ( !$fill && isset($width) ) {
592
+ $this->_set_line_style($width, "round", "round", $style);
593
+ }
594
+
595
+ $this->_pdf->ellipse($x, $this->y($y), $r1, 0, 0, 8, 0, 360, 1, $fill);
596
+ }
597
+
598
+ function image($img, $x, $y, $w, $h, $resolution = "normal") {
599
+ list($width, $height, $type) = dompdf_getimagesize($img);
600
+
601
+ $debug_png = $this->_dompdf->get_option("debug_png");
602
+
603
+ if ($debug_png) print "[image:$img|$width|$height|$type]";
604
+
605
+ switch ($type) {
606
+ case IMAGETYPE_JPEG:
607
+ if ($debug_png) print '!!!jpg!!!';
608
+ $this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
609
+ break;
610
+
611
+ case IMAGETYPE_GIF:
612
+ case IMAGETYPE_BMP:
613
+ if ($debug_png) print '!!!bmp or gif!!!';
614
+ // @todo use cache for BMP and GIF
615
+ $img = $this->_convert_gif_bmp_to_png($img, $type);
616
+
617
+ case IMAGETYPE_PNG:
618
+ if ($debug_png) print '!!!png!!!';
619
+
620
+ $this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
621
+ break;
622
+
623
+ default:
624
+ if ($debug_png) print '!!!unknown!!!';
625
+ }
626
+ }
627
+
628
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
629
+ $pdf = $this->_pdf;
630
+
631
+ $pdf->setColor($color);
632
+
633
+ $font .= ".afm";
634
+ $pdf->selectFont($font);
635
+
636
+ //Font_Metrics::get_font_height($font, $size) ==
637
+ //$this->get_font_height($font, $size) ==
638
+ //$this->_pdf->selectFont($font),$this->_pdf->getFontHeight($size)
639
+ //- FontBBoxheight+FontHeightOffset, scaled to $size, in pt
640
+ //$this->_pdf->getFontDescender($size)
641
+ //- Descender scaled to size
642
+ //
643
+ //$this->_pdf->fonts[$this->_pdf->currentFont] sizes:
644
+ //['FontBBox'][0] left, ['FontBBox'][1] bottom, ['FontBBox'][2] right, ['FontBBox'][3] top
645
+ //Maximum extent of all glyphs of the font from the baseline point
646
+ //['Ascender'] maximum height above baseline except accents
647
+ //['Descender'] maximum depth below baseline, negative number means below baseline
648
+ //['FontHeightOffset'] manual enhancement of .afm files to trim windows fonts. currently not used.
649
+ //Values are in 1/1000 pt for a font size of 1 pt
650
+ //
651
+ //['FontBBox'][1] should be close to ['Descender']
652
+ //['FontBBox'][3] should be close to ['Ascender']+Accents
653
+ //in practice, FontBBox values are a little bigger
654
+ //
655
+ //The text position is referenced to the baseline, not to the lower corner of the FontBBox,
656
+ //for what the left,top corner is given.
657
+ //FontBBox spans also the background box for the text.
658
+ //If the lower corner would be used as reference point, the Descents of the glyphs would
659
+ //hang over the background box border.
660
+ //Therefore compensate only the extent above the Baseline.
661
+ //
662
+ //print '<pre>['.$font.','.$size.','.$pdf->getFontHeight($size).','.$pdf->getFontDescender($size).','.$pdf->fonts[$pdf->currentFont]['FontBBox'][3].','.$pdf->fonts[$pdf->currentFont]['FontBBox'][1].','.$pdf->fonts[$pdf->currentFont]['FontHeightOffset'].','.$pdf->fonts[$pdf->currentFont]['Ascender'].','.$pdf->fonts[$pdf->currentFont]['Descender'].']</pre>';
663
+ //
664
+ //$pdf->addText($x, $this->y($y) - ($pdf->fonts[$pdf->currentFont]['FontBBox'][3]*$size)/1000, $size, $text, $angle, $word_space, $char_space);
665
+ $pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space);
666
+ }
667
+
668
+ //........................................................................
669
+
670
+ function javascript($code) {
671
+ $this->_pdf->addJavascript($code);
672
+ }
673
+
674
+ //........................................................................
675
+
676
+ /**
677
+ * Add a named destination (similar to <a name="foo">...</a> in html)
678
+ *
679
+ * @param string $anchorname The name of the named destination
680
+ */
681
+ function add_named_dest($anchorname) {
682
+ $this->_pdf->addDestination($anchorname, "Fit");
683
+ }
684
+
685
+ //........................................................................
686
+
687
+ /**
688
+ * Add a link to the pdf
689
+ *
690
+ * @param string $url The url to link to
691
+ * @param float $x The x position of the link
692
+ * @param float $y The y position of the link
693
+ * @param float $width The width of the link
694
+ * @param float $height The height of the link
695
+ */
696
+ function add_link($url, $x, $y, $width, $height) {
697
+
698
+ $y = $this->y($y) - $height;
699
+
700
+ if ( strpos($url, '#') === 0 ) {
701
+ // Local link
702
+ $name = substr($url,1);
703
+ if ( $name ) {
704
+ $this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
705
+ }
706
+
707
+ }
708
+ else {
709
+ $this->_pdf->addLink(rawurldecode($url), $x, $y, $x + $width, $y + $height);
710
+ }
711
+ }
712
+
713
+ function get_text_width($text, $font, $size, $word_spacing = 0, $char_spacing = 0) {
714
+ $this->_pdf->selectFont($font);
715
+
716
+ $unicode = $this->_dompdf->get_option("enable_unicode");
717
+ if (!$unicode) {
718
+ $text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
719
+ }
720
+
721
+ return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
722
+ }
723
+
724
+ function register_string_subset($font, $string) {
725
+ $this->_pdf->registerText($font, $string);
726
+ }
727
+
728
+ function get_font_height($font, $size) {
729
+ $this->_pdf->selectFont($font);
730
+
731
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
732
+ return $this->_pdf->getFontHeight($size) * $ratio;
733
+ }
734
+
735
+ /*function get_font_x_height($font, $size) {
736
+ $this->_pdf->selectFont($font);
737
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
738
+ return $this->_pdf->getFontXHeight($size) * $ratio;
739
+ }*/
740
+
741
+ function get_font_baseline($font, $size) {
742
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
743
+ return $this->get_font_height($font, $size) / $ratio;
744
+ }
745
+
746
+ /**
747
+ * Writes text at the specified x and y coordinates on every page
748
+ *
749
+ * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
750
+ * with their current values.
751
+ *
752
+ * See {@link Style::munge_colour()} for the format of the colour array.
753
+ *
754
+ * @param float $x
755
+ * @param float $y
756
+ * @param string $text the text to write
757
+ * @param string $font the font file to use
758
+ * @param float $size the font size, in points
759
+ * @param array $color
760
+ * @param float $word_space word spacing adjustment
761
+ * @param float $char_space char spacing adjustment
762
+ * @param float $angle angle to write the text at, measured CW starting from the x-axis
763
+ */
764
+ function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
765
+ $_t = "text";
766
+ $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle");
767
+ }
768
+
769
+ /**
770
+ * Processes a script on every page
771
+ *
772
+ * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
773
+ *
774
+ * This function can be used to add page numbers to all pages
775
+ * after the first one, for example.
776
+ *
777
+ * @param string $code the script code
778
+ * @param string $type the language type for script
779
+ */
780
+ function page_script($code, $type = "text/php") {
781
+ $_t = "script";
782
+ $this->_page_text[] = compact("_t", "code", "type");
783
+ }
784
+
785
+ function new_page() {
786
+ $this->_page_number++;
787
+ $this->_page_count++;
788
+
789
+ $ret = $this->_pdf->newPage();
790
+ $this->_pages[] = $ret;
791
+ return $ret;
792
+ }
793
+
794
+ /**
795
+ * Add text to each page after rendering is complete
796
+ */
797
+ protected function _add_page_text() {
798
+
799
+ if ( !count($this->_page_text) ) {
800
+ return;
801
+ }
802
+
803
+ $page_number = 1;
804
+ $eval = null;
805
+
806
+ foreach ($this->_pages as $pid) {
807
+ $this->reopen_object($pid);
808
+
809
+ foreach ($this->_page_text as $pt) {
810
+ extract($pt);
811
+
812
+ switch ($_t) {
813
+ case "text":
814
+ $text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"),
815
+ array($page_number, $this->_page_count), $text);
816
+ $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
817
+ break;
818
+
819
+ case "script":
820
+ if ( !$eval ) {
821
+ $eval = new PHP_Evaluator($this);
822
+ }
823
+ $eval->evaluate($code, array('PAGE_NUM' => $page_number, 'PAGE_COUNT' => $this->_page_count));
824
+ break;
825
+ }
826
+ }
827
+
828
+ $this->close_object();
829
+ $page_number++;
830
+ }
831
+ }
832
+
833
+ /**
834
+ * Streams the PDF directly to the browser
835
+ *
836
+ * @param string $filename the name of the PDF file
837
+ * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
838
+ */
839
+ function stream($filename, $options = null) {
840
+ // Add page text
841
+ $this->_add_page_text();
842
+
843
+ $options["Content-Disposition"] = $filename;
844
+ $this->_pdf->stream($options);
845
+ }
846
+
847
+ /**
848
+ * Returns the PDF as a string
849
+ *
850
+ * @param array $options Output options
851
+ * @return string
852
+ */
853
+ function output($options = null) {
854
+ $this->_add_page_text();
855
+
856
+ $debug = isset($options["compress"]) && $options["compress"] != 1;
857
+
858
+ return $this->_pdf->output($debug);
859
+ }
860
+
861
+ /**
862
+ * Returns logging messages generated by the Cpdf class
863
+ *
864
+ * @return string
865
+ */
866
+ function get_messages() {
867
+ return $this->_pdf->messages;
868
+ }
869
+ }
dompdf/include/css_color.cls.php ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ class CSS_Color {
11
+ static $cssColorNames = array(
12
+ "aliceblue" => "F0F8FF",
13
+ "antiquewhite" => "FAEBD7",
14
+ "aqua" => "00FFFF",
15
+ "aquamarine" => "7FFFD4",
16
+ "azure" => "F0FFFF",
17
+ "beige" => "F5F5DC",
18
+ "bisque" => "FFE4C4",
19
+ "black" => "000000",
20
+ "blanchedalmond" => "FFEBCD",
21
+ "blue" => "0000FF",
22
+ "blueviolet" => "8A2BE2",
23
+ "brown" => "A52A2A",
24
+ "burlywood" => "DEB887",
25
+ "cadetblue" => "5F9EA0",
26
+ "chartreuse" => "7FFF00",
27
+ "chocolate" => "D2691E",
28
+ "coral" => "FF7F50",
29
+ "cornflowerblue" => "6495ED",
30
+ "cornsilk" => "FFF8DC",
31
+ "crimson" => "DC143C",
32
+ "cyan" => "00FFFF",
33
+ "darkblue" => "00008B",
34
+ "darkcyan" => "008B8B",
35
+ "darkgoldenrod" => "B8860B",
36
+ "darkgray" => "A9A9A9",
37
+ "darkgreen" => "006400",
38
+ "darkgrey" => "A9A9A9",
39
+ "darkkhaki" => "BDB76B",
40
+ "darkmagenta" => "8B008B",
41
+ "darkolivegreen" => "556B2F",
42
+ "darkorange" => "FF8C00",
43
+ "darkorchid" => "9932CC",
44
+ "darkred" => "8B0000",
45
+ "darksalmon" => "E9967A",
46
+ "darkseagreen" => "8FBC8F",
47
+ "darkslateblue" => "483D8B",
48
+ "darkslategray" => "2F4F4F",
49
+ "darkslategrey" => "2F4F4F",
50
+ "darkturquoise" => "00CED1",
51
+ "darkviolet" => "9400D3",
52
+ "deeppink" => "FF1493",
53
+ "deepskyblue" => "00BFFF",
54
+ "dimgray" => "696969",
55
+ "dimgrey" => "696969",
56
+ "dodgerblue" => "1E90FF",
57
+ "firebrick" => "B22222",
58
+ "floralwhite" => "FFFAF0",
59
+ "forestgreen" => "228B22",
60
+ "fuchsia" => "FF00FF",
61
+ "gainsboro" => "DCDCDC",
62
+ "ghostwhite" => "F8F8FF",
63
+ "gold" => "FFD700",
64
+ "goldenrod" => "DAA520",
65
+ "gray" => "808080",
66
+ "green" => "008000",
67
+ "greenyellow" => "ADFF2F",
68
+ "grey" => "808080",
69
+ "honeydew" => "F0FFF0",
70
+ "hotpink" => "FF69B4",
71
+ "indianred" => "CD5C5C",
72
+ "indigo" => "4B0082",
73
+ "ivory" => "FFFFF0",
74
+ "khaki" => "F0E68C",
75
+ "lavender" => "E6E6FA",
76
+ "lavenderblush" => "FFF0F5",
77
+ "lawngreen" => "7CFC00",
78
+ "lemonchiffon" => "FFFACD",
79
+ "lightblue" => "ADD8E6",
80
+ "lightcoral" => "F08080",
81
+ "lightcyan" => "E0FFFF",
82
+ "lightgoldenrodyellow" => "FAFAD2",
83
+ "lightgray" => "D3D3D3",
84
+ "lightgreen" => "90EE90",
85
+ "lightgrey" => "D3D3D3",
86
+ "lightpink" => "FFB6C1",
87
+ "lightsalmon" => "FFA07A",
88
+ "lightseagreen" => "20B2AA",
89
+ "lightskyblue" => "87CEFA",
90
+ "lightslategray" => "778899",
91
+ "lightslategrey" => "778899",
92
+ "lightsteelblue" => "B0C4DE",
93
+ "lightyellow" => "FFFFE0",
94
+ "lime" => "00FF00",
95
+ "limegreen" => "32CD32",
96
+ "linen" => "FAF0E6",
97
+ "magenta" => "FF00FF",
98
+ "maroon" => "800000",
99
+ "mediumaquamarine" => "66CDAA",
100
+ "mediumblue" => "0000CD",
101
+ "mediumorchid" => "BA55D3",
102
+ "mediumpurple" => "9370DB",
103
+ "mediumseagreen" => "3CB371",
104
+ "mediumslateblue" => "7B68EE",
105
+ "mediumspringgreen" => "00FA9A",
106
+ "mediumturquoise" => "48D1CC",
107
+ "mediumvioletred" => "C71585",
108
+ "midnightblue" => "191970",
109
+ "mintcream" => "F5FFFA",
110
+ "mistyrose" => "FFE4E1",
111
+ "moccasin" => "FFE4B5",
112
+ "navajowhite" => "FFDEAD",
113
+ "navy" => "000080",
114
+ "oldlace" => "FDF5E6",
115
+ "olive" => "808000",
116
+ "olivedrab" => "6B8E23",
117
+ "orange" => "FFA500",
118
+ "orangered" => "FF4500",
119
+ "orchid" => "DA70D6",
120
+ "palegoldenrod" => "EEE8AA",
121
+ "palegreen" => "98FB98",
122
+ "paleturquoise" => "AFEEEE",
123
+ "palevioletred" => "DB7093",
124
+ "papayawhip" => "FFEFD5",
125
+ "peachpuff" => "FFDAB9",
126
+ "peru" => "CD853F",
127
+ "pink" => "FFC0CB",
128
+ "plum" => "DDA0DD",
129
+ "powderblue" => "B0E0E6",
130
+ "purple" => "800080",
131
+ "red" => "FF0000",
132
+ "rosybrown" => "BC8F8F",
133
+ "royalblue" => "4169E1",
134
+ "saddlebrown" => "8B4513",
135
+ "salmon" => "FA8072",
136
+ "sandybrown" => "F4A460",
137
+ "seagreen" => "2E8B57",
138
+ "seashell" => "FFF5EE",
139
+ "sienna" => "A0522D",
140
+ "silver" => "C0C0C0",
141
+ "skyblue" => "87CEEB",
142
+ "slateblue" => "6A5ACD",
143
+ "slategray" => "708090",
144
+ "slategrey" => "708090",
145
+ "snow" => "FFFAFA",
146
+ "springgreen" => "00FF7F",
147
+ "steelblue" => "4682B4",
148
+ "tan" => "D2B48C",
149
+ "teal" => "008080",
150
+ "thistle" => "D8BFD8",
151
+ "tomato" => "FF6347",
152
+ "turquoise" => "40E0D0",
153
+ "violet" => "EE82EE",
154
+ "wheat" => "F5DEB3",
155
+ "white" => "FFFFFF",
156
+ "whitesmoke" => "F5F5F5",
157
+ "yellow" => "FFFF00",
158
+ "yellowgreen" => "9ACD32",
159
+ );
160
+
161
+ static function parse($colour) {
162
+ if ( is_array($colour) ) {
163
+ // Assume the array has the right format...
164
+ // FIXME: should/could verify this.
165
+ return $colour;
166
+ }
167
+
168
+ static $cache = array();
169
+
170
+ $colour = strtolower($colour);
171
+
172
+ if ( isset($cache[$colour]) ) {
173
+ return $cache[$colour];
174
+ }
175
+
176
+ if ( in_array($colour, array("transparent", "inherit")) ) {
177
+ return $cache[$colour] = $colour;
178
+ }
179
+
180
+ if ( isset(self::$cssColorNames[$colour]) ) {
181
+ return $cache[$colour] = self::getArray(self::$cssColorNames[$colour]);
182
+ }
183
+
184
+ $length = mb_strlen($colour);
185
+
186
+ // #rgb format
187
+ if ( $length == 4 && $colour[0] === "#" ) {
188
+ return $cache[$colour] = self::getArray($colour[1].$colour[1].$colour[2].$colour[2].$colour[3].$colour[3]);
189
+ }
190
+
191
+ // #rrggbb format
192
+ else if ( $length == 7 && $colour[0] === "#" ) {
193
+ return $cache[$colour] = self::getArray(mb_substr($colour, 1, 6));
194
+ }
195
+
196
+ // rgb( r,g,b ) format
197
+ else if ( mb_strpos($colour, "rgb") !== false ) {
198
+ $i = mb_strpos($colour, "(");
199
+ $j = mb_strpos($colour, ")");
200
+
201
+ // Bad colour value
202
+ if ( $i === false || $j === false ) {
203
+ return null;
204
+ }
205
+
206
+ $triplet = explode(",", mb_substr($colour, $i+1, $j-$i-1));
207
+
208
+ if ( count($triplet) != 3 ) {
209
+ return null;
210
+ }
211
+
212
+ foreach (array_keys($triplet) as $c) {
213
+ $triplet[$c] = trim($triplet[$c]);
214
+
215
+ if ( $triplet[$c][mb_strlen($triplet[$c]) - 1] === "%" ) {
216
+ $triplet[$c] = round($triplet[$c] * 2.55);
217
+ }
218
+ }
219
+
220
+ return $cache[$colour] = self::getArray(vsprintf("%02X%02X%02X", $triplet));
221
+
222
+ }
223
+
224
+ // cmyk( c,m,y,k ) format
225
+ // http://www.w3.org/TR/css3-gcpm/#cmyk-colors
226
+ else if ( mb_strpos($colour, "cmyk") !== false ) {
227
+ $i = mb_strpos($colour, "(");
228
+ $j = mb_strpos($colour, ")");
229
+
230
+ // Bad colour value
231
+ if ( $i === false || $j === false ) {
232
+ return null;
233
+ }
234
+
235
+ $values = explode(",", mb_substr($colour, $i+1, $j-$i-1));
236
+
237
+ if ( count($values) != 4 ) {
238
+ return null;
239
+ }
240
+
241
+ foreach ($values as &$c) {
242
+ $c = floatval(trim($c));
243
+ if ($c > 1.0) $c = 1.0;
244
+ if ($c < 0.0) $c = 0.0;
245
+ }
246
+
247
+ return $cache[$colour] = self::getArray($values);
248
+ }
249
+ }
250
+
251
+ static function getArray($colour) {
252
+ $c = array(null, null, null, null, "hex" => null);
253
+
254
+ if (is_array($colour)) {
255
+ $c = $colour;
256
+ $c["c"] = $c[0];
257
+ $c["m"] = $c[1];
258
+ $c["y"] = $c[2];
259
+ $c["k"] = $c[3];
260
+ $c["hex"] = "cmyk($c[0],$c[1],$c[2],$c[3])";
261
+ }
262
+ else {
263
+ $c[0] = hexdec(mb_substr($colour, 0, 2)) / 0xff;
264
+ $c[1] = hexdec(mb_substr($colour, 2, 2)) / 0xff;
265
+ $c[2] = hexdec(mb_substr($colour, 4, 2)) / 0xff;
266
+ $c["r"] = $c[0];
267
+ $c["g"] = $c[1];
268
+ $c["b"] = $c[2];
269
+ $c["hex"] = "#$colour";
270
+ }
271
+
272
+ return $c;
273
+ }
274
+ }
dompdf/include/dompdf.cls.php ADDED
@@ -0,0 +1,1064 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * DOMPDF - PHP5 HTML to PDF renderer
12
+ *
13
+ * DOMPDF loads HTML and does its best to render it as a PDF. It gets its
14
+ * name from the new DomDocument PHP5 extension. Source HTML is first
15
+ * parsed by a DomDocument object. DOMPDF takes the resulting DOM tree and
16
+ * attaches a {@link Frame} object to each node. {@link Frame} objects store
17
+ * positioning and layout information and each has a reference to a {@link
18
+ * Style} object.
19
+ *
20
+ * Style information is loaded and parsed (see {@link Stylesheet}) and is
21
+ * applied to the frames in the tree by using XPath. CSS selectors are
22
+ * converted into XPath queries, and the computed {@link Style} objects are
23
+ * applied to the {@link Frame}s.
24
+ *
25
+ * {@link Frame}s are then decorated (in the design pattern sense of the
26
+ * word) based on their CSS display property ({@link
27
+ * http://www.w3.org/TR/CSS21/visuren.html#propdef-display}).
28
+ * Frame_Decorators augment the basic {@link Frame} class by adding
29
+ * additional properties and methods specific to the particular type of
30
+ * {@link Frame}. For example, in the CSS layout model, block frames
31
+ * (display: block;) contain line boxes that are usually filled with text or
32
+ * other inline frames. The Block_Frame_Decorator therefore adds a $lines
33
+ * property as well as methods to add {@link Frame}s to lines and to add
34
+ * additional lines. {@link Frame}s also are attached to specific
35
+ * Positioner and {@link Frame_Reflower} objects that contain the
36
+ * positioining and layout algorithm for a specific type of frame,
37
+ * respectively. This is an application of the Strategy pattern.
38
+ *
39
+ * Layout, or reflow, proceeds recursively (post-order) starting at the root
40
+ * of the document. Space constraints (containing block width & height) are
41
+ * pushed down, and resolved positions and sizes bubble up. Thus, every
42
+ * {@link Frame} in the document tree is traversed once (except for tables
43
+ * which use a two-pass layout algorithm). If you are interested in the
44
+ * details, see the reflow() method of the Reflower classes.
45
+ *
46
+ * Rendering is relatively straightforward once layout is complete. {@link
47
+ * Frame}s are rendered using an adapted {@link Cpdf} class, originally
48
+ * written by Wayne Munro, http://www.ros.co.nz/pdf/. (Some performance
49
+ * related changes have been made to the original {@link Cpdf} class, and
50
+ * the {@link CPDF_Adapter} class provides a simple, stateless interface to
51
+ * PDF generation.) PDFLib support has now also been added, via the {@link
52
+ * PDFLib_Adapter}.
53
+ *
54
+ *
55
+ * @package dompdf
56
+ */
57
+ class DOMPDF {
58
+
59
+ /**
60
+ * DomDocument representing the HTML document
61
+ *
62
+ * @var DOMDocument
63
+ */
64
+ protected $_xml;
65
+
66
+ /**
67
+ * Frame_Tree derived from the DOM tree
68
+ *
69
+ * @var Frame_Tree
70
+ */
71
+ protected $_tree;
72
+
73
+ /**
74
+ * Stylesheet for the document
75
+ *
76
+ * @var Stylesheet
77
+ */
78
+ protected $_css;
79
+
80
+ /**
81
+ * Actual PDF renderer
82
+ *
83
+ * @var Canvas
84
+ */
85
+ protected $_pdf;
86
+
87
+ /**
88
+ * Desired paper size ('letter', 'legal', 'A4', etc.)
89
+ *
90
+ * @var string
91
+ */
92
+ protected $_paper_size;
93
+
94
+ /**
95
+ * Paper orientation ('portrait' or 'landscape')
96
+ *
97
+ * @var string
98
+ */
99
+ protected $_paper_orientation;
100
+
101
+ /**
102
+ * Callbacks on new page and new element
103
+ *
104
+ * @var array
105
+ */
106
+ protected $_callbacks;
107
+
108
+ /**
109
+ * Experimental caching capability
110
+ *
111
+ * @var string
112
+ */
113
+ private $_cache_id;
114
+
115
+ /**
116
+ * Base hostname
117
+ *
118
+ * Used for relative paths/urls
119
+ * @var string
120
+ */
121
+ protected $_base_host;
122
+
123
+ /**
124
+ * Absolute base path
125
+ *
126
+ * Used for relative paths/urls
127
+ * @var string
128
+ */
129
+ protected $_base_path;
130
+
131
+ /**
132
+ * Protcol used to request file (file://, http://, etc)
133
+ *
134
+ * @var string
135
+ */
136
+ protected $_protocol;
137
+
138
+ /**
139
+ * HTTP context created with stream_context_create()
140
+ * Will be used for file_get_contents
141
+ *
142
+ * @var resource
143
+ */
144
+ protected $_http_context;
145
+
146
+ /**
147
+ * Timestamp of the script start time
148
+ *
149
+ * @var int
150
+ */
151
+ private $_start_time = null;
152
+
153
+ /**
154
+ * The system's locale
155
+ *
156
+ * @var string
157
+ */
158
+ private $_system_locale = null;
159
+
160
+ /**
161
+ * Tells if the system's locale is the C standard one
162
+ *
163
+ * @var bool
164
+ */
165
+ private $_locale_standard = false;
166
+
167
+ /**
168
+ * The default view of the PDF in the viewer
169
+ *
170
+ * @var string
171
+ */
172
+ private $_default_view = "Fit";
173
+
174
+ /**
175
+ * The default view options of the PDF in the viewer
176
+ *
177
+ * @var array
178
+ */
179
+ private $_default_view_options = array();
180
+
181
+ /**
182
+ * Tells wether the DOM document is in quirksmode (experimental)
183
+ *
184
+ * @var bool
185
+ */
186
+ private $_quirksmode = false;
187
+
188
+ /**
189
+ * The list of built-in fonts
190
+ *
191
+ * @var array
192
+ */
193
+ public static $native_fonts = array(
194
+ "courier", "courier-bold", "courier-oblique", "courier-boldoblique",
195
+ "helvetica", "helvetica-bold", "helvetica-oblique", "helvetica-boldoblique",
196
+ "times-roman", "times-bold", "times-italic", "times-bolditalic",
197
+ "symbol", "zapfdinbats"
198
+ );
199
+
200
+ private $_options = array(
201
+ // Directories
202
+ "temp_dir" => DOMPDF_TEMP_DIR,
203
+ "font_dir" => DOMPDF_FONT_DIR,
204
+ "font_cache" => DOMPDF_FONT_CACHE,
205
+ "chroot" => DOMPDF_CHROOT,
206
+ "log_output_file" => DOMPDF_LOG_OUTPUT_FILE,
207
+
208
+ // Rendering
209
+ "default_media_type" => DOMPDF_DEFAULT_MEDIA_TYPE,
210
+ "default_paper_size" => DOMPDF_DEFAULT_PAPER_SIZE,
211
+ "default_font" => DOMPDF_DEFAULT_FONT,
212
+ "dpi" => DOMPDF_DPI,
213
+ "font_height_ratio" => DOMPDF_FONT_HEIGHT_RATIO,
214
+
215
+ // Features
216
+ "enable_unicode" => DOMPDF_UNICODE_ENABLED,
217
+ "enable_php" => DOMPDF_ENABLE_PHP,
218
+ "enable_remote" => DOMPDF_ENABLE_REMOTE,
219
+ "enable_css_float" => DOMPDF_ENABLE_CSS_FLOAT,
220
+ "enable_javascript" => DOMPDF_ENABLE_JAVASCRIPT,
221
+ "enable_html5_parser" => DOMPDF_ENABLE_HTML5PARSER,
222
+ "enable_font_subsetting" => DOMPDF_ENABLE_FONTSUBSETTING,
223
+
224
+ // Debug
225
+ "debug_png" => DEBUGPNG,
226
+ "debug_keep_temp" => DEBUGKEEPTEMP,
227
+ "debug_css" => DEBUGCSS,
228
+ "debug_layout" => DEBUG_LAYOUT,
229
+ "debug_layout_lines" => DEBUG_LAYOUT_LINES,
230
+ "debug_layout_blocks" => DEBUG_LAYOUT_BLOCKS,
231
+ "debug_layout_inline" => DEBUG_LAYOUT_INLINE,
232
+ "debug_layout_padding_box" => DEBUG_LAYOUT_PADDINGBOX,
233
+
234
+ // Admin
235
+ "admin_username" => DOMPDF_ADMIN_USERNAME,
236
+ "admin_password" => DOMPDF_ADMIN_PASSWORD,
237
+ );
238
+
239
+ /**
240
+ * Class constructor
241
+ */
242
+ function __construct() {
243
+ $this->_locale_standard = sprintf('%.1f', 1.0) == '1.0';
244
+
245
+ $this->save_locale();
246
+
247
+ $this->_messages = array();
248
+ $this->_css = new Stylesheet($this);
249
+ $this->_pdf = null;
250
+ $this->_paper_size = "letter";
251
+ $this->_paper_orientation = "portrait";
252
+ $this->_base_protocol = "";
253
+ $this->_base_host = "";
254
+ $this->_base_path = "";
255
+ $this->_http_context = null;
256
+ $this->_callbacks = array();
257
+ $this->_cache_id = null;
258
+
259
+ $this->restore_locale();
260
+ }
261
+
262
+ /**
263
+ * Class destructor
264
+ */
265
+ function __destruct() {
266
+ clear_object($this);
267
+ }
268
+
269
+ /**
270
+ * Get the dompdf option value
271
+ *
272
+ * @param string $key
273
+ *
274
+ * @return mixed
275
+ * @throws DOMPDF_Exception
276
+ */
277
+ function get_option($key) {
278
+ if ( !array_key_exists($key, $this->_options) ) {
279
+ throw new DOMPDF_Exception("Option '$key' doesn't exist");
280
+ }
281
+
282
+ return $this->_options[$key];
283
+ }
284
+
285
+ /**
286
+ * @param string $key
287
+ * @param mixed $value
288
+ *
289
+ * @throws DOMPDF_Exception
290
+ */
291
+ function set_option($key, $value) {
292
+ if ( !array_key_exists($key, $this->_options) ) {
293
+ throw new DOMPDF_Exception("Option '$key' doesn't exist");
294
+ }
295
+
296
+ $this->_options[$key] = $value;
297
+ }
298
+
299
+ /**
300
+ * @param array $options
301
+ */
302
+ function set_options(array $options) {
303
+ foreach ($options as $key => $value) {
304
+ $this->set_option($key, $value);
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Save the system's locale configuration and
310
+ * set the right value for numeric formatting
311
+ */
312
+ private function save_locale() {
313
+ if ( $this->_locale_standard ) {
314
+ return;
315
+ }
316
+
317
+ $this->_system_locale = setlocale(LC_NUMERIC, "C");
318
+ }
319
+
320
+ /**
321
+ * Restore the system's locale configuration
322
+ */
323
+ private function restore_locale() {
324
+ if ( $this->_locale_standard ) {
325
+ return;
326
+ }
327
+
328
+ setlocale(LC_NUMERIC, $this->_system_locale);
329
+ }
330
+
331
+ /**
332
+ * Returns the underlying {@link Frame_Tree} object
333
+ *
334
+ * @return Frame_Tree
335
+ */
336
+ function get_tree() {
337
+ return $this->_tree;
338
+ }
339
+
340
+ /**
341
+ * Sets the protocol to use
342
+ * FIXME validate these
343
+ *
344
+ * @param string $proto
345
+ */
346
+ function set_protocol($proto) {
347
+ $this->_protocol = $proto;
348
+ }
349
+
350
+ /**
351
+ * Sets the base hostname
352
+ *
353
+ * @param string $host
354
+ */
355
+ function set_host($host) {
356
+ $this->_base_host = $host;
357
+ }
358
+
359
+ /**
360
+ * Sets the base path
361
+ *
362
+ * @param string $path
363
+ */
364
+ function set_base_path($path) {
365
+ $this->_base_path = $path;
366
+ }
367
+
368
+ /**
369
+ * Sets the HTTP context
370
+ *
371
+ * @param resource $http_context
372
+ */
373
+ function set_http_context($http_context) {
374
+ $this->_http_context = $http_context;
375
+ }
376
+
377
+ /**
378
+ * Sets the default view
379
+ *
380
+ * @param string $default_view The default document view
381
+ * @param array $options The view's options
382
+ */
383
+ function set_default_view($default_view, $options) {
384
+ $this->_default_view = $default_view;
385
+ $this->_default_view_options = $options;
386
+ }
387
+
388
+ /**
389
+ * Returns the protocol in use
390
+ *
391
+ * @return string
392
+ */
393
+ function get_protocol() {
394
+ return $this->_protocol;
395
+ }
396
+
397
+ /**
398
+ * Returns the base hostname
399
+ *
400
+ * @return string
401
+ */
402
+ function get_host() {
403
+ return $this->_base_host;
404
+ }
405
+
406
+ /**
407
+ * Returns the base path
408
+ *
409
+ * @return string
410
+ */
411
+ function get_base_path() {
412
+ return $this->_base_path;
413
+ }
414
+
415
+ /**
416
+ * Returns the HTTP context
417
+ *
418
+ * @return resource
419
+ */
420
+ function get_http_context() {
421
+ return $this->_http_context;
422
+ }
423
+
424
+ /**
425
+ * Return the underlying Canvas instance (e.g. CPDF_Adapter, GD_Adapter)
426
+ *
427
+ * @return Canvas
428
+ */
429
+ function get_canvas() {
430
+ return $this->_pdf;
431
+ }
432
+
433
+ /**
434
+ * Returns the callbacks array
435
+ *
436
+ * @return array
437
+ */
438
+ function get_callbacks() {
439
+ return $this->_callbacks;
440
+ }
441
+
442
+ /**
443
+ * Returns the stylesheet
444
+ *
445
+ * @return Stylesheet
446
+ */
447
+ function get_css() {
448
+ return $this->_css;
449
+ }
450
+
451
+ /**
452
+ * @return DOMDocument
453
+ */
454
+ function get_dom() {
455
+ return $this->_xml;
456
+ }
457
+
458
+ /**
459
+ * Loads an HTML file
460
+ * Parse errors are stored in the global array _dompdf_warnings.
461
+ *
462
+ * @param string $file a filename or url to load
463
+ *
464
+ * @throws DOMPDF_Exception
465
+ */
466
+ function load_html_file($file) {
467
+ $this->save_locale();
468
+
469
+ // Store parsing warnings as messages (this is to prevent output to the
470
+ // browser if the html is ugly and the dom extension complains,
471
+ // preventing the pdf from being streamed.)
472
+ if ( !$this->_protocol && !$this->_base_host && !$this->_base_path ) {
473
+ list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($file);
474
+ }
475
+
476
+ if ( !$this->get_option("enable_remote") && ($this->_protocol != "" && $this->_protocol !== "file://" ) ) {
477
+ throw new DOMPDF_Exception("Remote file requested, but DOMPDF_ENABLE_REMOTE is false.");
478
+ }
479
+
480
+ if ($this->_protocol == "" || $this->_protocol === "file://") {
481
+
482
+ $realfile = realpath($file);
483
+ if ( !$file ) {
484
+ throw new DOMPDF_Exception("File '$file' not found.");
485
+ }
486
+
487
+ $chroot = $this->get_option("chroot");
488
+ if ( strpos($realfile, $chroot) !== 0 ) {
489
+ throw new DOMPDF_Exception("Permission denied on $file. The file could not be found under the directory specified by DOMPDF_CHROOT.");
490
+ }
491
+
492
+ // Exclude dot files (e.g. .htaccess)
493
+ if ( substr(basename($realfile), 0, 1) === "." ) {
494
+ throw new DOMPDF_Exception("Permission denied on $file.");
495
+ }
496
+
497
+ $file = $realfile;
498
+ }
499
+
500
+ $contents = file_get_contents($file, null, $this->_http_context);
501
+ $encoding = null;
502
+
503
+ // See http://the-stickman.com/web-development/php/getting-http-response-headers-when-using-file_get_contents/
504
+ if ( isset($http_response_header) ) {
505
+ foreach($http_response_header as $_header) {
506
+ if ( preg_match("@Content-Type:\s*[\w/]+;\s*?charset=([^\s]+)@i", $_header, $matches) ) {
507
+ $encoding = strtoupper($matches[1]);
508
+ break;
509
+ }
510
+ }
511
+ }
512
+
513
+ $this->restore_locale();
514
+
515
+ $this->load_html($contents, $encoding);
516
+ }
517
+
518
+ /**
519
+ * Loads an HTML string
520
+ * Parse errors are stored in the global array _dompdf_warnings.
521
+ * @todo use the $encoding variable
522
+ *
523
+ * @param string $str HTML text to load
524
+ * @param string $encoding Not used yet
525
+ */
526
+ function load_html($str, $encoding = null) {
527
+ $this->save_locale();
528
+
529
+ // FIXME: Determine character encoding, switch to UTF8, update meta tag. Need better http/file stream encoding detection, currently relies on text or meta tag.
530
+ mb_detect_order('auto');
531
+
532
+ if (mb_detect_encoding($str) !== 'UTF-8') {
533
+ $metatags = array(
534
+ '@<meta\s+http-equiv="Content-Type"\s+content="(?:[\w/]+)(?:;\s*?charset=([^\s"]+))?@i',
535
+ '@<meta\s+content="(?:[\w/]+)(?:;\s*?charset=([^\s"]+))"?\s+http-equiv="Content-Type"@i',
536
+ '@<meta [^>]*charset\s*=\s*["\']?\s*([^"\' ]+)@i',
537
+ );
538
+
539
+ foreach($metatags as $metatag) {
540
+ if (preg_match($metatag, $str, $matches)) break;
541
+ }
542
+
543
+ if (mb_detect_encoding($str) == '') {
544
+ if (isset($matches[1])) {
545
+ $encoding = strtoupper($matches[1]);
546
+ }
547
+ else {
548
+ $encoding = 'UTF-8';
549
+ }
550
+ }
551
+ else {
552
+ if ( isset($matches[1]) ) {
553
+ $encoding = strtoupper($matches[1]);
554
+ }
555
+ else {
556
+ $encoding = 'auto';
557
+ }
558
+ }
559
+
560
+ if ( $encoding !== 'UTF-8' ) {
561
+ $str = mb_convert_encoding($str, 'UTF-8', $encoding);
562
+ }
563
+
564
+ if ( isset($matches[1]) ) {
565
+ $str = preg_replace('/charset=([^\s"]+)/i', 'charset=UTF-8', $str);
566
+ }
567
+ else {
568
+ $str = str_replace('<head>', '<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">', $str);
569
+ }
570
+ }
571
+ else {
572
+ $encoding = 'UTF-8';
573
+ }
574
+
575
+ // remove BOM mark from UTF-8, it's treated as document text by DOMDocument
576
+ // FIXME: roll this into the encoding detection using UTF-8/16/32 BOM (http://us2.php.net/manual/en/function.mb-detect-encoding.php#91051)?
577
+ if ( substr($str, 0, 3) == chr(0xEF).chr(0xBB).chr(0xBF) ) {
578
+ $str = substr($str, 3);
579
+ }
580
+
581
+ // Parse embedded php, first-pass
582
+ if ( $this->get_option("enable_php") ) {
583
+ ob_start();
584
+ eval("?" . ">$str");
585
+ $str = ob_get_clean();
586
+ }
587
+
588
+ // if the document contains non utf-8 with a utf-8 meta tag chars and was
589
+ // detected as utf-8 by mbstring, problems could happen.
590
+ // http://devzone.zend.com/article/8855
591
+ if ( $encoding !== 'UTF-8' ) {
592
+ $re = '/<meta ([^>]*)((?:charset=[^"\' ]+)([^>]*)|(?:charset=["\'][^"\' ]+["\']))([^>]*)>/i';
593
+ $str = preg_replace($re, '<meta $1$3>', $str);
594
+ }
595
+
596
+ // Store parsing warnings as messages
597
+ set_error_handler("record_warnings");
598
+
599
+ // @todo Take the quirksmode into account
600
+ // http://hsivonen.iki.fi/doctype/
601
+ // https://developer.mozilla.org/en/mozilla's_quirks_mode
602
+ $quirksmode = false;
603
+
604
+ if ( $this->get_option("enable_html5_parser") ) {
605
+ $tokenizer = new HTML5_Tokenizer($str);
606
+ $tokenizer->parse();
607
+ $doc = $tokenizer->save();
608
+
609
+ // Remove #text children nodes in nodes that shouldn't have
610
+ $tag_names = array("html", "table", "tbody", "thead", "tfoot", "tr");
611
+ foreach($tag_names as $tag_name) {
612
+ $nodes = $doc->getElementsByTagName($tag_name);
613
+
614
+ foreach($nodes as $node) {
615
+ self::remove_text_nodes($node);
616
+ }
617
+ }
618
+
619
+ $quirksmode = ($tokenizer->getTree()->getQuirksMode() > HTML5_TreeBuilder::NO_QUIRKS);
620
+ }
621
+ else {
622
+ $doc = new DOMDocument();
623
+ $doc->preserveWhiteSpace = true;
624
+ $doc->loadHTML($str);
625
+
626
+ // If some text is before the doctype, we are in quirksmode
627
+ if ( preg_match("/^(.+)<!doctype/i", ltrim($str), $matches) ) {
628
+ $quirksmode = true;
629
+ }
630
+ // If no doctype is provided, we are in quirksmode
631
+ elseif ( !preg_match("/^<!doctype/i", ltrim($str), $matches) ) {
632
+ $quirksmode = true;
633
+ }
634
+ else {
635
+ // HTML5 <!DOCTYPE html>
636
+ if ( !$doc->doctype->publicId && !$doc->doctype->systemId ) {
637
+ $quirksmode = false;
638
+ }
639
+
640
+ // not XHTML
641
+ if ( !preg_match("/xhtml/i", $doc->doctype->publicId) ) {
642
+ $quirksmode = true;
643
+ }
644
+ }
645
+ }
646
+
647
+ $this->_xml = $doc;
648
+ $this->_quirksmode = $quirksmode;
649
+
650
+ $this->_tree = new Frame_Tree($this->_xml);
651
+
652
+ restore_error_handler();
653
+
654
+ $this->restore_locale();
655
+ }
656
+
657
+ static function remove_text_nodes(DOMNode $node) {
658
+ $children = array();
659
+ for ($i = 0; $i < $node->childNodes->length; $i++) {
660
+ $child = $node->childNodes->item($i);
661
+ if ( $child->nodeName === "#text" ) {
662
+ $children[] = $child;
663
+ }
664
+ }
665
+
666
+ foreach($children as $child) {
667
+ $node->removeChild($child);
668
+ }
669
+ }
670
+
671
+ /**
672
+ * Builds the {@link Frame_Tree}, loads any CSS and applies the styles to
673
+ * the {@link Frame_Tree}
674
+ */
675
+ protected function _process_html() {
676
+ $this->save_locale();
677
+
678
+ $this->_tree->build_tree();
679
+
680
+ $this->_css->load_css_file(Stylesheet::DEFAULT_STYLESHEET, Stylesheet::ORIG_UA);
681
+
682
+ $acceptedmedia = Stylesheet::$ACCEPTED_GENERIC_MEDIA_TYPES;
683
+ $acceptedmedia[] = $this->get_option("default_media_type");
684
+
685
+ // <base href="" />
686
+ $base_nodes = $this->_xml->getElementsByTagName("base");
687
+ if ( $base_nodes->length && ($href = $base_nodes->item(0)->getAttribute("href")) ) {
688
+ list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($href);
689
+ }
690
+
691
+ // Set the base path of the Stylesheet to that of the file being processed
692
+ $this->_css->set_protocol($this->_protocol);
693
+ $this->_css->set_host($this->_base_host);
694
+ $this->_css->set_base_path($this->_base_path);
695
+
696
+ // Get all the stylesheets so that they are processed in document order
697
+ $xpath = new DOMXPath($this->_xml);
698
+ $stylesheets = $xpath->query("//*[name() = 'link' or name() = 'style']");
699
+
700
+ foreach($stylesheets as $tag) {
701
+ switch (strtolower($tag->nodeName)) {
702
+ // load <link rel="STYLESHEET" ... /> tags
703
+ case "link":
704
+ if ( mb_strtolower(stripos($tag->getAttribute("rel"), "stylesheet") !== false) || // may be "appendix stylesheet"
705
+ mb_strtolower($tag->getAttribute("type")) === "text/css" ) {
706
+ //Check if the css file is for an accepted media type
707
+ //media not given then always valid
708
+ $formedialist = preg_split("/[\s\n,]/", $tag->getAttribute("media"),-1, PREG_SPLIT_NO_EMPTY);
709
+ if ( count($formedialist) > 0 ) {
710
+ $accept = false;
711
+ foreach ( $formedialist as $type ) {
712
+ if ( in_array(mb_strtolower(trim($type)), $acceptedmedia) ) {
713
+ $accept = true;
714
+ break;
715
+ }
716
+ }
717
+
718
+ if (!$accept) {
719
+ //found at least one mediatype, but none of the accepted ones
720
+ //Skip this css file.
721
+ continue;
722
+ }
723
+ }
724
+
725
+ $url = $tag->getAttribute("href");
726
+ $url = build_url($this->_protocol, $this->_base_host, $this->_base_path, $url);
727
+
728
+ $this->_css->load_css_file($url, Stylesheet::ORIG_AUTHOR);
729
+ }
730
+ break;
731
+
732
+ // load <style> tags
733
+ case "style":
734
+ // Accept all <style> tags by default (note this is contrary to W3C
735
+ // HTML 4.0 spec:
736
+ // http://www.w3.org/TR/REC-html40/present/styles.html#adef-media
737
+ // which states that the default media type is 'screen'
738
+ if ( $tag->hasAttributes() &&
739
+ ($media = $tag->getAttribute("media")) &&
740
+ !in_array($media, $acceptedmedia) ) {
741
+ continue;
742
+ }
743
+
744
+ $css = "";
745
+ if ( $tag->hasChildNodes() ) {
746
+ $child = $tag->firstChild;
747
+ while ( $child ) {
748
+ $css .= $child->nodeValue; // Handle <style><!-- blah --></style>
749
+ $child = $child->nextSibling;
750
+ }
751
+ }
752
+ else {
753
+ $css = $tag->nodeValue;
754
+ }
755
+
756
+ $this->_css->load_css($css);
757
+ break;
758
+ }
759
+ }
760
+
761
+ $this->restore_locale();
762
+ }
763
+
764
+ /**
765
+ * Sets the paper size & orientation
766
+ *
767
+ * @param string $size 'letter', 'legal', 'A4', etc. {@link CPDF_Adapter::$PAPER_SIZES}
768
+ * @param string $orientation 'portrait' or 'landscape'
769
+ */
770
+ function set_paper($size, $orientation = "portrait") {
771
+ $this->_paper_size = $size;
772
+ $this->_paper_orientation = $orientation;
773
+ }
774
+
775
+ /**
776
+ * Enable experimental caching capability
777
+ * @access private
778
+ */
779
+ function enable_caching($cache_id) {
780
+ $this->_cache_id = $cache_id;
781
+ }
782
+
783
+ /**
784
+ * Sets callbacks for events like rendering of pages and elements.
785
+ * The callbacks array contains arrays with 'event' set to 'begin_page',
786
+ * 'end_page', 'begin_frame', or 'end_frame' and 'f' set to a function or
787
+ * object plus method to be called.
788
+ *
789
+ * The function 'f' must take an array as argument, which contains info
790
+ * about the event.
791
+ *
792
+ * @param array $callbacks the set of callbacks to set
793
+ */
794
+ function set_callbacks($callbacks) {
795
+ if (is_array($callbacks)) {
796
+ $this->_callbacks = array();
797
+ foreach ($callbacks as $c) {
798
+ if (is_array($c) && isset($c['event']) && isset($c['f'])) {
799
+ $event = $c['event'];
800
+ $f = $c['f'];
801
+ if (is_callable($f) && is_string($event)) {
802
+ $this->_callbacks[$event][] = $f;
803
+ }
804
+ }
805
+ }
806
+ }
807
+ }
808
+
809
+ /**
810
+ * Get the quirks mode
811
+ *
812
+ * @return boolean true if quirks mode is active
813
+ */
814
+ function get_quirksmode(){
815
+ return $this->_quirksmode;
816
+ }
817
+
818
+ function parse_default_view($value) {
819
+ $valid = array("XYZ", "Fit", "FitH", "FitV", "FitR", "FitB", "FitBH", "FitBV");
820
+
821
+ $options = preg_split("/\s*,\s*/", trim($value));
822
+ $default_view = array_shift($options);
823
+
824
+ if ( !in_array($default_view, $valid) ) {
825
+ return false;
826
+ }
827
+
828
+ $this->set_default_view($default_view, $options);
829
+ return true;
830
+ }
831
+
832
+ /**
833
+ * Renders the HTML to PDF
834
+ */
835
+ function render() {
836
+ $this->save_locale();
837
+
838
+ $log_output_file = $this->get_option("log_output_file");
839
+ if ( $log_output_file ) {
840
+ if ( !file_exists($log_output_file) && is_writable(dirname($log_output_file)) ) {
841
+ touch($log_output_file);
842
+ }
843
+
844
+ $this->_start_time = microtime(true);
845
+ ob_start();
846
+ }
847
+
848
+ //enable_mem_profile();
849
+
850
+ $this->_process_html();
851
+
852
+ $this->_css->apply_styles($this->_tree);
853
+
854
+ // @page style rules : size, margins
855
+ $page_styles = $this->_css->get_page_styles();
856
+
857
+ $base_page_style = $page_styles["base"];
858
+ unset($page_styles["base"]);
859
+
860
+ foreach($page_styles as $_page_style) {
861
+ $_page_style->inherit($base_page_style);
862
+ }
863
+
864
+ if ( is_array($base_page_style->size) ) {
865
+ $this->set_paper(array(0, 0, $base_page_style->size[0], $base_page_style->size[1]));
866
+ }
867
+
868
+ $this->_pdf = Canvas_Factory::get_instance($this, $this->_paper_size, $this->_paper_orientation);
869
+ Font_Metrics::init($this->_pdf);
870
+
871
+ if ( $this->get_option("enable_font_subsetting") && $this->_pdf instanceof CPDF_Adapter ) {
872
+ foreach ($this->_tree->get_frames() as $frame) {
873
+ $style = $frame->get_style();
874
+ $node = $frame->get_node();
875
+
876
+ // Handle text nodes
877
+ if ( $node->nodeName === "#text" ) {
878
+ $this->_pdf->register_string_subset($style->font_family, $node->nodeValue);
879
+ continue;
880
+ }
881
+
882
+ // Handle generated content (list items)
883
+ if ( $style->display === "list-item" ) {
884
+ $chars = List_Bullet_Renderer::get_counter_chars($style->list_style_type);
885
+ $this->_pdf->register_string_subset($style->font_family, $chars);
886
+ continue;
887
+ }
888
+
889
+ // TODO Handle other generated content (pseudo elements)
890
+ }
891
+ }
892
+
893
+ $root = null;
894
+
895
+ foreach ($this->_tree->get_frames() as $frame) {
896
+ // Set up the root frame
897
+ if ( is_null($root) ) {
898
+ $root = Frame_Factory::decorate_root( $this->_tree->get_root(), $this );
899
+ continue;
900
+ }
901
+
902
+ // Create the appropriate decorators, reflowers & positioners.
903
+ Frame_Factory::decorate_frame($frame, $this, $root);
904
+ }
905
+
906
+ // Add meta information
907
+ $title = $this->_xml->getElementsByTagName("title");
908
+ if ( $title->length ) {
909
+ $this->_pdf->add_info("Title", trim($title->item(0)->nodeValue));
910
+ }
911
+
912
+ $metas = $this->_xml->getElementsByTagName("meta");
913
+ $labels = array(
914
+ "author" => "Author",
915
+ "keywords" => "Keywords",
916
+ "description" => "Subject",
917
+ );
918
+ foreach($metas as $meta) {
919
+ $name = mb_strtolower($meta->getAttribute("name"));
920
+ $value = trim($meta->getAttribute("content"));
921
+
922
+ if ( isset($labels[$name]) ) {
923
+ $this->_pdf->add_info($labels[$name], $value);
924
+ continue;
925
+ }
926
+
927
+ if ( $name === "dompdf.view" && $this->parse_default_view($value) ) {
928
+ $this->_pdf->set_default_view($this->_default_view, $this->_default_view_options);
929
+ }
930
+ }
931
+
932
+ $root->set_containing_block(0, 0, $this->_pdf->get_width(), $this->_pdf->get_height());
933
+ $root->set_renderer(new Renderer($this));
934
+
935
+ // This is where the magic happens:
936
+ $root->reflow();
937
+
938
+ // Clean up cached images
939
+ Image_Cache::clear();
940
+
941
+ global $_dompdf_warnings, $_dompdf_show_warnings;
942
+ if ( $_dompdf_show_warnings ) {
943
+ echo '<b>DOMPDF Warnings</b><br><pre>';
944
+ foreach ($_dompdf_warnings as $msg) {
945
+ echo $msg . "\n";
946
+ }
947
+ echo $this->get_canvas()->get_cpdf()->messages;
948
+ echo '</pre>';
949
+ flush();
950
+ }
951
+
952
+ $this->restore_locale();
953
+ }
954
+
955
+ /**
956
+ * Add meta information to the PDF after rendering
957
+ */
958
+ function add_info($label, $value) {
959
+ if ( !is_null($this->_pdf) ) {
960
+ $this->_pdf->add_info($label, $value);
961
+ }
962
+ }
963
+
964
+ /**
965
+ * Writes the output buffer in the log file
966
+ *
967
+ * @return void
968
+ */
969
+ private function write_log() {
970
+ $log_output_file = $this->get_option("log_output_file");
971
+ if ( !$log_output_file || !is_writable($log_output_file) ) {
972
+ return;
973
+ }
974
+
975
+ $frames = Frame::$ID_COUNTER;
976
+ $memory = DOMPDF_memory_usage() / 1024;
977
+ $time = (microtime(true) - $this->_start_time) * 1000;
978
+
979
+ $out = sprintf(
980
+ "<span style='color: #000' title='Frames'>%6d</span>".
981
+ "<span style='color: #009' title='Memory'>%10.2f KB</span>".
982
+ "<span style='color: #900' title='Time'>%10.2f ms</span>".
983
+ "<span title='Quirksmode'> ".
984
+ ($this->_quirksmode ? "<span style='color: #d00'> ON</span>" : "<span style='color: #0d0'>OFF</span>").
985
+ "</span><br />", $frames, $memory, $time);
986
+
987
+ $out .= ob_get_clean();
988
+
989
+ $log_output_file = $this->get_option("log_output_file");
990
+ file_put_contents($log_output_file, $out);
991
+ }
992
+
993
+ /**
994
+ * Streams the PDF to the client
995
+ *
996
+ * The file will open a download dialog by default. The options
997
+ * parameter controls the output. Accepted options are:
998
+ *
999
+ * 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this
1000
+ * header is not included, off by default this header seems to
1001
+ * have caused some problems despite the fact that it is supposed
1002
+ * to solve them, so I am leaving it off by default.
1003
+ *
1004
+ * 'compress' = > 1 or 0 - apply content stream compression, this is
1005
+ * on (1) by default
1006
+ *
1007
+ * 'Attachment' => 1 or 0 - if 1, force the browser to open a
1008
+ * download dialog, on (1) by default
1009
+ *
1010
+ * @param string $filename the name of the streamed file
1011
+ * @param array $options header options (see above)
1012
+ */
1013
+ function stream($filename, $options = null) {
1014
+ $this->save_locale();
1015
+
1016
+ $this->write_log();
1017
+
1018
+ if ( !is_null($this->_pdf) ) {
1019
+ $this->_pdf->stream($filename, $options);
1020
+ }
1021
+
1022
+ $this->restore_locale();
1023
+ }
1024
+
1025
+ /**
1026
+ * Returns the PDF as a string
1027
+ *
1028
+ * The file will open a download dialog by default. The options
1029
+ * parameter controls the output. Accepted options are:
1030
+ *
1031
+ *
1032
+ * 'compress' = > 1 or 0 - apply content stream compression, this is
1033
+ * on (1) by default
1034
+ *
1035
+ *
1036
+ * @param array $options options (see above)
1037
+ *
1038
+ * @return string
1039
+ */
1040
+ function output($options = null) {
1041
+ $this->save_locale();
1042
+
1043
+ $this->write_log();
1044
+
1045
+ if ( is_null($this->_pdf) ) {
1046
+ return null;
1047
+ }
1048
+
1049
+ $output = $this->_pdf->output( $options );
1050
+
1051
+ $this->restore_locale();
1052
+
1053
+ return $output;
1054
+ }
1055
+
1056
+ /**
1057
+ * Returns the underlying HTML document as a string
1058
+ *
1059
+ * @return string
1060
+ */
1061
+ function output_html() {
1062
+ return $this->_xml->saveHTML();
1063
+ }
1064
+ }
dompdf/include/dompdf_exception.cls.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Standard exception thrown by DOMPDF classes
11
+ *
12
+ * @package dompdf
13
+ */
14
+ class DOMPDF_Exception extends Exception {
15
+
16
+ /**
17
+ * Class constructor
18
+ *
19
+ * @param string $message Error message
20
+ * @param int $code Error code
21
+ */
22
+ function __construct($message = null, $code = 0) {
23
+ parent::__construct($message, $code);
24
+ }
25
+
26
+ }
dompdf/include/dompdf_image_exception.cls.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Image exception thrown by DOMPDF
11
+ *
12
+ * @package dompdf
13
+ */
14
+ class DOMPDF_Image_Exception extends DOMPDF_Exception {
15
+
16
+ /**
17
+ * Class constructor
18
+ *
19
+ * @param string $message Error message
20
+ * @param int $code Error code
21
+ */
22
+ function __construct($message = null, $code = 0) {
23
+ parent::__construct($message, $code);
24
+ }
25
+
26
+ }
dompdf/include/file.skel ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author ...
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
dompdf/include/fixed_positioner.cls.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Positions fixely positioned frames
12
+ */
13
+ class Fixed_Positioner extends Positioner {
14
+
15
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
16
+
17
+ function position() {
18
+
19
+ $frame = $this->_frame;
20
+ $style = $frame->get_original_style();
21
+ $root = $frame->get_root();
22
+ $initialcb = $root->get_containing_block();
23
+ $initialcb_style = $root->get_style();
24
+
25
+ $p = $frame->find_block_parent();
26
+ if ( $p ) {
27
+ $p->add_line();
28
+ }
29
+
30
+ // Compute the margins of the @page style
31
+ $margin_top = $initialcb_style->length_in_pt($initialcb_style->margin_top, $initialcb["h"]);
32
+ $margin_right = $initialcb_style->length_in_pt($initialcb_style->margin_right, $initialcb["w"]);
33
+ $margin_bottom = $initialcb_style->length_in_pt($initialcb_style->margin_bottom, $initialcb["h"]);
34
+ $margin_left = $initialcb_style->length_in_pt($initialcb_style->margin_left, $initialcb["w"]);
35
+
36
+ // The needed computed style of the element
37
+ $height = $style->length_in_pt($style->height, $initialcb["h"]);
38
+ $width = $style->length_in_pt($style->width, $initialcb["w"]);
39
+
40
+ $top = $style->length_in_pt($style->top, $initialcb["h"]);
41
+ $right = $style->length_in_pt($style->right, $initialcb["w"]);
42
+ $bottom = $style->length_in_pt($style->bottom, $initialcb["h"]);
43
+ $left = $style->length_in_pt($style->left, $initialcb["w"]);
44
+
45
+ $y = $margin_top;
46
+ if ( isset($top) ) {
47
+ $y = $top + $margin_top;
48
+ if ( $top === "auto" ) {
49
+ $y = $margin_top;
50
+ if ( isset($bottom) && $bottom !== "auto" ) {
51
+ $y = $initialcb["h"] - $bottom - $margin_bottom;
52
+ $margin_height = $this->_frame->get_margin_height();
53
+ if ( $margin_height !== "auto" ) {
54
+ $y -= $margin_height;
55
+ }
56
+ else {
57
+ $y -= $height;
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ $x = $margin_left;
64
+ if ( isset($left) ) {
65
+ $x = $left + $margin_left;
66
+ if ( $left === "auto" ) {
67
+ $x = $margin_left;
68
+ if ( isset($right) && $right !== "auto" ) {
69
+ $x = $initialcb["w"] - $right - $margin_right;
70
+ $margin_width = $this->_frame->get_margin_width();
71
+ if ( $margin_width !== "auto" ) {
72
+ $x -= $margin_width;
73
+ }
74
+ else {
75
+ $x -= $width;
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ $frame->set_position($x, $y);
82
+
83
+ $children = $frame->get_children();
84
+ foreach($children as $child) {
85
+ $child->set_position($x, $y);
86
+ }
87
+ }
88
+ }
dompdf/include/font_metrics.cls.php ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ require_once DOMPDF_LIB_DIR . "/class.pdf.php";
12
+
13
+ /**
14
+ * Name of the font cache file
15
+ *
16
+ * This file must be writable by the webserver process only to update it
17
+ * with save_font_families() after adding the .afm file references of a new font family
18
+ * with Font_Metrics::save_font_families().
19
+ * This is typically done only from command line with load_font.php on converting
20
+ * ttf fonts to ufm with php-font-lib.
21
+ *
22
+ * Declared here because PHP5 prevents constants from being declared with expressions
23
+ */
24
+ if (!defined("__DOMPDF_FONT_CACHE_FILE")) {
25
+ if (file_exists(DOMPDF_FONT_DIR . "dompdf_font_family_cache")) {
26
+ define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache");
27
+ }
28
+ else {
29
+ define('__DOMPDF_FONT_CACHE_FILE', DOMPDF_FONT_DIR . "dompdf_font_family_cache.dist.php");
30
+ }
31
+ }
32
+
33
+ /**
34
+ * The font metrics class
35
+ *
36
+ * This class provides information about fonts and text. It can resolve
37
+ * font names into actual installed font files, as well as determine the
38
+ * size of text in a particular font and size.
39
+ *
40
+ * @static
41
+ * @package dompdf
42
+ */
43
+ class Font_Metrics {
44
+
45
+ /**
46
+ * @see __DOMPDF_FONT_CACHE_FILE
47
+ */
48
+ const CACHE_FILE = __DOMPDF_FONT_CACHE_FILE;
49
+
50
+ /**
51
+ * Underlying {@link Canvas} object to perform text size calculations
52
+ *
53
+ * @var Canvas
54
+ */
55
+ static protected $_pdf = null;
56
+
57
+ /**
58
+ * Array of font family names to font files
59
+ *
60
+ * Usually cached by the {@link load_font.php} script
61
+ *
62
+ * @var array
63
+ */
64
+ static protected $_font_lookup = array();
65
+
66
+
67
+ /**
68
+ * Class initialization
69
+ *
70
+ */
71
+ static function init(Canvas $canvas = null) {
72
+ if (!self::$_pdf) {
73
+ if (!$canvas) {
74
+ $canvas = Canvas_Factory::get_instance(new DOMPDF());
75
+ }
76
+
77
+ self::$_pdf = $canvas;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Calculates text size, in points
83
+ *
84
+ * @param string $text the text to be sized
85
+ * @param string $font the desired font
86
+ * @param float $size the desired font size
87
+ * @param float $word_spacing
88
+ * @param float $char_spacing
89
+ *
90
+ * @internal param float $spacing word spacing, if any
91
+ * @return float
92
+ */
93
+ static function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
94
+ //return self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
95
+
96
+ // @todo Make sure this cache is efficient before enabling it
97
+ static $cache = array();
98
+
99
+ if ( $text === "" ) {
100
+ return 0;
101
+ }
102
+
103
+ // Don't cache long strings
104
+ $use_cache = !isset($text[50]); // Faster than strlen
105
+
106
+ $key = "$font/$size/$word_spacing/$char_spacing";
107
+
108
+ if ( $use_cache && isset($cache[$key][$text]) ) {
109
+ return $cache[$key]["$text"];
110
+ }
111
+
112
+ $width = self::$_pdf->get_text_width($text, $font, $size, $word_spacing, $char_spacing);
113
+
114
+ if ( $use_cache ) {
115
+ $cache[$key][$text] = $width;
116
+ }
117
+
118
+ return $width;
119
+ }
120
+
121
+ /**
122
+ * Calculates font height
123
+ *
124
+ * @param string $font
125
+ * @param float $size
126
+ * @return float
127
+ */
128
+ static function get_font_height($font, $size) {
129
+ return self::$_pdf->get_font_height($font, $size);
130
+ }
131
+
132
+ /**
133
+ * Resolves a font family & subtype into an actual font file
134
+ * Subtype can be one of 'normal', 'bold', 'italic' or 'bold_italic'. If
135
+ * the particular font family has no suitable font file, the default font
136
+ * ({@link DOMPDF_DEFAULT_FONT}) is used. The font file returned
137
+ * is the absolute pathname to the font file on the system.
138
+ *
139
+ * @param string $family_raw
140
+ * @param string $subtype_raw
141
+ *
142
+ * @return string
143
+ */
144
+ static function get_font($family_raw, $subtype_raw = "normal") {
145
+ static $cache = array();
146
+
147
+ if ( isset($cache[$family_raw][$subtype_raw]) ) {
148
+ return $cache[$family_raw][$subtype_raw];
149
+ }
150
+
151
+ /* Allow calling for various fonts in search path. Therefore not immediately
152
+ * return replacement on non match.
153
+ * Only when called with NULL try replacement.
154
+ * When this is also missing there is really trouble.
155
+ * If only the subtype fails, nevertheless return failure.
156
+ * Only on checking the fallback font, check various subtypes on same font.
157
+ */
158
+
159
+ $subtype = strtolower($subtype_raw);
160
+
161
+ if ( $family_raw ) {
162
+ $family = str_replace( array("'", '"'), "", strtolower($family_raw));
163
+
164
+ if ( isset(self::$_font_lookup[$family][$subtype]) ) {
165
+ return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype];
166
+ }
167
+
168
+ return null;
169
+ }
170
+
171
+ $family = "serif";
172
+
173
+ if ( isset(self::$_font_lookup[$family][$subtype]) ) {
174
+ return $cache[$family_raw][$subtype_raw] = self::$_font_lookup[$family][$subtype];
175
+ }
176
+
177
+ if ( !isset(self::$_font_lookup[$family]) ) {
178
+ return null;
179
+ }
180
+
181
+ $family = self::$_font_lookup[$family];
182
+
183
+ foreach ( $family as $sub => $font ) {
184
+ if (strpos($subtype, $sub) !== false) {
185
+ return $cache[$family_raw][$subtype_raw] = $font;
186
+ }
187
+ }
188
+
189
+ if ($subtype !== "normal") {
190
+ foreach ( $family as $sub => $font ) {
191
+ if ($sub !== "normal") {
192
+ return $cache[$family_raw][$subtype_raw] = $font;
193
+ }
194
+ }
195
+ }
196
+
197
+ $subtype = "normal";
198
+
199
+ if ( isset($family[$subtype]) ) {
200
+ return $cache[$family_raw][$subtype_raw] = $family[$subtype];
201
+ }
202
+
203
+ return null;
204
+ }
205
+
206
+ static function get_family($family) {
207
+ $family = str_replace( array("'", '"'), "", mb_strtolower($family));
208
+
209
+ if ( isset(self::$_font_lookup[$family]) ) {
210
+ return self::$_font_lookup[$family];
211
+ }
212
+
213
+ return null;
214
+ }
215
+
216
+ /**
217
+ * Saves the stored font family cache
218
+ *
219
+ * The name and location of the cache file are determined by {@link
220
+ * Font_Metrics::CACHE_FILE}. This file should be writable by the
221
+ * webserver process.
222
+ *
223
+ * @see Font_Metrics::load_font_families()
224
+ */
225
+ static function save_font_families() {
226
+ // replace the path to the DOMPDF font directory with "DOMPDF_FONT_DIR" (allows for more portability)
227
+ $cache_data = var_export(self::$_font_lookup, true);
228
+ $cache_data = str_replace('\''.DOMPDF_FONT_DIR , 'DOMPDF_FONT_DIR . \'' , $cache_data);
229
+ $cache_data = "<"."?php return $cache_data ?".">";
230
+ file_put_contents(self::CACHE_FILE, $cache_data);
231
+ }
232
+
233
+ /**
234
+ * Loads the stored font family cache
235
+ *
236
+ * @see save_font_families()
237
+ */
238
+ static function load_font_families() {
239
+ if ( !is_readable(self::CACHE_FILE) ) {
240
+ return;
241
+ }
242
+
243
+ self::$_font_lookup = require_once self::CACHE_FILE;
244
+
245
+ // If the font family cache is still in the old format
246
+ if ( self::$_font_lookup === 1 ) {
247
+ $cache_data = file_get_contents(self::CACHE_FILE);
248
+ file_put_contents(self::CACHE_FILE, "<"."?php return $cache_data ?".">");
249
+ self::$_font_lookup = require_once self::CACHE_FILE;
250
+ }
251
+ }
252
+
253
+ static function get_type($type) {
254
+ if (preg_match("/bold/i", $type)) {
255
+ if (preg_match("/italic|oblique/i", $type)) {
256
+ $type = "bold_italic";
257
+ }
258
+ else {
259
+ $type = "bold";
260
+ }
261
+ }
262
+ elseif (preg_match("/italic|oblique/i", $type)) {
263
+ $type = "italic";
264
+ }
265
+ else {
266
+ $type = "normal";
267
+ }
268
+
269
+ return $type;
270
+ }
271
+
272
+ static function install_fonts($files) {
273
+ $names = array();
274
+
275
+ foreach($files as $file) {
276
+ $font = Font::load($file);
277
+ $records = $font->getData("name", "records");
278
+ $type = self::get_type($records[2]);
279
+ $names[mb_strtolower($records[1])][$type] = $file;
280
+ }
281
+
282
+ return $names;
283
+ }
284
+
285
+ static function get_system_fonts() {
286
+ $files = glob("/usr/share/fonts/truetype/*.ttf") +
287
+ glob("/usr/share/fonts/truetype/*/*.ttf") +
288
+ glob("/usr/share/fonts/truetype/*/*/*.ttf") +
289
+ glob("C:\\Windows\\fonts\\*.ttf") +
290
+ glob("C:\\WinNT\\fonts\\*.ttf") +
291
+ glob("/mnt/c_drive/WINDOWS/Fonts/");
292
+
293
+ return self::install_fonts($files);
294
+ }
295
+
296
+ /**
297
+ * Returns the current font lookup table
298
+ *
299
+ * @return array
300
+ */
301
+ static function get_font_families() {
302
+ return self::$_font_lookup;
303
+ }
304
+
305
+ static function set_font_family($fontname, $entry) {
306
+ self::$_font_lookup[mb_strtolower($fontname)] = $entry;
307
+ }
308
+
309
+ static function register_font($style, $remote_file) {
310
+ $fontname = mb_strtolower($style["family"]);
311
+ $families = Font_Metrics::get_font_families();
312
+
313
+ $entry = array();
314
+ if ( isset($families[$fontname]) ) {
315
+ $entry = $families[$fontname];
316
+ }
317
+
318
+ $local_file = DOMPDF_FONT_DIR . md5($remote_file);
319
+ $cache_entry = $local_file;
320
+ $local_file .= ".ttf";
321
+
322
+ $style_string = Font_Metrics::get_type("{$style['weight']} {$style['style']}");
323
+
324
+ if ( !isset($entry[$style_string]) ) {
325
+ $entry[$style_string] = $cache_entry;
326
+
327
+ Font_Metrics::set_font_family($fontname, $entry);
328
+
329
+ // Download the remote file
330
+ if ( !is_file($local_file) ) {
331
+ file_put_contents($local_file, file_get_contents($remote_file));
332
+ }
333
+
334
+ $font = Font::load($local_file);
335
+
336
+ if (!$font) {
337
+ return false;
338
+ }
339
+
340
+ $font->parse();
341
+ $font->saveAdobeFontMetrics("$cache_entry.ufm");
342
+
343
+ // Save the changes
344
+ Font_Metrics::save_font_families();
345
+ }
346
+
347
+ return true;
348
+ }
349
+ }
350
+
351
+ Font_Metrics::load_font_families();
dompdf/include/frame.cls.php ADDED
@@ -0,0 +1,1184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * The main Frame class
11
+ *
12
+ * This class represents a single HTML element. This class stores
13
+ * positioning information as well as containing block location and
14
+ * dimensions. Style information for the element is stored in a {@link
15
+ * Style} object. Tree structure is maintained via the parent & children
16
+ * links.
17
+ *
18
+ * @access protected
19
+ * @package dompdf
20
+ */
21
+ class Frame {
22
+
23
+ /**
24
+ * The DOMElement or DOMText object this frame represents
25
+ *
26
+ * @var DOMElement|DOMText
27
+ */
28
+ protected $_node;
29
+
30
+ /**
31
+ * Unique identifier for this frame. Used to reference this frame
32
+ * via the node.
33
+ *
34
+ * @var string
35
+ */
36
+ protected $_id;
37
+
38
+ /**
39
+ * Unique id counter
40
+ */
41
+ static /*protected*/ $ID_COUNTER = 0;
42
+
43
+ /**
44
+ * This frame's calculated style
45
+ *
46
+ * @var Style
47
+ */
48
+ protected $_style;
49
+
50
+ /**
51
+ * This frame's original style. Needed for cases where frames are
52
+ * split across pages.
53
+ *
54
+ * @var Style
55
+ */
56
+ protected $_original_style;
57
+
58
+ /**
59
+ * This frame's parent in the document tree.
60
+ *
61
+ * @var Frame
62
+ */
63
+ protected $_parent;
64
+
65
+ /**
66
+ * This frame's children
67
+ *
68
+ * @var Frame[]
69
+ */
70
+ protected $_frame_list;
71
+
72
+ /**
73
+ * This frame's first child. All children are handled as a
74
+ * doubly-linked list.
75
+ *
76
+ * @var Frame
77
+ */
78
+ protected $_first_child;
79
+
80
+ /**
81
+ * This frame's last child.
82
+ *
83
+ * @var Frame
84
+ */
85
+ protected $_last_child;
86
+
87
+ /**
88
+ * This frame's previous sibling in the document tree.
89
+ *
90
+ * @var Frame
91
+ */
92
+ protected $_prev_sibling;
93
+
94
+ /**
95
+ * This frame's next sibling in the document tree.
96
+ *
97
+ * @var Frame
98
+ */
99
+ protected $_next_sibling;
100
+
101
+ /**
102
+ * This frame's containing block (used in layout): array(x, y, w, h)
103
+ *
104
+ * @var float[]
105
+ */
106
+ protected $_containing_block;
107
+
108
+ /**
109
+ * Position on the page of the top-left corner of the margin box of
110
+ * this frame: array(x,y)
111
+ *
112
+ * @var float[]
113
+ */
114
+ protected $_position;
115
+
116
+ /**
117
+ * Absolute opacity of this frame
118
+ *
119
+ * @var float
120
+ */
121
+ protected $_opacity;
122
+
123
+ /**
124
+ * This frame's decorator
125
+ *
126
+ * @var Frame_Decorator
127
+ */
128
+ protected $_decorator;
129
+
130
+ /**
131
+ * This frame's containing line box
132
+ *
133
+ * @var Line_Box
134
+ */
135
+ protected $_containing_line;
136
+
137
+ protected $_is_cache = array();
138
+
139
+ /**
140
+ * Tells wether the frame was already pushed to the next page
141
+ *
142
+ * @var bool
143
+ */
144
+ public $_already_pushed = false;
145
+
146
+ public $_float_next_line = false;
147
+
148
+ static $_ws_state = self::WS_SPACE;
149
+
150
+ const WS_TEXT = 1;
151
+ const WS_SPACE = 2;
152
+
153
+ /**
154
+ * Class destructor
155
+ */
156
+ function __destruct() {
157
+ clear_object($this);
158
+ }
159
+
160
+ /**
161
+ * Class constructor
162
+ *
163
+ * @param DOMNode $node the DOMNode this frame represents
164
+ */
165
+ function __construct(DOMNode $node) {
166
+ $this->_node = $node;
167
+
168
+ $this->_parent = null;
169
+ $this->_first_child = null;
170
+ $this->_last_child = null;
171
+ $this->_prev_sibling = $this->_next_sibling = null;
172
+
173
+ $this->_style = null;
174
+ $this->_original_style = null;
175
+
176
+ $this->_containing_block = array(
177
+ "x" => null,
178
+ "y" => null,
179
+ "w" => null,
180
+ "h" => null,
181
+ );
182
+
183
+ $this->_containing_block[0] =& $this->_containing_block["x"];
184
+ $this->_containing_block[1] =& $this->_containing_block["y"];
185
+ $this->_containing_block[2] =& $this->_containing_block["w"];
186
+ $this->_containing_block[3] =& $this->_containing_block["h"];
187
+
188
+ $this->_position = array(
189
+ "x" => null,
190
+ "y" => null,
191
+ );
192
+
193
+ $this->_position[0] =& $this->_position["x"];
194
+ $this->_position[1] =& $this->_position["y"];
195
+
196
+ $this->_opacity = 1.0;
197
+ $this->_decorator = null;
198
+
199
+ $this->set_id( self::$ID_COUNTER++ );
200
+ }
201
+
202
+ // WIP : preprocessing to remove all the unused whitespace
203
+ protected function ws_trim(){
204
+ if ( $this->ws_keep() ) {
205
+ return;
206
+ }
207
+
208
+ switch(self::$_ws_state) {
209
+ case self::WS_SPACE:
210
+ $node = $this->_node;
211
+
212
+ if ( $node->nodeName === "#text" ) {
213
+ $node->nodeValue = preg_replace("/[ \t\r\n\f]+/u", " ", $node->nodeValue);
214
+
215
+ // starts with a whitespace
216
+ if ( isset($node->nodeValue[0]) && $node->nodeValue[0] === " " ) {
217
+ $node->nodeValue = ltrim($node->nodeValue);
218
+ }
219
+
220
+ // if not empty
221
+ if ( $node->nodeValue !== "" ) {
222
+ // change the current state (text)
223
+ self::$_ws_state = self::WS_TEXT;
224
+
225
+ // ends with a whitespace
226
+ if ( preg_match("/[ \t\r\n\f]+$/u", $node->nodeValue) ) {
227
+ $node->nodeValue = ltrim($node->nodeValue);
228
+ }
229
+ }
230
+ }
231
+ break;
232
+
233
+ case self::WS_TEXT:
234
+ }
235
+ }
236
+
237
+ protected function ws_keep(){
238
+ $whitespace = $this->get_style()->white_space;
239
+ return in_array($whitespace, array("pre", "pre-wrap", "pre-line"));
240
+ }
241
+
242
+ protected function ws_is_text(){
243
+ $node = $this->get_node();
244
+
245
+ if ($node->nodeName === "img") {
246
+ return true;
247
+ }
248
+
249
+ if ( !$this->is_in_flow() ) {
250
+ return false;
251
+ }
252
+
253
+ if ($this->is_text_node()) {
254
+ return trim($node->nodeValue) !== "";
255
+ }
256
+
257
+ return true;
258
+ }
259
+
260
+ /**
261
+ * "Destructor": forcibly free all references held by this frame
262
+ *
263
+ * @param bool $recursive if true, call dispose on all children
264
+ */
265
+ function dispose($recursive = false) {
266
+
267
+ if ( $recursive ) {
268
+ while ( $child = $this->_first_child ) {
269
+ $child->dispose(true);
270
+ }
271
+ }
272
+
273
+ // Remove this frame from the tree
274
+ if ( $this->_prev_sibling ) {
275
+ $this->_prev_sibling->_next_sibling = $this->_next_sibling;
276
+ }
277
+
278
+ if ( $this->_next_sibling ) {
279
+ $this->_next_sibling->_prev_sibling = $this->_prev_sibling;
280
+ }
281
+
282
+ if ( $this->_parent && $this->_parent->_first_child === $this ) {
283
+ $this->_parent->_first_child = $this->_next_sibling;
284
+ }
285
+
286
+ if ( $this->_parent && $this->_parent->_last_child === $this ) {
287
+ $this->_parent->_last_child = $this->_prev_sibling;
288
+ }
289
+
290
+ if ( $this->_parent ) {
291
+ $this->_parent->get_node()->removeChild($this->_node);
292
+ }
293
+
294
+ $this->_style->dispose();
295
+ $this->_style = null;
296
+ unset($this->_style);
297
+
298
+ $this->_original_style->dispose();
299
+ $this->_original_style = null;
300
+ unset($this->_original_style);
301
+
302
+ }
303
+
304
+ // Re-initialize the frame
305
+ function reset() {
306
+ $this->_position["x"] = null;
307
+ $this->_position["y"] = null;
308
+
309
+ $this->_containing_block["x"] = null;
310
+ $this->_containing_block["y"] = null;
311
+ $this->_containing_block["w"] = null;
312
+ $this->_containing_block["h"] = null;
313
+
314
+ $this->_style = null;
315
+ unset($this->_style);
316
+ $this->_style = clone $this->_original_style;
317
+ }
318
+
319
+ //........................................................................
320
+
321
+ /**
322
+ * @return DOMElement|DOMText
323
+ */
324
+ function get_node() {
325
+ return $this->_node;
326
+ }
327
+
328
+ /**
329
+ * @return string
330
+ */
331
+ function get_id() {
332
+ return $this->_id;
333
+ }
334
+
335
+ /**
336
+ * @return Style
337
+ */
338
+ function get_style() {
339
+ return $this->_style;
340
+ }
341
+
342
+ /**
343
+ * @return Style
344
+ */
345
+ function get_original_style() {
346
+ return $this->_original_style;
347
+ }
348
+
349
+ /**
350
+ * @return Frame
351
+ */
352
+ function get_parent() {
353
+ return $this->_parent;
354
+ }
355
+
356
+ /**
357
+ * @return Frame_Decorator
358
+ */
359
+ function get_decorator() {
360
+ return $this->_decorator;
361
+ }
362
+
363
+ /**
364
+ * @return Frame
365
+ */
366
+ function get_first_child() {
367
+ return $this->_first_child;
368
+ }
369
+
370
+ /**
371
+ * @return Frame
372
+ */
373
+ function get_last_child() {
374
+ return $this->_last_child;
375
+ }
376
+
377
+ /**
378
+ * @return Frame
379
+ */
380
+ function get_prev_sibling() {
381
+ return $this->_prev_sibling;
382
+ }
383
+
384
+ /**
385
+ * @return Frame
386
+ */
387
+ function get_next_sibling() {
388
+ return $this->_next_sibling;
389
+ }
390
+
391
+ /**
392
+ * @return FrameList|Frame[]
393
+ */
394
+ function get_children() {
395
+ if ( isset($this->_frame_list) ) {
396
+ return $this->_frame_list;
397
+ }
398
+
399
+ $this->_frame_list = new FrameList($this);
400
+ return $this->_frame_list;
401
+ }
402
+
403
+ // Layout property accessors
404
+
405
+ /**
406
+ * Containing block dimensions
407
+ *
408
+ * @param $i string The key of the wanted containing block's dimension (x, y, x, h)
409
+ *
410
+ * @return float[]|float
411
+ */
412
+ function get_containing_block($i = null) {
413
+ if ( isset($i) ) {
414
+ return $this->_containing_block[$i];
415
+ }
416
+ return $this->_containing_block;
417
+ }
418
+
419
+ /**
420
+ * Block position
421
+ *
422
+ * @param $i string The key of the wanted position value (x, y)
423
+ *
424
+ * @return array|float
425
+ */
426
+ function get_position($i = null) {
427
+ if ( isset($i) ) {
428
+ return $this->_position[$i];
429
+ }
430
+ return $this->_position;
431
+ }
432
+
433
+ //........................................................................
434
+
435
+ /**
436
+ * Return the height of the margin box of the frame, in pt. Meaningless
437
+ * unless the height has been calculated properly.
438
+ *
439
+ * @return float
440
+ */
441
+ function get_margin_height() {
442
+ $style = $this->_style;
443
+
444
+ return $style->length_in_pt(array(
445
+ $style->height,
446
+ $style->margin_top,
447
+ $style->margin_bottom,
448
+ $style->border_top_width,
449
+ $style->border_bottom_width,
450
+ $style->padding_top,
451
+ $style->padding_bottom
452
+ ), $this->_containing_block["h"]);
453
+ }
454
+
455
+ /**
456
+ * Return the width of the margin box of the frame, in pt. Meaningless
457
+ * unless the width has been calculated properly.
458
+ *
459
+ * @return float
460
+ */
461
+ function get_margin_width() {
462
+ $style = $this->_style;
463
+
464
+ return $style->length_in_pt(array(
465
+ $style->width,
466
+ $style->margin_left,
467
+ $style->margin_right,
468
+ $style->border_left_width,
469
+ $style->border_right_width,
470
+ $style->padding_left,
471
+ $style->padding_right
472
+ ), $this->_containing_block["w"]);
473
+ }
474
+
475
+ function get_break_margins(){
476
+ $style = $this->_style;
477
+
478
+ return $style->length_in_pt(array(
479
+ //$style->height,
480
+ $style->margin_top,
481
+ $style->margin_bottom,
482
+ $style->border_top_width,
483
+ $style->border_bottom_width,
484
+ $style->padding_top,
485
+ $style->padding_bottom
486
+ ), $this->_containing_block["h"]);
487
+ }
488
+
489
+ /**
490
+ * Return the padding box (x,y,w,h) of the frame
491
+ *
492
+ * @return array
493
+ */
494
+ function get_padding_box() {
495
+ $style = $this->_style;
496
+ $cb = $this->_containing_block;
497
+
498
+ $x = $this->_position["x"] +
499
+ $style->length_in_pt(array($style->margin_left,
500
+ $style->border_left_width),
501
+ $cb["w"]);
502
+
503
+ $y = $this->_position["y"] +
504
+ $style->length_in_pt(array($style->margin_top,
505
+ $style->border_top_width),
506
+ $cb["h"]);
507
+
508
+ $w = $style->length_in_pt(array($style->padding_left,
509
+ $style->width,
510
+ $style->padding_right),
511
+ $cb["w"]);
512
+
513
+ $h = $style->length_in_pt(array($style->padding_top,
514
+ $style->height,
515
+ $style->padding_bottom),
516
+ $cb["h"]);
517
+
518
+ return array(0 => $x, "x" => $x,
519
+ 1 => $y, "y" => $y,
520
+ 2 => $w, "w" => $w,
521
+ 3 => $h, "h" => $h);
522
+ }
523
+
524
+ /**
525
+ * Return the border box of the frame
526
+ *
527
+ * @return array
528
+ */
529
+ function get_border_box() {
530
+ $style = $this->_style;
531
+ $cb = $this->_containing_block;
532
+
533
+ $x = $this->_position["x"] + $style->length_in_pt($style->margin_left, $cb["w"]);
534
+
535
+ $y = $this->_position["y"] + $style->length_in_pt($style->margin_top, $cb["h"]);
536
+
537
+ $w = $style->length_in_pt(array($style->border_left_width,
538
+ $style->padding_left,
539
+ $style->width,
540
+ $style->padding_right,
541
+ $style->border_right_width),
542
+ $cb["w"]);
543
+
544
+ $h = $style->length_in_pt(array($style->border_top_width,
545
+ $style->padding_top,
546
+ $style->height,
547
+ $style->padding_bottom,
548
+ $style->border_bottom_width),
549
+ $cb["h"]);
550
+
551
+ return array(0 => $x, "x" => $x,
552
+ 1 => $y, "y" => $y,
553
+ 2 => $w, "w" => $w,
554
+ 3 => $h, "h" => $h);
555
+ }
556
+
557
+ function get_opacity($opacity = null) {
558
+ if ( $opacity !== null ) {
559
+ $this->set_opacity($opacity);
560
+ }
561
+ return $this->_opacity;
562
+ }
563
+
564
+ /**
565
+ * @return Line_Box
566
+ */
567
+ function &get_containing_line() {
568
+ return $this->_containing_line;
569
+ }
570
+
571
+ //........................................................................
572
+
573
+ // Set methods
574
+ function set_id($id) {
575
+ $this->_id = $id;
576
+
577
+ // We can only set attributes of DOMElement objects (nodeType == 1).
578
+ // Since these are the only objects that we can assign CSS rules to,
579
+ // this shortcoming is okay.
580
+ if ( $this->_node->nodeType == XML_ELEMENT_NODE ) {
581
+ $this->_node->setAttribute("frame_id", $id);
582
+ }
583
+ }
584
+
585
+ function set_style(Style $style) {
586
+ if ( is_null($this->_style) ) {
587
+ $this->_original_style = clone $style;
588
+ }
589
+
590
+ //$style->set_frame($this);
591
+ $this->_style = $style;
592
+ }
593
+
594
+ function set_decorator(Frame_Decorator $decorator) {
595
+ $this->_decorator = $decorator;
596
+ }
597
+
598
+ function set_containing_block($x = null, $y = null, $w = null, $h = null) {
599
+ if ( is_array($x) ){
600
+ foreach($x as $key => $val){
601
+ $$key = $val;
602
+ }
603
+ }
604
+
605
+ if (is_numeric($x)) {
606
+ $this->_containing_block["x"] = $x;
607
+ }
608
+
609
+ if (is_numeric($y)) {
610
+ $this->_containing_block["y"] = $y;
611
+ }
612
+
613
+ if (is_numeric($w)) {
614
+ $this->_containing_block["w"] = $w;
615
+ }
616
+
617
+ if (is_numeric($h)) {
618
+ $this->_containing_block["h"] = $h;
619
+ }
620
+ }
621
+
622
+ function set_position($x = null, $y = null) {
623
+ if ( is_array($x) ) {
624
+ extract($x);
625
+ }
626
+
627
+ if ( is_numeric($x) ) {
628
+ $this->_position["x"] = $x;
629
+ }
630
+
631
+ if ( is_numeric($y) ) {
632
+ $this->_position["y"] = $y;
633
+ }
634
+ }
635
+
636
+ function set_opacity($opacity) {
637
+ $parent = $this->get_parent();
638
+ $base_opacity = (($parent && $parent->_opacity !== null) ? $parent->_opacity : 1.0);
639
+ $this->_opacity = $base_opacity * $opacity;
640
+ }
641
+
642
+ function set_containing_line(Line_Box $line) {
643
+ $this->_containing_line = $line;
644
+ }
645
+
646
+ //........................................................................
647
+
648
+ /**
649
+ * Tells if the frame is a text node
650
+ * @return bool
651
+ */
652
+ function is_text_node() {
653
+ if ( isset($this->_is_cache["text_node"]) ) {
654
+ return $this->_is_cache["text_node"];
655
+ }
656
+
657
+ return $this->_is_cache["text_node"] = ($this->get_node()->nodeName === "#text");
658
+ }
659
+
660
+ function is_positionned() {
661
+ if ( isset($this->_is_cache["positionned"]) ) {
662
+ return $this->_is_cache["positionned"];
663
+ }
664
+
665
+ $position = $this->get_style()->position;
666
+
667
+ return $this->_is_cache["positionned"] = in_array($position, Style::$POSITIONNED_TYPES);
668
+ }
669
+
670
+ function is_absolute() {
671
+ if ( isset($this->_is_cache["absolute"]) ) {
672
+ return $this->_is_cache["absolute"];
673
+ }
674
+
675
+ $position = $this->get_style()->position;
676
+
677
+ return $this->_is_cache["absolute"] = ($position === "absolute" || $position === "fixed");
678
+ }
679
+
680
+ function is_block() {
681
+ if ( isset($this->_is_cache["block"]) ) {
682
+ return $this->_is_cache["block"];
683
+ }
684
+
685
+ return $this->_is_cache["block"] = in_array($this->get_style()->display, Style::$BLOCK_TYPES);
686
+ }
687
+
688
+ function is_in_flow() {
689
+ if ( isset($this->_is_cache["in_flow"]) ) {
690
+ return $this->_is_cache["in_flow"];
691
+ }
692
+
693
+ $enable_css_float = $this->get_style()->get_stylesheet()->get_dompdf()->get_option("enable_css_float");
694
+ return $this->_is_cache["in_flow"] = !($enable_css_float && $this->get_style()->float !== "none" || $this->is_absolute());
695
+ }
696
+
697
+ function is_pre(){
698
+ if ( isset($this->_is_cache["pre"]) ) {
699
+ return $this->_is_cache["pre"];
700
+ }
701
+
702
+ $white_space = $this->get_style()->white_space;
703
+
704
+ return $this->_is_cache["pre"] = in_array($white_space, array("pre", "pre-wrap"));
705
+ }
706
+
707
+ function is_table(){
708
+ if ( isset($this->_is_cache["table"]) ) {
709
+ return $this->_is_cache["table"];
710
+ }
711
+
712
+ $display = $this->get_style()->display;
713
+
714
+ return $this->_is_cache["table"] = in_array($display, Style::$TABLE_TYPES);
715
+ }
716
+
717
+
718
+ /**
719
+ * Inserts a new child at the beginning of the Frame
720
+ *
721
+ * @param $child Frame The new Frame to insert
722
+ * @param $update_node boolean Whether or not to update the DOM
723
+ */
724
+ function prepend_child(Frame $child, $update_node = true) {
725
+ if ( $update_node ) {
726
+ $this->_node->insertBefore($child->_node, $this->_first_child ? $this->_first_child->_node : null);
727
+ }
728
+
729
+ // Remove the child from its parent
730
+ if ( $child->_parent ) {
731
+ $child->_parent->remove_child($child, false);
732
+ }
733
+
734
+ $child->_parent = $this;
735
+ $child->_prev_sibling = null;
736
+
737
+ // Handle the first child
738
+ if ( !$this->_first_child ) {
739
+ $this->_first_child = $child;
740
+ $this->_last_child = $child;
741
+ $child->_next_sibling = null;
742
+ }
743
+ else {
744
+ $this->_first_child->_prev_sibling = $child;
745
+ $child->_next_sibling = $this->_first_child;
746
+ $this->_first_child = $child;
747
+ }
748
+ }
749
+
750
+ /**
751
+ * Inserts a new child at the end of the Frame
752
+ *
753
+ * @param $child Frame The new Frame to insert
754
+ * @param $update_node boolean Whether or not to update the DOM
755
+ */
756
+ function append_child(Frame $child, $update_node = true) {
757
+ if ( $update_node ) {
758
+ $this->_node->appendChild($child->_node);
759
+ }
760
+
761
+ // Remove the child from its parent
762
+ if ( $child->_parent ) {
763
+ $child->_parent->remove_child($child, false);
764
+ }
765
+
766
+ $child->_parent = $this;
767
+ $child->_next_sibling = null;
768
+
769
+ // Handle the first child
770
+ if ( !$this->_last_child ) {
771
+ $this->_first_child = $child;
772
+ $this->_last_child = $child;
773
+ $child->_prev_sibling = null;
774
+ }
775
+ else {
776
+ $this->_last_child->_next_sibling = $child;
777
+ $child->_prev_sibling = $this->_last_child;
778
+ $this->_last_child = $child;
779
+ }
780
+ }
781
+
782
+ /**
783
+ * Inserts a new child immediately before the specified frame
784
+ *
785
+ * @param $new_child Frame The new Frame to insert
786
+ * @param $ref Frame The Frame after the new Frame
787
+ * @param $update_node boolean Whether or not to update the DOM
788
+ *
789
+ * @throws DOMPDF_Exception
790
+ */
791
+ function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) {
792
+ if ( $ref === $this->_first_child ) {
793
+ $this->prepend_child($new_child, $update_node);
794
+ return;
795
+ }
796
+
797
+ if ( is_null($ref) ) {
798
+ $this->append_child($new_child, $update_node);
799
+ return;
800
+ }
801
+
802
+ if ( $ref->_parent !== $this ) {
803
+ throw new DOMPDF_Exception("Reference child is not a child of this node.");
804
+ }
805
+
806
+ // Update the node
807
+ if ( $update_node ) {
808
+ $this->_node->insertBefore($new_child->_node, $ref->_node);
809
+ }
810
+
811
+ // Remove the child from its parent
812
+ if ( $new_child->_parent ) {
813
+ $new_child->_parent->remove_child($new_child, false);
814
+ }
815
+
816
+ $new_child->_parent = $this;
817
+ $new_child->_next_sibling = $ref;
818
+ $new_child->_prev_sibling = $ref->_prev_sibling;
819
+
820
+ if ( $ref->_prev_sibling ) {
821
+ $ref->_prev_sibling->_next_sibling = $new_child;
822
+ }
823
+
824
+ $ref->_prev_sibling = $new_child;
825
+ }
826
+
827
+ /**
828
+ * Inserts a new child immediately after the specified frame
829
+ *
830
+ * @param $new_child Frame The new Frame to insert
831
+ * @param $ref Frame The Frame before the new Frame
832
+ * @param $update_node boolean Whether or not to update the DOM
833
+ *
834
+ * @throws DOMPDF_Exception
835
+ */
836
+ function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) {
837
+ if ( $ref === $this->_last_child ) {
838
+ $this->append_child($new_child, $update_node);
839
+ return;
840
+ }
841
+
842
+ if ( is_null($ref) ) {
843
+ $this->prepend_child($new_child, $update_node);
844
+ return;
845
+ }
846
+
847
+ if ( $ref->_parent !== $this ) {
848
+ throw new DOMPDF_Exception("Reference child is not a child of this node.");
849
+ }
850
+
851
+ // Update the node
852
+ if ( $update_node ) {
853
+ if ( $ref->_next_sibling ) {
854
+ $next_node = $ref->_next_sibling->_node;
855
+ $this->_node->insertBefore($new_child->_node, $next_node);
856
+ }
857
+ else {
858
+ $new_child->_node = $this->_node->appendChild($new_child->_node);
859
+ }
860
+ }
861
+
862
+ // Remove the child from its parent
863
+ if ( $new_child->_parent ) {
864
+ $new_child->_parent->remove_child($new_child, false);
865
+ }
866
+
867
+ $new_child->_parent = $this;
868
+ $new_child->_prev_sibling = $ref;
869
+ $new_child->_next_sibling = $ref->_next_sibling;
870
+
871
+ if ( $ref->_next_sibling ) {
872
+ $ref->_next_sibling->_prev_sibling = $new_child;
873
+ }
874
+
875
+ $ref->_next_sibling = $new_child;
876
+ }
877
+
878
+
879
+ /**
880
+ * Remove a child frame
881
+ *
882
+ * @param Frame $child
883
+ * @param boolean $update_node Whether or not to remove the DOM node
884
+ *
885
+ * @throws DOMPDF_Exception
886
+ * @return Frame The removed child frame
887
+ */
888
+ function remove_child(Frame $child, $update_node = true) {
889
+ if ( $child->_parent !== $this ) {
890
+ throw new DOMPDF_Exception("Child not found in this frame");
891
+ }
892
+
893
+ if ( $update_node ) {
894
+ $this->_node->removeChild($child->_node);
895
+ }
896
+
897
+ if ( $child === $this->_first_child ) {
898
+ $this->_first_child = $child->_next_sibling;
899
+ }
900
+
901
+ if ( $child === $this->_last_child ) {
902
+ $this->_last_child = $child->_prev_sibling;
903
+ }
904
+
905
+ if ( $child->_prev_sibling ) {
906
+ $child->_prev_sibling->_next_sibling = $child->_next_sibling;
907
+ }
908
+
909
+ if ( $child->_next_sibling ) {
910
+ $child->_next_sibling->_prev_sibling = $child->_prev_sibling;
911
+ }
912
+
913
+ $child->_next_sibling = null;
914
+ $child->_prev_sibling = null;
915
+ $child->_parent = null;
916
+ return $child;
917
+ }
918
+
919
+ //........................................................................
920
+
921
+ // Debugging function:
922
+ function __toString() {
923
+ // Skip empty text frames
924
+ // if ( $this->is_text_node() &&
925
+ // preg_replace("/\s/", "", $this->_node->data) === "" )
926
+ // return "";
927
+
928
+
929
+ $str = "<b>" . $this->_node->nodeName . ":</b><br/>";
930
+ //$str .= spl_object_hash($this->_node) . "<br/>";
931
+ $str .= "Id: " .$this->get_id() . "<br/>";
932
+ $str .= "Class: " .get_class($this) . "<br/>";
933
+
934
+ if ( $this->is_text_node() ) {
935
+ $tmp = htmlspecialchars($this->_node->nodeValue);
936
+ $str .= "<pre>'" . mb_substr($tmp,0,70) .
937
+ (mb_strlen($tmp) > 70 ? "..." : "") . "'</pre>";
938
+ }
939
+ elseif ( $css_class = $this->_node->getAttribute("class") ) {
940
+ $str .= "CSS class: '$css_class'<br/>";
941
+ }
942
+
943
+ if ( $this->_parent ) {
944
+ $str .= "\nParent:" . $this->_parent->_node->nodeName .
945
+ " (" . spl_object_hash($this->_parent->_node) . ") " .
946
+ "<br/>";
947
+ }
948
+
949
+ if ( $this->_prev_sibling ) {
950
+ $str .= "Prev: " . $this->_prev_sibling->_node->nodeName .
951
+ " (" . spl_object_hash($this->_prev_sibling->_node) . ") " .
952
+ "<br/>";
953
+ }
954
+
955
+ if ( $this->_next_sibling ) {
956
+ $str .= "Next: " . $this->_next_sibling->_node->nodeName .
957
+ " (" . spl_object_hash($this->_next_sibling->_node) . ") " .
958
+ "<br/>";
959
+ }
960
+
961
+ $d = $this->get_decorator();
962
+ while ($d && $d != $d->get_decorator()) {
963
+ $str .= "Decorator: " . get_class($d) . "<br/>";
964
+ $d = $d->get_decorator();
965
+ }
966
+
967
+ $str .= "Position: " . pre_r($this->_position, true);
968
+ $str .= "\nContaining block: " . pre_r($this->_containing_block, true);
969
+ $str .= "\nMargin width: " . pre_r($this->get_margin_width(), true);
970
+ $str .= "\nMargin height: " . pre_r($this->get_margin_height(), true);
971
+
972
+ $str .= "\nStyle: <pre>". $this->_style->__toString() . "</pre>";
973
+
974
+ if ( $this->_decorator instanceof Block_Frame_Decorator ) {
975
+ $str .= "Lines:<pre>";
976
+ foreach ($this->_decorator->get_line_boxes() as $line) {
977
+ foreach ($line->get_frames() as $frame) {
978
+ if ($frame instanceof Text_Frame_Decorator) {
979
+ $str .= "\ntext: ";
980
+ $str .= "'". htmlspecialchars($frame->get_text()) ."'";
981
+ }
982
+ else {
983
+ $str .= "\nBlock: " . $frame->get_node()->nodeName . " (" . spl_object_hash($frame->get_node()) . ")";
984
+ }
985
+ }
986
+
987
+ $str .=
988
+ "\ny => " . $line->y . "\n" .
989
+ "w => " . $line->w . "\n" .
990
+ "h => " . $line->h . "\n" .
991
+ "left => " . $line->left . "\n" .
992
+ "right => " . $line->right . "\n";
993
+ }
994
+ $str .= "</pre>";
995
+ }
996
+
997
+ $str .= "\n";
998
+ if ( php_sapi_name() === "cli" ) {
999
+ $str = strip_tags(str_replace(array("<br/>","<b>","</b>"),
1000
+ array("\n","",""),
1001
+ $str));
1002
+ }
1003
+
1004
+ return $str;
1005
+ }
1006
+ }
1007
+
1008
+ //------------------------------------------------------------------------
1009
+
1010
+ /**
1011
+ * Linked-list IteratorAggregate
1012
+ *
1013
+ * @access private
1014
+ * @package dompdf
1015
+ */
1016
+ class FrameList implements IteratorAggregate {
1017
+ protected $_frame;
1018
+
1019
+ function __construct($frame) { $this->_frame = $frame; }
1020
+ function getIterator() { return new FrameListIterator($this->_frame); }
1021
+ }
1022
+
1023
+ /**
1024
+ * Linked-list Iterator
1025
+ *
1026
+ * Returns children in order and allows for list to change during iteration,
1027
+ * provided the changes occur to or after the current element
1028
+ *
1029
+ * @access private
1030
+ * @package dompdf
1031
+ */
1032
+ class FrameListIterator implements Iterator {
1033
+
1034
+ /**
1035
+ * @var Frame
1036
+ */
1037
+ protected $_parent;
1038
+
1039
+ /**
1040
+ * @var Frame
1041
+ */
1042
+ protected $_cur;
1043
+
1044
+ /**
1045
+ * @var int
1046
+ */
1047
+ protected $_num;
1048
+
1049
+ function __construct(Frame $frame) {
1050
+ $this->_parent = $frame;
1051
+ $this->_cur = $frame->get_first_child();
1052
+ $this->_num = 0;
1053
+ }
1054
+
1055
+ function rewind() {
1056
+ $this->_cur = $this->_parent->get_first_child();
1057
+ $this->_num = 0;
1058
+ }
1059
+
1060
+ /**
1061
+ * @return bool
1062
+ */
1063
+ function valid() {
1064
+ return isset($this->_cur);// && ($this->_cur->get_prev_sibling() === $this->_prev);
1065
+ }
1066
+
1067
+ function key() { return $this->_num; }
1068
+
1069
+ /**
1070
+ * @return Frame
1071
+ */
1072
+ function current() { return $this->_cur; }
1073
+
1074
+ /**
1075
+ * @return Frame
1076
+ */
1077
+ function next() {
1078
+
1079
+ $ret = $this->_cur;
1080
+ if ( !$ret ) {
1081
+ return null;
1082
+ }
1083
+
1084
+ $this->_cur = $this->_cur->get_next_sibling();
1085
+ $this->_num++;
1086
+ return $ret;
1087
+ }
1088
+ }
1089
+
1090
+ //------------------------------------------------------------------------
1091
+
1092
+ /**
1093
+ * Pre-order IteratorAggregate
1094
+ *
1095
+ * @access private
1096
+ * @package dompdf
1097
+ */
1098
+ class FrameTreeList implements IteratorAggregate {
1099
+ /**
1100
+ * @var Frame
1101
+ */
1102
+ protected $_root;
1103
+
1104
+ function __construct(Frame $root) { $this->_root = $root; }
1105
+
1106
+ /**
1107
+ * @return FrameTreeIterator
1108
+ */
1109
+ function getIterator() { return new FrameTreeIterator($this->_root); }
1110
+ }
1111
+
1112
+ /**
1113
+ * Pre-order Iterator
1114
+ *
1115
+ * Returns frames in preorder traversal order (parent then children)
1116
+ *
1117
+ * @access private
1118
+ * @package dompdf
1119
+ */
1120
+ class FrameTreeIterator implements Iterator {
1121
+ /**
1122
+ * @var Frame
1123
+ */
1124
+ protected $_root;
1125
+ protected $_stack = array();
1126
+
1127
+ /**
1128
+ * @var int
1129
+ */
1130
+ protected $_num;
1131
+
1132
+ function __construct(Frame $root) {
1133
+ $this->_stack[] = $this->_root = $root;
1134
+ $this->_num = 0;
1135
+ }
1136
+
1137
+ function rewind() {
1138
+ $this->_stack = array($this->_root);
1139
+ $this->_num = 0;
1140
+ }
1141
+
1142
+ /**
1143
+ * @return bool
1144
+ */
1145
+ function valid() {
1146
+ return count($this->_stack) > 0;
1147
+ }
1148
+
1149
+ /**
1150
+ * @return int
1151
+ */
1152
+ function key() {
1153
+ return $this->_num;
1154
+ }
1155
+
1156
+ /**
1157
+ * @return Frame
1158
+ */
1159
+ function current() {
1160
+ return end($this->_stack);
1161
+ }
1162
+
1163
+ /**
1164
+ * @return Frame
1165
+ */
1166
+ function next() {
1167
+ $b = end($this->_stack);
1168
+
1169
+ // Pop last element
1170
+ unset($this->_stack[ key($this->_stack) ]);
1171
+ $this->_num++;
1172
+
1173
+ // Push all children onto the stack in reverse order
1174
+ if ( $c = $b->get_last_child() ) {
1175
+ $this->_stack[] = $c;
1176
+ while ( $c = $c->get_prev_sibling() ) {
1177
+ $this->_stack[] = $c;
1178
+ }
1179
+ }
1180
+
1181
+ return $b;
1182
+ }
1183
+ }
1184
+
dompdf/include/frame_decorator.cls.php ADDED
@@ -0,0 +1,696 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Base Frame_Decorator class
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ abstract class Frame_Decorator extends Frame {
16
+ const DEFAULT_COUNTER = "-dompdf-default-counter";
17
+
18
+ public $_counters = array(); // array([id] => counter_value) (for generated content)
19
+
20
+ /**
21
+ * The root node of the DOM tree
22
+ *
23
+ * @var Frame
24
+ */
25
+ protected $_root;
26
+
27
+ /**
28
+ * The decorated frame
29
+ *
30
+ * @var Frame
31
+ */
32
+ protected $_frame;
33
+
34
+ /**
35
+ * Positioner object used to position this frame (Strategy pattern)
36
+ *
37
+ * @var Positioner
38
+ */
39
+ protected $_positioner;
40
+
41
+ /**
42
+ * Reflower object used to calculate frame dimensions (Strategy pattern)
43
+ *
44
+ * @var Frame_Reflower
45
+ */
46
+ protected $_reflower;
47
+
48
+ /**
49
+ * Reference to the current dompdf instance
50
+ *
51
+ * @var DOMPDF
52
+ */
53
+ protected $_dompdf;
54
+
55
+ /**
56
+ * First block parent
57
+ *
58
+ * @var Block_Frame_Decorator
59
+ */
60
+ private $_block_parent;
61
+
62
+ /**
63
+ * First positionned parent (position: relative | absolute | fixed)
64
+ *
65
+ * @var Frame_Decorator
66
+ */
67
+ private $_positionned_parent;
68
+
69
+ /**
70
+ * Class constructor
71
+ *
72
+ * @param Frame $frame The decoration target
73
+ * @param DOMPDF $dompdf The DOMPDF object
74
+ */
75
+ function __construct(Frame $frame, DOMPDF $dompdf) {
76
+ $this->_frame = $frame;
77
+ $this->_root = null;
78
+ $this->_dompdf = $dompdf;
79
+ $frame->set_decorator($this);
80
+ }
81
+
82
+ /**
83
+ * "Destructor": foribly free all references held by this object
84
+ *
85
+ * @param bool $recursive if true, call dispose on all children
86
+ */
87
+ function dispose($recursive = false) {
88
+ if ( $recursive ) {
89
+ while ( $child = $this->get_first_child() ) {
90
+ $child->dispose(true);
91
+ }
92
+ }
93
+
94
+ $this->_root = null;
95
+ unset($this->_root);
96
+
97
+ $this->_frame->dispose(true);
98
+ $this->_frame = null;
99
+ unset($this->_frame);
100
+
101
+ $this->_positioner = null;
102
+ unset($this->_positioner);
103
+
104
+ $this->_reflower = null;
105
+ unset($this->_reflower);
106
+ }
107
+
108
+ /**
109
+ * Return a copy of this frame with $node as its node
110
+ *
111
+ * @param DOMNode $node
112
+ *
113
+ * @return Frame
114
+ */
115
+ function copy(DOMNode $node) {
116
+ $frame = new Frame($node);
117
+ $frame->set_style(clone $this->_frame->get_original_style());
118
+
119
+ return Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
120
+ }
121
+
122
+ /**
123
+ * Create a deep copy: copy this node and all children
124
+ *
125
+ * @return Frame
126
+ */
127
+ function deep_copy() {
128
+ $frame = new Frame($this->get_node()->cloneNode());
129
+ $frame->set_style(clone $this->_frame->get_original_style());
130
+
131
+ $deco = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
132
+
133
+ foreach ($this->get_children() as $child) {
134
+ $deco->append_child($child->deep_copy());
135
+ }
136
+
137
+ return $deco;
138
+ }
139
+
140
+ /**
141
+ * Delegate calls to decorated frame object
142
+ */
143
+ function reset() {
144
+ $this->_frame->reset();
145
+
146
+ $this->_counters = array();
147
+
148
+ // Reset all children
149
+ foreach ($this->get_children() as $child) {
150
+ $child->reset();
151
+ }
152
+ }
153
+
154
+ // Getters -----------
155
+ function get_id() {
156
+ return $this->_frame->get_id();
157
+ }
158
+
159
+ /**
160
+ * @return Frame
161
+ */
162
+ function get_frame() {
163
+ return $this->_frame;
164
+ }
165
+
166
+ /**
167
+ * @return DOMElement|DOMText
168
+ */
169
+ function get_node() {
170
+ return $this->_frame->get_node();
171
+ }
172
+
173
+ /**
174
+ * @return Style
175
+ */
176
+ function get_style() {
177
+ return $this->_frame->get_style();
178
+ }
179
+
180
+ /**
181
+ * @return Style
182
+ */
183
+ function get_original_style() {
184
+ return $this->_frame->get_original_style();
185
+ }
186
+
187
+ /**
188
+ * @param integer $i
189
+ *
190
+ * @return array|float
191
+ */
192
+ function get_containing_block($i = null) {
193
+ return $this->_frame->get_containing_block($i);
194
+ }
195
+
196
+ /**
197
+ * @param integer $i
198
+ *
199
+ * @return array|float
200
+ */
201
+ function get_position($i = null) {
202
+ return $this->_frame->get_position($i);
203
+ }
204
+
205
+ /**
206
+ * @return DOMPDF
207
+ */
208
+ function get_dompdf() {
209
+ return $this->_dompdf;
210
+ }
211
+
212
+ /**
213
+ * @return float
214
+ */
215
+ function get_margin_height() {
216
+ return $this->_frame->get_margin_height();
217
+ }
218
+
219
+ /**
220
+ * @return float
221
+ */
222
+ function get_margin_width() {
223
+ return $this->_frame->get_margin_width();
224
+ }
225
+
226
+ /**
227
+ * @return array
228
+ */
229
+ function get_padding_box() {
230
+ return $this->_frame->get_padding_box();
231
+ }
232
+
233
+ /**
234
+ * @return array
235
+ */
236
+ function get_border_box() {
237
+ return $this->_frame->get_border_box();
238
+ }
239
+
240
+ /**
241
+ * @param integer $id
242
+ */
243
+ function set_id($id) {
244
+ $this->_frame->set_id($id);
245
+ }
246
+
247
+ /**
248
+ * @param Style $style
249
+ */
250
+ function set_style(Style $style) {
251
+ $this->_frame->set_style($style);
252
+ }
253
+
254
+ /**
255
+ * @param float $x
256
+ * @param float $y
257
+ * @param float $w
258
+ * @param float $h
259
+ */
260
+ function set_containing_block($x = null, $y = null, $w = null, $h = null) {
261
+ $this->_frame->set_containing_block($x, $y, $w, $h);
262
+ }
263
+
264
+ /**
265
+ * @param float $x
266
+ * @param float $y
267
+ */
268
+ function set_position($x = null, $y = null) {
269
+ $this->_frame->set_position($x, $y);
270
+ }
271
+
272
+ /**
273
+ * @return string
274
+ */
275
+ function __toString() {
276
+ return $this->_frame->__toString();
277
+ }
278
+
279
+ /**
280
+ * @param Frame $child
281
+ * @param bool $update_node
282
+ */
283
+ function prepend_child(Frame $child, $update_node = true) {
284
+ while ( $child instanceof Frame_Decorator ) {
285
+ $child = $child->_frame;
286
+ }
287
+
288
+ $this->_frame->prepend_child($child, $update_node);
289
+ }
290
+
291
+ /**
292
+ * @param Frame $child
293
+ * @param bool $update_node
294
+ */
295
+ function append_child(Frame $child, $update_node = true) {
296
+ while ( $child instanceof Frame_Decorator ) {
297
+ $child = $child->_frame;
298
+ }
299
+
300
+ $this->_frame->append_child($child, $update_node);
301
+ }
302
+
303
+ /**
304
+ * @param Frame $new_child
305
+ * @param Frame $ref
306
+ * @param bool $update_node
307
+ */
308
+ function insert_child_before(Frame $new_child, Frame $ref, $update_node = true) {
309
+ while ( $new_child instanceof Frame_Decorator ) {
310
+ $new_child = $new_child->_frame;
311
+ }
312
+
313
+ if ( $ref instanceof Frame_Decorator ) {
314
+ $ref = $ref->_frame;
315
+ }
316
+
317
+ $this->_frame->insert_child_before($new_child, $ref, $update_node);
318
+ }
319
+
320
+ /**
321
+ * @param Frame $new_child
322
+ * @param Frame $ref
323
+ * @param bool $update_node
324
+ */
325
+ function insert_child_after(Frame $new_child, Frame $ref, $update_node = true) {
326
+ while ( $new_child instanceof Frame_Decorator ) {
327
+ $new_child = $new_child->_frame;
328
+ }
329
+
330
+ while ( $ref instanceof Frame_Decorator ) {
331
+ $ref = $ref->_frame;
332
+ }
333
+
334
+ $this->_frame->insert_child_after($new_child, $ref, $update_node);
335
+ }
336
+
337
+ /**
338
+ * @param Frame $child
339
+ * @param bool $update_node
340
+ *
341
+ * @return Frame
342
+ */
343
+ function remove_child(Frame $child, $update_node = true) {
344
+ while ( $child instanceof Frame_Decorator ) {
345
+ $child = $child->_frame;
346
+ }
347
+
348
+ return $this->_frame->remove_child($child, $update_node);
349
+ }
350
+
351
+ /**
352
+ * @return Frame_Decorator
353
+ */
354
+ function get_parent() {
355
+ $p = $this->_frame->get_parent();
356
+ if ( $p && $deco = $p->get_decorator() ) {
357
+ while ( $tmp = $deco->get_decorator() ) {
358
+ $deco = $tmp;
359
+ }
360
+
361
+ return $deco;
362
+ }
363
+ else if ( $p ) {
364
+ return $p;
365
+ }
366
+
367
+ return null;
368
+ }
369
+
370
+ /**
371
+ * @return Frame_Decorator
372
+ */
373
+ function get_first_child() {
374
+ $c = $this->_frame->get_first_child();
375
+ if ( $c && $deco = $c->get_decorator() ) {
376
+ while ( $tmp = $deco->get_decorator() ) {
377
+ $deco = $tmp;
378
+ }
379
+
380
+ return $deco;
381
+ }
382
+ else if ( $c ) {
383
+ return $c;
384
+ }
385
+
386
+ return null;
387
+ }
388
+
389
+ /**
390
+ * @return Frame_Decorator
391
+ */
392
+ function get_last_child() {
393
+ $c = $this->_frame->get_last_child();
394
+ if ( $c && $deco = $c->get_decorator() ) {
395
+ while ( $tmp = $deco->get_decorator() ) {
396
+ $deco = $tmp;
397
+ }
398
+
399
+ return $deco;
400
+ }
401
+ else if ( $c ) {
402
+ return $c;
403
+ }
404
+
405
+ return null;
406
+ }
407
+
408
+ /**
409
+ * @return Frame_Decorator
410
+ */
411
+ function get_prev_sibling() {
412
+ $s = $this->_frame->get_prev_sibling();
413
+ if ( $s && $deco = $s->get_decorator() ) {
414
+ while ( $tmp = $deco->get_decorator() ) {
415
+ $deco = $tmp;
416
+ }
417
+ return $deco;
418
+ }
419
+ else if ( $s ) {
420
+ return $s;
421
+ }
422
+
423
+ return null;
424
+ }
425
+
426
+ /**
427
+ * @return Frame_Decorator
428
+ */
429
+ function get_next_sibling() {
430
+ $s = $this->_frame->get_next_sibling();
431
+ if ( $s && $deco = $s->get_decorator() ) {
432
+ while ( $tmp = $deco->get_decorator() ) {
433
+ $deco = $tmp;
434
+ }
435
+
436
+ return $deco;
437
+ }
438
+ else if ( $s ) {
439
+ return $s;
440
+ }
441
+
442
+ return null;
443
+ }
444
+
445
+ /**
446
+ * @return FrameTreeList
447
+ */
448
+ function get_subtree() {
449
+ return new FrameTreeList($this);
450
+ }
451
+
452
+ function set_positioner(Positioner $posn) {
453
+ $this->_positioner = $posn;
454
+ if ( $this->_frame instanceof Frame_Decorator ) {
455
+ $this->_frame->set_positioner($posn);
456
+ }
457
+ }
458
+
459
+ function set_reflower(Frame_Reflower $reflower) {
460
+ $this->_reflower = $reflower;
461
+ if ( $this->_frame instanceof Frame_Decorator ) {
462
+ $this->_frame->set_reflower( $reflower );
463
+ }
464
+ }
465
+
466
+ /**
467
+ * @return Frame_Reflower
468
+ */
469
+ function get_reflower() {
470
+ return $this->_reflower;
471
+ }
472
+
473
+ /**
474
+ * @param Frame $root
475
+ */
476
+ function set_root(Frame $root) {
477
+ $this->_root = $root;
478
+
479
+ if ( $this->_frame instanceof Frame_Decorator ) {
480
+ $this->_frame->set_root($root);
481
+ }
482
+ }
483
+
484
+ /**
485
+ * @return Page_Frame_Decorator
486
+ */
487
+ function get_root() {
488
+ return $this->_root;
489
+ }
490
+
491
+ /**
492
+ * @return Block_Frame_Decorator
493
+ */
494
+ function find_block_parent() {
495
+ /*if ( $this->_block_parent && !isset($this->_block_parent->_splitted) ) {
496
+ return $this->_block_parent;
497
+ }*/
498
+
499
+ // Find our nearest block level parent
500
+ $p = $this->get_parent();
501
+
502
+ while ( $p ) {
503
+ if ( $p->is_block() ) {
504
+ break;
505
+ }
506
+
507
+ $p = $p->get_parent();
508
+ }
509
+
510
+ return $this->_block_parent = $p;
511
+ }
512
+
513
+ /**
514
+ * @return Frame_Decorator
515
+ */
516
+ function find_positionned_parent() {
517
+ /*if ( $this->_positionned_parent && !isset($this->_block_parent->_splitted) ) {
518
+ return $this->_positionned_parent;
519
+ }*/
520
+
521
+ // Find our nearest relative positionned parent
522
+ $p = $this->get_parent();
523
+ while ( $p ) {
524
+ if ( $p->is_positionned() ) {
525
+ break;
526
+ }
527
+
528
+ $p = $p->get_parent();
529
+ }
530
+
531
+ if ( !$p ) {
532
+ $p = $this->_root->get_first_child(); // <body>
533
+ }
534
+
535
+ return $this->_positionned_parent = $p;
536
+ }
537
+
538
+ /**
539
+ * split this frame at $child.
540
+ * The current frame is cloned and $child and all children following
541
+ * $child are added to the clone. The clone is then passed to the
542
+ * current frame's parent->split() method.
543
+ *
544
+ * @param Frame $child
545
+ * @param boolean $force_pagebreak
546
+ *
547
+ * @throws DOMPDF_Exception
548
+ * @return void
549
+ */
550
+ function split(Frame $child = null, $force_pagebreak = false) {
551
+ if ( is_null( $child ) ) {
552
+ $this->get_parent()->split($this, $force_pagebreak);
553
+ return;
554
+ }
555
+
556
+ if ( $child->get_parent() !== $this ) {
557
+ throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
558
+ }
559
+
560
+ $node = $this->_frame->get_node();
561
+
562
+ // mark the frame as splitted (don't use the find_***_parent cache)
563
+ //$this->_splitted = true;
564
+
565
+ $split = $this->copy( $node->cloneNode() );
566
+ $split->reset();
567
+ $split->get_original_style()->text_indent = 0;
568
+
569
+ // The body's properties must be kept
570
+ if ( $node->nodeName !== "body" ) {
571
+ // Style reset on the first and second parts
572
+ $style = $this->_frame->get_style();
573
+ $style->margin_bottom = 0;
574
+ $style->padding_bottom = 0;
575
+ $style->border_bottom = 0;
576
+
577
+ // second
578
+ $orig_style = $split->get_original_style();
579
+ $orig_style->text_indent = 0;
580
+ $orig_style->margin_top = 0;
581
+ $orig_style->padding_top = 0;
582
+ $orig_style->border_top = 0;
583
+ }
584
+
585
+ $this->get_parent()->insert_child_after($split, $this);
586
+
587
+ // Add $frame and all following siblings to the new split node
588
+ $iter = $child;
589
+ while ($iter) {
590
+ $frame = $iter;
591
+ $iter = $iter->get_next_sibling();
592
+ $frame->reset();
593
+ $split->append_child($frame);
594
+ }
595
+
596
+ $this->get_parent()->split($split, $force_pagebreak);
597
+ }
598
+
599
+ function reset_counter($id = self::DEFAULT_COUNTER, $value = 0) {
600
+ $this->get_parent()->_counters[$id] = intval($value);
601
+ }
602
+
603
+ function increment_counters($counters) {
604
+ foreach($counters as $id => $increment) {
605
+ $this->increment_counter($id, intval($increment));
606
+ }
607
+ }
608
+
609
+ function increment_counter($id = self::DEFAULT_COUNTER, $increment = 1) {
610
+ $counter_frame = $this->lookup_counter_frame($id);
611
+
612
+ if ( $counter_frame ) {
613
+ if ( !isset($counter_frame->_counters[$id]) ) {
614
+ $counter_frame->_counters[$id] = 0;
615
+ }
616
+
617
+ $counter_frame->_counters[$id] += $increment;
618
+ }
619
+ }
620
+
621
+ function lookup_counter_frame($id = self::DEFAULT_COUNTER) {
622
+ $f = $this->get_parent();
623
+
624
+ while( $f ) {
625
+ if( isset($f->_counters[$id]) ) {
626
+ return $f;
627
+ }
628
+ $fp = $f->get_parent();
629
+
630
+ if ( !$fp ) {
631
+ return $f;
632
+ }
633
+
634
+ $f = $fp;
635
+ }
636
+ }
637
+
638
+ // TODO: What version is the best : this one or the one in List_Bullet_Renderer ?
639
+ function counter_value($id = self::DEFAULT_COUNTER, $type = "decimal") {
640
+ $type = mb_strtolower($type);
641
+
642
+ if ( !isset($this->_counters[$id]) ) {
643
+ $this->_counters[$id] = 0;
644
+ }
645
+
646
+ $value = $this->_counters[$id];
647
+
648
+ switch ($type) {
649
+ default:
650
+ case "decimal":
651
+ return $value;
652
+
653
+ case "decimal-leading-zero":
654
+ return str_pad($value, 2, "0");
655
+
656
+ case "lower-roman":
657
+ return dec2roman($value);
658
+
659
+ case "upper-roman":
660
+ return mb_strtoupper(dec2roman($value));
661
+
662
+ case "lower-latin":
663
+ case "lower-alpha":
664
+ return chr( ($value % 26) + ord('a') - 1);
665
+
666
+ case "upper-latin":
667
+ case "upper-alpha":
668
+ return chr( ($value % 26) + ord('A') - 1);
669
+
670
+ case "lower-greek":
671
+ return unichr($value + 944);
672
+
673
+ case "upper-greek":
674
+ return unichr($value + 912);
675
+ }
676
+ }
677
+
678
+ final function position() {
679
+ $this->_positioner->position();
680
+ }
681
+
682
+ final function move($offset_x, $offset_y, $ignore_self = false) {
683
+ $this->_positioner->move($offset_x, $offset_y, $ignore_self);
684
+ }
685
+
686
+ final function reflow(Block_Frame_Decorator $block = null) {
687
+ // Uncomment this to see the frames before they're laid out, instead of
688
+ // during rendering.
689
+ //echo $this->_frame; flush();
690
+ $this->_reflower->reflow($block);
691
+ }
692
+
693
+ final function get_min_max_width() {
694
+ return $this->_reflower->get_min_max_width();
695
+ }
696
+ }
dompdf/include/frame_factory.cls.php ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Contains frame decorating logic
11
+ *
12
+ * This class is responsible for assigning the correct {@link Frame_Decorator},
13
+ * {@link Positioner}, and {@link Frame_Reflower} objects to {@link Frame}
14
+ * objects. This is determined primarily by the Frame's display type, but
15
+ * also by the Frame's node's type (e.g. DomElement vs. #text)
16
+ *
17
+ * @access private
18
+ * @package dompdf
19
+ */
20
+ class Frame_Factory {
21
+
22
+ /**
23
+ * Decorate the root Frame
24
+ *
25
+ * @param $root Frame The frame to decorate
26
+ * @param $dompdf DOMPDF The dompdf instance
27
+ * @return Page_Frame_Decorator
28
+ */
29
+ static function decorate_root(Frame $root, DOMPDF $dompdf) {
30
+ $frame = new Page_Frame_Decorator($root, $dompdf);
31
+ $frame->set_reflower( new Page_Frame_Reflower($frame) );
32
+ $root->set_decorator($frame);
33
+ return $frame;
34
+ }
35
+
36
+ /**
37
+ * Decorate a Frame
38
+ *
39
+ * @param Frame $frame The frame to decorate
40
+ * @param DOMPDF $dompdf The dompdf instance
41
+ * @param Frame $root The frame to decorate
42
+ *
43
+ * @throws DOMPDF_Exception
44
+ * @return Frame_Decorator
45
+ * FIXME: this is admittedly a little smelly...
46
+ */
47
+ static function decorate_frame(Frame $frame, DOMPDF $dompdf, Frame $root = null) {
48
+ if ( is_null($dompdf) ) {
49
+ throw new DOMPDF_Exception("The DOMPDF argument is required");
50
+ }
51
+
52
+ $style = $frame->get_style();
53
+
54
+ // Floating (and more generally out-of-flow) elements are blocks
55
+ // http://coding.smashingmagazine.com/2007/05/01/css-float-theory-things-you-should-know/
56
+ if ( !$frame->is_in_flow() && in_array($style->display, Style::$INLINE_TYPES)) {
57
+ $style->display = "block";
58
+ }
59
+
60
+ $display = $style->display;
61
+
62
+ switch ($display) {
63
+
64
+ case "block":
65
+ $positioner = "Block";
66
+ $decorator = "Block";
67
+ $reflower = "Block";
68
+ break;
69
+
70
+ case "inline-block":
71
+ $positioner = "Inline";
72
+ $decorator = "Block";
73
+ $reflower = "Block";
74
+ break;
75
+
76
+ case "inline":
77
+ $positioner = "Inline";
78
+ if ( $frame->is_text_node() ) {
79
+ $decorator = "Text";
80
+ $reflower = "Text";
81
+ }
82
+ else {
83
+ $enable_css_float = $dompdf->get_option("enable_css_float");
84
+ if ( $enable_css_float && $style->float !== "none" ) {
85
+ $decorator = "Block";
86
+ $reflower = "Block";
87
+ }
88
+ else {
89
+ $decorator = "Inline";
90
+ $reflower = "Inline";
91
+ }
92
+ }
93
+ break;
94
+
95
+ case "table":
96
+ $positioner = "Block";
97
+ $decorator = "Table";
98
+ $reflower = "Table";
99
+ break;
100
+
101
+ case "inline-table":
102
+ $positioner = "Inline";
103
+ $decorator = "Table";
104
+ $reflower = "Table";
105
+ break;
106
+
107
+ case "table-row-group":
108
+ case "table-header-group":
109
+ case "table-footer-group":
110
+ $positioner = "Null";
111
+ $decorator = "Table_Row_Group";
112
+ $reflower = "Table_Row_Group";
113
+ break;
114
+
115
+ case "table-row":
116
+ $positioner = "Null";
117
+ $decorator = "Table_Row";
118
+ $reflower = "Table_Row";
119
+ break;
120
+
121
+ case "table-cell":
122
+ $positioner = "Table_Cell";
123
+ $decorator = "Table_Cell";
124
+ $reflower = "Table_Cell";
125
+ break;
126
+
127
+ case "list-item":
128
+ $positioner = "Block";
129
+ $decorator = "Block";
130
+ $reflower = "Block";
131
+ break;
132
+
133
+ case "-dompdf-list-bullet":
134
+ if ( $style->list_style_position === "inside" ) {
135
+ $positioner = "Inline";
136
+ }
137
+ else {
138
+ $positioner = "List_Bullet";
139
+ }
140
+
141
+ if ( $style->list_style_image !== "none" ) {
142
+ $decorator = "List_Bullet_Image";
143
+ }
144
+ else {
145
+ $decorator = "List_Bullet";
146
+ }
147
+
148
+ $reflower = "List_Bullet";
149
+ break;
150
+
151
+ case "-dompdf-image":
152
+ $positioner = "Inline";
153
+ $decorator = "Image";
154
+ $reflower = "Image";
155
+ break;
156
+
157
+ case "-dompdf-br":
158
+ $positioner = "Inline";
159
+ $decorator = "Inline";
160
+ $reflower = "Inline";
161
+ break;
162
+
163
+ default:
164
+ // FIXME: should throw some sort of warning or something?
165
+ case "none":
166
+ $positioner = "Null";
167
+ $decorator = "Null";
168
+ $reflower = "Null";
169
+ break;
170
+ }
171
+
172
+ // Handle CSS position
173
+ $position = $style->position;
174
+
175
+ if ( $position === "absolute" ) {
176
+ $positioner = "Absolute";
177
+ }
178
+ else if ( $position === "fixed" ) {
179
+ $positioner = "Fixed";
180
+ }
181
+
182
+ $node = $frame->get_node();
183
+
184
+ // Handle nodeName
185
+ if ( $node->nodeName === "img" ) {
186
+ $style->display = "-dompdf-image";
187
+ $decorator = "Image";
188
+ $reflower = "Image";
189
+ }
190
+
191
+ $positioner .= "_Positioner";
192
+ $decorator .= "_Frame_Decorator";
193
+ $reflower .= "_Frame_Reflower";
194
+
195
+ $deco = new $decorator($frame, $dompdf);
196
+
197
+ $deco->set_positioner( new $positioner($deco) );
198
+ $deco->set_reflower( new $reflower($deco) );
199
+
200
+ if ( $root ) {
201
+ $deco->set_root($root);
202
+ }
203
+
204
+ if ( $display === "list-item" ) {
205
+ // Insert a list-bullet frame
206
+ $xml = $dompdf->get_dom();
207
+ $bullet_node = $xml->createElement("bullet"); // arbitrary choice
208
+ $b_f = new Frame($bullet_node);
209
+
210
+ $node = $frame->get_node();
211
+ $parent_node = $node->parentNode;
212
+
213
+ if ( $parent_node ) {
214
+ if ( !$parent_node->hasAttribute("dompdf-children-count") ) {
215
+ $xpath = new DOMXPath($xml);
216
+ $count = $xpath->query("li", $parent_node)->length;
217
+ $parent_node->setAttribute("dompdf-children-count", $count);
218
+ }
219
+
220
+ if ( is_numeric($node->getAttribute("value")) ) {
221
+ $index = intval($node->getAttribute("value"));
222
+ }
223
+ else {
224
+ if ( !$parent_node->hasAttribute("dompdf-counter") ) {
225
+ $index = ($parent_node->hasAttribute("start") ? $parent_node->getAttribute("start") : 1);
226
+ }
227
+ else {
228
+ $index = $parent_node->getAttribute("dompdf-counter")+1;
229
+ }
230
+ }
231
+
232
+ $parent_node->setAttribute("dompdf-counter", $index);
233
+ $bullet_node->setAttribute("dompdf-counter", $index);
234
+ }
235
+
236
+ $new_style = $dompdf->get_css()->create_style();
237
+ $new_style->display = "-dompdf-list-bullet";
238
+ $new_style->inherit($style);
239
+ $b_f->set_style($new_style);
240
+
241
+ $deco->prepend_child( Frame_Factory::decorate_frame($b_f, $dompdf, $root) );
242
+ }
243
+
244
+ return $deco;
245
+ }
246
+ }
dompdf/include/frame_reflower.cls.php ADDED
@@ -0,0 +1,449 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Base reflower class
11
+ *
12
+ * Reflower objects are responsible for determining the width and height of
13
+ * individual frames. They also create line and page breaks as necessary.
14
+ *
15
+ * @access private
16
+ * @package dompdf
17
+ */
18
+ abstract class Frame_Reflower {
19
+
20
+ /**
21
+ * Frame for this reflower
22
+ *
23
+ * @var Frame
24
+ */
25
+ protected $_frame;
26
+
27
+ /**
28
+ * Cached min/max size
29
+ *
30
+ * @var array
31
+ */
32
+ protected $_min_max_cache;
33
+
34
+ function __construct(Frame $frame) {
35
+ $this->_frame = $frame;
36
+ $this->_min_max_cache = null;
37
+ }
38
+
39
+ function dispose() {
40
+ clear_object($this);
41
+ }
42
+
43
+ /**
44
+ * @return DOMPDF
45
+ */
46
+ function get_dompdf() {
47
+ return $this->_frame->get_dompdf();
48
+ }
49
+
50
+ /**
51
+ * Collapse frames margins
52
+ * http://www.w3.org/TR/CSS2/box.html#collapsing-margins
53
+ */
54
+ protected function _collapse_margins() {
55
+ $frame = $this->_frame;
56
+ $cb = $frame->get_containing_block();
57
+ $style = $frame->get_style();
58
+
59
+ if ( !$frame->is_in_flow() ) {
60
+ return;
61
+ }
62
+
63
+ $t = $style->length_in_pt($style->margin_top, $cb["h"]);
64
+ $b = $style->length_in_pt($style->margin_bottom, $cb["h"]);
65
+
66
+ // Handle 'auto' values
67
+ if ( $t === "auto" ) {
68
+ $style->margin_top = "0pt";
69
+ $t = 0;
70
+ }
71
+
72
+ if ( $b === "auto" ) {
73
+ $style->margin_bottom = "0pt";
74
+ $b = 0;
75
+ }
76
+
77
+ // Collapse vertical margins:
78
+ $n = $frame->get_next_sibling();
79
+ if ( $n && !$n->is_block() ) {
80
+ while ( $n = $n->get_next_sibling() ) {
81
+ if ( $n->is_block() ) {
82
+ break;
83
+ }
84
+
85
+ if ( !$n->get_first_child() ) {
86
+ $n = null;
87
+ break;
88
+ }
89
+ }
90
+ }
91
+
92
+ if ( $n ) {
93
+ $n_style = $n->get_style();
94
+ $b = max($b, $n_style->length_in_pt($n_style->margin_top, $cb["h"]));
95
+ $n_style->margin_top = "0pt";
96
+ $style->margin_bottom = $b."pt";
97
+ }
98
+
99
+ // Collapse our first child's margin
100
+ /*$f = $this->_frame->get_first_child();
101
+ if ( $f && !$f->is_block() ) {
102
+ while ( $f = $f->get_next_sibling() ) {
103
+ if ( $f->is_block() ) {
104
+ break;
105
+ }
106
+
107
+ if ( !$f->get_first_child() ) {
108
+ $f = null;
109
+ break;
110
+ }
111
+ }
112
+ }
113
+
114
+ // Margin are collapsed only between block elements
115
+ if ( $f ) {
116
+ $f_style = $f->get_style();
117
+ $t = max($t, $f_style->length_in_pt($f_style->margin_top, $cb["h"]));
118
+ $style->margin_top = $t."pt";
119
+ $f_style->margin_bottom = "0pt";
120
+ }*/
121
+ }
122
+
123
+ //........................................................................
124
+
125
+ abstract function reflow(Block_Frame_Decorator $block = null);
126
+
127
+ //........................................................................
128
+
129
+ // Required for table layout: Returns an array(0 => min, 1 => max, "min"
130
+ // => min, "max" => max) of the minimum and maximum widths of this frame.
131
+ // This provides a basic implementation. Child classes should override
132
+ // this if necessary.
133
+ function get_min_max_width() {
134
+ if ( !is_null($this->_min_max_cache) ) {
135
+ return $this->_min_max_cache;
136
+ }
137
+
138
+ $style = $this->_frame->get_style();
139
+
140
+ // Account for margins & padding
141
+ $dims = array($style->padding_left,
142
+ $style->padding_right,
143
+ $style->border_left_width,
144
+ $style->border_right_width,
145
+ $style->margin_left,
146
+ $style->margin_right);
147
+
148
+ $cb_w = $this->_frame->get_containing_block("w");
149
+ $delta = $style->length_in_pt($dims, $cb_w);
150
+
151
+ // Handle degenerate case
152
+ if ( !$this->_frame->get_first_child() ) {
153
+ return $this->_min_max_cache = array(
154
+ $delta, $delta,
155
+ "min" => $delta,
156
+ "max" => $delta,
157
+ );
158
+ }
159
+
160
+ $low = array();
161
+ $high = array();
162
+
163
+ for ( $iter = $this->_frame->get_children()->getIterator();
164
+ $iter->valid();
165
+ $iter->next() ) {
166
+
167
+ $inline_min = 0;
168
+ $inline_max = 0;
169
+
170
+ // Add all adjacent inline widths together to calculate max width
171
+ while ( $iter->valid() && in_array( $iter->current()->get_style()->display, Style::$INLINE_TYPES ) ) {
172
+
173
+ $child = $iter->current();
174
+
175
+ $minmax = $child->get_min_max_width();
176
+
177
+ if ( in_array( $iter->current()->get_style()->white_space, array("pre", "nowrap") ) ) {
178
+ $inline_min += $minmax["min"];
179
+ }
180
+ else {
181
+ $low[] = $minmax["min"];
182
+ }
183
+
184
+ $inline_max += $minmax["max"];
185
+ $iter->next();
186
+
187
+ }
188
+
189
+ if ( $inline_max > 0 ) $high[] = $inline_max;
190
+ if ( $inline_min > 0 ) $low[] = $inline_min;
191
+
192
+ if ( $iter->valid() ) {
193
+ list($low[], $high[]) = $iter->current()->get_min_max_width();
194
+ continue;
195
+ }
196
+
197
+ }
198
+ $min = count($low) ? max($low) : 0;
199
+ $max = count($high) ? max($high) : 0;
200
+
201
+ // Use specified width if it is greater than the minimum defined by the
202
+ // content. If the width is a percentage ignore it for now.
203
+ $width = $style->width;
204
+ if ( $width !== "auto" && !is_percent($width) ) {
205
+ $width = $style->length_in_pt($width, $cb_w);
206
+ if ( $min < $width ) $min = $width;
207
+ if ( $max < $width ) $max = $width;
208
+ }
209
+
210
+ $min += $delta;
211
+ $max += $delta;
212
+ return $this->_min_max_cache = array($min, $max, "min"=>$min, "max"=>$max);
213
+ }
214
+
215
+ /**
216
+ * Parses a CSS string containing quotes and escaped hex characters
217
+ *
218
+ * @param $string string The CSS string to parse
219
+ * @param $single_trim
220
+ * @return string
221
+ */
222
+ protected function _parse_string($string, $single_trim = false) {
223
+ if ( $single_trim ) {
224
+ $string = preg_replace('/^[\"\']/', "", $string);
225
+ $string = preg_replace('/[\"\']$/', "", $string);
226
+ }
227
+ else {
228
+ $string = trim($string, "'\"");
229
+ }
230
+
231
+ $string = str_replace(array("\\\n",'\\"',"\\'"),
232
+ array("",'"',"'"), $string);
233
+
234
+ // Convert escaped hex characters into ascii characters (e.g. \A => newline)
235
+ $string = preg_replace_callback("/\\\\([0-9a-fA-F]{0,6})/",
236
+ create_function('$matches',
237
+ 'return unichr(hexdec($matches[1]));'),
238
+ $string);
239
+ return $string;
240
+ }
241
+
242
+ /**
243
+ * Parses a CSS "quotes" property
244
+ *
245
+ * @return array An array of pairs of quotes
246
+ */
247
+ protected function _parse_quotes() {
248
+
249
+ // Matches quote types
250
+ $re = '/(\'[^\']*\')|(\"[^\"]*\")/';
251
+
252
+ $quotes = $this->_frame->get_style()->quotes;
253
+
254
+ // split on spaces, except within quotes
255
+ if ( !preg_match_all($re, "$quotes", $matches, PREG_SET_ORDER) ) {
256
+ return;
257
+ }
258
+
259
+ $quotes_array = array();
260
+ foreach($matches as &$_quote){
261
+ $quotes_array[] = $this->_parse_string($_quote[0], true);
262
+ }
263
+
264
+ if ( empty($quotes_array) ) {
265
+ $quotes_array = array('"', '"');
266
+ }
267
+
268
+ return array_chunk($quotes_array, 2);
269
+ }
270
+
271
+ /**
272
+ * Parses the CSS "content" property
273
+ *
274
+ * @return string The resulting string
275
+ */
276
+ protected function _parse_content() {
277
+
278
+ // Matches generated content
279
+ $re = "/\n".
280
+ "\s(counters?\\([^)]*\\))|\n".
281
+ "\A(counters?\\([^)]*\\))|\n".
282
+ "\s([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\3|\n".
283
+ "\A([\"']) ( (?:[^\"']|\\\\[\"'])+ )(?<!\\\\)\\5|\n" .
284
+ "\s([^\s\"']+)|\n" .
285
+ "\A([^\s\"']+)\n".
286
+ "/xi";
287
+
288
+ $content = $this->_frame->get_style()->content;
289
+
290
+ $quotes = $this->_parse_quotes();
291
+
292
+ // split on spaces, except within quotes
293
+ if ( !preg_match_all($re, $content, $matches, PREG_SET_ORDER) ) {
294
+ return;
295
+ }
296
+
297
+ $text = "";
298
+
299
+ foreach ($matches as $match) {
300
+
301
+ if ( isset($match[2]) && $match[2] !== "" ) {
302
+ $match[1] = $match[2];
303
+ }
304
+
305
+ if ( isset($match[6]) && $match[6] !== "" ) {
306
+ $match[4] = $match[6];
307
+ }
308
+
309
+ if ( isset($match[8]) && $match[8] !== "" ) {
310
+ $match[7] = $match[8];
311
+ }
312
+
313
+ if ( isset($match[1]) && $match[1] !== "" ) {
314
+
315
+ // counters?(...)
316
+ $match[1] = mb_strtolower(trim($match[1]));
317
+
318
+ // Handle counter() references:
319
+ // http://www.w3.org/TR/CSS21/generate.html#content
320
+
321
+ $i = mb_strpos($match[1], ")");
322
+ if ( $i === false ) {
323
+ continue;
324
+ }
325
+
326
+ $args = explode(",", mb_substr($match[1], 8, $i - 8));
327
+ $counter_id = $args[0];
328
+
329
+ if ( $match[1][7] === "(" ) {
330
+ // counter(name [,style])
331
+
332
+ if ( isset($args[1]) ) {
333
+ $type = trim($args[1]);
334
+ }
335
+ else {
336
+ $type = null;
337
+ }
338
+
339
+ $p = $this->_frame->lookup_counter_frame($counter_id);
340
+
341
+ $text .= $p->counter_value($counter_id, $type);
342
+
343
+ }
344
+ else if ( $match[1][7] === "s" ) {
345
+ // counters(name, string [,style])
346
+ if ( isset($args[1]) ) {
347
+ $string = $this->_parse_string(trim($args[1]));
348
+ }
349
+ else {
350
+ $string = "";
351
+ }
352
+
353
+ if ( isset($args[2]) ) {
354
+ $type = $args[2];
355
+ }
356
+ else {
357
+ $type = null;
358
+ }
359
+
360
+ $p = $this->_frame->lookup_counter_frame($counter_id);
361
+ $tmp = "";
362
+ while ($p) {
363
+ $tmp = $p->counter_value($counter_id, $type) . $string . $tmp;
364
+ $p = $p->lookup_counter_frame($counter_id);
365
+ }
366
+ $text .= $tmp;
367
+
368
+ }
369
+ else {
370
+ // countertops?
371
+ continue;
372
+ }
373
+
374
+ }
375
+ else if ( isset($match[4]) && $match[4] !== "" ) {
376
+ // String match
377
+ $text .= $this->_parse_string($match[4]);
378
+ }
379
+ else if ( isset($match[7]) && $match[7] !== "" ) {
380
+ // Directive match
381
+
382
+ if ( $match[7] === "open-quote" ) {
383
+ // FIXME: do something here
384
+ $text .= $quotes[0][0];
385
+ }
386
+ else if ( $match[7] === "close-quote" ) {
387
+ // FIXME: do something else here
388
+ $text .= $quotes[0][1];
389
+ }
390
+ else if ( $match[7] === "no-open-quote" ) {
391
+ // FIXME:
392
+ }
393
+ else if ( $match[7] === "no-close-quote" ) {
394
+ // FIXME:
395
+ }
396
+ else if ( mb_strpos($match[7],"attr(") === 0 ) {
397
+
398
+ $i = mb_strpos($match[7],")");
399
+ if ( $i === false ) {
400
+ continue;
401
+ }
402
+
403
+ $attr = mb_substr($match[7], 5, $i - 5);
404
+ if ( $attr == "" ) {
405
+ continue;
406
+ }
407
+
408
+ $text .= $this->_frame->get_parent()->get_node()->getAttribute($attr);
409
+ }
410
+ else {
411
+ continue;
412
+ }
413
+ }
414
+ }
415
+
416
+ return $text;
417
+ }
418
+
419
+ /**
420
+ * Sets the generated content of a generated frame
421
+ */
422
+ protected function _set_content(){
423
+ $frame = $this->_frame;
424
+ $style = $frame->get_style();
425
+
426
+ if ( $style->counter_reset && ($reset = $style->counter_reset) !== "none" ) {
427
+ $vars = preg_split('/\s+/', trim($reset), 2);
428
+ $frame->reset_counter($vars[0], isset($vars[1]) ? $vars[1] : 0);
429
+ }
430
+
431
+ if ( $style->counter_increment && ($increment = $style->counter_increment) !== "none" ) {
432
+ $frame->increment_counters($increment);
433
+ }
434
+
435
+ if ( $style->content && !$frame->get_first_child() && $frame->get_node()->nodeName === "dompdf_generated" ) {
436
+ $content = $this->_parse_content();
437
+ $node = $frame->get_node()->ownerDocument->createTextNode($content);
438
+
439
+ $new_style = $style->get_stylesheet()->create_style();
440
+ $new_style->inherit($style);
441
+
442
+ $new_frame = new Frame($node);
443
+ $new_frame->set_style($new_style);
444
+
445
+ Frame_Factory::decorate_frame($new_frame, $frame->get_dompdf(), $frame->get_root());
446
+ $frame->append_child($new_frame);
447
+ }
448
+ }
449
+ }
dompdf/include/frame_tree.cls.php ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Represents an entire document as a tree of frames
11
+ *
12
+ * The Frame_Tree consists of {@link Frame} objects each tied to specific
13
+ * DOMNode objects in a specific DomDocument. The Frame_Tree has the same
14
+ * structure as the DomDocument, but adds additional capabalities for
15
+ * styling and layout.
16
+ *
17
+ * @package dompdf
18
+ * @access protected
19
+ */
20
+ class Frame_Tree {
21
+
22
+ /**
23
+ * Tags to ignore while parsing the tree
24
+ *
25
+ * @var array
26
+ */
27
+ static protected $_HIDDEN_TAGS = array("area", "base", "basefont", "head", "style",
28
+ "meta", "title", "colgroup",
29
+ "noembed", "noscript", "param", "#comment");
30
+ /**
31
+ * The main DomDocument
32
+ *
33
+ * @see http://ca2.php.net/manual/en/ref.dom.php
34
+ * @var DomDocument
35
+ */
36
+ protected $_dom;
37
+
38
+ /**
39
+ * The root node of the FrameTree.
40
+ *
41
+ * @var Frame
42
+ */
43
+ protected $_root;
44
+
45
+ /**
46
+ * Subtrees of absolutely positioned elements
47
+ *
48
+ * @var array of Frames
49
+ */
50
+ protected $_absolute_frames;
51
+
52
+ /**
53
+ * A mapping of {@link Frame} objects to DOMNode objects
54
+ *
55
+ * @var array
56
+ */
57
+ protected $_registry;
58
+
59
+
60
+ /**
61
+ * Class constructor
62
+ *
63
+ * @param DomDocument $dom the main DomDocument object representing the current html document
64
+ */
65
+ function __construct(DomDocument $dom) {
66
+ $this->_dom = $dom;
67
+ $this->_root = null;
68
+ $this->_registry = array();
69
+ }
70
+
71
+ function __destruct() {
72
+ clear_object($this);
73
+ }
74
+
75
+ /**
76
+ * Returns the DomDocument object representing the curent html document
77
+ *
78
+ * @return DOMDocument
79
+ */
80
+ function get_dom() {
81
+ return $this->_dom;
82
+ }
83
+
84
+ /**
85
+ * Returns the root frame of the tree
86
+ *
87
+ * @return Page_Frame_Decorator
88
+ */
89
+ function get_root() {
90
+ return $this->_root;
91
+ }
92
+
93
+ /**
94
+ * Returns a specific frame given its id
95
+ *
96
+ * @param string $id
97
+ * @return Frame
98
+ */
99
+ function get_frame($id) {
100
+ return isset($this->_registry[$id]) ? $this->_registry[$id] : null;
101
+ }
102
+
103
+ /**
104
+ * Returns a post-order iterator for all frames in the tree
105
+ *
106
+ * @return FrameTreeList|Frame[]
107
+ */
108
+ function get_frames() {
109
+ return new FrameTreeList($this->_root);
110
+ }
111
+
112
+ /**
113
+ * Builds the tree
114
+ */
115
+ function build_tree() {
116
+ $html = $this->_dom->getElementsByTagName("html")->item(0);
117
+ if ( is_null($html) ) {
118
+ $html = $this->_dom->firstChild;
119
+ }
120
+
121
+ if ( is_null($html) ) {
122
+ throw new DOMPDF_Exception("Requested HTML document contains no data.");
123
+ }
124
+
125
+ $this->fix_tables();
126
+
127
+ $this->_root = $this->_build_tree_r($html);
128
+ }
129
+
130
+ /**
131
+ * Adds missing TBODYs around TR
132
+ */
133
+ protected function fix_tables(){
134
+ $xp = new DOMXPath($this->_dom);
135
+
136
+ // Move table caption before the table
137
+ // FIXME find a better way to deal with it...
138
+ $captions = $xp->query("//table/caption");
139
+ foreach($captions as $caption) {
140
+ $table = $caption->parentNode;
141
+ $table->parentNode->insertBefore($caption, $table);
142
+ }
143
+
144
+ $rows = $xp->query("//table/tr");
145
+ foreach($rows as $row) {
146
+ $tbody = $this->_dom->createElement("tbody");
147
+ $tbody = $row->parentNode->insertBefore($tbody, $row);
148
+ $tbody->appendChild($row);
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Recursively adds {@link Frame} objects to the tree
154
+ *
155
+ * Recursively build a tree of Frame objects based on a dom tree.
156
+ * No layout information is calculated at this time, although the
157
+ * tree may be adjusted (i.e. nodes and frames for generated content
158
+ * and images may be created).
159
+ *
160
+ * @param DOMNode $node the current DOMNode being considered
161
+ * @return Frame
162
+ */
163
+ protected function _build_tree_r(DOMNode $node) {
164
+
165
+ $frame = new Frame($node);
166
+ $id = $frame->get_id();
167
+ $this->_registry[ $id ] = $frame;
168
+
169
+ if ( !$node->hasChildNodes() ) {
170
+ return $frame;
171
+ }
172
+
173
+ // Fixes 'cannot access undefined property for object with
174
+ // overloaded access', fix by Stefan radulian
175
+ // <stefan.radulian@symbion.at>
176
+ //foreach ($node->childNodes as $child) {
177
+
178
+ // Store the children in an array so that the tree can be modified
179
+ $children = array();
180
+ for ($i = 0; $i < $node->childNodes->length; $i++) {
181
+ $children[] = $node->childNodes->item($i);
182
+ }
183
+
184
+ foreach ($children as $child) {
185
+ $node_name = mb_strtolower($child->nodeName);
186
+
187
+ // Skip non-displaying nodes
188
+ if ( in_array($node_name, self::$_HIDDEN_TAGS) ) {
189
+ if ( $node_name !== "head" && $node_name !== "style" ) {
190
+ $child->parentNode->removeChild($child);
191
+ }
192
+
193
+ continue;
194
+ }
195
+
196
+ // Skip empty text nodes
197
+ if ( $node_name === "#text" && $child->nodeValue == "" ) {
198
+ $child->parentNode->removeChild($child);
199
+ continue;
200
+ }
201
+
202
+ // Skip empty image nodes
203
+ if ( $node_name === "img" && $child->getAttribute("src") == "" ) {
204
+ $child->parentNode->removeChild($child);
205
+ continue;
206
+ }
207
+
208
+ $frame->append_child($this->_build_tree_r($child), false);
209
+ }
210
+
211
+ return $frame;
212
+ }
213
+
214
+ public function insert_node(DOMNode $node, DOMNode $new_node, $pos) {
215
+ if ( $pos === "after" || !$node->firstChild ) {
216
+ $node->appendChild($new_node);
217
+ }
218
+ else {
219
+ $node->insertBefore($new_node, $node->firstChild);
220
+ }
221
+
222
+ $this->_build_tree_r($new_node);
223
+
224
+ $frame_id = $new_node->getAttribute("frame_id");
225
+ $frame = $this->get_frame($frame_id);
226
+
227
+ $parent_id = $node->getAttribute("frame_id");
228
+ $parent = $this->get_frame($parent_id);
229
+
230
+ if ( $pos === "before" ) {
231
+ $parent->prepend_child($frame, false);
232
+ }
233
+ else {
234
+ $parent->append_child($frame, false);
235
+ }
236
+
237
+ return $frame_id;
238
+ }
239
+ }
dompdf/include/functions.inc.php ADDED
@@ -0,0 +1,1050 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ if ( !defined('PHP_VERSION_ID') ) {
12
+ $version = explode('.', PHP_VERSION);
13
+ define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
14
+ }
15
+
16
+ function def($name, $value = true) {
17
+ if ( !defined($name) ) {
18
+ define($name, $value);
19
+ }
20
+ }
21
+
22
+ if ( !function_exists("pre_r") ) {
23
+ /**
24
+ * print_r wrapper for html/cli output
25
+ *
26
+ * Wraps print_r() output in < pre > tags if the current sapi is not 'cli'.
27
+ * Returns the output string instead of displaying it if $return is true.
28
+ *
29
+ * @param mixed $mixed variable or expression to display
30
+ * @param bool $return
31
+ *
32
+ * @return string
33
+ */
34
+ function pre_r($mixed, $return = false) {
35
+ if ( $return ) {
36
+ return "<pre>" . print_r($mixed, true) . "</pre>";
37
+ }
38
+
39
+ if ( php_sapi_name() !== "cli" ) {
40
+ echo "<pre>";
41
+ }
42
+
43
+ print_r($mixed);
44
+
45
+ if ( php_sapi_name() !== "cli" ) {
46
+ echo "</pre>";
47
+ }
48
+ else {
49
+ echo "\n";
50
+ }
51
+
52
+ flush();
53
+
54
+ }
55
+ }
56
+
57
+ if ( !function_exists("pre_var_dump") ) {
58
+ /**
59
+ * var_dump wrapper for html/cli output
60
+ *
61
+ * Wraps var_dump() output in < pre > tags if the current sapi is not 'cli'.
62
+ *
63
+ * @param mixed $mixed variable or expression to display.
64
+ */
65
+ function pre_var_dump($mixed) {
66
+ if ( php_sapi_name() !== "cli" ) {
67
+ echo "<pre>";
68
+ }
69
+
70
+ var_dump($mixed);
71
+
72
+ if ( php_sapi_name() !== "cli" ) {
73
+ echo "</pre>";
74
+ }
75
+ }
76
+ }
77
+
78
+ if ( !function_exists("d") ) {
79
+ /**
80
+ * generic debug function
81
+ *
82
+ * Takes everything and does its best to give a good debug output
83
+ *
84
+ * @param mixed $mixed variable or expression to display.
85
+ */
86
+ function d($mixed) {
87
+ if ( php_sapi_name() !== "cli" ) {
88
+ echo "<pre>";
89
+ }
90
+
91
+ // line
92
+ if ( $mixed instanceof Line_Box ) {
93
+ echo $mixed;
94
+ }
95
+
96
+ // other
97
+ else {
98
+ var_export($mixed);
99
+ }
100
+
101
+ if ( php_sapi_name() !== "cli" ) {
102
+ echo "</pre>";
103
+ }
104
+ }
105
+ }
106
+
107
+ /**
108
+ * builds a full url given a protocol, hostname, base path and url
109
+ *
110
+ * @param string $protocol
111
+ * @param string $host
112
+ * @param string $base_path
113
+ * @param string $url
114
+ * @return string
115
+ *
116
+ * Initially the trailing slash of $base_path was optional, and conditionally appended.
117
+ * However on dynamically created sites, where the page is given as url parameter,
118
+ * the base path might not end with an url.
119
+ * Therefore do not append a slash, and **require** the $base_url to ending in a slash
120
+ * when needed.
121
+ * Vice versa, on using the local file system path of a file, make sure that the slash
122
+ * is appended (o.k. also for Windows)
123
+ */
124
+ function build_url($protocol, $host, $base_path, $url) {
125
+ if ( strlen($url) == 0 ) {
126
+ //return $protocol . $host . rtrim($base_path, "/\\") . "/";
127
+ return $protocol . $host . $base_path;
128
+ }
129
+
130
+ // Is the url already fully qualified or a Data URI?
131
+ if ( mb_strpos($url, "://") !== false || mb_strpos($url, "data:") === 0 ) {
132
+ return $url;
133
+ }
134
+
135
+ $ret = $protocol;
136
+
137
+ if ( !in_array(mb_strtolower($protocol), array("http://", "https://", "ftp://", "ftps://")) ) {
138
+ //On Windows local file, an abs path can begin also with a '\' or a drive letter and colon
139
+ //drive: followed by a relative path would be a drive specific default folder.
140
+ //not known in php app code, treat as abs path
141
+ //($url[1] !== ':' || ($url[2]!=='\\' && $url[2]!=='/'))
142
+ if ( $url[0] !== '/' && (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN' || ($url[0] !== '\\' && $url[1] !== ':')) ) {
143
+ // For rel path and local acess we ignore the host, and run the path through realpath()
144
+ $ret .= realpath($base_path).'/';
145
+ }
146
+ $ret .= $url;
147
+ $ret = preg_replace('/\?(.*)$/', "", $ret);
148
+ return $ret;
149
+ }
150
+
151
+ //remote urls with backslash in html/css are not really correct, but lets be genereous
152
+ if ( $url[0] === '/' || $url[0] === '\\' ) {
153
+ // Absolute path
154
+ $ret .= $host . $url;
155
+ }
156
+ else {
157
+ // Relative path
158
+ //$base_path = $base_path !== "" ? rtrim($base_path, "/\\") . "/" : "";
159
+ $ret .= $host . $base_path . $url;
160
+ }
161
+
162
+ return $ret;
163
+
164
+ }
165
+
166
+ /**
167
+ * parse a full url or pathname and return an array(protocol, host, path,
168
+ * file + query + fragment)
169
+ *
170
+ * @param string $url
171
+ * @return array
172
+ */
173
+ function explode_url($url) {
174
+ $protocol = "";
175
+ $host = "";
176
+ $path = "";
177
+ $file = "";
178
+
179
+ $arr = parse_url($url);
180
+
181
+ // Exclude windows drive letters...
182
+ if ( isset($arr["scheme"]) && $arr["scheme"] !== "file" && strlen($arr["scheme"]) > 1 ) {
183
+ $protocol = $arr["scheme"] . "://";
184
+
185
+ if ( isset($arr["user"]) ) {
186
+ $host .= $arr["user"];
187
+
188
+ if ( isset($arr["pass"]) ) {
189
+ $host .= ":" . $arr["pass"];
190
+ }
191
+
192
+ $host .= "@";
193
+ }
194
+
195
+ if ( isset($arr["host"]) ) {
196
+ $host .= $arr["host"];
197
+ }
198
+
199
+ if ( isset($arr["port"]) ) {
200
+ $host .= ":" . $arr["port"];
201
+ }
202
+
203
+ if ( isset($arr["path"]) && $arr["path"] !== "" ) {
204
+ // Do we have a trailing slash?
205
+ if ( $arr["path"][ mb_strlen($arr["path"]) - 1 ] === "/" ) {
206
+ $path = $arr["path"];
207
+ $file = "";
208
+ }
209
+ else {
210
+ $path = rtrim(dirname($arr["path"]), '/\\') . "/";
211
+ $file = basename($arr["path"]);
212
+ }
213
+ }
214
+
215
+ if ( isset($arr["query"]) ) {
216
+ $file .= "?" . $arr["query"];
217
+ }
218
+
219
+ if ( isset($arr["fragment"]) ) {
220
+ $file .= "#" . $arr["fragment"];
221
+ }
222
+
223
+ }
224
+ else {
225
+
226
+ $i = mb_strpos($url, "file://");
227
+ if ( $i !== false ) {
228
+ $url = mb_substr($url, $i + 7);
229
+ }
230
+
231
+ $protocol = ""; // "file://"; ? why doesn't this work... It's because of
232
+ // network filenames like //COMPU/SHARENAME
233
+
234
+ $host = ""; // localhost, really
235
+ $file = basename($url);
236
+
237
+ $path = dirname($url);
238
+
239
+ // Check that the path exists
240
+ if ( $path !== false ) {
241
+ $path .= '/';
242
+
243
+ }
244
+ else {
245
+ // generate a url to access the file if no real path found.
246
+ $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://';
247
+
248
+ $host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : php_uname("n");
249
+
250
+ if ( substr($arr["path"], 0, 1) === '/' ) {
251
+ $path = dirname($arr["path"]);
252
+ }
253
+ else {
254
+ $path = '/' . rtrim(dirname($_SERVER["SCRIPT_NAME"]), '/') . '/' . $arr["path"];
255
+ }
256
+ }
257
+ }
258
+
259
+ $ret = array($protocol, $host, $path, $file,
260
+ "protocol" => $protocol,
261
+ "host" => $host,
262
+ "path" => $path,
263
+ "file" => $file);
264
+ return $ret;
265
+ }
266
+
267
+ /**
268
+ * Converts decimal numbers to roman numerals
269
+ *
270
+ * @param int $num
271
+ *
272
+ * @throws DOMPDF_Exception
273
+ * @return string
274
+ */
275
+ function dec2roman($num) {
276
+
277
+ static $ones = array("", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix");
278
+ static $tens = array("", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc");
279
+ static $hund = array("", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm");
280
+ static $thou = array("", "m", "mm", "mmm");
281
+
282
+ if ( !is_numeric($num) ) {
283
+ throw new DOMPDF_Exception("dec2roman() requires a numeric argument.");
284
+ }
285
+
286
+ if ( $num > 4000 || $num < 0 ) {
287
+ return "(out of range)";
288
+ }
289
+
290
+ $num = strrev((string)$num);
291
+
292
+ $ret = "";
293
+ switch (mb_strlen($num)) {
294
+ case 4: $ret .= $thou[$num[3]];
295
+ case 3: $ret .= $hund[$num[2]];
296
+ case 2: $ret .= $tens[$num[1]];
297
+ case 1: $ret .= $ones[$num[0]];
298
+ default: break;
299
+ }
300
+
301
+ return $ret;
302
+ }
303
+
304
+ /**
305
+ * Determines whether $value is a percentage or not
306
+ *
307
+ * @param float $value
308
+ *
309
+ * @return bool
310
+ */
311
+ function is_percent($value) {
312
+ return false !== mb_strpos($value, "%");
313
+ }
314
+
315
+ /**
316
+ * Parses a data URI scheme
317
+ * http://en.wikipedia.org/wiki/Data_URI_scheme
318
+ *
319
+ * @param string $data_uri The data URI to parse
320
+ *
321
+ * @return array The result with charset, mime type and decoded data
322
+ */
323
+ function parse_data_uri($data_uri) {
324
+ if (!preg_match('/^data:(?P<mime>[a-z0-9\/+-.]+)(;charset=(?P<charset>[a-z0-9-])+)?(?P<base64>;base64)?\,(?P<data>.*)?/i', $data_uri, $match)) {
325
+ return false;
326
+ }
327
+
328
+ $match['data'] = rawurldecode($match['data']);
329
+ $result = array(
330
+ 'charset' => $match['charset'] ? $match['charset'] : 'US-ASCII',
331
+ 'mime' => $match['mime'] ? $match['mime'] : 'text/plain',
332
+ 'data' => $match['base64'] ? base64_decode($match['data']) : $match['data'],
333
+ );
334
+
335
+ return $result;
336
+ }
337
+
338
+ /**
339
+ * mb_string compatibility
340
+ */
341
+ /*
342
+ * Update: Blue Liquid Designs
343
+ * Mods: Added a test to see if the function already exists
344
+ * Reason: Error when plugged into Wordpress on some hosts
345
+ * Date: Feb 12, 2013
346
+ */
347
+
348
+ if ( !function_exists("mb_strlen") ) {
349
+ define('MB_OVERLOAD_MAIL', 1);
350
+ define('MB_OVERLOAD_STRING', 2);
351
+ define('MB_OVERLOAD_REGEX', 4);
352
+ define('MB_CASE_UPPER', 0);
353
+ define('MB_CASE_LOWER', 1);
354
+ define('MB_CASE_TITLE', 2);
355
+
356
+ if ( !function_exists("mb_convert_encoding") ) {
357
+ function mb_convert_encoding($data, $to_encoding, $from_encoding = 'UTF-8') {
358
+ if (str_replace('-', '', strtolower($to_encoding)) === 'utf8') {
359
+ return utf8_encode($data);
360
+ }
361
+
362
+ return utf8_decode($data);
363
+ }
364
+ }
365
+
366
+ if ( !function_exists("mb_detect_encoding") ) {
367
+ function mb_detect_encoding($data, $encoding_list = array('iso-8859-1'), $strict = false) {
368
+ return 'iso-8859-1';
369
+ }
370
+ }
371
+
372
+ if ( !function_exists("mb_detect_order") ) {
373
+ function mb_detect_order($encoding_list = array('iso-8859-1')) {
374
+ return 'iso-8859-1';
375
+ }
376
+ }
377
+
378
+ if ( !function_exists("mb_internal_encoding") ) {
379
+ function mb_internal_encoding($encoding = null) {
380
+ if (isset($encoding)) {
381
+ return true;
382
+ }
383
+
384
+ return 'iso-8859-1';
385
+ }
386
+ }
387
+
388
+ if ( !function_exists("mb_strlen") ) {
389
+ function mb_strlen($str, $encoding = 'iso-8859-1') {
390
+ switch (str_replace('-', '', strtolower($encoding))) {
391
+ case "utf8": return strlen(utf8_encode($str));
392
+ case "8bit": return strlen($str);
393
+ default: return strlen(utf8_decode($str));
394
+ }
395
+ }
396
+ }
397
+
398
+ if ( !function_exists("mb_strpos") ) {
399
+ function mb_strpos($haystack, $needle, $offset = 0) {
400
+ return strpos($haystack, $needle, $offset);
401
+ }
402
+ }
403
+
404
+ if ( !function_exists("mb_strrpos") ) {
405
+ function mb_strrpos($haystack, $needle, $offset = 0) {
406
+ return strrpos($haystack, $needle, $offset);
407
+ }
408
+ }
409
+
410
+ if ( !function_exists("mb_strtolower") ) {
411
+ function mb_strtolower( $str ) {
412
+ return strtolower($str);
413
+ }
414
+ }
415
+
416
+ if ( !function_exists("mb_strtoupper") ) {
417
+ function mb_strtoupper( $str ) {
418
+ return strtoupper($str);
419
+ }
420
+ }
421
+
422
+ if(!function_exists('mb_substr'))
423
+ {
424
+ function mb_substr($string, $start, $length = null, $encoding = 'iso-8859-1') {
425
+ if ( is_null($length) ) {
426
+ return substr($string, $start);
427
+ }
428
+ return substr($string, $start, $length);
429
+ }
430
+ }
431
+
432
+ if ( !function_exists("mb_substr_count") ) {
433
+ function mb_substr_count($haystack, $needle, $encoding = 'iso-8859-1') {
434
+ return substr_count($haystack, $needle);
435
+ }
436
+ }
437
+
438
+ if ( !function_exists("mb_encode_numericentity") ) {
439
+ function mb_encode_numericentity($str, $convmap, $encoding) {
440
+ return htmlspecialchars($str);
441
+ }
442
+ }
443
+
444
+ if ( !function_exists("mb_convert_case") ) {
445
+ function mb_convert_case($str, $mode = MB_CASE_UPPER, $encoding = array()) {
446
+ switch($mode) {
447
+ case MB_CASE_UPPER: return mb_strtoupper($str);
448
+ case MB_CASE_LOWER: return mb_strtolower($str);
449
+ case MB_CASE_TITLE: return ucwords(mb_strtolower($str));
450
+ default: return $str;
451
+ }
452
+ }
453
+ }
454
+
455
+ if ( !function_exists("mb_list_encodings") ) {
456
+ function mb_list_encodings() {
457
+ return array(
458
+ "ISO-8859-1",
459
+ "UTF-8",
460
+ "8bit",
461
+ );
462
+ }
463
+ }
464
+ }
465
+
466
+ /**
467
+ * Decoder for RLE8 compression in windows bitmaps
468
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
469
+ *
470
+ * @param string $str Data to decode
471
+ * @param integer $width Image width
472
+ *
473
+ * @return string
474
+ */
475
+ function rle8_decode ($str, $width){
476
+ $lineWidth = $width + (3 - ($width-1) % 4);
477
+ $out = '';
478
+ $cnt = strlen($str);
479
+
480
+ for ($i = 0; $i <$cnt; $i++) {
481
+ $o = ord($str[$i]);
482
+ switch ($o){
483
+ case 0: # ESCAPE
484
+ $i++;
485
+ switch (ord($str[$i])){
486
+ case 0: # NEW LINE
487
+ $padCnt = $lineWidth - strlen($out)%$lineWidth;
488
+ if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line
489
+ break;
490
+ case 1: # END OF FILE
491
+ $padCnt = $lineWidth - strlen($out)%$lineWidth;
492
+ if ($padCnt<$lineWidth) $out .= str_repeat(chr(0), $padCnt); # pad line
493
+ break 3;
494
+ case 2: # DELTA
495
+ $i += 2;
496
+ break;
497
+ default: # ABSOLUTE MODE
498
+ $num = ord($str[$i]);
499
+ for ($j = 0; $j < $num; $j++)
500
+ $out .= $str[++$i];
501
+ if ($num % 2) $i++;
502
+ }
503
+ break;
504
+ default:
505
+ $out .= str_repeat($str[++$i], $o);
506
+ }
507
+ }
508
+ return $out;
509
+ }
510
+
511
+ /**
512
+ * Decoder for RLE4 compression in windows bitmaps
513
+ * see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_6x0u.asp
514
+ *
515
+ * @param string $str Data to decode
516
+ * @param integer $width Image width
517
+ *
518
+ * @return string
519
+ */
520
+ function rle4_decode ($str, $width) {
521
+ $w = floor($width/2) + ($width % 2);
522
+ $lineWidth = $w + (3 - ( ($width-1) / 2) % 4);
523
+ $pixels = array();
524
+ $cnt = strlen($str);
525
+ $c = 0;
526
+
527
+ for ($i = 0; $i < $cnt; $i++) {
528
+ $o = ord($str[$i]);
529
+ switch ($o) {
530
+ case 0: # ESCAPE
531
+ $i++;
532
+ switch (ord($str[$i])){
533
+ case 0: # NEW LINE
534
+ while (count($pixels)%$lineWidth != 0) {
535
+ $pixels[] = 0;
536
+ }
537
+ break;
538
+ case 1: # END OF FILE
539
+ while (count($pixels)%$lineWidth != 0) {
540
+ $pixels[] = 0;
541
+ }
542
+ break 3;
543
+ case 2: # DELTA
544
+ $i += 2;
545
+ break;
546
+ default: # ABSOLUTE MODE
547
+ $num = ord($str[$i]);
548
+ for ($j = 0; $j < $num; $j++) {
549
+ if ($j%2 == 0) {
550
+ $c = ord($str[++$i]);
551
+ $pixels[] = ($c & 240)>>4;
552
+ }
553
+ else {
554
+ $pixels[] = $c & 15;
555
+ }
556
+ }
557
+
558
+ if ($num % 2 == 0) {
559
+ $i++;
560
+ }
561
+ }
562
+ break;
563
+ default:
564
+ $c = ord($str[++$i]);
565
+ for ($j = 0; $j < $o; $j++) {
566
+ $pixels[] = ($j%2==0 ? ($c & 240)>>4 : $c & 15);
567
+ }
568
+ }
569
+ }
570
+
571
+ $out = '';
572
+ if (count($pixels)%2) {
573
+ $pixels[] = 0;
574
+ }
575
+
576
+ $cnt = count($pixels)/2;
577
+
578
+ for ($i = 0; $i < $cnt; $i++) {
579
+ $out .= chr(16*$pixels[2*$i] + $pixels[2*$i+1]);
580
+ }
581
+
582
+ return $out;
583
+ }
584
+
585
+ if ( !function_exists("imagecreatefrombmp") ) {
586
+
587
+ /**
588
+ * Credit goes to mgutt
589
+ * http://www.programmierer-forum.de/function-imagecreatefrombmp-welche-variante-laeuft-t143137.htm
590
+ * Modified by Fabien Menager to support RGB555 BMP format
591
+ */
592
+ function imagecreatefrombmp($filename) {
593
+ // version 1.00
594
+ if (!($fh = fopen($filename, 'rb'))) {
595
+ trigger_error('imagecreatefrombmp: Can not open ' . $filename, E_USER_WARNING);
596
+ return false;
597
+ }
598
+
599
+ $bytes_read = 0;
600
+
601
+ // read file header
602
+ $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14));
603
+
604
+ // check for bitmap
605
+ if ($meta['type'] != 19778) {
606
+ trigger_error('imagecreatefrombmp: ' . $filename . ' is not a bitmap!', E_USER_WARNING);
607
+ return false;
608
+ }
609
+
610
+ // read image header
611
+ $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40));
612
+ $bytes_read += 40;
613
+
614
+ // read additional bitfield header
615
+ if ($meta['compression'] == 3) {
616
+ $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12));
617
+ $bytes_read += 12;
618
+ }
619
+
620
+ // set bytes and padding
621
+ $meta['bytes'] = $meta['bits'] / 8;
622
+ $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4)- floor($meta['width'] * $meta['bytes'] / 4)));
623
+ if ($meta['decal'] == 4) {
624
+ $meta['decal'] = 0;
625
+ }
626
+
627
+ // obtain imagesize
628
+ if ($meta['imagesize'] < 1) {
629
+ $meta['imagesize'] = $meta['filesize'] - $meta['offset'];
630
+ // in rare cases filesize is equal to offset so we need to read physical size
631
+ if ($meta['imagesize'] < 1) {
632
+ $meta['imagesize'] = @filesize($filename) - $meta['offset'];
633
+ if ($meta['imagesize'] < 1) {
634
+ trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING);
635
+ return false;
636
+ }
637
+ }
638
+ }
639
+
640
+ // calculate colors
641
+ $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors'];
642
+
643
+ // read color palette
644
+ $palette = array();
645
+ if ($meta['bits'] < 16) {
646
+ $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
647
+ // in rare cases the color value is signed
648
+ if ($palette[1] < 0) {
649
+ foreach ($palette as $i => $color) {
650
+ $palette[$i] = $color + 16777216;
651
+ }
652
+ }
653
+ }
654
+
655
+ // ignore extra bitmap headers
656
+ if ($meta['headersize'] > $bytes_read) {
657
+ fread($fh, $meta['headersize'] - $bytes_read);
658
+ }
659
+
660
+ // create gd image
661
+ $im = imagecreatetruecolor($meta['width'], $meta['height']);
662
+ $data = fread($fh, $meta['imagesize']);
663
+
664
+ // uncompress data
665
+ switch ($meta['compression']) {
666
+ case 1: $data = rle8_decode($data, $meta['width']); break;
667
+ case 2: $data = rle4_decode($data, $meta['width']); break;
668
+ }
669
+
670
+ $p = 0;
671
+ $vide = chr(0);
672
+ $y = $meta['height'] - 1;
673
+ $error = 'imagecreatefrombmp: ' . $filename . ' has not enough data!';
674
+
675
+ // loop through the image data beginning with the lower left corner
676
+ while ($y >= 0) {
677
+ $x = 0;
678
+ while ($x < $meta['width']) {
679
+ switch ($meta['bits']) {
680
+ case 32:
681
+ case 24:
682
+ if (!($part = substr($data, $p, 3 /*$meta['bytes']*/))) {
683
+ trigger_error($error, E_USER_WARNING);
684
+ return $im;
685
+ }
686
+ $color = unpack('V', $part . $vide);
687
+ break;
688
+ case 16:
689
+ if (!($part = substr($data, $p, 2 /*$meta['bytes']*/))) {
690
+ trigger_error($error, E_USER_WARNING);
691
+ return $im;
692
+ }
693
+ $color = unpack('v', $part);
694
+
695
+ if (empty($meta['rMask']) || $meta['rMask'] != 0xf800) {
696
+ $color[1] = (($color[1] & 0x7c00) >> 7) * 65536 + (($color[1] & 0x03e0) >> 2) * 256 + (($color[1] & 0x001f) << 3); // 555
697
+ }
698
+ else {
699
+ $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3); // 565
700
+ }
701
+ break;
702
+ case 8:
703
+ $color = unpack('n', $vide . substr($data, $p, 1));
704
+ $color[1] = $palette[ $color[1] + 1 ];
705
+ break;
706
+ case 4:
707
+ $color = unpack('n', $vide . substr($data, floor($p), 1));
708
+ $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F;
709
+ $color[1] = $palette[ $color[1] + 1 ];
710
+ break;
711
+ case 1:
712
+ $color = unpack('n', $vide . substr($data, floor($p), 1));
713
+ switch (($p * 8) % 8) {
714
+ case 0: $color[1] = $color[1] >> 7; break;
715
+ case 1: $color[1] = ($color[1] & 0x40) >> 6; break;
716
+ case 2: $color[1] = ($color[1] & 0x20) >> 5; break;
717
+ case 3: $color[1] = ($color[1] & 0x10) >> 4; break;
718
+ case 4: $color[1] = ($color[1] & 0x8 ) >> 3; break;
719
+ case 5: $color[1] = ($color[1] & 0x4 ) >> 2; break;
720
+ case 6: $color[1] = ($color[1] & 0x2 ) >> 1; break;
721
+ case 7: $color[1] = ($color[1] & 0x1 ); break;
722
+ }
723
+ $color[1] = $palette[ $color[1] + 1 ];
724
+ break;
725
+ default:
726
+ trigger_error('imagecreatefrombmp: ' . $filename . ' has ' . $meta['bits'] . ' bits and this is not supported!', E_USER_WARNING);
727
+ return false;
728
+ }
729
+ imagesetpixel($im, $x, $y, $color[1]);
730
+ $x++;
731
+ $p += $meta['bytes'];
732
+ }
733
+ $y--;
734
+ $p += $meta['decal'];
735
+ }
736
+ fclose($fh);
737
+ return $im;
738
+ }
739
+ }
740
+
741
+ /**
742
+ * getimagesize doesn't give a good size for 32bit BMP image v5
743
+ *
744
+ * @param string $filename
745
+ * @return array The same format as getimagesize($filename)
746
+ */
747
+ function dompdf_getimagesize($filename) {
748
+ static $cache = array();
749
+
750
+ if ( isset($cache[$filename]) ) {
751
+ return $cache[$filename];
752
+ }
753
+
754
+ list($width, $height, $type) = getimagesize($filename);
755
+
756
+ if ( $width == null || $height == null ) {
757
+ $data = file_get_contents($filename, null, null, 0, 26);
758
+
759
+ if ( substr($data, 0, 2) === "BM" ) {
760
+ $meta = unpack('vtype/Vfilesize/Vreserved/Voffset/Vheadersize/Vwidth/Vheight', $data);
761
+ $width = (int)$meta['width'];
762
+ $height = (int)$meta['height'];
763
+ $type = IMAGETYPE_BMP;
764
+ }
765
+ }
766
+
767
+ return $cache[$filename] = array($width, $height, $type);
768
+ }
769
+
770
+ /**
771
+ * Converts a CMYK color to RGB
772
+ *
773
+ * @param float|float[] $c
774
+ * @param float $m
775
+ * @param float $y
776
+ * @param float $k
777
+ *
778
+ * @return float[]
779
+ */
780
+ function cmyk_to_rgb($c, $m = null, $y = null, $k = null) {
781
+ if (is_array($c)) {
782
+ list($c, $m, $y, $k) = $c;
783
+ }
784
+
785
+ $c *= 255;
786
+ $m *= 255;
787
+ $y *= 255;
788
+ $k *= 255;
789
+
790
+ $r = (1 - round(2.55 * ($c+$k))) ;
791
+ $g = (1 - round(2.55 * ($m+$k))) ;
792
+ $b = (1 - round(2.55 * ($y+$k))) ;
793
+
794
+ if ($r < 0) $r = 0;
795
+ if ($g < 0) $g = 0;
796
+ if ($b < 0) $b = 0;
797
+
798
+ return array(
799
+ $r, $g, $b,
800
+ "r" => $r, "g" => $g, "b" => $b
801
+ );
802
+ }
803
+
804
+ function unichr($c) {
805
+ if ($c <= 0x7F) {
806
+ return chr($c);
807
+ }
808
+ else if ($c <= 0x7FF) {
809
+ return chr(0xC0 | $c >> 6) . chr(0x80 | $c & 0x3F);
810
+ }
811
+ else if ($c <= 0xFFFF) {
812
+ return chr(0xE0 | $c >> 12) . chr(0x80 | $c >> 6 & 0x3F)
813
+ . chr(0x80 | $c & 0x3F);
814
+ }
815
+ else if ($c <= 0x10FFFF) {
816
+ return chr(0xF0 | $c >> 18) . chr(0x80 | $c >> 12 & 0x3F)
817
+ . chr(0x80 | $c >> 6 & 0x3F)
818
+ . chr(0x80 | $c & 0x3F);
819
+ }
820
+ return false;
821
+ }
822
+
823
+ if ( !function_exists("date_default_timezone_get") ) {
824
+ function date_default_timezone_get() {
825
+ return "";
826
+ }
827
+
828
+ function date_default_timezone_set($timezone_identifier) {
829
+ return true;
830
+ }
831
+ }
832
+
833
+ /**
834
+ * Stores warnings in an array for display later
835
+ * This function allows warnings generated by the DomDocument parser
836
+ * and CSS loader ({@link Stylesheet}) to be captured and displayed
837
+ * later. Without this function, errors are displayed immediately and
838
+ * PDF streaming is impossible.
839
+ * @see http://www.php.net/manual/en/function.set-error_handler.php
840
+ *
841
+ * @param int $errno
842
+ * @param string $errstr
843
+ * @param string $errfile
844
+ * @param string $errline
845
+ *
846
+ * @throws DOMPDF_Exception
847
+ */
848
+ function record_warnings($errno, $errstr, $errfile, $errline) {
849
+
850
+ // Not a warning or notice
851
+ if ( !($errno & (E_WARNING | E_NOTICE | E_USER_NOTICE | E_USER_WARNING )) ) {
852
+ throw new DOMPDF_Exception($errstr . " $errno");
853
+ }
854
+
855
+ global $_dompdf_warnings;
856
+ global $_dompdf_show_warnings;
857
+
858
+ if ( $_dompdf_show_warnings ) {
859
+ echo $errstr . "\n";
860
+ }
861
+
862
+ $_dompdf_warnings[] = $errstr;
863
+ }
864
+
865
+ /**
866
+ * Print a useful backtrace
867
+ */
868
+ function bt() {
869
+ if ( php_sapi_name() !== "cli") {
870
+ echo "<pre>";
871
+ }
872
+
873
+ $bt = debug_backtrace();
874
+
875
+ array_shift($bt); // remove actual bt() call
876
+ echo "\n";
877
+
878
+ $i = 0;
879
+ foreach ($bt as $call) {
880
+ $file = basename($call["file"]) . " (" . $call["line"] . ")";
881
+ if ( isset($call["class"]) ) {
882
+ $func = $call["class"] . "->" . $call["function"] . "()";
883
+ }
884
+ else {
885
+ $func = $call["function"] . "()";
886
+ }
887
+
888
+ echo "#" . str_pad($i, 2, " ", STR_PAD_RIGHT) . ": " . str_pad($file.":", 42) . " $func\n";
889
+ $i++;
890
+ }
891
+ echo "\n";
892
+
893
+ if ( php_sapi_name() !== "cli") {
894
+ echo "</pre>";
895
+ }
896
+ }
897
+
898
+ /**
899
+ * Print debug messages
900
+ *
901
+ * @param string $type The type of debug messages to print
902
+ * @param string $msg The message to show
903
+ */
904
+ function dompdf_debug($type, $msg) {
905
+ global $_DOMPDF_DEBUG_TYPES, $_dompdf_show_warnings, $_dompdf_debug;
906
+ if ( isset($_DOMPDF_DEBUG_TYPES[$type]) && ($_dompdf_show_warnings || $_dompdf_debug) ) {
907
+ $arr = debug_backtrace();
908
+
909
+ echo basename($arr[0]["file"]) . " (" . $arr[0]["line"] ."): " . $arr[1]["function"] . ": ";
910
+ pre_r($msg);
911
+ }
912
+ }
913
+
914
+ if ( !function_exists("print_memusage") ) {
915
+ /**
916
+ * Dump memory usage
917
+ */
918
+ function print_memusage() {
919
+ global $memusage;
920
+ echo "Memory Usage\n";
921
+ $prev = 0;
922
+ $initial = reset($memusage);
923
+ echo str_pad("Initial:", 40) . $initial . "\n\n";
924
+
925
+ foreach ($memusage as $key=>$mem) {
926
+ $mem -= $initial;
927
+ echo str_pad("$key:" , 40);
928
+ echo str_pad("$mem", 12) . "(diff: " . ($mem - $prev) . ")\n";
929
+ $prev = $mem;
930
+ }
931
+
932
+ echo "\n" . str_pad("Total:", 40) . memory_get_usage() . "\n";
933
+ }
934
+ }
935
+
936
+ if ( !function_exists("enable_mem_profile") ) {
937
+ /**
938
+ * Initialize memory profiling code
939
+ */
940
+ function enable_mem_profile() {
941
+ global $memusage;
942
+ $memusage = array("Startup" => memory_get_usage());
943
+ register_shutdown_function("print_memusage");
944
+ }
945
+ }
946
+
947
+ if ( !function_exists("mark_memusage") ) {
948
+ /**
949
+ * Record the current memory usage
950
+ *
951
+ * @param string $location a meaningful location
952
+ */
953
+ function mark_memusage($location) {
954
+ global $memusage;
955
+ if ( isset($memusage) ) {
956
+ $memusage[$location] = memory_get_usage();
957
+ }
958
+ }
959
+ }
960
+
961
+ if ( !function_exists('sys_get_temp_dir')) {
962
+ /**
963
+ * Find the current system temporary directory
964
+ *
965
+ * @link http://us.php.net/manual/en/function.sys-get-temp-dir.php#85261
966
+ */
967
+ function sys_get_temp_dir() {
968
+ if (!empty($_ENV['TMP'])) {
969
+ return realpath($_ENV['TMP']);
970
+ }
971
+
972
+ if (!empty($_ENV['TMPDIR'])) {
973
+ return realpath( $_ENV['TMPDIR']);
974
+ }
975
+
976
+ if (!empty($_ENV['TEMP'])) {
977
+ return realpath( $_ENV['TEMP']);
978
+ }
979
+
980
+ $tempfile=tempnam(uniqid(rand(), true), '');
981
+ if (file_exists($tempfile)) {
982
+ unlink($tempfile);
983
+ return realpath(dirname($tempfile));
984
+ }
985
+ }
986
+ }
987
+
988
+ if ( function_exists("memory_get_peak_usage") ) {
989
+ function DOMPDF_memory_usage(){
990
+ return memory_get_peak_usage(true);
991
+ }
992
+ }
993
+ else if ( function_exists("memory_get_usage") ) {
994
+ function DOMPDF_memory_usage(){
995
+ return memory_get_usage(true);
996
+ }
997
+ }
998
+ else {
999
+ function DOMPDF_memory_usage(){
1000
+ return "N/A";
1001
+ }
1002
+ }
1003
+
1004
+ if ( function_exists("curl_init") ) {
1005
+ function DOMPDF_fetch_url($url, &$headers = null) {
1006
+ $ch = curl_init($url);
1007
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
1008
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
1009
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1010
+ curl_setopt($ch, CURLOPT_HEADER, true);
1011
+
1012
+ $data = curl_exec($ch);
1013
+ $raw_headers = substr($data, 0, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
1014
+ $headers = preg_split("/[\n\r]+/", trim($raw_headers));
1015
+ $data = substr($data, curl_getinfo($ch, CURLINFO_HEADER_SIZE));
1016
+ curl_close($ch);
1017
+
1018
+ return $data;
1019
+ }
1020
+ }
1021
+ else {
1022
+ function DOMPDF_fetch_url($url, &$headers = null) {
1023
+ $data = file_get_contents($url);
1024
+ $headers = $http_response_header;
1025
+
1026
+ return $data;
1027
+ }
1028
+ }
1029
+
1030
+ /**
1031
+ * Affect null to the unused objects
1032
+ * @param mixed $object
1033
+ */
1034
+ if ( PHP_VERSION_ID < 50300 ) {
1035
+ function clear_object(&$object) {
1036
+ if ( is_object($object) ) {
1037
+ foreach ($object as &$value) {
1038
+ clear_object($value);
1039
+ }
1040
+ }
1041
+
1042
+ $object = null;
1043
+ unset($object);
1044
+ }
1045
+ }
1046
+ else {
1047
+ function clear_object(&$object) {
1048
+ // void
1049
+ }
1050
+ }
dompdf/include/gd_adapter.cls.php ADDED
@@ -0,0 +1,840 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Image rendering interface
12
+ *
13
+ * Renders to an image format supported by GD (jpeg, gif, png, xpm).
14
+ * Not super-useful day-to-day but handy nonetheless
15
+ *
16
+ * @package dompdf
17
+ */
18
+ class GD_Adapter implements Canvas {
19
+ /**
20
+ * @var DOMPDF
21
+ */
22
+ private $_dompdf;
23
+
24
+ /**
25
+ * Resource handle for the image
26
+ *
27
+ * @var resource
28
+ */
29
+ private $_img;
30
+
31
+ /**
32
+ * Image width in pixels
33
+ *
34
+ * @var int
35
+ */
36
+ private $_width;
37
+
38
+ /**
39
+ * Image height in pixels
40
+ *
41
+ * @var int
42
+ */
43
+ private $_height;
44
+
45
+ /**
46
+ * Current page number
47
+ *
48
+ * @var int
49
+ */
50
+ private $_page_number;
51
+
52
+ /**
53
+ * Total number of pages
54
+ *
55
+ * @var int
56
+ */
57
+ private $_page_count;
58
+
59
+ /**
60
+ * Image antialias factor
61
+ *
62
+ * @var float
63
+ */
64
+ private $_aa_factor;
65
+
66
+ /**
67
+ * Allocated colors
68
+ *
69
+ * @var array
70
+ */
71
+ private $_colors;
72
+
73
+ /**
74
+ * Background color
75
+ *
76
+ * @var int
77
+ */
78
+ private $_bg_color;
79
+
80
+ /**
81
+ * Class constructor
82
+ *
83
+ * @param mixed $size The size of image to create: array(x1,y1,x2,y2) or "letter", "legal", etc.
84
+ * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
85
+ * @param DOMPDF $dompdf
86
+ * @param float $aa_factor Anti-aliasing factor, 1 for no AA
87
+ * @param array $bg_color Image background color: array(r,g,b,a), 0 <= r,g,b,a <= 1
88
+ */
89
+ function __construct($size, $orientation = "portrait", DOMPDF $dompdf, $aa_factor = 1.0, $bg_color = array(1,1,1,0) ) {
90
+
91
+ if ( !is_array($size) ) {
92
+ $size = strtolower($size);
93
+
94
+ if ( isset(CPDF_Adapter::$PAPER_SIZES[$size]) ) {
95
+ $size = CPDF_Adapter::$PAPER_SIZES[$size];
96
+ }
97
+ else {
98
+ $size = CPDF_Adapter::$PAPER_SIZES["letter"];
99
+ }
100
+ }
101
+
102
+ if ( strtolower($orientation) === "landscape" ) {
103
+ list($size[2],$size[3]) = array($size[3],$size[2]);
104
+ }
105
+
106
+ $this->_dompdf = $dompdf;
107
+
108
+ if ( $aa_factor < 1 ) {
109
+ $aa_factor = 1;
110
+ }
111
+
112
+ $this->_aa_factor = $aa_factor;
113
+
114
+ $size[2] *= $aa_factor;
115
+ $size[3] *= $aa_factor;
116
+
117
+ $this->_width = $size[2] - $size[0];
118
+ $this->_height = $size[3] - $size[1];
119
+
120
+ $this->_img = imagecreatetruecolor($this->_width, $this->_height);
121
+
122
+ if ( is_null($bg_color) || !is_array($bg_color) ) {
123
+ // Pure white bg
124
+ $bg_color = array(1,1,1,0);
125
+ }
126
+
127
+ $this->_bg_color = $this->_allocate_color($bg_color);
128
+ imagealphablending($this->_img, true);
129
+ imagesavealpha($this->_img, true);
130
+ imagefill($this->_img, 0, 0, $this->_bg_color);
131
+
132
+ }
133
+
134
+ function get_dompdf(){
135
+ return $this->_dompdf;
136
+ }
137
+
138
+ /**
139
+ * Return the GF image resource
140
+ *
141
+ * @return resource
142
+ */
143
+ function get_image() { return $this->_img; }
144
+
145
+ /**
146
+ * Return the image's width in pixels
147
+ *
148
+ * @return float
149
+ */
150
+ function get_width() { return $this->_width / $this->_aa_factor; }
151
+
152
+ /**
153
+ * Return the image's height in pixels
154
+ *
155
+ * @return float
156
+ */
157
+ function get_height() { return $this->_height / $this->_aa_factor; }
158
+
159
+ /**
160
+ * Returns the current page number
161
+ * @return int
162
+ */
163
+ function get_page_number() { return $this->_page_number; }
164
+
165
+ /**
166
+ * Returns the total number of pages in the document
167
+ * @return int
168
+ */
169
+ function get_page_count() { return $this->_page_count; }
170
+
171
+ /**
172
+ * Sets the current page number
173
+ *
174
+ * @param int $num
175
+ */
176
+ function set_page_number($num) { $this->_page_number = $num; }
177
+
178
+ /**
179
+ * Sets the page count
180
+ *
181
+ * @param int $count
182
+ */
183
+ function set_page_count($count) { $this->_page_count = $count; }
184
+
185
+ /**
186
+ * Sets the opacity
187
+ *
188
+ * @param $opacity
189
+ * @param $mode
190
+ */
191
+ function set_opacity($opacity, $mode = "Normal") {
192
+ // FIXME
193
+ }
194
+
195
+ /**
196
+ * Allocate a new color. Allocate with GD as needed and store
197
+ * previously allocated colors in $this->_colors.
198
+ *
199
+ * @param array $color The new current color
200
+ * @return int The allocated color
201
+ */
202
+ private function _allocate_color($color) {
203
+
204
+ if ( isset($color["c"]) ) {
205
+ $color = cmyk_to_rgb($color);
206
+ }
207
+
208
+ // Full opacity if no alpha set
209
+ if ( !isset($color[3]) )
210
+ $color[3] = 0;
211
+
212
+ list($r,$g,$b,$a) = $color;
213
+
214
+ $r *= 255;
215
+ $g *= 255;
216
+ $b *= 255;
217
+ $a *= 127;
218
+
219
+ // Clip values
220
+ $r = $r > 255 ? 255 : $r;
221
+ $g = $g > 255 ? 255 : $g;
222
+ $b = $b > 255 ? 255 : $b;
223
+ $a = $a > 127 ? 127 : $a;
224
+
225
+ $r = $r < 0 ? 0 : $r;
226
+ $g = $g < 0 ? 0 : $g;
227
+ $b = $b < 0 ? 0 : $b;
228
+ $a = $a < 0 ? 0 : $a;
229
+
230
+ $key = sprintf("#%02X%02X%02X%02X", $r, $g, $b, $a);
231
+
232
+ if ( isset($this->_colors[$key]) )
233
+ return $this->_colors[$key];
234
+
235
+ if ( $a != 0 )
236
+ $this->_colors[$key] = imagecolorallocatealpha($this->_img, $r, $g, $b, $a);
237
+ else
238
+ $this->_colors[$key] = imagecolorallocate($this->_img, $r, $g, $b);
239
+
240
+ return $this->_colors[$key];
241
+
242
+ }
243
+
244
+ /**
245
+ * Draws a line from x1,y1 to x2,y2
246
+ *
247
+ * See {@link Style::munge_color()} for the format of the color array.
248
+ * See {@link Cpdf::setLineStyle()} for a description of the format of the
249
+ * $style parameter (aka dash).
250
+ *
251
+ * @param float $x1
252
+ * @param float $y1
253
+ * @param float $x2
254
+ * @param float $y2
255
+ * @param array $color
256
+ * @param float $width
257
+ * @param array $style
258
+ */
259
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
260
+
261
+ // Scale by the AA factor
262
+ $x1 *= $this->_aa_factor;
263
+ $y1 *= $this->_aa_factor;
264
+ $x2 *= $this->_aa_factor;
265
+ $y2 *= $this->_aa_factor;
266
+ $width *= $this->_aa_factor;
267
+
268
+ $c = $this->_allocate_color($color);
269
+
270
+ // Convert the style array if required
271
+ if ( !is_null($style) ) {
272
+ $gd_style = array();
273
+
274
+ if ( count($style) == 1 ) {
275
+ for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
276
+ $gd_style[] = $c;
277
+ }
278
+
279
+ for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++) {
280
+ $gd_style[] = $this->_bg_color;
281
+ }
282
+
283
+ } else {
284
+
285
+ $i = 0;
286
+ foreach ($style as $length) {
287
+
288
+ if ( $i % 2 == 0 ) {
289
+ // 'On' pattern
290
+ for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++)
291
+ $gd_style[] = $c;
292
+
293
+ } else {
294
+ // Off pattern
295
+ for ($i = 0; $i < $style[0] * $this->_aa_factor; $i++)
296
+ $gd_style[] = $this->_bg_color;
297
+
298
+ }
299
+ $i++;
300
+ }
301
+ }
302
+
303
+ imagesetstyle($this->_img, $gd_style);
304
+ $c = IMG_COLOR_STYLED;
305
+ }
306
+
307
+ imagesetthickness($this->_img, $width);
308
+
309
+ imageline($this->_img, $x1, $y1, $x2, $y2, $c);
310
+
311
+ }
312
+
313
+ function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
314
+ // @todo
315
+ }
316
+
317
+ /**
318
+ * Draws a rectangle at x1,y1 with width w and height h
319
+ *
320
+ * See {@link Style::munge_color()} for the format of the color array.
321
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
322
+ * parameter (aka dash)
323
+ *
324
+ * @param float $x1
325
+ * @param float $y1
326
+ * @param float $w
327
+ * @param float $h
328
+ * @param array $color
329
+ * @param float $width
330
+ * @param array $style
331
+ */
332
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
333
+
334
+ // Scale by the AA factor
335
+ $x1 *= $this->_aa_factor;
336
+ $y1 *= $this->_aa_factor;
337
+ $w *= $this->_aa_factor;
338
+ $h *= $this->_aa_factor;
339
+
340
+ $c = $this->_allocate_color($color);
341
+
342
+ // Convert the style array if required
343
+ if ( !is_null($style) ) {
344
+ $gd_style = array();
345
+
346
+ foreach ($style as $length) {
347
+ for ($i = 0; $i < $length; $i++) {
348
+ $gd_style[] = $c;
349
+ }
350
+ }
351
+
352
+ imagesetstyle($this->_img, $gd_style);
353
+ $c = IMG_COLOR_STYLED;
354
+ }
355
+
356
+ imagesetthickness($this->_img, $width);
357
+
358
+ imagerectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c);
359
+
360
+ }
361
+
362
+ /**
363
+ * Draws a filled rectangle at x1,y1 with width w and height h
364
+ *
365
+ * See {@link Style::munge_color()} for the format of the color array.
366
+ *
367
+ * @param float $x1
368
+ * @param float $y1
369
+ * @param float $w
370
+ * @param float $h
371
+ * @param array $color
372
+ */
373
+ function filled_rectangle($x1, $y1, $w, $h, $color) {
374
+
375
+ // Scale by the AA factor
376
+ $x1 *= $this->_aa_factor;
377
+ $y1 *= $this->_aa_factor;
378
+ $w *= $this->_aa_factor;
379
+ $h *= $this->_aa_factor;
380
+
381
+ $c = $this->_allocate_color($color);
382
+
383
+ imagefilledrectangle($this->_img, $x1, $y1, $x1 + $w, $y1 + $h, $c);
384
+
385
+ }
386
+
387
+ /**
388
+ * Starts a clipping rectangle at x1,y1 with width w and height h
389
+ *
390
+ * @param float $x1
391
+ * @param float $y1
392
+ * @param float $w
393
+ * @param float $h
394
+ */
395
+ function clipping_rectangle($x1, $y1, $w, $h) {
396
+ // @todo
397
+ }
398
+
399
+ function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
400
+ // @todo
401
+ }
402
+
403
+ /**
404
+ * Ends the last clipping shape
405
+ */
406
+ function clipping_end() {
407
+ // @todo
408
+ }
409
+
410
+ function save() {
411
+ // @todo
412
+ }
413
+
414
+ function restore() {
415
+ // @todo
416
+ }
417
+
418
+ function rotate($angle, $x, $y) {
419
+ // @todo
420
+ }
421
+
422
+ function skew($angle_x, $angle_y, $x, $y) {
423
+ // @todo
424
+ }
425
+
426
+ function scale($s_x, $s_y, $x, $y) {
427
+ // @todo
428
+ }
429
+
430
+ function translate($t_x, $t_y) {
431
+ // @todo
432
+ }
433
+
434
+ function transform($a, $b, $c, $d, $e, $f) {
435
+ // @todo
436
+ }
437
+
438
+ /**
439
+ * Draws a polygon
440
+ *
441
+ * The polygon is formed by joining all the points stored in the $points
442
+ * array. $points has the following structure:
443
+ * <code>
444
+ * array(0 => x1,
445
+ * 1 => y1,
446
+ * 2 => x2,
447
+ * 3 => y2,
448
+ * ...
449
+ * );
450
+ * </code>
451
+ *
452
+ * See {@link Style::munge_color()} for the format of the color array.
453
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
454
+ * parameter (aka dash)
455
+ *
456
+ * @param array $points
457
+ * @param array $color
458
+ * @param float $width
459
+ * @param array $style
460
+ * @param bool $fill Fills the polygon if true
461
+ */
462
+ function polygon($points, $color, $width = null, $style = null, $fill = false) {
463
+
464
+ // Scale each point by the AA factor
465
+ foreach (array_keys($points) as $i)
466
+ $points[$i] *= $this->_aa_factor;
467
+
468
+ $c = $this->_allocate_color($color);
469
+
470
+ // Convert the style array if required
471
+ if ( !is_null($style) && !$fill ) {
472
+ $gd_style = array();
473
+
474
+ foreach ($style as $length) {
475
+ for ($i = 0; $i < $length; $i++) {
476
+ $gd_style[] = $c;
477
+ }
478
+ }
479
+
480
+ imagesetstyle($this->_img, $gd_style);
481
+ $c = IMG_COLOR_STYLED;
482
+ }
483
+
484
+ imagesetthickness($this->_img, $width);
485
+
486
+ if ( $fill )
487
+ imagefilledpolygon($this->_img, $points, count($points) / 2, $c);
488
+ else
489
+ imagepolygon($this->_img, $points, count($points) / 2, $c);
490
+
491
+ }
492
+
493
+ /**
494
+ * Draws a circle at $x,$y with radius $r
495
+ *
496
+ * See {@link Style::munge_color()} for the format of the color array.
497
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
498
+ * parameter (aka dash)
499
+ *
500
+ * @param float $x
501
+ * @param float $y
502
+ * @param float $r
503
+ * @param array $color
504
+ * @param float $width
505
+ * @param array $style
506
+ * @param bool $fill Fills the circle if true
507
+ */
508
+ function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
509
+
510
+ // Scale by the AA factor
511
+ $x *= $this->_aa_factor;
512
+ $y *= $this->_aa_factor;
513
+ $r *= $this->_aa_factor;
514
+
515
+ $c = $this->_allocate_color($color);
516
+
517
+ // Convert the style array if required
518
+ if ( !is_null($style) && !$fill ) {
519
+ $gd_style = array();
520
+
521
+ foreach ($style as $length) {
522
+ for ($i = 0; $i < $length; $i++) {
523
+ $gd_style[] = $c;
524
+ }
525
+ }
526
+
527
+ imagesetstyle($this->_img, $gd_style);
528
+ $c = IMG_COLOR_STYLED;
529
+ }
530
+
531
+ imagesetthickness($this->_img, $width);
532
+
533
+ if ( $fill )
534
+ imagefilledellipse($this->_img, $x, $y, $r, $r, $c);
535
+ else
536
+ imageellipse($this->_img, $x, $y, $r, $r, $c);
537
+
538
+ }
539
+
540
+ /**
541
+ * Add an image to the pdf.
542
+ * The image is placed at the specified x and y coordinates with the
543
+ * given width and height.
544
+ *
545
+ * @param string $img_url the path to the image
546
+ * @param float $x x position
547
+ * @param float $y y position
548
+ * @param int $w width (in pixels)
549
+ * @param int $h height (in pixels)
550
+ * @param string $resolution
551
+ *
552
+ * @return void
553
+ * @internal param string $img_type the type (e.g. extension) of the image
554
+ */
555
+ function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
556
+ $img_type = Image_Cache::detect_type($img_url);
557
+ $img_ext = Image_Cache::type_to_ext($img_type);
558
+
559
+ if ( !$img_ext ) {
560
+ return;
561
+ }
562
+
563
+ $func = "imagecreatefrom$img_ext";
564
+ $src = @$func($img_url);
565
+
566
+ if ( !$src ) {
567
+ return; // Probably should add to $_dompdf_errors or whatever here
568
+ }
569
+
570
+ // Scale by the AA factor
571
+ $x *= $this->_aa_factor;
572
+ $y *= $this->_aa_factor;
573
+
574
+ $w *= $this->_aa_factor;
575
+ $h *= $this->_aa_factor;
576
+
577
+ $img_w = imagesx($src);
578
+ $img_h = imagesy($src);
579
+
580
+ imagecopyresampled($this->_img, $src, $x, $y, 0, 0, $w, $h, $img_w, $img_h);
581
+
582
+ }
583
+
584
+ /**
585
+ * Writes text at the specified x and y coordinates
586
+ * See {@link Style::munge_color()} for the format of the color array.
587
+ *
588
+ * @param float $x
589
+ * @param float $y
590
+ * @param string $text the text to write
591
+ * @param string $font the font file to use
592
+ * @param float $size the font size, in points
593
+ * @param array $color
594
+ * @param float $word_spacing word spacing adjustment
595
+ * @param float $char_spacing
596
+ * @param float $angle Text angle
597
+ *
598
+ * @return void
599
+ */
600
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0.0, $char_spacing = 0.0, $angle = 0.0) {
601
+
602
+ // Scale by the AA factor
603
+ $x *= $this->_aa_factor;
604
+ $y *= $this->_aa_factor;
605
+ $size *= $this->_aa_factor;
606
+
607
+ $h = $this->get_font_height($font, $size);
608
+ $c = $this->_allocate_color($color);
609
+
610
+ $text = mb_encode_numericentity($text, array(0x0080, 0xff, 0, 0xff), 'UTF-8');
611
+
612
+ $font = $this->get_ttf_file($font);
613
+
614
+ // FIXME: word spacing
615
+ @imagettftext($this->_img, $size, $angle, $x, $y + $h, $c, $font, $text);
616
+
617
+ }
618
+
619
+ function javascript($code) {
620
+ // Not implemented
621
+ }
622
+
623
+ /**
624
+ * Add a named destination (similar to <a name="foo">...</a> in html)
625
+ *
626
+ * @param string $anchorname The name of the named destination
627
+ */
628
+ function add_named_dest($anchorname) {
629
+ // Not implemented
630
+ }
631
+
632
+ /**
633
+ * Add a link to the pdf
634
+ *
635
+ * @param string $url The url to link to
636
+ * @param float $x The x position of the link
637
+ * @param float $y The y position of the link
638
+ * @param float $width The width of the link
639
+ * @param float $height The height of the link
640
+ */
641
+ function add_link($url, $x, $y, $width, $height) {
642
+ // Not implemented
643
+ }
644
+
645
+ /**
646
+ * Add meta information to the PDF
647
+ *
648
+ * @param string $label label of the value (Creator, Producer, etc.)
649
+ * @param string $value the text to set
650
+ */
651
+ function add_info($label, $value) {
652
+ // N/A
653
+ }
654
+
655
+ function set_default_view($view, $options = array()) {
656
+ // N/A
657
+ }
658
+
659
+ /**
660
+ * Calculates text size, in points
661
+ *
662
+ * @param string $text the text to be sized
663
+ * @param string $font the desired font
664
+ * @param float $size the desired font size
665
+ * @param float $word_spacing word spacing, if any
666
+ * @param float $char_spacing char spacing, if any
667
+ *
668
+ * @return float
669
+ */
670
+ function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
671
+ $font = $this->get_ttf_file($font);
672
+
673
+ $text = mb_encode_numericentity($text, array(0x0080, 0xffff, 0, 0xffff), 'UTF-8');
674
+
675
+ // FIXME: word spacing
676
+ list($x1,,$x2) = @imagettfbbox($size, 0, $font, $text);
677
+ return $x2 - $x1;
678
+ }
679
+
680
+ function get_ttf_file($font) {
681
+ if ( strpos($font, '.ttf') === false )
682
+ $font .= ".ttf";
683
+
684
+ /*$filename = substr(strtolower(basename($font)), 0, -4);
685
+
686
+ if ( in_array($filename, DOMPDF::$native_fonts) ) {
687
+ return "arial.ttf";
688
+ }*/
689
+
690
+ return $font;
691
+ }
692
+
693
+ /**
694
+ * Calculates font height, in points
695
+ *
696
+ * @param string $font
697
+ * @param float $size
698
+ * @return float
699
+ */
700
+ function get_font_height($font, $size) {
701
+ $font = $this->get_ttf_file($font);
702
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
703
+
704
+ // FIXME: word spacing
705
+ list(,$y2,,,,$y1) = imagettfbbox($size, 0, $font, "MXjpqytfhl"); // Test string with ascenders, descenders and caps
706
+ return ($y2 - $y1) * $ratio;
707
+ }
708
+
709
+ function get_font_baseline($font, $size) {
710
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
711
+ return $this->get_font_height($font, $size) / $ratio;
712
+ }
713
+
714
+ /**
715
+ * Starts a new page
716
+ *
717
+ * Subsequent drawing operations will appear on the new page.
718
+ */
719
+ function new_page() {
720
+ $this->_page_number++;
721
+ $this->_page_count++;
722
+ }
723
+
724
+ function open_object(){
725
+ // N/A
726
+ }
727
+
728
+ function close_object(){
729
+ // N/A
730
+ }
731
+
732
+ function add_object(){
733
+ // N/A
734
+ }
735
+
736
+ function page_text(){
737
+ // N/A
738
+ }
739
+
740
+ /**
741
+ * Streams the image directly to the browser
742
+ *
743
+ * @param string $filename the name of the image file (ignored)
744
+ * @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
745
+ */
746
+ function stream($filename, $options = null) {
747
+
748
+ // Perform any antialiasing
749
+ if ( $this->_aa_factor != 1 ) {
750
+ $dst_w = $this->_width / $this->_aa_factor;
751
+ $dst_h = $this->_height / $this->_aa_factor;
752
+ $dst = imagecreatetruecolor($dst_w, $dst_h);
753
+ imagecopyresampled($dst, $this->_img, 0, 0, 0, 0,
754
+ $dst_w, $dst_h,
755
+ $this->_width, $this->_height);
756
+ } else {
757
+ $dst = $this->_img;
758
+ }
759
+
760
+ if ( !isset($options["type"]) )
761
+ $options["type"] = "png";
762
+
763
+ $type = strtolower($options["type"]);
764
+
765
+ header("Cache-Control: private");
766
+
767
+ switch ($type) {
768
+
769
+ case "jpg":
770
+ case "jpeg":
771
+ if ( !isset($options["quality"]) )
772
+ $options["quality"] = 75;
773
+
774
+ header("Content-type: image/jpeg");
775
+ imagejpeg($dst, '', $options["quality"]);
776
+ break;
777
+
778
+ case "png":
779
+ default:
780
+ header("Content-type: image/png");
781
+ imagepng($dst);
782
+ break;
783
+ }
784
+
785
+ if ( $this->_aa_factor != 1 )
786
+ imagedestroy($dst);
787
+ }
788
+
789
+ /**
790
+ * Returns the PNG as a string
791
+ *
792
+ * @param array $options associative array, 'type' => jpeg|jpg|png, 'quality' => 0 - 100 (jpeg only)
793
+ * @return string
794
+ */
795
+ function output($options = null) {
796
+
797
+ if ( $this->_aa_factor != 1 ) {
798
+ $dst_w = $this->_width / $this->_aa_factor;
799
+ $dst_h = $this->_height / $this->_aa_factor;
800
+ $dst = imagecreatetruecolor($dst_w, $dst_h);
801
+ imagecopyresampled($dst, $this->_img, 0, 0, 0, 0,
802
+ $dst_w, $dst_h,
803
+ $this->_width, $this->_height);
804
+ } else {
805
+ $dst = $this->_img;
806
+ }
807
+
808
+ if ( !isset($options["type"]) )
809
+ $options["type"] = "png";
810
+
811
+ $type = $options["type"];
812
+
813
+ ob_start();
814
+
815
+ switch ($type) {
816
+
817
+ case "jpg":
818
+ case "jpeg":
819
+ if ( !isset($options["quality"]) )
820
+ $options["quality"] = 75;
821
+
822
+ imagejpeg($dst, '', $options["quality"]);
823
+ break;
824
+
825
+ case "png":
826
+ default:
827
+ imagepng($dst);
828
+ break;
829
+ }
830
+
831
+ $image = ob_get_clean();
832
+
833
+ if ( $this->_aa_factor != 1 )
834
+ imagedestroy($dst);
835
+
836
+ return $image;
837
+ }
838
+
839
+
840
+ }
dompdf/include/image_cache.cls.php ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ error_reporting(E_ALL);
3
+
4
+ /**
5
+ * @package dompdf
6
+ * @link http://www.dompdf.com/
7
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
8
+ * @author Helmut Tischer <htischer@weihenstephan.org>
9
+ * @author Fabien Ménager <fabien.menager@gmail.com>
10
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
11
+ */
12
+
13
+ /**
14
+ * Static class that resolves image urls and downloads and caches
15
+ * remote images if required.
16
+ *
17
+ * @access private
18
+ * @package dompdf
19
+ */
20
+ class Image_Cache {
21
+
22
+ /**
23
+ * Array of downloaded images. Cached so that identical images are
24
+ * not needlessly downloaded.
25
+ *
26
+ * @var array
27
+ */
28
+ static protected $_cache = array();
29
+
30
+ /**
31
+ * The url to the "broken image" used when images can't be loade
32
+ *
33
+ * @var string
34
+ */
35
+ public static $broken_image;
36
+
37
+ /**
38
+ * Resolve and fetch an image for use.
39
+ *
40
+ * @param string $url The url of the image
41
+ * @param string $protocol Default protocol if none specified in $url
42
+ * @param string $host Default host if none specified in $url
43
+ * @param string $base_path Default path if none specified in $url
44
+ * @param DOMPDF $dompdf The DOMPDF instance
45
+ *
46
+ * @throws DOMPDF_Image_Exception
47
+ * @return array An array with two elements: The local path to the image and the image extension
48
+ */
49
+ static function resolve_url($url, $protocol, $host, $base_path, DOMPDF $dompdf) {
50
+ $parsed_url = explode_url($url);
51
+ $message = null;
52
+
53
+ $remote = ($protocol && $protocol !== "file://") || ($parsed_url['protocol'] != "");
54
+
55
+ $data_uri = strpos($parsed_url['protocol'], "data:") === 0;
56
+ $full_url = null;
57
+ $enable_remote = $dompdf->get_option("enable_remote");
58
+
59
+ try {
60
+
61
+ // Remote not allowed and is not DataURI
62
+ if ( !$enable_remote && $remote && !$data_uri ) {
63
+ throw new DOMPDF_Image_Exception("DOMPDF_ENABLE_REMOTE is set to FALSE");
64
+ }
65
+
66
+ // Remote allowed or DataURI
67
+ else if ( $enable_remote && $remote || $data_uri ) {
68
+ // Download remote files to a temporary directory
69
+ $full_url = build_url($protocol, $host, $base_path, $url);
70
+
71
+ // From cache
72
+ if ( isset(self::$_cache[$full_url]) ) {
73
+ $resolved_url = self::$_cache[$full_url];
74
+ }
75
+
76
+ // From remote
77
+ else {
78
+ $tmp_dir = $dompdf->get_option("temp_dir");
79
+ $resolved_url = tempnam($tmp_dir, "ca_dompdf_img_");
80
+ $image = "";
81
+
82
+ if ($data_uri) {
83
+ if ($parsed_data_uri = parse_data_uri($url)) {
84
+ $image = $parsed_data_uri['data'];
85
+ }
86
+ }
87
+ else {
88
+ set_error_handler("record_warnings");
89
+ $image = file_get_contents($full_url);
90
+ restore_error_handler();
91
+ }
92
+
93
+ // Image not found or invalid
94
+ if(strlen($image) == 0) {
95
+ /* try one last technique to display images */
96
+ if( !class_exists( 'WP_Http' ) ) {
97
+ include_once( ABSPATH . WPINC. '/class-http.php' );
98
+ }
99
+ $request = new WP_Http();
100
+ $result = $request->request($full_url);
101
+ if ( !is_wp_error($result) ) {
102
+ $image = $result['body'];
103
+ }
104
+ }
105
+
106
+ // Image not found or invalid
107
+ if ( strlen($image) == 0 ) {
108
+ $msg = ($datauri ? "Data-URI could not be parsed" : "Image not found");
109
+ throw new DOMPDF_Image_Exception($msg);
110
+ }
111
+
112
+ // Image found, put in cache and process
113
+ else {
114
+ //e.g. fetch.php?media=url.jpg&cache=1
115
+ //- Image file name might be one of the dynamic parts of the url, don't strip off!
116
+ //- a remote url does not need to have a file extension at all
117
+ //- local cached file does not have a matching file extension
118
+ //Therefore get image type from the content
119
+ file_put_contents($resolved_url, $image);
120
+ }
121
+ }
122
+ }
123
+
124
+ // Not remote, local image
125
+ else {
126
+ $resolved_url = build_url($protocol, $host, $base_path, $url);
127
+ }
128
+
129
+ // Check if the local file is readable
130
+ if ( !is_readable($resolved_url) || !filesize($resolved_url) ) {
131
+ throw new DOMPDF_Image_Exception("Image not readable or empty");
132
+ }
133
+
134
+ // Check is the file is an image
135
+ else {
136
+ list($width, $height, $type) = dompdf_getimagesize($resolved_url);
137
+
138
+ // Known image type
139
+ if ( $width && $height && in_array($type, array(IMAGETYPE_GIF, IMAGETYPE_PNG, IMAGETYPE_JPEG, IMAGETYPE_BMP)) ) {
140
+ //Don't put replacement image into cache - otherwise it will be deleted on cache cleanup.
141
+ //Only execute on successful caching of remote image.
142
+ if ( $enable_remote && $remote || $data_uri ) {
143
+ self::$_cache[$full_url] = $resolved_url;
144
+ }
145
+ }
146
+
147
+ // Unknown image type
148
+ else {
149
+ throw new DOMPDF_Image_Exception("Image type unknown");
150
+ }
151
+ }
152
+ }
153
+ catch(DOMPDF_Image_Exception $e) {
154
+ $resolved_url = self::$broken_image;
155
+ $type = IMAGETYPE_PNG;
156
+ $message = $e->getMessage()." \n $url";
157
+ }
158
+
159
+ return array($resolved_url, $type, $message);
160
+ }
161
+
162
+ /**
163
+ * Unlink all cached images (i.e. temporary images either downloaded
164
+ * or converted)
165
+ */
166
+ static function clear() {
167
+ if ( empty(self::$_cache) || DEBUGKEEPTEMP ) return;
168
+
169
+ foreach ( self::$_cache as $file ) {
170
+ if (DEBUGPNG) print "[clear unlink $file]";
171
+ unlink($file);
172
+ }
173
+
174
+ self::$_cache = array();
175
+ }
176
+
177
+ static function detect_type($file) {
178
+ list(, , $type) = dompdf_getimagesize($file);
179
+ return $type;
180
+ }
181
+
182
+ static function type_to_ext($type) {
183
+ $image_types = array(
184
+ IMAGETYPE_GIF => "gif",
185
+ IMAGETYPE_PNG => "png",
186
+ IMAGETYPE_JPEG => "jpeg",
187
+ IMAGETYPE_BMP => "bmp",
188
+ );
189
+
190
+ return (isset($image_types[$type]) ? $image_types[$type] : null);
191
+ }
192
+
193
+ static function is_broken($url) {
194
+ return $url === self::$broken_image;
195
+ }
196
+ }
197
+
198
+ Image_Cache::$broken_image = DOMPDF_LIB_DIR . "/res/broken_image.png";
dompdf/include/image_frame_decorator.cls.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Decorates frames for image layout and rendering
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Image_Frame_Decorator extends Frame_Decorator {
17
+
18
+ /**
19
+ * The path to the image file (note that remote images are
20
+ * downloaded locally to DOMPDF_TEMP_DIR).
21
+ *
22
+ * @var string
23
+ */
24
+ protected $_image_url;
25
+
26
+ /**
27
+ * The image's file error message
28
+ *
29
+ * @var string
30
+ */
31
+ protected $_image_msg;
32
+
33
+ /**
34
+ * Class constructor
35
+ *
36
+ * @param Frame $frame the frame to decorate
37
+ * @param DOMPDF $dompdf the document's dompdf object (required to resolve relative & remote urls)
38
+ */
39
+ function __construct(Frame $frame, DOMPDF $dompdf) {
40
+ parent::__construct($frame, $dompdf);
41
+ $url = $frame->get_node()->getAttribute("src");
42
+
43
+ $debug_png = $dompdf->get_option("debug_png");
44
+ if ($debug_png) print '[__construct '.$url.']';
45
+
46
+ list($this->_image_url, /*$type*/, $this->_image_msg) = Image_Cache::resolve_url(
47
+ $url,
48
+ $dompdf->get_protocol(),
49
+ $dompdf->get_host(),
50
+ $dompdf->get_base_path(),
51
+ $dompdf
52
+ );
53
+
54
+ if ( Image_Cache::is_broken($this->_image_url) &&
55
+ $alt = $frame->get_node()->getAttribute("alt") ) {
56
+ $style = $frame->get_style();
57
+ $style->width = (4/3)*Font_Metrics::get_text_width($alt, $style->font_family, $style->font_size, $style->word_spacing);
58
+ $style->height = Font_Metrics::get_font_height($style->font_family, $style->font_size);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Return the image's url
64
+ *
65
+ * @return string The url of this image
66
+ */
67
+ function get_image_url() {
68
+ return $this->_image_url;
69
+ }
70
+
71
+ /**
72
+ * Return the image's error message
73
+ *
74
+ * @return string The image's error message
75
+ */
76
+ function get_image_msg() {
77
+ return $this->_image_msg;
78
+ }
79
+
80
+ }
dompdf/include/image_frame_reflower.cls.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Image reflower class
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Image_Frame_Reflower extends Frame_Reflower {
17
+
18
+ function __construct(Image_Frame_Decorator $frame) {
19
+ parent::__construct($frame);
20
+ }
21
+
22
+ function reflow(Block_Frame_Decorator $block = null) {
23
+ $this->_frame->position();
24
+
25
+ //FLOAT
26
+ //$frame = $this->_frame;
27
+ //$page = $frame->get_root();
28
+
29
+ //$enable_css_float = $this->get_dompdf()->get_option("enable_css_float");
30
+ //if ($enable_css_float && $frame->get_style()->float !== "none" ) {
31
+ // $page->add_floating_frame($this);
32
+ //}
33
+ // Set the frame's width
34
+ $this->get_min_max_width();
35
+
36
+ if ( $block ) {
37
+ $block->add_frame_to_line($this->_frame);
38
+ }
39
+ }
40
+
41
+ function get_min_max_width() {
42
+ if (DEBUGPNG) {
43
+ // Determine the image's size. Time consuming. Only when really needed?
44
+ list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url());
45
+ print "get_min_max_width() ".
46
+ $this->_frame->get_style()->width.' '.
47
+ $this->_frame->get_style()->height.';'.
48
+ $this->_frame->get_parent()->get_style()->width." ".
49
+ $this->_frame->get_parent()->get_style()->height.";".
50
+ $this->_frame->get_parent()->get_parent()->get_style()->width.' '.
51
+ $this->_frame->get_parent()->get_parent()->get_style()->height.';'.
52
+ $img_width. ' '.
53
+ $img_height.'|' ;
54
+ }
55
+
56
+ $style = $this->_frame->get_style();
57
+
58
+ $width_forced = true;
59
+ $height_forced = true;
60
+
61
+ //own style auto or invalid value: use natural size in px
62
+ //own style value: ignore suffix text including unit, use given number as px
63
+ //own style %: walk up parent chain until found available space in pt; fill available space
64
+ //
65
+ //special ignored unit: e.g. 10ex: e treated as exponent; x ignored; 10e completely invalid ->like auto
66
+
67
+ $width = ($style->width > 0 ? $style->width : 0);
68
+ if ( is_percent($width) ) {
69
+ $t = 0.0;
70
+ for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
71
+ $f_style = $f->get_style();
72
+ $t = $f_style->length_in_pt($f_style->width);
73
+ if ($t != 0) {
74
+ break;
75
+ }
76
+ }
77
+ $width = ((float)rtrim($width,"%") * $t)/100; //maybe 0
78
+ } elseif ( !mb_strpos($width, 'pt') ) {
79
+ // Don't set image original size if "%" branch was 0 or size not given.
80
+ // Otherwise aspect changed on %/auto combination for width/height
81
+ // Resample according to px per inch
82
+ // See also List_Bullet_Image_Frame_Decorator::__construct
83
+ $width = $style->length_in_pt($width);
84
+ }
85
+
86
+ $height = ($style->height > 0 ? $style->height : 0);
87
+ if ( is_percent($height) ) {
88
+ $t = 0.0;
89
+ for ($f = $this->_frame->get_parent(); $f; $f = $f->get_parent()) {
90
+ $f_style = $f->get_style();
91
+ $t = $f_style->length_in_pt($f_style->height);
92
+ if ($t != 0) {
93
+ break;
94
+ }
95
+ }
96
+ $height = ((float)rtrim($height,"%") * $t)/100; //maybe 0
97
+ } elseif ( !mb_strpos($height, 'pt') ) {
98
+ // Don't set image original size if "%" branch was 0 or size not given.
99
+ // Otherwise aspect changed on %/auto combination for width/height
100
+ // Resample according to px per inch
101
+ // See also List_Bullet_Image_Frame_Decorator::__construct
102
+ $height = $style->length_in_pt($height);
103
+ }
104
+
105
+ if ($width == 0 || $height == 0) {
106
+ // Determine the image's size. Time consuming. Only when really needed!
107
+ list($img_width, $img_height) = dompdf_getimagesize($this->_frame->get_image_url());
108
+
109
+ // don't treat 0 as error. Can be downscaled or can be catched elsewhere if image not readable.
110
+ // Resample according to px per inch
111
+ // See also List_Bullet_Image_Frame_Decorator::__construct
112
+ if ($width == 0 && $height == 0) {
113
+ $dpi = $this->_frame->get_dompdf()->get_option("dpi");
114
+ $width = (float)($img_width * 72) / $dpi;
115
+ $height = (float)($img_height * 72) / $dpi;
116
+ $width_forced = false;
117
+ $height_forced = false;
118
+ } elseif ($height == 0 && $width != 0) {
119
+ $height_forced = false;
120
+ $height = ($width / $img_width) * $img_height; //keep aspect ratio
121
+ } elseif ($width == 0 && $height != 0) {
122
+ $width_forced = false;
123
+ $width = ($height / $img_height) * $img_width; //keep aspect ratio
124
+ }
125
+ }
126
+
127
+ // Handle min/max width/height
128
+ if ( $style->min_width !== "none" ||
129
+ $style->max_width !== "none" ||
130
+ $style->min_height !== "none" ||
131
+ $style->max_height !== "none" ) {
132
+
133
+ list(/*$x*/, /*$y*/, $w, $h) = $this->_frame->get_containing_block();
134
+
135
+ $min_width = $style->length_in_pt($style->min_width, $w);
136
+ $max_width = $style->length_in_pt($style->max_width, $w);
137
+ $min_height = $style->length_in_pt($style->min_height, $h);
138
+ $max_height = $style->length_in_pt($style->max_height, $h);
139
+
140
+ if ( $max_width !== "none" && $width > $max_width ) {
141
+ if ( !$height_forced ) {
142
+ $height *= $max_width / $width;
143
+ }
144
+
145
+ $width = $max_width;
146
+ }
147
+
148
+ if ( $min_width !== "none" && $width < $min_width ) {
149
+ if ( !$height_forced ) {
150
+ $height *= $min_width / $width;
151
+ }
152
+
153
+ $width = $min_width;
154
+ }
155
+
156
+ if ( $max_height !== "none" && $height > $max_height ) {
157
+ if ( !$width_forced ) {
158
+ $width *= $max_height / $height;
159
+ }
160
+
161
+ $height = $max_height;
162
+ }
163
+
164
+ if ( $min_height !== "none" && $height < $min_height ) {
165
+ if ( !$width_forced ) {
166
+ $width *= $min_height / $height;
167
+ }
168
+
169
+ $height = $min_height;
170
+ }
171
+ }
172
+
173
+ if (DEBUGPNG) print $width.' '.$height.';';
174
+
175
+ $style->width = $width . "pt";
176
+ $style->height = $height . "pt";
177
+
178
+ $style->min_width = "none";
179
+ $style->max_width = "none";
180
+ $style->min_height = "none";
181
+ $style->max_height = "none";
182
+
183
+ return array( $width, $width, "min" => $width, "max" => $width);
184
+
185
+ }
186
+ }
dompdf/include/image_renderer.cls.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Image renderer
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Image_Renderer extends Block_Renderer {
17
+
18
+ function render(Frame $frame) {
19
+ // Render background & borders
20
+ $style = $frame->get_style();
21
+ $cb = $frame->get_containing_block();
22
+ list($x, $y, $w, $h) = $frame->get_border_box();
23
+
24
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
25
+
26
+ list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
27
+
28
+ $has_border_radius = $tl + $tr + $br + $bl > 0;
29
+
30
+ if ( $has_border_radius ) {
31
+ $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
32
+ }
33
+
34
+ if ( ($bg = $style->background_color) !== "transparent" ) {
35
+ $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
36
+ }
37
+
38
+ if ( ($url = $style->background_image) && $url !== "none" ) {
39
+ $this->_background_image($url, $x, $y, $w, $h, $style);
40
+ }
41
+
42
+ if ( $has_border_radius ) {
43
+ $this->_canvas->clipping_end();
44
+ }
45
+
46
+ $this->_render_border($frame);
47
+ $this->_render_outline($frame);
48
+
49
+ list($x, $y) = $frame->get_padding_box();
50
+
51
+ $x += $style->length_in_pt($style->padding_left, $cb["w"]);
52
+ $y += $style->length_in_pt($style->padding_top, $cb["h"]);
53
+
54
+ $w = $style->length_in_pt($style->width, $cb["w"]);
55
+ $h = $style->length_in_pt($style->height, $cb["h"]);
56
+
57
+ if ( $has_border_radius ) {
58
+ list($wt, $wr, $wb, $wl) = array(
59
+ $style->border_top_width,
60
+ $style->border_right_width,
61
+ $style->border_bottom_width,
62
+ $style->border_left_width,
63
+ );
64
+
65
+ // we have to get the "inner" radius
66
+ if ( $tl > 0 ) {
67
+ $tl -= ($wt + $wl) / 2;
68
+ }
69
+ if ( $tr > 0 ) {
70
+ $tr -= ($wt + $wr) / 2;
71
+ }
72
+ if ( $br > 0 ) {
73
+ $br -= ($wb + $wr) / 2;
74
+ }
75
+ if ( $bl > 0 ) {
76
+ $bl -= ($wb + $wl) / 2;
77
+ }
78
+
79
+ $this->_canvas->clipping_roundrectangle( $x, $y, $w, $h, $tl, $tr, $br, $bl );
80
+ }
81
+
82
+ $src = $frame->get_image_url();
83
+ $alt = null;
84
+
85
+ if ( Image_Cache::is_broken($src) &&
86
+ $alt = $frame->get_node()->getAttribute("alt") ) {
87
+ $font = $style->font_family;
88
+ $size = $style->font_size;
89
+ $spacing = $style->word_spacing;
90
+ $this->_canvas->text($x, $y, $alt,
91
+ $font, $size,
92
+ $style->color, $spacing);
93
+ }
94
+ else {
95
+ $this->_canvas->image( $src, $x, $y, $w, $h, $style->image_resolution);
96
+ }
97
+
98
+ if ( $has_border_radius ) {
99
+ $this->_canvas->clipping_end();
100
+ }
101
+
102
+ if ( $msg = $frame->get_image_msg() ) {
103
+ $parts = preg_split("/\s*\n\s*/", $msg);
104
+ $height = 10;
105
+ $_y = $alt ? $y+$h-count($parts)*$height : $y;
106
+
107
+ foreach($parts as $i => $_part) {
108
+ $this->_canvas->text($x, $_y + $i*$height, $_part, "times", $height*0.8, array(0.5, 0.5, 0.5));
109
+ }
110
+ }
111
+
112
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
113
+ $this->_debug_layout($frame->get_border_box(), "blue");
114
+ if (DEBUG_LAYOUT_PADDINGBOX) {
115
+ $this->_debug_layout($frame->get_padding_box(), "blue", array(0.5, 0.5));
116
+ }
117
+ }
118
+ }
119
+ }
dompdf/include/inline_frame_decorator.cls.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Decorates frames for inline layout
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Inline_Frame_Decorator extends Frame_Decorator {
17
+
18
+ function __construct(Frame $frame, DOMPDF $dompdf) { parent::__construct($frame, $dompdf); }
19
+
20
+ function split(Frame $frame = null, $force_pagebreak = false) {
21
+
22
+ if ( is_null($frame) ) {
23
+ $this->get_parent()->split($this, $force_pagebreak);
24
+ return;
25
+ }
26
+
27
+ if ( $frame->get_parent() !== $this )
28
+ throw new DOMPDF_Exception("Unable to split: frame is not a child of this one.");
29
+
30
+ $split = $this->copy( $this->_frame->get_node()->cloneNode() );
31
+ $this->get_parent()->insert_child_after($split, $this);
32
+
33
+ // Unset the current node's right style properties
34
+ $style = $this->_frame->get_style();
35
+ $style->margin_right = 0;
36
+ $style->padding_right = 0;
37
+ $style->border_right_width = 0;
38
+
39
+ // Unset the split node's left style properties since we don't want them
40
+ // to propagate
41
+ $style = $split->get_style();
42
+ $style->margin_left = 0;
43
+ $style->padding_left = 0;
44
+ $style->border_left_width = 0;
45
+
46
+ //On continuation of inline element on next line,
47
+ //don't repeat non-vertically repeatble background images
48
+ //See e.g. in testcase image_variants, long desriptions
49
+ if ( ($url = $style->background_image) && $url !== "none"
50
+ && ($repeat = $style->background_repeat) && $repeat !== "repeat" && $repeat !== "repeat-y"
51
+ ) {
52
+ $style->background_image = "none";
53
+ }
54
+
55
+ // Add $frame and all following siblings to the new split node
56
+ $iter = $frame;
57
+ while ($iter) {
58
+ $frame = $iter;
59
+ $iter = $iter->get_next_sibling();
60
+ $frame->reset();
61
+ $split->append_child($frame);
62
+ }
63
+
64
+ $page_breaks = array("always", "left", "right");
65
+ $frame_style = $frame->get_style();
66
+ if( $force_pagebreak ||
67
+ in_array($frame_style->page_break_before, $page_breaks) ||
68
+ in_array($frame_style->page_break_after, $page_breaks) ) {
69
+
70
+ $this->get_parent()->split($split, true);
71
+ }
72
+ }
73
+
74
+ }
dompdf/include/inline_frame_reflower.cls.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows inline frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Inline_Frame_Reflower extends Frame_Reflower {
16
+
17
+ function __construct(Frame $frame) { parent::__construct($frame); }
18
+
19
+ //........................................................................
20
+
21
+ function reflow(Block_Frame_Decorator $block = null) {
22
+ $frame = $this->_frame;
23
+
24
+ // Check if a page break is forced
25
+ $page = $frame->get_root();
26
+ $page->check_forced_page_break($frame);
27
+
28
+ if ( $page->is_full() )
29
+ return;
30
+
31
+ $style = $frame->get_style();
32
+
33
+ // Generated content
34
+ $this->_set_content();
35
+
36
+ $frame->position();
37
+
38
+ $cb = $frame->get_containing_block();
39
+
40
+ // Add our margin, padding & border to the first and last children
41
+ if ( ($f = $frame->get_first_child()) && $f instanceof Text_Frame_Decorator ) {
42
+ $f_style = $f->get_style();
43
+ $f_style->margin_left = $style->margin_left;
44
+ $f_style->padding_left = $style->padding_left;
45
+ $f_style->border_left = $style->border_left;
46
+ }
47
+
48
+ if ( ($l = $frame->get_last_child()) && $l instanceof Text_Frame_Decorator ) {
49
+ $l_style = $l->get_style();
50
+ $l_style->margin_right = $style->margin_right;
51
+ $l_style->padding_right = $style->padding_right;
52
+ $l_style->border_right = $style->border_right;
53
+ }
54
+
55
+ if ( $block ) {
56
+ $block->add_frame_to_line($this->_frame);
57
+ }
58
+
59
+ // Set the containing blocks and reflow each child. The containing
60
+ // block is not changed by line boxes.
61
+ foreach ( $frame->get_children() as $child ) {
62
+ $child->set_containing_block($cb);
63
+ $child->reflow($block);
64
+ }
65
+ }
66
+ }
dompdf/include/inline_positioner.cls.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Positions inline frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Inline_Positioner extends Positioner {
16
+
17
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
18
+
19
+ //........................................................................
20
+
21
+ function position() {
22
+ /**
23
+ * Find our nearest block level parent and access its lines property.
24
+ * @var Block_Frame_Decorator
25
+ */
26
+ $p = $this->_frame->find_block_parent();
27
+
28
+ // Debugging code:
29
+
30
+ // pre_r("\nPositioning:");
31
+ // pre_r("Me: " . $this->_frame->get_node()->nodeName . " (" . spl_object_hash($this->_frame->get_node()) . ")");
32
+ // pre_r("Parent: " . $p->get_node()->nodeName . " (" . spl_object_hash($p->get_node()) . ")");
33
+
34
+ // End debugging
35
+
36
+ if ( !$p )
37
+ throw new DOMPDF_Exception("No block-level parent found. Not good.");
38
+
39
+ $f = $this->_frame;
40
+
41
+ $cb = $f->get_containing_block();
42
+ $line = $p->get_current_line_box();
43
+
44
+ // Skip the page break if in a fixed position element
45
+ $is_fixed = false;
46
+ while($f = $f->get_parent()) {
47
+ if($f->get_style()->position === "fixed") {
48
+ $is_fixed = true;
49
+ break;
50
+ }
51
+ }
52
+
53
+ $f = $this->_frame;
54
+
55
+ if ( !$is_fixed && $f->get_parent() &&
56
+ $f->get_parent() instanceof Inline_Frame_Decorator &&
57
+ $f->is_text_node() ) {
58
+
59
+ $min_max = $f->get_reflower()->get_min_max_width();
60
+
61
+ // If the frame doesn't fit in the current line, a line break occurs
62
+ if ( $min_max["min"] > ($cb["w"] - $line->left - $line->w - $line->right) ) {
63
+ $p->add_line();
64
+ }
65
+ }
66
+
67
+ $f->set_position($cb["x"] + $line->w, $line->y);
68
+
69
+ }
70
+ }
dompdf/include/inline_renderer.cls.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Renders inline frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Inline_Renderer extends Abstract_Renderer {
16
+
17
+ //........................................................................
18
+
19
+ function render(Frame $frame) {
20
+ $style = $frame->get_style();
21
+
22
+ if ( !$frame->get_first_child() )
23
+ return; // No children, no service
24
+
25
+ // Draw the left border if applicable
26
+ $bp = $style->get_border_properties();
27
+ $widths = array($style->length_in_pt($bp["top"]["width"]),
28
+ $style->length_in_pt($bp["right"]["width"]),
29
+ $style->length_in_pt($bp["bottom"]["width"]),
30
+ $style->length_in_pt($bp["left"]["width"]));
31
+
32
+ // Draw the background & border behind each child. To do this we need
33
+ // to figure out just how much space each child takes:
34
+ list($x, $y) = $frame->get_first_child()->get_position();
35
+ $w = null;
36
+ $h = 0;
37
+ // $x += $widths[3];
38
+ // $y += $widths[0];
39
+
40
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
41
+
42
+ $first_row = true;
43
+
44
+ foreach ($frame->get_children() as $child) {
45
+ list($child_x, $child_y, $child_w, $child_h) = $child->get_padding_box();
46
+
47
+ if ( !is_null($w) && $child_x < $x + $w ) {
48
+ //This branch seems to be supposed to being called on the first part
49
+ //of an inline html element, and the part after the if clause for the
50
+ //parts after a line break.
51
+ //But because $w initially mostly is 0, and gets updated only on the next
52
+ //round, this seem to be never executed and the common close always.
53
+
54
+ // The next child is on another line. Draw the background &
55
+ // borders on this line.
56
+
57
+ // Background:
58
+ if ( ($bg = $style->background_color) !== "transparent" )
59
+ $this->_canvas->filled_rectangle( $x, $y, $w, $h, $bg);
60
+
61
+ if ( ($url = $style->background_image) && $url !== "none" ) {
62
+ $this->_background_image($url, $x, $y, $w, $h, $style);
63
+ }
64
+
65
+ // If this is the first row, draw the left border
66
+ if ( $first_row ) {
67
+
68
+ if ( $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $bp["left"]["width"] > 0 ) {
69
+ $method = "_border_" . $bp["left"]["style"];
70
+ $this->$method($x, $y, $h + $widths[0] + $widths[2], $bp["left"]["color"], $widths, "left");
71
+ }
72
+ $first_row = false;
73
+ }
74
+
75
+ // Draw the top & bottom borders
76
+ if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $bp["top"]["width"] > 0 ) {
77
+ $method = "_border_" . $bp["top"]["style"];
78
+ $this->$method($x, $y, $w + $widths[1] + $widths[3], $bp["top"]["color"], $widths, "top");
79
+ }
80
+
81
+ if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $bp["bottom"]["width"] > 0 ) {
82
+ $method = "_border_" . $bp["bottom"]["style"];
83
+ $this->$method($x, $y + $h + $widths[0] + $widths[2], $w + $widths[1] + $widths[3], $bp["bottom"]["color"], $widths, "bottom");
84
+ }
85
+
86
+ // Handle anchors & links
87
+ $link_node = null;
88
+ if ( $frame->get_node()->nodeName === "a" ) {
89
+ $link_node = $frame->get_node();
90
+ }
91
+ else if ( $frame->get_parent()->get_node()->nodeName === "a" ){
92
+ $link_node = $frame->get_parent()->get_node();
93
+ }
94
+
95
+ if ( $link_node && $href = $link_node->getAttribute("href") ) {
96
+ $this->_canvas->add_link($href, $x, $y, $w, $h);
97
+ }
98
+
99
+ $x = $child_x;
100
+ $y = $child_y;
101
+ $w = $child_w;
102
+ $h = $child_h;
103
+ continue;
104
+ }
105
+
106
+ if ( is_null($w) )
107
+ $w = $child_w;
108
+ else
109
+ $w += $child_w;
110
+
111
+ $h = max($h, $child_h);
112
+
113
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_INLINE) {
114
+ $this->_debug_layout($child->get_border_box(), "blue");
115
+ if (DEBUG_LAYOUT_PADDINGBOX) {
116
+ $this->_debug_layout($child->get_padding_box(), "blue", array(0.5, 0.5));
117
+ }
118
+ }
119
+ }
120
+
121
+
122
+ // Handle the last child
123
+ if ( ($bg = $style->background_color) !== "transparent" )
124
+ $this->_canvas->filled_rectangle( $x + $widths[3], $y + $widths[0], $w, $h, $bg);
125
+
126
+ //On continuation lines (after line break) of inline elements, the style got copied.
127
+ //But a non repeatable background image should not be repeated on the next line.
128
+ //But removing the background image above has never an effect, and removing it below
129
+ //removes it always, even on the initial line.
130
+ //Need to handle it elsewhere, e.g. on certain ...clone()... usages.
131
+ // Repeat not given: default is Style::__construct
132
+ // ... && (!($repeat = $style->background_repeat) || $repeat === "repeat" ...
133
+ //different position? $this->_background_image($url, $x, $y, $w, $h, $style);
134
+ if ( ($url = $style->background_image) && $url !== "none" )
135
+ $this->_background_image($url, $x + $widths[3], $y + $widths[0], $w, $h, $style);
136
+
137
+ // Add the border widths
138
+ $w += $widths[1] + $widths[3];
139
+ $h += $widths[0] + $widths[2];
140
+
141
+ // make sure the border and background start inside the left margin
142
+ $left_margin = $style->length_in_pt($style->margin_left);
143
+ $x += $left_margin;
144
+
145
+ // If this is the first row, draw the left border too
146
+ if ( $first_row && $bp["left"]["style"] !== "none" && $bp["left"]["color"] !== "transparent" && $widths[3] > 0 ) {
147
+ $method = "_border_" . $bp["left"]["style"];
148
+ $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left");
149
+ }
150
+
151
+ // Draw the top & bottom borders
152
+ if ( $bp["top"]["style"] !== "none" && $bp["top"]["color"] !== "transparent" && $widths[0] > 0 ) {
153
+ $method = "_border_" . $bp["top"]["style"];
154
+ $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top");
155
+ }
156
+
157
+ if ( $bp["bottom"]["style"] !== "none" && $bp["bottom"]["color"] !== "transparent" && $widths[2] > 0 ) {
158
+ $method = "_border_" . $bp["bottom"]["style"];
159
+ $this->$method($x, $y + $h, $w, $bp["bottom"]["color"], $widths, "bottom");
160
+ }
161
+
162
+ // pre_var_dump(get_class($frame->get_next_sibling()));
163
+ // $last_row = get_class($frame->get_next_sibling()) !== 'Inline_Frame_Decorator';
164
+ // Draw the right border if this is the last row
165
+ if ( $bp["right"]["style"] !== "none" && $bp["right"]["color"] !== "transparent" && $widths[1] > 0 ) {
166
+ $method = "_border_" . $bp["right"]["style"];
167
+ $this->$method($x + $w, $y, $h, $bp["right"]["color"], $widths, "right");
168
+ }
169
+
170
+ // Only two levels of links frames
171
+ $link_node = null;
172
+ if ( $frame->get_node()->nodeName === "a" ) {
173
+ $link_node = $frame->get_node();
174
+
175
+ if ( ($name = $link_node->getAttribute("name")) || ($name = $link_node->getAttribute("id")) ) {
176
+ $this->_canvas->add_named_dest($name);
177
+ }
178
+ }
179
+
180
+ if ( $frame->get_parent() && $frame->get_parent()->get_node()->nodeName === "a" ){
181
+ $link_node = $frame->get_parent()->get_node();
182
+ }
183
+
184
+ // Handle anchors & links
185
+ if ( $link_node ) {
186
+ if ( $href = $link_node->getAttribute("href") )
187
+ $this->_canvas->add_link($href, $x, $y, $w, $h);
188
+ }
189
+ }
190
+ }
dompdf/include/javascript_embedder.cls.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Fabien Ménager <fabien.menager@gmail.com>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Embeds Javascript into the PDF document
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Javascript_Embedder {
16
+
17
+ /**
18
+ * @var DOMPDF
19
+ */
20
+ protected $_dompdf;
21
+
22
+ function __construct(DOMPDF $dompdf) {
23
+ $this->_dompdf = $dompdf;
24
+ }
25
+
26
+ function insert($script) {
27
+ $this->_dompdf->get_canvas()->javascript($script);
28
+ }
29
+
30
+ function render(Frame $frame) {
31
+ if ( !$this->_dompdf->get_option("enable_javascript") ) {
32
+ return;
33
+ }
34
+
35
+ $this->insert($frame->get_node()->nodeValue);
36
+ }
37
+ }
dompdf/include/line_box.cls.php ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Fabien Ménager <fabien.menager@gmail.com>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * The line box class
11
+ *
12
+ * This class represents a line box
13
+ * http://www.w3.org/TR/CSS2/visuren.html#line-box
14
+ *
15
+ * @access protected
16
+ * @package dompdf
17
+ */
18
+ class Line_Box {
19
+
20
+ /**
21
+ * @var Block_Frame_Decorator
22
+ */
23
+ protected $_block_frame;
24
+
25
+ /**
26
+ * @var Frame[]
27
+ */
28
+ protected $_frames = array();
29
+
30
+ /**
31
+ * @var integer
32
+ */
33
+ public $wc = 0;
34
+
35
+ /**
36
+ * @var float
37
+ */
38
+ public $y = null;
39
+
40
+ /**
41
+ * @var float
42
+ */
43
+ public $w = 0.0;
44
+
45
+ /**
46
+ * @var float
47
+ */
48
+ public $h = 0.0;
49
+
50
+ /**
51
+ * @var float
52
+ */
53
+ public $left = 0.0;
54
+
55
+ /**
56
+ * @var float
57
+ */
58
+ public $right = 0.0;
59
+
60
+ /**
61
+ * @var Frame
62
+ */
63
+ public $tallest_frame = null;
64
+
65
+ /**
66
+ * @var bool[]
67
+ */
68
+ public $floating_blocks = array();
69
+
70
+ /**
71
+ * @var bool
72
+ */
73
+ public $br = false;
74
+
75
+ /**
76
+ * Class constructor
77
+ *
78
+ * @param Block_Frame_Decorator $frame the Block_Frame_Decorator containing this line
79
+ */
80
+ function __construct(Block_Frame_Decorator $frame, $y = 0) {
81
+ $this->_block_frame = $frame;
82
+ $this->_frames = array();
83
+ $this->y = $y;
84
+
85
+ $this->get_float_offsets();
86
+ }
87
+
88
+ /**
89
+ * Returns the floating elements inside the first floating parent
90
+ *
91
+ * @param Page_Frame_Decorator $root
92
+ *
93
+ * @return Frame[]
94
+ */
95
+ function get_floats_inside(Page_Frame_Decorator $root) {
96
+ $floating_frames = $root->get_floating_frames();
97
+
98
+ if ( count($floating_frames) == 0 ) {
99
+ return $floating_frames;
100
+ }
101
+
102
+ // Find nearest floating element
103
+ $p = $this->_block_frame;
104
+ while( $p->get_style()->float === "none" ) {
105
+ $parent = $p->get_parent();
106
+
107
+ if ( !$parent ) {
108
+ break;
109
+ }
110
+
111
+ $p = $parent;
112
+ }
113
+
114
+ if ( $p == $root ) {
115
+ return $floating_frames;
116
+ }
117
+
118
+ $parent = $p;
119
+
120
+ $childs = array();
121
+
122
+ foreach ($floating_frames as $_floating) {
123
+ $p = $_floating->get_parent();
124
+
125
+ while (($p = $p->get_parent()) && $p !== $parent);
126
+
127
+ if ( $p ) {
128
+ $childs[] = $p;
129
+ }
130
+ }
131
+
132
+ return $childs;
133
+ }
134
+
135
+ function get_float_offsets() {
136
+ $enable_css_float = $this->_block_frame->get_dompdf()->get_option("enable_css_float");
137
+ if ( !$enable_css_float ) {
138
+ return;
139
+ }
140
+
141
+ static $anti_infinite_loop = 500; // FIXME smelly hack
142
+
143
+ $reflower = $this->_block_frame->get_reflower();
144
+
145
+ if ( !$reflower ) {
146
+ return;
147
+ }
148
+
149
+ $cb_w = null;
150
+
151
+ $block = $this->_block_frame;
152
+ $root = $block->get_root();
153
+
154
+ if ( !$root ) {
155
+ return;
156
+ }
157
+
158
+ $floating_frames = $this->get_floats_inside($root);
159
+
160
+ foreach ( $floating_frames as $child_key => $floating_frame ) {
161
+ $id = $floating_frame->get_id();
162
+
163
+ if ( isset($this->floating_blocks[$id]) ) {
164
+ continue;
165
+ }
166
+
167
+ $floating_style = $floating_frame->get_style();
168
+ $float = $floating_style->float;
169
+
170
+ $floating_width = $floating_frame->get_margin_width();
171
+
172
+ if (!$cb_w) {
173
+ $cb_w = $floating_frame->get_containing_block("w");
174
+ }
175
+
176
+ $line_w = $this->get_width();
177
+
178
+ if ( !$floating_frame->_float_next_line && ($cb_w <= $line_w + $floating_width) && ($cb_w > $line_w) ) {
179
+ $floating_frame->_float_next_line = true;
180
+ continue;
181
+ }
182
+
183
+ // If the child is still shifted by the floating element
184
+ if ( $anti_infinite_loop-- > 0 &&
185
+ $floating_frame->get_position("y") + $floating_frame->get_margin_height() > $this->y &&
186
+ $block->get_position("x") + $block->get_margin_width() > $floating_frame->get_position("x")
187
+ ) {
188
+ if ( $float === "left" )
189
+ $this->left += $floating_width;
190
+ else
191
+ $this->right += $floating_width;
192
+
193
+ $this->floating_blocks[$id] = true;
194
+ }
195
+
196
+ // else, the floating element won't shift anymore
197
+ else {
198
+ $root->remove_floating_frame($child_key);
199
+ }
200
+ }
201
+ }
202
+
203
+ /**
204
+ * @return float
205
+ */
206
+ function get_width(){
207
+ return $this->left + $this->w + $this->right;
208
+ }
209
+
210
+ /**
211
+ * @return Block_Frame_Decorator
212
+ */
213
+ function get_block_frame() {
214
+ return $this->_block_frame;
215
+ }
216
+
217
+ /**
218
+ * @return Frame[]
219
+ */
220
+ function &get_frames() {
221
+ return $this->_frames;
222
+ }
223
+
224
+ /**
225
+ * @param Frame $frame
226
+ */
227
+ function add_frame(Frame $frame) {
228
+ $this->_frames[] = $frame;
229
+ }
230
+
231
+ function __toString(){
232
+ $props = array("wc", "y", "w", "h", "left", "right", "br");
233
+ $s = "";
234
+ foreach($props as $prop) {
235
+ $s .= "$prop: ".$this->$prop."\n";
236
+ }
237
+ $s .= count($this->_frames)." frames\n";
238
+ return $s;
239
+ }
240
+ /*function __get($prop) {
241
+ if (!isset($this->{"_$prop"})) return;
242
+ return $this->{"_$prop"};
243
+ }*/
244
+ }
245
+
246
+ /*
247
+ class LineBoxList implements Iterator {
248
+ private $_p = 0;
249
+ private $_lines = array();
250
+
251
+ }
252
+ */
dompdf/include/list_bullet_frame_decorator.cls.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Decorates frames for list bullet rendering
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class List_Bullet_Frame_Decorator extends Frame_Decorator {
17
+
18
+ const BULLET_PADDING = 1; // Distance from bullet to text in pt
19
+ // As fraction of font size (including descent). See also DECO_THICKNESS.
20
+ const BULLET_THICKNESS = 0.04; // Thickness of bullet outline. Screen: 0.08, print: better less, e.g. 0.04
21
+ const BULLET_DESCENT = 0.3; //descent of font below baseline. Todo: Guessed for now.
22
+ const BULLET_SIZE = 0.35; // bullet diameter. For now 0.5 of font_size without descent.
23
+
24
+ static $BULLET_TYPES = array("disc", "circle", "square");
25
+
26
+ //........................................................................
27
+
28
+ function __construct(Frame $frame, DOMPDF $dompdf) {
29
+ parent::__construct($frame, $dompdf);
30
+ }
31
+
32
+ function get_margin_width() {
33
+ $style = $this->_frame->get_style();
34
+
35
+ // Small hack to prevent extra indenting of list text on list_style_position === "inside"
36
+ // and on suppressed bullet
37
+ if ( $style->list_style_position === "outside" ||
38
+ $style->list_style_type === "none" ) {
39
+ return 0;
40
+ }
41
+
42
+ return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING;
43
+ }
44
+
45
+ //hits only on "inset" lists items, to increase height of box
46
+ function get_margin_height() {
47
+ $style = $this->_frame->get_style();
48
+
49
+ if ( $style->list_style_type === "none" ) {
50
+ return 0;
51
+ }
52
+
53
+ return $style->get_font_size() * self::BULLET_SIZE + 2 * self::BULLET_PADDING;
54
+ }
55
+
56
+ function get_width() {
57
+ return $this->get_margin_height();
58
+ }
59
+
60
+ function get_height() {
61
+ return $this->get_margin_height();
62
+ }
63
+
64
+ //........................................................................
65
+ }
dompdf/include/list_bullet_frame_reflower.cls.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows list bullets
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class List_Bullet_Frame_Reflower extends Frame_Reflower {
16
+
17
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
18
+
19
+ //........................................................................
20
+
21
+ function reflow(Block_Frame_Decorator $block = null) {
22
+ $style = $this->_frame->get_style();
23
+
24
+ $style->width = $this->_frame->get_width();
25
+ $this->_frame->position();
26
+
27
+ if ( $style->list_style_position === "inside" ) {
28
+ $p = $this->_frame->find_block_parent();
29
+ $p->add_frame_to_line($this->_frame);
30
+ }
31
+
32
+ }
33
+ }
dompdf/include/list_bullet_image_frame_decorator.cls.php ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Decorates frames for list bullets with custom images
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class List_Bullet_Image_Frame_Decorator extends Frame_Decorator {
17
+
18
+ /**
19
+ * The underlying image frame
20
+ *
21
+ * @var Image_Frame_Decorator
22
+ */
23
+ protected $_img;
24
+
25
+ /**
26
+ * The image's width in pixels
27
+ *
28
+ * @var int
29
+ */
30
+ protected $_width;
31
+
32
+ /**
33
+ * The image's height in pixels
34
+ *
35
+ * @var int
36
+ */
37
+ protected $_height;
38
+
39
+ /**
40
+ * Class constructor
41
+ *
42
+ * @param Frame $frame the bullet frame to decorate
43
+ * @param DOMPDF $dompdf the document's dompdf object
44
+ */
45
+ function __construct(Frame $frame, DOMPDF $dompdf) {
46
+ $style = $frame->get_style();
47
+ $url = $style->list_style_image;
48
+ $frame->get_node()->setAttribute("src", $url);
49
+ $this->_img = new Image_Frame_Decorator($frame, $dompdf);
50
+ parent::__construct($this->_img, $dompdf);
51
+ list($width, $height) = dompdf_getimagesize($this->_img->get_image_url());
52
+
53
+ // Resample the bullet image to be consistent with 'auto' sized images
54
+ // See also Image_Frame_Reflower::get_min_max_width
55
+ // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
56
+ $dpi = $this->_dompdf->get_option("dpi");
57
+ $this->_width = ((float)rtrim($width, "px") * 72) / $dpi;
58
+ $this->_height = ((float)rtrim($height, "px") * 72) / $dpi;
59
+
60
+ //If an image is taller as the containing block/box, the box should be extended.
61
+ //Neighbour elements are overwriting the overlapping image areas.
62
+ //Todo: Where can the box size be extended?
63
+ //Code below has no effect.
64
+ //See block_frame_reflower _calculate_restricted_height
65
+ //See generated_frame_reflower, Dompdf:render() "list-item", "-dompdf-list-bullet"S.
66
+ //Leave for now
67
+ //if ($style->min_height < $this->_height ) {
68
+ // $style->min_height = $this->_height;
69
+ //}
70
+ //$style->height = "auto";
71
+ }
72
+
73
+ /**
74
+ * Return the bullet's width
75
+ *
76
+ * @return int
77
+ */
78
+ function get_width() {
79
+ //ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator
80
+ //for proper alignment of bullet image and text. Allow image to not fitting on left border.
81
+ //This controls the distance between bullet image and text
82
+ //return $this->_width;
83
+ return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE +
84
+ 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
85
+ }
86
+
87
+ /**
88
+ * Return the bullet's height
89
+ *
90
+ * @return int
91
+ */
92
+ function get_height() {
93
+ //based on image height
94
+ return $this->_height;
95
+ }
96
+
97
+ /**
98
+ * Override get_margin_width
99
+ *
100
+ * @return int
101
+ */
102
+ function get_margin_width() {
103
+ //ignore image width, use same width as on predefined bullet List_Bullet_Frame_Decorator
104
+ //for proper alignment of bullet image and text. Allow image to not fitting on left border.
105
+ //This controls the extra indentation of text to make room for the bullet image.
106
+ //Here use actual image size, not predefined bullet size
107
+ //return $this->_frame->get_style()->get_font_size()*List_Bullet_Frame_Decorator::BULLET_SIZE +
108
+ // 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
109
+
110
+ // Small hack to prevent indenting of list text
111
+ // Image Might not exist, then position like on list_bullet_frame_decorator fallback to none.
112
+ if ( $this->_frame->get_style()->list_style_position === "outside" ||
113
+ $this->_width == 0)
114
+ return 0;
115
+ //This aligns the "inside" image position with the text.
116
+ //The text starts to the right of the image.
117
+ //Between the image and the text there is an added margin of image width.
118
+ //Where this comes from is unknown.
119
+ //The corresponding List_Bullet_Frame_Decorator sets a smaller margin. bullet size?
120
+ return $this->_width + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
121
+ }
122
+
123
+ /**
124
+ * Override get_margin_height()
125
+ *
126
+ * @return int
127
+ */
128
+ function get_margin_height() {
129
+ //Hits only on "inset" lists items, to increase height of box
130
+ //based on image height
131
+ return $this->_height + 2 * List_Bullet_Frame_Decorator::BULLET_PADDING;
132
+ }
133
+
134
+ /**
135
+ * Return image url
136
+ *
137
+ * @return string
138
+ */
139
+ function get_image_url() {
140
+ return $this->_img->get_image_url();
141
+ }
142
+
143
+ }
dompdf/include/list_bullet_positioner.cls.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Positions list bullets
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class List_Bullet_Positioner extends Positioner {
17
+
18
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
19
+
20
+ //........................................................................
21
+
22
+ function position() {
23
+
24
+ // Bullets & friends are positioned an absolute distance to the left of
25
+ // the content edge of their parent element
26
+ $cb = $this->_frame->get_containing_block();
27
+
28
+ // Note: this differs from most frames in that we must position
29
+ // ourselves after determining our width
30
+ $x = $cb["x"] - $this->_frame->get_width();
31
+
32
+ $p = $this->_frame->find_block_parent();
33
+
34
+ $y = $p->get_current_line_box()->y;
35
+
36
+ // This is a bit of a hack...
37
+ $n = $this->_frame->get_next_sibling();
38
+ if ( $n ) {
39
+ $style = $n->get_style();
40
+ $line_height = $style->length_in_pt($style->line_height, $style->get_font_size());
41
+ $offset = $style->length_in_pt($line_height, $n->get_containing_block("h")) - $this->_frame->get_height();
42
+ $y += $offset / 2;
43
+ }
44
+
45
+ // Now the position is the left top of the block which should be marked with the bullet.
46
+ // We tried to find out the y of the start of the first text character within the block.
47
+ // But the top margin/padding does not fit, neither from this nor from the next sibling
48
+ // The "bit of a hack" above does not work also.
49
+
50
+ // Instead let's position the bullet vertically centered to the block which should be marked.
51
+ // But for get_next_sibling() the get_containing_block is all zero, and for find_block_parent()
52
+ // the get_containing_block is paper width and the entire list as height.
53
+
54
+ // if ($p) {
55
+ // //$cb = $n->get_containing_block();
56
+ // $cb = $p->get_containing_block();
57
+ // $y += $cb["h"]/2;
58
+ // print 'cb:'.$cb["x"].':'.$cb["y"].':'.$cb["w"].':'.$cb["h"].':';
59
+ // }
60
+
61
+ // Todo:
62
+ // For now give up on the above. Use Guesswork with font y-pos in the middle of the line spacing
63
+
64
+ /*$style = $p->get_style();
65
+ $font_size = $style->get_font_size();
66
+ $line_height = $style->length_in_pt($style->line_height, $font_size);
67
+ $y += ($line_height - $font_size) / 2; */
68
+
69
+ //Position is x-end y-top of character position of the bullet.
70
+ $this->_frame->set_position($x, $y);
71
+
72
+ }
73
+ }
dompdf/include/list_bullet_renderer.cls.php ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Renders list bullets
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class List_Bullet_Renderer extends Abstract_Renderer {
17
+ static function get_counter_chars($type) {
18
+ static $cache = array();
19
+
20
+ if ( isset($cache[$type]) ) {
21
+ return $cache[$type];
22
+ }
23
+
24
+ $uppercase = false;
25
+ $text = "";
26
+
27
+ switch ($type) {
28
+ case "decimal-leading-zero":
29
+ case "decimal":
30
+ case "1":
31
+ return "0123456789";
32
+
33
+ case "upper-alpha":
34
+ case "upper-latin":
35
+ case "A":
36
+ $uppercase = true;
37
+ case "lower-alpha":
38
+ case "lower-latin":
39
+ case "a":
40
+ $text = "abcdefghijklmnopqrstuvwxyz";
41
+ break;
42
+
43
+ case "upper-roman":
44
+ case "I":
45
+ $uppercase = true;
46
+ case "lower-roman":
47
+ case "i":
48
+ $text = "ivxlcdm";
49
+ break;
50
+
51
+ case "lower-greek":
52
+ for($i = 0; $i < 24; $i++) {
53
+ $text .= unichr($i+944);
54
+ }
55
+ break;
56
+ }
57
+
58
+ if ( $uppercase ) {
59
+ $text = strtoupper($text);
60
+ }
61
+
62
+ return $cache[$type] = "$text.";
63
+ }
64
+
65
+ /**
66
+ * @param integer $n
67
+ * @param string $type
68
+ * @param integer $pad
69
+ *
70
+ * @return string
71
+ */
72
+ private function make_counter($n, $type, $pad = null){
73
+ $n = intval($n);
74
+ $text = "";
75
+ $uppercase = false;
76
+
77
+ switch ($type) {
78
+ case "decimal-leading-zero":
79
+ case "decimal":
80
+ case "1":
81
+ if ($pad)
82
+ $text = str_pad($n, $pad, "0", STR_PAD_LEFT);
83
+ else
84
+ $text = $n;
85
+ break;
86
+
87
+ case "upper-alpha":
88
+ case "upper-latin":
89
+ case "A":
90
+ $uppercase = true;
91
+ case "lower-alpha":
92
+ case "lower-latin":
93
+ case "a":
94
+ $text = chr( ($n % 26) + ord('a') - 1);
95
+ break;
96
+
97
+ case "upper-roman":
98
+ case "I":
99
+ $uppercase = true;
100
+ case "lower-roman":
101
+ case "i":
102
+ $text = dec2roman($n);
103
+ break;
104
+
105
+ case "lower-greek":
106
+ $text = unichr($n + 944);
107
+ break;
108
+ }
109
+
110
+ if ( $uppercase ) {
111
+ $text = strtoupper($text);
112
+ }
113
+
114
+ return "$text.";
115
+ }
116
+
117
+ function render(Frame $frame) {
118
+ $style = $frame->get_style();
119
+ $font_size = $style->get_font_size();
120
+ $line_height = $style->length_in_pt($style->line_height, $frame->get_containing_block("w"));
121
+
122
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
123
+
124
+ // Handle list-style-image
125
+ // If list style image is requested but missing, fall back to predefined types
126
+ if ( $style->list_style_image !== "none" &&
127
+ !Image_Cache::is_broken($img = $frame->get_image_url())) {
128
+
129
+ list($x,$y) = $frame->get_position();
130
+
131
+ //For expected size and aspect, instead of box size, use image natural size scaled to DPI.
132
+ // Resample the bullet image to be consistent with 'auto' sized images
133
+ // See also Image_Frame_Reflower::get_min_max_width
134
+ // Tested php ver: value measured in px, suffix "px" not in value: rtrim unnecessary.
135
+ //$w = $frame->get_width();
136
+ //$h = $frame->get_height();
137
+ list($width, $height) = dompdf_getimagesize($img);
138
+ $dpi = $this->_dompdf->get_option("dpi");
139
+ $w = ((float)rtrim($width, "px") * 72) / $dpi;
140
+ $h = ((float)rtrim($height, "px") * 72) / $dpi;
141
+
142
+ $x -= $w;
143
+ $y -= ($line_height - $font_size)/2; //Reverse hinting of list_bullet_positioner
144
+
145
+ $this->_canvas->image( $img, $x, $y, $w, $h);
146
+
147
+ } else {
148
+
149
+ $bullet_style = $style->list_style_type;
150
+
151
+ $fill = false;
152
+
153
+ switch ($bullet_style) {
154
+
155
+ default:
156
+ case "disc":
157
+ $fill = true;
158
+
159
+ case "circle":
160
+ list($x,$y) = $frame->get_position();
161
+ $r = ($font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE /*-List_Bullet_Frame_Decorator::BULLET_THICKNESS*/ ))/2;
162
+ $x -= $font_size*(List_Bullet_Frame_Decorator::BULLET_SIZE/2);
163
+ $y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT))/2;
164
+ $o = $font_size*List_Bullet_Frame_Decorator::BULLET_THICKNESS;
165
+ $this->_canvas->circle($x, $y, $r, $style->color, $o, null, $fill);
166
+ break;
167
+
168
+ case "square":
169
+ list($x, $y) = $frame->get_position();
170
+ $w = $font_size*List_Bullet_Frame_Decorator::BULLET_SIZE;
171
+ $x -= $w;
172
+ $y += ($font_size*(1-List_Bullet_Frame_Decorator::BULLET_DESCENT-List_Bullet_Frame_Decorator::BULLET_SIZE))/2;
173
+ $this->_canvas->filled_rectangle($x, $y, $w, $w, $style->color);
174
+ break;
175
+
176
+ case "decimal-leading-zero":
177
+ case "decimal":
178
+ case "lower-alpha":
179
+ case "lower-latin":
180
+ case "lower-roman":
181
+ case "lower-greek":
182
+ case "upper-alpha":
183
+ case "upper-latin":
184
+ case "upper-roman":
185
+ case "1": // HTML 4.0 compatibility
186
+ case "a":
187
+ case "i":
188
+ case "A":
189
+ case "I":
190
+ $li = $frame->get_parent();
191
+
192
+ $pad = null;
193
+ if ( $bullet_style === "decimal-leading-zero" ) {
194
+ $pad = strlen($li->get_parent()->get_node()->getAttribute("dompdf-children-count"));
195
+ }
196
+
197
+ $index = $frame->get_node()->getAttribute("dompdf-counter");
198
+ $text = $this->make_counter($index, $bullet_style, $pad);
199
+
200
+ if ( trim($text) == "" ) {
201
+ return;
202
+ }
203
+
204
+ $spacing = 0;
205
+ $font_family = $style->font_family;
206
+
207
+ $line = $li->get_containing_line();
208
+ list($x, $y) = array($frame->get_position("x"), $line->y);
209
+
210
+ $x -= Font_Metrics::get_text_width($text, $font_family, $font_size, $spacing);
211
+
212
+ // Take line-height into account
213
+ $line_height = $style->line_height;
214
+ $y += ($line_height - $font_size) / 4; // FIXME I thought it should be 2, but 4 gives better results
215
+
216
+ $this->_canvas->text($x, $y, $text,
217
+ $font_family, $font_size,
218
+ $style->color, $spacing);
219
+
220
+ case "none":
221
+ break;
222
+ }
223
+ }
224
+ }
225
+ }
dompdf/include/null_frame_decorator.cls.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Dummy decorator
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Null_Frame_Decorator extends Frame_Decorator {
16
+
17
+ function __construct(Frame $frame, DOMPDF $dompdf) {
18
+ parent::__construct($frame, $dompdf);
19
+ $style = $this->_frame->get_style();
20
+ $style->width = 0;
21
+ $style->height = 0;
22
+ $style->margin = 0;
23
+ $style->padding = 0;
24
+ }
25
+
26
+ }
dompdf/include/null_frame_reflower.cls.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Dummy reflower
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Null_Frame_Reflower extends Frame_Reflower {
16
+
17
+ function __construct(Frame $frame) { parent::__construct($frame); }
18
+
19
+ function reflow(Block_Frame_Decorator $block = null) { return; }
20
+
21
+ }
dompdf/include/null_positioner.cls.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Dummy positioner
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Null_Positioner extends Positioner {
16
+
17
+ function __construct(Frame_Decorator $frame) {
18
+ parent::__construct($frame);
19
+ }
20
+
21
+ function position() { return; }
22
+
23
+ }
dompdf/include/page_cache.cls.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Caches individual rendered PDF pages
11
+ *
12
+ * Not totally implemented yet. Use at your own risk ;)
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ * @static
17
+ */
18
+ class Page_Cache {
19
+
20
+ const DB_USER = "dompdf_page_cache";
21
+ const DB_PASS = "some meaningful password";
22
+ const DB_NAME = "dompdf_page_cache";
23
+
24
+ static private $__connection = null;
25
+
26
+ static function init() {
27
+ if ( is_null(self::$__connection) ) {
28
+ $con_str = "host=" . DB_HOST .
29
+ " dbname=" . self::DB_NAME .
30
+ " user=" . self::DB_USER .
31
+ " password=" . self::DB_PASS;
32
+
33
+ if ( !self::$__connection = pg_connect($con_str) )
34
+ throw new Exception("Database connection failed.");
35
+ }
36
+ }
37
+
38
+ function __construct() { throw new Exception("Can not create instance of Page_Class. Class is static."); }
39
+
40
+ private static function __query($sql) {
41
+ if ( !($res = pg_query(self::$__connection, $sql)) )
42
+ throw new Exception(pg_last_error(self::$__connection));
43
+ return $res;
44
+ }
45
+
46
+ static function store_page($id, $page_num, $data) {
47
+ $where = "WHERE id='" . pg_escape_string($id) . "' AND ".
48
+ "page_num=". pg_escape_string($page_num);
49
+
50
+ $res = self::__query("SELECT timestamp FROM page_cache ". $where);
51
+
52
+ $row = pg_fetch_assoc($res);
53
+
54
+ if ( $row )
55
+ self::__query("UPDATE page_cache SET data='" . pg_escape_string($data) . "' " . $where);
56
+ else
57
+ self::__query("INSERT INTO page_cache (id, page_num, data) VALUES ('" . pg_escape_string($id) . "', ".
58
+ pg_escape_string($page_num) . ", ".
59
+ "'". pg_escape_string($data) . "')");
60
+
61
+ }
62
+
63
+ static function store_fonts($id, $fonts) {
64
+ self::__query("BEGIN");
65
+ // Update the font information
66
+ self::__query("DELETE FROM page_fonts WHERE id='" . pg_escape_string($id) . "'");
67
+
68
+ foreach (array_keys($fonts) as $font)
69
+ self::__query("INSERT INTO page_fonts (id, font_name) VALUES ('" .
70
+ pg_escape_string($id) . "', '" . pg_escape_string($font) . "')");
71
+ self::__query("COMMIT");
72
+ }
73
+
74
+ // static function retrieve_page($id, $page_num) {
75
+
76
+ // $res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ".
77
+ // "page_num=". pg_escape_string($page_num));
78
+
79
+ // $row = pg_fetch_assoc($res);
80
+
81
+ // return pg_unescape_bytea($row["data"]);
82
+
83
+ // }
84
+
85
+ static function get_page_timestamp($id, $page_num) {
86
+ $res = self::__query("SELECT timestamp FROM page_cache WHERE id='" . pg_escape_string($id) . "' AND ".
87
+ "page_num=". pg_escape_string($page_num));
88
+
89
+ $row = pg_fetch_assoc($res);
90
+
91
+ return $row["timestamp"];
92
+
93
+ }
94
+
95
+ // Adds the cached document referenced by $id to the provided pdf
96
+ static function insert_cached_document(CPDF_Adapter $pdf, $id, $new_page = true) {
97
+ $res = self::__query("SELECT font_name FROM page_fonts WHERE id='" . pg_escape_string($id) . "'");
98
+
99
+ // Ensure that the fonts needed by the cached document are loaded into
100
+ // the pdf
101
+ while ($row = pg_fetch_assoc($res))
102
+ $pdf->get_cpdf()->selectFont($row["font_name"]);
103
+
104
+ $res = self::__query("SELECT data FROM page_cache WHERE id='" . pg_escape_string($id) . "'");
105
+
106
+ if ( $new_page )
107
+ $pdf->new_page();
108
+
109
+ $first = true;
110
+ while ($row = pg_fetch_assoc($res)) {
111
+
112
+ if ( !$first )
113
+ $pdf->new_page();
114
+ else
115
+ $first = false;
116
+
117
+ $page = $pdf->reopen_serialized_object($row["data"]);
118
+ //$pdf->close_object();
119
+ $pdf->add_object($page, "add");
120
+
121
+ }
122
+
123
+ }
124
+ }
125
+
126
+ Page_Cache::init();
dompdf/include/page_frame_decorator.cls.php ADDED
@@ -0,0 +1,603 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Decorates frames for page layout
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Page_Frame_Decorator extends Frame_Decorator {
16
+
17
+ /**
18
+ * y value of bottom page margin
19
+ *
20
+ * @var float
21
+ */
22
+ protected $_bottom_page_margin;
23
+
24
+ /**
25
+ * Flag indicating page is full.
26
+ *
27
+ * @var bool
28
+ */
29
+ protected $_page_full;
30
+
31
+ /**
32
+ * Number of tables currently being reflowed
33
+ *
34
+ * @var int
35
+ */
36
+ protected $_in_table;
37
+
38
+ /**
39
+ * The pdf renderer
40
+ *
41
+ * @var Renderer
42
+ */
43
+ protected $_renderer;
44
+
45
+ /**
46
+ * This page's floating frames
47
+ *
48
+ * @var array
49
+ */
50
+ protected $_floating_frames = array();
51
+
52
+ //........................................................................
53
+
54
+ /**
55
+ * Class constructor
56
+ *
57
+ * @param Frame $frame the frame to decorate
58
+ * @param DOMPDF $dompdf
59
+ */
60
+ function __construct(Frame $frame, DOMPDF $dompdf) {
61
+ parent::__construct($frame, $dompdf);
62
+ $this->_page_full = false;
63
+ $this->_in_table = 0;
64
+ $this->_bottom_page_margin = null;
65
+ }
66
+
67
+ /**
68
+ * Set the renderer used for this pdf
69
+ *
70
+ * @param Renderer $renderer the renderer to use
71
+ */
72
+ function set_renderer($renderer) {
73
+ $this->_renderer = $renderer;
74
+ }
75
+
76
+ /**
77
+ * Return the renderer used for this pdf
78
+ *
79
+ * @return Renderer
80
+ */
81
+ function get_renderer() {
82
+ return $this->_renderer;
83
+ }
84
+
85
+ /**
86
+ * Set the frame's containing block. Overridden to set $this->_bottom_page_margin.
87
+ *
88
+ * @param float $x
89
+ * @param float $y
90
+ * @param float $w
91
+ * @param float $h
92
+ */
93
+ function set_containing_block($x = null, $y = null, $w = null, $h = null) {
94
+ parent::set_containing_block($x,$y,$w,$h);
95
+ //$w = $this->get_containing_block("w");
96
+ if ( isset($h) )
97
+ $this->_bottom_page_margin = $h; // - $this->_frame->get_style()->length_in_pt($this->_frame->get_style()->margin_bottom, $w);
98
+ }
99
+
100
+ /**
101
+ * Returns true if the page is full and is no longer accepting frames.
102
+ *
103
+ * @return bool
104
+ */
105
+ function is_full() {
106
+ return $this->_page_full;
107
+ }
108
+
109
+ /**
110
+ * Start a new page by resetting the full flag.
111
+ */
112
+ function next_page() {
113
+ $this->_floating_frames = array();
114
+ $this->_renderer->new_page();
115
+ $this->_page_full = false;
116
+ }
117
+
118
+ /**
119
+ * Indicate to the page that a table is currently being reflowed.
120
+ */
121
+ function table_reflow_start() {
122
+ $this->_in_table++;
123
+ }
124
+
125
+ /**
126
+ * Indicate to the page that table reflow is finished.
127
+ */
128
+ function table_reflow_end() {
129
+ $this->_in_table--;
130
+ }
131
+
132
+ /**
133
+ * Return whether we are currently in a nested table or not
134
+ *
135
+ * @return bool
136
+ */
137
+ function in_nested_table() {
138
+ return $this->_in_table > 1;
139
+ }
140
+
141
+ /**
142
+ * Check if a forced page break is required before $frame. This uses the
143
+ * frame's page_break_before property as well as the preceeding frame's
144
+ * page_break_after property.
145
+ *
146
+ * @link http://www.w3.org/TR/CSS21/page.html#forced
147
+ *
148
+ * @param Frame $frame the frame to check
149
+ * @return bool true if a page break occured
150
+ */
151
+ function check_forced_page_break(Frame $frame) {
152
+
153
+ // Skip check if page is already split
154
+ if ( $this->_page_full )
155
+ return null;
156
+
157
+ $block_types = array("block", "list-item", "table", "inline");
158
+ $page_breaks = array("always", "left", "right");
159
+
160
+ $style = $frame->get_style();
161
+
162
+ if ( !in_array($style->display, $block_types) )
163
+ return false;
164
+
165
+ // Find the previous block-level sibling
166
+ $prev = $frame->get_prev_sibling();
167
+
168
+ while ( $prev && !in_array($prev->get_style()->display, $block_types) )
169
+ $prev = $prev->get_prev_sibling();
170
+
171
+
172
+ if ( in_array($style->page_break_before, $page_breaks) ) {
173
+
174
+ // Prevent cascading splits
175
+ $frame->split(null, true);
176
+ // We have to grab the style again here because split() resets
177
+ // $frame->style to the frame's orignal style.
178
+ $frame->get_style()->page_break_before = "auto";
179
+ $this->_page_full = true;
180
+
181
+ return true;
182
+ }
183
+
184
+ if ( $prev && in_array($prev->get_style()->page_break_after, $page_breaks) ) {
185
+ // Prevent cascading splits
186
+ $frame->split(null, true);
187
+ $prev->get_style()->page_break_after = "auto";
188
+ $this->_page_full = true;
189
+ return true;
190
+ }
191
+
192
+ if( $prev && $prev->get_last_child() && $frame->get_node()->nodeName != "body" ) {
193
+ $prev_last_child = $prev->get_last_child();
194
+ if ( in_array($prev_last_child->get_style()->page_break_after, $page_breaks) ) {
195
+ $frame->split(null, true);
196
+ $prev_last_child->get_style()->page_break_after = "auto";
197
+ $this->_page_full = true;
198
+ return true;
199
+ }
200
+ }
201
+
202
+
203
+ return false;
204
+ }
205
+
206
+ /**
207
+ * Determine if a page break is allowed before $frame
208
+ * http://www.w3.org/TR/CSS21/page.html#allowed-page-breaks
209
+ *
210
+ * In the normal flow, page breaks can occur at the following places:
211
+ *
212
+ * 1. In the vertical margin between block boxes. When a page
213
+ * break occurs here, the used values of the relevant
214
+ * 'margin-top' and 'margin-bottom' properties are set to '0'.
215
+ * 2. Between line boxes inside a block box.
216
+ *
217
+ * These breaks are subject to the following rules:
218
+ *
219
+ * * Rule A: Breaking at (1) is allowed only if the
220
+ * 'page-break-after' and 'page-break-before' properties of
221
+ * all the elements generating boxes that meet at this margin
222
+ * allow it, which is when at least one of them has the value
223
+ * 'always', 'left', or 'right', or when all of them are
224
+ * 'auto'.
225
+ *
226
+ * * Rule B: However, if all of them are 'auto' and the
227
+ * nearest common ancestor of all the elements has a
228
+ * 'page-break-inside' value of 'avoid', then breaking here is
229
+ * not allowed.
230
+ *
231
+ * * Rule C: Breaking at (2) is allowed only if the number of
232
+ * line boxes between the break and the start of the enclosing
233
+ * block box is the value of 'orphans' or more, and the number
234
+ * of line boxes between the break and the end of the box is
235
+ * the value of 'widows' or more.
236
+ *
237
+ * * Rule D: In addition, breaking at (2) is allowed only if
238
+ * the 'page-break-inside' property is 'auto'.
239
+ *
240
+ * If the above doesn't provide enough break points to keep
241
+ * content from overflowing the page boxes, then rules B and D are
242
+ * dropped in order to find additional breakpoints.
243
+ *
244
+ * If that still does not lead to sufficient break points, rules A
245
+ * and C are dropped as well, to find still more break points.
246
+ *
247
+ * We will also allow breaks between table rows. However, when
248
+ * splitting a table, the table headers should carry over to the
249
+ * next page (but they don't yet).
250
+ *
251
+ * @param Frame $frame the frame to check
252
+ * @return bool true if a break is allowed, false otherwise
253
+ */
254
+ protected function _page_break_allowed(Frame $frame) {
255
+
256
+ $block_types = array("block", "list-item", "table", "-dompdf-image");
257
+ dompdf_debug("page-break", "_page_break_allowed(" . $frame->get_node()->nodeName. ")");
258
+ $display = $frame->get_style()->display;
259
+
260
+ // Block Frames (1):
261
+ if ( in_array($display, $block_types) ) {
262
+
263
+ // Avoid breaks within table-cells
264
+ if ( $this->_in_table ) {
265
+ dompdf_debug("page-break", "In table: " . $this->_in_table);
266
+ return false;
267
+ }
268
+
269
+ // Rules A & B
270
+
271
+ if ( $frame->get_style()->page_break_before === "avoid" ) {
272
+ dompdf_debug("page-break", "before: avoid");
273
+ return false;
274
+ }
275
+
276
+ // Find the preceeding block-level sibling
277
+ $prev = $frame->get_prev_sibling();
278
+ while ( $prev && !in_array($prev->get_style()->display, $block_types) )
279
+ $prev = $prev->get_prev_sibling();
280
+
281
+ // Does the previous element allow a page break after?
282
+ if ( $prev && $prev->get_style()->page_break_after === "avoid" ) {
283
+ dompdf_debug("page-break", "after: avoid");
284
+ return false;
285
+ }
286
+
287
+ // If both $prev & $frame have the same parent, check the parent's
288
+ // page_break_inside property.
289
+ $parent = $frame->get_parent();
290
+ if ( $prev && $parent && $parent->get_style()->page_break_inside === "avoid" ) {
291
+ dompdf_debug("page-break", "parent inside: avoid");
292
+ return false;
293
+ }
294
+
295
+ // To prevent cascading page breaks when a top-level element has
296
+ // page-break-inside: avoid, ensure that at least one frame is
297
+ // on the page before splitting.
298
+ if ( $parent->get_node()->nodeName === "body" && !$prev ) {
299
+ // We are the body's first child
300
+ dompdf_debug("page-break", "Body's first child.");
301
+ return false;
302
+ }
303
+
304
+ // If the frame is the first block-level frame, use the value from
305
+ // $frame's parent instead.
306
+ if ( !$prev && $parent )
307
+ return $this->_page_break_allowed( $parent );
308
+
309
+ dompdf_debug("page-break", "block: break allowed");
310
+ return true;
311
+
312
+ }
313
+
314
+ // Inline frames (2):
315
+ else if ( in_array($display, Style::$INLINE_TYPES) ) {
316
+
317
+ // Avoid breaks within table-cells
318
+ if ( $this->_in_table ) {
319
+ dompdf_debug("page-break", "In table: " . $this->_in_table);
320
+ return false;
321
+ }
322
+
323
+ // Rule C
324
+ $block_parent = $frame->find_block_parent();
325
+ if ( count($block_parent->get_line_boxes() ) < $frame->get_style()->orphans ) {
326
+ dompdf_debug("page-break", "orphans");
327
+ return false;
328
+ }
329
+
330
+ // FIXME: Checking widows is tricky without having laid out the
331
+ // remaining line boxes. Just ignore it for now...
332
+
333
+ // Rule D
334
+ $p = $block_parent;
335
+ while ($p) {
336
+ if ( $p->get_style()->page_break_inside === "avoid" ) {
337
+ dompdf_debug("page-break", "parent->inside: avoid");
338
+ return false;
339
+ }
340
+ $p = $p->find_block_parent();
341
+ }
342
+
343
+ // To prevent cascading page breaks when a top-level element has
344
+ // page-break-inside: avoid, ensure that at least one frame with
345
+ // some content is on the page before splitting.
346
+ $prev = $frame->get_prev_sibling();
347
+ while ( $prev && ($prev->is_text_node() && trim($prev->get_node()->nodeValue) == "") )
348
+ $prev = $prev->get_prev_sibling();
349
+
350
+ if ( $block_parent->get_node()->nodeName === "body" && !$prev ) {
351
+ // We are the body's first child
352
+ dompdf_debug("page-break", "Body's first child.");
353
+ return false;
354
+ }
355
+
356
+ // Skip breaks on empty text nodes
357
+ if ( $frame->is_text_node() &&
358
+ $frame->get_node()->nodeValue == "" )
359
+ return false;
360
+
361
+ dompdf_debug("page-break", "inline: break allowed");
362
+ return true;
363
+
364
+ // Table-rows
365
+ } else if ( $display === "table-row" ) {
366
+
367
+ // Simply check if the parent table's page_break_inside property is
368
+ // not 'avoid'
369
+ $p = Table_Frame_Decorator::find_parent_table($frame);
370
+
371
+ while ($p) {
372
+ if ( $p->get_style()->page_break_inside === "avoid" ) {
373
+ dompdf_debug("page-break", "parent->inside: avoid");
374
+ return false;
375
+ }
376
+ $p = $p->find_block_parent();
377
+ }
378
+
379
+ // Avoid breaking after the first row of a table
380
+ if ( $p && $p->get_first_child() === $frame) {
381
+ dompdf_debug("page-break", "table: first-row");
382
+ return false;
383
+ }
384
+
385
+ // If this is a nested table, prevent the page from breaking
386
+ if ( $this->_in_table > 1 ) {
387
+ dompdf_debug("page-break", "table: nested table");
388
+ return false;
389
+ }
390
+
391
+ dompdf_debug("page-break","table-row/row-groups: break allowed");
392
+ return true;
393
+
394
+ } else if ( in_array($display, Table_Frame_Decorator::$ROW_GROUPS) ) {
395
+
396
+ // Disallow breaks at row-groups: only split at row boundaries
397
+ return false;
398
+
399
+ } else {
400
+
401
+ dompdf_debug("page-break", "? " . $frame->get_style()->display . "");
402
+ return false;
403
+ }
404
+
405
+ }
406
+
407
+ /**
408
+ * Check if $frame will fit on the page. If the frame does not fit,
409
+ * the frame tree is modified so that a page break occurs in the
410
+ * correct location.
411
+ *
412
+ * @param Frame $frame the frame to check
413
+ * @return Frame the frame following the page break
414
+ */
415
+ function check_page_break(Frame $frame) {
416
+ // Do not split if we have already or if the frame was already
417
+ // pushed to the next page (prevents infinite loops)
418
+ if ( $this->_page_full || $frame->_already_pushed ) {
419
+ return false;
420
+ }
421
+
422
+ // If the frame is absolute of fixed it shouldn't break
423
+ $p = $frame;
424
+ do {
425
+ if ( $p->is_absolute() )
426
+ return false;
427
+ } while ( $p = $p->get_parent() );
428
+
429
+ $margin_height = $frame->get_margin_height();
430
+
431
+ // FIXME If the row is taller than the page and
432
+ // if it the first of the page, we don't break
433
+ if ( $frame->get_style()->display === "table-row" &&
434
+ !$frame->get_prev_sibling() &&
435
+ $margin_height > $this->get_margin_height() )
436
+ return false;
437
+
438
+ // Determine the frame's maximum y value
439
+ $max_y = $frame->get_position("y") + $margin_height;
440
+
441
+ // If a split is to occur here, then the bottom margins & paddings of all
442
+ // parents of $frame must fit on the page as well:
443
+ $p = $frame->get_parent();
444
+ while ( $p ) {
445
+ $style = $p->get_style();
446
+ $max_y += $style->length_in_pt(array($style->margin_bottom,
447
+ $style->padding_bottom,
448
+ $style->border_bottom_width));
449
+ $p = $p->get_parent();
450
+ }
451
+
452
+
453
+ // Check if $frame flows off the page
454
+ if ( $max_y <= $this->_bottom_page_margin )
455
+ // no: do nothing
456
+ return false;
457
+
458
+ dompdf_debug("page-break", "check_page_break");
459
+ dompdf_debug("page-break", "in_table: " . $this->_in_table);
460
+
461
+ // yes: determine page break location
462
+ $iter = $frame;
463
+ $flg = false;
464
+
465
+ $in_table = $this->_in_table;
466
+
467
+ dompdf_debug("page-break","Starting search");
468
+ while ( $iter ) {
469
+ // echo "\nbacktrack: " .$iter->get_node()->nodeName ." ".spl_object_hash($iter->get_node()). "";
470
+ if ( $iter === $this ) {
471
+ dompdf_debug("page-break", "reached root.");
472
+ // We've reached the root in our search. Just split at $frame.
473
+ break;
474
+ }
475
+
476
+ if ( $this->_page_break_allowed($iter) ) {
477
+ dompdf_debug("page-break","break allowed, splitting.");
478
+ $iter->split(null, true);
479
+ $this->_page_full = true;
480
+ $this->_in_table = $in_table;
481
+ $frame->_already_pushed = true;
482
+ return true;
483
+ }
484
+
485
+ if ( !$flg && $next = $iter->get_last_child() ) {
486
+ dompdf_debug("page-break", "following last child.");
487
+
488
+ if ( $next->is_table() )
489
+ $this->_in_table++;
490
+
491
+ $iter = $next;
492
+ continue;
493
+ }
494
+
495
+ if ( $next = $iter->get_prev_sibling() ) {
496
+ dompdf_debug("page-break", "following prev sibling.");
497
+
498
+ if ( $next->is_table() && !$iter->is_table() )
499
+ $this->_in_table++;
500
+
501
+ else if ( !$next->is_table() && $iter->is_table() )
502
+ $this->_in_table--;
503
+
504
+ // Avoid bug with whitespace after blocks
505
+ while ( $next = $iter->get_last_child() ) {
506
+ // Already selected last child, do nothing more
507
+ }
508
+
509
+ $iter = $next;
510
+ $flg = false;
511
+ continue;
512
+ }
513
+
514
+ if ( $next = $iter->get_parent() ) {
515
+ dompdf_debug("page-break", "following parent.");
516
+
517
+ if ( $iter->is_table() )
518
+ $this->_in_table--;
519
+
520
+ $iter = $next;
521
+ $flg = true;
522
+ continue;
523
+ }
524
+
525
+ break;
526
+ }
527
+
528
+ $this->_in_table = $in_table;
529
+
530
+ // No valid page break found. Just break at $frame.
531
+ dompdf_debug("page-break", "no valid break found, just splitting.");
532
+
533
+ // If we are in a table, backtrack to the nearest top-level table row
534
+ if ( $this->_in_table ) {
535
+ $num_tables = $this->_in_table - 1;
536
+
537
+ $iter = $frame;
538
+ while ( $iter && $num_tables && $iter->get_style()->display !== "table" ) {
539
+ $iter = $iter->get_parent();
540
+ $num_tables--;
541
+ }
542
+
543
+ $iter = $frame;
544
+ while ($iter && $iter->get_style()->display !== "table-row" )
545
+ $iter = $iter->get_parent();
546
+ }
547
+
548
+ $frame->split(null, true);
549
+ $this->_page_full = true;
550
+ $frame->_already_pushed = true;
551
+ return true;
552
+
553
+ }
554
+
555
+ //........................................................................
556
+
557
+ function split(Frame $frame = null, $force_pagebreak = false) {
558
+ // Do nothing
559
+ }
560
+
561
+ /**
562
+ * Add a floating frame
563
+ *
564
+ * @param Frame $frame
565
+ *
566
+ * @return void
567
+ */
568
+ function add_floating_frame(Frame $frame) {
569
+ array_unshift($this->_floating_frames, $frame);
570
+ }
571
+
572
+ /**
573
+ * @return Frame[]
574
+ */
575
+ function get_floating_frames() {
576
+ return $this->_floating_frames;
577
+ }
578
+
579
+ public function remove_floating_frame($key) {
580
+ unset($this->_floating_frames[$key]);
581
+ }
582
+
583
+ public function get_lowest_float_offset(Frame $child) {
584
+ $style = $child->get_style();
585
+ $side = $style->clear;
586
+ $float = $style->float;
587
+
588
+ $y = 0;
589
+
590
+ foreach($this->_floating_frames as $key => $frame) {
591
+ if ( $side === "both" || $frame->get_style()->float === $side ) {
592
+ $y = max($y, $frame->get_position("y") + $frame->get_margin_height());
593
+
594
+ if ( $float !== "none" ) {
595
+ $this->remove_floating_frame($key);
596
+ }
597
+ }
598
+ }
599
+
600
+ return $y;
601
+ }
602
+
603
+ }
dompdf/include/page_frame_reflower.cls.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Reflows pages
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Page_Frame_Reflower extends Frame_Reflower {
17
+
18
+ /**
19
+ * Cache of the callbacks array
20
+ *
21
+ * @var array
22
+ */
23
+ private $_callbacks;
24
+
25
+ /**
26
+ * Cache of the canvas
27
+ *
28
+ * @var Canvas
29
+ */
30
+ private $_canvas;
31
+
32
+ function __construct(Page_Frame_Decorator $frame) { parent::__construct($frame); }
33
+
34
+ function apply_page_style(Frame $frame, $page_number){
35
+ $style = $frame->get_style();
36
+ $page_styles = $style->get_stylesheet()->get_page_styles();
37
+
38
+ // http://www.w3.org/TR/CSS21/page.html#page-selectors
39
+ if ( count($page_styles) > 1 ) {
40
+ $odd = $page_number % 2 == 1;
41
+ $first = $page_number == 1;
42
+
43
+ $style = clone $page_styles["base"];
44
+
45
+ // FIXME RTL
46
+ if ( $odd && isset($page_styles[":right"]) ) {
47
+ $style->merge($page_styles[":right"]);
48
+ }
49
+
50
+ if ( $odd && isset($page_styles[":odd"]) ) {
51
+ $style->merge($page_styles[":odd"]);
52
+ }
53
+
54
+ // FIXME RTL
55
+ if ( !$odd && isset($page_styles[":left"]) ) {
56
+ $style->merge($page_styles[":left"]);
57
+ }
58
+
59
+ if ( !$odd && isset($page_styles[":even"]) ) {
60
+ $style->merge($page_styles[":even"]);
61
+ }
62
+
63
+ if ( $first && isset($page_styles[":first"]) ) {
64
+ $style->merge($page_styles[":first"]);
65
+ }
66
+
67
+ $frame->set_style($style);
68
+ }
69
+ }
70
+
71
+ //........................................................................
72
+
73
+ /**
74
+ * Paged layout:
75
+ * http://www.w3.org/TR/CSS21/page.html
76
+ */
77
+ function reflow(Block_Frame_Decorator $block = null) {
78
+ $fixed_children = array();
79
+ $prev_child = null;
80
+ $child = $this->_frame->get_first_child();
81
+ $current_page = 0;
82
+
83
+ while ($child) {
84
+ $this->apply_page_style($this->_frame, $current_page + 1);
85
+
86
+ $style = $this->_frame->get_style();
87
+
88
+ // Pages are only concerned with margins
89
+ $cb = $this->_frame->get_containing_block();
90
+ $left = $style->length_in_pt($style->margin_left, $cb["w"]);
91
+ $right = $style->length_in_pt($style->margin_right, $cb["w"]);
92
+ $top = $style->length_in_pt($style->margin_top, $cb["h"]);
93
+ $bottom = $style->length_in_pt($style->margin_bottom, $cb["h"]);
94
+
95
+ $content_x = $cb["x"] + $left;
96
+ $content_y = $cb["y"] + $top;
97
+ $content_width = $cb["w"] - $left - $right;
98
+ $content_height = $cb["h"] - $top - $bottom;
99
+
100
+ // Only if it's the first page, we save the nodes with a fixed position
101
+ if ($current_page == 0) {
102
+ $children = $child->get_children();
103
+ foreach ($children as $onechild) {
104
+ if ($onechild->get_style()->position === "fixed") {
105
+ $fixed_children[] = $onechild->deep_copy();
106
+ }
107
+ }
108
+ $fixed_children = array_reverse($fixed_children);
109
+ }
110
+
111
+ $child->set_containing_block($content_x, $content_y, $content_width, $content_height);
112
+
113
+ // Check for begin reflow callback
114
+ $this->_check_callbacks("begin_page_reflow", $child);
115
+
116
+ //Insert a copy of each node which have a fixed position
117
+ if ($current_page >= 1) {
118
+ foreach ($fixed_children as $fixed_child) {
119
+ $child->insert_child_before($fixed_child->deep_copy(), $child->get_first_child());
120
+ }
121
+ }
122
+
123
+ $child->reflow();
124
+ $next_child = $child->get_next_sibling();
125
+
126
+ // Check for begin render callback
127
+ $this->_check_callbacks("begin_page_render", $child);
128
+
129
+ // Render the page
130
+ $this->_frame->get_renderer()->render($child);
131
+
132
+ // Check for end render callback
133
+ $this->_check_callbacks("end_page_render", $child);
134
+
135
+ if ( $next_child ) {
136
+ $this->_frame->next_page();
137
+ }
138
+
139
+ // Wait to dispose of all frames on the previous page
140
+ // so callback will have access to them
141
+ if ( $prev_child ) {
142
+ $prev_child->dispose(true);
143
+ }
144
+ $prev_child = $child;
145
+ $child = $next_child;
146
+ $current_page++;
147
+ }
148
+
149
+ // Dispose of previous page if it still exists
150
+ if ( $prev_child ) {
151
+ $prev_child->dispose(true);
152
+ }
153
+ }
154
+
155
+ //........................................................................
156
+
157
+ /**
158
+ * Check for callbacks that need to be performed when a given event
159
+ * gets triggered on a page
160
+ *
161
+ * @param string $event the type of event
162
+ * @param Frame $frame the frame that event is triggered on
163
+ */
164
+ protected function _check_callbacks($event, $frame) {
165
+ if (!isset($this->_callbacks)) {
166
+ $dompdf = $this->_frame->get_dompdf();
167
+ $this->_callbacks = $dompdf->get_callbacks();
168
+ $this->_canvas = $dompdf->get_canvas();
169
+ }
170
+
171
+ if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) {
172
+ $info = array(0 => $this->_canvas, "canvas" => $this->_canvas,
173
+ 1 => $frame, "frame" => $frame);
174
+ $fs = $this->_callbacks[$event];
175
+ foreach ($fs as $f) {
176
+ if (is_callable($f)) {
177
+ if (is_array($f)) {
178
+ $f[0]->$f[1]($info);
179
+ } else {
180
+ $f($info);
181
+ }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ }
dompdf/include/pdflib_adapter.cls.php ADDED
@@ -0,0 +1,1085 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * PDF rendering interface
12
+ *
13
+ * PDFLib_Adapter provides a simple, stateless interface to the one
14
+ * provided by PDFLib.
15
+ *
16
+ * Unless otherwise mentioned, all dimensions are in points (1/72 in).
17
+ * The coordinate origin is in the top left corner and y values
18
+ * increase downwards.
19
+ *
20
+ * See {@link http://www.pdflib.com/} for more complete documentation
21
+ * on the underlying PDFlib functions.
22
+ *
23
+ * @package dompdf
24
+ */
25
+ class PDFLib_Adapter implements Canvas {
26
+
27
+ /**
28
+ * Dimensions of paper sizes in points
29
+ *
30
+ * @var array;
31
+ */
32
+ static public $PAPER_SIZES = array(); // Set to CPDF_Adapter::$PAPER_SIZES below.
33
+
34
+ /**
35
+ * Whether to create PDFs in memory or on disk
36
+ *
37
+ * @var bool
38
+ */
39
+ static $IN_MEMORY = true;
40
+
41
+ /**
42
+ * @var DOMPDF
43
+ */
44
+ private $_dompdf;
45
+
46
+ /**
47
+ * Instance of PDFLib class
48
+ *
49
+ * @var PDFlib
50
+ */
51
+ private $_pdf;
52
+
53
+ /**
54
+ * Name of temporary file used for PDFs created on disk
55
+ *
56
+ * @var string
57
+ */
58
+ private $_file;
59
+
60
+ /**
61
+ * PDF width, in points
62
+ *
63
+ * @var float
64
+ */
65
+ private $_width;
66
+
67
+ /**
68
+ * PDF height, in points
69
+ *
70
+ * @var float
71
+ */
72
+ private $_height;
73
+
74
+ /**
75
+ * Last fill colour used
76
+ *
77
+ * @var array
78
+ */
79
+ private $_last_fill_color;
80
+
81
+ /**
82
+ * Last stroke colour used
83
+ *
84
+ * @var array
85
+ */
86
+ private $_last_stroke_color;
87
+
88
+ /**
89
+ * Cache of image handles
90
+ *
91
+ * @var array
92
+ */
93
+ private $_imgs;
94
+
95
+ /**
96
+ * Cache of font handles
97
+ *
98
+ * @var array
99
+ */
100
+ private $_fonts;
101
+
102
+ /**
103
+ * List of objects (templates) to add to multiple pages
104
+ *
105
+ * @var array
106
+ */
107
+ private $_objs;
108
+
109
+ /**
110
+ * Current page number
111
+ *
112
+ * @var int
113
+ */
114
+ private $_page_number;
115
+
116
+ /**
117
+ * Total number of pages
118
+ *
119
+ * @var int
120
+ */
121
+ private $_page_count;
122
+
123
+ /**
124
+ * Text to display on every page
125
+ *
126
+ * @var array
127
+ */
128
+ private $_page_text;
129
+
130
+ /**
131
+ * Array of pages for accesing after rendering is initially complete
132
+ *
133
+ * @var array
134
+ */
135
+ private $_pages;
136
+
137
+ /**
138
+ * Class constructor
139
+ *
140
+ * @param mixed $paper The size of paper to use either a string (see {@link CPDF_Adapter::$PAPER_SIZES}) or
141
+ * an array(xmin,ymin,xmax,ymax)
142
+ * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
143
+ * @param DOMPDF $dompdf
144
+ */
145
+ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
146
+ if ( is_array($paper) ) {
147
+ $size = $paper;
148
+ }
149
+ else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) ) {
150
+ $size = self::$PAPER_SIZES[mb_strtolower($paper)];
151
+ }
152
+ else {
153
+ $size = self::$PAPER_SIZES["letter"];
154
+ }
155
+
156
+ if ( mb_strtolower($orientation) === "landscape" ) {
157
+ list($size[2], $size[3]) = array($size[3], $size[2]);
158
+ }
159
+
160
+ $this->_width = $size[2] - $size[0];
161
+ $this->_height= $size[3] - $size[1];
162
+
163
+ $this->_dompdf = $dompdf;
164
+
165
+ $this->_pdf = new PDFLib();
166
+
167
+ if ( defined("DOMPDF_PDFLIB_LICENSE") )
168
+ $this->_pdf->set_parameter( "license", DOMPDF_PDFLIB_LICENSE);
169
+
170
+ $this->_pdf->set_parameter("textformat", "utf8");
171
+ $this->_pdf->set_parameter("fontwarning", "false");
172
+
173
+ $this->_pdf->set_info("Creator", "DOMPDF");
174
+
175
+ // Silence pedantic warnings about missing TZ settings
176
+ $tz = @date_default_timezone_get();
177
+ date_default_timezone_set("UTC");
178
+ $this->_pdf->set_info("Date", date("Y-m-d"));
179
+ date_default_timezone_set($tz);
180
+
181
+ if ( self::$IN_MEMORY )
182
+ $this->_pdf->begin_document("","");
183
+ else {
184
+ $tmp_dir = $this->_dompdf->get_options("temp_dir");
185
+ $tmp_name = tempnam($tmp_dir, "libdompdf_pdf_");
186
+ @unlink($tmp_name);
187
+ $this->_file = "$tmp_name.pdf";
188
+ $this->_pdf->begin_document($this->_file,"");
189
+ }
190
+
191
+ $this->_pdf->begin_page_ext($this->_width, $this->_height, "");
192
+
193
+ $this->_page_number = $this->_page_count = 1;
194
+ $this->_page_text = array();
195
+
196
+ $this->_imgs = array();
197
+ $this->_fonts = array();
198
+ $this->_objs = array();
199
+
200
+ // Set up font paths
201
+ $families = Font_Metrics::get_font_families();
202
+ foreach ($families as $files) {
203
+ foreach ($files as $file) {
204
+ $face = basename($file);
205
+ $afm = null;
206
+
207
+ // Prefer ttfs to afms
208
+ if ( file_exists("$file.ttf") ) {
209
+ $outline = "$file.ttf";
210
+
211
+ } else if ( file_exists("$file.TTF") ) {
212
+ $outline = "$file.TTF";
213
+
214
+ } else if ( file_exists("$file.pfb") ) {
215
+ $outline = "$file.pfb";
216
+
217
+ if ( file_exists("$file.afm") ) {
218
+ $afm = "$file.afm";
219
+ }
220
+
221
+ } else if ( file_exists("$file.PFB") ) {
222
+ $outline = "$file.PFB";
223
+ if ( file_exists("$file.AFM") ) {
224
+ $afm = "$file.AFM";
225
+ }
226
+ } else {
227
+ continue;
228
+ }
229
+
230
+ $this->_pdf->set_parameter("FontOutline", "\{$face\}=\{$outline\}");
231
+
232
+ if ( !is_null($afm) ) {
233
+ $this->_pdf->set_parameter("FontAFM", "\{$face\}=\{$afm\}");
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ function get_dompdf(){
240
+ return $this->_dompdf;
241
+ }
242
+
243
+ /**
244
+ * Close the pdf
245
+ */
246
+ protected function _close() {
247
+ $this->_place_objects();
248
+
249
+ // Close all pages
250
+ $this->_pdf->suspend_page("");
251
+ for ($p = 1; $p <= $this->_page_count; $p++) {
252
+ $this->_pdf->resume_page("pagenumber=$p");
253
+ $this->_pdf->end_page_ext("");
254
+ }
255
+
256
+ $this->_pdf->end_document("");
257
+ }
258
+
259
+
260
+ /**
261
+ * Returns the PDFLib instance
262
+ *
263
+ * @return PDFLib
264
+ */
265
+ function get_pdflib() {
266
+ return $this->_pdf;
267
+ }
268
+
269
+ /**
270
+ * Add meta information to the PDF
271
+ *
272
+ * @param string $label label of the value (Creator, Producter, etc.)
273
+ * @param string $value the text to set
274
+ */
275
+ function add_info($label, $value) {
276
+ $this->_pdf->set_info($label, $value);
277
+ }
278
+
279
+ /**
280
+ * Opens a new 'object' (template in PDFLib-speak)
281
+ *
282
+ * While an object is open, all drawing actions are recorded to the
283
+ * object instead of being drawn on the current page. Objects can
284
+ * be added later to a specific page or to several pages.
285
+ *
286
+ * The return value is an integer ID for the new object.
287
+ *
288
+ * @see PDFLib_Adapter::close_object()
289
+ * @see PDFLib_Adapter::add_object()
290
+ *
291
+ * @return int
292
+ */
293
+ function open_object() {
294
+ $this->_pdf->suspend_page("");
295
+ $ret = $this->_pdf->begin_template($this->_width, $this->_height);
296
+ $this->_pdf->save();
297
+ $this->_objs[$ret] = array("start_page" => $this->_page_number);
298
+ return $ret;
299
+ }
300
+
301
+ /**
302
+ * Reopen an existing object (NOT IMPLEMENTED)
303
+ * PDFLib does not seem to support reopening templates.
304
+ *
305
+ * @param int $object the ID of a previously opened object
306
+ *
307
+ * @throws DOMPDF_Exception
308
+ * @return void
309
+ */
310
+ function reopen_object($object) {
311
+ throw new DOMPDF_Exception("PDFLib does not support reopening objects.");
312
+ }
313
+
314
+ /**
315
+ * Close the current template
316
+ *
317
+ * @see PDFLib_Adapter::open_object()
318
+ */
319
+ function close_object() {
320
+ $this->_pdf->restore();
321
+ $this->_pdf->end_template();
322
+ $this->_pdf->resume_page("pagenumber=".$this->_page_number);
323
+ }
324
+
325
+ /**
326
+ * Adds the specified object to the document
327
+ *
328
+ * $where can be one of:
329
+ * - 'add' add to current page only
330
+ * - 'all' add to every page from the current one onwards
331
+ * - 'odd' add to all odd numbered pages from now on
332
+ * - 'even' add to all even numbered pages from now on
333
+ * - 'next' add the object to the next page only
334
+ * - 'nextodd' add to all odd numbered pages from the next one
335
+ * - 'nexteven' add to all even numbered pages from the next one
336
+ *
337
+ * @param int $object the object handle returned by open_object()
338
+ * @param string $where
339
+ */
340
+ function add_object($object, $where = 'all') {
341
+
342
+ if ( mb_strpos($where, "next") !== false ) {
343
+ $this->_objs[$object]["start_page"]++;
344
+ $where = str_replace("next", "", $where);
345
+ if ( $where == "" )
346
+ $where = "add";
347
+ }
348
+
349
+ $this->_objs[$object]["where"] = $where;
350
+ }
351
+
352
+ /**
353
+ * Stops the specified template from appearing in the document.
354
+ *
355
+ * The object will stop being displayed on the page following the
356
+ * current one.
357
+ *
358
+ * @param int $object
359
+ */
360
+ function stop_object($object) {
361
+
362
+ if ( !isset($this->_objs[$object]) )
363
+ return;
364
+
365
+ $start = $this->_objs[$object]["start_page"];
366
+ $where = $this->_objs[$object]["where"];
367
+
368
+ // Place the object on this page if required
369
+ if ( $this->_page_number >= $start &&
370
+ (($this->_page_number % 2 == 0 && $where === "even") ||
371
+ ($this->_page_number % 2 == 1 && $where === "odd") ||
372
+ ($where === "all")) ) {
373
+ $this->_pdf->fit_image($object, 0, 0, "");
374
+ }
375
+
376
+ $this->_objs[$object] = null;
377
+ unset($this->_objs[$object]);
378
+ }
379
+
380
+ /**
381
+ * Add all active objects to the current page
382
+ */
383
+ protected function _place_objects() {
384
+
385
+ foreach ( $this->_objs as $obj => $props ) {
386
+ $start = $props["start_page"];
387
+ $where = $props["where"];
388
+
389
+ // Place the object on this page if required
390
+ if ( $this->_page_number >= $start &&
391
+ (($this->_page_number % 2 == 0 && $where === "even") ||
392
+ ($this->_page_number % 2 == 1 && $where === "odd") ||
393
+ ($where === "all")) ) {
394
+ $this->_pdf->fit_image($obj,0,0,"");
395
+ }
396
+ }
397
+
398
+ }
399
+
400
+ function get_width() { return $this->_width; }
401
+
402
+ function get_height() { return $this->_height; }
403
+
404
+ function get_page_number() { return $this->_page_number; }
405
+
406
+ function get_page_count() { return $this->_page_count; }
407
+
408
+ function set_page_number($num) { $this->_page_number = (int)$num; }
409
+
410
+ function set_page_count($count) { $this->_page_count = (int)$count; }
411
+
412
+
413
+ /**
414
+ * Sets the line style
415
+ *
416
+ * @param float $width
417
+ * @param $cap
418
+ * @param string $join
419
+ * @param array $dash
420
+ *
421
+ * @return void
422
+ */
423
+ protected function _set_line_style($width, $cap, $join, $dash) {
424
+
425
+ if ( count($dash) == 1 )
426
+ $dash[] = $dash[0];
427
+
428
+ if ( count($dash) > 1 )
429
+ $this->_pdf->setdashpattern("dasharray={" . implode(" ", $dash) . "}");
430
+ else
431
+ $this->_pdf->setdash(0,0);
432
+
433
+ switch ( $join ) {
434
+ case "miter":
435
+ $this->_pdf->setlinejoin(0);
436
+ break;
437
+
438
+ case "round":
439
+ $this->_pdf->setlinejoin(1);
440
+ break;
441
+
442
+ case "bevel":
443
+ $this->_pdf->setlinejoin(2);
444
+ break;
445
+
446
+ default:
447
+ break;
448
+ }
449
+
450
+ switch ( $cap ) {
451
+ case "butt":
452
+ $this->_pdf->setlinecap(0);
453
+ break;
454
+
455
+ case "round":
456
+ $this->_pdf->setlinecap(1);
457
+ break;
458
+
459
+ case "square":
460
+ $this->_pdf->setlinecap(2);
461
+ break;
462
+
463
+ default:
464
+ break;
465
+ }
466
+
467
+ $this->_pdf->setlinewidth($width);
468
+
469
+ }
470
+
471
+ /**
472
+ * Sets the line color
473
+ *
474
+ * @param array $color array(r,g,b)
475
+ */
476
+ protected function _set_stroke_color($color) {
477
+ if($this->_last_stroke_color == $color)
478
+ return;
479
+
480
+ $this->_last_stroke_color = $color;
481
+
482
+ if (isset($color[3])) {
483
+ $type = "cmyk";
484
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]);
485
+ }
486
+ elseif (isset($color[2])) {
487
+ $type = "rgb";
488
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null);
489
+ }
490
+ else {
491
+ $type = "gray";
492
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null);
493
+ }
494
+
495
+ $this->_pdf->setcolor("stroke", $type, $c1, $c2, $c3, $c4);
496
+ }
497
+
498
+ /**
499
+ * Sets the fill color
500
+ *
501
+ * @param array $color array(r,g,b)
502
+ */
503
+ protected function _set_fill_color($color) {
504
+ if($this->_last_fill_color == $color)
505
+ return;
506
+
507
+ $this->_last_fill_color = $color;
508
+
509
+ if (isset($color[3])) {
510
+ $type = "cmyk";
511
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], $color[3]);
512
+ }
513
+ elseif (isset($color[2])) {
514
+ $type = "rgb";
515
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], $color[2], null);
516
+ }
517
+ else {
518
+ $type = "gray";
519
+ list($c1, $c2, $c3, $c4) = array($color[0], $color[1], null, null);
520
+ }
521
+
522
+ $this->_pdf->setcolor("fill", $type, $c1, $c2, $c3, $c4);
523
+ }
524
+
525
+ /**
526
+ * Sets the opacity
527
+ *
528
+ * @param $opacity
529
+ * @param $mode
530
+ */
531
+ function set_opacity($opacity, $mode = "Normal") {
532
+ if ( $mode === "Normal" ) {
533
+ $gstate = $this->_pdf->create_gstate("opacityfill=$opacity opacitystroke=$opacity");
534
+ $this->_pdf->set_gstate($gstate);
535
+ }
536
+ }
537
+
538
+ function set_default_view($view, $options = array()) {
539
+ // TODO
540
+ // http://www.pdflib.com/fileadmin/pdflib/pdf/manuals/PDFlib-8.0.2-API-reference.pdf
541
+ /**
542
+ * fitheight Fit the page height to the window, with the x coordinate left at the left edge of the window.
543
+ * fitrect Fit the rectangle specified by left, bottom, right, and top to the window.
544
+ * fitvisible Fit the visible contents of the page (the ArtBox) to the window.
545
+ * fitvisibleheight Fit the visible contents of the page to the window with the x coordinate left at the left edge of the window.
546
+ * fitvisiblewidth Fit the visible contents of the page to the window with the y coordinate top at the top edge of the window.
547
+ * fitwidth Fit the page width to the window, with the y coordinate top at the top edge of the window.
548
+ * fitwindow Fit the complete page to the window.
549
+ * fixed
550
+ */
551
+ //$this->_pdf->set_parameter("openaction", $view);
552
+ }
553
+
554
+ /**
555
+ * Loads a specific font and stores the corresponding descriptor.
556
+ *
557
+ * @param string $font
558
+ * @param string $encoding
559
+ * @param string $options
560
+ *
561
+ * @return int the font descriptor for the font
562
+ */
563
+ protected function _load_font($font, $encoding = null, $options = "") {
564
+
565
+ // Check if the font is a native PDF font
566
+ // Embed non-native fonts
567
+ $test = strtolower(basename($font));
568
+ if ( in_array($test, DOMPDF::$native_fonts) ) {
569
+ $font = basename($font);
570
+
571
+ } else {
572
+ // Embed non-native fonts
573
+ $options .= " embedding=true";
574
+ }
575
+
576
+ if ( is_null($encoding) ) {
577
+
578
+ // Unicode encoding is only available for the commerical
579
+ // version of PDFlib and not PDFlib-Lite
580
+ if ( defined("DOMPDF_PDFLIB_LICENSE") )
581
+ $encoding = "unicode";
582
+ else
583
+ $encoding = "auto";
584
+
585
+ }
586
+
587
+ $key = "$font:$encoding:$options";
588
+
589
+ if ( isset($this->_fonts[$key]) )
590
+ return $this->_fonts[$key];
591
+
592
+ else {
593
+
594
+ $this->_fonts[$key] = $this->_pdf->load_font($font, $encoding, $options);
595
+ return $this->_fonts[$key];
596
+
597
+ }
598
+
599
+ }
600
+
601
+ /**
602
+ * Remaps y coords from 4th to 1st quadrant
603
+ *
604
+ * @param float $y
605
+ * @return float
606
+ */
607
+ protected function y($y) { return $this->_height - $y; }
608
+
609
+ //........................................................................
610
+
611
+ /**
612
+ * @param float $x1
613
+ * @param float $y1
614
+ * @param float $x2
615
+ * @param float $y2
616
+ * @param array $color
617
+ * @param float $width
618
+ * @param array $style
619
+ */
620
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
621
+ $this->_set_line_style($width, "butt", "", $style);
622
+ $this->_set_stroke_color($color);
623
+
624
+ $y1 = $this->y($y1);
625
+ $y2 = $this->y($y2);
626
+
627
+ $this->_pdf->moveto($x1, $y1);
628
+ $this->_pdf->lineto($x2, $y2);
629
+ $this->_pdf->stroke();
630
+ }
631
+
632
+ function arc($x1, $y1, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
633
+ $this->_set_line_style($width, "butt", "", $style);
634
+ $this->_set_stroke_color($color);
635
+
636
+ $y1 = $this->y($y1);
637
+
638
+ $this->_pdf->arc($x1, $y1, $r1, $astart, $aend);
639
+ $this->_pdf->stroke();
640
+ }
641
+
642
+ //........................................................................
643
+
644
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
645
+ $this->_set_stroke_color($color);
646
+ $this->_set_line_style($width, "butt", "", $style);
647
+
648
+ $y1 = $this->y($y1) - $h;
649
+
650
+ $this->_pdf->rect($x1, $y1, $w, $h);
651
+ $this->_pdf->stroke();
652
+ }
653
+
654
+ //........................................................................
655
+
656
+ function filled_rectangle($x1, $y1, $w, $h, $color) {
657
+ $this->_set_fill_color($color);
658
+
659
+ $y1 = $this->y($y1) - $h;
660
+
661
+ $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h));
662
+ $this->_pdf->fill();
663
+ }
664
+
665
+ function clipping_rectangle($x1, $y1, $w, $h) {
666
+ $this->_pdf->save();
667
+
668
+ $y1 = $this->y($y1) - $h;
669
+
670
+ $this->_pdf->rect(floatval($x1), floatval($y1), floatval($w), floatval($h));
671
+ $this->_pdf->clip();
672
+ }
673
+
674
+ function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
675
+ // @todo
676
+ $this->clipping_rectangle($x1, $y1, $w, $h);
677
+ }
678
+
679
+ function clipping_end() {
680
+ $this->_pdf->restore();
681
+ }
682
+
683
+ function save() {
684
+ $this->_pdf->save();
685
+ }
686
+
687
+ function restore() {
688
+ $this->_pdf->restore();
689
+ }
690
+
691
+ function rotate($angle, $x, $y) {
692
+ $pdf = $this->_pdf;
693
+ $pdf->translate($x, $this->_height-$y);
694
+ $pdf->rotate(-$angle);
695
+ $pdf->translate(-$x, -$this->_height+$y);
696
+ }
697
+
698
+ function skew($angle_x, $angle_y, $x, $y) {
699
+ $pdf = $this->_pdf;
700
+ $pdf->translate($x, $this->_height-$y);
701
+ $pdf->skew($angle_y, $angle_x); // Needs to be inverted
702
+ $pdf->translate(-$x, -$this->_height+$y);
703
+ }
704
+
705
+ function scale($s_x, $s_y, $x, $y) {
706
+ $pdf = $this->_pdf;
707
+ $pdf->translate($x, $this->_height-$y);
708
+ $pdf->scale($s_x, $s_y);
709
+ $pdf->translate(-$x, -$this->_height+$y);
710
+ }
711
+
712
+ function translate($t_x, $t_y) {
713
+ $this->_pdf->translate($t_x, -$t_y);
714
+ }
715
+
716
+ function transform($a, $b, $c, $d, $e, $f) {
717
+ $this->_pdf->concat($a, $b, $c, $d, $e, $f);
718
+ }
719
+
720
+ //........................................................................
721
+
722
+ function polygon($points, $color, $width = null, $style = null, $fill = false) {
723
+
724
+ $this->_set_fill_color($color);
725
+ $this->_set_stroke_color($color);
726
+
727
+ if ( !$fill && isset($width) )
728
+ $this->_set_line_style($width, "square", "miter", $style);
729
+
730
+ $y = $this->y(array_pop($points));
731
+ $x = array_pop($points);
732
+ $this->_pdf->moveto($x,$y);
733
+
734
+ while (count($points) > 1) {
735
+ $y = $this->y(array_pop($points));
736
+ $x = array_pop($points);
737
+ $this->_pdf->lineto($x,$y);
738
+ }
739
+
740
+ if ( $fill )
741
+ $this->_pdf->fill();
742
+ else
743
+ $this->_pdf->closepath_stroke();
744
+ }
745
+
746
+ //........................................................................
747
+
748
+ function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
749
+
750
+ $this->_set_fill_color($color);
751
+ $this->_set_stroke_color($color);
752
+
753
+ if ( !$fill && isset($width) )
754
+ $this->_set_line_style($width, "round", "round", $style);
755
+
756
+ $y = $this->y($y);
757
+
758
+ $this->_pdf->circle($x, $y, $r);
759
+
760
+ if ( $fill )
761
+ $this->_pdf->fill();
762
+ else
763
+ $this->_pdf->stroke();
764
+
765
+ }
766
+
767
+ //........................................................................
768
+
769
+ function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
770
+ $w = (int)$w;
771
+ $h = (int)$h;
772
+
773
+ $img_type = Image_Cache::detect_type($img_url);
774
+ $img_ext = Image_Cache::type_to_ext($img_type);
775
+
776
+ if ( !isset($this->_imgs[$img_url]) ) {
777
+ $this->_imgs[$img_url] = $this->_pdf->load_image($img_ext, $img_url, "");
778
+ }
779
+
780
+ $img = $this->_imgs[$img_url];
781
+
782
+ $y = $this->y($y) - $h;
783
+ $this->_pdf->fit_image($img, $x, $y, 'boxsize={'."$w $h".'} fitmethod=entire');
784
+ }
785
+
786
+ //........................................................................
787
+
788
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_spacing = 0, $char_spacing = 0, $angle = 0) {
789
+ $fh = $this->_load_font($font);
790
+
791
+ $this->_pdf->setfont($fh, $size);
792
+ $this->_set_fill_color($color);
793
+
794
+ $y = $this->y($y) - Font_Metrics::get_font_height($font, $size);
795
+
796
+ $word_spacing = (float)$word_spacing;
797
+ $char_spacing = (float)$char_spacing;
798
+ $angle = -(float)$angle;
799
+
800
+ $this->_pdf->fit_textline($text, $x, $y, "rotate=$angle wordspacing=$word_spacing charspacing=$char_spacing ");
801
+
802
+ }
803
+
804
+ //........................................................................
805
+
806
+ function javascript($code) {
807
+ if ( defined("DOMPDF_PDFLIB_LICENSE") ) {
808
+ $this->_pdf->create_action("JavaScript", $code);
809
+ }
810
+ }
811
+
812
+ //........................................................................
813
+
814
+ /**
815
+ * Add a named destination (similar to <a name="foo">...</a> in html)
816
+ *
817
+ * @param string $anchorname The name of the named destination
818
+ */
819
+ function add_named_dest($anchorname) {
820
+ $this->_pdf->add_nameddest($anchorname,"");
821
+ }
822
+
823
+ //........................................................................
824
+
825
+ /**
826
+ * Add a link to the pdf
827
+ *
828
+ * @param string $url The url to link to
829
+ * @param float $x The x position of the link
830
+ * @param float $y The y position of the link
831
+ * @param float $width The width of the link
832
+ * @param float $height The height of the link
833
+ */
834
+ function add_link($url, $x, $y, $width, $height) {
835
+
836
+ $y = $this->y($y) - $height;
837
+ if ( strpos($url, '#') === 0 ) {
838
+ // Local link
839
+ $name = substr($url,1);
840
+ if ( $name )
841
+ $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} destname=". substr($url,1) . " linewidth=0");
842
+ } else {
843
+
844
+ list($proto, $host, $path, $file) = explode_url($url);
845
+
846
+ if ( $proto == "" || $proto === "file://" )
847
+ return; // Local links are not allowed
848
+ $url = build_url($proto, $host, $path, $file);
849
+ $url = '{' . rawurldecode($url) . '}';
850
+
851
+ $action = $this->_pdf->create_action("URI", "url=" . $url);
852
+ $this->_pdf->create_annotation($x, $y, $x + $width, $y + $height, 'Link', "contents={$url} action={activate=$action} linewidth=0");
853
+ }
854
+ }
855
+
856
+ //........................................................................
857
+
858
+ function get_text_width($text, $font, $size, $word_spacing = 0, $letter_spacing = 0) {
859
+ $fh = $this->_load_font($font);
860
+
861
+ // Determine the additional width due to extra spacing
862
+ $num_spaces = mb_substr_count($text," ");
863
+ $delta = $word_spacing * $num_spaces;
864
+
865
+ if ( $letter_spacing ) {
866
+ $num_chars = mb_strlen($text);
867
+ $delta += ($num_chars - $num_spaces) * $letter_spacing;
868
+ }
869
+
870
+ return $this->_pdf->stringwidth($text, $fh, $size) + $delta;
871
+ }
872
+
873
+ //........................................................................
874
+
875
+ function get_font_height($font, $size) {
876
+
877
+ $fh = $this->_load_font($font);
878
+
879
+ $this->_pdf->setfont($fh, $size);
880
+
881
+ $asc = $this->_pdf->get_value("ascender", $fh);
882
+ $desc = $this->_pdf->get_value("descender", $fh);
883
+
884
+ // $desc is usually < 0,
885
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
886
+ return $size * ($asc - $desc) * $ratio;
887
+ }
888
+
889
+ function get_font_baseline($font, $size) {
890
+ $ratio = $this->_dompdf->get_option("font_height_ratio");
891
+ return $this->get_font_height($font, $size) / $ratio * 1.1;
892
+ }
893
+
894
+ //........................................................................
895
+
896
+ /**
897
+ * Writes text at the specified x and y coordinates on every page
898
+ *
899
+ * The strings '{PAGE_NUM}' and '{PAGE_COUNT}' are automatically replaced
900
+ * with their current values.
901
+ *
902
+ * See {@link Style::munge_colour()} for the format of the colour array.
903
+ *
904
+ * @param float $x
905
+ * @param float $y
906
+ * @param string $text the text to write
907
+ * @param string $font the font file to use
908
+ * @param float $size the font size, in points
909
+ * @param array $color
910
+ * @param float $word_space word spacing adjustment
911
+ * @param float $char_space char spacing adjustment
912
+ * @param float $angle angle to write the text at, measured CW starting from the x-axis
913
+ */
914
+ function page_text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
915
+ $_t = "text";
916
+ $this->_page_text[] = compact("_t", "x", "y", "text", "font", "size", "color", "word_space", "char_space", "angle");
917
+ }
918
+
919
+ //........................................................................
920
+
921
+ /**
922
+ * Processes a script on every page
923
+ *
924
+ * The variables $pdf, $PAGE_NUM, and $PAGE_COUNT are available.
925
+ *
926
+ * This function can be used to add page numbers to all pages
927
+ * after the first one, for example.
928
+ *
929
+ * @param string $code the script code
930
+ * @param string $type the language type for script
931
+ */
932
+ function page_script($code, $type = "text/php") {
933
+ $_t = "script";
934
+ $this->_page_text[] = compact("_t", "code", "type");
935
+ }
936
+
937
+ //........................................................................
938
+
939
+ function new_page() {
940
+
941
+ // Add objects to the current page
942
+ $this->_place_objects();
943
+
944
+ $this->_pdf->suspend_page("");
945
+ $this->_pdf->begin_page_ext($this->_width, $this->_height, "");
946
+ $this->_page_number = ++$this->_page_count;
947
+
948
+ }
949
+
950
+ //........................................................................
951
+
952
+ /**
953
+ * Add text to each page after rendering is complete
954
+ */
955
+ protected function _add_page_text() {
956
+
957
+ if ( !count($this->_page_text) )
958
+ return;
959
+
960
+ $this->_pdf->suspend_page("");
961
+
962
+ for ($p = 1; $p <= $this->_page_count; $p++) {
963
+ $this->_pdf->resume_page("pagenumber=$p");
964
+
965
+ foreach ($this->_page_text as $pt) {
966
+ extract($pt);
967
+
968
+ switch ($_t) {
969
+
970
+ case "text":
971
+ $text = str_replace(array("{PAGE_NUM}","{PAGE_COUNT}"),
972
+ array($p, $this->_page_count), $text);
973
+ $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
974
+ break;
975
+
976
+ case "script":
977
+ if (!$eval) {
978
+ $eval = new PHP_Evaluator($this);
979
+ }
980
+ $eval->evaluate($code, array('PAGE_NUM' => $p, 'PAGE_COUNT' => $this->_page_count));
981
+ break;
982
+ }
983
+ }
984
+
985
+ $this->_pdf->suspend_page("");
986
+ }
987
+
988
+ $this->_pdf->resume_page("pagenumber=".$this->_page_number);
989
+ }
990
+
991
+ //........................................................................
992
+
993
+ function stream($filename, $options = null) {
994
+
995
+ // Add page text
996
+ $this->_add_page_text();
997
+
998
+ if ( isset($options["compress"]) && $options["compress"] != 1 )
999
+ $this->_pdf->set_value("compress", 0);
1000
+ else
1001
+ $this->_pdf->set_value("compress", 6);
1002
+
1003
+ $this->_close();
1004
+
1005
+ $data = "";
1006
+
1007
+ if ( self::$IN_MEMORY ) {
1008
+ $data = $this->_pdf->get_buffer();
1009
+ //$size = strlen($data);
1010
+ } else {
1011
+ //$size = filesize($this->_file);
1012
+ }
1013
+
1014
+
1015
+ $filename = str_replace(array("\n","'"),"", $filename);
1016
+ $attach = (isset($options["Attachment"]) && $options["Attachment"]) ? "attachment" : "inline";
1017
+
1018
+ header("Cache-Control: private");
1019
+ header("Content-type: application/pdf");
1020
+ header("Content-Disposition: $attach; filename=\"$filename\"");
1021
+
1022
+ //header("Content-length: " . $size);
1023
+
1024
+ if ( self::$IN_MEMORY )
1025
+ echo $data;
1026
+
1027
+ else {
1028
+
1029
+ // Chunked readfile()
1030
+ $chunk = (1 << 21); // 2 MB
1031
+ $fh = fopen($this->_file, "rb");
1032
+ if ( !$fh )
1033
+ throw new DOMPDF_Exception("Unable to load temporary PDF file: " . $this->_file);
1034
+
1035
+ while ( !feof($fh) )
1036
+ echo fread($fh,$chunk);
1037
+ fclose($fh);
1038
+
1039
+ //debugpng
1040
+ if (DEBUGPNG) print '[pdflib stream unlink '.$this->_file.']';
1041
+ if (!DEBUGKEEPTEMP)
1042
+
1043
+ unlink($this->_file);
1044
+ $this->_file = null;
1045
+ unset($this->_file);
1046
+ }
1047
+
1048
+ flush();
1049
+ }
1050
+
1051
+ //........................................................................
1052
+
1053
+ function output($options = null) {
1054
+
1055
+ // Add page text
1056
+ $this->_add_page_text();
1057
+
1058
+ if ( isset($options["compress"]) && $options["compress"] != 1 )
1059
+ $this->_pdf->set_value("compress", 0);
1060
+ else
1061
+ $this->_pdf->set_value("compress", 6);
1062
+
1063
+ $this->_close();
1064
+
1065
+ if ( self::$IN_MEMORY )
1066
+ $data = $this->_pdf->get_buffer();
1067
+
1068
+ else {
1069
+ $data = file_get_contents($this->_file);
1070
+
1071
+ //debugpng
1072
+ if (DEBUGPNG) print '[pdflib output unlink '.$this->_file.']';
1073
+ if (!DEBUGKEEPTEMP)
1074
+
1075
+ unlink($this->_file);
1076
+ $this->_file = null;
1077
+ unset($this->_file);
1078
+ }
1079
+
1080
+ return $data;
1081
+ }
1082
+ }
1083
+
1084
+ // Workaround for idiotic limitation on statics...
1085
+ PDFLib_Adapter::$PAPER_SIZES = CPDF_Adapter::$PAPER_SIZES;
dompdf/include/php_evaluator.cls.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Executes inline PHP code during the rendering process
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class PHP_Evaluator {
16
+
17
+ /**
18
+ * @var Canvas
19
+ */
20
+ protected $_canvas;
21
+
22
+ function __construct(Canvas $canvas) {
23
+ $this->_canvas = $canvas;
24
+ }
25
+
26
+ function evaluate($code, $vars = array()) {
27
+ if ( !$this->_canvas->get_dompdf()->get_option("enable_php") ) {
28
+ return;
29
+ }
30
+
31
+ // Set up some variables for the inline code
32
+ $pdf = $this->_canvas;
33
+ $PAGE_NUM = $pdf->get_page_number();
34
+ $PAGE_COUNT = $pdf->get_page_count();
35
+
36
+ // Override those variables if passed in
37
+ foreach ($vars as $k => $v) {
38
+ $$k = $v;
39
+ }
40
+
41
+ //$code = html_entity_decode($code); // @todo uncomment this when tested
42
+ eval($code);
43
+ }
44
+
45
+ function render(Frame $frame) {
46
+ $this->evaluate($frame->get_node()->nodeValue);
47
+ }
48
+ }
dompdf/include/positioner.cls.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Base Positioner class
11
+ *
12
+ * Defines postioner interface
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ */
17
+ abstract class Positioner {
18
+
19
+ /**
20
+ * @var Frame_Decorator
21
+ */
22
+ protected $_frame;
23
+
24
+ //........................................................................
25
+
26
+ function __construct(Frame_Decorator $frame) {
27
+ $this->_frame = $frame;
28
+ }
29
+
30
+ /**
31
+ * Class destructor
32
+ */
33
+ function __destruct() {
34
+ clear_object($this);
35
+ }
36
+ //........................................................................
37
+
38
+ abstract function position();
39
+
40
+ function move($offset_x, $offset_y, $ignore_self = false) {
41
+ list($x, $y) = $this->_frame->get_position();
42
+
43
+ if ( !$ignore_self ) {
44
+ $this->_frame->set_position($x + $offset_x, $y + $offset_y);
45
+ }
46
+
47
+ foreach($this->_frame->get_children() as $child) {
48
+ $child->move($offset_x, $offset_y);
49
+ }
50
+ }
51
+ }
dompdf/include/renderer.cls.php ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Concrete renderer
11
+ *
12
+ * Instantiates several specific renderers in order to render any given
13
+ * frame.
14
+ *
15
+ * @access private
16
+ * @package dompdf
17
+ */
18
+ class Renderer extends Abstract_Renderer {
19
+
20
+ /**
21
+ * Array of renderers for specific frame types
22
+ *
23
+ * @var Abstract_Renderer[]
24
+ */
25
+ protected $_renderers;
26
+
27
+ /**
28
+ * Cache of the callbacks array
29
+ *
30
+ * @var array
31
+ */
32
+ private $_callbacks;
33
+
34
+ /**
35
+ * Class destructor
36
+ */
37
+ function __destruct() {
38
+ clear_object($this);
39
+ }
40
+
41
+ /**
42
+ * Advance the canvas to the next page
43
+ */
44
+ function new_page() {
45
+ $this->_canvas->new_page();
46
+ }
47
+
48
+ /**
49
+ * Render frames recursively
50
+ *
51
+ * @param Frame $frame the frame to render
52
+ */
53
+ function render(Frame $frame) {
54
+ global $_dompdf_debug;
55
+
56
+ if ( $_dompdf_debug ) {
57
+ echo $frame;
58
+ flush();
59
+ }
60
+
61
+ $style = $frame->get_style();
62
+
63
+ if ( in_array($style->visibility, array("hidden", "collapse")) ) {
64
+ return;
65
+ }
66
+
67
+ $display = $style->display;
68
+
69
+ // Starts the CSS transformation
70
+ if ( $style->transform && is_array($style->transform) ) {
71
+ $this->_canvas->save();
72
+ list($x, $y) = $frame->get_padding_box();
73
+ $origin = $style->transform_origin;
74
+
75
+ foreach($style->transform as $transform) {
76
+ list($function, $values) = $transform;
77
+ if ( $function === "matrix" ) {
78
+ $function = "transform";
79
+ }
80
+
81
+ $values = array_map("floatval", $values);
82
+ $values[] = $x + $style->length_in_pt($origin[0], $style->width);
83
+ $values[] = $y + $style->length_in_pt($origin[1], $style->height);
84
+
85
+ call_user_func_array(array($this->_canvas, $function), $values);
86
+ }
87
+ }
88
+
89
+ switch ($display) {
90
+
91
+ case "block":
92
+ case "list-item":
93
+ case "inline-block":
94
+ case "table":
95
+ case "inline-table":
96
+ $this->_render_frame("block", $frame);
97
+ break;
98
+
99
+ case "inline":
100
+ if ( $frame->is_text_node() )
101
+ $this->_render_frame("text", $frame);
102
+ else
103
+ $this->_render_frame("inline", $frame);
104
+ break;
105
+
106
+ case "table-cell":
107
+ $this->_render_frame("table-cell", $frame);
108
+ break;
109
+
110
+ case "table-row-group":
111
+ case "table-header-group":
112
+ case "table-footer-group":
113
+ $this->_render_frame("table-row-group", $frame);
114
+ break;
115
+
116
+ case "-dompdf-list-bullet":
117
+ $this->_render_frame("list-bullet", $frame);
118
+ break;
119
+
120
+ case "-dompdf-image":
121
+ $this->_render_frame("image", $frame);
122
+ break;
123
+
124
+ case "none":
125
+ $node = $frame->get_node();
126
+
127
+ if ( $node->nodeName === "script" ) {
128
+ if ( $node->getAttribute("type") === "text/php" ||
129
+ $node->getAttribute("language") === "php" ) {
130
+ // Evaluate embedded php scripts
131
+ $this->_render_frame("php", $frame);
132
+ }
133
+
134
+ elseif ( $node->getAttribute("type") === "text/javascript" ||
135
+ $node->getAttribute("language") === "javascript" ) {
136
+ // Insert JavaScript
137
+ $this->_render_frame("javascript", $frame);
138
+ }
139
+ }
140
+
141
+ // Don't render children, so skip to next iter
142
+ return;
143
+
144
+ default:
145
+ break;
146
+
147
+ }
148
+
149
+ // Starts the overflow: hidden box
150
+ if ( $style->overflow === "hidden" ) {
151
+ list($x, $y, $w, $h) = $frame->get_padding_box();
152
+
153
+ // get border radii
154
+ $style = $frame->get_style();
155
+ list($tl, $tr, $br, $bl) = $style->get_computed_border_radius($w, $h);
156
+
157
+ if ( $tl + $tr + $br + $bl > 0 ) {
158
+ $this->_canvas->clipping_roundrectangle($x, $y, $w, $h, $tl, $tr, $br, $bl);
159
+ }
160
+ else {
161
+ $this->_canvas->clipping_rectangle($x, $y, $w, $h);
162
+ }
163
+ }
164
+
165
+ $stack = array();
166
+
167
+ foreach ($frame->get_children() as $child) {
168
+ // < 0 : nagative z-index
169
+ // = 0 : no z-index, no stacking context
170
+ // = 1 : stacking context without z-index
171
+ // > 1 : z-index
172
+ $child_style = $child->get_style();
173
+ $child_z_index = $child_style->z_index;
174
+ $z_index = 0;
175
+
176
+ if ( $child_z_index !== "auto" ) {
177
+ $z_index = intval($child_z_index) + 1;
178
+ }
179
+ elseif ( $child_style->float !== "none" || $child->is_positionned()) {
180
+ $z_index = 1;
181
+ }
182
+
183
+ $stack[$z_index][] = $child;
184
+ }
185
+
186
+ ksort($stack);
187
+
188
+ foreach ($stack as $by_index) {
189
+ foreach($by_index as $child) {
190
+ $this->render($child);
191
+ }
192
+ }
193
+
194
+ // Ends the overflow: hidden box
195
+ if ( $style->overflow === "hidden" ) {
196
+ $this->_canvas->clipping_end();
197
+ }
198
+
199
+ if ( $style->transform && is_array($style->transform) ) {
200
+ $this->_canvas->restore();
201
+ }
202
+
203
+ // Check for end frame callback
204
+ $this->_check_callbacks("end_frame", $frame);
205
+ }
206
+
207
+ /**
208
+ * Check for callbacks that need to be performed when a given event
209
+ * gets triggered on a frame
210
+ *
211
+ * @param string $event the type of event
212
+ * @param Frame $frame the frame that event is triggered on
213
+ */
214
+ protected function _check_callbacks($event, $frame) {
215
+ if (!isset($this->_callbacks)) {
216
+ $this->_callbacks = $this->_dompdf->get_callbacks();
217
+ }
218
+
219
+ if (is_array($this->_callbacks) && isset($this->_callbacks[$event])) {
220
+ $info = array(0 => $this->_canvas, "canvas" => $this->_canvas,
221
+ 1 => $frame, "frame" => $frame);
222
+ $fs = $this->_callbacks[$event];
223
+ foreach ($fs as $f) {
224
+ if (is_callable($f)) {
225
+ if (is_array($f)) {
226
+ $f[0]->$f[1]($info);
227
+ } else {
228
+ $f($info);
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Render a single frame
237
+ *
238
+ * Creates Renderer objects on demand
239
+ *
240
+ * @param string $type type of renderer to use
241
+ * @param Frame $frame the frame to render
242
+ */
243
+ protected function _render_frame($type, $frame) {
244
+
245
+ if ( !isset($this->_renderers[$type]) ) {
246
+
247
+ switch ($type) {
248
+ case "block":
249
+ $this->_renderers[$type] = new Block_Renderer($this->_dompdf);
250
+ break;
251
+
252
+ case "inline":
253
+ $this->_renderers[$type] = new Inline_Renderer($this->_dompdf);
254
+ break;
255
+
256
+ case "text":
257
+ $this->_renderers[$type] = new Text_Renderer($this->_dompdf);
258
+ break;
259
+
260
+ case "image":
261
+ $this->_renderers[$type] = new Image_Renderer($this->_dompdf);
262
+ break;
263
+
264
+ case "table-cell":
265
+ $this->_renderers[$type] = new Table_Cell_Renderer($this->_dompdf);
266
+ break;
267
+
268
+ case "table-row-group":
269
+ $this->_renderers[$type] = new Table_Row_Group_Renderer($this->_dompdf);
270
+ break;
271
+
272
+ case "list-bullet":
273
+ $this->_renderers[$type] = new List_Bullet_Renderer($this->_dompdf);
274
+ break;
275
+
276
+ case "php":
277
+ $this->_renderers[$type] = new PHP_Evaluator($this->_canvas);
278
+ break;
279
+
280
+ case "javascript":
281
+ $this->_renderers[$type] = new Javascript_Embedder($this->_dompdf);
282
+ break;
283
+
284
+ }
285
+ }
286
+
287
+ $this->_renderers[$type]->render($frame);
288
+
289
+ }
290
+ }
dompdf/include/style.cls.php ADDED
@@ -0,0 +1,2435 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * Represents CSS properties.
13
+ *
14
+ * The Style class is responsible for handling and storing CSS properties.
15
+ * It includes methods to resolve colours and lengths, as well as getters &
16
+ * setters for many CSS properites.
17
+ *
18
+ * Actual CSS parsing is performed in the {@link Stylesheet} class.
19
+ *
20
+ * @package dompdf
21
+ */
22
+ class Style {
23
+
24
+ const CSS_IDENTIFIER = "-?[_a-zA-Z]+[_a-zA-Z0-9-]*";
25
+ const CSS_INTEGER = "-?\d+";
26
+
27
+ /**
28
+ * Default font size, in points.
29
+ *
30
+ * @var float
31
+ */
32
+ static $default_font_size = 12;
33
+
34
+ /**
35
+ * Default line height, as a fraction of the font size.
36
+ *
37
+ * @var float
38
+ */
39
+ static $default_line_height = 1.2;
40
+
41
+ /**
42
+ * Default "absolute" font sizes relative to the default font-size
43
+ * http://www.w3.org/TR/css3-fonts/#font-size-the-font-size-property
44
+ * @var array<float>
45
+ */
46
+ static $font_size_keywords = array(
47
+ "xx-small" => 0.6, // 3/5
48
+ "x-small" => 0.75, // 3/4
49
+ "small" => 0.889, // 8/9
50
+ "medium" => 1, // 1
51
+ "large" => 1.2, // 6/5
52
+ "x-large" => 1.5, // 3/2
53
+ "xx-large" => 2.0, // 2/1
54
+ );
55
+
56
+ /**
57
+ * List of all inline types. Should really be a constant.
58
+ *
59
+ * @var array
60
+ */
61
+ static $INLINE_TYPES = array("inline");
62
+
63
+ /**
64
+ * List of all block types. Should really be a constant.
65
+ *
66
+ * @var array
67
+ */
68
+ static $BLOCK_TYPES = array("block", "inline-block", "table-cell", "list-item");
69
+
70
+ /**
71
+ * List of all positionned types. Should really be a constant.
72
+ *
73
+ * @var array
74
+ */
75
+ static $POSITIONNED_TYPES = array("relative", "absolute", "fixed");
76
+
77
+ /**
78
+ * List of all table types. Should really be a constant.
79
+ *
80
+ * @var array;
81
+ */
82
+ static $TABLE_TYPES = array("table", "inline-table");
83
+
84
+ /**
85
+ * List of valid border styles. Should also really be a constant.
86
+ *
87
+ * @var array
88
+ */
89
+ static $BORDER_STYLES = array("none", "hidden", "dotted", "dashed", "solid",
90
+ "double", "groove", "ridge", "inset", "outset");
91
+
92
+ /**
93
+ * Default style values.
94
+ *
95
+ * @link http://www.w3.org/TR/CSS21/propidx.html
96
+ *
97
+ * @var array
98
+ */
99
+ static protected $_defaults = null;
100
+
101
+ /**
102
+ * List of inherited properties
103
+ *
104
+ * @link http://www.w3.org/TR/CSS21/propidx.html
105
+ *
106
+ * @var array
107
+ */
108
+ static protected $_inherited = null;
109
+
110
+ /**
111
+ * Caches method_exists result
112
+ *
113
+ * @var array<bool>
114
+ */
115
+ static protected $_methods_cache = array();
116
+
117
+ /**
118
+ * The stylesheet this style belongs to
119
+ *
120
+ * @see Stylesheet
121
+ * @var Stylesheet
122
+ */
123
+ protected $_stylesheet; // stylesheet this style is attached to
124
+
125
+ /**
126
+ * Main array of all CSS properties & values
127
+ *
128
+ * @var array
129
+ */
130
+ protected $_props;
131
+
132
+ /* var instead of protected would allow access outside of class */
133
+ protected $_important_props;
134
+
135
+ /**
136
+ * Cached property values
137
+ *
138
+ * @var array
139
+ */
140
+ protected $_prop_cache;
141
+
142
+ /**
143
+ * Font size of parent element in document tree. Used for relative font
144
+ * size resolution.
145
+ *
146
+ * @var float
147
+ */
148
+ protected $_parent_font_size; // Font size of parent element
149
+
150
+ protected $_font_family;
151
+
152
+ /**
153
+ * @var Frame
154
+ */
155
+ protected $_frame;
156
+
157
+ /**
158
+ * The origin of the style
159
+ *
160
+ * @var int
161
+ */
162
+ protected $_origin = Stylesheet::ORIG_AUTHOR;
163
+
164
+ // private members
165
+ /**
166
+ * True once the font size is resolved absolutely
167
+ *
168
+ * @var bool
169
+ */
170
+ private $__font_size_calculated; // Cache flag
171
+
172
+ /**
173
+ * The computed border radius
174
+ */
175
+ private $_computed_border_radius = null;
176
+
177
+ /**
178
+ * @var bool
179
+ */
180
+ public $_has_border_radius = false;
181
+
182
+ /**
183
+ * Class constructor
184
+ *
185
+ * @param Stylesheet $stylesheet the stylesheet this Style is associated with.
186
+ * @param int $origin
187
+ */
188
+ function __construct(Stylesheet $stylesheet, $origin = Stylesheet::ORIG_AUTHOR) {
189
+ $this->_props = array();
190
+ $this->_important_props = array();
191
+ $this->_stylesheet = $stylesheet;
192
+ $this->_origin = $origin;
193
+ $this->_parent_font_size = null;
194
+ $this->__font_size_calculated = false;
195
+
196
+ if ( !isset(self::$_defaults) ) {
197
+
198
+ // Shorthand
199
+ $d =& self::$_defaults;
200
+
201
+ // All CSS 2.1 properties, and their default values
202
+ $d["azimuth"] = "center";
203
+ $d["background_attachment"] = "scroll";
204
+ $d["background_color"] = "transparent";
205
+ $d["background_image"] = "none";
206
+ $d["background_image_resolution"] = "normal";
207
+ $d["_dompdf_background_image_resolution"] = $d["background_image_resolution"];
208
+ $d["background_position"] = "0% 0%";
209
+ $d["background_repeat"] = "repeat";
210
+ $d["background"] = "";
211
+ $d["border_collapse"] = "separate";
212
+ $d["border_color"] = "";
213
+ $d["border_spacing"] = "0";
214
+ $d["border_style"] = "";
215
+ $d["border_top"] = "";
216
+ $d["border_right"] = "";
217
+ $d["border_bottom"] = "";
218
+ $d["border_left"] = "";
219
+ $d["border_top_color"] = "";
220
+ $d["border_right_color"] = "";
221
+ $d["border_bottom_color"] = "";
222
+ $d["border_left_color"] = "";
223
+ $d["border_top_style"] = "none";
224
+ $d["border_right_style"] = "none";
225
+ $d["border_bottom_style"] = "none";
226
+ $d["border_left_style"] = "none";
227
+ $d["border_top_width"] = "medium";
228
+ $d["border_right_width"] = "medium";
229
+ $d["border_bottom_width"] = "medium";
230
+ $d["border_left_width"] = "medium";
231
+ $d["border_width"] = "medium";
232
+ $d["border_bottom_left_radius"] = "";
233
+ $d["border_bottom_right_radius"] = "";
234
+ $d["border_top_left_radius"] = "";
235
+ $d["border_top_right_radius"] = "";
236
+ $d["border_radius"] = "";
237
+ $d["border"] = "";
238
+ $d["bottom"] = "auto";
239
+ $d["caption_side"] = "top";
240
+ $d["clear"] = "none";
241
+ $d["clip"] = "auto";
242
+ $d["color"] = "#000000";
243
+ $d["content"] = "normal";
244
+ $d["counter_increment"] = "none";
245
+ $d["counter_reset"] = "none";
246
+ $d["cue_after"] = "none";
247
+ $d["cue_before"] = "none";
248
+ $d["cue"] = "";
249
+ $d["cursor"] = "auto";
250
+ $d["direction"] = "ltr";
251
+ $d["display"] = "inline";
252
+ $d["elevation"] = "level";
253
+ $d["empty_cells"] = "show";
254
+ $d["float"] = "none";
255
+ $d["font_family"] = $stylesheet->get_dompdf()->get_option("default_font");
256
+ $d["font_size"] = "medium";
257
+ $d["font_style"] = "normal";
258
+ $d["font_variant"] = "normal";
259
+ $d["font_weight"] = "normal";
260
+ $d["font"] = "";
261
+ $d["height"] = "auto";
262
+ $d["image_resolution"] = "normal";
263
+ $d["_dompdf_image_resolution"] = $d["image_resolution"];
264
+ $d["left"] = "auto";
265
+ $d["letter_spacing"] = "normal";
266
+ $d["line_height"] = "normal";
267
+ $d["list_style_image"] = "none";
268
+ $d["list_style_position"] = "outside";
269
+ $d["list_style_type"] = "disc";
270
+ $d["list_style"] = "";
271
+ $d["margin_right"] = "0";
272
+ $d["margin_left"] = "0";
273
+ $d["margin_top"] = "0";
274
+ $d["margin_bottom"] = "0";
275
+ $d["margin"] = "";
276
+ $d["max_height"] = "none";
277
+ $d["max_width"] = "none";
278
+ $d["min_height"] = "0";
279
+ $d["min_width"] = "0";
280
+ $d["opacity"] = "1.0"; // CSS3
281
+ $d["orphans"] = "2";
282
+ $d["outline_color"] = ""; // "invert" special color is not supported
283
+ $d["outline_style"] = "none";
284
+ $d["outline_width"] = "medium";
285
+ $d["outline"] = "";
286
+ $d["overflow"] = "visible";
287
+ $d["padding_top"] = "0";
288
+ $d["padding_right"] = "0";
289
+ $d["padding_bottom"] = "0";
290
+ $d["padding_left"] = "0";
291
+ $d["padding"] = "";
292
+ $d["page_break_after"] = "auto";
293
+ $d["page_break_before"] = "auto";
294
+ $d["page_break_inside"] = "auto";
295
+ $d["pause_after"] = "0";
296
+ $d["pause_before"] = "0";
297
+ $d["pause"] = "";
298
+ $d["pitch_range"] = "50";
299
+ $d["pitch"] = "medium";
300
+ $d["play_during"] = "auto";
301
+ $d["position"] = "static";
302
+ $d["quotes"] = "";
303
+ $d["richness"] = "50";
304
+ $d["right"] = "auto";
305
+ $d["size"] = "auto"; // @page
306
+ $d["speak_header"] = "once";
307
+ $d["speak_numeral"] = "continuous";
308
+ $d["speak_punctuation"] = "none";
309
+ $d["speak"] = "normal";
310
+ $d["speech_rate"] = "medium";
311
+ $d["stress"] = "50";
312
+ $d["table_layout"] = "auto";
313
+ $d["text_align"] = "left";
314
+ $d["text_decoration"] = "none";
315
+ $d["text_indent"] = "0";
316
+ $d["text_transform"] = "none";
317
+ $d["top"] = "auto";
318
+ $d["transform"] = "none"; // CSS3
319
+ $d["transform_origin"] = "50% 50%"; // CSS3
320
+ $d["_webkit_transform"] = $d["transform"]; // CSS3
321
+ $d["_webkit_transform_origin"] = $d["transform_origin"]; // CSS3
322
+ $d["unicode_bidi"] = "normal";
323
+ $d["vertical_align"] = "baseline";
324
+ $d["visibility"] = "visible";
325
+ $d["voice_family"] = "";
326
+ $d["volume"] = "medium";
327
+ $d["white_space"] = "normal";
328
+ $d["word_wrap"] = "normal";
329
+ $d["widows"] = "2";
330
+ $d["width"] = "auto";
331
+ $d["word_spacing"] = "normal";
332
+ $d["z_index"] = "auto";
333
+
334
+ // for @font-face
335
+ $d["src"] = "";
336
+ $d["unicode_range"] = "";
337
+
338
+ // Properties that inherit by default
339
+ self::$_inherited = array("azimuth",
340
+ "background_image_resolution",
341
+ "border_collapse",
342
+ "border_spacing",
343
+ "caption_side",
344
+ "color",
345
+ "cursor",
346
+ "direction",
347
+ "elevation",
348
+ "empty_cells",
349
+ "font_family",
350
+ "font_size",
351
+ "font_style",
352
+ "font_variant",
353
+ "font_weight",
354
+ "font",
355
+ "image_resolution",
356
+ "letter_spacing",
357
+ "line_height",
358
+ "list_style_image",
359
+ "list_style_position",
360
+ "list_style_type",
361
+ "list_style",
362
+ "orphans",
363
+ "page_break_inside",
364
+ "pitch_range",
365
+ "pitch",
366
+ "quotes",
367
+ "richness",
368
+ "speak_header",
369
+ "speak_numeral",
370
+ "speak_punctuation",
371
+ "speak",
372
+ "speech_rate",
373
+ "stress",
374
+ "text_align",
375
+ "text_indent",
376
+ "text_transform",
377
+ "visibility",
378
+ "voice_family",
379
+ "volume",
380
+ "white_space",
381
+ "word_wrap",
382
+ "widows",
383
+ "word_spacing");
384
+ }
385
+
386
+ }
387
+
388
+ /**
389
+ * "Destructor": forcibly free all references held by this object
390
+ */
391
+ function dispose() {
392
+ clear_object($this);
393
+ }
394
+
395
+ function set_frame(Frame $frame) {
396
+ $this->_frame = $frame;
397
+ }
398
+
399
+ function get_frame() {
400
+ return $this->_frame;
401
+ }
402
+
403
+ function set_origin($origin) {
404
+ $this->_origin = $origin;
405
+ }
406
+
407
+ function get_origin() {
408
+ return $this->_origin;
409
+ }
410
+
411
+ /**
412
+ * returns the {@link Stylesheet} this Style is associated with.
413
+ *
414
+ * @return Stylesheet
415
+ */
416
+ function get_stylesheet() { return $this->_stylesheet; }
417
+
418
+ /**
419
+ * Converts any CSS length value into an absolute length in points.
420
+ *
421
+ * length_in_pt() takes a single length (e.g. '1em') or an array of
422
+ * lengths and returns an absolute length. If an array is passed, then
423
+ * the return value is the sum of all elements.
424
+ *
425
+ * If a reference size is not provided, the default font size is used
426
+ * ({@link Style::$default_font_size}).
427
+ *
428
+ * @param float|array $length the length or array of lengths to resolve
429
+ * @param float $ref_size an absolute reference size to resolve percentage lengths
430
+ * @return float
431
+ */
432
+ function length_in_pt($length, $ref_size = null) {
433
+ static $cache = array();
434
+
435
+ if ( !is_array($length) ) {
436
+ $length = array($length);
437
+ }
438
+
439
+ if ( !isset($ref_size) ) {
440
+ $ref_size = self::$default_font_size;
441
+ }
442
+
443
+ $key = implode("@", $length)."/$ref_size";
444
+
445
+ if ( isset($cache[$key]) ) {
446
+ return $cache[$key];
447
+ }
448
+
449
+ $ret = 0;
450
+ foreach ($length as $l) {
451
+
452
+ if ( $l === "auto" ) {
453
+ return "auto";
454
+ }
455
+
456
+ if ( $l === "none" ) {
457
+ return "none";
458
+ }
459
+
460
+ // Assume numeric values are already in points
461
+ if ( is_numeric($l) ) {
462
+ $ret += $l;
463
+ continue;
464
+ }
465
+
466
+ if ( $l === "normal" ) {
467
+ $ret += $ref_size;
468
+ continue;
469
+ }
470
+
471
+ // Border lengths
472
+ if ( $l === "thin" ) {
473
+ $ret += 0.5;
474
+ continue;
475
+ }
476
+
477
+ if ( $l === "medium" ) {
478
+ $ret += 1.5;
479
+ continue;
480
+ }
481
+
482
+ if ( $l === "thick" ) {
483
+ $ret += 2.5;
484
+ continue;
485
+ }
486
+
487
+ if ( ($i = mb_strpos($l, "px")) !== false ) {
488
+ $dpi = $this->_stylesheet->get_dompdf()->get_option("dpi");
489
+ $ret += ( mb_substr($l, 0, $i) * 72 ) / $dpi;
490
+ continue;
491
+ }
492
+
493
+ if ( ($i = mb_strpos($l, "pt")) !== false ) {
494
+ $ret += (float)mb_substr($l, 0, $i);
495
+ continue;
496
+ }
497
+
498
+ if ( ($i = mb_strpos($l, "%")) !== false ) {
499
+ $ret += (float)mb_substr($l, 0, $i)/100 * $ref_size;
500
+ continue;
501
+ }
502
+
503
+ if ( ($i = mb_strpos($l, "rem")) !== false ) {
504
+ $ret += (float)mb_substr($l, 0, $i) * $this->_stylesheet->get_dompdf()->get_tree()->get_root()->get_style()->font_size;
505
+ continue;
506
+ }
507
+
508
+ if ( ($i = mb_strpos($l, "em")) !== false ) {
509
+ $ret += (float)mb_substr($l, 0, $i) * $this->__get("font_size");
510
+ continue;
511
+ }
512
+
513
+ if ( ($i = mb_strpos($l, "cm")) !== false ) {
514
+ $ret += mb_substr($l, 0, $i) * 72 / 2.54;
515
+ continue;
516
+ }
517
+
518
+ if ( ($i = mb_strpos($l, "mm")) !== false ) {
519
+ $ret += mb_substr($l, 0, $i) * 72 / 25.4;
520
+ continue;
521
+ }
522
+
523
+ // FIXME: em:ex ratio?
524
+ if ( ($i = mb_strpos($l, "ex")) !== false ) {
525
+ $ret += mb_substr($l, 0, $i) * $this->__get("font_size") / 2;
526
+ continue;
527
+ }
528
+
529
+ if ( ($i = mb_strpos($l, "in")) !== false ) {
530
+ $ret += (float)mb_substr($l, 0, $i) * 72;
531
+ continue;
532
+ }
533
+
534
+ if ( ($i = mb_strpos($l, "pc")) !== false ) {
535
+ $ret += (float)mb_substr($l, 0, $i) * 12;
536
+ continue;
537
+ }
538
+
539
+ // Bogus value
540
+ $ret += $ref_size;
541
+ }
542
+
543
+ return $cache[$key] = $ret;
544
+ }
545
+
546
+
547
+ /**
548
+ * Set inherited properties in this style using values in $parent
549
+ *
550
+ * @param Style $parent
551
+ *
552
+ * @return Style
553
+ */
554
+ function inherit(Style $parent) {
555
+
556
+ // Set parent font size
557
+ $this->_parent_font_size = $parent->get_font_size();
558
+
559
+ foreach (self::$_inherited as $prop) {
560
+ //inherit the !important property also.
561
+ //if local property is also !important, don't inherit.
562
+ if ( isset($parent->_props[$prop]) &&
563
+ ( !isset($this->_props[$prop]) ||
564
+ ( isset($parent->_important_props[$prop]) && !isset($this->_important_props[$prop]) )
565
+ )
566
+ ) {
567
+ if ( isset($parent->_important_props[$prop]) ) {
568
+ $this->_important_props[$prop] = true;
569
+ }
570
+ //see __set and __get, on all assignments clear cache!
571
+ $this->_prop_cache[$prop] = null;
572
+ $this->_props[$prop] = $parent->_props[$prop];
573
+ }
574
+ }
575
+
576
+ foreach ($this->_props as $prop => $value) {
577
+ if ( $value === "inherit" ) {
578
+ if ( isset($parent->_important_props[$prop]) ) {
579
+ $this->_important_props[$prop] = true;
580
+ }
581
+ //do not assign direct, but
582
+ //implicite assignment through __set, redirect to specialized, get value with __get
583
+ //This is for computing defaults if the parent setting is also missing.
584
+ //Therefore do not directly assign the value without __set
585
+ //set _important_props before that to be able to propagate.
586
+ //see __set and __get, on all assignments clear cache!
587
+ //$this->_prop_cache[$prop] = null;
588
+ //$this->_props[$prop] = $parent->_props[$prop];
589
+ //props_set for more obvious explicite assignment not implemented, because
590
+ //too many implicite uses.
591
+ // $this->props_set($prop, $parent->$prop);
592
+ $this->__set($prop, $parent->__get($prop));
593
+ }
594
+ }
595
+
596
+ return $this;
597
+ }
598
+
599
+ /**
600
+ * Override properties in this style with those in $style
601
+ *
602
+ * @param Style $style
603
+ */
604
+ function merge(Style $style) {
605
+ //treat the !important attribute
606
+ //if old rule has !important attribute, override with new rule only if
607
+ //the new rule is also !important
608
+ foreach($style->_props as $prop => $val ) {
609
+ if (isset($style->_important_props[$prop])) {
610
+ $this->_important_props[$prop] = true;
611
+ //see __set and __get, on all assignments clear cache!
612
+ $this->_prop_cache[$prop] = null;
613
+ $this->_props[$prop] = $val;
614
+ }
615
+ else if ( !isset($this->_important_props[$prop]) ) {
616
+ //see __set and __get, on all assignments clear cache!
617
+ $this->_prop_cache[$prop] = null;
618
+ $this->_props[$prop] = $val;
619
+ }
620
+ }
621
+
622
+ if ( isset($style->_props["font_size"]) ) {
623
+ $this->__font_size_calculated = false;
624
+ }
625
+ }
626
+
627
+ /**
628
+ * Returns an array(r, g, b, "r"=> r, "g"=>g, "b"=>b, "hex"=>"#rrggbb")
629
+ * based on the provided CSS colour value.
630
+ *
631
+ * @param string $colour
632
+ * @return array
633
+ */
634
+ function munge_colour($colour) { return CSS_Color::parse($colour); }
635
+
636
+ /**
637
+ * Alias for {@link Style::munge_colour()}
638
+ *
639
+ * @param string $color
640
+ * @return array
641
+ */
642
+ function munge_color($color) { return CSS_Color::parse($color); }
643
+
644
+ /* direct access to _important_props array from outside would work only when declared as
645
+ * 'var $_important_props;' instead of 'protected $_important_props;'
646
+ * Don't call _set/__get on missing attribute. Therefore need a special access.
647
+ * Assume that __set will be also called when this is called, so do not check validity again.
648
+ * Only created, if !important exists -> always set true.
649
+ */
650
+ function important_set($prop) {
651
+ $prop = str_replace("-", "_", $prop);
652
+ $this->_important_props[$prop] = true;
653
+ }
654
+
655
+ function important_get($prop) {
656
+ return isset($this->_important_props[$prop]);
657
+ }
658
+
659
+ /**
660
+ * PHP5 overloaded setter
661
+ *
662
+ * This function along with {@link Style::__get()} permit a user of the
663
+ * Style class to access any (CSS) property using the following syntax:
664
+ * <code>
665
+ * Style->margin_top = "1em";
666
+ * echo (Style->margin_top);
667
+ * </code>
668
+ *
669
+ * __set() automatically calls the provided set function, if one exists,
670
+ * otherwise it sets the property directly. Typically, __set() is not
671
+ * called directly from outside of this class.
672
+ *
673
+ * On each modification clear cache to return accurate setting.
674
+ * Also affects direct settings not using __set
675
+ * For easier finding all assignments, attempted to allowing only explicite assignment:
676
+ * Very many uses, e.g. frame_reflower.cls.php -> for now leave as it is
677
+ * function __set($prop, $val) {
678
+ * throw new DOMPDF_Exception("Implicite replacement of assignment by __set. Not good.");
679
+ * }
680
+ * function props_set($prop, $val) { ... }
681
+ *
682
+ * @param string $prop the property to set
683
+ * @param mixed $val the value of the property
684
+ *
685
+ */
686
+ function __set($prop, $val) {
687
+ $prop = str_replace("-", "_", $prop);
688
+ $this->_prop_cache[$prop] = null;
689
+
690
+ if ( !isset(self::$_defaults[$prop]) ) {
691
+ global $_dompdf_warnings;
692
+ $_dompdf_warnings[] = "'$prop' is not a valid CSS2 property.";
693
+ return;
694
+ }
695
+
696
+ if ( $prop !== "content" && is_string($val) && strlen($val) > 5 && mb_strpos($val, "url") === false ) {
697
+ $val = mb_strtolower(trim(str_replace(array("\n", "\t"), array(" "), $val)));
698
+ $val = preg_replace("/([0-9]+) (pt|px|pc|em|ex|in|cm|mm|%)/S", "\\1\\2", $val);
699
+ }
700
+
701
+ $method = "set_$prop";
702
+
703
+ if ( !isset(self::$_methods_cache[$method]) ) {
704
+ self::$_methods_cache[$method] = method_exists($this, $method);
705
+ }
706
+
707
+ if ( self::$_methods_cache[$method] ) {
708
+ $this->$method($val);
709
+ }
710
+ else {
711
+ $this->_props[$prop] = $val;
712
+ }
713
+ }
714
+
715
+ /**
716
+ * PHP5 overloaded getter
717
+ * Along with {@link Style::__set()} __get() provides access to all CSS
718
+ * properties directly. Typically __get() is not called directly outside
719
+ * of this class.
720
+ * On each modification clear cache to return accurate setting.
721
+ * Also affects direct settings not using __set
722
+ *
723
+ * @param string $prop
724
+ *
725
+ * @throws DOMPDF_Exception
726
+ * @return mixed
727
+ */
728
+ function __get($prop) {
729
+ if ( !isset(self::$_defaults[$prop]) ) {
730
+ throw new DOMPDF_Exception("'$prop' is not a valid CSS2 property.");
731
+ }
732
+
733
+ if ( isset($this->_prop_cache[$prop]) && $this->_prop_cache[$prop] != null ) {
734
+ return $this->_prop_cache[$prop];
735
+ }
736
+
737
+ $method = "get_$prop";
738
+
739
+ // Fall back on defaults if property is not set
740
+ if ( !isset($this->_props[$prop]) ) {
741
+ $this->_props[$prop] = self::$_defaults[$prop];
742
+ }
743
+
744
+ if ( !isset(self::$_methods_cache[$method]) ) {
745
+ self::$_methods_cache[$method] = method_exists($this, $method);
746
+ }
747
+
748
+ if ( self::$_methods_cache[$method] ) {
749
+ return $this->_prop_cache[$prop] = $this->$method();
750
+ }
751
+
752
+ return $this->_prop_cache[$prop] = $this->_props[$prop];
753
+ }
754
+
755
+ function get_font_family_raw(){
756
+ return trim($this->_props["font_family"], " \t\n\r\x0B\"'");
757
+ }
758
+
759
+ /**
760
+ * Getter for the 'font-family' CSS property.
761
+ * Uses the {@link Font_Metrics} class to resolve the font family into an
762
+ * actual font file.
763
+ *
764
+ * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-family
765
+ * @throws DOMPDF_Exception
766
+ *
767
+ * @return string
768
+ */
769
+ function get_font_family() {
770
+ if ( isset($this->_font_family) ) {
771
+ return $this->_font_family;
772
+ }
773
+
774
+ $DEBUGCSS=DEBUGCSS; //=DEBUGCSS; Allow override of global setting for ad hoc debug
775
+
776
+ // Select the appropriate font. First determine the subtype, then check
777
+ // the specified font-families for a candidate.
778
+
779
+ // Resolve font-weight
780
+ $weight = $this->__get("font_weight");
781
+
782
+ if ( is_numeric($weight) ) {
783
+ if ( $weight < 600 ) {
784
+ $weight = "normal";
785
+ }
786
+ else {
787
+ $weight = "bold";
788
+ }
789
+ }
790
+ else if ( $weight === "bold" || $weight === "bolder" ) {
791
+ $weight = "bold";
792
+ }
793
+ else {
794
+ $weight = "normal";
795
+ }
796
+
797
+ // Resolve font-style
798
+ $font_style = $this->__get("font_style");
799
+
800
+ if ( $weight === "bold" && ($font_style === "italic" || $font_style === "oblique") ) {
801
+ $subtype = "bold_italic";
802
+ }
803
+ else if ( $weight === "bold" && $font_style !== "italic" && $font_style !== "oblique" ) {
804
+ $subtype = "bold";
805
+ }
806
+ else if ( $weight !== "bold" && ($font_style === "italic" || $font_style === "oblique") ) {
807
+ $subtype = "italic";
808
+ }
809
+ else {
810
+ $subtype = "normal";
811
+ }
812
+
813
+ // Resolve the font family
814
+ if ( $DEBUGCSS ) {
815
+ print "<pre>[get_font_family:";
816
+ print '('.$this->_props["font_family"].'.'.$font_style.'.'.$this->__get("font_weight").'.'.$weight.'.'.$subtype.')';
817
+ }
818
+
819
+ $families = preg_split("/\s*,\s*/", $this->_props["font_family"]);
820
+
821
+ $font = null;
822
+ foreach($families as $family) {
823
+ //remove leading and trailing string delimiters, e.g. on font names with spaces;
824
+ //remove leading and trailing whitespace
825
+ $family = trim($family, " \t\n\r\x0B\"'");
826
+ if ( $DEBUGCSS ) {
827
+ print '('.$family.')';
828
+ }
829
+ $font = Font_Metrics::get_font($family, $subtype);
830
+
831
+ if ( $font ) {
832
+ if ($DEBUGCSS) print '('.$font.")get_font_family]\n</pre>";
833
+ return $this->_font_family = $font;
834
+ }
835
+ }
836
+
837
+ $family = null;
838
+ if ( $DEBUGCSS ) {
839
+ print '(default)';
840
+ }
841
+ $font = Font_Metrics::get_font($family, $subtype);
842
+
843
+ if ( $font ) {
844
+ if ( $DEBUGCSS ) print '('.$font.")get_font_family]\n</pre>";
845
+ return$this->_font_family = $font;
846
+ }
847
+
848
+ throw new DOMPDF_Exception("Unable to find a suitable font replacement for: '" . $this->_props["font_family"] ."'");
849
+
850
+ }
851
+
852
+ /**
853
+ * Returns the resolved font size, in points
854
+ *
855
+ * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
856
+ * @return float
857
+ */
858
+ function get_font_size() {
859
+
860
+ if ( $this->__font_size_calculated ) {
861
+ return $this->_props["font_size"];
862
+ }
863
+
864
+ if ( !isset($this->_props["font_size"]) ) {
865
+ $fs = self::$_defaults["font_size"];
866
+ }
867
+ else {
868
+ $fs = $this->_props["font_size"];
869
+ }
870
+
871
+ if ( !isset($this->_parent_font_size) ) {
872
+ $this->_parent_font_size = self::$default_font_size;
873
+ }
874
+
875
+ switch ($fs) {
876
+ case "xx-small":
877
+ case "x-small":
878
+ case "small":
879
+ case "medium":
880
+ case "large":
881
+ case "x-large":
882
+ case "xx-large":
883
+ $fs = self::$default_font_size * self::$font_size_keywords[$fs];
884
+ break;
885
+
886
+ case "smaller":
887
+ $fs = 8/9 * $this->_parent_font_size;
888
+ break;
889
+
890
+ case "larger":
891
+ $fs = 6/5 * $this->_parent_font_size;
892
+ break;
893
+
894
+ default:
895
+ break;
896
+ }
897
+
898
+ // Ensure relative sizes resolve to something
899
+ if ( ($i = mb_strpos($fs, "em")) !== false ) {
900
+ $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size;
901
+ }
902
+ else if ( ($i = mb_strpos($fs, "ex")) !== false ) {
903
+ $fs = mb_substr($fs, 0, $i) * $this->_parent_font_size;
904
+ }
905
+ else {
906
+ $fs = $this->length_in_pt($fs);
907
+ }
908
+
909
+ //see __set and __get, on all assignments clear cache!
910
+ $this->_prop_cache["font_size"] = null;
911
+ $this->_props["font_size"] = $fs;
912
+ $this->__font_size_calculated = true;
913
+ return $this->_props["font_size"];
914
+
915
+ }
916
+
917
+ /**
918
+ * @link http://www.w3.org/TR/CSS21/text.html#propdef-word-spacing
919
+ * @return float
920
+ */
921
+ function get_word_spacing() {
922
+ if ( $this->_props["word_spacing"] === "normal" ) {
923
+ return 0;
924
+ }
925
+
926
+ return $this->_props["word_spacing"];
927
+ }
928
+
929
+ /**
930
+ * @link http://www.w3.org/TR/CSS21/text.html#propdef-letter-spacing
931
+ * @return float
932
+ */
933
+ function get_letter_spacing() {
934
+ if ( $this->_props["letter_spacing"] === "normal" ) {
935
+ return 0;
936
+ }
937
+
938
+ return $this->_props["letter_spacing"];
939
+ }
940
+
941
+ /**
942
+ * @link http://www.w3.org/TR/CSS21/visudet.html#propdef-line-height
943
+ * @return float
944
+ */
945
+ function get_line_height() {
946
+ $line_height = $this->_props["line_height"];
947
+
948
+ if ( $line_height === "normal" ) {
949
+ return self::$default_line_height * $this->get_font_size();
950
+ }
951
+
952
+ if ( is_numeric($line_height) ) {
953
+ return $this->length_in_pt( $line_height . "em", $this->get_font_size());
954
+ }
955
+
956
+ return $this->length_in_pt( $line_height, $this->_parent_font_size );
957
+ }
958
+
959
+ /**
960
+ * Returns the colour as an array
961
+ *
962
+ * The array has the following format:
963
+ * <code>array(r,g,b, "r" => r, "g" => g, "b" => b, "hex" => "#rrggbb")</code>
964
+ *
965
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
966
+ * @return array
967
+ */
968
+ function get_color() {
969
+ return $this->munge_color( $this->_props["color"] );
970
+ }
971
+
972
+ /**
973
+ * Returns the background colour as an array
974
+ *
975
+ * The returned array has the same format as {@link Style::get_color()}
976
+ *
977
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
978
+ * @return array
979
+ */
980
+ function get_background_color() {
981
+ return $this->munge_color( $this->_props["background_color"] );
982
+ }
983
+
984
+ /**
985
+ * Returns the background position as an array
986
+ *
987
+ * The returned array has the following format:
988
+ * <code>array(x,y, "x" => x, "y" => y)</code>
989
+ *
990
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
991
+ * @return array
992
+ */
993
+ function get_background_position() {
994
+ $tmp = explode(" ", $this->_props["background_position"]);
995
+
996
+ switch ($tmp[0]) {
997
+ case "left":
998
+ $x = "0%";
999
+ break;
1000
+
1001
+ case "right":
1002
+ $x = "100%";
1003
+ break;
1004
+
1005
+ case "top":
1006
+ $y = "0%";
1007
+ break;
1008
+
1009
+ case "bottom":
1010
+ $y = "100%";
1011
+ break;
1012
+
1013
+ case "center":
1014
+ $x = "50%";
1015
+ $y = "50%";
1016
+ break;
1017
+
1018
+ default:
1019
+ $x = $tmp[0];
1020
+ break;
1021
+ }
1022
+
1023
+ if ( isset($tmp[1]) ) {
1024
+
1025
+ switch ($tmp[1]) {
1026
+ case "left":
1027
+ $x = "0%";
1028
+ break;
1029
+
1030
+ case "right":
1031
+ $x = "100%";
1032
+ break;
1033
+
1034
+ case "top":
1035
+ $y = "0%";
1036
+ break;
1037
+
1038
+ case "bottom":
1039
+ $y = "100%";
1040
+ break;
1041
+
1042
+ case "center":
1043
+ if ( $tmp[0] === "left" || $tmp[0] === "right" || $tmp[0] === "center" ) {
1044
+ $y = "50%";
1045
+ }
1046
+ else {
1047
+ $x = "50%";
1048
+ }
1049
+ break;
1050
+
1051
+ default:
1052
+ $y = $tmp[1];
1053
+ break;
1054
+ }
1055
+
1056
+ }
1057
+ else {
1058
+ $y = "50%";
1059
+ }
1060
+
1061
+ if ( !isset($x) ) {
1062
+ $x = "0%";
1063
+ }
1064
+
1065
+ if ( !isset($y) ) {
1066
+ $y = "0%";
1067
+ }
1068
+
1069
+ return array(
1070
+ 0 => $x, "x" => $x,
1071
+ 1 => $y, "y" => $y,
1072
+ );
1073
+ }
1074
+
1075
+
1076
+ /**
1077
+ * Returns the background as it is currently stored
1078
+ *
1079
+ * (currently anyway only for completeness.
1080
+ * not used for further processing)
1081
+ *
1082
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
1083
+ * @return string
1084
+ */
1085
+ function get_background_attachment() {
1086
+ return $this->_props["background_attachment"];
1087
+ }
1088
+
1089
+
1090
+ /**
1091
+ * Returns the background_repeat as it is currently stored
1092
+ *
1093
+ * (currently anyway only for completeness.
1094
+ * not used for further processing)
1095
+ *
1096
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
1097
+ * @return string
1098
+ */
1099
+ function get_background_repeat() {
1100
+ return $this->_props["background_repeat"];
1101
+ }
1102
+
1103
+
1104
+ /**
1105
+ * Returns the background as it is currently stored
1106
+ *
1107
+ * (currently anyway only for completeness.
1108
+ * not used for further processing, but the individual get_background_xxx)
1109
+ *
1110
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
1111
+ * @return string
1112
+ */
1113
+ function get_background() {
1114
+ return $this->_props["background"];
1115
+ }
1116
+
1117
+
1118
+ /**#@+
1119
+ * Returns the border colour as an array
1120
+ *
1121
+ * See {@link Style::get_color()}
1122
+ *
1123
+ * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
1124
+ * @return array
1125
+ */
1126
+ function get_border_top_color() {
1127
+ if ( $this->_props["border_top_color"] === "" ) {
1128
+ //see __set and __get, on all assignments clear cache!
1129
+ $this->_prop_cache["border_top_color"] = null;
1130
+ $this->_props["border_top_color"] = $this->__get("color");
1131
+ }
1132
+
1133
+ return $this->munge_color($this->_props["border_top_color"]);
1134
+ }
1135
+
1136
+ function get_border_right_color() {
1137
+ if ( $this->_props["border_right_color"] === "" ) {
1138
+ //see __set and __get, on all assignments clear cache!
1139
+ $this->_prop_cache["border_right_color"] = null;
1140
+ $this->_props["border_right_color"] = $this->__get("color");
1141
+ }
1142
+
1143
+ return $this->munge_color($this->_props["border_right_color"]);
1144
+ }
1145
+
1146
+ function get_border_bottom_color() {
1147
+ if ( $this->_props["border_bottom_color"] === "" ) {
1148
+ //see __set and __get, on all assignments clear cache!
1149
+ $this->_prop_cache["border_bottom_color"] = null;
1150
+ $this->_props["border_bottom_color"] = $this->__get("color");
1151
+ }
1152
+
1153
+ return $this->munge_color($this->_props["border_bottom_color"]);
1154
+ }
1155
+
1156
+ function get_border_left_color() {
1157
+ if ( $this->_props["border_left_color"] === "" ) {
1158
+ //see __set and __get, on all assignments clear cache!
1159
+ $this->_prop_cache["border_left_color"] = null;
1160
+ $this->_props["border_left_color"] = $this->__get("color");
1161
+ }
1162
+
1163
+ return $this->munge_color($this->_props["border_left_color"]);
1164
+ }
1165
+
1166
+ /**#@-*/
1167
+
1168
+ /**#@+
1169
+ * Returns the border width, as it is currently stored
1170
+ *
1171
+ * @link http://www.w3.org/TR/CSS21/box.html#border-width-properties
1172
+ * @return float|string
1173
+ */
1174
+ function get_border_top_width() {
1175
+ $style = $this->__get("border_top_style");
1176
+ return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_top_width"]) : 0;
1177
+ }
1178
+
1179
+ function get_border_right_width() {
1180
+ $style = $this->__get("border_right_style");
1181
+ return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_right_width"]) : 0;
1182
+ }
1183
+
1184
+ function get_border_bottom_width() {
1185
+ $style = $this->__get("border_bottom_style");
1186
+ return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_bottom_width"]) : 0;
1187
+ }
1188
+
1189
+ function get_border_left_width() {
1190
+ $style = $this->__get("border_left_style");
1191
+ return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["border_left_width"]) : 0;
1192
+ }
1193
+ /**#@-*/
1194
+
1195
+ /**
1196
+ * Return an array of all border properties.
1197
+ *
1198
+ * The returned array has the following structure:
1199
+ * <code>
1200
+ * array("top" => array("width" => [border-width],
1201
+ * "style" => [border-style],
1202
+ * "color" => [border-color (array)]),
1203
+ * "bottom" ... )
1204
+ * </code>
1205
+ *
1206
+ * @return array
1207
+ */
1208
+ function get_border_properties() {
1209
+ return array(
1210
+ "top" => array(
1211
+ "width" => $this->__get("border_top_width"),
1212
+ "style" => $this->__get("border_top_style"),
1213
+ "color" => $this->__get("border_top_color"),
1214
+ ),
1215
+ "bottom" => array(
1216
+ "width" => $this->__get("border_bottom_width"),
1217
+ "style" => $this->__get("border_bottom_style"),
1218
+ "color" => $this->__get("border_bottom_color"),
1219
+ ),
1220
+ "right" => array(
1221
+ "width" => $this->__get("border_right_width"),
1222
+ "style" => $this->__get("border_right_style"),
1223
+ "color" => $this->__get("border_right_color"),
1224
+ ),
1225
+ "left" => array(
1226
+ "width" => $this->__get("border_left_width"),
1227
+ "style" => $this->__get("border_left_style"),
1228
+ "color" => $this->__get("border_left_color"),
1229
+ ),
1230
+ );
1231
+ }
1232
+
1233
+ /**
1234
+ * Return a single border property
1235
+ *
1236
+ * @param string $side
1237
+ *
1238
+ * @return mixed
1239
+ */
1240
+ protected function _get_border($side) {
1241
+ $color = $this->__get("border_" . $side . "_color");
1242
+
1243
+ return $this->__get("border_" . $side . "_width") . " " .
1244
+ $this->__get("border_" . $side . "_style") . " " . $color["hex"];
1245
+ }
1246
+
1247
+ /**#@+
1248
+ * Return full border properties as a string
1249
+ *
1250
+ * Border properties are returned just as specified in CSS:
1251
+ * <pre>[width] [style] [color]</pre>
1252
+ * e.g. "1px solid blue"
1253
+ *
1254
+ * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
1255
+ * @return string
1256
+ */
1257
+ function get_border_top() {
1258
+ return $this->_get_border("top");
1259
+ }
1260
+
1261
+ function get_border_right() {
1262
+ return $this->_get_border("right");
1263
+ }
1264
+
1265
+ function get_border_bottom() {
1266
+ return $this->_get_border("bottom");
1267
+ }
1268
+
1269
+ function get_border_left() {
1270
+ return $this->_get_border("left");
1271
+ }
1272
+ /**#@-*/
1273
+
1274
+ function get_computed_border_radius($w, $h) {
1275
+ if ( !empty($this->_computed_border_radius) ) {
1276
+ return $this->_computed_border_radius;
1277
+ }
1278
+
1279
+ $rTL = $this->__get("border_top_left_radius");
1280
+ $rTR = $this->__get("border_top_right_radius");
1281
+ $rBL = $this->__get("border_bottom_left_radius");
1282
+ $rBR = $this->__get("border_bottom_right_radius");
1283
+
1284
+ if ( $rTL + $rTR + $rBL + $rBR == 0 ) {
1285
+ return $this->_computed_border_radius = array(
1286
+ 0, 0, 0, 0,
1287
+ "top-left" => 0,
1288
+ "top-right" => 0,
1289
+ "bottom-right" => 0,
1290
+ "bottom-left" => 0,
1291
+ );
1292
+ }
1293
+
1294
+ $t = $this->__get("border_top_width");
1295
+ $r = $this->__get("border_right_width");
1296
+ $b = $this->__get("border_bottom_width");
1297
+ $l = $this->__get("border_left_width");
1298
+
1299
+ $rTL = min($rTL, $h - $rBL - $t/2 - $b/2, $w - $rTR - $l/2 - $r/2);
1300
+ $rTR = min($rTR, $h - $rBR - $t/2 - $b/2, $w - $rTL - $l/2 - $r/2);
1301
+ $rBL = min($rBL, $h - $rTL - $t/2 - $b/2, $w - $rBR - $l/2 - $r/2);
1302
+ $rBR = min($rBR, $h - $rTR - $t/2 - $b/2, $w - $rBL - $l/2 - $r/2);
1303
+
1304
+ return $this->_computed_border_radius = array(
1305
+ $rTL, $rTR, $rBR, $rBL,
1306
+ "top-left" => $rTL,
1307
+ "top-right" => $rTR,
1308
+ "bottom-right" => $rBR,
1309
+ "bottom-left" => $rBL,
1310
+ );
1311
+ }
1312
+ /**#@-*/
1313
+
1314
+
1315
+ /**
1316
+ * Returns the outline colour as an array
1317
+ *
1318
+ * See {@link Style::get_color()}
1319
+ *
1320
+ * @link http://www.w3.org/TR/CSS21/box.html#border-color-properties
1321
+ * @return array
1322
+ */
1323
+ function get_outline_color() {
1324
+ if ( $this->_props["outline_color"] === "" ) {
1325
+ //see __set and __get, on all assignments clear cache!
1326
+ $this->_prop_cache["outline_color"] = null;
1327
+ $this->_props["outline_color"] = $this->__get("color");
1328
+ }
1329
+
1330
+ return $this->munge_color($this->_props["outline_color"]);
1331
+ }
1332
+
1333
+ /**#@+
1334
+ * Returns the outline width, as it is currently stored
1335
+ * @return float|string
1336
+ */
1337
+ function get_outline_width() {
1338
+ $style = $this->__get("outline_style");
1339
+ return $style !== "none" && $style !== "hidden" ? $this->length_in_pt($this->_props["outline_width"]) : 0;
1340
+ }
1341
+
1342
+ /**#@+
1343
+ * Return full outline properties as a string
1344
+ *
1345
+ * Outline properties are returned just as specified in CSS:
1346
+ * <pre>[width] [style] [color]</pre>
1347
+ * e.g. "1px solid blue"
1348
+ *
1349
+ * @link http://www.w3.org/TR/CSS21/box.html#border-shorthand-properties
1350
+ * @return string
1351
+ */
1352
+ function get_outline() {
1353
+ $color = $this->__get("outline_color");
1354
+ return
1355
+ $this->__get("outline_width") . " " .
1356
+ $this->__get("outline_style") . " " .
1357
+ $color["hex"];
1358
+ }
1359
+ /**#@-*/
1360
+
1361
+ /**
1362
+ * Returns border spacing as an array
1363
+ *
1364
+ * The array has the format (h_space,v_space)
1365
+ *
1366
+ * @link http://www.w3.org/TR/CSS21/tables.html#propdef-border-spacing
1367
+ * @return array
1368
+ */
1369
+ function get_border_spacing() {
1370
+ return explode(" ", $this->_props["border_spacing"]);
1371
+ }
1372
+
1373
+ /*==============================*/
1374
+
1375
+ /*
1376
+ !important attribute
1377
+ For basic functionality of the !important attribute with overloading
1378
+ of several styles of an element, changes in inherit(), merge() and _parse_properties()
1379
+ are sufficient [helpers var $_important_props, __construct(), important_set(), important_get()]
1380
+
1381
+ Only for combined attributes extra treatment needed. See below.
1382
+
1383
+ div { border: 1px red; }
1384
+ div { border: solid; } // Not combined! Only one occurence of same style per context
1385
+ //
1386
+ div { border: 1px red; }
1387
+ div a { border: solid; } // Adding to border style ok by inheritance
1388
+ //
1389
+ div { border-style: solid; } // Adding to border style ok because of different styles
1390
+ div { border: 1px red; }
1391
+ //
1392
+ div { border-style: solid; !important} // border: overrides, even though not !important
1393
+ div { border: 1px dashed red; }
1394
+ //
1395
+ div { border: 1px red; !important }
1396
+ div a { border-style: solid; } // Need to override because not set
1397
+
1398
+ Special treatment:
1399
+ At individual property like border-top-width need to check whether overriding value is also !important.
1400
+ Also store the !important condition for later overrides.
1401
+ Since not known who is initiating the override, need to get passed !important as parameter.
1402
+ !important Paramter taken as in the original style in the css file.
1403
+ When property border !important given, do not mark subsets like border_style as important. Only
1404
+ individual properties.
1405
+
1406
+ Note:
1407
+ Setting individual property directly from css with e.g. set_border_top_style() is not needed, because
1408
+ missing set funcions handled by a generic handler __set(), including the !important.
1409
+ Setting individual property of as sub-property is handled below.
1410
+
1411
+ Implementation see at _set_style_side_type()
1412
+ Callers _set_style_sides_type(), _set_style_type, _set_style_type_important()
1413
+
1414
+ Related functionality for background, padding, margin, font, list_style
1415
+ */
1416
+
1417
+ /* Generalized set function for individual attribute of combined style.
1418
+ * With check for !important
1419
+ * Applicable for background, border, padding, margin, font, list_style
1420
+ * Note: $type has a leading underscore (or is empty), the others not.
1421
+ */
1422
+ protected function _set_style_side_type($style, $side, $type, $val, $important) {
1423
+ $prop = $style.'_'.$side.$type;
1424
+
1425
+ if ( !isset($this->_important_props[$prop]) || $important) {
1426
+ //see __set and __get, on all assignments clear cache!
1427
+ $this->_prop_cache[$prop] = null;
1428
+ if ( $important ) {
1429
+ $this->_important_props[$prop] = true;
1430
+ }
1431
+ $this->_props[$prop] = $val;
1432
+ }
1433
+ }
1434
+
1435
+ protected function _set_style_sides_type($style,$top,$right,$bottom,$left,$type,$important) {
1436
+ $this->_set_style_side_type($style,'top',$type,$top,$important);
1437
+ $this->_set_style_side_type($style,'right',$type,$right,$important);
1438
+ $this->_set_style_side_type($style,'bottom',$type,$bottom,$important);
1439
+ $this->_set_style_side_type($style,'left',$type,$left,$important);
1440
+ }
1441
+
1442
+ protected function _set_style_type($style,$type,$val,$important) {
1443
+ $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
1444
+ $arr = explode(" ", $val);
1445
+
1446
+ switch (count($arr)) {
1447
+ case 1: $this->_set_style_sides_type($style,$arr[0],$arr[0],$arr[0],$arr[0],$type,$important); break;
1448
+ case 2: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[0],$arr[1],$type,$important); break;
1449
+ case 3: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[2],$arr[1],$type,$important); break;
1450
+ case 4: $this->_set_style_sides_type($style,$arr[0],$arr[1],$arr[2],$arr[3],$type,$important); break;
1451
+ }
1452
+
1453
+ //see __set and __get, on all assignments clear cache!
1454
+ $this->_prop_cache[$style.$type] = null;
1455
+ $this->_props[$style.$type] = $val;
1456
+ }
1457
+
1458
+ protected function _set_style_type_important($style,$type,$val) {
1459
+ $this->_set_style_type($style,$type,$val,isset($this->_important_props[$style.$type]));
1460
+ }
1461
+
1462
+ /* Anyway only called if _important matches and is assigned
1463
+ * E.g. _set_style_side_type($style,$side,'',str_replace("none", "0px", $val),isset($this->_important_props[$style.'_'.$side]));
1464
+ */
1465
+ protected function _set_style_side_width_important($style,$side,$val) {
1466
+ //see __set and __get, on all assignments clear cache!
1467
+ $this->_prop_cache[$style.'_'.$side] = null;
1468
+ $this->_props[$style.'_'.$side] = str_replace("none", "0px", $val);
1469
+ }
1470
+
1471
+ protected function _set_style($style,$val,$important) {
1472
+ if ( !isset($this->_important_props[$style]) || $important) {
1473
+ if ( $important ) {
1474
+ $this->_important_props[$style] = true;
1475
+ }
1476
+ //see __set and __get, on all assignments clear cache!
1477
+ $this->_prop_cache[$style] = null;
1478
+ $this->_props[$style] = $val;
1479
+ }
1480
+ }
1481
+
1482
+ protected function _image($val) {
1483
+ $DEBUGCSS=DEBUGCSS;
1484
+ $parsed_url = "none";
1485
+
1486
+ if ( mb_strpos($val, "url") === false ) {
1487
+ $path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none
1488
+ }
1489
+ else {
1490
+ $val = preg_replace("/url\(['\"]?([^'\")]+)['\"]?\)/","\\1", trim($val));
1491
+
1492
+ // Resolve the url now in the context of the current stylesheet
1493
+ $parsed_url = explode_url($val);
1494
+ if ( $parsed_url["protocol"] == "" && $this->_stylesheet->get_protocol() == "" ) {
1495
+ if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\' ) {
1496
+ $path = $_SERVER["DOCUMENT_ROOT"].'/';
1497
+ }
1498
+ else {
1499
+ $path = $this->_stylesheet->get_base_path();
1500
+ }
1501
+
1502
+ $path .= $parsed_url["path"] . $parsed_url["file"];
1503
+ $path = realpath($path);
1504
+ // If realpath returns FALSE then specifically state that there is no background image
1505
+ if ( !$path ) {
1506
+ $path = 'none';
1507
+ }
1508
+ }
1509
+ else {
1510
+ $path = build_url($this->_stylesheet->get_protocol(),
1511
+ $this->_stylesheet->get_host(),
1512
+ $this->_stylesheet->get_base_path(),
1513
+ $val);
1514
+ }
1515
+ }
1516
+ if ($DEBUGCSS) {
1517
+ print "<pre>[_image\n";
1518
+ print_r($parsed_url);
1519
+ print $this->_stylesheet->get_protocol()."\n".$this->_stylesheet->get_base_path()."\n".$path."\n";
1520
+ print "_image]</pre>";;
1521
+ }
1522
+ return $path;
1523
+ }
1524
+
1525
+ /*======================*/
1526
+
1527
+ /**
1528
+ * Sets colour
1529
+ *
1530
+ * The colour parameter can be any valid CSS colour value
1531
+ *
1532
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-color
1533
+ * @param string $color
1534
+ */
1535
+ function set_color($color) {
1536
+ $col = $this->munge_colour($color);
1537
+
1538
+ if ( is_null($col) || !isset($col["hex"]) ) {
1539
+ $color = "inherit";
1540
+ }
1541
+ else {
1542
+ $color = $col["hex"];
1543
+ }
1544
+
1545
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1546
+ $this->_prop_cache["color"] = null;
1547
+ $this->_props["color"] = $color;
1548
+ }
1549
+
1550
+ /**
1551
+ * Sets the background colour
1552
+ *
1553
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-color
1554
+ * @param string $color
1555
+ */
1556
+ function set_background_color($color) {
1557
+ $col = $this->munge_colour($color);
1558
+
1559
+ if ( is_null($col) ) {
1560
+ return;
1561
+ //$col = self::$_defaults["background_color"];
1562
+ }
1563
+
1564
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1565
+ $this->_prop_cache["background_color"] = null;
1566
+ $this->_props["background_color"] = is_array($col) ? $col["hex"] : $col;
1567
+ }
1568
+
1569
+ /**
1570
+ * Set the background image url
1571
+ * @link http://www.w3.org/TR/CSS21/colors.html#background-properties
1572
+ *
1573
+ * @param string $val
1574
+ */
1575
+ function set_background_image($val) {
1576
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1577
+ $this->_prop_cache["background_image"] = null;
1578
+ $this->_props["background_image"] = $this->_image($val);
1579
+ }
1580
+
1581
+ /**
1582
+ * Sets the background repeat
1583
+ *
1584
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-repeat
1585
+ * @param string $val
1586
+ */
1587
+ function set_background_repeat($val) {
1588
+ if ( is_null($val) ) {
1589
+ $val = self::$_defaults["background_repeat"];
1590
+ }
1591
+
1592
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1593
+ $this->_prop_cache["background_repeat"] = null;
1594
+ $this->_props["background_repeat"] = $val;
1595
+ }
1596
+
1597
+ /**
1598
+ * Sets the background attachment
1599
+ *
1600
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-attachment
1601
+ * @param string $val
1602
+ */
1603
+ function set_background_attachment($val) {
1604
+ if ( is_null($val) ) {
1605
+ $val = self::$_defaults["background_attachment"];
1606
+ }
1607
+
1608
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1609
+ $this->_prop_cache["background_attachment"] = null;
1610
+ $this->_props["background_attachment"] = $val;
1611
+ }
1612
+
1613
+ /**
1614
+ * Sets the background position
1615
+ *
1616
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background-position
1617
+ * @param string $val
1618
+ */
1619
+ function set_background_position($val) {
1620
+ if ( is_null($val) ) {
1621
+ $val = self::$_defaults["background_position"];
1622
+ }
1623
+
1624
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1625
+ $this->_prop_cache["background_position"] = null;
1626
+ $this->_props["background_position"] = $val;
1627
+ }
1628
+
1629
+ /**
1630
+ * Sets the background - combined options
1631
+ *
1632
+ * @link http://www.w3.org/TR/CSS21/colors.html#propdef-background
1633
+ * @param string $val
1634
+ */
1635
+ function set_background($val) {
1636
+ $val = trim($val);
1637
+ $important = isset($this->_important_props["background"]);
1638
+
1639
+ if ( $val === "none" ) {
1640
+ $this->_set_style("background_image", "none", $important);
1641
+ $this->_set_style("background_color", "transparent", $important);
1642
+ }
1643
+ else {
1644
+ $pos = array();
1645
+ $tmp = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
1646
+ $tmp = preg_split("/\s+/", $tmp);
1647
+
1648
+ foreach($tmp as $attr) {
1649
+ if ( mb_substr($attr, 0, 3) === "url" || $attr === "none" ) {
1650
+ $this->_set_style("background_image", $this->_image($attr), $important);
1651
+ }
1652
+ elseif ( $attr === "fixed" || $attr === "scroll" ) {
1653
+ $this->_set_style("background_attachment", $attr, $important);
1654
+ }
1655
+ elseif ( $attr === "repeat" || $attr === "repeat-x" || $attr === "repeat-y" || $attr === "no-repeat" ) {
1656
+ $this->_set_style("background_repeat", $attr, $important);
1657
+ }
1658
+ elseif ( ($col = $this->munge_color($attr)) != null ) {
1659
+ $this->_set_style("background_color", is_array($col) ? $col["hex"] : $col, $important);
1660
+ }
1661
+ else {
1662
+ $pos[] = $attr;
1663
+ }
1664
+ }
1665
+
1666
+ if (count($pos)) {
1667
+ $this->_set_style("background_position", implode(" ", $pos), $important);
1668
+ }
1669
+ }
1670
+
1671
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1672
+ $this->_prop_cache["background"] = null;
1673
+ $this->_props["background"] = $val;
1674
+ }
1675
+
1676
+ /**
1677
+ * Sets the font size
1678
+ *
1679
+ * $size can be any acceptable CSS size
1680
+ *
1681
+ * @link http://www.w3.org/TR/CSS21/fonts.html#propdef-font-size
1682
+ * @param string|float $size
1683
+ */
1684
+ function set_font_size($size) {
1685
+ $this->__font_size_calculated = false;
1686
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1687
+ $this->_prop_cache["font_size"] = null;
1688
+ $this->_props["font_size"] = $size;
1689
+ }
1690
+
1691
+ /**
1692
+ * Sets the font style
1693
+ *
1694
+ * combined attributes
1695
+ * set individual attributes also, respecting !important mark
1696
+ * exactly this order, separate by space. Multiple fonts separated by comma:
1697
+ * font-style, font-variant, font-weight, font-size, line-height, font-family
1698
+ *
1699
+ * Other than with border and list, existing partial attributes should
1700
+ * reset when starting here, even when not mentioned.
1701
+ * If individual attribute is !important and explicite or implicite replacement is not,
1702
+ * keep individual attribute
1703
+ *
1704
+ * require whitespace as delimiters for single value attributes
1705
+ * On delimiter "/" treat first as font height, second as line height
1706
+ * treat all remaining at the end of line as font
1707
+ * font-style, font-variant, font-weight, font-size, line-height, font-family
1708
+ *
1709
+ * missing font-size and font-family might be not allowed, but accept it here and
1710
+ * use default (medium size, enpty font name)
1711
+ *
1712
+ * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
1713
+ * @param $val
1714
+ */
1715
+ function set_font($val) {
1716
+ $this->__font_size_calculated = false;
1717
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1718
+ $this->_prop_cache["font"] = null;
1719
+ $this->_props["font"] = $val;
1720
+
1721
+ $important = isset($this->_important_props["font"]);
1722
+
1723
+ if ( preg_match("/^(italic|oblique|normal)\s*(.*)$/i",$val,$match) ) {
1724
+ $this->_set_style("font_style", $match[1], $important);
1725
+ $val = $match[2];
1726
+ }
1727
+ else {
1728
+ $this->_set_style("font_style", self::$_defaults["font_style"], $important);
1729
+ }
1730
+
1731
+ if ( preg_match("/^(small-caps|normal)\s*(.*)$/i",$val,$match) ) {
1732
+ $this->_set_style("font_variant", $match[1], $important);
1733
+ $val = $match[2];
1734
+ }
1735
+ else {
1736
+ $this->_set_style("font_variant", self::$_defaults["font_variant"], $important);
1737
+ }
1738
+
1739
+ //matching numeric value followed by unit -> this is indeed a subsequent font size. Skip!
1740
+ if ( preg_match("/^(bold|bolder|lighter|100|200|300|400|500|600|700|800|900|normal)\s*(.*)$/i", $val, $match) &&
1741
+ !preg_match("/^(?:pt|px|pc|em|ex|in|cm|mm|%)/",$match[2])
1742
+ ) {
1743
+ $this->_set_style("font_weight", $match[1], $important);
1744
+ $val = $match[2];
1745
+ }
1746
+ else {
1747
+ $this->_set_style("font_weight", self::$_defaults["font_weight"], $important);
1748
+ }
1749
+
1750
+ if ( preg_match("/^(xx-small|x-small|small|medium|large|x-large|xx-large|smaller|larger|\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))\s*(.*)$/i",$val,$match) ) {
1751
+ $this->_set_style("font_size", $match[1], $important);
1752
+ $val = $match[2];
1753
+ if ( preg_match("/^\/\s*(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))\s*(.*)$/i", $val, $match ) ) {
1754
+ $this->_set_style("line_height", $match[1], $important);
1755
+ $val = $match[2];
1756
+ }
1757
+ else {
1758
+ $this->_set_style("line_height", self::$_defaults["line_height"], $important);
1759
+ }
1760
+ }
1761
+ else {
1762
+ $this->_set_style("font_size", self::$_defaults["font_size"], $important);
1763
+ $this->_set_style("line_height", self::$_defaults["line_height"], $important);
1764
+ }
1765
+
1766
+ if( strlen($val) != 0 ) {
1767
+ $this->_set_style("font_family", $val, $important);
1768
+ }
1769
+ else {
1770
+ $this->_set_style("font_family", self::$_defaults["font_family"], $important);
1771
+ }
1772
+ }
1773
+
1774
+ /**#@+
1775
+ * Sets page break properties
1776
+ *
1777
+ * @link http://www.w3.org/TR/CSS21/page.html#page-breaks
1778
+ * @param string $break
1779
+ */
1780
+ function set_page_break_before($break) {
1781
+ if ( $break === "left" || $break === "right" ) {
1782
+ $break = "always";
1783
+ }
1784
+
1785
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1786
+ $this->_prop_cache["page_break_before"] = null;
1787
+ $this->_props["page_break_before"] = $break;
1788
+ }
1789
+
1790
+ function set_page_break_after($break) {
1791
+ if ( $break === "left" || $break === "right" ) {
1792
+ $break = "always";
1793
+ }
1794
+
1795
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1796
+ $this->_prop_cache["page_break_after"] = null;
1797
+ $this->_props["page_break_after"] = $break;
1798
+ }
1799
+ /**#@-*/
1800
+
1801
+ //........................................................................
1802
+
1803
+ /**#@+
1804
+ * Sets the margin size
1805
+ *
1806
+ * @link http://www.w3.org/TR/CSS21/box.html#margin-properties
1807
+ * @param $val
1808
+ */
1809
+ function set_margin_top($val) {
1810
+ $this->_set_style_side_width_important('margin','top',$val);
1811
+ }
1812
+
1813
+ function set_margin_right($val) {
1814
+ $this->_set_style_side_width_important('margin','right',$val);
1815
+ }
1816
+
1817
+ function set_margin_bottom($val) {
1818
+ $this->_set_style_side_width_important('margin','bottom',$val);
1819
+ }
1820
+
1821
+ function set_margin_left($val) {
1822
+ $this->_set_style_side_width_important('margin','left',$val);
1823
+ }
1824
+
1825
+ function set_margin($val) {
1826
+ $val = str_replace("none", "0px", $val);
1827
+ $this->_set_style_type_important('margin','',$val);
1828
+ }
1829
+ /**#@-*/
1830
+
1831
+ /**#@+
1832
+ * Sets the padding size
1833
+ *
1834
+ * @link http://www.w3.org/TR/CSS21/box.html#padding-properties
1835
+ * @param $val
1836
+ */
1837
+ function set_padding_top($val) {
1838
+ $this->_set_style_side_width_important('padding','top',$val);
1839
+ }
1840
+
1841
+ function set_padding_right($val) {
1842
+ $this->_set_style_side_width_important('padding','right',$val);
1843
+ }
1844
+
1845
+ function set_padding_bottom($val) {
1846
+ $this->_set_style_side_width_important('padding','bottom',$val);
1847
+ }
1848
+
1849
+ function set_padding_left($val) {
1850
+ $this->_set_style_side_width_important('padding','left',$val);
1851
+ }
1852
+
1853
+ function set_padding($val) {
1854
+ $val = str_replace("none", "0px", $val);
1855
+ $this->_set_style_type_important('padding','',$val);
1856
+ }
1857
+ /**#@-*/
1858
+
1859
+ /**
1860
+ * Sets a single border
1861
+ *
1862
+ * @param string $side
1863
+ * @param string $border_spec ([width] [style] [color])
1864
+ * @param boolean $important
1865
+ */
1866
+ protected function _set_border($side, $border_spec, $important) {
1867
+ $border_spec = preg_replace("/\s*\,\s*/", ",", $border_spec);
1868
+ //$border_spec = str_replace(",", " ", $border_spec); // Why did we have this ?? rbg(10, 102, 10) > rgb(10 102 10)
1869
+ $arr = explode(" ", $border_spec);
1870
+
1871
+ // FIXME: handle partial values
1872
+
1873
+ //For consistency of individal and combined properties, and with ie8 and firefox3
1874
+ //reset all attributes, even if only partially given
1875
+ $this->_set_style_side_type('border',$side,'_style',self::$_defaults['border_'.$side.'_style'],$important);
1876
+ $this->_set_style_side_type('border',$side,'_width',self::$_defaults['border_'.$side.'_width'],$important);
1877
+ $this->_set_style_side_type('border',$side,'_color',self::$_defaults['border_'.$side.'_color'],$important);
1878
+
1879
+ foreach ($arr as $value) {
1880
+ $value = trim($value);
1881
+ if ( in_array($value, self::$BORDER_STYLES) ) {
1882
+ $this->_set_style_side_type('border',$side,'_style',$value,$important);
1883
+ }
1884
+ else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value ) ) {
1885
+ $this->_set_style_side_type('border',$side,'_width',$value,$important);
1886
+ }
1887
+ else {
1888
+ // must be colour
1889
+ $this->_set_style_side_type('border',$side,'_color',$value,$important);
1890
+ }
1891
+ }
1892
+
1893
+ //see __set and __get, on all assignments clear cache!
1894
+ $this->_prop_cache['border_'.$side] = null;
1895
+ $this->_props['border_'.$side] = $border_spec;
1896
+ }
1897
+
1898
+ /**
1899
+ * Sets the border styles
1900
+ *
1901
+ * @link http://www.w3.org/TR/CSS21/box.html#border-properties
1902
+ * @param string $val
1903
+ */
1904
+ function set_border_top($val) {
1905
+ $this->_set_border("top", $val, isset($this->_important_props['border_top']));
1906
+ }
1907
+
1908
+ function set_border_right($val) {
1909
+ $this->_set_border("right", $val, isset($this->_important_props['border_right']));
1910
+ }
1911
+
1912
+ function set_border_bottom($val) {
1913
+ $this->_set_border("bottom", $val, isset($this->_important_props['border_bottom']));
1914
+ }
1915
+
1916
+ function set_border_left($val) {
1917
+ $this->_set_border("left", $val, isset($this->_important_props['border_left']));
1918
+ }
1919
+
1920
+ function set_border($val) {
1921
+ $important = isset($this->_important_props["border"]);
1922
+ $this->_set_border("top", $val, $important);
1923
+ $this->_set_border("right", $val, $important);
1924
+ $this->_set_border("bottom", $val, $important);
1925
+ $this->_set_border("left", $val, $important);
1926
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
1927
+ $this->_prop_cache["border"] = null;
1928
+ $this->_props["border"] = $val;
1929
+ }
1930
+
1931
+ function set_border_width($val) {
1932
+ $this->_set_style_type_important('border','_width',$val);
1933
+ }
1934
+
1935
+ function set_border_color($val) {
1936
+ $this->_set_style_type_important('border','_color',$val);
1937
+ }
1938
+
1939
+ function set_border_style($val) {
1940
+ $this->_set_style_type_important('border','_style',$val);
1941
+ }
1942
+
1943
+ /**
1944
+ * Sets the border radius size
1945
+ *
1946
+ * http://www.w3.org/TR/css3-background/#corners
1947
+ */
1948
+ function set_border_top_left_radius($val) {
1949
+ $this->_set_border_radius_corner($val, "top_left");
1950
+ }
1951
+
1952
+ function set_border_top_right_radius($val) {
1953
+ $this->_set_border_radius_corner($val, "top_right");
1954
+ }
1955
+
1956
+ function set_border_bottom_left_radius($val) {
1957
+ $this->_set_border_radius_corner($val, "bottom_left");
1958
+ }
1959
+
1960
+ function set_border_bottom_right_radius($val) {
1961
+ $this->_set_border_radius_corner($val, "bottom_right");
1962
+ }
1963
+
1964
+ function set_border_radius($val) {
1965
+ $val = preg_replace("/\s*\,\s*/", ",", $val); // when border-radius has spaces
1966
+ $arr = explode(" ", $val);
1967
+
1968
+ switch (count($arr)) {
1969
+ case 1: $this->_set_border_radii($arr[0],$arr[0],$arr[0],$arr[0]); break;
1970
+ case 2: $this->_set_border_radii($arr[0],$arr[1],$arr[0],$arr[1]); break;
1971
+ case 3: $this->_set_border_radii($arr[0],$arr[1],$arr[2],$arr[1]); break;
1972
+ case 4: $this->_set_border_radii($arr[0],$arr[1],$arr[2],$arr[3]); break;
1973
+ }
1974
+ }
1975
+
1976
+ protected function _set_border_radii($val1, $val2, $val3, $val4) {
1977
+ $this->_set_border_radius_corner($val1, "top_left");
1978
+ $this->_set_border_radius_corner($val2, "top_right");
1979
+ $this->_set_border_radius_corner($val3, "bottom_right");
1980
+ $this->_set_border_radius_corner($val4, "bottom_left");
1981
+ }
1982
+
1983
+ protected function _set_border_radius_corner($val, $corner) {
1984
+ $this->_has_border_radius = true;
1985
+
1986
+ //see __set and __get, on all assignments clear cache!
1987
+ $this->_prop_cache["border_" . $corner . "_radius"] = null;
1988
+
1989
+ $this->_props["border_" . $corner . "_radius"] = $this->length_in_pt($val);
1990
+ }
1991
+
1992
+ /**
1993
+ * Sets the outline styles
1994
+ *
1995
+ * @link http://www.w3.org/TR/CSS21/ui.html#dynamic-outlines
1996
+ * @param string $val
1997
+ */
1998
+ function set_outline($val) {
1999
+ $important = isset($this->_important_props["outline"]);
2000
+
2001
+ $props = array(
2002
+ "outline_style",
2003
+ "outline_width",
2004
+ "outline_color",
2005
+ );
2006
+
2007
+ foreach($props as $prop) {
2008
+ $_val = self::$_defaults[$prop];
2009
+
2010
+ if ( !isset($this->_important_props[$prop]) || $important) {
2011
+ //see __set and __get, on all assignments clear cache!
2012
+ $this->_prop_cache[$prop] = null;
2013
+ if ( $important ) {
2014
+ $this->_important_props[$prop] = true;
2015
+ }
2016
+ $this->_props[$prop] = $_val;
2017
+ }
2018
+ }
2019
+
2020
+ $val = preg_replace("/\s*\,\s*/", ",", $val); // when rgb() has spaces
2021
+ $arr = explode(" ", $val);
2022
+ foreach ($arr as $value) {
2023
+ $value = trim($value);
2024
+
2025
+ if ( in_array($value, self::$BORDER_STYLES) ) {
2026
+ $this->set_outline_style($value);
2027
+ }
2028
+ else if ( preg_match("/[.0-9]+(?:px|pt|pc|em|ex|%|in|mm|cm)|(?:thin|medium|thick)/", $value ) ) {
2029
+ $this->set_outline_width($value);
2030
+ }
2031
+ else {
2032
+ // must be colour
2033
+ $this->set_outline_color($value);
2034
+ }
2035
+ }
2036
+
2037
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2038
+ $this->_prop_cache["outline"] = null;
2039
+ $this->_props["outline"] = $val;
2040
+ }
2041
+
2042
+ function set_outline_width($val) {
2043
+ $this->_set_style_type_important('outline','_width',$val);
2044
+ }
2045
+
2046
+ function set_outline_color($val) {
2047
+ $this->_set_style_type_important('outline','_color',$val);
2048
+ }
2049
+
2050
+ function set_outline_style($val) {
2051
+ $this->_set_style_type_important('outline','_style',$val);
2052
+ }
2053
+
2054
+ /**
2055
+ * Sets the border spacing
2056
+ *
2057
+ * @link http://www.w3.org/TR/CSS21/box.html#border-properties
2058
+ * @param float $val
2059
+ */
2060
+ function set_border_spacing($val) {
2061
+ $arr = explode(" ", $val);
2062
+
2063
+ if ( count($arr) == 1 ) {
2064
+ $arr[1] = $arr[0];
2065
+ }
2066
+
2067
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2068
+ $this->_prop_cache["border_spacing"] = null;
2069
+ $this->_props["border_spacing"] = "$arr[0] $arr[1]";
2070
+ }
2071
+
2072
+ /**
2073
+ * Sets the list style image
2074
+ *
2075
+ * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style-image
2076
+ * @param $val
2077
+ */
2078
+ function set_list_style_image($val) {
2079
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2080
+ $this->_prop_cache["list_style_image"] = null;
2081
+ $this->_props["list_style_image"] = $this->_image($val);
2082
+ }
2083
+
2084
+ /**
2085
+ * Sets the list style
2086
+ *
2087
+ * @link http://www.w3.org/TR/CSS21/generate.html#propdef-list-style
2088
+ * @param $val
2089
+ */
2090
+ function set_list_style($val) {
2091
+ $important = isset($this->_important_props["list_style"]);
2092
+ $arr = explode(" ", str_replace(",", " ", $val));
2093
+
2094
+ static $types = array(
2095
+ "disc", "circle", "square",
2096
+ "decimal-leading-zero", "decimal", "1",
2097
+ "lower-roman", "upper-roman", "a", "A",
2098
+ "lower-greek",
2099
+ "lower-latin", "upper-latin",
2100
+ "lower-alpha", "upper-alpha",
2101
+ "armenian", "georgian", "hebrew",
2102
+ "cjk-ideographic", "hiragana", "katakana",
2103
+ "hiragana-iroha", "katakana-iroha", "none"
2104
+ );
2105
+
2106
+ static $positions = array("inside", "outside");
2107
+
2108
+ foreach ($arr as $value) {
2109
+ /* http://www.w3.org/TR/CSS21/generate.html#list-style
2110
+ * A value of 'none' for the 'list-style' property sets both 'list-style-type' and 'list-style-image' to 'none'
2111
+ */
2112
+ if ( $value === "none" ) {
2113
+ $this->_set_style("list_style_type", $value, $important);
2114
+ $this->_set_style("list_style_image", $value, $important);
2115
+ continue;
2116
+ }
2117
+
2118
+ //On setting or merging or inheriting list_style_image as well as list_style_type,
2119
+ //and url exists, then url has precedence, otherwise fall back to list_style_type
2120
+ //Firefox is wrong here (list_style_image gets overwritten on explicite list_style_type)
2121
+ //Internet Explorer 7/8 and dompdf is right.
2122
+
2123
+ if ( mb_substr($value, 0, 3) === "url" ) {
2124
+ $this->_set_style("list_style_image", $this->_image($value), $important);
2125
+ continue;
2126
+ }
2127
+
2128
+ if ( in_array($value, $types) ) {
2129
+ $this->_set_style("list_style_type", $value, $important);
2130
+ }
2131
+ else if ( in_array($value, $positions) ) {
2132
+ $this->_set_style("list_style_position", $value, $important);
2133
+ }
2134
+ }
2135
+
2136
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2137
+ $this->_prop_cache["list_style"] = null;
2138
+ $this->_props["list_style"] = $val;
2139
+ }
2140
+
2141
+ function set_size($val) {
2142
+ $length_re = "/(\d+\s*(?:pt|px|pc|em|ex|in|cm|mm|%))/";
2143
+
2144
+ $val = mb_strtolower($val);
2145
+
2146
+ if ( $val === "auto" ) {
2147
+ return;
2148
+ }
2149
+
2150
+ $parts = preg_split("/\s+/", $val);
2151
+
2152
+ $computed = array();
2153
+ if ( preg_match($length_re, $parts[0]) ) {
2154
+ $computed[] = $this->length_in_pt($parts[0]);
2155
+
2156
+ if ( isset($parts[1]) && preg_match($length_re, $parts[1]) ) {
2157
+ $computed[] = $this->length_in_pt($parts[1]);
2158
+ }
2159
+ else {
2160
+ $computed[] = $computed[0];
2161
+ }
2162
+ }
2163
+ elseif ( isset(CPDF_Adapter::$PAPER_SIZES[$parts[0]]) ) {
2164
+ $computed = array_slice(CPDF_Adapter::$PAPER_SIZES[$parts[0]], 2, 2);
2165
+
2166
+ if ( isset($parts[1]) && $parts[1] === "landscape" ) {
2167
+ $computed = array_reverse($computed);
2168
+ }
2169
+ }
2170
+ else {
2171
+ return;
2172
+ }
2173
+
2174
+ $this->_props["size"] = $computed;
2175
+ }
2176
+
2177
+ /**
2178
+ * Sets the CSS3 transform property
2179
+ *
2180
+ * @link http://www.w3.org/TR/css3-2d-transforms/#transform-property
2181
+ * @param string $val
2182
+ */
2183
+ function set_transform($val) {
2184
+ $number = "\s*([^,\s]+)\s*";
2185
+ $tr_value = "\s*([^,\s]+)\s*";
2186
+ $angle = "\s*([^,\s]+(?:deg|rad)?)\s*";
2187
+
2188
+ if ( !preg_match_all("/[a-z]+\([^\)]+\)/i", $val, $parts, PREG_SET_ORDER) ) {
2189
+ return;
2190
+ }
2191
+
2192
+ $functions = array(
2193
+ //"matrix" => "\($number,$number,$number,$number,$number,$number\)",
2194
+
2195
+ "translate" => "\($tr_value(?:,$tr_value)?\)",
2196
+ "translateX" => "\($tr_value\)",
2197
+ "translateY" => "\($tr_value\)",
2198
+
2199
+ "scale" => "\($number(?:,$number)?\)",
2200
+ "scaleX" => "\($number\)",
2201
+ "scaleY" => "\($number\)",
2202
+
2203
+ "rotate" => "\($angle\)",
2204
+
2205
+ "skew" => "\($angle(?:,$angle)?\)",
2206
+ "skewX" => "\($angle\)",
2207
+ "skewY" => "\($angle\)",
2208
+ );
2209
+
2210
+ $transforms = array();
2211
+
2212
+ foreach($parts as $part) {
2213
+ $t = $part[0];
2214
+
2215
+ foreach($functions as $name => $pattern) {
2216
+ if ( preg_match("/$name\s*$pattern/i", $t, $matches) ) {
2217
+ $values = array_slice($matches, 1);
2218
+
2219
+ switch($name) {
2220
+ // <angle> units
2221
+ case "rotate":
2222
+ case "skew":
2223
+ case "skewX":
2224
+ case "skewY":
2225
+
2226
+ foreach($values as $i => $value) {
2227
+ if ( strpos($value, "rad") ) {
2228
+ $values[$i] = rad2deg(floatval($value));
2229
+ }
2230
+ else {
2231
+ $values[$i] = floatval($value);
2232
+ }
2233
+ }
2234
+
2235
+ switch($name) {
2236
+ case "skew":
2237
+ if ( !isset($values[1]) ) {
2238
+ $values[1] = 0;
2239
+ }
2240
+ break;
2241
+ case "skewX":
2242
+ $name = "skew";
2243
+ $values = array($values[0], 0);
2244
+ break;
2245
+ case "skewY":
2246
+ $name = "skew";
2247
+ $values = array(0, $values[0]);
2248
+ break;
2249
+ }
2250
+ break;
2251
+
2252
+ // <translation-value> units
2253
+ case "translate":
2254
+ $values[0] = $this->length_in_pt($values[0], $this->width);
2255
+
2256
+ if ( isset($values[1]) ) {
2257
+ $values[1] = $this->length_in_pt($values[1], $this->height);
2258
+ }
2259
+ else {
2260
+ $values[1] = 0;
2261
+ }
2262
+ break;
2263
+
2264
+ case "translateX":
2265
+ $name = "translate";
2266
+ $values = array($this->length_in_pt($values[0], $this->width), 0);
2267
+ break;
2268
+
2269
+ case "translateY":
2270
+ $name = "translate";
2271
+ $values = array(0, $this->length_in_pt($values[0], $this->height));
2272
+ break;
2273
+
2274
+ // <number> units
2275
+ case "scale":
2276
+ if ( !isset($values[1]) ) {
2277
+ $values[1] = $values[0];
2278
+ }
2279
+ break;
2280
+
2281
+ case "scaleX":
2282
+ $name = "scale";
2283
+ $values = array($values[0], 1.0);
2284
+ break;
2285
+
2286
+ case "scaleY":
2287
+ $name = "scale";
2288
+ $values = array(1.0, $values[0]);
2289
+ break;
2290
+ }
2291
+
2292
+ $transforms[] = array(
2293
+ $name,
2294
+ $values,
2295
+ );
2296
+ }
2297
+ }
2298
+ }
2299
+
2300
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2301
+ $this->_prop_cache["transform"] = null;
2302
+ $this->_props["transform"] = $transforms;
2303
+ }
2304
+
2305
+ function set__webkit_transform($val) {
2306
+ $this->set_transform($val);
2307
+ }
2308
+
2309
+ function set__webkit_transform_origin($val) {
2310
+ $this->set_transform_origin($val);
2311
+ }
2312
+
2313
+ /**
2314
+ * Sets the CSS3 transform-origin property
2315
+ *
2316
+ * @link http://www.w3.org/TR/css3-2d-transforms/#transform-origin
2317
+ * @param string $val
2318
+ */
2319
+ function set_transform_origin($val) {
2320
+ $values = preg_split("/\s+/", $val);
2321
+
2322
+ if ( count($values) === 0) {
2323
+ return;
2324
+ }
2325
+
2326
+ foreach($values as &$value) {
2327
+ if ( in_array($value, array("top", "left")) ) {
2328
+ $value = 0;
2329
+ }
2330
+
2331
+ if ( in_array($value, array("bottom", "right")) ) {
2332
+ $value = "100%";
2333
+ }
2334
+ }
2335
+
2336
+ if ( !isset($values[1]) ) {
2337
+ $values[1] = $values[0];
2338
+ }
2339
+
2340
+ //see __set and __get, on all assignments clear cache, not needed on direct set through __set
2341
+ $this->_prop_cache["transform_origin"] = null;
2342
+ $this->_props["transform_origin"] = $values;
2343
+ }
2344
+
2345
+ protected function parse_image_resolution($val) {
2346
+ // If exif data could be get:
2347
+ // $re = '/^\s*(\d+|normal|auto)(?:\s*,\s*(\d+|normal))?\s*$/';
2348
+
2349
+ $re = '/^\s*(\d+|normal|auto)\s*$/';
2350
+
2351
+ if ( !preg_match($re, $val, $matches) ) {
2352
+ return null;
2353
+ }
2354
+
2355
+ return $matches[1];
2356
+ }
2357
+
2358
+ // auto | normal | dpi
2359
+ function set_background_image_resolution($val) {
2360
+ $parsed = $this->parse_image_resolution($val);
2361
+
2362
+ $this->_prop_cache["background_image_resolution"] = null;
2363
+ $this->_props["background_image_resolution"] = $parsed;
2364
+ }
2365
+
2366
+ // auto | normal | dpi
2367
+ function set_image_resolution($val) {
2368
+ $parsed = $this->parse_image_resolution($val);
2369
+
2370
+ $this->_prop_cache["image_resolution"] = null;
2371
+ $this->_props["image_resolution"] = $parsed;
2372
+ }
2373
+
2374
+ function set__dompdf_background_image_resolution($val) {
2375
+ $this->set_background_image_resolution($val);
2376
+ }
2377
+
2378
+ function set__dompdf_image_resolution($val) {
2379
+ $this->set_image_resolution($val);
2380
+ }
2381
+
2382
+ function set_z_index($val) {
2383
+ if ( round($val) != $val && $val !== "auto" ) {
2384
+ return;
2385
+ }
2386
+
2387
+ $this->_prop_cache["z_index"] = null;
2388
+ $this->_props["z_index"] = $val;
2389
+ }
2390
+
2391
+ function set_counter_increment($val) {
2392
+ $val = trim($val);
2393
+ $value = null;
2394
+
2395
+ if ( in_array($val, array("none", "inherit")) ) {
2396
+ $value = $val;
2397
+ }
2398
+ else {
2399
+ if ( preg_match_all("/(".self::CSS_IDENTIFIER.")(?:\s+(".self::CSS_INTEGER."))?/", $val, $matches, PREG_SET_ORDER) ){
2400
+ $value = array();
2401
+ foreach($matches as $match) {
2402
+ $value[$match[1]] = isset($match[2]) ? $match[2] : 1;
2403
+ }
2404
+ }
2405
+ }
2406
+
2407
+ $this->_prop_cache["counter_increment"] = null;
2408
+ $this->_props["counter_increment"] = $value;
2409
+ }
2410
+
2411
+ /**
2412
+ * Generate a string representation of the Style
2413
+ *
2414
+ * This dumps the entire property array into a string via print_r. Useful
2415
+ * for debugging.
2416
+ *
2417
+ * @return string
2418
+ */
2419
+ /*DEBUGCSS print: see below additional debugging util*/
2420
+ function __toString() {
2421
+ return print_r(array_merge(array("parent_font_size" => $this->_parent_font_size),
2422
+ $this->_props), true);
2423
+ }
2424
+
2425
+ /*DEBUGCSS*/ function debug_print() {
2426
+ /*DEBUGCSS*/ print "parent_font_size:".$this->_parent_font_size . ";\n";
2427
+ /*DEBUGCSS*/ foreach($this->_props as $prop => $val ) {
2428
+ /*DEBUGCSS*/ print $prop.':'.$val;
2429
+ /*DEBUGCSS*/ if (isset($this->_important_props[$prop])) {
2430
+ /*DEBUGCSS*/ print '!important';
2431
+ /*DEBUGCSS*/ }
2432
+ /*DEBUGCSS*/ print ";\n";
2433
+ /*DEBUGCSS*/ }
2434
+ /*DEBUGCSS*/ }
2435
+ }
dompdf/include/stylesheet.cls.php ADDED
@@ -0,0 +1,1434 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * The location of the default built-in CSS file.
13
+ * {@link Stylesheet::DEFAULT_STYLESHEET}
14
+ */
15
+ define('__DEFAULT_STYLESHEET', DOMPDF_LIB_DIR . DIRECTORY_SEPARATOR . "res" . DIRECTORY_SEPARATOR . "html.css");
16
+
17
+ /**
18
+ * The master stylesheet class
19
+ *
20
+ * The Stylesheet class is responsible for parsing stylesheets and style
21
+ * tags/attributes. It also acts as a registry of the individual Style
22
+ * objects generated by the current set of loaded CSS files and style
23
+ * elements.
24
+ *
25
+ * @see Style
26
+ * @package dompdf
27
+ */
28
+ class Stylesheet {
29
+
30
+ /**
31
+ * The location of the default built-in CSS file.
32
+ */
33
+ const DEFAULT_STYLESHEET = __DEFAULT_STYLESHEET;
34
+
35
+ /**
36
+ * User agent stylesheet origin
37
+ *
38
+ * @var int
39
+ */
40
+ const ORIG_UA = 1;
41
+
42
+ /**
43
+ * User normal stylesheet origin
44
+ *
45
+ * @var int
46
+ */
47
+ const ORIG_USER = 2;
48
+
49
+ /**
50
+ * Author normal stylesheet origin
51
+ *
52
+ * @var int
53
+ */
54
+ const ORIG_AUTHOR = 3;
55
+
56
+ private static $_stylesheet_origins = array(
57
+ self::ORIG_UA => -0x0FFFFFFF, // user agent style sheets
58
+ self::ORIG_USER => -0x0000FFFF, // user normal style sheets
59
+ self::ORIG_AUTHOR => 0x00000000, // author normal style sheets
60
+ );
61
+
62
+ /**
63
+ * Current dompdf instance
64
+ *
65
+ * @var DOMPDF
66
+ */
67
+ private $_dompdf;
68
+
69
+ /**
70
+ * Array of currently defined styles
71
+ *
72
+ * @var Style[]
73
+ */
74
+ private $_styles;
75
+
76
+ /**
77
+ * Base protocol of the document being parsed
78
+ * Used to handle relative urls.
79
+ *
80
+ * @var string
81
+ */
82
+ private $_protocol;
83
+
84
+ /**
85
+ * Base hostname of the document being parsed
86
+ * Used to handle relative urls.
87
+ *
88
+ * @var string
89
+ */
90
+ private $_base_host;
91
+
92
+ /**
93
+ * Base path of the document being parsed
94
+ * Used to handle relative urls.
95
+ *
96
+ * @var string
97
+ */
98
+ private $_base_path;
99
+
100
+ /**
101
+ * The styles defined by @page rules
102
+ *
103
+ * @var array<Style>
104
+ */
105
+ private $_page_styles;
106
+
107
+ /**
108
+ * List of loaded files, used to prevent recursion
109
+ *
110
+ * @var array
111
+ */
112
+ private $_loaded_files;
113
+
114
+ /**
115
+ * Current stylesheet origin
116
+ *
117
+ * @var int
118
+ */
119
+ private $_current_origin = self::ORIG_UA;
120
+
121
+ /**
122
+ * Accepted CSS media types
123
+ * List of types and parsing rules for future extensions:
124
+ * http://www.w3.org/TR/REC-html40/types.html
125
+ * screen, tty, tv, projection, handheld, print, braille, aural, all
126
+ * The following are non standard extensions for undocumented specific environments.
127
+ * static, visual, bitmap, paged, dompdf
128
+ * Note, even though the generated pdf file is intended for print output,
129
+ * the desired content might be different (e.g. screen or projection view of html file).
130
+ * Therefore allow specification of content by dompdf setting DOMPDF_DEFAULT_MEDIA_TYPE.
131
+ * If given, replace media "print" by DOMPDF_DEFAULT_MEDIA_TYPE.
132
+ * (Previous version $ACCEPTED_MEDIA_TYPES = $ACCEPTED_GENERIC_MEDIA_TYPES + $ACCEPTED_DEFAULT_MEDIA_TYPE)
133
+ */
134
+ static $ACCEPTED_DEFAULT_MEDIA_TYPE = "print";
135
+ static $ACCEPTED_GENERIC_MEDIA_TYPES = array("all", "static", "visual", "bitmap", "paged", "dompdf");
136
+
137
+ /**
138
+ * The class constructor.
139
+ *
140
+ * The base protocol, host & path are initialized to those of
141
+ * the current script.
142
+ */
143
+ function __construct(DOMPDF $dompdf) {
144
+ $this->_dompdf = $dompdf;
145
+ $this->_styles = array();
146
+ $this->_loaded_files = array();
147
+ list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($_SERVER["SCRIPT_FILENAME"]);
148
+ $this->_page_styles = array("base" => null);
149
+ }
150
+
151
+ /**
152
+ * Class destructor
153
+ */
154
+ function __destruct() {
155
+ clear_object($this);
156
+ }
157
+
158
+ /**
159
+ * Set the base protocol
160
+ *
161
+ * @param string $protocol
162
+ */
163
+ function set_protocol($protocol) { $this->_protocol = $protocol; }
164
+
165
+ /**
166
+ * Set the base host
167
+ *
168
+ * @param string $host
169
+ */
170
+ function set_host($host) { $this->_base_host = $host; }
171
+
172
+ /**
173
+ * Set the base path
174
+ *
175
+ * @param string $path
176
+ */
177
+ function set_base_path($path) { $this->_base_path = $path; }
178
+
179
+ /**
180
+ * Return the DOMPDF object
181
+ *
182
+ * @return DOMPDF
183
+ */
184
+ function get_dompdf() { return $this->_dompdf; }
185
+
186
+ /**
187
+ * Return the base protocol for this stylesheet
188
+ *
189
+ * @return string
190
+ */
191
+ function get_protocol() { return $this->_protocol; }
192
+
193
+ /**
194
+ * Return the base host for this stylesheet
195
+ *
196
+ * @return string
197
+ */
198
+ function get_host() { return $this->_base_host; }
199
+
200
+ /**
201
+ * Return the base path for this stylesheet
202
+ *
203
+ * @return string
204
+ */
205
+ function get_base_path() { return $this->_base_path; }
206
+
207
+ /**
208
+ * Return the array of page styles
209
+ *
210
+ * @return Style[]
211
+ */
212
+ function get_page_styles() { return $this->_page_styles; }
213
+
214
+ /**
215
+ * Add a new Style object to the stylesheet
216
+ * add_style() adds a new Style object to the current stylesheet, or
217
+ * merges a new Style with an existing one.
218
+ *
219
+ * @param string $key the Style's selector
220
+ * @param Style $style the Style to be added
221
+ *
222
+ * @throws DOMPDF_Exception
223
+ */
224
+ function add_style($key, Style $style) {
225
+ if ( !is_string($key) ) {
226
+ throw new DOMPDF_Exception("CSS rule must be keyed by a string.");
227
+ }
228
+
229
+ if ( isset($this->_styles[$key]) ) {
230
+ $this->_styles[$key]->merge($style);
231
+ }
232
+ else {
233
+ $this->_styles[$key] = clone $style;
234
+ }
235
+
236
+ $this->_styles[$key]->set_origin( $this->_current_origin );
237
+ }
238
+
239
+ /**
240
+ * lookup a specifc Style object
241
+ *
242
+ * lookup() returns the Style specified by $key, or null if the Style is
243
+ * not found.
244
+ *
245
+ * @param string $key the selector of the requested Style
246
+ * @return Style
247
+ */
248
+ function lookup($key) {
249
+ if ( !isset($this->_styles[$key]) ) {
250
+ return null;
251
+ }
252
+
253
+ return $this->_styles[$key];
254
+ }
255
+
256
+ /**
257
+ * create a new Style object associated with this stylesheet
258
+ *
259
+ * @param Style $parent The style of this style's parent in the DOM tree
260
+ * @return Style
261
+ */
262
+ function create_style(Style $parent = null) {
263
+ return new Style($this, $this->_current_origin);
264
+ }
265
+
266
+ /**
267
+ * load and parse a CSS string
268
+ *
269
+ * @param string $css
270
+ */
271
+ function load_css(&$css) { $this->_parse_css($css); }
272
+
273
+
274
+ /**
275
+ * load and parse a CSS file
276
+ *
277
+ * @param string $file
278
+ * @param int $origin
279
+ */
280
+ function load_css_file($file, $origin = self::ORIG_AUTHOR) {
281
+ if ( $origin ) {
282
+ $this->_current_origin = $origin;
283
+ }
284
+
285
+ // Prevent circular references
286
+ if ( isset($this->_loaded_files[$file]) ) {
287
+ return;
288
+ }
289
+
290
+ $this->_loaded_files[$file] = true;
291
+
292
+ if ( strpos($file, "data:") === 0) {
293
+ $parsed = parse_data_uri($file);
294
+ $css = $parsed["data"];
295
+ }
296
+ else {
297
+ $parsed_url = explode_url($file);
298
+
299
+ list($this->_protocol, $this->_base_host, $this->_base_path, $filename) = $parsed_url;
300
+
301
+ // Fix submitted by Nick Oostveen for aliased directory support:
302
+ if ( $this->_protocol == "" ) {
303
+ $file = $this->_base_path . $filename;
304
+ }
305
+ else {
306
+ $file = build_url($this->_protocol, $this->_base_host, $this->_base_path, $filename);
307
+ }
308
+
309
+ set_error_handler("record_warnings");
310
+ $css = file_get_contents($file, null, $this->_dompdf->get_http_context());
311
+ restore_error_handler();
312
+
313
+ $good_mime_type = true;
314
+
315
+ // See http://the-stickman.com/web-development/php/getting-http-response-headers-when-using-file_get_contents/
316
+ if ( isset($http_response_header) && !$this->_dompdf->get_quirksmode() ) {
317
+ foreach($http_response_header as $_header) {
318
+ if ( preg_match("@Content-Type:\s*([\w/]+)@i", $_header, $matches) &&
319
+ ($matches[1] !== "text/css") ) {
320
+ $good_mime_type = false;
321
+ }
322
+ }
323
+ }
324
+
325
+ if ( !$good_mime_type || $css == "" ) {
326
+ if($css == "")
327
+ {
328
+ if( !class_exists( 'WP_Http' ) ) {
329
+ include_once( ABSPATH . WPINC. '/class-http.php' );
330
+ }
331
+ $request = new WP_Http();
332
+ $result = $request->request($file);
333
+ if ( !is_wp_error($result) ) {
334
+ $css = $result['body'];
335
+ }
336
+ }
337
+
338
+ if($css == "")
339
+ {
340
+ record_warnings(E_USER_WARNING, "Unable to load css file $file", __FILE__, __LINE__);
341
+ return;
342
+ }
343
+ }
344
+ }
345
+
346
+ $this->_parse_css($css);
347
+ }
348
+
349
+ /**
350
+ * @link http://www.w3.org/TR/CSS21/cascade.html#specificity
351
+ *
352
+ * @param string $selector
353
+ * @param int $origin :
354
+ * - ua: user agent style sheets
355
+ * - un: user normal style sheets
356
+ * - an: author normal style sheets
357
+ * - ai: author important style sheets
358
+ * - ui: user important style sheets
359
+ *
360
+ * @return int
361
+ */
362
+ private function _specificity($selector, $origin = self::ORIG_AUTHOR) {
363
+ // http://www.w3.org/TR/CSS21/cascade.html#specificity
364
+ // ignoring the ":" pseudoclass modifyers
365
+ // also ignored in _css_selector_to_xpath
366
+
367
+ $a = ($selector === "!attr") ? 1 : 0;
368
+
369
+ $b = min(mb_substr_count($selector, "#"), 255);
370
+
371
+ $c = min(mb_substr_count($selector, ".") +
372
+ mb_substr_count($selector, "["), 255);
373
+
374
+ $d = min(mb_substr_count($selector, " ") +
375
+ mb_substr_count($selector, ">") +
376
+ mb_substr_count($selector, "+"), 255);
377
+
378
+ //If a normal element name is at the begining of the string,
379
+ //a leading whitespace might have been removed on whitespace collapsing and removal
380
+ //therefore there might be one whitespace less as selected element names
381
+ //this can lead to a too small specificity
382
+ //see _css_selector_to_xpath
383
+
384
+ if ( !in_array($selector[0], array(" ", ">", ".", "#", "+", ":", "["))/* && $selector !== "*"*/) {
385
+ $d++;
386
+ }
387
+
388
+ if (DEBUGCSS) {
389
+ /*DEBUGCSS*/ print "<pre>\n";
390
+ /*DEBUGCSS*/ printf("_specificity(): 0x%08x \"%s\"\n", ($a << 24) | ($b << 16) | ($c << 8) | ($d), $selector);
391
+ /*DEBUGCSS*/ print "</pre>";
392
+ }
393
+
394
+ return self::$_stylesheet_origins[$origin] + ($a << 24) | ($b << 16) | ($c << 8) | ($d);
395
+ }
396
+
397
+ /**
398
+ * Converts a CSS selector to an XPath query.
399
+ *
400
+ * @param string $selector
401
+ * @param bool $first_pass
402
+ *
403
+ * @throws DOMPDF_Exception
404
+ * @return string
405
+ */
406
+ private function _css_selector_to_xpath($selector, $first_pass = false) {
407
+
408
+ // Collapse white space and strip whitespace around delimiters
409
+ // $search = array("/\\s+/", "/\\s+([.>#+:])\\s+/");
410
+ // $replace = array(" ", "\\1");
411
+ // $selector = preg_replace($search, $replace, trim($selector));
412
+
413
+ // Initial query (non-absolute)
414
+ $query = "//";
415
+
416
+ // Will contain :before and :after if they must be created
417
+ $pseudo_elements = array();
418
+
419
+ // Parse the selector
420
+ //$s = preg_split("/([ :>.#+])/", $selector, -1, PREG_SPLIT_DELIM_CAPTURE);
421
+
422
+ $delimiters = array(" ", ">", ".", "#", "+", ":", "[", "(");
423
+
424
+ // Add an implicit * at the beginning of the selector
425
+ // if it begins with an attribute selector
426
+ if ( $selector[0] === "[" ) {
427
+ $selector = "*$selector";
428
+ }
429
+
430
+ // Add an implicit space at the beginning of the selector if there is no
431
+ // delimiter there already.
432
+ if ( !in_array($selector[0], $delimiters) ) {
433
+ $selector = " $selector";
434
+ }
435
+
436
+ $tok = "";
437
+ $len = mb_strlen($selector);
438
+ $i = 0;
439
+
440
+ while ( $i < $len ) {
441
+
442
+ $s = $selector[$i];
443
+ $i++;
444
+
445
+ // Eat characters up to the next delimiter
446
+ $tok = "";
447
+ $in_attr = false;
448
+
449
+ while ($i < $len) {
450
+ $c = $selector[$i];
451
+ $c_prev = $selector[$i-1];
452
+
453
+ if ( !$in_attr && in_array($c, $delimiters) ) {
454
+ break;
455
+ }
456
+
457
+ if ( $c_prev === "[" ) {
458
+ $in_attr = true;
459
+ }
460
+
461
+ $tok .= $selector[$i++];
462
+
463
+ if ( $in_attr && $c === "]" ) {
464
+ $in_attr = false;
465
+ break;
466
+ }
467
+ }
468
+
469
+ switch ($s) {
470
+
471
+ case " ":
472
+ case ">":
473
+ // All elements matching the next token that are direct children of
474
+ // the current token
475
+ $expr = $s === " " ? "descendant" : "child";
476
+
477
+ if ( mb_substr($query, -1, 1) !== "/" ) {
478
+ $query .= "/";
479
+ }
480
+
481
+ // Tag names are case-insensitive
482
+ $tok = strtolower($tok);
483
+
484
+ if ( !$tok ) {
485
+ $tok = "*";
486
+ }
487
+
488
+ $query .= "$expr::$tok";
489
+ $tok = "";
490
+ break;
491
+
492
+ case ".":
493
+ case "#":
494
+ // All elements matching the current token with a class/id equal to
495
+ // the _next_ token.
496
+
497
+ $attr = $s === "." ? "class" : "id";
498
+
499
+ // empty class/id == *
500
+ if ( mb_substr($query, -1, 1) === "/" ) {
501
+ $query .= "*";
502
+ }
503
+
504
+ // Match multiple classes: $tok contains the current selected
505
+ // class. Search for class attributes with class="$tok",
506
+ // class=".* $tok .*" and class=".* $tok"
507
+
508
+ // This doesn't work because libxml only supports XPath 1.0...
509
+ //$query .= "[matches(@$attr,\"^${tok}\$|^${tok}[ ]+|[ ]+${tok}\$|[ ]+${tok}[ ]+\")]";
510
+
511
+ // Query improvement by Michael Sheakoski <michael@mjsdigital.com>:
512
+ $query .= "[contains(concat(' ', @$attr, ' '), concat(' ', '$tok', ' '))]";
513
+ $tok = "";
514
+ break;
515
+
516
+ case "+":
517
+ // All sibling elements that folow the current token
518
+ if ( mb_substr($query, -1, 1) !== "/" ) {
519
+ $query .= "/";
520
+ }
521
+
522
+ $query .= "following-sibling::$tok";
523
+ $tok = "";
524
+ break;
525
+
526
+ case ":":
527
+ $i2 = $i-strlen($tok)-2; // the char before ":"
528
+ if ( !isset($selector[$i2]) || in_array($selector[$i2], $delimiters) ) {
529
+ $query .= "*";
530
+ }
531
+
532
+ $last = false;
533
+
534
+ // Pseudo-classes
535
+ switch ($tok) {
536
+
537
+ case "first-child":
538
+ $query .= "[1]";
539
+ $tok = "";
540
+ break;
541
+
542
+ case "last-child":
543
+ $query .= "[not(following-sibling::*)]";
544
+ $tok = "";
545
+ break;
546
+
547
+ case "first-of-type":
548
+ $query .= "[position() = 1]";
549
+ $tok = "";
550
+ break;
551
+
552
+ case "last-of-type":
553
+ $query .= "[position() = last()]";
554
+ $tok = "";
555
+ break;
556
+
557
+ // an+b, n, odd, and even
558
+ case "nth-last-of-type":
559
+ case "nth-last-child":
560
+ $last = true;
561
+
562
+ case "nth-of-type":
563
+ case "nth-child":
564
+ $p = $i+1;
565
+ $nth = trim(mb_substr($selector, $p, strpos($selector, ")", $i)-$p));
566
+
567
+ // 1
568
+ if ( preg_match("/^\d+$/", $nth) ) {
569
+ $condition = "position() = $nth";
570
+ }
571
+
572
+ // odd
573
+ elseif ( $nth === "odd" ) {
574
+ $condition = "(position() mod 2) = 1";
575
+ }
576
+
577
+ // even
578
+ elseif ( $nth === "even" ) {
579
+ $condition = "(position() mod 2) = 0";
580
+ }
581
+
582
+ // an+b
583
+ else {
584
+ $condition = $this->_selector_an_plus_b($nth, $last);
585
+ }
586
+
587
+ $query .= "[$condition]";
588
+ $tok = "";
589
+ break;
590
+
591
+ case "link":
592
+ $query .= "[@href]";
593
+ $tok = "";
594
+ break;
595
+
596
+ case "first-line": // TODO
597
+ case "first-letter": // TODO
598
+
599
+ // N/A
600
+ case "active":
601
+ case "hover":
602
+ case "visited":
603
+ $query .= "[false()]";
604
+ $tok = "";
605
+ break;
606
+
607
+ /* Pseudo-elements */
608
+ case "before":
609
+ case "after":
610
+ if ( $first_pass ) {
611
+ $pseudo_elements[$tok] = $tok;
612
+ }
613
+ else {
614
+ $query .= "/*[@$tok]";
615
+ }
616
+
617
+ $tok = "";
618
+ break;
619
+
620
+ case "empty":
621
+ $query .= "[not(*) and not(normalize-space())]";
622
+ $tok = "";
623
+ break;
624
+
625
+ case "disabled":
626
+ case "checked":
627
+ $query .= "[@$tok]";
628
+ $tok = "";
629
+ break;
630
+
631
+ case "enabled":
632
+ $query .= "[not(@disabled)]";
633
+ $tok = "";
634
+ break;
635
+ }
636
+
637
+ break;
638
+
639
+ case "[":
640
+ // Attribute selectors. All with an attribute matching the following token(s)
641
+ $attr_delimiters = array("=", "]", "~", "|", "$", "^", "*");
642
+ $tok_len = mb_strlen($tok);
643
+ $j = 0;
644
+
645
+ $attr = "";
646
+ $op = "";
647
+ $value = "";
648
+
649
+ while ( $j < $tok_len ) {
650
+ if ( in_array($tok[$j], $attr_delimiters) ) {
651
+ break;
652
+ }
653
+ $attr .= $tok[$j++];
654
+ }
655
+
656
+ switch ( $tok[$j] ) {
657
+
658
+ case "~":
659
+ case "|":
660
+ case "$":
661
+ case "^":
662
+ case "*":
663
+ $op .= $tok[$j++];
664
+
665
+ if ( $tok[$j] !== "=" ) {
666
+ throw new DOMPDF_Exception("Invalid CSS selector syntax: invalid attribute selector: $selector");
667
+ }
668
+
669
+ $op .= $tok[$j];
670
+ break;
671
+
672
+ case "=":
673
+ $op = "=";
674
+ break;
675
+
676
+ }
677
+
678
+ // Read the attribute value, if required
679
+ if ( $op != "" ) {
680
+ $j++;
681
+ while ( $j < $tok_len ) {
682
+ if ( $tok[$j] === "]" ) {
683
+ break;
684
+ }
685
+ $value .= $tok[$j++];
686
+ }
687
+ }
688
+
689
+ if ( $attr == "" ) {
690
+ throw new DOMPDF_Exception("Invalid CSS selector syntax: missing attribute name");
691
+ }
692
+
693
+ $value = trim($value, "\"'");
694
+
695
+ switch ( $op ) {
696
+
697
+ case "":
698
+ $query .= "[@$attr]";
699
+ break;
700
+
701
+ case "=":
702
+ $query .= "[@$attr=\"$value\"]";
703
+ break;
704
+
705
+ case "~=":
706
+ // FIXME: this will break if $value contains quoted strings
707
+ // (e.g. [type~="a b c" "d e f"])
708
+ $values = explode(" ", $value);
709
+ $query .= "[";
710
+
711
+ foreach ( $values as $val ) {
712
+ $query .= "@$attr=\"$val\" or ";
713
+ }
714
+
715
+ $query = rtrim($query, " or ") . "]";
716
+ break;
717
+
718
+ case "|=":
719
+ $values = explode("-", $value);
720
+ $query .= "[";
721
+
722
+ foreach ( $values as $val ) {
723
+ $query .= "starts-with(@$attr, \"$val\") or ";
724
+ }
725
+
726
+ $query = rtrim($query, " or ") . "]";
727
+ break;
728
+
729
+ case "$=":
730
+ $query .= "[substring(@$attr, string-length(@$attr)-".(strlen($value) - 1).")=\"$value\"]";
731
+ break;
732
+
733
+ case "^=":
734
+ $query .= "[starts-with(@$attr,\"$value\")]";
735
+ break;
736
+
737
+ case "*=":
738
+ $query .= "[contains(@$attr,\"$value\")]";
739
+ break;
740
+ }
741
+
742
+ break;
743
+ }
744
+ }
745
+ $i++;
746
+
747
+ // case ":":
748
+ // // Pseudo selectors: ignore for now. Partially handled directly
749
+ // // below.
750
+
751
+ // // Skip until the next special character, leaving the token as-is
752
+ // while ( $i < $len ) {
753
+ // if ( in_array($selector[$i], $delimiters) )
754
+ // break;
755
+ // $i++;
756
+ // }
757
+ // break;
758
+
759
+ // default:
760
+ // // Add the character to the token
761
+ // $tok .= $selector[$i++];
762
+ // break;
763
+ // }
764
+
765
+ // }
766
+
767
+
768
+ // Trim the trailing '/' from the query
769
+ if ( mb_strlen($query) > 2 ) {
770
+ $query = rtrim($query, "/");
771
+ }
772
+
773
+ return array("query" => $query, "pseudo_elements" => $pseudo_elements);
774
+ }
775
+
776
+ // https://github.com/tenderlove/nokogiri/blob/master/lib/nokogiri/css/xpath_visitor.rb
777
+ protected function _selector_an_plus_b($expr, $last = false) {
778
+ $expr = preg_replace("/\s/", "", $expr);
779
+ if ( !preg_match("/^(?P<a>-?[0-9]*)?n(?P<b>[-+]?[0-9]+)?$/", $expr, $matches)) {
780
+ return "false()";
781
+ }
782
+
783
+ $a = ((isset($matches["a"]) && $matches["a"] !== "") ? intval($matches["a"]) : 1);
784
+ $b = ((isset($matches["b"]) && $matches["b"] !== "") ? intval($matches["b"]) : 0);
785
+
786
+ $position = ($last ? "(last()-position()+1)" : "position()");
787
+
788
+ if ( $b == 0 ) {
789
+ return "($position mod $a) = 0";
790
+ }
791
+ else {
792
+ $compare = (($a < 0) ? "<=" : ">=");
793
+ $b2 = -$b;
794
+ if ( $b2 >= 0 ) {
795
+ $b2 = "+$b2";
796
+ }
797
+ return "($position $compare $b) and ((($position $b2) mod ".abs($a).") = 0)";
798
+ }
799
+ }
800
+
801
+ /**
802
+ * applies all current styles to a particular document tree
803
+ *
804
+ * apply_styles() applies all currently loaded styles to the provided
805
+ * {@link Frame_Tree}. Aside from parsing CSS, this is the main purpose
806
+ * of this class.
807
+ *
808
+ * @param Frame_Tree $tree
809
+ */
810
+ function apply_styles(Frame_Tree $tree) {
811
+ // Use XPath to select nodes. This would be easier if we could attach
812
+ // Frame objects directly to DOMNodes using the setUserData() method, but
813
+ // we can't do that just yet. Instead, we set a _node attribute_ in
814
+ // Frame->set_id() and use that as a handle on the Frame object via
815
+ // Frame_Tree::$_registry.
816
+
817
+ // We create a scratch array of styles indexed by frame id. Once all
818
+ // styles have been assigned, we order the cached styles by specificity
819
+ // and create a final style object to assign to the frame.
820
+
821
+ // FIXME: this is not particularly robust...
822
+
823
+ $styles = array();
824
+ $xp = new DOMXPath($tree->get_dom());
825
+
826
+ // Add generated content
827
+ foreach ($this->_styles as $selector => $style) {
828
+ if ( strpos($selector, ":before") === false && strpos($selector, ":after") === false ) {
829
+ continue;
830
+ }
831
+
832
+ $query = $this->_css_selector_to_xpath($selector, true);
833
+
834
+ // Retrieve the nodes
835
+ $nodes = @$xp->query($query["query"]);
836
+ if ( $nodes == null ) {
837
+ record_warnings(E_USER_WARNING, "The CSS selector '$selector' is not valid", __FILE__, __LINE__);
838
+ continue;
839
+ }
840
+
841
+ foreach ($nodes as $node) {
842
+ foreach ($query["pseudo_elements"] as $pos) {
843
+ // Do not add a new pseudo element if another one already matched
844
+ if ( $node->hasAttribute("dompdf_{$pos}_frame_id") ) {
845
+ continue;
846
+ }
847
+
848
+ if (($src = $this->_image($style->content)) !== "none") {
849
+ $new_node = $node->ownerDocument->createElement("img_generated");
850
+ $new_node->setAttribute("src", $src);
851
+ }
852
+ else {
853
+ $new_node = $node->ownerDocument->createElement("dompdf_generated");
854
+ }
855
+
856
+ $new_node->setAttribute($pos, $pos);
857
+
858
+ $new_frame_id = $tree->insert_node($node, $new_node, $pos);
859
+ $node->setAttribute("dompdf_{$pos}_frame_id", $new_frame_id);
860
+ }
861
+ }
862
+ }
863
+
864
+ // Apply all styles in stylesheet
865
+ foreach ($this->_styles as $selector => $style) {
866
+ $query = $this->_css_selector_to_xpath($selector);
867
+
868
+ // Retrieve the nodes
869
+ $nodes = @$xp->query($query["query"]);
870
+ if ( $nodes == null ) {
871
+ record_warnings(E_USER_WARNING, "The CSS selector '$selector' is not valid", __FILE__, __LINE__);
872
+ continue;
873
+ }
874
+
875
+ foreach ($nodes as $node) {
876
+ // Retrieve the node id
877
+ // Only DOMElements get styles
878
+ if ( $node->nodeType != XML_ELEMENT_NODE ) {
879
+ continue;
880
+ }
881
+
882
+ $id = $node->getAttribute("frame_id");
883
+
884
+ // Assign the current style to the scratch array
885
+ $spec = $this->_specificity($selector);
886
+ $styles[$id][$spec][] = $style;
887
+ }
888
+ }
889
+
890
+ // Now create the styles and assign them to the appropriate frames. (We
891
+ // iterate over the tree using an implicit Frame_Tree iterator.)
892
+ $root_flg = false;
893
+ foreach ($tree->get_frames() as $frame) {
894
+ // pre_r($frame->get_node()->nodeName . ":");
895
+ if ( !$root_flg && $this->_page_styles["base"] ) {
896
+ $style = $this->_page_styles["base"];
897
+ $root_flg = true;
898
+ }
899
+ else {
900
+ $style = $this->create_style();
901
+ }
902
+
903
+ // Find nearest DOMElement parent
904
+ $p = $frame;
905
+ while ( $p = $p->get_parent() ) {
906
+ if ( $p->get_node()->nodeType == XML_ELEMENT_NODE ) {
907
+ break;
908
+ }
909
+ }
910
+
911
+ // Styles can only be applied directly to DOMElements; anonymous
912
+ // frames inherit from their parent
913
+ if ( $frame->get_node()->nodeType != XML_ELEMENT_NODE ) {
914
+ if ( $p ) {
915
+ $style->inherit($p->get_style());
916
+ }
917
+
918
+ $frame->set_style($style);
919
+ continue;
920
+ }
921
+
922
+ $id = $frame->get_id();
923
+
924
+ // Handle HTML 4.0 attributes
925
+ Attribute_Translator::translate_attributes($frame);
926
+ if ( ($str = $frame->get_node()->getAttribute(Attribute_Translator::$_style_attr)) !== "" ) {
927
+ // Lowest specificity
928
+ $styles[$id][1][] = $this->_parse_properties($str);
929
+ }
930
+
931
+ // Locate any additional style attributes
932
+ if ( ($str = $frame->get_node()->getAttribute("style")) !== "" ) {
933
+ // Destroy CSS comments
934
+ $str = preg_replace("'/\*.*?\*/'si", "", $str);
935
+
936
+ $spec = $this->_specificity("!attr");
937
+ $styles[$id][$spec][] = $this->_parse_properties($str);
938
+ }
939
+
940
+ // Grab the applicable styles
941
+ if ( isset($styles[$id]) ) {
942
+
943
+ $applied_styles = $styles[ $frame->get_id() ];
944
+
945
+ // Sort by specificity
946
+ ksort($applied_styles);
947
+
948
+ if (DEBUGCSS) {
949
+ $debug_nodename = $frame->get_node()->nodeName;
950
+ print "<pre>\n[$debug_nodename\n";
951
+ foreach ($applied_styles as $spec => $arr) {
952
+ printf("specificity: 0x%08x\n",$spec);
953
+ foreach ($arr as $s) {
954
+ print "[\n";
955
+ $s->debug_print();
956
+ print "]\n";
957
+ }
958
+ }
959
+ }
960
+
961
+ // Merge the new styles with the inherited styles
962
+ foreach ($applied_styles as $arr) {
963
+ foreach ($arr as $s) {
964
+ $style->merge($s);
965
+ }
966
+ }
967
+ }
968
+
969
+ // Inherit parent's styles if required
970
+ if ( $p ) {
971
+
972
+ if (DEBUGCSS) {
973
+ print "inherit:\n";
974
+ print "[\n";
975
+ $p->get_style()->debug_print();
976
+ print "]\n";
977
+ }
978
+
979
+ $style->inherit( $p->get_style() );
980
+ }
981
+
982
+ if (DEBUGCSS) {
983
+ print "DomElementStyle:\n";
984
+ print "[\n";
985
+ $style->debug_print();
986
+ print "]\n";
987
+ print "/$debug_nodename]\n</pre>";
988
+ }
989
+
990
+ /*DEBUGCSS print: see below different print debugging method
991
+ pre_r($frame->get_node()->nodeName . ":");
992
+ echo "<pre>";
993
+ echo $style;
994
+ echo "</pre>";*/
995
+ $frame->set_style($style);
996
+
997
+ }
998
+
999
+ // We're done! Clean out the registry of all styles since we
1000
+ // won't be needing this later.
1001
+ foreach ( array_keys($this->_styles) as $key ) {
1002
+ $this->_styles[$key] = null;
1003
+ unset($this->_styles[$key]);
1004
+ }
1005
+
1006
+ }
1007
+
1008
+ /**
1009
+ * parse a CSS string using a regex parser
1010
+ * Called by {@link Stylesheet::parse_css()}
1011
+ *
1012
+ * @param string $str
1013
+ *
1014
+ * @throws DOMPDF_Exception
1015
+ */
1016
+ private function _parse_css($str) {
1017
+
1018
+ $str = trim($str);
1019
+
1020
+ // Destroy comments and remove HTML comments
1021
+ $css = preg_replace(array(
1022
+ "'/\*.*?\*/'si",
1023
+ "/^<!--/",
1024
+ "/-->$/"
1025
+ ), "", $str);
1026
+
1027
+ // FIXME: handle '{' within strings, e.g. [attr="string {}"]
1028
+
1029
+ // Something more legible:
1030
+ $re =
1031
+ "/\s* # Skip leading whitespace \n".
1032
+ "( @([^\s]+)\s+([^{;]*) (?:;|({)) )? # Match @rules followed by ';' or '{' \n".
1033
+ "(?(1) # Only parse sub-sections if we're in an @rule... \n".
1034
+ " (?(4) # ...and if there was a leading '{' \n".
1035
+ " \s*( (?:(?>[^{}]+) ({)? # Parse rulesets and individual @page rules \n".
1036
+ " (?(6) (?>[^}]*) }) \s*)+? \n".
1037
+ " ) \n".
1038
+ " }) # Balancing '}' \n".
1039
+ "| # Branch to match regular rules (not preceeded by '@')\n".
1040
+ "([^{]*{[^}]*})) # Parse normal rulesets\n".
1041
+ "/xs";
1042
+
1043
+ if ( preg_match_all($re, $css, $matches, PREG_SET_ORDER) === false ) {
1044
+ // An error occured
1045
+ throw new DOMPDF_Exception("Error parsing css file: preg_match_all() failed.");
1046
+ }
1047
+
1048
+ // After matching, the array indicies are set as follows:
1049
+ //
1050
+ // [0] => complete text of match
1051
+ // [1] => contains '@import ...;' or '@media {' if applicable
1052
+ // [2] => text following @ for cases where [1] is set
1053
+ // [3] => media types or full text following '@import ...;'
1054
+ // [4] => '{', if present
1055
+ // [5] => rulesets within media rules
1056
+ // [6] => '{', within media rules
1057
+ // [7] => individual rules, outside of media rules
1058
+ //
1059
+ //pre_r($matches);
1060
+ foreach ( $matches as $match ) {
1061
+ $match[2] = trim($match[2]);
1062
+
1063
+ if ( $match[2] !== "" ) {
1064
+ // Handle @rules
1065
+ switch ($match[2]) {
1066
+
1067
+ case "import":
1068
+ $this->_parse_import($match[3]);
1069
+ break;
1070
+
1071
+ case "media":
1072
+ $acceptedmedia = self::$ACCEPTED_GENERIC_MEDIA_TYPES;
1073
+ $acceptedmedia[] = $this->_dompdf->get_option("default_media_type");
1074
+
1075
+ $media = preg_split("/\s*,\s*/", mb_strtolower(trim($match[3])));
1076
+
1077
+ if ( count(array_intersect($acceptedmedia, $media)) ) {
1078
+ $this->_parse_sections($match[5]);
1079
+ }
1080
+ break;
1081
+
1082
+ case "page":
1083
+ //This handles @page to be applied to page oriented media
1084
+ //Note: This has a reduced syntax:
1085
+ //@page { margin:1cm; color:blue; }
1086
+ //Not a sequence of styles like a full.css, but only the properties
1087
+ //of a single style, which is applied to the very first "root" frame before
1088
+ //processing other styles of the frame.
1089
+ //Working properties:
1090
+ // margin (for margin around edge of paper)
1091
+ // font-family (default font of pages)
1092
+ // color (default text color of pages)
1093
+ //Non working properties:
1094
+ // border
1095
+ // padding
1096
+ // background-color
1097
+ //Todo:Reason is unknown
1098
+ //Other properties (like further font or border attributes) not tested.
1099
+ //If a border or background color around each paper sheet is desired,
1100
+ //assign it to the <body> tag, possibly only for the css of the correct media type.
1101
+
1102
+ // If the page has a name, skip the style.
1103
+ $page_selector = trim($match[3]);
1104
+
1105
+ $key = null;
1106
+ switch($page_selector) {
1107
+ case "":
1108
+ $key = "base";
1109
+ break;
1110
+
1111
+ case ":left":
1112
+ case ":right":
1113
+ case ":odd":
1114
+ case ":even":
1115
+ case ":first":
1116
+ $key = $page_selector;
1117
+
1118
+ default: continue;
1119
+ }
1120
+
1121
+ // Store the style for later...
1122
+ if ( empty($this->_page_styles[$key]) ) {
1123
+ $this->_page_styles[$key] = $this->_parse_properties($match[5]);
1124
+ }
1125
+ else {
1126
+ $this->_page_styles[$key]->merge($this->_parse_properties($match[5]));
1127
+ }
1128
+ break;
1129
+
1130
+ case "font-face":
1131
+ $this->_parse_font_face($match[5]);
1132
+ break;
1133
+
1134
+ default:
1135
+ // ignore everything else
1136
+ break;
1137
+ }
1138
+
1139
+ continue;
1140
+ }
1141
+
1142
+ if ( $match[7] !== "" ) {
1143
+ $this->_parse_sections($match[7]);
1144
+ }
1145
+
1146
+ }
1147
+ }
1148
+
1149
+ /* See also style.cls Style::_image(), refactoring?, works also for imported css files */
1150
+ protected function _image($val) {
1151
+ $DEBUGCSS=DEBUGCSS;
1152
+ $parsed_url = "none";
1153
+
1154
+ if ( mb_strpos($val, "url") === false ) {
1155
+ $path = "none"; //Don't resolve no image -> otherwise would prefix path and no longer recognize as none
1156
+ }
1157
+ else {
1158
+ $val = preg_replace("/url\(['\"]?([^'\")]+)['\"]?\)/","\\1", trim($val));
1159
+
1160
+ // Resolve the url now in the context of the current stylesheet
1161
+ $parsed_url = explode_url($val);
1162
+ if ( $parsed_url["protocol"] == "" && $this->get_protocol() == "" ) {
1163
+ if ($parsed_url["path"][0] === '/' || $parsed_url["path"][0] === '\\' ) {
1164
+ $path = $_SERVER["DOCUMENT_ROOT"].'/';
1165
+ }
1166
+ else {
1167
+ $path = $this->get_base_path();
1168
+ }
1169
+
1170
+ $path .= $parsed_url["path"] . $parsed_url["file"];
1171
+ $path = realpath($path);
1172
+ // If realpath returns FALSE then specifically state that there is no background image
1173
+ // FIXME: Is this causing problems for imported CSS files? There are some './none' references when running the test cases.
1174
+ if (!$path) { $path = 'none'; }
1175
+ }
1176
+ else {
1177
+ $path = build_url($this->get_protocol(),
1178
+ $this->get_host(),
1179
+ $this->get_base_path(),
1180
+ $val);
1181
+ }
1182
+ }
1183
+
1184
+ if ($DEBUGCSS) {
1185
+ print "<pre>[_image\n";
1186
+ print_r($parsed_url);
1187
+ print $this->get_protocol()."\n".$this->get_base_path()."\n".$path."\n";
1188
+ print "_image]</pre>";;
1189
+ }
1190
+
1191
+ return $path;
1192
+ }
1193
+
1194
+ /**
1195
+ * parse @import{} sections
1196
+ *
1197
+ * @param string $url the url of the imported CSS file
1198
+ */
1199
+ private function _parse_import($url) {
1200
+ $arr = preg_split("/[\s\n,]/", $url,-1, PREG_SPLIT_NO_EMPTY);
1201
+ $url = array_shift($arr);
1202
+ $accept = false;
1203
+
1204
+ if ( count($arr) > 0 ) {
1205
+ $acceptedmedia = self::$ACCEPTED_GENERIC_MEDIA_TYPES;
1206
+ $acceptedmedia[] = $this->_dompdf->get_option("default_media_type");
1207
+
1208
+ // @import url media_type [media_type...]
1209
+ foreach ( $arr as $type ) {
1210
+ if ( in_array(mb_strtolower(trim($type)), $acceptedmedia) ) {
1211
+ $accept = true;
1212
+ break;
1213
+ }
1214
+ }
1215
+
1216
+ }
1217
+ else {
1218
+ // unconditional import
1219
+ $accept = true;
1220
+ }
1221
+
1222
+ if ( $accept ) {
1223
+ // Store our current base url properties in case the new url is elsewhere
1224
+ $protocol = $this->_protocol;
1225
+ $host = $this->_base_host;
1226
+ $path = $this->_base_path;
1227
+
1228
+ // $url = str_replace(array('"',"url", "(", ")"), "", $url);
1229
+ // If the protocol is php, assume that we will import using file://
1230
+ // $url = build_url($protocol == "php://" ? "file://" : $protocol, $host, $path, $url);
1231
+ // Above does not work for subfolders and absolute urls.
1232
+ // Todo: As above, do we need to replace php or file to an empty protocol for local files?
1233
+
1234
+ $url = $this->_image($url);
1235
+
1236
+ $this->load_css_file($url);
1237
+
1238
+ // Restore the current base url
1239
+ $this->_protocol = $protocol;
1240
+ $this->_base_host = $host;
1241
+ $this->_base_path = $path;
1242
+ }
1243
+
1244
+ }
1245
+
1246
+ /**
1247
+ * parse @font-face{} sections
1248
+ * http://www.w3.org/TR/css3-fonts/#the-font-face-rule
1249
+ *
1250
+ * @param string $str CSS @font-face rules
1251
+ * @return Style
1252
+ */
1253
+ private function _parse_font_face($str) {
1254
+ $descriptors = $this->_parse_properties($str);
1255
+
1256
+ preg_match_all("/(url|local)\s*\([\"\']?([^\"\'\)]+)[\"\']?\)\s*(format\s*\([\"\']?([^\"\'\)]+)[\"\']?\))?/i", $descriptors->src, $src);
1257
+
1258
+ $sources = array();
1259
+ $valid_sources = array();
1260
+
1261
+ foreach($src[0] as $i => $value) {
1262
+ $source = array(
1263
+ "local" => strtolower($src[1][$i]) === "local",
1264
+ "uri" => $src[2][$i],
1265
+ "format" => $src[4][$i],
1266
+ "path" => build_url($this->_protocol, $this->_base_host, $this->_base_path, $src[2][$i]),
1267
+ );
1268
+
1269
+ if ( !$source["local"] && in_array($source["format"], array("", "woff", "opentype", "truetype")) ) {
1270
+ $valid_sources[] = $source;
1271
+ }
1272
+
1273
+ $sources[] = $source;
1274
+ }
1275
+
1276
+ // No valid sources
1277
+ if ( empty($valid_sources) ) {
1278
+ return;
1279
+ }
1280
+
1281
+ $style = array(
1282
+ "family" => $descriptors->get_font_family_raw(),
1283
+ "weight" => $descriptors->font_weight,
1284
+ "style" => $descriptors->font_style,
1285
+ );
1286
+
1287
+ Font_Metrics::register_font($style, $valid_sources[0]["path"]);
1288
+ }
1289
+
1290
+ /**
1291
+ * parse regular CSS blocks
1292
+ *
1293
+ * _parse_properties() creates a new Style object based on the provided
1294
+ * CSS rules.
1295
+ *
1296
+ * @param string $str CSS rules
1297
+ * @return Style
1298
+ */
1299
+ private function _parse_properties($str) {
1300
+ $properties = preg_split("/;(?=(?:[^\(]*\([^\)]*\))*(?![^\)]*\)))/", $str);
1301
+
1302
+ if (DEBUGCSS) print '[_parse_properties';
1303
+
1304
+ // Create the style
1305
+ $style = new Style($this);
1306
+
1307
+ foreach ($properties as $prop) {
1308
+ // If the $prop contains an url, the regex may be wrong
1309
+ // @todo: fix the regex so that it works everytime
1310
+ /*if (strpos($prop, "url(") === false) {
1311
+ if (preg_match("/([a-z-]+)\s*:\s*[^:]+$/i", $prop, $m))
1312
+ $prop = $m[0];
1313
+ }*/
1314
+ //A css property can have " ! important" appended (whitespace optional)
1315
+ //strip this off to decode core of the property correctly.
1316
+ //Pass on in the style to allow proper handling:
1317
+ //!important properties can only be overridden by other !important ones.
1318
+ //$style->$prop_name = is a shortcut of $style->__set($prop_name,$value);.
1319
+ //If no specific set function available, set _props["prop_name"]
1320
+ //style is always copied completely, or $_props handled separately
1321
+ //Therefore set a _important_props["prop_name"]=true to indicate the modifier
1322
+
1323
+ /* Instead of short code, prefer the typical case with fast code
1324
+ $important = preg_match("/(.*?)!\s*important/",$prop,$match);
1325
+ if ( $important ) {
1326
+ $prop = $match[1];
1327
+ }
1328
+ $prop = trim($prop);
1329
+ */
1330
+ if (DEBUGCSS) print '(';
1331
+
1332
+ $important = false;
1333
+ $prop = trim($prop);
1334
+
1335
+ if ( substr($prop, -9) === 'important' ) {
1336
+ $prop_tmp = rtrim(substr($prop, 0, -9));
1337
+
1338
+ if ( substr($prop_tmp, -1) === '!' ) {
1339
+ $prop = rtrim(substr($prop_tmp, 0, -1));
1340
+ $important = true;
1341
+ }
1342
+ }
1343
+
1344
+ if ( $prop === "" ) {
1345
+ if (DEBUGCSS) print 'empty)';
1346
+ continue;
1347
+ }
1348
+
1349
+ $i = mb_strpos($prop, ":");
1350
+ if ( $i === false ) {
1351
+ if (DEBUGCSS) print 'novalue'.$prop.')';
1352
+ continue;
1353
+ }
1354
+
1355
+ $prop_name = rtrim(mb_strtolower(mb_substr($prop, 0, $i)));
1356
+ $value = ltrim(mb_substr($prop, $i+1));
1357
+ if (DEBUGCSS) print $prop_name.':='.$value.($important?'!IMPORTANT':'').')';
1358
+ //New style, anyway empty
1359
+ //if ($important || !$style->important_get($prop_name) ) {
1360
+ //$style->$prop_name = array($value,$important);
1361
+ //assignment might be replaced by overloading through __set,
1362
+ //and overloaded functions might check _important_props,
1363
+ //therefore set _important_props first.
1364
+ if ($important) {
1365
+ $style->important_set($prop_name);
1366
+ }
1367
+ //For easier debugging, don't use overloading of assignments with __set
1368
+ $style->$prop_name = $value;
1369
+ //$style->props_set($prop_name, $value);
1370
+ }
1371
+ if (DEBUGCSS) print '_parse_properties]';
1372
+
1373
+ return $style;
1374
+ }
1375
+
1376
+ /**
1377
+ * parse selector + rulesets
1378
+ *
1379
+ * @param string $str CSS selectors and rulesets
1380
+ */
1381
+ private function _parse_sections($str) {
1382
+ // Pre-process: collapse all whitespace and strip whitespace around '>',
1383
+ // '.', ':', '+', '#'
1384
+
1385
+ $patterns = array("/[\\s\n]+/", "/\\s+([>.:+#])\\s+/");
1386
+ $replacements = array(" ", "\\1");
1387
+ $str = preg_replace($patterns, $replacements, $str);
1388
+
1389
+ $sections = explode("}", $str);
1390
+ if (DEBUGCSS) print '[_parse_sections';
1391
+ foreach ($sections as $sect) {
1392
+ $i = mb_strpos($sect, "{");
1393
+
1394
+ $selectors = explode(",", mb_substr($sect, 0, $i));
1395
+ if (DEBUGCSS) print '[section';
1396
+ $style = $this->_parse_properties(trim(mb_substr($sect, $i+1)));
1397
+
1398
+ // Assign it to the selected elements
1399
+ foreach ($selectors as $selector) {
1400
+ $selector = trim($selector);
1401
+
1402
+ if ($selector == "") {
1403
+ if (DEBUGCSS) print '#empty#';
1404
+ continue;
1405
+ }
1406
+ if (DEBUGCSS) print '#'.$selector.'#';
1407
+ //if (DEBUGCSS) { if (strpos($selector,'p') !== false) print '!!!p!!!#'; }
1408
+
1409
+ $this->add_style($selector, $style);
1410
+ }
1411
+
1412
+ if (DEBUGCSS) print 'section]';
1413
+ }
1414
+
1415
+ if (DEBUGCSS) print '_parse_sections]';
1416
+ }
1417
+
1418
+ /**
1419
+ * dumps the entire stylesheet as a string
1420
+ *
1421
+ * Generates a string of each selector and associated style in the
1422
+ * Stylesheet. Useful for debugging.
1423
+ *
1424
+ * @return string
1425
+ */
1426
+ function __toString() {
1427
+ $str = "";
1428
+ foreach ($this->_styles as $selector => $style) {
1429
+ $str .= "$selector => " . $style->__toString() . "\n";
1430
+ }
1431
+
1432
+ return $str;
1433
+ }
1434
+ }
dompdf/include/table_cell_frame_decorator.cls.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Decorates table cells for layout
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Cell_Frame_Decorator extends Block_Frame_Decorator {
16
+
17
+ protected $_resolved_borders;
18
+ protected $_content_height;
19
+
20
+ //........................................................................
21
+
22
+ function __construct(Frame $frame, DOMPDF $dompdf) {
23
+ parent::__construct($frame, $dompdf);
24
+ $this->_resolved_borders = array();
25
+ $this->_content_height = 0;
26
+ }
27
+
28
+ //........................................................................
29
+
30
+ function reset() {
31
+ parent::reset();
32
+ $this->_resolved_borders = array();
33
+ $this->_content_height = 0;
34
+ $this->_frame->reset();
35
+ }
36
+
37
+ function get_content_height() {
38
+ return $this->_content_height;
39
+ }
40
+
41
+ function set_content_height($height) {
42
+ $this->_content_height = $height;
43
+ }
44
+
45
+ function set_cell_height($height) {
46
+ $style = $this->get_style();
47
+ $v_space = $style->length_in_pt(array($style->margin_top,
48
+ $style->padding_top,
49
+ $style->border_top_width,
50
+ $style->border_bottom_width,
51
+ $style->padding_bottom,
52
+ $style->margin_bottom),
53
+ $style->width);
54
+
55
+ $new_height = $height - $v_space;
56
+ $style->height = $new_height;
57
+
58
+ if ( $new_height > $this->_content_height ) {
59
+ $y_offset = 0;
60
+
61
+ // Adjust our vertical alignment
62
+ switch ($style->vertical_align) {
63
+ default:
64
+ case "baseline":
65
+ // FIXME: this isn't right
66
+
67
+ case "top":
68
+ // Don't need to do anything
69
+ return;
70
+
71
+ case "middle":
72
+ $y_offset = ($new_height - $this->_content_height) / 2;
73
+ break;
74
+
75
+ case "bottom":
76
+ $y_offset = $new_height - $this->_content_height;
77
+ break;
78
+ }
79
+
80
+ if ( $y_offset ) {
81
+ // Move our children
82
+ foreach ( $this->get_line_boxes() as $line ) {
83
+ foreach ( $line->get_frames() as $frame )
84
+ $frame->move( 0, $y_offset );
85
+ }
86
+ }
87
+ }
88
+
89
+ }
90
+
91
+ function set_resolved_border($side, $border_spec) {
92
+ $this->_resolved_borders[$side] = $border_spec;
93
+ }
94
+
95
+ //........................................................................
96
+
97
+ function get_resolved_border($side) {
98
+ return $this->_resolved_borders[$side];
99
+ }
100
+
101
+ function get_resolved_borders() { return $this->_resolved_borders; }
102
+ }
dompdf/include/table_cell_frame_reflower.cls.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows table cells
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Cell_Frame_Reflower extends Block_Frame_Reflower {
16
+
17
+ //........................................................................
18
+
19
+ function __construct(Block_Frame_Decorator $frame) {
20
+ parent::__construct($frame);
21
+ }
22
+
23
+ //........................................................................
24
+
25
+ function reflow(Block_Frame_Decorator $block = null) {
26
+
27
+ $style = $this->_frame->get_style();
28
+
29
+ $table = Table_Frame_Decorator::find_parent_table($this->_frame);
30
+ $cellmap = $table->get_cellmap();
31
+
32
+ list($x, $y) = $cellmap->get_frame_position($this->_frame);
33
+ $this->_frame->set_position($x, $y);
34
+
35
+ $cells = $cellmap->get_spanned_cells($this->_frame);
36
+
37
+ $w = 0;
38
+ foreach ( $cells["columns"] as $i ) {
39
+ $col = $cellmap->get_column( $i );
40
+ $w += $col["used-width"];
41
+ }
42
+
43
+ //FIXME?
44
+ $h = $this->_frame->get_containing_block("h");
45
+
46
+ $left_space = $style->length_in_pt(array($style->margin_left,
47
+ $style->padding_left,
48
+ $style->border_left_width),
49
+ $w);
50
+
51
+ $right_space = $style->length_in_pt(array($style->padding_right,
52
+ $style->margin_right,
53
+ $style->border_right_width),
54
+ $w);
55
+
56
+ $top_space = $style->length_in_pt(array($style->margin_top,
57
+ $style->padding_top,
58
+ $style->border_top_width),
59
+ $h);
60
+ $bottom_space = $style->length_in_pt(array($style->margin_bottom,
61
+ $style->padding_bottom,
62
+ $style->border_bottom_width),
63
+ $h);
64
+
65
+ $style->width = $cb_w = $w - $left_space - $right_space;
66
+
67
+ $content_x = $x + $left_space;
68
+ $content_y = $line_y = $y + $top_space;
69
+
70
+ // Adjust the first line based on the text-indent property
71
+ $indent = $style->length_in_pt($style->text_indent, $w);
72
+ $this->_frame->increase_line_width($indent);
73
+
74
+ $page = $this->_frame->get_root();
75
+
76
+ // Set the y position of the first line in the cell
77
+ $line_box = $this->_frame->get_current_line_box();
78
+ $line_box->y = $line_y;
79
+
80
+ // Set the containing blocks and reflow each child
81
+ foreach ( $this->_frame->get_children() as $child ) {
82
+
83
+ if ( $page->is_full() )
84
+ break;
85
+
86
+ $child->set_containing_block($content_x, $content_y, $cb_w, $h);
87
+
88
+ $this->process_clear($child);
89
+
90
+ $child->reflow($this->_frame);
91
+
92
+ $this->process_float($child, $x + $left_space, $w - $right_space - $left_space);
93
+ }
94
+
95
+ // Determine our height
96
+ $style_height = $style->length_in_pt($style->height, $h);
97
+
98
+ $this->_frame->set_content_height($this->_calculate_content_height());
99
+
100
+ $height = max($style_height, $this->_frame->get_content_height());
101
+
102
+ // Let the cellmap know our height
103
+ $cell_height = $height / count($cells["rows"]);
104
+
105
+ if ($style_height <= $height)
106
+ $cell_height += $top_space + $bottom_space;
107
+
108
+ foreach ($cells["rows"] as $i)
109
+ $cellmap->set_row_height($i, $cell_height);
110
+
111
+ $style->height = $height;
112
+
113
+ $this->_text_align();
114
+
115
+ $this->vertical_align();
116
+
117
+ }
118
+
119
+ }
dompdf/include/table_cell_positioner.cls.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Positions table cells
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Cell_Positioner extends Positioner {
16
+
17
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
18
+
19
+ //........................................................................
20
+
21
+ function position() {
22
+
23
+ $table = Table_Frame_Decorator::find_parent_table($this->_frame);
24
+ $cellmap = $table->get_cellmap();
25
+ $this->_frame->set_position($cellmap->get_frame_position($this->_frame));
26
+
27
+ }
28
+ }
dompdf/include/table_cell_renderer.cls.php ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Renders table cells
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Cell_Renderer extends Block_Renderer {
16
+
17
+ //........................................................................
18
+
19
+ function render(Frame $frame) {
20
+ $style = $frame->get_style();
21
+
22
+ if ( trim($frame->get_node()->nodeValue) === "" && $style->empty_cells === "hide" ) {
23
+ return;
24
+ }
25
+
26
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
27
+ list($x, $y, $w, $h) = $frame->get_border_box();
28
+
29
+ // Draw our background, border and content
30
+ if ( ($bg = $style->background_color) !== "transparent" ) {
31
+ $this->_canvas->filled_rectangle($x, $y, $w, $h, $bg);
32
+ }
33
+
34
+ if ( ($url = $style->background_image) && $url !== "none" ) {
35
+ $this->_background_image($url, $x, $y, $w, $h, $style);
36
+ }
37
+
38
+ $table = Table_Frame_Decorator::find_parent_table($frame);
39
+
40
+ if ( $table->get_style()->border_collapse !== "collapse" ) {
41
+ $this->_render_border($frame);
42
+ $this->_render_outline($frame);
43
+ return;
44
+ }
45
+
46
+ // The collapsed case is slightly complicated...
47
+ // @todo Add support for outlines here
48
+
49
+ $cellmap = $table->get_cellmap();
50
+ $cells = $cellmap->get_spanned_cells($frame);
51
+ $num_rows = $cellmap->get_num_rows();
52
+ $num_cols = $cellmap->get_num_cols();
53
+
54
+ // Determine the top row spanned by this cell
55
+ $i = $cells["rows"][0];
56
+ $top_row = $cellmap->get_row($i);
57
+
58
+ // Determine if this cell borders on the bottom of the table. If so,
59
+ // then we draw its bottom border. Otherwise the next row down will
60
+ // draw its top border instead.
61
+ if (in_array( $num_rows - 1, $cells["rows"])) {
62
+ $draw_bottom = true;
63
+ $bottom_row = $cellmap->get_row($num_rows - 1);
64
+ } else
65
+ $draw_bottom = false;
66
+
67
+
68
+ // Draw the horizontal borders
69
+ foreach ( $cells["columns"] as $j ) {
70
+ $bp = $cellmap->get_border_properties($i, $j);
71
+
72
+ $y = $top_row["y"] - $bp["top"]["width"] / 2;
73
+
74
+ $col = $cellmap->get_column($j);
75
+ $x = $col["x"] - $bp["left"]["width"] / 2;
76
+ $w = $col["used-width"] + ($bp["left"]["width"] + $bp["right"]["width"] ) / 2;
77
+
78
+ if ( $bp["top"]["style"] !== "none" && $bp["top"]["width"] > 0 ) {
79
+ $widths = array($bp["top"]["width"],
80
+ $bp["right"]["width"],
81
+ $bp["bottom"]["width"],
82
+ $bp["left"]["width"]);
83
+ $method = "_border_". $bp["top"]["style"];
84
+ $this->$method($x, $y, $w, $bp["top"]["color"], $widths, "top", "square");
85
+ }
86
+
87
+ if ( $draw_bottom ) {
88
+ $bp = $cellmap->get_border_properties($num_rows - 1, $j);
89
+ if ( $bp["bottom"]["style"] === "none" || $bp["bottom"]["width"] <= 0 )
90
+ continue;
91
+
92
+ $y = $bottom_row["y"] + $bottom_row["height"] + $bp["bottom"]["width"] / 2;
93
+
94
+ $widths = array($bp["top"]["width"],
95
+ $bp["right"]["width"],
96
+ $bp["bottom"]["width"],
97
+ $bp["left"]["width"]);
98
+ $method = "_border_". $bp["bottom"]["style"];
99
+ $this->$method($x, $y, $w, $bp["bottom"]["color"], $widths, "bottom", "square");
100
+
101
+ }
102
+ }
103
+
104
+ $j = $cells["columns"][0];
105
+
106
+ $left_col = $cellmap->get_column($j);
107
+
108
+ if (in_array($num_cols - 1, $cells["columns"])) {
109
+ $draw_right = true;
110
+ $right_col = $cellmap->get_column($num_cols - 1);
111
+ } else
112
+ $draw_right = false;
113
+
114
+ // Draw the vertical borders
115
+ foreach ( $cells["rows"] as $i ) {
116
+ $bp = $cellmap->get_border_properties($i, $j);
117
+
118
+ $x = $left_col["x"] - $bp["left"]["width"] / 2;
119
+
120
+ $row = $cellmap->get_row($i);
121
+
122
+ $y = $row["y"] - $bp["top"]["width"] / 2;
123
+ $h = $row["height"] + ($bp["top"]["width"] + $bp["bottom"]["width"])/ 2;
124
+
125
+ if ( $bp["left"]["style"] !== "none" && $bp["left"]["width"] > 0 ) {
126
+
127
+ $widths = array($bp["top"]["width"],
128
+ $bp["right"]["width"],
129
+ $bp["bottom"]["width"],
130
+ $bp["left"]["width"]);
131
+
132
+ $method = "_border_" . $bp["left"]["style"];
133
+ $this->$method($x, $y, $h, $bp["left"]["color"], $widths, "left", "square");
134
+ }
135
+
136
+ if ( $draw_right ) {
137
+ $bp = $cellmap->get_border_properties($i, $num_cols - 1);
138
+ if ( $bp["right"]["style"] === "none" || $bp["right"]["width"] <= 0 )
139
+ continue;
140
+
141
+ $x = $right_col["x"] + $right_col["used-width"] + $bp["right"]["width"] / 2;
142
+
143
+ $widths = array($bp["top"]["width"],
144
+ $bp["right"]["width"],
145
+ $bp["bottom"]["width"],
146
+ $bp["left"]["width"]);
147
+
148
+ $method = "_border_" . $bp["right"]["style"];
149
+ $this->$method($x, $y, $h, $bp["right"]["color"], $widths, "right", "square");
150
+
151
+ }
152
+ }
153
+
154
+ }
155
+ }
dompdf/include/table_frame_decorator.cls.php ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Decorates Frames for table layout
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Frame_Decorator extends Frame_Decorator {
16
+ static $VALID_CHILDREN = array("table-row-group",
17
+ "table-row",
18
+ "table-header-group",
19
+ "table-footer-group",
20
+ "table-column",
21
+ "table-column-group",
22
+ "table-caption",
23
+ "table-cell");
24
+
25
+ static $ROW_GROUPS = array('table-row-group',
26
+ 'table-header-group',
27
+ 'table-footer-group');
28
+
29
+ /**
30
+ * The Cellmap object for this table. The cellmap maps table cells
31
+ * to rows and columns, and aids in calculating column widths.
32
+ *
33
+ * @var Cellmap
34
+ */
35
+ protected $_cellmap;
36
+
37
+ /**
38
+ * The minimum width of the table, in pt
39
+ *
40
+ * @var float
41
+ */
42
+ protected $_min_width;
43
+
44
+ /**
45
+ * The maximum width of the table, in pt
46
+ *
47
+ * @var float
48
+ */
49
+ protected $_max_width;
50
+
51
+ /**
52
+ * Table header rows. Each table header is duplicated when a table
53
+ * spans pages.
54
+ *
55
+ * @var array
56
+ */
57
+ protected $_headers;
58
+
59
+ /**
60
+ * Table footer rows. Each table footer is duplicated when a table
61
+ * spans pages.
62
+ *
63
+ * @var array
64
+ */
65
+ protected $_footers;
66
+
67
+ /**
68
+ * Class constructor
69
+ *
70
+ * @param Frame $frame the frame to decorate
71
+ * @param DOMPDF $dompdf
72
+ */
73
+ function __construct(Frame $frame, DOMPDF $dompdf) {
74
+ parent::__construct($frame, $dompdf);
75
+ $this->_cellmap = new Cellmap($this);
76
+
77
+ if ( $frame->get_style()->table_layout === "fixed" ) {
78
+ $this->_cellmap->set_layout_fixed(true);
79
+ }
80
+
81
+ $this->_min_width = null;
82
+ $this->_max_width = null;
83
+ $this->_headers = array();
84
+ $this->_footers = array();
85
+ }
86
+
87
+
88
+ function reset() {
89
+ parent::reset();
90
+ $this->_cellmap->reset();
91
+ $this->_min_width = null;
92
+ $this->_max_width = null;
93
+ $this->_headers = array();
94
+ $this->_footers = array();
95
+ $this->_reflower->reset();
96
+ }
97
+
98
+ //........................................................................
99
+
100
+ /**
101
+ * split the table at $row. $row and all subsequent rows will be
102
+ * added to the clone. This method is overidden in order to remove
103
+ * frames from the cellmap properly.
104
+ *
105
+ * @param Frame $child
106
+ * @param bool $force_pagebreak
107
+ *
108
+ * @return void
109
+ */
110
+ function split(Frame $child = null, $force_pagebreak = false) {
111
+
112
+ if ( is_null($child) ) {
113
+ parent::split();
114
+ return;
115
+ }
116
+
117
+ // If $child is a header or if it is the first non-header row, do
118
+ // not duplicate headers, simply move the table to the next page.
119
+ if ( count($this->_headers) && !in_array($child, $this->_headers, true) &&
120
+ !in_array($child->get_prev_sibling(), $this->_headers, true) ) {
121
+
122
+ $first_header = null;
123
+
124
+ // Insert copies of the table headers before $child
125
+ foreach ($this->_headers as $header) {
126
+
127
+ $new_header = $header->deep_copy();
128
+
129
+ if ( is_null($first_header) )
130
+ $first_header = $new_header;
131
+
132
+ $this->insert_child_before($new_header, $child);
133
+ }
134
+
135
+ parent::split($first_header);
136
+
137
+ } else if ( in_array($child->get_style()->display, self::$ROW_GROUPS) ) {
138
+
139
+ // Individual rows should have already been handled
140
+ parent::split($child);
141
+
142
+ } else {
143
+
144
+ $iter = $child;
145
+
146
+ while ($iter) {
147
+ $this->_cellmap->remove_row($iter);
148
+ $iter = $iter->get_next_sibling();
149
+ }
150
+
151
+ parent::split($child);
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Return a copy of this frame with $node as its node
157
+ *
158
+ * @param DOMNode $node
159
+ * @return Frame
160
+ */
161
+ function copy(DOMNode $node) {
162
+ $deco = parent::copy($node);
163
+
164
+ // In order to keep columns' widths through pages
165
+ $deco->_cellmap->set_columns($this->_cellmap->get_columns());
166
+ $deco->_cellmap->lock_columns();
167
+
168
+ return $deco;
169
+ }
170
+
171
+ /**
172
+ * Static function to locate the parent table of a frame
173
+ *
174
+ * @param Frame $frame
175
+ * @return Table_Frame_Decorator the table that is an ancestor of $frame
176
+ */
177
+ static function find_parent_table(Frame $frame) {
178
+
179
+ while ( $frame = $frame->get_parent() )
180
+ if ( $frame->is_table() )
181
+ break;
182
+
183
+ return $frame;
184
+ }
185
+
186
+ /**
187
+ * Return this table's Cellmap
188
+ *
189
+ * @return Cellmap
190
+ */
191
+ function get_cellmap() { return $this->_cellmap; }
192
+
193
+ /**
194
+ * Return the minimum width of this table
195
+ *
196
+ * @return float
197
+ */
198
+ function get_min_width() { return $this->_min_width; }
199
+
200
+ /**
201
+ * Return the maximum width of this table
202
+ *
203
+ * @return float
204
+ */
205
+ function get_max_width() { return $this->_max_width; }
206
+
207
+ /**
208
+ * Set the minimum width of the table
209
+ *
210
+ * @param float $width the new minimum width
211
+ */
212
+ function set_min_width($width) { $this->_min_width = $width; }
213
+
214
+ /**
215
+ * Set the maximum width of the table
216
+ *
217
+ * @param float $width the new maximum width
218
+ */
219
+ function set_max_width($width) { $this->_max_width = $width; }
220
+
221
+ /**
222
+ * Restructure tree so that the table has the correct structure.
223
+ * Invalid children (i.e. all non-table-rows) are moved below the
224
+ * table.
225
+ */
226
+ function normalise() {
227
+
228
+ // Store frames generated by invalid tags and move them outside the table
229
+ $erroneous_frames = array();
230
+ $anon_row = false;
231
+ $iter = $this->get_first_child();
232
+ while ( $iter ) {
233
+ $child = $iter;
234
+ $iter = $iter->get_next_sibling();
235
+
236
+ $display = $child->get_style()->display;
237
+
238
+ if ( $anon_row ) {
239
+
240
+ if ( $display === "table-row" ) {
241
+ // Add the previous anonymous row
242
+ $this->insert_child_before($table_row, $child);
243
+
244
+ $table_row->normalise();
245
+ $child->normalise();
246
+ $anon_row = false;
247
+ continue;
248
+ }
249
+
250
+ // add the child to the anonymous row
251
+ $table_row->append_child($child);
252
+ continue;
253
+
254
+ } else {
255
+
256
+ if ( $display === "table-row" ) {
257
+ $child->normalise();
258
+ continue;
259
+ }
260
+
261
+ if ( $display === "table-cell" ) {
262
+ // Create an anonymous table row
263
+ $tr = $this->get_node()->ownerDocument->createElement("tr");
264
+
265
+ $frame = new Frame($tr);
266
+
267
+ $css = $this->get_style()->get_stylesheet();
268
+ $style = $css->create_style();
269
+ $style->inherit($this->get_style());
270
+
271
+ // Lookup styles for tr tags. If the user wants styles to work
272
+ // better, they should make the tr explicit... I'm not going to
273
+ // try to guess what they intended.
274
+ if ( $tr_style = $css->lookup("tr") )
275
+ $style->merge($tr_style);
276
+
277
+ // Okay, I have absolutely no idea why I need this clone here, but
278
+ // if it's omitted, php (as of 2004-07-28) segfaults.
279
+ $frame->set_style(clone $style);
280
+ $table_row = Frame_Factory::decorate_frame($frame, $this->_dompdf, $this->_root);
281
+
282
+ // Add the cell to the row
283
+ $table_row->append_child($child);
284
+
285
+ $anon_row = true;
286
+ continue;
287
+ }
288
+
289
+ if ( !in_array($display, self::$VALID_CHILDREN) ) {
290
+ $erroneous_frames[] = $child;
291
+ continue;
292
+ }
293
+
294
+ // Normalise other table parts (i.e. row groups)
295
+ foreach ($child->get_children() as $grandchild) {
296
+ if ( $grandchild->get_style()->display === "table-row" ) {
297
+ $grandchild->normalise();
298
+ }
299
+ }
300
+
301
+ // Add headers and footers
302
+ if ( $display === "table-header-group" )
303
+ $this->_headers[] = $child;
304
+
305
+ else if ( $display === "table-footer-group" )
306
+ $this->_footers[] = $child;
307
+ }
308
+ }
309
+
310
+ if ( $anon_row ) {
311
+ // Add the row to the table
312
+ $this->_frame->append_child($table_row);
313
+ $table_row->normalise();
314
+ $this->_cellmap->add_row();
315
+ }
316
+
317
+ foreach ($erroneous_frames as $frame)
318
+ $this->move_after($frame);
319
+
320
+ }
321
+
322
+ //........................................................................
323
+
324
+ /**
325
+ * Moves the specified frame and it's corresponding node outside of
326
+ * the table.
327
+ *
328
+ * @param Frame $frame the frame to move
329
+ */
330
+ function move_after(Frame $frame) {
331
+ $this->get_parent()->insert_child_after($frame, $this);
332
+ }
333
+
334
+ }
dompdf/include/table_frame_reflower.cls.php ADDED
@@ -0,0 +1,578 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows tables
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Frame_Reflower extends Frame_Reflower {
16
+ /**
17
+ * Frame for this reflower
18
+ *
19
+ * @var Table_Frame_Decorator
20
+ */
21
+ protected $_frame;
22
+
23
+ /**
24
+ * Cache of results between call to get_min_max_width and assign_widths
25
+ *
26
+ * @var array
27
+ */
28
+ protected $_state;
29
+
30
+ function __construct(Table_Frame_Decorator $frame) {
31
+ $this->_state = null;
32
+ parent::__construct($frame);
33
+ }
34
+
35
+ /**
36
+ * State is held here so it needs to be reset along with the decorator
37
+ */
38
+ function reset() {
39
+ $this->_state = null;
40
+ $this->_min_max_cache = null;
41
+ }
42
+
43
+ //........................................................................
44
+
45
+ protected function _assign_widths() {
46
+ $style = $this->_frame->get_style();
47
+
48
+ // Find the min/max width of the table and sort the columns into
49
+ // absolute/percent/auto arrays
50
+ $min_width = $this->_state["min_width"];
51
+ $max_width = $this->_state["max_width"];
52
+ $percent_used = $this->_state["percent_used"];
53
+ $absolute_used = $this->_state["absolute_used"];
54
+ $auto_min = $this->_state["auto_min"];
55
+
56
+ $absolute =& $this->_state["absolute"];
57
+ $percent =& $this->_state["percent"];
58
+ $auto =& $this->_state["auto"];
59
+
60
+ // Determine the actual width of the table
61
+ $cb = $this->_frame->get_containing_block();
62
+ $columns =& $this->_frame->get_cellmap()->get_columns();
63
+
64
+ $width = $style->width;
65
+
66
+ // Calculate padding & border fudge factor
67
+ $left = $style->margin_left;
68
+ $right = $style->margin_right;
69
+
70
+ $centered = ( $left === "auto" && $right === "auto" );
71
+
72
+ $left = $left === "auto" ? 0 : $style->length_in_pt($left, $cb["w"]);
73
+ $right = $right === "auto" ? 0 : $style->length_in_pt($right, $cb["w"]);
74
+
75
+ $delta = $left + $right;
76
+
77
+ if ( !$centered ) {
78
+ $delta += $style->length_in_pt(array(
79
+ $style->padding_left,
80
+ $style->border_left_width,
81
+ $style->border_right_width,
82
+ $style->padding_right),
83
+ $cb["w"]);
84
+ }
85
+
86
+ $min_table_width = $style->length_in_pt( $style->min_width, $cb["w"] - $delta );
87
+
88
+ // min & max widths already include borders & padding
89
+ $min_width -= $delta;
90
+ $max_width -= $delta;
91
+
92
+ if ( $width !== "auto" ) {
93
+
94
+ $preferred_width = $style->length_in_pt($width, $cb["w"]) - $delta;
95
+
96
+ if ( $preferred_width < $min_table_width )
97
+ $preferred_width = $min_table_width;
98
+
99
+ if ( $preferred_width > $min_width )
100
+ $width = $preferred_width;
101
+ else
102
+ $width = $min_width;
103
+
104
+ } else {
105
+
106
+ if ( $max_width + $delta < $cb["w"] )
107
+ $width = $max_width;
108
+ else if ( $cb["w"] - $delta > $min_width )
109
+ $width = $cb["w"] - $delta;
110
+ else
111
+ $width = $min_width;
112
+
113
+ if ( $width < $min_table_width )
114
+ $width = $min_table_width;
115
+
116
+ }
117
+
118
+ // Store our resolved width
119
+ $style->width = $width;
120
+
121
+ $cellmap = $this->_frame->get_cellmap();
122
+
123
+ if ( $cellmap->is_columns_locked() ) {
124
+ return;
125
+ }
126
+
127
+ // If the whole table fits on the page, then assign each column it's max width
128
+ if ( $width == $max_width ) {
129
+
130
+ foreach (array_keys($columns) as $i)
131
+ $cellmap->set_column_width($i, $columns[$i]["max-width"]);
132
+
133
+ return;
134
+ }
135
+
136
+ // Determine leftover and assign it evenly to all columns
137
+ if ( $width > $min_width ) {
138
+
139
+ // We have four cases to deal with:
140
+ //
141
+ // 1. All columns are auto--no widths have been specified. In this
142
+ // case we distribute extra space across all columns weighted by max-width.
143
+ //
144
+ // 2. Only absolute widths have been specified. In this case we
145
+ // distribute any extra space equally among 'width: auto' columns, or all
146
+ // columns if no auto columns have been specified.
147
+ //
148
+ // 3. Only percentage widths have been specified. In this case we
149
+ // normalize the percentage values and distribute any remaining % to
150
+ // width: auto columns. We then proceed to assign widths as fractions
151
+ // of the table width.
152
+ //
153
+ // 4. Both absolute and percentage widths have been specified.
154
+
155
+ $increment = 0;
156
+
157
+ // Case 1:
158
+ if ( $absolute_used == 0 && $percent_used == 0 ) {
159
+ $increment = $width - $min_width;
160
+
161
+ foreach (array_keys($columns) as $i) {
162
+ $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment * ($columns[$i]["max-width"] / $max_width));
163
+ }
164
+ return;
165
+ }
166
+
167
+
168
+ // Case 2
169
+ if ( $absolute_used > 0 && $percent_used == 0 ) {
170
+
171
+ if ( count($auto) > 0 )
172
+ $increment = ($width - $auto_min - $absolute_used) / count($auto);
173
+
174
+ // Use the absolutely specified width or the increment
175
+ foreach (array_keys($columns) as $i) {
176
+
177
+ if ( $columns[$i]["absolute"] > 0 && count($auto) )
178
+ $cellmap->set_column_width($i, $columns[$i]["min-width"]);
179
+ else if ( count($auto) )
180
+ $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
181
+ else {
182
+ // All absolute columns
183
+ $increment = ($width - $absolute_used) * $columns[$i]["absolute"] / $absolute_used;
184
+
185
+ $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
186
+ }
187
+
188
+ }
189
+ return;
190
+ }
191
+
192
+
193
+ // Case 3:
194
+ if ( $absolute_used == 0 && $percent_used > 0 ) {
195
+
196
+ $scale = null;
197
+ $remaining = null;
198
+
199
+ // Scale percent values if the total percentage is > 100, or if all
200
+ // values are specified as percentages.
201
+ if ( $percent_used > 100 || count($auto) == 0)
202
+ $scale = 100 / $percent_used;
203
+ else
204
+ $scale = 1;
205
+
206
+ // Account for the minimum space used by the unassigned auto columns
207
+ $used_width = $auto_min;
208
+
209
+ foreach ($percent as $i) {
210
+ $columns[$i]["percent"] *= $scale;
211
+
212
+ $slack = $width - $used_width;
213
+
214
+ $w = min($columns[$i]["percent"] * $width/100, $slack);
215
+
216
+ if ( $w < $columns[$i]["min-width"] )
217
+ $w = $columns[$i]["min-width"];
218
+
219
+ $cellmap->set_column_width($i, $w);
220
+ $used_width += $w;
221
+
222
+ }
223
+
224
+ // This works because $used_width includes the min-width of each
225
+ // unassigned column
226
+ if ( count($auto) > 0 ) {
227
+ $increment = ($width - $used_width) / count($auto);
228
+
229
+ foreach ($auto as $i)
230
+ $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
231
+
232
+ }
233
+ return;
234
+ }
235
+
236
+ // Case 4:
237
+
238
+ // First-come, first served
239
+ if ( $absolute_used > 0 && $percent_used > 0 ) {
240
+
241
+ $used_width = $auto_min;
242
+
243
+ foreach ($absolute as $i) {
244
+ $cellmap->set_column_width($i, $columns[$i]["min-width"]);
245
+ $used_width += $columns[$i]["min-width"];
246
+ }
247
+
248
+ // Scale percent values if the total percentage is > 100 or there
249
+ // are no auto values to take up slack
250
+ if ( $percent_used > 100 || count($auto) == 0 )
251
+ $scale = 100 / $percent_used;
252
+ else
253
+ $scale = 1;
254
+
255
+ $remaining_width = $width - $used_width;
256
+
257
+ foreach ($percent as $i) {
258
+ $slack = $remaining_width - $used_width;
259
+
260
+ $columns[$i]["percent"] *= $scale;
261
+ $w = min($columns[$i]["percent"] * $remaining_width / 100, $slack);
262
+
263
+ if ( $w < $columns[$i]["min-width"] )
264
+ $w = $columns[$i]["min-width"];
265
+
266
+ $columns[$i]["used-width"] = $w;
267
+ $used_width += $w;
268
+ }
269
+
270
+ if ( count($auto) > 0 ) {
271
+ $increment = ($width - $used_width) / count($auto);
272
+
273
+ foreach ($auto as $i)
274
+ $cellmap->set_column_width($i, $columns[$i]["min-width"] + $increment);
275
+
276
+ }
277
+
278
+ return;
279
+ }
280
+
281
+
282
+ } else { // we are over constrained
283
+
284
+ // Each column gets its minimum width
285
+ foreach (array_keys($columns) as $i)
286
+ $cellmap->set_column_width($i, $columns[$i]["min-width"]);
287
+
288
+ }
289
+ }
290
+
291
+ //........................................................................
292
+
293
+ // Determine the frame's height based on min/max height
294
+ protected function _calculate_height() {
295
+
296
+ $style = $this->_frame->get_style();
297
+ $height = $style->height;
298
+
299
+ $cellmap = $this->_frame->get_cellmap();
300
+ $cellmap->assign_frame_heights();
301
+ $rows = $cellmap->get_rows();
302
+
303
+ // Determine our content height
304
+ $content_height = 0;
305
+ foreach ( $rows as $r )
306
+ $content_height += $r["height"];
307
+
308
+ $cb = $this->_frame->get_containing_block();
309
+
310
+ if ( !($style->overflow === "visible" ||
311
+ ($style->overflow === "hidden" && $height === "auto")) ) {
312
+
313
+ // Only handle min/max height if the height is independent of the frame's content
314
+
315
+ $min_height = $style->min_height;
316
+ $max_height = $style->max_height;
317
+
318
+ if ( isset($cb["h"]) ) {
319
+ $min_height = $style->length_in_pt($min_height, $cb["h"]);
320
+ $max_height = $style->length_in_pt($max_height, $cb["h"]);
321
+
322
+ } else if ( isset($cb["w"]) ) {
323
+
324
+ if ( mb_strpos($min_height, "%") !== false )
325
+ $min_height = 0;
326
+ else
327
+ $min_height = $style->length_in_pt($min_height, $cb["w"]);
328
+
329
+ if ( mb_strpos($max_height, "%") !== false )
330
+ $max_height = "none";
331
+ else
332
+ $max_height = $style->length_in_pt($max_height, $cb["w"]);
333
+ }
334
+
335
+ if ( $max_height !== "none" && $min_height > $max_height )
336
+ // Swap 'em
337
+ list($max_height, $min_height) = array($min_height, $max_height);
338
+
339
+ if ( $max_height !== "none" && $height > $max_height )
340
+ $height = $max_height;
341
+
342
+ if ( $height < $min_height )
343
+ $height = $min_height;
344
+
345
+ } else {
346
+
347
+ // Use the content height or the height value, whichever is greater
348
+ if ( $height !== "auto" ) {
349
+ $height = $style->length_in_pt($height, $cb["h"]);
350
+
351
+ if ( $height <= $content_height )
352
+ $height = $content_height;
353
+ else
354
+ $cellmap->set_frame_heights($height,$content_height);
355
+
356
+ } else
357
+ $height = $content_height;
358
+
359
+ }
360
+
361
+ return $height;
362
+
363
+ }
364
+ //........................................................................
365
+
366
+ /**
367
+ * @param Block_Frame_Decorator $block
368
+ */
369
+ function reflow(Block_Frame_Decorator $block = null) {
370
+ /**
371
+ * @var Table_Frame_Decorator
372
+ */
373
+ $frame = $this->_frame;
374
+
375
+ // Check if a page break is forced
376
+ $page = $frame->get_root();
377
+ $page->check_forced_page_break($frame);
378
+
379
+ // Bail if the page is full
380
+ if ( $page->is_full() )
381
+ return;
382
+
383
+ // Let the page know that we're reflowing a table so that splits
384
+ // are suppressed (simply setting page-break-inside: avoid won't
385
+ // work because we may have an arbitrary number of block elements
386
+ // inside tds.)
387
+ $page->table_reflow_start();
388
+
389
+ // Collapse vertical margins, if required
390
+ $this->_collapse_margins();
391
+
392
+ $frame->position();
393
+
394
+ // Table layout algorithm:
395
+ // http://www.w3.org/TR/CSS21/tables.html#auto-table-layout
396
+
397
+ if ( is_null($this->_state) )
398
+ $this->get_min_max_width();
399
+
400
+ $cb = $frame->get_containing_block();
401
+ $style = $frame->get_style();
402
+
403
+ // This is slightly inexact, but should be okay. Add half the
404
+ // border-spacing to the table as padding. The other half is added to
405
+ // the cells themselves.
406
+ if ( $style->border_collapse === "separate" ) {
407
+ list($h, $v) = $style->border_spacing;
408
+
409
+ $v = $style->length_in_pt($v) / 2;
410
+ $h = $style->length_in_pt($h) / 2;
411
+
412
+ $style->padding_left = $style->length_in_pt($style->padding_left, $cb["w"]) + $h;
413
+ $style->padding_right = $style->length_in_pt($style->padding_right, $cb["w"]) + $h;
414
+ $style->padding_top = $style->length_in_pt($style->padding_top, $cb["h"]) + $v;
415
+ $style->padding_bottom = $style->length_in_pt($style->padding_bottom, $cb["h"]) + $v;
416
+
417
+ }
418
+
419
+ $this->_assign_widths();
420
+
421
+ // Adjust left & right margins, if they are auto
422
+ $width = $style->width;
423
+ $left = $style->margin_left;
424
+ $right = $style->margin_right;
425
+
426
+ $diff = $cb["w"] - $width;
427
+
428
+ if ( $left === "auto" && $right === "auto" ) {
429
+ if ( $diff < 0 ) {
430
+ $left = 0;
431
+ $right = $diff;
432
+ }
433
+ else {
434
+ $left = $right = $diff / 2;
435
+ }
436
+
437
+ $style->margin_left = "$left pt";
438
+ $style->margin_right = "$right pt";
439
+
440
+ } else {
441
+ if ( $left === "auto" ) {
442
+ $left = $style->length_in_pt($cb["w"] - $right - $width, $cb["w"]);
443
+ }
444
+ if ( $right === "auto" ) {
445
+ $left = $style->length_in_pt($left, $cb["w"]);
446
+ }
447
+ }
448
+
449
+ list($x, $y) = $frame->get_position();
450
+
451
+ // Determine the content edge
452
+ $content_x = $x + $left + $style->length_in_pt(array($style->padding_left,
453
+ $style->border_left_width), $cb["w"]);
454
+ $content_y = $y + $style->length_in_pt(array($style->margin_top,
455
+ $style->border_top_width,
456
+ $style->padding_top), $cb["h"]);
457
+
458
+ if ( isset($cb["h"]) )
459
+ $h = $cb["h"];
460
+ else
461
+ $h = null;
462
+
463
+ $cellmap = $frame->get_cellmap();
464
+ $col =& $cellmap->get_column(0);
465
+ $col["x"] = $content_x;
466
+
467
+ $row =& $cellmap->get_row(0);
468
+ $row["y"] = $content_y;
469
+
470
+ $cellmap->assign_x_positions();
471
+
472
+ // Set the containing block of each child & reflow
473
+ foreach ( $frame->get_children() as $child ) {
474
+
475
+ // Bail if the page is full
476
+ if ( !$page->in_nested_table() && $page->is_full() )
477
+ break;
478
+
479
+ $child->set_containing_block($content_x, $content_y, $width, $h);
480
+ $child->reflow();
481
+
482
+ if ( !$page->in_nested_table() )
483
+ // Check if a split has occured
484
+ $page->check_page_break($child);
485
+
486
+ }
487
+
488
+ // Assign heights to our cells:
489
+ $style->height = $this->_calculate_height();
490
+
491
+ if ( $style->border_collapse === "collapse" ) {
492
+ // Unset our borders because our cells are now using them
493
+ $style->border_style = "none";
494
+ }
495
+
496
+ $page->table_reflow_end();
497
+
498
+ // Debugging:
499
+ //echo ($this->_frame->get_cellmap());
500
+
501
+ if ( $block && $style->float === "none" && $frame->is_in_flow() ) {
502
+ $block->add_frame_to_line($frame);
503
+ $block->add_line();
504
+ }
505
+ }
506
+
507
+ //........................................................................
508
+
509
+ function get_min_max_width() {
510
+
511
+ if ( !is_null($this->_min_max_cache) )
512
+ return $this->_min_max_cache;
513
+
514
+ $style = $this->_frame->get_style();
515
+
516
+ $this->_frame->normalise();
517
+
518
+ // Add the cells to the cellmap (this will calcluate column widths as
519
+ // frames are added)
520
+ $this->_frame->get_cellmap()->add_frame($this->_frame);
521
+
522
+ // Find the min/max width of the table and sort the columns into
523
+ // absolute/percent/auto arrays
524
+ $this->_state = array();
525
+ $this->_state["min_width"] = 0;
526
+ $this->_state["max_width"] = 0;
527
+
528
+ $this->_state["percent_used"] = 0;
529
+ $this->_state["absolute_used"] = 0;
530
+ $this->_state["auto_min"] = 0;
531
+
532
+ $this->_state["absolute"] = array();
533
+ $this->_state["percent"] = array();
534
+ $this->_state["auto"] = array();
535
+
536
+ $columns =& $this->_frame->get_cellmap()->get_columns();
537
+ foreach (array_keys($columns) as $i) {
538
+ $this->_state["min_width"] += $columns[$i]["min-width"];
539
+ $this->_state["max_width"] += $columns[$i]["max-width"];
540
+
541
+ if ( $columns[$i]["absolute"] > 0 ) {
542
+ $this->_state["absolute"][] = $i;
543
+ $this->_state["absolute_used"] += $columns[$i]["absolute"];
544
+
545
+ } else if ( $columns[$i]["percent"] > 0 ) {
546
+ $this->_state["percent"][] = $i;
547
+ $this->_state["percent_used"] += $columns[$i]["percent"];
548
+
549
+ } else {
550
+ $this->_state["auto"][] = $i;
551
+ $this->_state["auto_min"] += $columns[$i]["min-width"];
552
+ }
553
+ }
554
+
555
+ // Account for margins & padding
556
+ $dims = array($style->border_left_width,
557
+ $style->border_right_width,
558
+ $style->padding_left,
559
+ $style->padding_right,
560
+ $style->margin_left,
561
+ $style->margin_right);
562
+
563
+ if ( $style->border_collapse !== "collapse" )
564
+ list($dims[]) = $style->border_spacing;
565
+
566
+ $delta = $style->length_in_pt($dims, $this->_frame->get_containing_block("w"));
567
+
568
+ $this->_state["min_width"] += $delta;
569
+ $this->_state["max_width"] += $delta;
570
+
571
+ return $this->_min_max_cache = array(
572
+ $this->_state["min_width"],
573
+ $this->_state["max_width"],
574
+ "min" => $this->_state["min_width"],
575
+ "max" => $this->_state["max_width"],
576
+ );
577
+ }
578
+ }
dompdf/include/table_row_frame_decorator.cls.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Decorates Frames for table row layout
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Row_Frame_Decorator extends Frame_Decorator {
16
+
17
+ // protected members
18
+
19
+ function __construct(Frame $frame, DOMPDF $dompdf) {
20
+ parent::__construct($frame, $dompdf);
21
+ }
22
+
23
+ //........................................................................
24
+
25
+ /**
26
+ * Remove all non table-cell frames from this row and move them after
27
+ * the table.
28
+ */
29
+ function normalise() {
30
+
31
+ // Find our table parent
32
+ $p = Table_Frame_Decorator::find_parent_table($this);
33
+
34
+ $erroneous_frames = array();
35
+ foreach ($this->get_children() as $child) {
36
+ $display = $child->get_style()->display;
37
+
38
+ if ( $display !== "table-cell" )
39
+ $erroneous_frames[] = $child;
40
+ }
41
+
42
+ // dump the extra nodes after the table.
43
+ foreach ($erroneous_frames as $frame)
44
+ $p->move_after($frame);
45
+ }
46
+
47
+
48
+ }
dompdf/include/table_row_frame_reflower.cls.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows table rows
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Row_Frame_Reflower extends Frame_Reflower {
16
+
17
+
18
+ function __construct(Table_Row_Frame_Decorator $frame) {
19
+ parent::__construct($frame);
20
+ }
21
+
22
+ //........................................................................
23
+
24
+ function reflow(Block_Frame_Decorator $block = null) {
25
+ $page = $this->_frame->get_root();
26
+
27
+ if ( $page->is_full() )
28
+ return;
29
+
30
+ $this->_frame->position();
31
+ $style = $this->_frame->get_style();
32
+ $cb = $this->_frame->get_containing_block();
33
+
34
+ foreach ($this->_frame->get_children() as $child) {
35
+
36
+ if ( $page->is_full() )
37
+ return;
38
+
39
+ $child->set_containing_block($cb);
40
+ $child->reflow();
41
+
42
+ }
43
+
44
+ if ( $page->is_full() )
45
+ return;
46
+
47
+ $table = Table_Frame_Decorator::find_parent_table($this->_frame);
48
+ $cellmap = $table->get_cellmap();
49
+ $style->width = $cellmap->get_frame_width($this->_frame);
50
+ $style->height = $cellmap->get_frame_height($this->_frame);
51
+
52
+ $this->_frame->set_position($cellmap->get_frame_position($this->_frame));
53
+
54
+ }
55
+
56
+ //........................................................................
57
+
58
+ function get_min_max_width() {
59
+ throw new DOMPDF_Exception("Min/max width is undefined for table rows");
60
+ }
61
+ }
dompdf/include/table_row_group_frame_decorator.cls.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Table row group decorator
11
+ *
12
+ * Overrides split() method for tbody, thead & tfoot elements
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ */
17
+ class Table_Row_Group_Frame_Decorator extends Frame_Decorator {
18
+
19
+ /**
20
+ * Class constructor
21
+ *
22
+ * @param Frame $frame Frame to decorate
23
+ * @param DOMPDF $dompdf Current dompdf instance
24
+ */
25
+ function __construct(Frame $frame, DOMPDF $dompdf) {
26
+ parent::__construct($frame, $dompdf);
27
+ }
28
+
29
+ /**
30
+ * Override split() to remove all child rows and this element from the cellmap
31
+ *
32
+ * @param Frame $child
33
+ * @param bool $force_pagebreak
34
+ *
35
+ * @return void
36
+ */
37
+ function split(Frame $child = null, $force_pagebreak = false) {
38
+
39
+ if ( is_null($child) ) {
40
+ parent::split();
41
+ return;
42
+ }
43
+
44
+ // Remove child & all subsequent rows from the cellmap
45
+ $cellmap = $this->get_parent()->get_cellmap();
46
+ $iter = $child;
47
+
48
+ while ( $iter ) {
49
+ $cellmap->remove_row($iter);
50
+ $iter = $iter->get_next_sibling();
51
+ }
52
+
53
+ // If we are splitting at the first child remove the
54
+ // table-row-group from the cellmap as well
55
+ if ( $child === $this->get_first_child() ) {
56
+ $cellmap->remove_row_group($this);
57
+ parent::split();
58
+ return;
59
+ }
60
+
61
+ $cellmap->update_row_group($this, $child->get_prev_sibling());
62
+ parent::split($child);
63
+
64
+ }
65
+ }
66
+
dompdf/include/table_row_group_frame_reflower.cls.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Reflows table row groups (e.g. tbody tags)
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Row_Group_Frame_Reflower extends Frame_Reflower {
16
+
17
+ function __construct($frame) {
18
+ parent::__construct($frame);
19
+ }
20
+
21
+ function reflow(Block_Frame_Decorator $block = null) {
22
+ $page = $this->_frame->get_root();
23
+
24
+ $style = $this->_frame->get_style();
25
+
26
+ // Our width is equal to the width of our parent table
27
+ $table = Table_Frame_Decorator::find_parent_table($this->_frame);
28
+
29
+ $cb = $this->_frame->get_containing_block();
30
+
31
+ foreach ( $this->_frame->get_children() as $child) {
32
+ // Bail if the page is full
33
+ if ( $page->is_full() )
34
+ return;
35
+
36
+ $child->set_containing_block($cb["x"], $cb["y"], $cb["w"], $cb["h"]);
37
+ $child->reflow();
38
+
39
+ // Check if a split has occured
40
+ $page->check_page_break($child);
41
+
42
+ }
43
+
44
+ if ( $page->is_full() )
45
+ return;
46
+
47
+ $cellmap = $table->get_cellmap();
48
+ $style->width = $cellmap->get_frame_width($this->_frame);
49
+ $style->height = $cellmap->get_frame_height($this->_frame);
50
+
51
+ $this->_frame->set_position($cellmap->get_frame_position($this->_frame));
52
+
53
+ if ( $table->get_style()->border_collapse === "collapse" )
54
+ // Unset our borders because our cells are now using them
55
+ $style->border_style = "none";
56
+
57
+ }
58
+
59
+ }
dompdf/include/table_row_group_renderer.cls.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Renders block frames
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Row_Group_Renderer extends Block_Renderer {
16
+
17
+ //........................................................................
18
+
19
+ function render(Frame $frame) {
20
+ $style = $frame->get_style();
21
+
22
+ $this->_set_opacity( $frame->get_opacity( $style->opacity ) );
23
+
24
+ $this->_render_border($frame);
25
+ $this->_render_outline($frame);
26
+
27
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_BLOCKS) {
28
+ $this->_debug_layout($frame->get_border_box(), "red");
29
+ if (DEBUG_LAYOUT_PADDINGBOX) {
30
+ $this->_debug_layout($frame->get_padding_box(), "red", array(0.5, 0.5));
31
+ }
32
+ }
33
+
34
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES && $frame->get_decorator()) {
35
+ foreach ($frame->get_decorator()->get_line_boxes() as $line) {
36
+ $frame->_debug_layout(array($line->x, $line->y, $line->w, $line->h), "orange");
37
+ }
38
+ }
39
+ }
40
+ }
dompdf/include/table_row_positioner.cls.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ /**
10
+ * Positions table rows
11
+ *
12
+ * @access private
13
+ * @package dompdf
14
+ */
15
+ class Table_Row_Positioner extends Positioner {
16
+
17
+ function __construct(Frame_Decorator $frame) { parent::__construct($frame); }
18
+
19
+ //........................................................................
20
+
21
+ function position() {
22
+
23
+ $cb = $this->_frame->get_containing_block();
24
+ $p = $this->_frame->get_prev_sibling();
25
+
26
+ if ( $p )
27
+ $y = $p->get_position("y") + $p->get_margin_height();
28
+
29
+ else
30
+ $y = $cb["y"];
31
+
32
+ $this->_frame->set_position($cb["x"], $y);
33
+
34
+ }
35
+ }
dompdf/include/tcpdf_adapter.cls.php ADDED
@@ -0,0 +1,628 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
7
+ */
8
+
9
+ require_once DOMPDF_LIB_DIR . '/tcpdf/tcpdf.php';
10
+
11
+ /**
12
+ * TCPDF PDF Rendering interface
13
+ *
14
+ * TCPDF_Adapter provides a simple, stateless interface to TCPDF.
15
+ *
16
+ * Unless otherwise mentioned, all dimensions are in points (1/72 in).
17
+ * The coordinate origin is in the top left corner and y values
18
+ * increase downwards.
19
+ *
20
+ * See {@link http://tcpdf.sourceforge.net} for more information on
21
+ * the underlying TCPDF class.
22
+ *
23
+ * @package dompdf
24
+ */
25
+ class TCPDF_Adapter implements Canvas {
26
+
27
+ /**
28
+ * Dimensions of paper sizes in points
29
+ *
30
+ * @var array;
31
+ */
32
+ static public $PAPER_SIZES = array(); // Set to CPDF_Adapter::$PAPER_SIZES below.
33
+
34
+ /**
35
+ * @var DOMPDF
36
+ */
37
+ private $_dompdf;
38
+
39
+ /**
40
+ * Instance of the TCPDF class
41
+ *
42
+ * @var TCPDF
43
+ */
44
+ private $_pdf;
45
+
46
+ /**
47
+ * PDF width in points
48
+ *
49
+ * @var float
50
+ */
51
+ private $_width;
52
+
53
+ /**
54
+ * PDF height in points
55
+ *
56
+ * @var float
57
+ */
58
+ private $_height;
59
+
60
+ /**
61
+ * Last fill colour used
62
+ *
63
+ * @var array
64
+ */
65
+ private $_last_fill_color;
66
+
67
+ /**
68
+ * Last stroke colour used
69
+ *
70
+ * @var array
71
+ */
72
+ private $_last_stroke_color;
73
+
74
+ /**
75
+ * Last line width used
76
+ *
77
+ * @var float
78
+ */
79
+ private $_last_line_width;
80
+
81
+ /**
82
+ * Total number of pages
83
+ *
84
+ * @var int
85
+ */
86
+ private $_page_count;
87
+
88
+ /**
89
+ * Text to display on every page
90
+ *
91
+ * @var array
92
+ */
93
+ private $_page_text;
94
+
95
+ /**
96
+ * Array of pages for accessing after initial rendering is complete
97
+ *
98
+ * @var array
99
+ */
100
+ private $_pages;
101
+
102
+ /**
103
+ * Class constructor
104
+ *
105
+ * @param mixed $paper The size of paper to use either a string (see {@link CPDF_Adapter::$PAPER_SIZES}) or
106
+ * an array(xmin,ymin,xmax,ymax)
107
+ * @param string $orientation The orientation of the document (either 'landscape' or 'portrait')
108
+ * @param DOMPDF $dompdf
109
+ */
110
+ function __construct($paper = "letter", $orientation = "portrait", DOMPDF $dompdf) {
111
+
112
+ if ( is_array($paper) )
113
+ $size = $paper;
114
+ else if ( isset(self::$PAPER_SIZES[mb_strtolower($paper)]) )
115
+ $size = self::$PAPER_SIZES[$paper];
116
+ else
117
+ $size = self::$PAPER_SIZES["letter"];
118
+
119
+ if ( mb_strtolower($orientation) === "landscape" ) {
120
+ list($size[2], $size[3]) = array($size[3], $size[2]);
121
+ }
122
+
123
+ $this->_width = $size[2] - $size[0];
124
+ $this->_height = $size[3] - $size[1];
125
+
126
+ $this->_dompdf = $dompdf;
127
+
128
+ $this->_pdf = new TCPDF("P", "pt", array($this->_width, $this->_height));
129
+ $this->_pdf->Setcreator("DOMPDF Converter");
130
+
131
+ $this->_pdf->AddPage();
132
+
133
+ $this->_page_number = $this->_page_count = 1;
134
+ $this->_page_text = array();
135
+
136
+ $this->_last_fill_color = null;
137
+ $this->_last_stroke_color = null;
138
+ $this->_last_line_width = null;
139
+ }
140
+
141
+ function get_dompdf(){
142
+ return $this->_dompdf;
143
+ }
144
+
145
+ /**
146
+ * Remaps y coords from 4th to 1st quadrant
147
+ *
148
+ * @param float $y
149
+ * @return float
150
+ */
151
+ protected function y($y) { return $this->_height - $y; }
152
+
153
+ /**
154
+ * Sets the stroke colour
155
+ *
156
+ * @param array $color
157
+ *
158
+ * @return void
159
+ */
160
+ protected function _set_stroke_colour($color) {
161
+ $color[0] = round(255 * $color[0]);
162
+ $color[1] = round(255 * $color[1]);
163
+ $color[2] = round(255 * $color[2]);
164
+
165
+ if ( is_null($this->_last_stroke_color) || $color != $this->_last_stroke_color ) {
166
+ $this->_pdf->SetDrawColor($color[0],$color[1],$color[2]);
167
+ $this->_last_stroke_color = $color;
168
+ }
169
+
170
+ }
171
+
172
+ /**
173
+ * Sets the fill colour
174
+ *
175
+ * @param array $color
176
+ */
177
+ protected function _set_fill_colour($color) {
178
+ $color[0] = round(255 * $color[0]);
179
+ $color[1] = round(255 * $color[1]);
180
+ $color[2] = round(255 * $color[2]);
181
+
182
+ if ( is_null($this->_last_fill_color) || $color != $this->_last_fill_color ) {
183
+ $this->_pdf->SetDrawColor($color[0],$color[1],$color[2]);
184
+ $this->_last_fill_color = $color;
185
+ }
186
+
187
+ }
188
+
189
+ /**
190
+ * Return the TCPDF instance
191
+ *
192
+ * @return TCPDF
193
+ */
194
+ function get_tcpdf() { return $this->_pdf; }
195
+
196
+ /**
197
+ * Returns the current page number
198
+ *
199
+ * @return int
200
+ */
201
+ function get_page_number() {
202
+ return $this->_page_number;
203
+ }
204
+
205
+ /**
206
+ * Returns the total number of pages
207
+ *
208
+ * @return int
209
+ */
210
+ function get_page_count() {
211
+ return $this->_page_count;
212
+ }
213
+
214
+ /**
215
+ * Sets the total number of pages
216
+ *
217
+ * @param int $count
218
+ */
219
+ function set_page_count($count) {
220
+ $this->_page_count = (int)$count;
221
+ }
222
+
223
+ /**
224
+ * Draws a line from x1,y1 to x2,y2
225
+ *
226
+ * See {@link Style::munge_colour()} for the format of the colour array.
227
+ * See {@link Cpdf::setLineStyle()} for a description of the format of the
228
+ * $style parameter (aka dash).
229
+ *
230
+ * @param float $x1
231
+ * @param float $y1
232
+ * @param float $x2
233
+ * @param float $y2
234
+ * @param array $color
235
+ * @param float $width
236
+ * @param array $style
237
+ */
238
+ function line($x1, $y1, $x2, $y2, $color, $width, $style = null) {
239
+
240
+ if ( is_null($this->_last_line_width) || $width != $this->_last_line_width ) {
241
+ $this->_pdf->SetLineWidth($width);
242
+ $this->_last_line_width = $width;
243
+ }
244
+
245
+ $this->_set_stroke_colour($color);
246
+
247
+ // FIXME: ugh, need to handle different styles here
248
+ $this->_pdf->line($x1, $y1, $x2, $y2);
249
+ }
250
+
251
+ /**
252
+ * Draws a rectangle at x1,y1 with width w and height h
253
+ *
254
+ * See {@link Style::munge_colour()} for the format of the colour array.
255
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
256
+ * parameter (aka dash)
257
+ *
258
+ * @param float $x1
259
+ * @param float $y1
260
+ * @param float $w
261
+ * @param float $h
262
+ * @param array $color
263
+ * @param float $width
264
+ * @param array $style
265
+ */
266
+ function rectangle($x1, $y1, $w, $h, $color, $width, $style = null) {
267
+
268
+ if ( is_null($this->_last_line_width) || $width != $this->_last_line_width ) {
269
+ $this->_pdf->SetLineWidth($width);
270
+ $this->_last_line_width = $width;
271
+ }
272
+
273
+ $this->_set_stroke_colour($color);
274
+
275
+ // FIXME: ugh, need to handle styles here
276
+ $this->_pdf->rect($x1, $y1, $w, $h);
277
+
278
+ }
279
+
280
+ /**
281
+ * Draws a filled rectangle at x1,y1 with width w and height h
282
+ *
283
+ * See {@link Style::munge_colour()} for the format of the colour array.
284
+ *
285
+ * @param float $x1
286
+ * @param float $y1
287
+ * @param float $w
288
+ * @param float $h
289
+ * @param array $color
290
+ */
291
+ function filled_rectangle($x1, $y1, $w, $h, $color) {
292
+
293
+ $this->_set_fill_colour($color);
294
+
295
+ // FIXME: ugh, need to handle styles here
296
+ $this->_pdf->rect($x1, $y1, $w, $h, "F");
297
+ }
298
+
299
+ /**
300
+ * Draws a polygon
301
+ *
302
+ * The polygon is formed by joining all the points stored in the $points
303
+ * array. $points has the following structure:
304
+ * <code>
305
+ * array(0 => x1,
306
+ * 1 => y1,
307
+ * 2 => x2,
308
+ * 3 => y2,
309
+ * ...
310
+ * );
311
+ * </code>
312
+ *
313
+ * See {@link Style::munge_colour()} for the format of the colour array.
314
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
315
+ * parameter (aka dash)
316
+ *
317
+ * @param array $points
318
+ * @param array $color
319
+ * @param float $width
320
+ * @param array $style
321
+ * @param bool $fill Fills the polygon if true
322
+ */
323
+ function polygon($points, $color, $width = null, $style = null, $fill = false) {
324
+ // FIXME
325
+ }
326
+
327
+ /**
328
+ * Draws a circle at $x,$y with radius $r
329
+ *
330
+ * See {@link Style::munge_colour()} for the format of the colour array.
331
+ * See {@link Cpdf::setLineStyle()} for a description of the $style
332
+ * parameter (aka dash)
333
+ *
334
+ * @param float $x
335
+ * @param float $y
336
+ * @param float $r
337
+ * @param array $color
338
+ * @param float $width
339
+ * @param array $style
340
+ * @param bool $fill Fills the circle if true
341
+ */
342
+ function circle($x, $y, $r, $color, $width = null, $style = null, $fill = false) {
343
+ // FIXME
344
+ }
345
+
346
+ /**
347
+ * Add an image to the pdf.
348
+ * The image is placed at the specified x and y coordinates with the
349
+ * given width and height.
350
+ *
351
+ * @param string $img_url the path to the image
352
+ * @param float $x x position
353
+ * @param float $y y position
354
+ * @param int $w width (in pixels)
355
+ * @param int $h height (in pixels)
356
+ * @param string $resolution
357
+ *
358
+ * @return void
359
+ */
360
+ function image($img_url, $x, $y, $w, $h, $resolution = "normal") {
361
+ // FIXME
362
+ }
363
+
364
+ /**
365
+ * Writes text at the specified x and y coordinates
366
+ * See {@link Style::munge_colour()} for the format of the colour array.
367
+ *
368
+ * @param float $x
369
+ * @param float $y
370
+ * @param string $text the text to write
371
+ * @param string $font the font file to use
372
+ * @param float $size the font size, in points
373
+ * @param array $color
374
+ * @param float $word_space word spacing adjustment
375
+ * @param float $char_space
376
+ * @param float $angle
377
+ *
378
+ * @return void
379
+ */
380
+ function text($x, $y, $text, $font, $size, $color = array(0,0,0), $word_space = 0.0, $char_space = 0.0, $angle = 0.0) {
381
+ // FIXME
382
+ }
383
+
384
+ function javascript($code) {
385
+ // FIXME
386
+ }
387
+
388
+ /**
389
+ * Add a named destination (similar to <a name="foo">...</a> in html)
390
+ *
391
+ * @param string $anchorname The name of the named destination
392
+ */
393
+ function add_named_dest($anchorname) {
394
+ // FIXME
395
+ }
396
+
397
+ /**
398
+ * Add a link to the pdf
399
+ *
400
+ * @param string $url The url to link to
401
+ * @param float $x The x position of the link
402
+ * @param float $y The y position of the link
403
+ * @param float $width The width of the link
404
+ * @param float $height The height of the link
405
+ */
406
+ function add_link($url, $x, $y, $width, $height) {
407
+ // FIXME
408
+ }
409
+
410
+ /**
411
+ * Add meta information to the PDF
412
+ *
413
+ * @param string $label label of the value (Creator, Producer, etc.)
414
+ * @param string $value the text to set
415
+ */
416
+ function add_info($label, $value) {
417
+ $method = "Set$label";
418
+ if ( in_array("Title", "Author", "Keywords", "Subject") && method_exists($this->_pdf, $method) ) {
419
+ $this->_pdf->$method($value);
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Calculates text size, in points
425
+ *
426
+ * @param string $text the text to be sized
427
+ * @param string $font the desired font
428
+ * @param float $size the desired font size
429
+ * @param float $word_spacing word spacing, if any
430
+ * @param float $char_spacing
431
+ *
432
+ * @return float
433
+ */
434
+ function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0) {
435
+ // FIXME
436
+ }
437
+
438
+ /**
439
+ * Calculates font height, in points
440
+ *
441
+ * @param string $font
442
+ * @param float $size
443
+ * @return float
444
+ */
445
+ function get_font_height($font, $size) {
446
+ // FIXME
447
+ }
448
+
449
+
450
+ /**
451
+ * Starts a new page
452
+ *
453
+ * Subsequent drawing operations will appear on the new page.
454
+ */
455
+ function new_page() {
456
+ // FIXME
457
+ }
458
+
459
+ /**
460
+ * Streams the PDF directly to the browser
461
+ *
462
+ * @param string $filename the name of the PDF file
463
+ * @param array $options associative array, 'Attachment' => 0 or 1, 'compress' => 1 or 0
464
+ */
465
+ function stream($filename, $options = null) {
466
+ // FIXME
467
+ }
468
+
469
+ /**
470
+ * Returns the PDF as a string
471
+ *
472
+ * @param array $options associative array: 'compress' => 1 or 0
473
+ * @return string
474
+ */
475
+ function output($options = null) {
476
+ // FIXME
477
+ }
478
+
479
+ /**
480
+ * Starts a clipping rectangle at x1,y1 with width w and height h
481
+ *
482
+ * @param float $x1
483
+ * @param float $y1
484
+ * @param float $w
485
+ * @param float $h
486
+ */
487
+ function clipping_rectangle($x1, $y1, $w, $h) {
488
+ // TODO: Implement clipping_rectangle() method.
489
+ }
490
+
491
+ /**
492
+ * Starts a rounded clipping rectangle at x1,y1 with width w and height h
493
+ *
494
+ * @param float $x1
495
+ * @param float $y1
496
+ * @param float $w
497
+ * @param float $h
498
+ * @param float $tl
499
+ * @param float $tr
500
+ * @param float $br
501
+ * @param float $bl
502
+ *
503
+ * @return void
504
+ */
505
+ function clipping_roundrectangle($x1, $y1, $w, $h, $tl, $tr, $br, $bl) {
506
+ // TODO: Implement clipping_roundrectangle() method.
507
+ }
508
+
509
+ /**
510
+ * Ends the last clipping shape
511
+ */
512
+ function clipping_end() {
513
+ // TODO: Implement clipping_end() method.
514
+ }
515
+
516
+ /**
517
+ * Save current state
518
+ */
519
+ function save() {
520
+ // TODO: Implement save() method.
521
+ }
522
+
523
+ /**
524
+ * Restore last state
525
+ */
526
+ function restore() {
527
+ // TODO: Implement restore() method.
528
+ }
529
+
530
+ /**
531
+ * Rotate
532
+ */
533
+ function rotate($angle, $x, $y) {
534
+ // TODO: Implement rotate() method.
535
+ }
536
+
537
+ /**
538
+ * Skew
539
+ */
540
+ function skew($angle_x, $angle_y, $x, $y) {
541
+ // TODO: Implement skew() method.
542
+ }
543
+
544
+ /**
545
+ * Scale
546
+ */
547
+ function scale($s_x, $s_y, $x, $y) {
548
+ // TODO: Implement scale() method.
549
+ }
550
+
551
+ /**
552
+ * Translate
553
+ */
554
+ function translate($t_x, $t_y) {
555
+ // TODO: Implement translate() method.
556
+ }
557
+
558
+ /**
559
+ * Transform
560
+ */
561
+ function transform($a, $b, $c, $d, $e, $f) {
562
+ // TODO: Implement transform() method.
563
+ }
564
+
565
+ /**
566
+ * Add an arc to the PDF
567
+ * See {@link Style::munge_colour()} for the format of the colour array.
568
+ *
569
+ * @param float $x X coordinate of the arc
570
+ * @param float $y Y coordinate of the arc
571
+ * @param float $r1 Radius 1
572
+ * @param float $r2 Radius 2
573
+ * @param float $astart Start angle in degrees
574
+ * @param float $aend End angle in degrees
575
+ * @param array $color Color
576
+ * @param float $width
577
+ * @param array $style
578
+ *
579
+ * @return void
580
+ */
581
+ function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = array()) {
582
+ // TODO: Implement arc() method.
583
+ }
584
+
585
+ /**
586
+ * Calculates font baseline, in points
587
+ *
588
+ * @param string $font
589
+ * @param float $size
590
+ *
591
+ * @return float
592
+ */
593
+ function get_font_baseline($font, $size) {
594
+ // TODO: Implement get_font_baseline() method.
595
+ }
596
+
597
+ /**
598
+ * Sets the opacity
599
+ *
600
+ * @param float $opacity
601
+ * @param string $mode
602
+ */
603
+ function set_opacity($opacity, $mode = "Normal") {
604
+ // TODO: Implement set_opacity() method.
605
+ }
606
+
607
+ /**
608
+ * Sets the default view
609
+ *
610
+ * @param string $view
611
+ * 'XYZ' left, top, zoom
612
+ * 'Fit'
613
+ * 'FitH' top
614
+ * 'FitV' left
615
+ * 'FitR' left,bottom,right
616
+ * 'FitB'
617
+ * 'FitBH' top
618
+ * 'FitBV' left
619
+ * @param array $options
620
+ *
621
+ * @return void
622
+ */
623
+ function set_default_view($view, $options = array()) {
624
+ // TODO: Implement set_default_view() method.
625
+ }}
626
+
627
+ // Workaround for idiotic limitation on statics...
628
+ TCPDF_Adapter::$PAPER_SIZES = CPDF_Adapter::$PAPER_SIZES;
dompdf/include/text_frame_decorator.cls.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Brian Sweeney <eclecticgeek@gmail.com>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * Decorates Frame objects for text layout
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ */
17
+ class Text_Frame_Decorator extends Frame_Decorator {
18
+
19
+ // protected members
20
+ protected $_text_spacing;
21
+
22
+ // buggy DOMText::splitText (PHP < 5.2.7)
23
+ public static $_buggy_splittext;
24
+
25
+ function __construct(Frame $frame, DOMPDF $dompdf) {
26
+ if ( !$frame->is_text_node() )
27
+ throw new DOMPDF_Exception("Text_Decorator can only be applied to #text nodes.");
28
+
29
+ parent::__construct($frame, $dompdf);
30
+ $this->_text_spacing = null;
31
+ }
32
+
33
+ //........................................................................
34
+
35
+ function reset() {
36
+ parent::reset();
37
+ $this->_text_spacing = null;
38
+ }
39
+
40
+ //........................................................................
41
+
42
+ // Accessor methods
43
+ function get_text_spacing() { return $this->_text_spacing; }
44
+
45
+ function get_text() {
46
+ // FIXME: this should be in a child class (and is incorrect)
47
+ // if ( $this->_frame->get_style()->content !== "normal" ) {
48
+ // $this->_frame->get_node()->data = $this->_frame->get_style()->content;
49
+ // $this->_frame->get_style()->content = "normal";
50
+ // }
51
+
52
+ // pre_r("---");
53
+ // $style = $this->_frame->get_style();
54
+ // var_dump($text = $this->_frame->get_node()->data);
55
+ // var_dump($asc = utf8_decode($text));
56
+ // for ($i = 0; $i < strlen($asc); $i++)
57
+ // pre_r("$i: " . $asc[$i] . " - " . ord($asc[$i]));
58
+ // pre_r("width: " . Font_Metrics::get_text_width($text, $style->font_family, $style->font_size));
59
+
60
+ return $this->_frame->get_node()->data;
61
+ }
62
+
63
+ //........................................................................
64
+
65
+ // Vertical margins & padding do not apply to text frames
66
+
67
+ // http://www.w3.org/TR/CSS21/visudet.html#inline-non-replaced:
68
+ //
69
+ // The vertical padding, border and margin of an inline, non-replaced box
70
+ // start at the top and bottom of the content area, not the
71
+ // 'line-height'. But only the 'line-height' is used to calculate the
72
+ // height of the line box.
73
+ function get_margin_height() {
74
+ // This function is called in add_frame_to_line() and is used to
75
+ // determine the line height, so we actually want to return the
76
+ // 'line-height' property, not the actual margin box
77
+ $style = $this->get_parent()->get_style();
78
+ $font = $style->font_family;
79
+ $size = $style->font_size;
80
+
81
+ /*
82
+ pre_r('-----');
83
+ pre_r($style->line_height);
84
+ pre_r($style->font_size);
85
+ pre_r(Font_Metrics::get_font_height($font, $size));
86
+ pre_r(($style->line_height / $size) * Font_Metrics::get_font_height($font, $size));
87
+ */
88
+
89
+ return ($style->line_height / $size) * Font_Metrics::get_font_height($font, $size);
90
+
91
+ }
92
+
93
+ function get_padding_box() {
94
+ $pb = $this->_frame->get_padding_box();
95
+ $pb[3] = $pb["h"] = $this->_frame->get_style()->height;
96
+ return $pb;
97
+ }
98
+ //........................................................................
99
+
100
+ // Set method
101
+ function set_text_spacing($spacing) {
102
+ $style = $this->_frame->get_style();
103
+
104
+ $this->_text_spacing = $spacing;
105
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
106
+
107
+ // Re-adjust our width to account for the change in spacing
108
+ $style->width = Font_Metrics::get_text_width($this->get_text(), $style->font_family, $style->font_size, $spacing, $char_spacing);
109
+ }
110
+
111
+ //........................................................................
112
+
113
+ // Recalculate the text width
114
+ function recalculate_width() {
115
+ $style = $this->get_style();
116
+ $text = $this->get_text();
117
+ $size = $style->font_size;
118
+ $font = $style->font_family;
119
+ $word_spacing = $style->length_in_pt($style->word_spacing);
120
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
121
+
122
+ return $style->width = Font_Metrics::get_text_width($text, $font, $size, $word_spacing, $char_spacing);
123
+ }
124
+
125
+ //........................................................................
126
+
127
+ // Text manipulation methods
128
+
129
+ // split the text in this frame at the offset specified. The remaining
130
+ // text is added a sibling frame following this one and is returned.
131
+ function split_text($offset) {
132
+ if ( $offset == 0 )
133
+ return null;
134
+
135
+ if ( self::$_buggy_splittext ) {
136
+ // workaround to solve DOMText::spliText() bug parsing multibyte strings
137
+ $node = $this->_frame->get_node();
138
+ $txt0 = $node->substringData(0, $offset);
139
+ $txt1 = $node->substringData($offset, mb_strlen($node->textContent)-1);
140
+
141
+ $node->replaceData(0, mb_strlen($node->textContent), $txt0);
142
+ $split = $node->parentNode->appendChild(new DOMText($txt1));
143
+ }
144
+ else {
145
+ $split = $this->_frame->get_node()->splitText($offset);
146
+ }
147
+
148
+ $deco = $this->copy($split);
149
+
150
+ $p = $this->get_parent();
151
+ $p->insert_child_after($deco, $this, false);
152
+
153
+ if ( $p instanceof Inline_Frame_Decorator )
154
+ $p->split($deco);
155
+
156
+ return $deco;
157
+ }
158
+
159
+ //........................................................................
160
+
161
+ function delete_text($offset, $count) {
162
+ $this->_frame->get_node()->deleteData($offset, $count);
163
+ }
164
+
165
+ //........................................................................
166
+
167
+ function set_text($text) {
168
+ $this->_frame->get_node()->data = $text;
169
+ }
170
+
171
+ }
172
+
173
+ Text_Frame_Decorator::$_buggy_splittext = PHP_VERSION_ID < 50207;
dompdf/include/text_frame_reflower.cls.php ADDED
@@ -0,0 +1,441 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Fabien Ménager <fabien.menager@gmail.com>
7
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
8
+ */
9
+
10
+ /**
11
+ * Reflows text frames.
12
+ *
13
+ * @access private
14
+ * @package dompdf
15
+ */
16
+ class Text_Frame_Reflower extends Frame_Reflower {
17
+
18
+ /**
19
+ * @var Block_Frame_Decorator
20
+ */
21
+ protected $_block_parent; // Nearest block-level ancestor
22
+
23
+ /**
24
+ * @var Text_Frame_Decorator
25
+ */
26
+ protected $_frame;
27
+
28
+ public static $_whitespace_pattern = "/[ \t\r\n\f]+/u";
29
+
30
+ function __construct(Text_Frame_Decorator $frame) { parent::__construct($frame); }
31
+
32
+ //........................................................................
33
+
34
+ protected function _collapse_white_space($text) {
35
+ //$text = $this->_frame->get_text();
36
+ // if ( $this->_block_parent->get_current_line_box->w == 0 )
37
+ // $text = ltrim($text, " \n\r\t");
38
+ return preg_replace(self::$_whitespace_pattern, " ", $text);
39
+ }
40
+
41
+ //........................................................................
42
+
43
+ protected function _line_break($text) {
44
+ $style = $this->_frame->get_style();
45
+ $size = $style->font_size;
46
+ $font = $style->font_family;
47
+ $current_line = $this->_block_parent->get_current_line_box();
48
+
49
+ // Determine the available width
50
+ $line_width = $this->_frame->get_containing_block("w");
51
+ $current_line_width = $current_line->left + $current_line->w + $current_line->right;
52
+
53
+ $available_width = $line_width - $current_line_width;
54
+
55
+ // Account for word-spacing
56
+ $word_spacing = $style->length_in_pt($style->word_spacing);
57
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
58
+
59
+ // Determine the frame width including margin, padding & border
60
+ $text_width = Font_Metrics::get_text_width($text, $font, $size, $word_spacing, $char_spacing);
61
+ $mbp_width =
62
+ $style->length_in_pt( array( $style->margin_left,
63
+ $style->border_left_width,
64
+ $style->padding_left,
65
+ $style->padding_right,
66
+ $style->border_right_width,
67
+ $style->margin_right), $line_width );
68
+
69
+ $frame_width = $text_width + $mbp_width;
70
+
71
+ // Debugging:
72
+ // pre_r("Text: '" . htmlspecialchars($text). "'");
73
+ // pre_r("width: " .$frame_width);
74
+ // pre_r("textwidth + delta: $text_width + $mbp_width");
75
+ // pre_r("font-size: $size");
76
+ // pre_r("cb[w]: " .$line_width);
77
+ // pre_r("available width: " . $available_width);
78
+ // pre_r("current line width: " . $current_line_width);
79
+
80
+ // pre_r($words);
81
+
82
+ if ( $frame_width <= $available_width )
83
+ return false;
84
+
85
+ // split the text into words
86
+ $words = preg_split('/([\s-]+)/u', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
87
+ $wc = count($words);
88
+
89
+ // Determine the split point
90
+ $width = 0;
91
+ $str = "";
92
+ reset($words);
93
+
94
+ // @todo support <shy>, <wbr>
95
+ for ($i = 0; $i < $wc; $i += 2) {
96
+ $word = $words[$i] . (isset($words[$i+1]) ? $words[$i+1] : "");
97
+ $word_width = Font_Metrics::get_text_width($word, $font, $size, $word_spacing, $char_spacing);
98
+ if ( $width + $word_width + $mbp_width > $available_width )
99
+ break;
100
+
101
+ $width += $word_width;
102
+ $str .= $word;
103
+ }
104
+
105
+ $break_word = ($style->word_wrap === "break-word");
106
+
107
+ // The first word has overflowed. Force it onto the line
108
+ if ( $current_line_width == 0 && $width == 0 ) {
109
+
110
+ $s = "";
111
+ $last_width = 0;
112
+
113
+ if ( $break_word ) {
114
+ for ( $j = 0; $j < strlen($word); $j++ ) {
115
+ $s .= $word[$j];
116
+ $_width = Font_Metrics::get_text_width($s, $font, $size, $word_spacing, $char_spacing);
117
+ if ($_width > $available_width) {
118
+ break;
119
+ }
120
+
121
+ $last_width = $_width;
122
+ }
123
+ }
124
+
125
+ if ( $break_word && $last_width > 0 ) {
126
+ $width += $last_width;
127
+ $str .= substr($s, 0, -1);
128
+ }
129
+ else {
130
+ $width += $word_width;
131
+ $str .= $word;
132
+ }
133
+ }
134
+
135
+ $offset = mb_strlen($str);
136
+
137
+ // More debugging:
138
+ // pre_var_dump($str);
139
+ // pre_r("Width: ". $width);
140
+ // pre_r("Offset: " . $offset);
141
+
142
+ return $offset;
143
+
144
+ }
145
+
146
+ //........................................................................
147
+
148
+ protected function _newline_break($text) {
149
+
150
+ if ( ($i = mb_strpos($text, "\n")) === false)
151
+ return false;
152
+
153
+ return $i+1;
154
+
155
+ }
156
+
157
+ //........................................................................
158
+
159
+ protected function _layout_line() {
160
+ $frame = $this->_frame;
161
+ $style = $frame->get_style();
162
+ $text = $frame->get_text();
163
+ $size = $style->font_size;
164
+ $font = $style->font_family;
165
+
166
+ // Determine the text height
167
+ $style->height = Font_Metrics::get_font_height( $font, $size );
168
+
169
+ $split = false;
170
+ $add_line = false;
171
+
172
+ // Handle text transform:
173
+ // http://www.w3.org/TR/CSS21/text.html#propdef-text-transform
174
+ switch (strtolower($style->text_transform)) {
175
+ default: break;
176
+ case "capitalize": $text = mb_convert_case($text, MB_CASE_TITLE); break;
177
+ case "uppercase": $text = mb_convert_case($text, MB_CASE_UPPER); break;
178
+ case "lowercase": $text = mb_convert_case($text, MB_CASE_LOWER); break;
179
+ }
180
+
181
+ // Handle white-space property:
182
+ // http://www.w3.org/TR/CSS21/text.html#propdef-white-space
183
+ switch ($style->white_space) {
184
+
185
+ default:
186
+ case "normal":
187
+ $frame->set_text( $text = $this->_collapse_white_space($text) );
188
+ if ( $text == "" )
189
+ break;
190
+
191
+ $split = $this->_line_break($text);
192
+ break;
193
+
194
+ case "pre":
195
+ $split = $this->_newline_break($text);
196
+ $add_line = $split !== false;
197
+ break;
198
+
199
+ case "nowrap":
200
+ $frame->set_text( $text = $this->_collapse_white_space($text) );
201
+ break;
202
+
203
+ case "pre-wrap":
204
+ $split = $this->_newline_break($text);
205
+
206
+ if ( ($tmp = $this->_line_break($text)) !== false ) {
207
+ $add_line = $split < $tmp;
208
+ $split = min($tmp, $split);
209
+ } else
210
+ $add_line = true;
211
+
212
+ break;
213
+
214
+ case "pre-line":
215
+ // Collapse white-space except for \n
216
+ $frame->set_text( $text = preg_replace( "/[ \t]+/u", " ", $text ) );
217
+
218
+ if ( $text == "" )
219
+ break;
220
+
221
+ $split = $this->_newline_break($text);
222
+
223
+ if ( ($tmp = $this->_line_break($text)) !== false ) {
224
+ $add_line = $split < $tmp;
225
+ $split = min($tmp, $split);
226
+ } else
227
+ $add_line = true;
228
+
229
+ break;
230
+
231
+ }
232
+
233
+ // Handle degenerate case
234
+ if ( $text === "" )
235
+ return;
236
+
237
+ if ( $split !== false) {
238
+
239
+ // Handle edge cases
240
+ if ( $split == 0 && $text === " " ) {
241
+ $frame->set_text("");
242
+ return;
243
+ }
244
+
245
+ if ( $split == 0 ) {
246
+
247
+ // Trim newlines from the beginning of the line
248
+ //$this->_frame->set_text(ltrim($text, "\n\r"));
249
+
250
+ $this->_block_parent->add_line();
251
+ $frame->position();
252
+
253
+ // Layout the new line
254
+ $this->_layout_line();
255
+
256
+ }
257
+
258
+ else if ( $split < mb_strlen($frame->get_text()) ) {
259
+ // split the line if required
260
+ $frame->split_text($split);
261
+
262
+ $t = $frame->get_text();
263
+
264
+ // Remove any trailing newlines
265
+ if ( $split > 1 && $t[$split-1] === "\n" && !$frame->is_pre() )
266
+ $frame->set_text( mb_substr($t, 0, -1) );
267
+
268
+ // Do we need to trim spaces on wrapped lines? This might be desired, however, we
269
+ // can't trim the lines here or the layout will be affected if trimming the line
270
+ // leaves enough space to fit the next word in the text stream (because pdf layout
271
+ // is performed elsewhere).
272
+ /*if (!$this->_frame->get_prev_sibling() && !$this->_frame->get_next_sibling()) {
273
+ $t = $this->_frame->get_text();
274
+ $this->_frame->set_text( trim($t) );
275
+ }*/
276
+ }
277
+
278
+ if ( $add_line ) {
279
+ $this->_block_parent->add_line();
280
+ $frame->position();
281
+ }
282
+
283
+ } else {
284
+
285
+ // Remove empty space from start and end of line, but only where there isn't an inline sibling
286
+ // and the parent node isn't an inline element with siblings
287
+ // FIXME: Include non-breaking spaces?
288
+ $t = $frame->get_text();
289
+ $parent = $frame->get_parent();
290
+ $is_inline_frame = get_class($parent) === 'Inline_Frame_Decorator';
291
+
292
+ if ((!$is_inline_frame && !$frame->get_next_sibling()) ||
293
+ ( $is_inline_frame && !$parent->get_next_sibling())) {
294
+ $t = rtrim($t);
295
+ }
296
+
297
+ if ((!$is_inline_frame && !$frame->get_prev_sibling())/* ||
298
+ ( $is_inline_frame && !$parent->get_prev_sibling())*/) { // <span><span>A<span>B</span> C</span></span> fails (the whitespace is removed)
299
+ $t = ltrim($t);
300
+ }
301
+
302
+ $frame->set_text( $t );
303
+
304
+ }
305
+
306
+ // Set our new width
307
+ $width = $frame->recalculate_width();
308
+ }
309
+
310
+ //........................................................................
311
+
312
+ function reflow(Block_Frame_Decorator $block = null) {
313
+ $frame = $this->_frame;
314
+ $page = $frame->get_root();
315
+ $page->check_forced_page_break($this->_frame);
316
+
317
+ if ( $page->is_full() )
318
+ return;
319
+
320
+ $this->_block_parent = /*isset($block) ? $block : */$frame->find_block_parent();
321
+
322
+ // Left trim the text if this is the first text on the line and we're
323
+ // collapsing white space
324
+ // if ( $this->_block_parent->get_current_line()->w == 0 &&
325
+ // ($frame->get_style()->white_space !== "pre" ||
326
+ // $frame->get_style()->white_space !== "pre-wrap") ) {
327
+ // $frame->set_text( ltrim( $frame->get_text() ) );
328
+ // }
329
+
330
+ $frame->position();
331
+
332
+ $this->_layout_line();
333
+
334
+ if ( $block ) {
335
+ $block->add_frame_to_line($frame);
336
+ }
337
+ }
338
+
339
+ //........................................................................
340
+
341
+ // Returns an array(0 => min, 1 => max, "min" => min, "max" => max) of the
342
+ // minimum and maximum widths of this frame
343
+ function get_min_max_width() {
344
+ /*if ( !is_null($this->_min_max_cache) )
345
+ return $this->_min_max_cache;*/
346
+ $frame = $this->_frame;
347
+ $style = $frame->get_style();
348
+ $this->_block_parent = $frame->find_block_parent();
349
+ $line_width = $frame->get_containing_block("w");
350
+
351
+ $str = $text = $frame->get_text();
352
+ $size = $style->font_size;
353
+ $font = $style->font_family;
354
+
355
+ $word_spacing = $style->length_in_pt($style->word_spacing);
356
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
357
+
358
+ switch($style->white_space) {
359
+
360
+ default:
361
+ case "normal":
362
+ $str = preg_replace(self::$_whitespace_pattern," ", $str);
363
+ case "pre-wrap":
364
+ case "pre-line":
365
+
366
+ // Find the longest word (i.e. minimum length)
367
+
368
+ // This technique (using arrays & an anonymous function) is actually
369
+ // faster than doing a single-pass character by character scan. Heh,
370
+ // yes I took the time to bench it ;)
371
+ $words = array_flip(preg_split("/[\s-]+/u",$str, -1, PREG_SPLIT_DELIM_CAPTURE));
372
+ /*foreach($words as &$word) {
373
+ $word = Font_Metrics::get_text_width($word, $font, $size, $word_spacing, $char_spacing);
374
+ }*/
375
+ array_walk($words, create_function('&$val,$str',
376
+ '$val = Font_Metrics::get_text_width($str, "'.addslashes($font).'", '.$size.', '.$word_spacing.', '.$char_spacing.');'));
377
+ arsort($words);
378
+ $min = reset($words);
379
+ break;
380
+
381
+ case "pre":
382
+ $lines = array_flip(preg_split("/\n/u", $str));
383
+ /*foreach($words as &$word) {
384
+ $word = Font_Metrics::get_text_width($word, $font, $size, $word_spacing, $char_spacing);
385
+ }*/
386
+ array_walk($lines, create_function('&$val,$str',
387
+ '$val = Font_Metrics::get_text_width($str, "'.addslashes($font).'", '.$size.', '.$word_spacing.', '.$char_spacing.');'));
388
+
389
+ arsort($lines);
390
+ $min = reset($lines);
391
+ break;
392
+
393
+ case "nowrap":
394
+ $min = Font_Metrics::get_text_width($this->_collapse_white_space($str), $font, $size, $word_spacing, $char_spacing);
395
+ break;
396
+
397
+ }
398
+
399
+ switch ($style->white_space) {
400
+
401
+ default:
402
+ case "normal":
403
+ case "nowrap":
404
+ $str = preg_replace(self::$_whitespace_pattern," ", $text);
405
+ break;
406
+
407
+ case "pre-line":
408
+ //XXX: Is this correct?
409
+ $str = preg_replace( "/[ \t]+/u", " ", $text);
410
+
411
+ case "pre-wrap":
412
+ // Find the longest word (i.e. minimum length)
413
+ $lines = array_flip(preg_split("/\n/", $text));
414
+ /*foreach($words as &$word) {
415
+ $word = Font_Metrics::get_text_width($word, $font, $size, $word_spacing, $char_spacing);
416
+ }*/
417
+ array_walk($lines, create_function('&$val,$str',
418
+ '$val = Font_Metrics::get_text_width($str, "'.$font.'", '.$size.', '.$word_spacing.', '.$char_spacing.');'));
419
+ arsort($lines);
420
+ reset($lines);
421
+ $str = key($lines);
422
+ break;
423
+
424
+ }
425
+
426
+ $max = Font_Metrics::get_text_width($str, $font, $size, $word_spacing, $char_spacing);
427
+
428
+ $delta = $style->length_in_pt(array($style->margin_left,
429
+ $style->border_left_width,
430
+ $style->padding_left,
431
+ $style->padding_right,
432
+ $style->border_right_width,
433
+ $style->margin_right), $line_width);
434
+ $min += $delta;
435
+ $max += $delta;
436
+
437
+ return $this->_min_max_cache = array($min, $max, "min" => $min, "max" => $max);
438
+
439
+ }
440
+
441
+ }
dompdf/include/text_renderer.cls.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package dompdf
4
+ * @link http://www.dompdf.com/
5
+ * @author Benj Carson <benjcarson@digitaljunkies.ca>
6
+ * @author Helmut Tischer <htischer@weihenstephan.org>
7
+ * @author Fabien Ménager <fabien.menager@gmail.com>
8
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
9
+ */
10
+
11
+ /**
12
+ * Renders text frames
13
+ *
14
+ * @access private
15
+ * @package dompdf
16
+ */
17
+ class Text_Renderer extends Abstract_Renderer {
18
+
19
+ const DECO_THICKNESS = 0.02; // Thickness of underline. Screen: 0.08, print: better less, e.g. 0.04
20
+
21
+ //Tweaking if $base and $descent are not accurate.
22
+ //Check method_exists( $this->_canvas, "get_cpdf" )
23
+ //- For cpdf these can and must stay 0, because font metrics are used directly.
24
+ //- For other renderers, if different values are wanted, separate the parameter sets.
25
+ // But $size and $size-$height seem to be accurate enough
26
+ const UNDERLINE_OFFSET = 0.0; // Relative to bottom of text, as fraction of height.
27
+ const OVERLINE_OFFSET = 0.0; // Relative to top of text
28
+ const LINETHROUGH_OFFSET = 0.0; // Relative to centre of text.
29
+ const DECO_EXTENSION = 0.0; // How far to extend lines past either end, in pt
30
+
31
+ //........................................................................
32
+
33
+ /**
34
+ * @param Text_Frame_Decorator $frame
35
+ */
36
+ function render(Frame $frame) {
37
+ $text = $frame->get_text();
38
+ if ( trim($text) === "" )
39
+ return;
40
+
41
+ $style = $frame->get_style();
42
+ list($x, $y) = $frame->get_position();
43
+ $cb = $frame->get_containing_block();
44
+
45
+ if ( ($ml = $style->margin_left) === "auto" || $ml === "none" )
46
+ $ml = 0;
47
+
48
+ if ( ($pl = $style->padding_left) === "auto" || $pl === "none" )
49
+ $pl = 0;
50
+
51
+ if ( ($bl = $style->border_left_width) === "auto" || $bl === "none" )
52
+ $bl = 0;
53
+
54
+ $x += $style->length_in_pt( array($ml, $pl, $bl), $cb["w"] );
55
+
56
+ $font = $style->font_family;
57
+ $size = $frame_font_size = $style->font_size;
58
+ $height = $style->height;
59
+ $word_spacing = $frame->get_text_spacing() + $style->length_in_pt($style->word_spacing);
60
+ $char_spacing = $style->length_in_pt($style->letter_spacing);
61
+ $width = $style->width;
62
+
63
+ /*$text = str_replace(
64
+ array("{PAGE_NUM}"),
65
+ array($this->_canvas->get_page_number()),
66
+ $text
67
+ );*/
68
+
69
+ $this->_canvas->text($x, $y, $text,
70
+ $font, $size,
71
+ $style->color, $word_spacing, $char_spacing);
72
+
73
+ $line = $frame->get_containing_line();
74
+
75
+ // FIXME Instead of using the tallest frame to position,
76
+ // the decoration, the text should be well placed
77
+ if ( false && $line->tallest_frame ) {
78
+ $base_frame = $line->tallest_frame;
79
+ $style = $base_frame->get_style();
80
+ $size = $style->font_size;
81
+ $height = $line->h * ($size / $style->line_height);
82
+ }
83
+
84
+ $line_thickness = $size * self::DECO_THICKNESS;
85
+ $underline_offset = $size * self::UNDERLINE_OFFSET;
86
+ $overline_offset = $size * self::OVERLINE_OFFSET;
87
+ $linethrough_offset = $size * self::LINETHROUGH_OFFSET;
88
+ $underline_position = -0.08;
89
+
90
+ if ( $this->_canvas instanceof CPDF_Adapter ) {
91
+ $cpdf_font = $this->_canvas->get_cpdf()->fonts[$style->font_family];
92
+
93
+ if (isset($cpdf_font["UnderlinePosition"])) {
94
+ $underline_position = $cpdf_font["UnderlinePosition"]/1000;
95
+ }
96
+
97
+ if (isset($cpdf_font["UnderlineThickness"])) {
98
+ $line_thickness = $size * ($cpdf_font["UnderlineThickness"]/1000);
99
+ }
100
+ }
101
+
102
+ $descent = $size * $underline_position;
103
+ $base = $size;
104
+
105
+ // Handle text decoration:
106
+ // http://www.w3.org/TR/CSS21/text.html#propdef-text-decoration
107
+
108
+ // Draw all applicable text-decorations. Start with the root and work our way down.
109
+ $p = $frame;
110
+ $stack = array();
111
+ while ( $p = $p->get_parent() )
112
+ $stack[] = $p;
113
+
114
+ while ( isset($stack[0]) ) {
115
+ $f = array_pop($stack);
116
+
117
+ if ( ($text_deco = $f->get_style()->text_decoration) === "none" )
118
+ continue;
119
+
120
+ $deco_y = $y; //$line->y;
121
+ $color = $f->get_style()->color;
122
+
123
+ switch ($text_deco) {
124
+
125
+ default:
126
+ continue;
127
+
128
+ case "underline":
129
+ $deco_y += $base - $descent + $underline_offset + $line_thickness/2;
130
+ break;
131
+
132
+ case "overline":
133
+ $deco_y += $overline_offset + $line_thickness/2;
134
+ break;
135
+
136
+ case "line-through":
137
+ $deco_y += $base * 0.7 + $linethrough_offset;
138
+ break;
139
+ }
140
+
141
+ $dx = 0;
142
+ $x1 = $x - self::DECO_EXTENSION;
143
+ $x2 = $x + $width + $dx + self::DECO_EXTENSION;
144
+ $this->_canvas->line($x1, $deco_y, $x2, $deco_y, $color, $line_thickness);
145
+ }
146
+
147
+ if (DEBUG_LAYOUT && DEBUG_LAYOUT_LINES) {
148
+ $text_width = Font_Metrics::get_text_width($text, $font, $frame_font_size);
149
+ $this->_debug_layout(array($x, $y, $text_width+($line->wc-1)*$word_spacing, $frame_font_size), "orange", array(0.5, 0.5));
150
+ }
151
+ }
152
+ }
dompdf/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php header("Location: www/"); ?>
dompdf/lib/_notes/dwsync.xml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8" ?>
2
+ <dwsync>
3
+ <file name="class.pdf.php" server="blueliquiddesigns.com.au" local="130050839400000000" remote="130050839400000000" />
4
+ </dwsync>
dompdf/lib/class.pdf.php ADDED
@@ -0,0 +1,4590 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * A PHP class to provide the basic functionality to create a pdf document without
4
+ * any requirement for additional modules.
5
+ *
6
+ * Extended by Orion Richardson to support Unicode / UTF-8 characters using
7
+ * TCPDF and others as a guide.
8
+ *
9
+ * @author Wayne Munro <pdf@ros.co.nz>
10
+ * @author Orion Richardson <orionr@yahoo.com>
11
+ * @author Helmut Tischer <htischer@weihenstephan.org>
12
+ * @author Ryan H. Masten <ryan.masten@gmail.com>
13
+ * @author Brian Sweeney <eclecticgeek@gmail.com>
14
+ * @author Fabien Ménager <fabien.menager@gmail.com>
15
+ * @license Public Domain http://creativecommons.org/licenses/publicdomain/
16
+ * @package Cpdf
17
+ */
18
+ class Cpdf {
19
+
20
+ /**
21
+ * @var integer The current number of pdf objects in the document
22
+ */
23
+ public $numObj = 0;
24
+
25
+ /**
26
+ * @var array This array contains all of the pdf objects, ready for final assembly
27
+ */
28
+ public $objects = array();
29
+
30
+ /**
31
+ * @var integer The objectId (number within the objects array) of the document catalog
32
+ */
33
+ public $catalogId;
34
+
35
+ /**
36
+ * @var array Array carrying information about the fonts that the system currently knows about
37
+ * Used to ensure that a font is not loaded twice, among other things
38
+ */
39
+ public $fonts = array();
40
+
41
+ /**
42
+ * @var string The default font metrics file to use if no other font has been loaded.
43
+ * The path to the directory containing the font metrics should be included
44
+ */
45
+ public $defaultFont = './fonts/Helvetica.afm';
46
+
47
+ /**
48
+ * @string A record of the current font
49
+ */
50
+ public $currentFont = '';
51
+
52
+ /**
53
+ * @var string The current base font
54
+ */
55
+ public $currentBaseFont = '';
56
+
57
+ /**
58
+ * @var integer The number of the current font within the font array
59
+ */
60
+ public $currentFontNum = 0;
61
+
62
+ /**
63
+ * @var integer
64
+ */
65
+ public $currentNode;
66
+
67
+ /**
68
+ * @var integer Object number of the current page
69
+ */
70
+ public $currentPage;
71
+
72
+ /**
73
+ * @var integer Object number of the currently active contents block
74
+ */
75
+ public $currentContents;
76
+
77
+ /**
78
+ * @var integer Number of fonts within the system
79
+ */
80
+ public $numFonts = 0;
81
+
82
+ /**
83
+ * @var integer Number of graphic state resources used
84
+ */
85
+ private $numStates = 0;
86
+
87
+ /**
88
+ * @var array Current colour for fill operations, defaults to inactive value,
89
+ * all three components should be between 0 and 1 inclusive when active
90
+ */
91
+ public $currentColour = null;
92
+
93
+ /**
94
+ * @var array Current colour for stroke operations (lines etc.)
95
+ */
96
+ public $currentStrokeColour = null;
97
+
98
+ /**
99
+ * @var string Current style that lines are drawn in
100
+ */
101
+ public $currentLineStyle = '';
102
+
103
+ /**
104
+ * @var array Current line transparency (partial graphics state)
105
+ */
106
+ public $currentLineTransparency = array("mode" => "Normal", "opacity" => 1.0);
107
+
108
+ /**
109
+ * array Current fill transparency (partial graphics state)
110
+ */
111
+ public $currentFillTransparency = array("mode" => "Normal", "opacity" => 1.0);
112
+
113
+ /**
114
+ * @var array An array which is used to save the state of the document, mainly the colours and styles
115
+ * it is used to temporarily change to another state, the change back to what it was before
116
+ */
117
+ public $stateStack = array();
118
+
119
+ /**
120
+ * @var integer Number of elements within the state stack
121
+ */
122
+ public $nStateStack = 0;
123
+
124
+ /**
125
+ * @var integer Number of page objects within the document
126
+ */
127
+ public $numPages = 0;
128
+
129
+ /**
130
+ * @var array Object Id storage stack
131
+ */
132
+ public $stack = array();
133
+
134
+ /**
135
+ * @var integer Number of elements within the object Id storage stack
136
+ */
137
+ public $nStack = 0;
138
+
139
+ /**
140
+ * an array which contains information about the objects which are not firmly attached to pages
141
+ * these have been added with the addObject function
142
+ */
143
+ public $looseObjects = array();
144
+
145
+ /**
146
+ * array contains infomation about how the loose objects are to be added to the document
147
+ */
148
+ public $addLooseObjects = array();
149
+
150
+ /**
151
+ * @var integer The objectId of the information object for the document
152
+ * this contains authorship, title etc.
153
+ */
154
+ public $infoObject = 0;
155
+
156
+ /**
157
+ * @var integer Number of images being tracked within the document
158
+ */
159
+ public $numImages = 0;
160
+
161
+ /**
162
+ * @var array An array containing options about the document
163
+ * it defaults to turning on the compression of the objects
164
+ */
165
+ public $options = array('compression'=>true);
166
+
167
+ /**
168
+ * @var integer The objectId of the first page of the document
169
+ */
170
+ public $firstPageId;
171
+
172
+ /**
173
+ * @var float Used to track the last used value of the inter-word spacing, this is so that it is known
174
+ * when the spacing is changed.
175
+ */
176
+ public $wordSpaceAdjust = 0;
177
+
178
+ /**
179
+ * @var float Used to track the last used value of the inter-letter spacing, this is so that it is known
180
+ * when the spacing is changed.
181
+ */
182
+ public $charSpaceAdjust = 0;
183
+
184
+ /**
185
+ * @var integer The object Id of the procset object
186
+ */
187
+ public $procsetObjectId;
188
+
189
+ /**
190
+ * @var array Store the information about the relationship between font families
191
+ * this used so that the code knows which font is the bold version of another font, etc.
192
+ * the value of this array is initialised in the constuctor function.
193
+ */
194
+ public $fontFamilies = array();
195
+
196
+ /**
197
+ * @var string Folder for php serialized formats of font metrics files.
198
+ * If empty string, use same folder as original metrics files.
199
+ * This can be passed in from class creator.
200
+ * If this folder does not exist or is not writable, Cpdf will be **much** slower.
201
+ * Because of potential trouble with php safe mode, folder cannot be created at runtime.
202
+ */
203
+ public $fontcache = '';
204
+
205
+ /**
206
+ * @var integer The version of the font metrics cache file.
207
+ * This value must be manually incremented whenever the internal font data structure is modified.
208
+ */
209
+ public $fontcacheVersion = 6;
210
+
211
+ /**
212
+ * @var string Temporary folder.
213
+ * If empty string, will attempty system tmp folder.
214
+ * This can be passed in from class creator.
215
+ * Only used for conversion of gd images to jpeg images.
216
+ */
217
+ public $tmp = '';
218
+
219
+ /**
220
+ * @var string Track if the current font is bolded or italicised
221
+ */
222
+ public $currentTextState = '';
223
+
224
+ /**
225
+ * @var string Messages are stored here during processing, these can be selected afterwards to give some useful debug information
226
+ */
227
+ public $messages = '';
228
+
229
+ /**
230
+ * @var string The ancryption array for the document encryption is stored here
231
+ */
232
+ public $arc4 = '';
233
+
234
+ /**
235
+ * @var integer The object Id of the encryption information
236
+ */
237
+ public $arc4_objnum = 0;
238
+
239
+ /**
240
+ * @var string The file identifier, used to uniquely identify a pdf document
241
+ */
242
+ public $fileIdentifier = '';
243
+
244
+ /**
245
+ * @var boolean A flag to say if a document is to be encrypted or not
246
+ */
247
+ public $encrypted = false;
248
+
249
+ /**
250
+ * @var string The encryption key for the encryption of all the document content (structure is not encrypted)
251
+ */
252
+ public $encryptionKey = '';
253
+
254
+ /**
255
+ * @var array Array which forms a stack to keep track of nested callback functions
256
+ */
257
+ public $callback = array();
258
+
259
+ /**
260
+ * @var integer The number of callback functions in the callback array
261
+ */
262
+ public $nCallback = 0;
263
+
264
+ /**
265
+ * @var array Store label->id pairs for named destinations, these will be used to replace internal links
266
+ * done this way so that destinations can be defined after the location that links to them
267
+ */
268
+ public $destinations = array();
269
+
270
+ /**
271
+ * @var array Store the stack for the transaction commands, each item in here is a record of the values of all the
272
+ * publiciables within the class, so that the user can rollback at will (from each 'start' command)
273
+ * note that this includes the objects array, so these can be large.
274
+ */
275
+ public $checkpoint = '';
276
+
277
+ /**
278
+ * @var array Table of Image origin filenames and image labels which were already added with o_image().
279
+ * Allows to merge identical images
280
+ */
281
+ public $imagelist = array();
282
+
283
+ /**
284
+ * @var boolean Whether the text passed in should be treated as Unicode or just local character set.
285
+ */
286
+ public $isUnicode = false;
287
+
288
+ /**
289
+ * @var string the JavaScript code of the document
290
+ */
291
+ public $javascript = '';
292
+
293
+ /**
294
+ * @var boolean whether the compression is possible
295
+ */
296
+ protected $compressionReady = false;
297
+
298
+ /**
299
+ * @var array Current page size
300
+ */
301
+ protected $currentPageSize = array("width" => 0, "height" => 0);
302
+
303
+ /**
304
+ * @var array All the chars that will be required in the font subsets
305
+ */
306
+ protected $stringSubsets = array();
307
+
308
+ /**
309
+ * @var string The target internal encoding
310
+ */
311
+ static protected $targetEncoding = 'iso-8859-1';
312
+
313
+ /**
314
+ * @var array The list of the core fonts
315
+ */
316
+ static protected $coreFonts = array(
317
+ 'courier', 'courier-bold', 'courier-oblique', 'courier-boldoblique',
318
+ 'helvetica', 'helvetica-bold', 'helvetica-oblique', 'helvetica-boldoblique',
319
+ 'times-roman', 'times-bold', 'times-italic', 'times-bolditalic',
320
+ 'symbol', 'zapfdingbats'
321
+ );
322
+
323
+ /**
324
+ * Class constructor
325
+ * This will start a new document
326
+ *
327
+ * @param array $pageSize Array of 4 numbers, defining the bottom left and upper right corner of the page. first two are normally zero.
328
+ * @param boolean $isUnicode Whether text will be treated as Unicode or not.
329
+ * @param string $fontcache The font cache folder
330
+ * @param string $tmp The temporary folder
331
+ */
332
+ function __construct($pageSize = array(0, 0, 612, 792), $isUnicode = false, $fontcache = '', $tmp = '') {
333
+ $this->isUnicode = $isUnicode;
334
+ $this->fontcache = $fontcache;
335
+ $this->tmp = $tmp;
336
+ $this->newDocument($pageSize);
337
+
338
+ $this->compressionReady = function_exists('gzcompress');
339
+
340
+ if ( in_array('Windows-1252', mb_list_encodings()) ) {
341
+ self::$targetEncoding = 'Windows-1252';
342
+ }
343
+
344
+ // also initialize the font families that are known about already
345
+ $this->setFontFamily('init');
346
+ // $this->fileIdentifier = md5('xxxxxxxx'.time());
347
+ }
348
+
349
+ /**
350
+ * Document object methods (internal use only)
351
+ *
352
+ * There is about one object method for each type of object in the pdf document
353
+ * Each function has the same call list ($id,$action,$options).
354
+ * $id = the object ID of the object, or what it is to be if it is being created
355
+ * $action = a string specifying the action to be performed, though ALL must support:
356
+ * 'new' - create the object with the id $id
357
+ * 'out' - produce the output for the pdf object
358
+ * $options = optional, a string or array containing the various parameters for the object
359
+ *
360
+ * These, in conjunction with the output function are the ONLY way for output to be produced
361
+ * within the pdf 'file'.
362
+ */
363
+
364
+ /**
365
+ * Destination object, used to specify the location for the user to jump to, presently on opening
366
+ */
367
+ protected function o_destination($id, $action, $options = '') {
368
+ if ($action !== 'new') {
369
+ $o = &$this->objects[$id];
370
+ }
371
+
372
+ switch ($action) {
373
+ case 'new':
374
+ $this->objects[$id] = array('t'=>'destination', 'info'=>array());
375
+ $tmp = '';
376
+ switch ($options['type']) {
377
+ case 'XYZ':
378
+ case 'FitR':
379
+ $tmp = ' '.$options['p3'].$tmp;
380
+ case 'FitH':
381
+ case 'FitV':
382
+ case 'FitBH':
383
+ case 'FitBV':
384
+ $tmp = ' '.$options['p1'].' '.$options['p2'].$tmp;
385
+ case 'Fit':
386
+ case 'FitB':
387
+ $tmp = $options['type'].$tmp;
388
+ $this->objects[$id]['info']['string'] = $tmp;
389
+ $this->objects[$id]['info']['page'] = $options['page'];
390
+ }
391
+ break;
392
+
393
+ case 'out':
394
+ $tmp = $o['info'];
395
+ $res = "\n$id 0 obj\n".'['.$tmp['page'].' 0 R /'.$tmp['string']."]\nendobj";
396
+ return $res;
397
+ }
398
+ }
399
+
400
+ /**
401
+ * set the viewer preferences
402
+ */
403
+ protected function o_viewerPreferences($id, $action, $options = '') {
404
+ if ($action !== 'new') {
405
+ $o = & $this->objects[$id];
406
+ }
407
+
408
+ switch ($action) {
409
+ case 'new':
410
+ $this->objects[$id] = array('t'=>'viewerPreferences', 'info'=>array());
411
+ break;
412
+
413
+ case 'add':
414
+ foreach ($options as $k=>$v) {
415
+ switch ($k) {
416
+ case 'HideToolbar':
417
+ case 'HideMenubar':
418
+ case 'HideWindowUI':
419
+ case 'FitWindow':
420
+ case 'CenterWindow':
421
+ case 'NonFullScreenPageMode':
422
+ case 'Direction':
423
+ $o['info'][$k] = $v;
424
+ break;
425
+ }
426
+ }
427
+ break;
428
+
429
+ case 'out':
430
+ $res = "\n$id 0 obj\n<< ";
431
+ foreach ($o['info'] as $k=>$v) {
432
+ $res.= "\n/$k $v";
433
+ }
434
+ $res.= "\n>>\n";
435
+ return $res;
436
+ }
437
+ }
438
+
439
+ /**
440
+ * define the document catalog, the overall controller for the document
441
+ */
442
+ protected function o_catalog($id, $action, $options = '') {
443
+ if ($action !== 'new') {
444
+ $o = & $this->objects[$id];
445
+ }
446
+
447
+ switch ($action) {
448
+ case 'new':
449
+ $this->objects[$id] = array('t'=>'catalog', 'info'=>array());
450
+ $this->catalogId = $id;
451
+ break;
452
+
453
+ case 'outlines':
454
+ case 'pages':
455
+ case 'openHere':
456
+ case 'javascript':
457
+ $o['info'][$action] = $options;
458
+ break;
459
+
460
+ case 'viewerPreferences':
461
+ if (!isset($o['info']['viewerPreferences'])) {
462
+ $this->numObj++;
463
+ $this->o_viewerPreferences($this->numObj, 'new');
464
+ $o['info']['viewerPreferences'] = $this->numObj;
465
+ }
466
+
467
+ $vp = $o['info']['viewerPreferences'];
468
+ $this->o_viewerPreferences($vp, 'add', $options);
469
+
470
+ break;
471
+
472
+ case 'out':
473
+ $res = "\n$id 0 obj\n<< /Type /Catalog";
474
+
475
+ foreach ($o['info'] as $k=>$v) {
476
+ switch ($k) {
477
+ case 'outlines':
478
+ $res.= "\n/Outlines $v 0 R";
479
+ break;
480
+
481
+ case 'pages':
482
+ $res.= "\n/Pages $v 0 R";
483
+ break;
484
+
485
+ case 'viewerPreferences':
486
+ $res.= "\n/ViewerPreferences $v 0 R";
487
+ break;
488
+
489
+ case 'openHere':
490
+ $res.= "\n/OpenAction $v 0 R";
491
+ break;
492
+
493
+ case 'javascript':
494
+ $res.= "\n/Names <</JavaScript $v 0 R>>";
495
+ break;
496
+ }
497
+ }
498
+
499
+ $res.= " >>\nendobj";
500
+ return $res;
501
+ }
502
+ }
503
+
504
+ /**
505
+ * object which is a parent to the pages in the document
506
+ */
507
+ protected function o_pages($id, $action, $options = '') {
508
+ if ($action !== 'new') {
509
+ $o = & $this->objects[$id];
510
+ }
511
+
512
+ switch ($action) {
513
+ case 'new':
514
+ $this->objects[$id] = array('t'=>'pages', 'info'=>array());
515
+ $this->o_catalog($this->catalogId, 'pages', $id);
516
+ break;
517
+
518
+ case 'page':
519
+ if (!is_array($options)) {
520
+ // then it will just be the id of the new page
521
+ $o['info']['pages'][] = $options;
522
+ }
523
+ else {
524
+ // then it should be an array having 'id','rid','pos', where rid=the page to which this one will be placed relative
525
+ // and pos is either 'before' or 'after', saying where this page will fit.
526
+ if (isset($options['id']) && isset($options['rid']) && isset($options['pos'])) {
527
+ $i = array_search($options['rid'], $o['info']['pages']);
528
+ if (isset($o['info']['pages'][$i]) && $o['info']['pages'][$i] == $options['rid']) {
529
+
530
+ // then there is a match
531
+ // make a space
532
+ switch ($options['pos']) {
533
+ case 'before':
534
+ $k = $i;
535
+ break;
536
+
537
+ case 'after':
538
+ $k = $i+1;
539
+ break;
540
+
541
+ default:
542
+ $k = -1;
543
+ break;
544
+ }
545
+
546
+ if ($k >= 0) {
547
+ for ($j = count($o['info']['pages']) -1;$j >= $k;$j--) {
548
+ $o['info']['pages'][$j+1] = $o['info']['pages'][$j];
549
+ }
550
+
551
+ $o['info']['pages'][$k] = $options['id'];
552
+ }
553
+ }
554
+ }
555
+ }
556
+ break;
557
+
558
+ case 'procset':
559
+ $o['info']['procset'] = $options;
560
+ break;
561
+
562
+ case 'mediaBox':
563
+ $o['info']['mediaBox'] = $options;
564
+ // which should be an array of 4 numbers
565
+ $this->currentPageSize = array('width' => $options[2], 'height' => $options[3]);
566
+ break;
567
+
568
+ case 'font':
569
+ $o['info']['fonts'][] = array('objNum'=>$options['objNum'], 'fontNum'=>$options['fontNum']);
570
+ break;
571
+
572
+ case 'extGState':
573
+ $o['info']['extGStates'][] = array('objNum' => $options['objNum'], 'stateNum' => $options['stateNum']);
574
+ break;
575
+
576
+ case 'xObject':
577
+ $o['info']['xObjects'][] = array('objNum'=>$options['objNum'], 'label'=>$options['label']);
578
+ break;
579
+
580
+ case 'out':
581
+ if (count($o['info']['pages'])) {
582
+ $res = "\n$id 0 obj\n<< /Type /Pages\n/Kids [";
583
+ foreach ($o['info']['pages'] as $v) {
584
+ $res.= "$v 0 R\n";
585
+ }
586
+
587
+ $res.= "]\n/Count ".count($this->objects[$id]['info']['pages']);
588
+
589
+ if ( (isset($o['info']['fonts']) && count($o['info']['fonts'])) ||
590
+ isset($o['info']['procset']) ||
591
+ (isset($o['info']['extGStates']) && count($o['info']['extGStates']))) {
592
+ $res.= "\n/Resources <<";
593
+
594
+ if (isset($o['info']['procset'])) {
595
+ $res.= "\n/ProcSet ".$o['info']['procset']." 0 R";
596
+ }
597
+
598
+ if (isset($o['info']['fonts']) && count($o['info']['fonts'])) {
599
+ $res.= "\n/Font << ";
600
+ foreach ($o['info']['fonts'] as $finfo) {
601
+ $res.= "\n/F".$finfo['fontNum']." ".$finfo['objNum']." 0 R";
602
+ }
603
+ $res.= "\n>>";
604
+ }
605
+
606
+ if (isset($o['info']['xObjects']) && count($o['info']['xObjects'])) {
607
+ $res.= "\n/XObject << ";
608
+ foreach ($o['info']['xObjects'] as $finfo) {
609
+ $res.= "\n/".$finfo['label']." ".$finfo['objNum']." 0 R";
610
+ }
611
+ $res.= "\n>>";
612
+ }
613
+
614
+ if ( isset($o['info']['extGStates']) && count($o['info']['extGStates'])) {
615
+ $res.= "\n/ExtGState << ";
616
+ foreach ($o['info']['extGStates'] as $gstate) {
617
+ $res.= "\n/GS" . $gstate['stateNum'] . " " . $gstate['objNum'] . " 0 R";
618
+ }
619
+ $res.= "\n>>";
620
+ }
621
+
622
+ $res.= "\n>>";
623
+ if (isset($o['info']['mediaBox'])) {
624
+ $tmp = $o['info']['mediaBox'];
625
+ $res.= "\n/MediaBox [".sprintf('%.3F %.3F %.3F %.3F', $tmp[0], $tmp[1], $tmp[2], $tmp[3]) .']';
626
+ }
627
+ }
628
+
629
+ $res.= "\n >>\nendobj";
630
+ }
631
+ else {
632
+ $res = "\n$id 0 obj\n<< /Type /Pages\n/Count 0\n>>\nendobj";
633
+ }
634
+
635
+ return $res;
636
+ }
637
+ }
638
+
639
+ /**
640
+ * define the outlines in the doc, empty for now
641
+ */
642
+ protected function o_outlines($id, $action, $options = '') {
643
+ if ($action !== 'new') {
644
+ $o = &$this->objects[$id];
645
+ }
646
+
647
+ switch ($action) {
648
+ case 'new':
649
+ $this->objects[$id] = array('t'=>'outlines', 'info'=>array('outlines'=>array()));
650
+ $this->o_catalog($this->catalogId, 'outlines', $id);
651
+ break;
652
+
653
+ case 'outline':
654
+ $o['info']['outlines'][] = $options;
655
+ break;
656
+
657
+ case 'out':
658
+ if (count($o['info']['outlines'])) {
659
+ $res = "\n$id 0 obj\n<< /Type /Outlines /Kids [";
660
+ foreach ($o['info']['outlines'] as $v) {
661
+ $res.= "$v 0 R ";
662
+ }
663
+
664
+ $res.= "] /Count ".count($o['info']['outlines']) ." >>\nendobj";
665
+ } else {
666
+ $res = "\n$id 0 obj\n<< /Type /Outlines /Count 0 >>\nendobj";
667
+ }
668
+
669
+ return $res;
670
+ }
671
+ }
672
+
673
+ /**
674
+ * an object to hold the font description
675
+ */
676
+ protected function o_font($id, $action, $options = '') {
677
+ if ($action !== 'new') {
678
+ $o = &$this->objects[$id];
679
+ }
680
+
681
+ switch ($action) {
682
+ case 'new':
683
+ $this->objects[$id] = array('t' => 'font', 'info' => array('name' => $options['name'], 'fontFileName' => $options['fontFileName'], 'SubType' => 'Type1'));
684
+ $fontNum = $this->numFonts;
685
+ $this->objects[$id]['info']['fontNum'] = $fontNum;
686
+
687
+ // deal with the encoding and the differences
688
+ if (isset($options['differences'])) {
689
+ // then we'll need an encoding dictionary
690
+ $this->numObj++;
691
+ $this->o_fontEncoding($this->numObj, 'new', $options);
692
+ $this->objects[$id]['info']['encodingDictionary'] = $this->numObj;
693
+ }
694
+ else if (isset($options['encoding'])) {
695
+ // we can specify encoding here
696
+ switch ($options['encoding']) {
697
+ case 'WinAnsiEncoding':
698
+ case 'MacRomanEncoding':
699
+ case 'MacExpertEncoding':
700
+ $this->objects[$id]['info']['encoding'] = $options['encoding'];
701
+ break;
702
+
703
+ case 'none':
704
+ break;
705
+
706
+ default:
707
+ $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
708
+ break;
709
+ }
710
+ }
711
+ else {
712
+ $this->objects[$id]['info']['encoding'] = 'WinAnsiEncoding';
713
+ }
714
+
715
+ if ($this->fonts[$options['fontFileName']]['isUnicode']) {
716
+ // For Unicode fonts, we need to incorporate font data into
717
+ // sub-sections that are linked from the primary font section.
718
+ // Look at o_fontGIDtoCID and o_fontDescendentCID functions
719
+ // for more informaiton.
720
+ //
721
+ // All of this code is adapted from the excellent changes made to
722
+ // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
723
+
724
+ $toUnicodeId = ++$this->numObj;
725
+ $this->o_contents($toUnicodeId, 'new', 'raw');
726
+ $this->objects[$id]['info']['toUnicode'] = $toUnicodeId;
727
+
728
+ $stream = <<<EOT
729
+ /CIDInit /ProcSet findresource begin
730
+ 12 dict begin
731
+ begincmap
732
+ /CIDSystemInfo
733
+ <</Registry (Adobe)
734
+ /Ordering (UCS)
735
+ /Supplement 0
736
+ >> def
737
+ /CMapName /Adobe-Identity-UCS def
738
+ /CMapType 2 def
739
+ 1 begincodespacerange
740
+ <0000> <FFFF>
741
+ endcodespacerange
742
+ 1 beginbfrange
743
+ <0000> <FFFF> <0000>
744
+ endbfrange
745
+ endcmap
746
+ CMapName currentdict /CMap defineresource pop
747
+ end
748
+ end
749
+ EOT;
750
+
751
+ $res = "<</Length " . mb_strlen($stream, '8bit') . " >>\n";
752
+ $res .= "stream\n" . $stream . "endstream";
753
+
754
+ $this->objects[$toUnicodeId]['c'] = $res;
755
+
756
+ $cidFontId = ++$this->numObj;
757
+ $this->o_fontDescendentCID($cidFontId, 'new', $options);
758
+ $this->objects[$id]['info']['cidFont'] = $cidFontId;
759
+ }
760
+
761
+ // also tell the pages node about the new font
762
+ $this->o_pages($this->currentNode, 'font', array('fontNum' => $fontNum, 'objNum' => $id));
763
+ break;
764
+
765
+ case 'add':
766
+ foreach ($options as $k => $v) {
767
+ switch ($k) {
768
+ case 'BaseFont':
769
+ $o['info']['name'] = $v;
770
+ break;
771
+ case 'FirstChar':
772
+ case 'LastChar':
773
+ case 'Widths':
774
+ case 'FontDescriptor':
775
+ case 'SubType':
776
+ $this->addMessage('o_font '.$k." : ".$v);
777
+ $o['info'][$k] = $v;
778
+ break;
779
+ }
780
+ }
781
+
782
+ // pass values down to descendent font
783
+ if (isset($o['info']['cidFont'])) {
784
+ $this->o_fontDescendentCID($o['info']['cidFont'], 'add', $options);
785
+ }
786
+ break;
787
+
788
+ case 'out':
789
+ if ($this->fonts[$this->objects[$id]['info']['fontFileName']]['isUnicode']) {
790
+ // For Unicode fonts, we need to incorporate font data into
791
+ // sub-sections that are linked from the primary font section.
792
+ // Look at o_fontGIDtoCID and o_fontDescendentCID functions
793
+ // for more informaiton.
794
+ //
795
+ // All of this code is adapted from the excellent changes made to
796
+ // transform FPDF to TCPDF (http://tcpdf.sourceforge.net/)
797
+
798
+ $res = "\n$id 0 obj\n<</Type /Font\n/Subtype /Type0\n";
799
+ $res.= "/BaseFont /".$o['info']['name']."\n";
800
+
801
+ // The horizontal identity mapping for 2-byte CIDs; may be used
802
+ // with CIDFonts using any Registry, Ordering, and Supplement values.
803
+ $res.= "/Encoding /Identity-H\n";
804
+ $res.= "/DescendantFonts [".$o['info']['cidFont']." 0 R]\n";
805
+ $res.= "/ToUnicode ".$o['info']['toUnicode']." 0 R\n";
806
+ $res.= ">>\n";
807
+ $res.= "endobj";
808
+ } else {
809
+ $res = "\n$id 0 obj\n<< /Type /Font\n/Subtype /".$o['info']['SubType']."\n";
810
+ $res.= "/Name /F".$o['info']['fontNum']."\n";
811
+ $res.= "/BaseFont /".$o['info']['name']."\n";
812
+
813
+ if (isset($o['info']['encodingDictionary'])) {
814
+ // then place a reference to the dictionary
815
+ $res.= "/Encoding ".$o['info']['encodingDictionary']." 0 R\n";
816
+ } else if (isset($o['info']['encoding'])) {
817
+ // use the specified encoding
818
+ $res.= "/Encoding /".$o['info']['encoding']."\n";
819
+ }
820
+
821
+ if (isset($o['info']['FirstChar'])) {
822
+ $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
823
+ }
824
+
825
+ if (isset($o['info']['LastChar'])) {
826
+ $res.= "/LastChar ".$o['info']['LastChar']."\n";
827
+ }
828
+
829
+ if (isset($o['info']['Widths'])) {
830
+ $res.= "/Widths ".$o['info']['Widths']." 0 R\n";
831
+ }
832
+
833
+ if (isset($o['info']['FontDescriptor'])) {
834
+ $res.= "/FontDescriptor ".$o['info']['FontDescriptor']." 0 R\n";
835
+ }
836
+
837
+ $res.= ">>\n";
838
+ $res.= "endobj";
839
+ }
840
+
841
+ return $res;
842
+ }
843
+ }
844
+
845
+ /**
846
+ * a font descriptor, needed for including additional fonts
847
+ */
848
+ protected function o_fontDescriptor($id, $action, $options = '') {
849
+ if ($action !== 'new') {
850
+ $o = & $this->objects[$id];
851
+ }
852
+
853
+ switch ($action) {
854
+ case 'new':
855
+ $this->objects[$id] = array('t'=>'fontDescriptor', 'info'=>$options);
856
+ break;
857
+
858
+ case 'out':
859
+ $res = "\n$id 0 obj\n<< /Type /FontDescriptor\n";
860
+ foreach ($o['info'] as $label => $value) {
861
+ switch ($label) {
862
+ case 'Ascent':
863
+ case 'CapHeight':
864
+ case 'Descent':
865
+ case 'Flags':
866
+ case 'ItalicAngle':
867
+ case 'StemV':
868
+ case 'AvgWidth':
869
+ case 'Leading':
870
+ case 'MaxWidth':
871
+ case 'MissingWidth':
872
+ case 'StemH':
873
+ case 'XHeight':
874
+ case 'CharSet':
875
+ if (mb_strlen($value, '8bit')) {
876
+ $res.= "/$label $value\n";
877
+ }
878
+
879
+ break;
880
+ case 'FontFile':
881
+ case 'FontFile2':
882
+ case 'FontFile3':
883
+ $res.= "/$label $value 0 R\n";
884
+ break;
885
+
886
+ case 'FontBBox':
887
+ $res.= "/$label [$value[0] $value[1] $value[2] $value[3]]\n";
888
+ break;
889
+
890
+ case 'FontName':
891
+ $res.= "/$label /$value\n";
892
+ break;
893
+ }
894
+ }
895
+
896
+ $res.= ">>\nendobj";
897
+
898
+ return $res;
899
+ }
900
+ }
901
+
902
+ /**
903
+ * the font encoding
904
+ */
905
+ protected function o_fontEncoding($id, $action, $options = '') {
906
+ if ($action !== 'new') {
907
+ $o = & $this->objects[$id];
908
+ }
909
+
910
+ switch ($action) {
911
+ case 'new':
912
+ // the options array should contain 'differences' and maybe 'encoding'
913
+ $this->objects[$id] = array('t'=>'fontEncoding', 'info'=>$options);
914
+ break;
915
+
916
+ case 'out':
917
+ $res = "\n$id 0 obj\n<< /Type /Encoding\n";
918
+ if (!isset($o['info']['encoding'])) {
919
+ $o['info']['encoding'] = 'WinAnsiEncoding';
920
+ }
921
+
922
+ if ($o['info']['encoding'] !== 'none') {
923
+ $res.= "/BaseEncoding /".$o['info']['encoding']."\n";
924
+ }
925
+
926
+ $res.= "/Differences \n[";
927
+
928
+ $onum = -100;
929
+
930
+ foreach ($o['info']['differences'] as $num=>$label) {
931
+ if ($num != $onum+1) {
932
+ // we cannot make use of consecutive numbering
933
+ $res.= "\n$num /$label";
934
+ } else {
935
+ $res.= " /$label";
936
+ }
937
+
938
+ $onum = $num;
939
+ }
940
+
941
+ $res.= "\n]\n>>\nendobj";
942
+ return $res;
943
+ }
944
+ }
945
+
946
+ /**
947
+ * a descendent cid font, needed for unicode fonts
948
+ */
949
+ protected function o_fontDescendentCID($id, $action, $options = '') {
950
+ if ($action !== 'new') {
951
+ $o = & $this->objects[$id];
952
+ }
953
+
954
+ switch ($action) {
955
+ case 'new':
956
+ $this->objects[$id] = array('t'=>'fontDescendentCID', 'info'=>$options);
957
+
958
+ // we need a CID system info section
959
+ $cidSystemInfoId = ++$this->numObj;
960
+ $this->o_contents($cidSystemInfoId, 'new', 'raw');
961
+ $this->objects[$id]['info']['cidSystemInfo'] = $cidSystemInfoId;
962
+ $res= "<</Registry (Adobe)\n"; // A string identifying an issuer of character collections
963
+ $res.= "/Ordering (UCS)\n"; // A string that uniquely names a character collection issued by a specific registry
964
+ $res.= "/Supplement 0\n"; // The supplement number of the character collection.
965
+ $res.= ">>";
966
+ $this->objects[$cidSystemInfoId]['c'] = $res;
967
+
968
+ // and a CID to GID map
969
+ $cidToGidMapId = ++$this->numObj;
970
+ $this->o_fontGIDtoCIDMap($cidToGidMapId, 'new', $options);
971
+ $this->objects[$id]['info']['cidToGidMap'] = $cidToGidMapId;
972
+ break;
973
+
974
+ case 'add':
975
+ foreach ($options as $k => $v) {
976
+ switch ($k) {
977
+ case 'BaseFont':
978
+ $o['info']['name'] = $v;
979
+ break;
980
+
981
+ case 'FirstChar':
982
+ case 'LastChar':
983
+ case 'MissingWidth':
984
+ case 'FontDescriptor':
985
+ case 'SubType':
986
+ $this->addMessage("o_fontDescendentCID $k : $v");
987
+ $o['info'][$k] = $v;
988
+ break;
989
+ }
990
+ }
991
+
992
+ // pass values down to cid to gid map
993
+ $this->o_fontGIDtoCIDMap($o['info']['cidToGidMap'], 'add', $options);
994
+ break;
995
+
996
+ case 'out':
997
+ $res = "\n$id 0 obj\n";
998
+ $res.= "<</Type /Font\n";
999
+ $res.= "/Subtype /CIDFontType2\n";
1000
+ $res.= "/BaseFont /".$o['info']['name']."\n";
1001
+ $res.= "/CIDSystemInfo ".$o['info']['cidSystemInfo']." 0 R\n";
1002
+ // if (isset($o['info']['FirstChar'])) {
1003
+ // $res.= "/FirstChar ".$o['info']['FirstChar']."\n";
1004
+ // }
1005
+
1006
+ // if (isset($o['info']['LastChar'])) {
1007
+ // $res.= "/LastChar ".$o['info']['LastChar']."\n";
1008
+ // }
1009
+ if (isset($o['info']['FontDescriptor'])) {
1010
+ $res.= "/FontDescriptor ".$o['info']['FontDescriptor']." 0 R\n";
1011
+ }
1012
+
1013
+ if (isset($o['info']['MissingWidth'])) {
1014
+ $res.= "/DW ".$o['info']['MissingWidth']."\n";
1015
+ }
1016
+
1017
+ if (isset($o['info']['fontFileName']) && isset($this->fonts[$o['info']['fontFileName']]['CIDWidths'])) {
1018
+ $cid_widths = &$this->fonts[$o['info']['fontFileName']]['CIDWidths'];
1019
+ $w = '';
1020
+ foreach ($cid_widths as $cid => $width) {
1021
+ $w .= "$cid [$width] ";
1022
+ }
1023
+ $res.= "/W [$w]\n";
1024
+ }
1025
+
1026
+ $res.= "/CIDToGIDMap ".$o['info']['cidToGidMap']." 0 R\n";
1027
+ $res.= ">>\n";
1028
+ $res.= "endobj";
1029
+
1030
+ return $res;
1031
+ }
1032
+ }
1033
+
1034
+ /**
1035
+ * a font glyph to character map, needed for unicode fonts
1036
+ */
1037
+ protected function o_fontGIDtoCIDMap($id, $action, $options = '') {
1038
+ if ($action !== 'new') {
1039
+ $o = & $this->objects[$id];
1040
+ }
1041
+
1042
+ switch ($action) {
1043
+ case 'new':
1044
+ $this->objects[$id] = array('t'=>'fontGIDtoCIDMap', 'info'=>$options);
1045
+ break;
1046
+
1047
+ case 'out':
1048
+ $res = "\n$id 0 obj\n";
1049
+ $fontFileName = $o['info']['fontFileName'];
1050
+ $tmp = $this->fonts[$fontFileName]['CIDtoGID'] = base64_decode($this->fonts[$fontFileName]['CIDtoGID']);
1051
+
1052
+ $compressed = isset($this->fonts[$fontFileName]['CIDtoGID_Compressed']) &&
1053
+ $this->fonts[$fontFileName]['CIDtoGID_Compressed'];
1054
+
1055
+ if (!$compressed && isset($o['raw'])) {
1056
+ $res.= $tmp;
1057
+ } else {
1058
+ $res.= "<<";
1059
+
1060
+ if (!$compressed && $this->compressionReady && $this->options['compression']) {
1061
+ // then implement ZLIB based compression on this content stream
1062
+ $compressed = true;
1063
+ $tmp = gzcompress($tmp, 6);
1064
+ }
1065
+ if ($compressed) {
1066
+ $res.= "\n/Filter /FlateDecode";
1067
+ }
1068
+
1069
+ $res.= "\n/Length ".mb_strlen($tmp, '8bit') .">>\nstream\n$tmp\nendstream";
1070
+ }
1071
+
1072
+ $res.= "\nendobj";
1073
+ return $res;
1074
+ }
1075
+ }
1076
+
1077
+ /**
1078
+ * the document procset, solves some problems with printing to old PS printers
1079
+ */
1080
+ protected function o_procset($id, $action, $options = '') {
1081
+ if ($action !== 'new') {
1082
+ $o = & $this->objects[$id];
1083
+ }
1084
+
1085
+ switch ($action) {
1086
+ case 'new':
1087
+ $this->objects[$id] = array('t'=>'procset', 'info'=>array('PDF'=>1, 'Text'=>1));
1088
+ $this->o_pages($this->currentNode, 'procset', $id);
1089
+ $this->procsetObjectId = $id;
1090
+ break;
1091
+
1092
+ case 'add':
1093
+ // this is to add new items to the procset list, despite the fact that this is considered
1094
+ // obselete, the items are required for printing to some postscript printers
1095
+ switch ($options) {
1096
+ case 'ImageB':
1097
+ case 'ImageC':
1098
+ case 'ImageI':
1099
+ $o['info'][$options] = 1;
1100
+ break;
1101
+ }
1102
+ break;
1103
+
1104
+ case 'out':
1105
+ $res = "\n$id 0 obj\n[";
1106
+ foreach ($o['info'] as $label=>$val) {
1107
+ $res.= "/$label ";
1108
+ }
1109
+ $res.= "]\nendobj";
1110
+ return $res;
1111
+ }
1112
+ }
1113
+
1114
+ /**
1115
+ * define the document information
1116
+ */
1117
+ protected function o_info($id, $action, $options = '') {
1118
+ if ($action !== 'new') {
1119
+ $o = & $this->objects[$id];
1120
+ }
1121
+
1122
+ switch ($action) {
1123
+ case 'new':
1124
+ $this->infoObject = $id;
1125
+ $date = 'D:'.@date('Ymd');
1126
+ $this->objects[$id] = array('t'=>'info', 'info'=>array('Creator'=>'R and OS php pdf writer, http://www.ros.co.nz', 'CreationDate'=>$date));
1127
+ break;
1128
+ case 'Title':
1129
+ case 'Author':
1130
+ case 'Subject':
1131
+ case 'Keywords':
1132
+ case 'Creator':
1133
+ case 'Producer':
1134
+ case 'CreationDate':
1135
+ case 'ModDate':
1136
+ case 'Trapped':
1137
+ $o['info'][$action] = $options;
1138
+ break;
1139
+
1140
+ case 'out':
1141
+ if ($this->encrypted) {
1142
+ $this->encryptInit($id);
1143
+ }
1144
+
1145
+ $res = "\n$id 0 obj\n<<\n";
1146
+ foreach ($o['info'] as $k=>$v) {
1147
+ $res.= "/$k (";
1148
+ // dates must be outputted as-is, without Unicode transformations
1149
+ $raw = ($k === 'CreationDate' || $k === 'ModDate');
1150
+ $c = $v;
1151
+
1152
+ if ($this->encrypted) {
1153
+ $c = $this->ARC4($c);
1154
+ }
1155
+
1156
+ $res.= ($raw) ? $c : $this->filterText($c);
1157
+ $res.= ")\n";
1158
+ }
1159
+
1160
+ $res.= ">>\nendobj";
1161
+ return $res;
1162
+ }
1163
+ }
1164
+
1165
+ /**
1166
+ * an action object, used to link to URLS initially
1167
+ */
1168
+ protected function o_action($id, $action, $options = '') {
1169
+ if ($action !== 'new') {
1170
+ $o = & $this->objects[$id];
1171
+ }
1172
+
1173
+ switch ($action) {
1174
+ case 'new':
1175
+ if (is_array($options)) {
1176
+ $this->objects[$id] = array('t'=>'action', 'info'=>$options, 'type'=>$options['type']);
1177
+ } else {
1178
+ // then assume a URI action
1179
+ $this->objects[$id] = array('t'=>'action', 'info'=>$options, 'type'=>'URI');
1180
+ }
1181
+ break;
1182
+
1183
+ case 'out':
1184
+ if ($this->encrypted) {
1185
+ $this->encryptInit($id);
1186
+ }
1187
+
1188
+ $res = "\n$id 0 obj\n<< /Type /Action";
1189
+ switch ($o['type']) {
1190
+ case 'ilink':
1191
+ if (!isset($this->destinations[(string)$o['info']['label']])) break;
1192
+
1193
+ // there will be an 'label' setting, this is the name of the destination
1194
+ $res.= "\n/S /GoTo\n/D ".$this->destinations[(string)$o['info']['label']]." 0 R";
1195
+ break;
1196
+
1197
+ case 'URI':
1198
+ $res.= "\n/S /URI\n/URI (";
1199
+ if ($this->encrypted) {
1200
+ $res.= $this->filterText($this->ARC4($o['info']), true, false);
1201
+ } else {
1202
+ $res.= $this->filterText($o['info'], true, false);
1203
+ }
1204
+
1205
+ $res.= ")";
1206
+ break;
1207
+ }
1208
+
1209
+ $res.= "\n>>\nendobj";
1210
+ return $res;
1211
+ }
1212
+ }
1213
+
1214
+ /**
1215
+ * an annotation object, this will add an annotation to the current page.
1216
+ * initially will support just link annotations
1217
+ */
1218
+ protected function o_annotation($id, $action, $options = '') {
1219
+ if ($action !== 'new') {
1220
+ $o = & $this->objects[$id];
1221
+ }
1222
+
1223
+ switch ($action) {
1224
+ case 'new':
1225
+ // add the annotation to the current page
1226
+ $pageId = $this->currentPage;
1227
+ $this->o_page($pageId, 'annot', $id);
1228
+
1229
+ // and add the action object which is going to be required
1230
+ switch ($options['type']) {
1231
+ case 'link':
1232
+ $this->objects[$id] = array('t'=>'annotation', 'info'=>$options);
1233
+ $this->numObj++;
1234
+ $this->o_action($this->numObj, 'new', $options['url']);
1235
+ $this->objects[$id]['info']['actionId'] = $this->numObj;
1236
+ break;
1237
+
1238
+ case 'ilink':
1239
+ // this is to a named internal link
1240
+ $label = $options['label'];
1241
+ $this->objects[$id] = array('t'=>'annotation', 'info'=>$options);
1242
+ $this->numObj++;
1243
+ $this->o_action($this->numObj, 'new', array('type'=>'ilink', 'label'=>$label));
1244
+ $this->objects[$id]['info']['actionId'] = $this->numObj;
1245
+ break;
1246
+ }
1247
+ break;
1248
+
1249
+ case 'out':
1250
+ $res = "\n$id 0 obj\n<< /Type /Annot";
1251
+ switch ($o['info']['type']) {
1252
+ case 'link':
1253
+ case 'ilink':
1254
+ $res.= "\n/Subtype /Link";
1255
+ break;
1256
+ }
1257
+ $res.= "\n/A ".$o['info']['actionId']." 0 R";
1258
+ $res.= "\n/Border [0 0 0]";
1259
+ $res.= "\n/H /I";
1260
+ $res.= "\n/Rect [ ";
1261
+
1262
+ foreach ($o['info']['rect'] as $v) {
1263
+ $res.= sprintf("%.4F ", $v);
1264
+ }
1265
+
1266
+ $res.= "]";
1267
+ $res.= "\n>>\nendobj";
1268
+ return $res;
1269
+ }
1270
+ }
1271
+
1272
+ /**
1273
+ * a page object, it also creates a contents object to hold its contents
1274
+ */
1275
+ protected function o_page($id, $action, $options = '') {
1276
+ if ($action !== 'new') {
1277
+ $o = & $this->objects[$id];
1278
+ }
1279
+
1280
+ switch ($action) {
1281
+ case 'new':
1282
+ $this->numPages++;
1283
+ $this->objects[$id] = array('t'=>'page', 'info'=>array('parent'=>$this->currentNode, 'pageNum'=>$this->numPages));
1284
+
1285
+ if (is_array($options)) {
1286
+ // then this must be a page insertion, array should contain 'rid','pos'=[before|after]
1287
+ $options['id'] = $id;
1288
+ $this->o_pages($this->currentNode, 'page', $options);
1289
+ } else {
1290
+ $this->o_pages($this->currentNode, 'page', $id);
1291
+ }
1292
+
1293
+ $this->currentPage = $id;
1294
+ //make a contents object to go with this page
1295
+ $this->numObj++;
1296
+ $this->o_contents($this->numObj, 'new', $id);
1297
+ $this->currentContents = $this->numObj;
1298
+ $this->objects[$id]['info']['contents'] = array();
1299
+ $this->objects[$id]['info']['contents'][] = $this->numObj;
1300
+
1301
+ $match = ($this->numPages%2 ? 'odd' : 'even');
1302
+ foreach ($this->addLooseObjects as $oId=>$target) {
1303
+ if ($target === 'all' || $match === $target) {
1304
+ $this->objects[$id]['info']['contents'][] = $oId;
1305
+ }
1306
+ }
1307
+ break;
1308
+
1309
+ case 'content':
1310
+ $o['info']['contents'][] = $options;
1311
+ break;
1312
+
1313
+ case 'annot':
1314
+ // add an annotation to this page
1315
+ if (!isset($o['info']['annot'])) {
1316
+ $o['info']['annot'] = array();
1317
+ }
1318
+
1319
+ // $options should contain the id of the annotation dictionary
1320
+ $o['info']['annot'][] = $options;
1321
+ break;
1322
+
1323
+ case 'out':
1324
+ $res = "\n$id 0 obj\n<< /Type /Page";
1325
+ $res.= "\n/Parent ".$o['info']['parent']." 0 R";
1326
+
1327
+ if (isset($o['info']['annot'])) {
1328
+ $res.= "\n/Annots [";
1329
+ foreach ($o['info']['annot'] as $aId) {
1330
+ $res.= " $aId 0 R";
1331
+ }
1332
+ $res.= " ]";
1333
+ }
1334
+
1335
+ $count = count($o['info']['contents']);
1336
+ if ($count == 1) {
1337
+ $res.= "\n/Contents ".$o['info']['contents'][0]." 0 R";
1338
+ } else if ($count>1) {
1339
+ $res.= "\n/Contents [\n";
1340
+
1341
+ // reverse the page contents so added objects are below normal content
1342
+ //foreach (array_reverse($o['info']['contents']) as $cId) {
1343
+ // Back to normal now that I've got transparency working --Benj
1344
+ foreach ($o['info']['contents'] as $cId) {
1345
+ $res.= "$cId 0 R\n";
1346
+ }
1347
+ $res.= "]";
1348
+ }
1349
+
1350
+ $res.= "\n>>\nendobj";
1351
+ return $res;
1352
+ }
1353
+ }
1354
+
1355
+ /**
1356
+ * the contents objects hold all of the content which appears on pages
1357
+ */
1358
+ protected function o_contents($id, $action, $options = '') {
1359
+ if ($action !== 'new') {
1360
+ $o = & $this->objects[$id];
1361
+ }
1362
+
1363
+ switch ($action) {
1364
+ case 'new':
1365
+ $this->objects[$id] = array('t'=>'contents', 'c'=>'', 'info'=>array());
1366
+ if (mb_strlen($options, '8bit') && intval($options)) {
1367
+ // then this contents is the primary for a page
1368
+ $this->objects[$id]['onPage'] = $options;
1369
+ } else if ($options === 'raw') {
1370
+ // then this page contains some other type of system object
1371
+ $this->objects[$id]['raw'] = 1;
1372
+ }
1373
+ break;
1374
+
1375
+ case 'add':
1376
+ // add more options to the decleration
1377
+ foreach ($options as $k=>$v) {
1378
+ $o['info'][$k] = $v;
1379
+ }
1380
+
1381
+ case 'out':
1382
+ $tmp = $o['c'];
1383
+ $res = "\n$id 0 obj\n";
1384
+
1385
+ if (isset($this->objects[$id]['raw'])) {
1386
+ $res.= $tmp;
1387
+ } else {
1388
+ $res.= "<<";
1389
+ if ($this->compressionReady && $this->options['compression']) {
1390
+ // then implement ZLIB based compression on this content stream
1391
+ $res.= " /Filter /FlateDecode";
1392
+ $tmp = gzcompress($tmp, 6);
1393
+ }
1394
+
1395
+ if ($this->encrypted) {
1396
+ $this->encryptInit($id);
1397
+ $tmp = $this->ARC4($tmp);
1398
+ }
1399
+
1400
+ foreach ($o['info'] as $k=>$v) {
1401
+ $res.= "\n/$k $v";
1402
+ }
1403
+
1404
+ $res.= "\n/Length ".mb_strlen($tmp, '8bit') ." >>\nstream\n$tmp\nendstream";
1405
+ }
1406
+
1407
+ $res.= "\nendobj";
1408
+ return $res;
1409
+ }
1410
+ }
1411
+
1412
+ protected function o_embedjs($id, $action) {
1413
+ if ($action !== 'new') {
1414
+ $o = & $this->objects[$id];
1415
+ }
1416
+
1417
+ switch ($action) {
1418
+ case 'new':
1419
+ $this->objects[$id] = array('t'=>'embedjs', 'info'=>array(
1420
+ 'Names' => '[(EmbeddedJS) '.($id+1).' 0 R]'
1421
+ ));
1422
+ break;
1423
+
1424
+ case 'out':
1425
+ $res = "\n$id 0 obj\n<< ";
1426
+ foreach ($o['info'] as $k=>$v) {
1427
+ $res.= "\n/$k $v";
1428
+ }
1429
+ $res.= "\n>>\nendobj";
1430
+ return $res;
1431
+ }
1432
+ }
1433
+
1434
+ protected function o_javascript($id, $action, $code = '') {
1435
+ if ($action !== 'new') {
1436
+ $o = & $this->objects[$id];
1437
+ }
1438
+
1439
+ switch ($action) {
1440
+ case 'new':
1441
+ $this->objects[$id] = array('t'=>'javascript', 'info'=>array(
1442
+ 'S' => '/JavaScript',
1443
+ 'JS' => '('.$this->filterText($code).')',
1444
+ ));
1445
+ break;
1446
+
1447
+ case 'out':
1448
+ $res = "\n$id 0 obj\n<< ";
1449
+ foreach ($o['info'] as $k=>$v) {
1450
+ $res.= "\n/$k $v";
1451
+ }
1452
+ $res.= "\n>>\nendobj";
1453
+ return $res;
1454
+ }
1455
+ }
1456
+
1457
+ /**
1458
+ * an image object, will be an XObject in the document, includes description and data
1459
+ */
1460
+ protected function o_image($id, $action, $options = '') {
1461
+ if ($action !== 'new') {
1462
+ $o = & $this->objects[$id];
1463
+ }
1464
+
1465
+ switch ($action) {
1466
+ case 'new':
1467
+ // make the new object
1468
+ $this->objects[$id] = array('t'=>'image', 'data'=>&$options['data'], 'info'=>array());
1469
+
1470
+ $info =& $this->objects[$id]['info'];
1471
+
1472
+ $info['Type'] = '/XObject';
1473
+ $info['Subtype'] = '/Image';
1474
+ $info['Width'] = $options['iw'];
1475
+ $info['Height'] = $options['ih'];
1476
+
1477
+ if (isset($options['masked']) && $options['masked']) {
1478
+ $info['SMask'] = ($this->numObj-1).' 0 R';
1479
+ }
1480
+
1481
+ if (!isset($options['type']) || $options['type'] === 'jpg') {
1482
+ if (!isset($options['channels'])) {
1483
+ $options['channels'] = 3;
1484
+ }
1485
+
1486
+ switch ($options['channels']) {
1487
+ case 1: $info['ColorSpace'] = '/DeviceGray'; break;
1488
+ case 4: $info['ColorSpace'] = '/DeviceCMYK'; break;
1489
+ default: $info['ColorSpace'] = '/DeviceRGB'; break;
1490
+ }
1491
+
1492
+ if ($info['ColorSpace'] === '/DeviceCMYK') {
1493
+ $info['Decode'] = '[1 0 1 0 1 0 1 0]';
1494
+ }
1495
+
1496
+ $info['Filter'] = '/DCTDecode';
1497
+ $info['BitsPerComponent'] = 8;
1498
+ }
1499
+
1500
+ else if ($options['type'] === 'png') {
1501
+ $info['Filter'] = '/FlateDecode';
1502
+ $info['DecodeParms'] = '<< /Predictor 15 /Colors '.$options['ncolor'].' /Columns '.$options['iw'].' /BitsPerComponent '.$options['bitsPerComponent'].'>>';
1503
+
1504
+ if ($options['isMask']) {
1505
+ $info['ColorSpace'] = '/DeviceGray';
1506
+ }
1507
+ else {
1508
+ if (mb_strlen($options['pdata'], '8bit')) {
1509
+ $tmp = ' [ /Indexed /DeviceRGB '.(mb_strlen($options['pdata'], '8bit') /3-1) .' ';
1510
+ $this->numObj++;
1511
+ $this->o_contents($this->numObj, 'new');
1512
+ $this->objects[$this->numObj]['c'] = $options['pdata'];
1513
+ $tmp.= $this->numObj.' 0 R';
1514
+ $tmp.= ' ]';
1515
+ $info['ColorSpace'] = $tmp;
1516
+
1517
+ if (isset($options['transparency'])) {
1518
+ $transparency = $options['transparency'];
1519
+ switch ($transparency['type']) {
1520
+ case 'indexed':
1521
+ $tmp = ' [ '.$transparency['data'].' '.$transparency['data'].'] ';
1522
+ $info['Mask'] = $tmp;
1523
+ break;
1524
+
1525
+ case 'color-key':
1526
+ $tmp = ' [ '.
1527
+ $transparency['r'] . ' ' . $transparency['r'] .
1528
+ $transparency['g'] . ' ' . $transparency['g'] .
1529
+ $transparency['b'] . ' ' . $transparency['b'] .
1530
+ ' ] ';
1531
+ $info['Mask'] = $tmp;
1532
+ break;
1533
+ }
1534
+ }
1535
+ } else {
1536
+ if (isset($options['transparency'])) {
1537
+ $transparency = $options['transparency'];
1538
+
1539
+ switch ($transparency['type']) {
1540
+ case 'indexed':
1541
+ $tmp = ' [ '.$transparency['data'].' '.$transparency['data'].'] ';
1542
+ $info['Mask'] = $tmp;
1543
+ break;
1544
+
1545
+ case 'color-key':
1546
+ $tmp = ' [ '.
1547
+ $transparency['r'] . ' ' . $transparency['r'] . ' ' .
1548
+ $transparency['g'] . ' ' . $transparency['g'] . ' ' .
1549
+ $transparency['b'] . ' ' . $transparency['b'] .
1550
+ ' ] ';
1551
+ $info['Mask'] = $tmp;
1552
+ break;
1553
+ }
1554
+ }
1555
+ $info['ColorSpace'] = '/'.$options['color'];
1556
+ }
1557
+ }
1558
+
1559
+ $info['BitsPerComponent'] = $options['bitsPerComponent'];
1560
+ }
1561
+
1562
+ // assign it a place in the named resource dictionary as an external object, according to
1563
+ // the label passed in with it.
1564
+ $this->o_pages($this->currentNode, 'xObject', array('label'=>$options['label'], 'objNum'=>$id));
1565
+
1566
+ // also make sure that we have the right procset object for it.
1567
+ $this->o_procset($this->procsetObjectId, 'add', 'ImageC');
1568
+ break;
1569
+
1570
+ case 'out':
1571
+ $tmp = &$o['data'];
1572
+ $res = "\n$id 0 obj\n<<";
1573
+
1574
+ foreach ($o['info'] as $k=>$v) {
1575
+ $res.= "\n/$k $v";
1576
+ }
1577
+
1578
+ if ($this->encrypted) {
1579
+ $this->encryptInit($id);
1580
+ $tmp = $this->ARC4($tmp);
1581
+ }
1582
+
1583
+ $res.= "\n/Length ".mb_strlen($tmp, '8bit') .">>\nstream\n$tmp\nendstream\nendobj";
1584
+
1585
+ return $res;
1586
+ }
1587
+ }
1588
+
1589
+ /**
1590
+ * graphics state object
1591
+ */
1592
+ protected function o_extGState($id, $action, $options = "") {
1593
+ static $valid_params = array("LW", "LC", "LC", "LJ", "ML",
1594
+ "D", "RI", "OP", "op", "OPM",
1595
+ "Font", "BG", "BG2", "UCR",
1596
+ "TR", "TR2", "HT", "FL",
1597
+ "SM", "SA", "BM", "SMask",
1598
+ "CA", "ca", "AIS", "TK");
1599
+
1600
+ if ($action !== "new") {
1601
+ $o = & $this->objects[$id];
1602
+ }
1603
+
1604
+ switch ($action) {
1605
+ case "new":
1606
+ $this->objects[$id] = array('t' => 'extGState', 'info' => $options);
1607
+
1608
+ // Tell the pages about the new resource
1609
+ $this->numStates++;
1610
+ $this->o_pages($this->currentNode, 'extGState', array("objNum" => $id, "stateNum" => $this->numStates));
1611
+ break;
1612
+
1613
+ case "out":
1614
+ $res = "\n$id 0 obj\n<< /Type /ExtGState\n";
1615
+
1616
+ foreach ($o["info"] as $k => $v) {
1617
+ if ( !in_array($k, $valid_params))
1618
+ continue;
1619
+ $res.= "/$k $v\n";
1620
+ }
1621
+
1622
+ $res.= ">>\nendobj";
1623
+ return $res;
1624
+ }
1625
+ }
1626
+
1627
+ /**
1628
+ * encryption object.
1629
+ */
1630
+ protected function o_encryption($id, $action, $options = '') {
1631
+ if ($action !== 'new') {
1632
+ $o = & $this->objects[$id];
1633
+ }
1634
+
1635
+ switch ($action) {
1636
+ case 'new':
1637
+ // make the new object
1638
+ $this->objects[$id] = array('t'=>'encryption', 'info'=>$options);
1639
+ $this->arc4_objnum = $id;
1640
+
1641
+ // figure out the additional paramaters required
1642
+ $pad = chr(0x28) .chr(0xBF) .chr(0x4E) .chr(0x5E) .chr(0x4E) .chr(0x75) .chr(0x8A) .chr(0x41)
1643
+ .chr(0x64) .chr(0x00) .chr(0x4E) .chr(0x56) .chr(0xFF) .chr(0xFA) .chr(0x01) .chr(0x08)
1644
+ .chr(0x2E) .chr(0x2E) .chr(0x00) .chr(0xB6) .chr(0xD0) .chr(0x68) .chr(0x3E) .chr(0x80)
1645
+ .chr(0x2F) .chr(0x0C) .chr(0xA9) .chr(0xFE) .chr(0x64) .chr(0x53) .chr(0x69) .chr(0x7A);
1646
+
1647
+ $len = mb_strlen($options['owner'], '8bit');
1648
+
1649
+ if ($len > 32) {
1650
+ $owner = substr($options['owner'], 0, 32);
1651
+ } else if ($len < 32) {
1652
+ $owner = $options['owner'].substr($pad, 0, 32-$len);
1653
+ } else {
1654
+ $owner = $options['owner'];
1655
+ }
1656
+
1657
+ $len = mb_strlen($options['user'], '8bit');
1658
+ if ($len > 32) {
1659
+ $user = substr($options['user'], 0, 32);
1660
+ } else if ($len < 32) {
1661
+ $user = $options['user'].substr($pad, 0, 32-$len);
1662
+ } else {
1663
+ $user = $options['user'];
1664
+ }
1665
+
1666
+ $tmp = $this->md5_16($owner);
1667
+ $okey = substr($tmp, 0, 5);
1668
+ $this->ARC4_init($okey);
1669
+ $ovalue = $this->ARC4($user);
1670
+ $this->objects[$id]['info']['O'] = $ovalue;
1671
+
1672
+ // now make the u value, phew.
1673
+ $tmp = $this->md5_16($user.$ovalue.chr($options['p']) .chr(255) .chr(255) .chr(255) .$this->fileIdentifier);
1674
+
1675
+ $ukey = substr($tmp, 0, 5);
1676
+ $this->ARC4_init($ukey);
1677
+ $this->encryptionKey = $ukey;
1678
+ $this->encrypted = true;
1679
+ $uvalue = $this->ARC4($pad);
1680
+ $this->objects[$id]['info']['U'] = $uvalue;
1681
+ $this->encryptionKey = $ukey;
1682
+ // initialize the arc4 array
1683
+ break;
1684
+
1685
+ case 'out':
1686
+ $res = "\n$id 0 obj\n<<";
1687
+ $res.= "\n/Filter /Standard";
1688
+ $res.= "\n/V 1";
1689
+ $res.= "\n/R 2";
1690
+ $res.= "\n/O (".$this->filterText($o['info']['O'], true, false) .')';
1691
+ $res.= "\n/U (".$this->filterText($o['info']['U'], true, false) .')';
1692
+ // and the p-value needs to be converted to account for the twos-complement approach
1693
+ $o['info']['p'] = (($o['info']['p']^255) +1) *-1;
1694
+ $res.= "\n/P ".($o['info']['p']);
1695
+ $res.= "\n>>\nendobj";
1696
+ return $res;
1697
+ }
1698
+ }
1699
+
1700
+ /**
1701
+ * ARC4 functions
1702
+ * A series of function to implement ARC4 encoding in PHP
1703
+ */
1704
+
1705
+ /**
1706
+ * calculate the 16 byte version of the 128 bit md5 digest of the string
1707
+ */
1708
+ function md5_16($string) {
1709
+ $tmp = md5($string);
1710
+ $out = '';
1711
+ for ($i = 0;$i <= 30;$i = $i+2) {
1712
+ $out.= chr(hexdec(substr($tmp, $i, 2)));
1713
+ }
1714
+ return $out;
1715
+ }
1716
+
1717
+ /**
1718
+ * initialize the encryption for processing a particular object
1719
+ */
1720
+ function encryptInit($id) {
1721
+ $tmp = $this->encryptionKey;
1722
+ $hex = dechex($id);
1723
+ if (mb_strlen($hex, '8bit') <6) {
1724
+ $hex = substr('000000', 0, 6-mb_strlen($hex, '8bit')) .$hex;
1725
+ }
1726
+ $tmp.= chr(hexdec(substr($hex, 4, 2))) .chr(hexdec(substr($hex, 2, 2))) .chr(hexdec(substr($hex, 0, 2))) .chr(0) .chr(0);
1727
+ $key = $this->md5_16($tmp);
1728
+ $this->ARC4_init(substr($key, 0, 10));
1729
+ }
1730
+
1731
+ /**
1732
+ * initialize the ARC4 encryption
1733
+ */
1734
+ function ARC4_init($key = '') {
1735
+ $this->arc4 = '';
1736
+
1737
+ // setup the control array
1738
+ if (mb_strlen($key, '8bit') == 0) {
1739
+ return;
1740
+ }
1741
+
1742
+ $k = '';
1743
+ while (mb_strlen($k, '8bit') <256) {
1744
+ $k.= $key;
1745
+ }
1746
+
1747
+ $k = substr($k, 0, 256);
1748
+ for ($i = 0;$i<256;$i++) {
1749
+ $this->arc4.= chr($i);
1750
+ }
1751
+
1752
+ $j = 0;
1753
+
1754
+ for ($i = 0;$i<256;$i++) {
1755
+ $t = $this->arc4[$i];
1756
+ $j = ($j + ord($t) + ord($k[$i])) %256;
1757
+ $this->arc4[$i] = $this->arc4[$j];
1758
+ $this->arc4[$j] = $t;
1759
+ }
1760
+ }
1761
+
1762
+ /**
1763
+ * ARC4 encrypt a text string
1764
+ */
1765
+ function ARC4($text) {
1766
+ $len = mb_strlen($text, '8bit');
1767
+ $a = 0;
1768
+ $b = 0;
1769
+ $c = $this->arc4;
1770
+ $out = '';
1771
+ for ($i = 0;$i<$len;$i++) {
1772
+ $a = ($a+1) %256;
1773
+ $t = $c[$a];
1774
+ $b = ($b+ord($t)) %256;
1775
+ $c[$a] = $c[$b];
1776
+ $c[$b] = $t;
1777
+ $k = ord($c[(ord($c[$a]) +ord($c[$b])) %256]);
1778
+ $out.= chr(ord($text[$i]) ^ $k);
1779
+ }
1780
+ return $out;
1781
+ }
1782
+
1783
+ /**
1784
+ * functions which can be called to adjust or add to the document
1785
+ */
1786
+
1787
+ /**
1788
+ * add a link in the document to an external URL
1789
+ */
1790
+ function addLink($url, $x0, $y0, $x1, $y1) {
1791
+ $this->numObj++;
1792
+ $info = array('type'=>'link', 'url'=>$url, 'rect'=>array($x0, $y0, $x1, $y1));
1793
+ $this->o_annotation($this->numObj, 'new', $info);
1794
+ }
1795
+
1796
+ /**
1797
+ * add a link in the document to an internal destination (ie. within the document)
1798
+ */
1799
+ function addInternalLink($label, $x0, $y0, $x1, $y1) {
1800
+ $this->numObj++;
1801
+ $info = array('type'=>'ilink', 'label'=>$label, 'rect'=>array($x0, $y0, $x1, $y1));
1802
+ $this->o_annotation($this->numObj, 'new', $info);
1803
+ }
1804
+
1805
+ /**
1806
+ * set the encryption of the document
1807
+ * can be used to turn it on and/or set the passwords which it will have.
1808
+ * also the functions that the user will have are set here, such as print, modify, add
1809
+ */
1810
+ function setEncryption($userPass = '', $ownerPass = '', $pc = array()) {
1811
+ $p = bindec("11000000");
1812
+
1813
+ $options = array('print'=>4, 'modify'=>8, 'copy'=>16, 'add'=>32);
1814
+
1815
+ foreach ($pc as $k=>$v) {
1816
+ if ($v && isset($options[$k])) {
1817
+ $p+= $options[$k];
1818
+ } else if (isset($options[$v])) {
1819
+ $p+= $options[$v];
1820
+ }
1821
+ }
1822
+
1823
+ // implement encryption on the document
1824
+ if ($this->arc4_objnum == 0) {
1825
+ // then the block does not exist already, add it.
1826
+ $this->numObj++;
1827
+ if (mb_strlen($ownerPass) == 0) {
1828
+ $ownerPass = $userPass;
1829
+ }
1830
+
1831
+ $this->o_encryption($this->numObj, 'new', array('user'=>$userPass, 'owner'=>$ownerPass, 'p'=>$p));
1832
+ }
1833
+ }
1834
+
1835
+ /**
1836
+ * should be used for internal checks, not implemented as yet
1837
+ */
1838
+ function checkAllHere() {
1839
+ }
1840
+
1841
+ /**
1842
+ * return the pdf stream as a string returned from the function
1843
+ */
1844
+ function output($debug = false) {
1845
+ if ($debug) {
1846
+ // turn compression off
1847
+ $this->options['compression'] = false;
1848
+ }
1849
+
1850
+ if ($this->javascript) {
1851
+ $this->numObj++;
1852
+
1853
+ $js_id = $this->numObj;
1854
+ $this->o_embedjs($js_id, 'new');
1855
+ $this->o_javascript(++$this->numObj, 'new', $this->javascript);
1856
+
1857
+ $id = $this->catalogId;
1858
+
1859
+ $this->o_catalog($id, 'javascript', $js_id);
1860
+ }
1861
+
1862
+ if ($this->arc4_objnum) {
1863
+ $this->ARC4_init($this->encryptionKey);
1864
+ }
1865
+
1866
+ $this->checkAllHere();
1867
+
1868
+
1869
+ $xref = array();
1870
+ $content = '%PDF-1.3';
1871
+ $pos = mb_strlen($content, '8bit');
1872
+
1873
+ foreach ($this->objects as $k=>$v) {
1874
+ $tmp = 'o_'.$v['t'];
1875
+ $cont = $this->$tmp($k, 'out');
1876
+ $content.= $cont;
1877
+ $xref[] = $pos;
1878
+ $pos+= mb_strlen($cont, '8bit');
1879
+ }
1880
+
1881
+ $content.= "\nxref\n0 ".(count($xref) +1) ."\n0000000000 65535 f \n";
1882
+
1883
+ foreach ($xref as $p) {
1884
+ $content.= str_pad($p, 10, "0", STR_PAD_LEFT) . " 00000 n \n";
1885
+ }
1886
+
1887
+ $content.= "trailer\n<<\n/Size ".(count($xref) +1) ."\n/Root 1 0 R\n/Info $this->infoObject 0 R\n";
1888
+
1889
+ // if encryption has been applied to this document then add the marker for this dictionary
1890
+ if ($this->arc4_objnum > 0) {
1891
+ $content.= "/Encrypt $this->arc4_objnum 0 R\n";
1892
+ }
1893
+
1894
+ if (mb_strlen($this->fileIdentifier, '8bit')) {
1895
+ $content.= "/ID[<$this->fileIdentifier><$this->fileIdentifier>]\n";
1896
+ }
1897
+
1898
+ $content.= ">>\nstartxref\n$pos\n%%EOF\n";
1899
+
1900
+ return $content;
1901
+ }
1902
+
1903
+ /**
1904
+ * intialize a new document
1905
+ * if this is called on an existing document results may be unpredictable, but the existing document would be lost at minimum
1906
+ * this function is called automatically by the constructor function
1907
+ */
1908
+ private function newDocument($pageSize = array(0, 0, 612, 792)) {
1909
+ $this->numObj = 0;
1910
+ $this->objects = array();
1911
+
1912
+ $this->numObj++;
1913
+ $this->o_catalog($this->numObj, 'new');
1914
+
1915
+ $this->numObj++;
1916
+ $this->o_outlines($this->numObj, 'new');
1917
+
1918
+ $this->numObj++;
1919
+ $this->o_pages($this->numObj, 'new');
1920
+
1921
+ $this->o_pages($this->numObj, 'mediaBox', $pageSize);
1922
+ $this->currentNode = 3;
1923
+
1924
+ $this->numObj++;
1925
+ $this->o_procset($this->numObj, 'new');
1926
+
1927
+ $this->numObj++;
1928
+ $this->o_info($this->numObj, 'new');
1929
+
1930
+ $this->numObj++;
1931
+ $this->o_page($this->numObj, 'new');
1932
+
1933
+ // need to store the first page id as there is no way to get it to the user during
1934
+ // startup
1935
+ $this->firstPageId = $this->currentContents;
1936
+ }
1937
+
1938
+ /**
1939
+ * open the font file and return a php structure containing it.
1940
+ * first check if this one has been done before and saved in a form more suited to php
1941
+ * note that if a php serialized version does not exist it will try and make one, but will
1942
+ * require write access to the directory to do it... it is MUCH faster to have these serialized
1943
+ * files.
1944
+ */
1945
+ private function openFont($font) {
1946
+ // assume that $font contains the path and file but not the extension
1947
+ $pos = strrpos($font, '/');
1948
+
1949
+ if ($pos === false) {
1950
+ $dir = './';
1951
+ $name = $font;
1952
+ } else {
1953
+ $dir = substr($font, 0, $pos+1);
1954
+ $name = substr($font, $pos+1);
1955
+ }
1956
+
1957
+ $fontcache = $this->fontcache;
1958
+ if ($fontcache == '') {
1959
+ $fontcache = $dir;
1960
+ }
1961
+
1962
+ //$name filename without folder and extension of font metrics
1963
+ //$dir folder of font metrics
1964
+ //$fontcache folder of runtime created php serialized version of font metrics.
1965
+ // If this is not given, the same folder as the font metrics will be used.
1966
+ // Storing and reusing serialized versions improves speed much
1967
+
1968
+ $this->addMessage("openFont: $font - $name");
1969
+
1970
+ if ( !$this->isUnicode || in_array(mb_strtolower(basename($name)), self::$coreFonts) ) {
1971
+ $metrics_name = "$name.afm";
1972
+ }
1973
+ else {
1974
+ $metrics_name = "$name.ufm";
1975
+ }
1976
+
1977
+ $cache_name = "$metrics_name.php";
1978
+ $this->addMessage("metrics: $metrics_name, cache: $cache_name");
1979
+
1980
+ if (file_exists($fontcache . $cache_name)) {
1981
+ $this->addMessage("openFont: php file exists $fontcache$cache_name");
1982
+ $this->fonts[$font] = require($fontcache . $cache_name);
1983
+
1984
+ if (!isset($this->fonts[$font]['_version_']) || $this->fonts[$font]['_version_'] != $this->fontcacheVersion) {
1985
+ // if the font file is old, then clear it out and prepare for re-creation
1986
+ $this->addMessage('openFont: clear out, make way for new version.');
1987
+ $this->fonts[$font] = null;
1988
+ unset($this->fonts[$font]);
1989
+ }
1990
+ }
1991
+ else {
1992
+ $old_cache_name = "php_$metrics_name";
1993
+ if (file_exists($fontcache . $old_cache_name)) {
1994
+ $this->addMessage("openFont: php file doesn't exist $fontcache$cache_name, creating it from the old format");
1995
+ $old_cache = file_get_contents($fontcache . $old_cache_name);
1996
+ file_put_contents($fontcache . $cache_name, '<?php return ' . $old_cache . ';');
1997
+ return $this->openFont($font);
1998
+ }
1999
+ }
2000
+
2001
+ if (!isset($this->fonts[$font]) && file_exists($dir . $metrics_name)) {
2002
+ // then rebuild the php_<font>.afm file from the <font>.afm file
2003
+ $this->addMessage("openFont: build php file from $dir$metrics_name");
2004
+ $data = array();
2005
+
2006
+ // 20 => 'space'
2007
+ $data['codeToName'] = array();
2008
+
2009
+ // Since we're not going to enable Unicode for the core fonts we need to use a font-based
2010
+ // setting for Unicode support rather than a global setting.
2011
+ $data['isUnicode'] = (strtolower(substr($metrics_name, -3)) !== 'afm');
2012
+
2013
+ $cidtogid = '';
2014
+ if ($data['isUnicode']) {
2015
+ $cidtogid = str_pad('', 256*256*2, "\x00");
2016
+ }
2017
+
2018
+ $file = file($dir . $metrics_name);
2019
+
2020
+ foreach ($file as $rowA) {
2021
+ $row = trim($rowA);
2022
+ $pos = strpos($row, ' ');
2023
+
2024
+ if ($pos) {
2025
+ // then there must be some keyword
2026
+ $key = substr($row, 0, $pos);
2027
+ switch ($key) {
2028
+ case 'FontName':
2029
+ case 'FullName':
2030
+ case 'FamilyName':
2031
+ case 'PostScriptName':
2032
+ case 'Weight':
2033
+ case 'ItalicAngle':
2034
+ case 'IsFixedPitch':
2035
+ case 'CharacterSet':
2036
+ case 'UnderlinePosition':
2037
+ case 'UnderlineThickness':
2038
+ case 'Version':
2039
+ case 'EncodingScheme':
2040
+ case 'CapHeight':
2041
+ case 'XHeight':
2042
+ case 'Ascender':
2043
+ case 'Descender':
2044
+ case 'StdHW':
2045
+ case 'StdVW':
2046
+ case 'StartCharMetrics':
2047
+ case 'FontHeightOffset': // OAR - Added so we can offset the height calculation of a Windows font. Otherwise it's too big.
2048
+ $data[$key] = trim(substr($row, $pos));
2049
+ break;
2050
+
2051
+ case 'FontBBox':
2052
+ $data[$key] = explode(' ', trim(substr($row, $pos)));
2053
+ break;
2054
+
2055
+ //C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
2056
+ case 'C': // Found in AFM files
2057
+ $bits = explode(';', trim($row));
2058
+ $dtmp = array();
2059
+
2060
+ foreach ($bits as $bit) {
2061
+ $bits2 = explode(' ', trim($bit));
2062
+ if (mb_strlen($bits2[0], '8bit') == 0) continue;
2063
+
2064
+ if (count($bits2) >2) {
2065
+ $dtmp[$bits2[0]] = array();
2066
+ for ($i = 1;$i<count($bits2);$i++) {
2067
+ $dtmp[$bits2[0]][] = $bits2[$i];
2068
+ }
2069
+ } else if (count($bits2) == 2) {
2070
+ $dtmp[$bits2[0]] = $bits2[1];
2071
+ }
2072
+ }
2073
+
2074
+ $c = (int)$dtmp['C'];
2075
+ $n = $dtmp['N'];
2076
+ $width = floatval($dtmp['WX']);
2077
+
2078
+ if ($c >= 0) {
2079
+ if ($c != hexdec($n)) {
2080
+ $data['codeToName'][$c] = $n;
2081
+ }
2082
+ $data['C'][$c] = $width;
2083
+ } else {
2084
+ $data['C'][$n] = $width;
2085
+ }
2086
+
2087
+ if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
2088
+ $data['MissingWidth'] = $width;
2089
+ }
2090
+
2091
+ break;
2092
+
2093
+ // U 827 ; WX 0 ; N squaresubnosp ; G 675 ;
2094
+ case 'U': // Found in UFM files
2095
+ if (!$data['isUnicode']) break;
2096
+
2097
+ $bits = explode(';', trim($row));
2098
+ $dtmp = array();
2099
+
2100
+ foreach ($bits as $bit) {
2101
+ $bits2 = explode(' ', trim($bit));
2102
+ if (mb_strlen($bits2[0], '8bit') === 0) continue;
2103
+
2104
+ if (count($bits2) >2) {
2105
+ $dtmp[$bits2[0]] = array();
2106
+ for ($i = 1;$i<count($bits2);$i++) {
2107
+ $dtmp[$bits2[0]][] = $bits2[$i];
2108
+ }
2109
+ } else if (count($bits2) == 2) {
2110
+ $dtmp[$bits2[0]] = $bits2[1];
2111
+ }
2112
+ }
2113
+
2114
+ $c = (int)$dtmp['U'];
2115
+ $n = $dtmp['N'];
2116
+ $glyph = $dtmp['G'];
2117
+ $width = floatval($dtmp['WX']);
2118
+
2119
+ if ($c >= 0) {
2120
+ // Set values in CID to GID map
2121
+ if ($c >= 0 && $c < 0xFFFF && $glyph) {
2122
+ $cidtogid[$c*2] = chr($glyph >> 8);
2123
+ $cidtogid[$c*2 + 1] = chr($glyph & 0xFF);
2124
+ }
2125
+
2126
+ if ($c != hexdec($n)) {
2127
+ $data['codeToName'][$c] = $n;
2128
+ }
2129
+ $data['C'][$c] = $width;
2130
+ } else {
2131
+ $data['C'][$n] = $width;
2132
+ }
2133
+
2134
+ if (!isset($data['MissingWidth']) && $c == -1 && $n === '.notdef') {
2135
+ $data['MissingWidth'] = $width;
2136
+ }
2137
+
2138
+ break;
2139
+
2140
+ case 'KPX':
2141
+ break; // don't include them as they are not used yet
2142
+ //KPX Adieresis yacute -40
2143
+ $bits = explode(' ', trim($row));
2144
+ $data['KPX'][$bits[1]][$bits[2]] = $bits[3];
2145
+ break;
2146
+ }
2147
+ }
2148
+ }
2149
+
2150
+ // echo $cidtogid; die("CIDtoGID Displayed!");
2151
+ if ($this->compressionReady && $this->options['compression']) {
2152
+ // then implement ZLIB based compression on CIDtoGID string
2153
+ $data['CIDtoGID_Compressed'] = true;
2154
+ $cidtogid = gzcompress($cidtogid, 6);
2155
+ }
2156
+ $data['CIDtoGID'] = base64_encode($cidtogid);
2157
+ $data['_version_'] = $this->fontcacheVersion;
2158
+ $this->fonts[$font] = $data;
2159
+
2160
+ //Because of potential trouble with php safe mode, expect that the folder already exists.
2161
+ //If not existing, this will hit performance because of missing cached results.
2162
+ if ( is_dir(substr($fontcache,0,-1)) && is_writable(substr($fontcache,0,-1)) ) {
2163
+ file_put_contents($fontcache . $cache_name, '<?php return ' . var_export($data, true) . ';');
2164
+ }
2165
+ $data = null;
2166
+ }
2167
+
2168
+ if (!isset($this->fonts[$font])) {
2169
+ $this->addMessage("openFont: no font file found for $font. Do you need to run load_font.php?");
2170
+ //echo 'Font not Found '.$font;
2171
+ }
2172
+
2173
+ //pre_r($this->messages);
2174
+ }
2175
+
2176
+ /**
2177
+ * if the font is not loaded then load it and make the required object
2178
+ * else just make it the current font
2179
+ * the encoding array can contain 'encoding'=> 'none','WinAnsiEncoding','MacRomanEncoding' or 'MacExpertEncoding'
2180
+ * note that encoding='none' will need to be used for symbolic fonts
2181
+ * and 'differences' => an array of mappings between numbers 0->255 and character names.
2182
+ *
2183
+ */
2184
+ function selectFont($fontName, $encoding = '', $set = true) {
2185
+ $ext = substr($fontName, -4);
2186
+ if ($ext === '.afm' || $ext === '.ufm') {
2187
+ $fontName = substr($fontName, 0, mb_strlen($fontName)-4);
2188
+ }
2189
+
2190
+ if (!isset($this->fonts[$fontName])) {
2191
+ $this->addMessage("selectFont: selecting - $fontName - $encoding, $set");
2192
+
2193
+ // load the file
2194
+ $this->openFont($fontName);
2195
+
2196
+ if (isset($this->fonts[$fontName])) {
2197
+ $this->numObj++;
2198
+ $this->numFonts++;
2199
+
2200
+ $font = &$this->fonts[$fontName];
2201
+
2202
+ //$this->numFonts = md5($fontName);
2203
+ $pos = strrpos($fontName, '/');
2204
+ // $dir = substr($fontName,0,$pos+1);
2205
+ $name = substr($fontName, $pos+1);
2206
+ $options = array('name' => $name, 'fontFileName' => $fontName);
2207
+
2208
+ if (is_array($encoding)) {
2209
+ // then encoding and differences might be set
2210
+ if (isset($encoding['encoding'])) {
2211
+ $options['encoding'] = $encoding['encoding'];
2212
+ }
2213
+
2214
+ if (isset($encoding['differences'])) {
2215
+ $options['differences'] = $encoding['differences'];
2216
+ }
2217
+ } else if (mb_strlen($encoding, '8bit')) {
2218
+ // then perhaps only the encoding has been set
2219
+ $options['encoding'] = $encoding;
2220
+ }
2221
+
2222
+ $fontObj = $this->numObj;
2223
+ $this->o_font($this->numObj, 'new', $options);
2224
+ $font['fontNum'] = $this->numFonts;
2225
+
2226
+ // if this is a '.afm' font, and there is a '.pfa' file to go with it ( as there
2227
+ // should be for all non-basic fonts), then load it into an object and put the
2228
+ // references into the font object
2229
+ $basefile = $fontName;
2230
+
2231
+ $fbtype = '';
2232
+ if (file_exists("$basefile.pfb")) {
2233
+ $fbtype = 'pfb';
2234
+ }
2235
+ elseif (file_exists("$basefile.ttf")) {
2236
+ $fbtype = 'ttf';
2237
+ }
2238
+
2239
+ $fbfile = "$basefile.$fbtype";
2240
+
2241
+ // $pfbfile = substr($fontName,0,strlen($fontName)-4).'.pfb';
2242
+ // $ttffile = substr($fontName,0,strlen($fontName)-4).'.ttf';
2243
+ $this->addMessage('selectFont: checking for - '.$fbfile);
2244
+
2245
+ // OAR - I don't understand this old check
2246
+ // if (substr($fontName, -4) === '.afm' && strlen($fbtype)) {
2247
+ if ($fbtype) {
2248
+ $adobeFontName = isset($font['PostScriptName']) ? $font['PostScriptName'] : $font['FontName'];
2249
+ // $fontObj = $this->numObj;
2250
+ $this->addMessage("selectFont: adding font file - $fbfile - $adobeFontName");
2251
+
2252
+ // find the array of font widths, and put that into an object.
2253
+ $firstChar = -1;
2254
+ $lastChar = 0;
2255
+ $widths = array();
2256
+ $cid_widths = array();
2257
+
2258
+ foreach ($font['C'] as $num => $d) {
2259
+ if (intval($num) >0 || $num == '0') {
2260
+ if (!$font['isUnicode']) {
2261
+ // With Unicode, widths array isn't used
2262
+ if ($lastChar>0 && $num>$lastChar+1) {
2263
+ for ($i = $lastChar+1;$i<$num;$i++) {
2264
+ $widths[] = 0;
2265
+ }
2266
+ }
2267
+ }
2268
+
2269
+ $widths[] = $d;
2270
+
2271
+ if ($font['isUnicode']) {
2272
+ $cid_widths[$num] = $d;
2273
+ }
2274
+
2275
+ if ($firstChar == -1) {
2276
+ $firstChar = $num;
2277
+ }
2278
+
2279
+ $lastChar = $num;
2280
+ }
2281
+ }
2282
+
2283
+ // also need to adjust the widths for the differences array
2284
+ if (isset($options['differences'])) {
2285
+ foreach ($options['differences'] as $charNum => $charName) {
2286
+ if ($charNum > $lastChar) {
2287
+ if (!$font['isUnicode']) {
2288
+ // With Unicode, widths array isn't used
2289
+ for ($i = $lastChar + 1; $i <= $charNum; $i++) {
2290
+ $widths[] = 0;
2291
+ }
2292
+ }
2293
+
2294
+ $lastChar = $charNum;
2295
+ }
2296
+
2297
+ if (isset($font['C'][$charName])) {
2298
+ $widths[$charNum-$firstChar] = $font['C'][$charName];
2299
+ if ($font['isUnicode']) {
2300
+ $cid_widths[$charName] = $font['C'][$charName];
2301
+ }
2302
+ }
2303
+ }
2304
+ }
2305
+
2306
+ if ($font['isUnicode']) {
2307
+ $font['CIDWidths'] = $cid_widths;
2308
+ }
2309
+
2310
+ $this->addMessage('selectFont: FirstChar = '.$firstChar);
2311
+ $this->addMessage('selectFont: LastChar = '.$lastChar);
2312
+
2313
+ $widthid = -1;
2314
+
2315
+ if (!$font['isUnicode']) {
2316
+ // With Unicode, widths array isn't used
2317
+
2318
+ $this->numObj++;
2319
+ $this->o_contents($this->numObj, 'new', 'raw');
2320
+ $this->objects[$this->numObj]['c'].= '['.implode(' ', $widths).']';
2321
+ $widthid = $this->numObj;
2322
+ }
2323
+
2324
+ $missing_width = 500;
2325
+ $stemV = 70;
2326
+
2327
+ if (isset($font['MissingWidth'])) {
2328
+ $missing_width = $font['MissingWidth'];
2329
+ }
2330
+ if (isset($font['StdVW'])) {
2331
+ $stemV = $font['StdVW'];
2332
+ } elseif (isset($font['Weight']) && preg_match('!(bold|black)!i', $font['Weight'])) {
2333
+ $stemV = 120;
2334
+ }
2335
+
2336
+ // load the pfb file, and put that into an object too.
2337
+ // note that pdf supports only binary format type 1 font files, though there is a
2338
+ // simple utility to convert them from pfa to pfb.
2339
+ if (!$this->isUnicode || $fbtype !== 'ttf' || empty($this->stringSubsets)) {
2340
+ $data = file_get_contents($fbfile);
2341
+ }
2342
+ else {
2343
+ $this->stringSubsets[$fontName][] = 32; // Force space if not in yet
2344
+
2345
+ $subset = $this->stringSubsets[$fontName];
2346
+ sort($subset);
2347
+
2348
+ // Load font
2349
+ $font_obj = Font::load($fbfile);
2350
+ $font_obj->parse();
2351
+
2352
+ // Define subset
2353
+ $font_obj->setSubset($subset);
2354
+ $font_obj->reduce();
2355
+
2356
+ // Write new font
2357
+ $tmp_name = "$fbfile.tmp.".uniqid();
2358
+ $font_obj->open($tmp_name, Font_Binary_Stream::modeWrite);
2359
+ $font_obj->encode(array("OS/2"));
2360
+ $font_obj->close();
2361
+
2362
+ // Parse the new font to get cid2gid and widths
2363
+ $font_obj = Font::load($tmp_name);
2364
+
2365
+ // Find Unicode char map table
2366
+ $subtable = null;
2367
+ foreach ($font_obj->getData("cmap", "subtables") as $_subtable) {
2368
+ if ($_subtable["platformID"] == 0 || $_subtable["platformID"] == 3 && $_subtable["platformSpecificID"] == 1) {
2369
+ $subtable = $_subtable;
2370
+ break;
2371
+ }
2372
+ }
2373
+
2374
+ if ($subtable) {
2375
+ $glyphIndexArray = $subtable["glyphIndexArray"];
2376
+ $hmtx = $font_obj->getData("hmtx");
2377
+
2378
+ unset($glyphIndexArray[0xFFFF]);
2379
+
2380
+ $cidtogid = str_pad('', max(array_keys($glyphIndexArray))*2+1, "\x00");
2381
+ $font['CIDWidths'] = array();
2382
+ foreach ($glyphIndexArray as $cid => $gid) {
2383
+ if ($cid >= 0 && $cid < 0xFFFF && $gid) {
2384
+ $cidtogid[$cid*2] = chr($gid >> 8);
2385
+ $cidtogid[$cid*2 + 1] = chr($gid & 0xFF);
2386
+ }
2387
+
2388
+ $width = $font_obj->normalizeFUnit(isset($hmtx[$gid]) ? $hmtx[$gid][0] : $hmtx[0][0]);
2389
+ $font['CIDWidths'][$cid] = $width;
2390
+ }
2391
+
2392
+ $font['CIDtoGID'] = base64_encode(gzcompress($cidtogid));
2393
+ $font['CIDtoGID_Compressed'] = true;
2394
+
2395
+ $data = file_get_contents($tmp_name);
2396
+ }
2397
+ else {
2398
+ $data = file_get_contents($fbfile);
2399
+ }
2400
+
2401
+ $font_obj->close();
2402
+ unlink($tmp_name);
2403
+ }
2404
+
2405
+ // create the font descriptor
2406
+ $this->numObj++;
2407
+ $fontDescriptorId = $this->numObj;
2408
+
2409
+ $this->numObj++;
2410
+ $pfbid = $this->numObj;
2411
+
2412
+ // determine flags (more than a little flakey, hopefully will not matter much)
2413
+ $flags = 0;
2414
+
2415
+ if ($font['ItalicAngle'] != 0) {
2416
+ $flags+= pow(2, 6);
2417
+ }
2418
+
2419
+ if ($font['IsFixedPitch'] === 'true') {
2420
+ $flags+= 1;
2421
+ }
2422
+
2423
+ $flags+= pow(2, 5); // assume non-sybolic
2424
+ $list = array(
2425
+ 'Ascent' => 'Ascender',
2426
+ 'CapHeight' => 'CapHeight',
2427
+ 'MissingWidth' => 'MissingWidth',
2428
+ 'Descent' => 'Descender',
2429
+ 'FontBBox' => 'FontBBox',
2430
+ 'ItalicAngle' => 'ItalicAngle'
2431
+ );
2432
+ $fdopt = array(
2433
+ 'Flags' => $flags,
2434
+ 'FontName' => $adobeFontName,
2435
+ 'StemV' => $stemV
2436
+ );
2437
+
2438
+ foreach ($list as $k => $v) {
2439
+ if (isset($font[$v])) {
2440
+ $fdopt[$k] = $font[$v];
2441
+ }
2442
+ }
2443
+
2444
+ if ($fbtype === 'pfb') {
2445
+ $fdopt['FontFile'] = $pfbid;
2446
+ } else if ($fbtype === 'ttf') {
2447
+ $fdopt['FontFile2'] = $pfbid;
2448
+ }
2449
+
2450
+ $this->o_fontDescriptor($fontDescriptorId, 'new', $fdopt);
2451
+
2452
+ // embed the font program
2453
+ $this->o_contents($this->numObj, 'new');
2454
+ $this->objects[$pfbid]['c'].= $data;
2455
+
2456
+ // determine the cruicial lengths within this file
2457
+ if ($fbtype === 'pfb') {
2458
+ $l1 = strpos($data, 'eexec') +6;
2459
+ $l2 = strpos($data, '00000000') -$l1;
2460
+ $l3 = mb_strlen($data, '8bit') -$l2-$l1;
2461
+ $this->o_contents($this->numObj, 'add', array('Length1' => $l1, 'Length2' => $l2, 'Length3' => $l3));
2462
+ } else if ($fbtype == 'ttf') {
2463
+ $l1 = mb_strlen($data, '8bit');
2464
+ $this->o_contents($this->numObj, 'add', array('Length1' => $l1));
2465
+ }
2466
+
2467
+ // tell the font object about all this new stuff
2468
+ $tmp = array(
2469
+ 'BaseFont' => $adobeFontName,
2470
+ 'MissingWidth' => $missing_width,
2471
+ 'Widths' => $widthid,
2472
+ 'FirstChar' => $firstChar,
2473
+ 'LastChar' => $lastChar,
2474
+ 'FontDescriptor' => $fontDescriptorId,
2475
+ );
2476
+
2477
+ if ($fbtype === 'ttf') {
2478
+ $tmp['SubType'] = 'TrueType';
2479
+ }
2480
+
2481
+ $this->addMessage("adding extra info to font.($fontObj)");
2482
+
2483
+ foreach ($tmp as $fk => $fv) {
2484
+ $this->addMessage("$fk : $fv");
2485
+ }
2486
+
2487
+ $this->o_font($fontObj, 'add', $tmp);
2488
+ } else {
2489
+ $this->addMessage('selectFont: pfb or ttf file not found, ok if this is one of the 14 standard fonts');
2490
+ }
2491
+
2492
+ // also set the differences here, note that this means that these will take effect only the
2493
+ //first time that a font is selected, else they are ignored
2494
+ if (isset($options['differences'])) {
2495
+ $font['differences'] = $options['differences'];
2496
+ }
2497
+ }
2498
+ }
2499
+
2500
+ if ($set && isset($this->fonts[$fontName])) {
2501
+ // so if for some reason the font was not set in the last one then it will not be selected
2502
+ $this->currentBaseFont = $fontName;
2503
+
2504
+ // the next lines mean that if a new font is selected, then the current text state will be
2505
+ // applied to it as well.
2506
+ $this->currentFont = $this->currentBaseFont;
2507
+ $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
2508
+
2509
+ //$this->setCurrentFont();
2510
+ }
2511
+
2512
+ return $this->currentFontNum;
2513
+ //return $this->numObj;
2514
+ }
2515
+
2516
+ /**
2517
+ * sets up the current font, based on the font families, and the current text state
2518
+ * note that this system is quite flexible, a bold-italic font can be completely different to a
2519
+ * italic-bold font, and even bold-bold will have to be defined within the family to have meaning
2520
+ * This function is to be called whenever the currentTextState is changed, it will update
2521
+ * the currentFont setting to whatever the appropriatte family one is.
2522
+ * If the user calls selectFont themselves then that will reset the currentBaseFont, and the currentFont
2523
+ * This function will change the currentFont to whatever it should be, but will not change the
2524
+ * currentBaseFont.
2525
+ */
2526
+ private function setCurrentFont() {
2527
+ // if (strlen($this->currentBaseFont) == 0){
2528
+ // // then assume an initial font
2529
+ // $this->selectFont($this->defaultFont);
2530
+ // }
2531
+ // $cf = substr($this->currentBaseFont,strrpos($this->currentBaseFont,'/')+1);
2532
+ // if (strlen($this->currentTextState)
2533
+ // && isset($this->fontFamilies[$cf])
2534
+ // && isset($this->fontFamilies[$cf][$this->currentTextState])){
2535
+ // // then we are in some state or another
2536
+ // // and this font has a family, and the current setting exists within it
2537
+ // // select the font, then return it
2538
+ // $nf = substr($this->currentBaseFont,0,strrpos($this->currentBaseFont,'/')+1).$this->fontFamilies[$cf][$this->currentTextState];
2539
+ // $this->selectFont($nf,'',0);
2540
+ // $this->currentFont = $nf;
2541
+ // $this->currentFontNum = $this->fonts[$nf]['fontNum'];
2542
+ // } else {
2543
+ // // the this font must not have the right family member for the current state
2544
+ // // simply assume the base font
2545
+ $this->currentFont = $this->currentBaseFont;
2546
+ $this->currentFontNum = $this->fonts[$this->currentFont]['fontNum'];
2547
+ // }
2548
+ }
2549
+
2550
+ /**
2551
+ * function for the user to find out what the ID is of the first page that was created during
2552
+ * startup - useful if they wish to add something to it later.
2553
+ */
2554
+ function getFirstPageId() {
2555
+ return $this->firstPageId;
2556
+ }
2557
+
2558
+ /**
2559
+ * add content to the currently active object
2560
+ */
2561
+ private function addContent($content) {
2562
+ $this->objects[$this->currentContents]['c'] .= $content;
2563
+ }
2564
+
2565
+ /**
2566
+ * sets the colour for fill operations
2567
+ */
2568
+ function setColor($color, $force = false) {
2569
+ $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
2570
+
2571
+ if (!$force && $this->currentColour == $new_color) {
2572
+ return;
2573
+ }
2574
+
2575
+ if (isset($new_color[3])) {
2576
+ $this->currentColour = $new_color;
2577
+ $this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F k", $this->currentColour));
2578
+ }
2579
+
2580
+ elseif (isset($new_color[2])) {
2581
+ $this->currentColour = $new_color;
2582
+ $this->addContent(vsprintf("\n%.3F %.3F %.3F rg", $this->currentColour));
2583
+ }
2584
+ }
2585
+
2586
+ /**
2587
+ * sets the colour for stroke operations
2588
+ */
2589
+ function setStrokeColor($color, $force = false) {
2590
+ $new_color = array($color[0], $color[1], $color[2], isset($color[3]) ? $color[3] : null);
2591
+
2592
+ if (!$force && $this->currentStrokeColour == $new_color) return;
2593
+
2594
+ if (isset($new_color[3])) {
2595
+ $this->currentStrokeColour = $new_color;
2596
+ $this->addContent(vsprintf("\n%.3F %.3F %.3F %.3F K", $this->currentStrokeColour));
2597
+ }
2598
+
2599
+ elseif (isset($new_color[2])) {
2600
+ $this->currentStrokeColour = $new_color;
2601
+ $this->addContent(vsprintf("\n%.3F %.3F %.3F RG", $this->currentStrokeColour));
2602
+ }
2603
+ }
2604
+
2605
+ /**
2606
+ * Set the graphics state for compositions
2607
+ */
2608
+ function setGraphicsState($parameters) {
2609
+ // Create a new graphics state object
2610
+ // FIXME: should actually keep track of states that have already been created...
2611
+ $this->numObj++;
2612
+ $this->o_extGState($this->numObj, 'new', $parameters);
2613
+ $this->addContent("\n/GS$this->numStates gs");
2614
+ }
2615
+
2616
+ /**
2617
+ * Set current blend mode & opacity for lines.
2618
+ *
2619
+ * Valid blend modes are:
2620
+ *
2621
+ * Normal, Multiply, Screen, Overlay, Darken, Lighten,
2622
+ * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
2623
+ * Exclusion
2624
+ *
2625
+ * @param string $mode the blend mode to use
2626
+ * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
2627
+ */
2628
+ function setLineTransparency($mode, $opacity) {
2629
+ static $blend_modes = array("Normal", "Multiply", "Screen",
2630
+ "Overlay", "Darken", "Lighten",
2631
+ "ColorDogde", "ColorBurn", "HardLight",
2632
+ "SoftLight", "Difference", "Exclusion");
2633
+
2634
+ if ( !in_array($mode, $blend_modes) )
2635
+ $mode = "Normal";
2636
+
2637
+ // Only create a new graphics state if required
2638
+ if ( $mode === $this->currentLineTransparency["mode"] &&
2639
+ $opacity == $this->currentLineTransparency["opacity"] )
2640
+ return;
2641
+
2642
+ $this->currentLineTransparency["mode"] = $mode;
2643
+ $this->currentLineTransparency["opacity"] = $opacity;
2644
+
2645
+ $options = array("BM" => "/$mode",
2646
+ "CA" => (float)$opacity);
2647
+
2648
+ $this->setGraphicsState($options);
2649
+ }
2650
+
2651
+ /**
2652
+ * Set current blend mode & opacity for filled objects.
2653
+ *
2654
+ * Valid blend modes are:
2655
+ *
2656
+ * Normal, Multiply, Screen, Overlay, Darken, Lighten,
2657
+ * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
2658
+ * Exclusion
2659
+ *
2660
+ * @param string $mode the blend mode to use
2661
+ * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
2662
+ */
2663
+ function setFillTransparency($mode, $opacity) {
2664
+ static $blend_modes = array("Normal", "Multiply", "Screen",
2665
+ "Overlay", "Darken", "Lighten",
2666
+ "ColorDogde", "ColorBurn", "HardLight",
2667
+ "SoftLight", "Difference", "Exclusion");
2668
+
2669
+ if ( !in_array($mode, $blend_modes) ) {
2670
+ $mode = "Normal";
2671
+ }
2672
+
2673
+ if ( $mode === $this->currentFillTransparency["mode"] &&
2674
+ $opacity == $this->currentFillTransparency["opacity"] ) {
2675
+ return;
2676
+ }
2677
+
2678
+ $this->currentFillTransparency["mode"] = $mode;
2679
+ $this->currentFillTransparency["opacity"] = $opacity;
2680
+
2681
+ $options = array(
2682
+ "BM" => "/$mode",
2683
+ "ca" => (float)$opacity,
2684
+ );
2685
+
2686
+ $this->setGraphicsState($options);
2687
+ }
2688
+
2689
+ /**
2690
+ * draw a line from one set of coordinates to another
2691
+ */
2692
+ function line($x1, $y1, $x2, $y2, $stroke = true) {
2693
+ $this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F l", $x1, $y1, $x2, $y2));
2694
+
2695
+ if ($stroke) {
2696
+ $this->addContent(' S');
2697
+ }
2698
+ }
2699
+
2700
+ /**
2701
+ * draw a bezier curve based on 4 control points
2702
+ */
2703
+ function curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3) {
2704
+ // in the current line style, draw a bezier curve from (x0,y0) to (x3,y3) using the other two points
2705
+ // as the control points for the curve.
2706
+ $this->addContent(sprintf("\n%.3F %.3F m %.3F %.3F %.3F %.3F %.3F %.3F c S", $x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3));
2707
+ }
2708
+
2709
+ /**
2710
+ * draw a part of an ellipse
2711
+ */
2712
+ function partEllipse($x0, $y0, $astart, $afinish, $r1, $r2 = 0, $angle = 0, $nSeg = 8) {
2713
+ $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, false);
2714
+ }
2715
+
2716
+ /**
2717
+ * draw a filled ellipse
2718
+ */
2719
+ function filledEllipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360) {
2720
+ return $this->ellipse($x0, $y0, $r1, $r2, $angle, $nSeg, $astart, $afinish, true, true);
2721
+ }
2722
+
2723
+ /**
2724
+ * draw an ellipse
2725
+ * note that the part and filled ellipse are just special cases of this function
2726
+ *
2727
+ * draws an ellipse in the current line style
2728
+ * centered at $x0,$y0, radii $r1,$r2
2729
+ * if $r2 is not set, then a circle is drawn
2730
+ * from $astart to $afinish, measured in degrees, running anti-clockwise from the right hand side of the ellipse.
2731
+ * nSeg is not allowed to be less than 2, as this will simply draw a line (and will even draw a
2732
+ * pretty crappy shape at 2, as we are approximating with bezier curves.
2733
+ */
2734
+ function ellipse($x0, $y0, $r1, $r2 = 0, $angle = 0, $nSeg = 8, $astart = 0, $afinish = 360, $close = true, $fill = false, $stroke = true, $incomplete = false) {
2735
+ if ($r1 == 0) {
2736
+ return;
2737
+ }
2738
+
2739
+ if ($r2 == 0) {
2740
+ $r2 = $r1;
2741
+ }
2742
+
2743
+ if ($nSeg < 2) {
2744
+ $nSeg = 2;
2745
+ }
2746
+
2747
+ $astart = deg2rad((float)$astart);
2748
+ $afinish = deg2rad((float)$afinish);
2749
+ $totalAngle = $afinish-$astart;
2750
+
2751
+ $dt = $totalAngle/$nSeg;
2752
+ $dtm = $dt/3;
2753
+
2754
+ if ($angle != 0) {
2755
+ $a = -1*deg2rad((float)$angle);
2756
+
2757
+ $this->addContent(sprintf("\n q %.3F %.3F %.3F %.3F %.3F %.3F cm", cos($a), -sin($a), sin($a), cos($a), $x0, $y0));
2758
+
2759
+ $x0 = 0;
2760
+ $y0 = 0;
2761
+ }
2762
+
2763
+ $t1 = $astart;
2764
+ $a0 = $x0 + $r1*cos($t1);
2765
+ $b0 = $y0 + $r2*sin($t1);
2766
+ $c0 = -$r1 * sin($t1);
2767
+ $d0 = $r2 * cos($t1);
2768
+
2769
+ if (!$incomplete) {
2770
+ $this->addContent(sprintf("\n%.3F %.3F m ", $a0, $b0));
2771
+ }
2772
+
2773
+ for ($i = 1; $i <= $nSeg; $i++) {
2774
+ // draw this bit of the total curve
2775
+ $t1 = $i * $dt + $astart;
2776
+ $a1 = $x0 + $r1 * cos($t1);
2777
+ $b1 = $y0 + $r2 * sin($t1);
2778
+ $c1 = -$r1 * sin($t1);
2779
+ $d1 = $r2 * cos($t1);
2780
+
2781
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F %.3F %.3F c", ($a0+$c0*$dtm), ($b0+$d0*$dtm), ($a1-$c1*$dtm), ($b1-$d1*$dtm), $a1, $b1));
2782
+
2783
+ $a0 = $a1;
2784
+ $b0 = $b1;
2785
+ $c0 = $c1;
2786
+ $d0 = $d1;
2787
+ }
2788
+
2789
+ if (!$incomplete) {
2790
+ if ($fill) {
2791
+ $this->addContent(' f');
2792
+ }
2793
+ else if ($close) {
2794
+ $this->addContent(' s'); // small 's' signifies closing the path as well
2795
+ }
2796
+ else if ($stroke) {
2797
+ $this->addContent(' S');
2798
+ }
2799
+ }
2800
+
2801
+ if ($angle != 0) {
2802
+ $this->addContent(' Q');
2803
+ }
2804
+ }
2805
+
2806
+ /**
2807
+ * this sets the line drawing style.
2808
+ * width, is the thickness of the line in user units
2809
+ * cap is the type of cap to put on the line, values can be 'butt','round','square'
2810
+ * where the diffference between 'square' and 'butt' is that 'square' projects a flat end past the
2811
+ * end of the line.
2812
+ * join can be 'miter', 'round', 'bevel'
2813
+ * dash is an array which sets the dash pattern, is a series of length values, which are the lengths of the
2814
+ * on and off dashes.
2815
+ * (2) represents 2 on, 2 off, 2 on , 2 off ...
2816
+ * (2,1) is 2 on, 1 off, 2 on, 1 off.. etc
2817
+ * phase is a modifier on the dash pattern which is used to shift the point at which the pattern starts.
2818
+ */
2819
+ function setLineStyle($width = 1, $cap = '', $join = '', $dash = '', $phase = 0) {
2820
+ // this is quite inefficient in that it sets all the parameters whenever 1 is changed, but will fix another day
2821
+ $string = '';
2822
+
2823
+ if ($width > 0) {
2824
+ $string.= "$width w";
2825
+ }
2826
+
2827
+ $ca = array('butt' => 0, 'round' => 1, 'square' => 2);
2828
+
2829
+ if (isset($ca[$cap])) {
2830
+ $string.= " $ca[$cap] J";
2831
+ }
2832
+
2833
+ $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
2834
+
2835
+ if (isset($ja[$join])) {
2836
+ $string.= " $ja[$join] j";
2837
+ }
2838
+
2839
+ if (is_array($dash)) {
2840
+ $string.= ' [ ' . implode(' ', $dash) . " ] $phase d";
2841
+ }
2842
+
2843
+ $this->currentLineStyle = $string;
2844
+ $this->addContent("\n$string");
2845
+ }
2846
+
2847
+ /**
2848
+ * draw a polygon, the syntax for this is similar to the GD polygon command
2849
+ */
2850
+ function polygon($p, $np, $f = false) {
2851
+ $this->addContent(sprintf("\n%.3F %.3F m ", $p[0], $p[1]));
2852
+
2853
+ for ($i = 2; $i < $np * 2; $i = $i + 2) {
2854
+ $this->addContent(sprintf("%.3F %.3F l ", $p[$i], $p[$i+1]));
2855
+ }
2856
+
2857
+ if ($f) {
2858
+ $this->addContent(' f');
2859
+ } else {
2860
+ $this->addContent(' S');
2861
+ }
2862
+ }
2863
+
2864
+ /**
2865
+ * a filled rectangle, note that it is the width and height of the rectangle which are the secondary paramaters, not
2866
+ * the coordinates of the upper-right corner
2867
+ */
2868
+ function filledRectangle($x1, $y1, $width, $height) {
2869
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re f", $x1, $y1, $width, $height));
2870
+ }
2871
+
2872
+ /**
2873
+ * draw a rectangle, note that it is the width and height of the rectangle which are the secondary paramaters, not
2874
+ * the coordinates of the upper-right corner
2875
+ */
2876
+ function rectangle($x1, $y1, $width, $height) {
2877
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re S", $x1, $y1, $width, $height));
2878
+ }
2879
+
2880
+ /**
2881
+ * save the current graphic state
2882
+ */
2883
+ function save() {
2884
+ // we must reset the colour cache or it will keep bad colours after clipping
2885
+ $this->currentColour = null;
2886
+ $this->currentStrokeColour = null;
2887
+ $this->addContent("\nq");
2888
+ }
2889
+
2890
+ /**
2891
+ * restore the last graphic state
2892
+ */
2893
+ function restore() {
2894
+ $this->addContent("\nQ");
2895
+ }
2896
+
2897
+ /**
2898
+ * draw a clipping rectangle, all the elements added after this will be clipped
2899
+ */
2900
+ function clippingRectangle($x1, $y1, $width, $height) {
2901
+ $this->save();
2902
+ $this->addContent(sprintf("\n%.3F %.3F %.3F %.3F re W n", $x1, $y1, $width, $height));
2903
+ }
2904
+
2905
+ /**
2906
+ * draw a clipping rounded rectangle, all the elements added after this will be clipped
2907
+ */
2908
+ function clippingRectangleRounded($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL) {
2909
+ $this->save();
2910
+
2911
+ // start: top edge, left end
2912
+ $this->addContent(sprintf("\n%.3F %.3F m ", $x1, $y1 - $rTL + $h));
2913
+
2914
+ // curve: bottom-left corner
2915
+ $this->ellipse($x1 + $rBL, $y1 + $rBL, $rBL, 0, 0, 8, 180, 270, false, false, false, true);
2916
+
2917
+ // line: right edge, bottom end
2918
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w - $rBR, $y1));
2919
+
2920
+ // curve: bottom-right corner
2921
+ $this->ellipse($x1 + $w - $rBR, $y1 + $rBR, $rBR, 0, 0, 8, 270, 360, false, false, false, true);
2922
+
2923
+ // line: right edge, top end
2924
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $w, $y1 + $h - $rTR));
2925
+
2926
+ // curve: bottom-right corner
2927
+ $this->ellipse($x1 + $w - $rTR, $y1 + $h - $rTR, $rTR, 0, 0, 8, 0, 90, false, false, false, true);
2928
+
2929
+ // line: bottom edge, right end
2930
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rTL, $y1 + $h));
2931
+
2932
+ // curve: top-right corner
2933
+ $this->ellipse($x1 + $rTL, $y1 + $h - $rTL, $rTL, 0, 0, 8, 90, 180, false, false, false, true);
2934
+
2935
+ // line: top edge, left end
2936
+ $this->addContent(sprintf("\n%.3F %.3F l ", $x1 + $rBL, $y1));
2937
+
2938
+ // Close & clip
2939
+ $this->addContent(" W n");
2940
+ }
2941
+
2942
+ /**
2943
+ * ends the last clipping shape
2944
+ */
2945
+ function clippingEnd() {
2946
+ $this->restore();
2947
+ }
2948
+
2949
+ /**
2950
+ * scale
2951
+ * @param float $s_x scaling factor for width as percent
2952
+ * @param float $s_y scaling factor for height as percent
2953
+ * @param float $x Origin abscisse
2954
+ * @param float $y Origin ordinate
2955
+ */
2956
+ function scale($s_x, $s_y, $x, $y) {
2957
+ $y = $this->currentPageSize["height"] - $y;
2958
+
2959
+ $tm = array(
2960
+ $s_x, 0,
2961
+ 0, $s_y,
2962
+ $x*(1-$s_x), $y*(1-$s_y)
2963
+ );
2964
+
2965
+ $this->transform($tm);
2966
+ }
2967
+
2968
+ /**
2969
+ * translate
2970
+ * @param float $t_x movement to the right
2971
+ * @param float $t_y movement to the bottom
2972
+ */
2973
+ function translate($t_x, $t_y) {
2974
+ $tm = array(
2975
+ 1, 0,
2976
+ 0, 1,
2977
+ $t_x, -$t_y
2978
+ );
2979
+
2980
+ $this->transform($tm);
2981
+ }
2982
+
2983
+ /**
2984
+ * rotate
2985
+ * @param float $angle angle in degrees for counter-clockwise rotation
2986
+ * @param float $x Origin abscisse
2987
+ * @param float $y Origin ordinate
2988
+ */
2989
+ function rotate($angle, $x, $y) {
2990
+ $y = $this->currentPageSize["height"] - $y;
2991
+
2992
+ $a = deg2rad($angle);
2993
+ $cos_a = cos($a);
2994
+ $sin_a = sin($a);
2995
+
2996
+ $tm = array(
2997
+ $cos_a, -$sin_a,
2998
+ $sin_a, $cos_a,
2999
+ $x - $sin_a*$y - $cos_a*$x, $y - $cos_a*$y + $sin_a*$x,
3000
+ );
3001
+
3002
+ $this->transform($tm);
3003
+ }
3004
+
3005
+ /**
3006
+ * skew
3007
+ * @param float $angle_x
3008
+ * @param float $angle_y
3009
+ * @param float $x Origin abscisse
3010
+ * @param float $y Origin ordinate
3011
+ */
3012
+ function skew($angle_x, $angle_y, $x, $y) {
3013
+ $y = $this->currentPageSize["height"] - $y;
3014
+
3015
+ $tan_x = tan(deg2rad($angle_x));
3016
+ $tan_y = tan(deg2rad($angle_y));
3017
+
3018
+ $tm = array(
3019
+ 1, -$tan_y,
3020
+ -$tan_x, 1,
3021
+ $tan_x*$y, $tan_y*$x,
3022
+ );
3023
+
3024
+ $this->transform($tm);
3025
+ }
3026
+
3027
+ /**
3028
+ * apply graphic transformations
3029
+ * @param array $tm transformation matrix
3030
+ */
3031
+ function transform($tm) {
3032
+ $this->addContent(vsprintf("\n %.3F %.3F %.3F %.3F %.3F %.3F cm", $tm));
3033
+ }
3034
+
3035
+ /**
3036
+ * add a new page to the document
3037
+ * this also makes the new page the current active object
3038
+ */
3039
+ function newPage($insert = 0, $id = 0, $pos = 'after') {
3040
+ // if there is a state saved, then go up the stack closing them
3041
+ // then on the new page, re-open them with the right setings
3042
+
3043
+ if ($this->nStateStack) {
3044
+ for ($i = $this->nStateStack;$i >= 1;$i--) {
3045
+ $this->restoreState($i);
3046
+ }
3047
+ }
3048
+
3049
+ $this->numObj++;
3050
+
3051
+ if ($insert) {
3052
+ // the id from the ezPdf class is the id of the contents of the page, not the page object itself
3053
+ // query that object to find the parent
3054
+ $rid = $this->objects[$id]['onPage'];
3055
+ $opt = array('rid' => $rid, 'pos' => $pos);
3056
+ $this->o_page($this->numObj, 'new', $opt);
3057
+ } else {
3058
+ $this->o_page($this->numObj, 'new');
3059
+ }
3060
+
3061
+ // if there is a stack saved, then put that onto the page
3062
+ if ($this->nStateStack) {
3063
+ for ($i = 1;$i <= $this->nStateStack;$i++) {
3064
+ $this->saveState($i);
3065
+ }
3066
+ }
3067
+
3068
+ // and if there has been a stroke or fill colour set, then transfer them
3069
+ if (isset($this->currentColour)) {
3070
+ $this->setColor($this->currentColour, true);
3071
+ }
3072
+
3073
+ if (isset($this->currentStrokeColour)) {
3074
+ $this->setStrokeColor($this->currentStrokeColour, true);
3075
+ }
3076
+
3077
+ // if there is a line style set, then put this in too
3078
+ if (mb_strlen($this->currentLineStyle, '8bit')) {
3079
+ $this->addContent("\n$this->currentLineStyle");
3080
+ }
3081
+
3082
+ // the call to the o_page object set currentContents to the present page, so this can be returned as the page id
3083
+ return $this->currentContents;
3084
+ }
3085
+
3086
+ /**
3087
+ * output the pdf code, streaming it to the browser
3088
+ * the relevant headers are set so that hopefully the browser will recognise it
3089
+ */
3090
+ function stream($options = '') {
3091
+ // setting the options allows the adjustment of the headers
3092
+ // values at the moment are:
3093
+ // 'Content-Disposition' => 'filename' - sets the filename, though not too sure how well this will
3094
+ // work as in my trial the browser seems to use the filename of the php file with .pdf on the end
3095
+ // 'Accept-Ranges' => 1 or 0 - if this is not set to 1, then this header is not included, off by default
3096
+ // this header seems to have caused some problems despite tha fact that it is supposed to solve
3097
+ // them, so I am leaving it off by default.
3098
+ // 'compress' = > 1 or 0 - apply content stream compression, this is on (1) by default
3099
+ // 'Attachment' => 1 or 0 - if 1, force the browser to open a download dialog
3100
+ if (!is_array($options)) {
3101
+ $options = array();
3102
+ }
3103
+
3104
+ if ( headers_sent()) {
3105
+ die("Unable to stream pdf: headers already sent");
3106
+ }
3107
+
3108
+ $debug = empty($options['compression']);
3109
+ $tmp = ltrim($this->output($debug));
3110
+
3111
+ header("Cache-Control: private");
3112
+ header("Content-type: application/pdf");
3113
+
3114
+ //FIXME: I don't know that this is sufficient for determining content length (i.e. what about transport compression?)
3115
+ header("Content-Length: " . mb_strlen($tmp, '8bit'));
3116
+ $fileName = (isset($options['Content-Disposition']) ? $options['Content-Disposition'] : 'file.pdf');
3117
+
3118
+ if ( !isset($options["Attachment"]))
3119
+ $options["Attachment"] = true;
3120
+
3121
+ $attachment = $options["Attachment"] ? "attachment" : "inline";
3122
+
3123
+ header("Content-Disposition: $attachment; filename=\"$fileName\"");
3124
+
3125
+ if (isset($options['Accept-Ranges']) && $options['Accept-Ranges'] == 1) {
3126
+ //FIXME: Is this the correct value ... spec says 1#range-unit
3127
+ header("Accept-Ranges: " . mb_strlen($tmp, '8bit'));
3128
+ }
3129
+
3130
+ echo $tmp;
3131
+ flush();
3132
+ }
3133
+
3134
+ /**
3135
+ * return the height in units of the current font in the given size
3136
+ */
3137
+ function getFontHeight($size) {
3138
+ if (!$this->numFonts) {
3139
+ $this->selectFont($this->defaultFont);
3140
+ }
3141
+
3142
+ $font = $this->fonts[$this->currentFont];
3143
+
3144
+ // for the current font, and the given size, what is the height of the font in user units
3145
+ if ( isset($font['Ascender']) && isset($font['Descender']) ) {
3146
+ $h = $font['Ascender']-$font['Descender'];
3147
+ }
3148
+ else {
3149
+ $h = $font['FontBBox'][3]-$font['FontBBox'][1];
3150
+ }
3151
+
3152
+ // have to adjust by a font offset for Windows fonts. unfortunately it looks like
3153
+ // the bounding box calculations are wrong and I don't know why.
3154
+ if (isset($font['FontHeightOffset'])) {
3155
+ // For CourierNew from Windows this needs to be -646 to match the
3156
+ // Adobe native Courier font.
3157
+ //
3158
+ // For FreeMono from GNU this needs to be -337 to match the
3159
+ // Courier font.
3160
+ //
3161
+ // Both have been added manually to the .afm and .ufm files.
3162
+ $h += (int)$font['FontHeightOffset'];
3163
+ }
3164
+
3165
+ return $size*$h/1000;
3166
+ }
3167
+
3168
+ function getFontXHeight($size) {
3169
+ if (!$this->numFonts) {
3170
+ $this->selectFont($this->defaultFont);
3171
+ }
3172
+
3173
+ $font = $this->fonts[$this->currentFont];
3174
+
3175
+ // for the current font, and the given size, what is the height of the font in user units
3176
+ if ( isset($font['XHeight']) ) {
3177
+ $xh = $font['Ascender']-$font['Descender'];
3178
+ }
3179
+ else {
3180
+ $xh = $this->getFontHeight($size) / 2;
3181
+ }
3182
+
3183
+ return $size*$xh/1000;
3184
+ }
3185
+
3186
+ /**
3187
+ * return the font descender, this will normally return a negative number
3188
+ * if you add this number to the baseline, you get the level of the bottom of the font
3189
+ * it is in the pdf user units
3190
+ */
3191
+ function getFontDescender($size) {
3192
+ // note that this will most likely return a negative value
3193
+ if (!$this->numFonts) {
3194
+ $this->selectFont($this->defaultFont);
3195
+ }
3196
+
3197
+ //$h = $this->fonts[$this->currentFont]['FontBBox'][1];
3198
+ $h = $this->fonts[$this->currentFont]['Descender'];
3199
+
3200
+ return $size*$h/1000;
3201
+ }
3202
+
3203
+ /**
3204
+ * filter the text, this is applied to all text just before being inserted into the pdf document
3205
+ * it escapes the various things that need to be escaped, and so on
3206
+ *
3207
+ * @access private
3208
+ */
3209
+ function filterText($text, $bom = true, $convert_encoding = true) {
3210
+ if (!$this->numFonts) {
3211
+ $this->selectFont($this->defaultFont);
3212
+ }
3213
+
3214
+ if ($convert_encoding) {
3215
+ $cf = $this->currentFont;
3216
+ if (isset($this->fonts[$cf]) && $this->fonts[$cf]['isUnicode']) {
3217
+ //$text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
3218
+ $text = $this->utf8toUtf16BE($text, $bom);
3219
+ } else {
3220
+ //$text = html_entity_decode($text, ENT_QUOTES);
3221
+ $text = mb_convert_encoding($text, self::$targetEncoding, 'UTF-8');
3222
+ }
3223
+ }
3224
+
3225
+ // the chr(13) substitution fixes a bug seen in TCPDF (bug #1421290)
3226
+ return strtr($text, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
3227
+ }
3228
+
3229
+ /**
3230
+ * return array containing codepoints (UTF-8 character values) for the
3231
+ * string passed in.
3232
+ *
3233
+ * based on the excellent TCPDF code by Nicola Asuni and the
3234
+ * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
3235
+ *
3236
+ * @access private
3237
+ * @author Orion Richardson
3238
+ * @since January 5, 2008
3239
+ * @param string $text UTF-8 string to process
3240
+ * @return array UTF-8 codepoints array for the string
3241
+ */
3242
+ function utf8toCodePointsArray(&$text) {
3243
+ $length = mb_strlen($text, '8bit'); // http://www.php.net/manual/en/function.mb-strlen.php#77040
3244
+ $unicode = array(); // array containing unicode values
3245
+ $bytes = array(); // array containing single character byte sequences
3246
+ $numbytes = 1; // number of octetc needed to represent the UTF-8 character
3247
+
3248
+ for ($i = 0; $i < $length; $i++) {
3249
+ $c = ord($text[$i]); // get one string character at time
3250
+ if (count($bytes) === 0) { // get starting octect
3251
+ if ($c <= 0x7F) {
3252
+ $unicode[] = $c; // use the character "as is" because is ASCII
3253
+ $numbytes = 1;
3254
+ } elseif (($c >> 0x05) === 0x06) { // 2 bytes character (0x06 = 110 BIN)
3255
+ $bytes[] = ($c - 0xC0) << 0x06;
3256
+ $numbytes = 2;
3257
+ } elseif (($c >> 0x04) === 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
3258
+ $bytes[] = ($c - 0xE0) << 0x0C;
3259
+ $numbytes = 3;
3260
+ } elseif (($c >> 0x03) === 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
3261
+ $bytes[] = ($c - 0xF0) << 0x12;
3262
+ $numbytes = 4;
3263
+ } else {
3264
+ // use replacement character for other invalid sequences
3265
+ $unicode[] = 0xFFFD;
3266
+ $bytes = array();
3267
+ $numbytes = 1;
3268
+ }
3269
+ } elseif (($c >> 0x06) === 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
3270
+ $bytes[] = $c - 0x80;
3271
+ if (count($bytes) === $numbytes) {
3272
+ // compose UTF-8 bytes to a single unicode value
3273
+ $c = $bytes[0];
3274
+ for ($j = 1; $j < $numbytes; $j++) {
3275
+ $c += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
3276
+ }
3277
+ if ((($c >= 0xD800) AND ($c <= 0xDFFF)) OR ($c >= 0x10FFFF)) {
3278
+ // The definition of UTF-8 prohibits encoding character numbers between
3279
+ // U+D800 and U+DFFF, which are reserved for use with the UTF-16
3280
+ // encoding form (as surrogate pairs) and do not directly represent
3281
+ // characters.
3282
+ $unicode[] = 0xFFFD; // use replacement character
3283
+ } else {
3284
+ $unicode[] = $c; // add char to array
3285
+ }
3286
+ // reset data for next char
3287
+ $bytes = array();
3288
+ $numbytes = 1;
3289
+ }
3290
+ } else {
3291
+ // use replacement character for other invalid sequences
3292
+ $unicode[] = 0xFFFD;
3293
+ $bytes = array();
3294
+ $numbytes = 1;
3295
+ }
3296
+ }
3297
+ return $unicode;
3298
+ }
3299
+
3300
+ /**
3301
+ * convert UTF-8 to UTF-16 with an additional byte order marker
3302
+ * at the front if required.
3303
+ *
3304
+ * based on the excellent TCPDF code by Nicola Asuni and the
3305
+ * RFC for UTF-8 at http://www.faqs.org/rfcs/rfc3629.html
3306
+ *
3307
+ * @access private
3308
+ * @author Orion Richardson
3309
+ * @since January 5, 2008
3310
+ * @param string $text UTF-8 string to process
3311
+ * @param boolean $bom whether to add the byte order marker
3312
+ * @return string UTF-16 result string
3313
+ */
3314
+ function utf8toUtf16BE(&$text, $bom = true) {
3315
+ $cf = $this->currentFont;
3316
+ if (!$this->fonts[$cf]['isUnicode']) return $text;
3317
+ $out = $bom ? "\xFE\xFF" : '';
3318
+
3319
+ $unicode = $this->utf8toCodePointsArray($text);
3320
+ foreach ($unicode as $c) {
3321
+ if ($c === 0xFFFD) {
3322
+ $out .= "\xFF\xFD"; // replacement character
3323
+ } elseif ($c < 0x10000) {
3324
+ $out .= chr($c >> 0x08) . chr($c & 0xFF);
3325
+ } else {
3326
+ $c -= 0x10000;
3327
+ $w1 = 0xD800 | ($c >> 0x10);
3328
+ $w2 = 0xDC00 | ($c & 0x3FF);
3329
+ $out .= chr($w1 >> 0x08) . chr($w1 & 0xFF) . chr($w2 >> 0x08) . chr($w2 & 0xFF);
3330
+ }
3331
+ }
3332
+ return $out;
3333
+ }
3334
+
3335
+ /**
3336
+ * given a start position and information about how text is to be laid out, calculate where
3337
+ * on the page the text will end
3338
+ */
3339
+ private function getTextPosition($x, $y, $angle, $size, $wa, $text) {
3340
+ // given this information return an array containing x and y for the end position as elements 0 and 1
3341
+ $w = $this->getTextWidth($size, $text);
3342
+
3343
+ // need to adjust for the number of spaces in this text
3344
+ $words = explode(' ', $text);
3345
+ $nspaces = count($words) -1;
3346
+ $w+= $wa*$nspaces;
3347
+ $a = deg2rad((float)$angle);
3348
+
3349
+ return array(cos($a) *$w+$x, -sin($a) *$w+$y);
3350
+ }
3351
+
3352
+ /**
3353
+ * Callback method used by smallCaps
3354
+ *
3355
+ * @param array $matches
3356
+ * @return string
3357
+ */
3358
+ function toUpper($matches) {
3359
+ return mb_strtoupper($matches[0]);
3360
+ }
3361
+
3362
+ function concatMatches($matches) {
3363
+ $str = "";
3364
+ foreach ($matches as $match){
3365
+ $str .= $match[0];
3366
+ }
3367
+ return $str;
3368
+ }
3369
+
3370
+ /**
3371
+ * add text to the document, at a specified location, size and angle on the page
3372
+ */
3373
+ function registerText($font, $text) {
3374
+ if ( !$this->isUnicode || in_array(mb_strtolower(basename($font)), self::$coreFonts) ) {
3375
+ return;
3376
+ }
3377
+
3378
+ if ( !isset($this->stringSubsets[$font]) ) {
3379
+ $this->stringSubsets[$font] = array();
3380
+ }
3381
+
3382
+ $this->stringSubsets[$font] = array_unique(array_merge($this->stringSubsets[$font], $this->utf8toCodePointsArray($text)));
3383
+ }
3384
+
3385
+ /**
3386
+ * add text to the document, at a specified location, size and angle on the page
3387
+ */
3388
+ function addText($x, $y, $size, $text, $angle = 0, $wordSpaceAdjust = 0, $charSpaceAdjust = 0, $smallCaps = false) {
3389
+ if (!$this->numFonts) {
3390
+ $this->selectFont($this->defaultFont);
3391
+ }
3392
+
3393
+ $text = str_replace(array("\r", "\n"), "", $text);
3394
+
3395
+ if ( $smallCaps ) {
3396
+ preg_match_all("/(\P{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
3397
+ $lower = $this->concatMatches($matches);
3398
+ d($lower);
3399
+
3400
+ preg_match_all("/(\p{Ll}+)/u", $text, $matches, PREG_SET_ORDER);
3401
+ $other = $this->concatMatches($matches);
3402
+ d($other);
3403
+
3404
+ //$text = preg_replace_callback("/\p{Ll}/u", array($this, "toUpper"), $text);
3405
+ }
3406
+
3407
+ // if there are any open callbacks, then they should be called, to show the start of the line
3408
+ if ($this->nCallback>0) {
3409
+ for ($i = $this->nCallback;$i>0;$i--) {
3410
+ // call each function
3411
+ $info = array(
3412
+ 'x' => $x,
3413
+ 'y' => $y,
3414
+ 'angle' => $angle,
3415
+ 'status' => 'sol',
3416
+ 'p' => $this->callback[$i]['p'],
3417
+ 'nCallback' => $this->callback[$i]['nCallback'],
3418
+ 'height' => $this->callback[$i]['height'],
3419
+ 'descender' => $this->callback[$i]['descender'],
3420
+ );
3421
+
3422
+ $func = $this->callback[$i]['f'];
3423
+ $this->$func($info);
3424
+ }
3425
+ }
3426
+
3427
+ if ($angle == 0) {
3428
+ $this->addContent(sprintf("\nBT %.3F %.3F Td", $x, $y));
3429
+ } else {
3430
+ $a = deg2rad((float)$angle);
3431
+ $this->addContent(sprintf("\nBT %.3F %.3F %.3F %.3F %.3F %.3F Tm", cos($a), -sin($a), sin($a), cos($a), $x, $y));
3432
+ }
3433
+
3434
+ if ($wordSpaceAdjust != 0 || $wordSpaceAdjust != $this->wordSpaceAdjust) {
3435
+ $this->wordSpaceAdjust = $wordSpaceAdjust;
3436
+ $this->addContent(sprintf(" %.3F Tw", $wordSpaceAdjust));
3437
+ }
3438
+
3439
+ if ($charSpaceAdjust != 0 || $charSpaceAdjust != $this->charSpaceAdjust) {
3440
+ $this->charSpaceAdjust = $charSpaceAdjust;
3441
+ $this->addContent(sprintf(" %.3F Tc", $charSpaceAdjust));
3442
+ }
3443
+
3444
+ $len = mb_strlen($text);
3445
+ $start = 0;
3446
+
3447
+ if ($start < $len) {
3448
+ $part = $text; // OAR - Don't need this anymore, given that $start always equals zero. substr($text, $start);
3449
+ $place_text = $this->filterText($part, false);
3450
+ // modify unicode text so that extra word spacing is manually implemented (bug #)
3451
+ $cf = $this->currentFont;
3452
+ if ($this->fonts[$cf]['isUnicode'] && $wordSpaceAdjust != 0) {
3453
+ $space_scale = 1000 / $size;
3454
+ //$place_text = str_replace(' ', ') ( ) '.($this->getTextWidth($size, chr(32), $wordSpaceAdjust)*-75).' (', $place_text);
3455
+ $place_text = str_replace(' ', ' ) '.(-round($space_scale*$wordSpaceAdjust)).' (', $place_text);
3456
+ }
3457
+ $this->addContent(" /F$this->currentFontNum ".sprintf('%.1F Tf ', $size));
3458
+ $this->addContent(" [($place_text)] TJ");
3459
+ }
3460
+
3461
+ $this->addContent(' ET');
3462
+
3463
+ // if there are any open callbacks, then they should be called, to show the end of the line
3464
+ if ($this->nCallback>0) {
3465
+ for ($i = $this->nCallback;$i>0;$i--) {
3466
+ // call each function
3467
+ $tmp = $this->getTextPosition($x, $y, $angle, $size, $wordSpaceAdjust, $text);
3468
+ $info = array(
3469
+ 'x' => $tmp[0],
3470
+ 'y' => $tmp[1],
3471
+ 'angle' => $angle,
3472
+ 'status' => 'eol',
3473
+ 'p' => $this->callback[$i]['p'],
3474
+ 'nCallback' => $this->callback[$i]['nCallback'],
3475
+ 'height' => $this->callback[$i]['height'],
3476
+ 'descender' => $this->callback[$i]['descender']
3477
+ );
3478
+ $func = $this->callback[$i]['f'];
3479
+ $this->$func($info);
3480
+ }
3481
+ }
3482
+ }
3483
+
3484
+ /**
3485
+ * calculate how wide a given text string will be on a page, at a given size.
3486
+ * this can be called externally, but is alse used by the other class functions
3487
+ */
3488
+ function getTextWidth($size, $text, $word_spacing = 0, $char_spacing = 0) {
3489
+ static $ord_cache = array();
3490
+
3491
+ // this function should not change any of the settings, though it will need to
3492
+ // track any directives which change during calculation, so copy them at the start
3493
+ // and put them back at the end.
3494
+ $store_currentTextState = $this->currentTextState;
3495
+
3496
+ if (!$this->numFonts) {
3497
+ $this->selectFont($this->defaultFont);
3498
+ }
3499
+
3500
+ $text = str_replace(array("\r", "\n"), "", $text);
3501
+
3502
+ // converts a number or a float to a string so it can get the width
3503
+ $text = "$text";
3504
+
3505
+ // hmm, this is where it all starts to get tricky - use the font information to
3506
+ // calculate the width of each character, add them up and convert to user units
3507
+ $w = 0;
3508
+ $cf = $this->currentFont;
3509
+ $current_font = $this->fonts[$cf];
3510
+ $space_scale = 1000 / $size;
3511
+ $n_spaces = 0;
3512
+
3513
+ if ( $current_font['isUnicode']) {
3514
+ // for Unicode, use the code points array to calculate width rather
3515
+ // than just the string itself
3516
+ $unicode = $this->utf8toCodePointsArray($text);
3517
+
3518
+ foreach ($unicode as $char) {
3519
+ // check if we have to replace character
3520
+ if ( isset($current_font['differences'][$char])) {
3521
+ $char = $current_font['differences'][$char];
3522
+ }
3523
+
3524
+ if ( isset($current_font['C'][$char]) ) {
3525
+ $char_width = $current_font['C'][$char];
3526
+
3527
+ // add the character width
3528
+ $w += $char_width;
3529
+
3530
+ // add additional padding for space
3531
+ if ( isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space' ) { // Space
3532
+ $w += $word_spacing * $space_scale;
3533
+ $n_spaces++;
3534
+ }
3535
+ }
3536
+ }
3537
+
3538
+ // add additionnal char spacing
3539
+ if ( $char_spacing != 0 ) {
3540
+ $w += $char_spacing * $space_scale * (count($unicode) + $n_spaces);
3541
+ }
3542
+
3543
+ } else {
3544
+ // If CPDF is in Unicode mode but the current font does not support Unicode we need to convert the character set to Windows-1252
3545
+ if ( $this->isUnicode ) {
3546
+ $text = mb_convert_encoding($text, 'Windows-1252', 'UTF-8');
3547
+ }
3548
+
3549
+ $len = mb_strlen($text, 'Windows-1252');
3550
+
3551
+ for ($i = 0; $i < $len; $i++) {
3552
+ $c = $text[$i];
3553
+ $char = isset($ord_cache[$c]) ? $ord_cache[$c] : ($ord_cache[$c] = ord($c));
3554
+
3555
+ // check if we have to replace character
3556
+ if ( isset($current_font['differences'][$char])) {
3557
+ $char = $current_font['differences'][$char];
3558
+ }
3559
+
3560
+ if ( isset($current_font['C'][$char]) ) {
3561
+ $char_width = $current_font['C'][$char];
3562
+
3563
+ // add the character width
3564
+ $w += $char_width;
3565
+
3566
+ // add additional padding for space
3567
+ if ( isset($current_font['codeToName'][$char]) && $current_font['codeToName'][$char] === 'space' ) { // Space
3568
+ $w += $word_spacing * $space_scale;
3569
+ $n_spaces++;
3570
+ }
3571
+ }
3572
+ }
3573
+
3574
+ // add additionnal char spacing
3575
+ if ( $char_spacing != 0 ) {
3576
+ $w += $char_spacing * $space_scale * ($len + $n_spaces);
3577
+ }
3578
+ }
3579
+
3580
+ $this->currentTextState = $store_currentTextState;
3581
+ $this->setCurrentFont();
3582
+
3583
+ return $w*$size/1000;
3584
+ }
3585
+
3586
+ /**
3587
+ * this will be called at a new page to return the state to what it was on the
3588
+ * end of the previous page, before the stack was closed down
3589
+ * This is to get around not being able to have open 'q' across pages
3590
+ *
3591
+ */
3592
+ function saveState($pageEnd = 0) {
3593
+ if ($pageEnd) {
3594
+ // this will be called at a new page to return the state to what it was on the
3595
+ // end of the previous page, before the stack was closed down
3596
+ // This is to get around not being able to have open 'q' across pages
3597
+ $opt = $this->stateStack[$pageEnd];
3598
+ // ok to use this as stack starts numbering at 1
3599
+ $this->setColor($opt['col'], true);
3600
+ $this->setStrokeColor($opt['str'], true);
3601
+ $this->addContent("\n".$opt['lin']);
3602
+ // $this->currentLineStyle = $opt['lin'];
3603
+ } else {
3604
+ $this->nStateStack++;
3605
+ $this->stateStack[$this->nStateStack] = array(
3606
+ 'col' => $this->currentColour,
3607
+ 'str' => $this->currentStrokeColour,
3608
+ 'lin' => $this->currentLineStyle
3609
+ );
3610
+ }
3611
+
3612
+ $this->save();
3613
+ }
3614
+
3615
+ /**
3616
+ * restore a previously saved state
3617
+ */
3618
+ function restoreState($pageEnd = 0) {
3619
+ if (!$pageEnd) {
3620
+ $n = $this->nStateStack;
3621
+ $this->currentColour = $this->stateStack[$n]['col'];
3622
+ $this->currentStrokeColour = $this->stateStack[$n]['str'];
3623
+ $this->addContent("\n".$this->stateStack[$n]['lin']);
3624
+ $this->currentLineStyle = $this->stateStack[$n]['lin'];
3625
+ $this->stateStack[$n] = null;
3626
+ unset($this->stateStack[$n]);
3627
+ $this->nStateStack--;
3628
+ }
3629
+
3630
+ $this->restore();
3631
+ }
3632
+
3633
+ /**
3634
+ * make a loose object, the output will go into this object, until it is closed, then will revert to
3635
+ * the current one.
3636
+ * this object will not appear until it is included within a page.
3637
+ * the function will return the object number
3638
+ */
3639
+ function openObject() {
3640
+ $this->nStack++;
3641
+ $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
3642
+ // add a new object of the content type, to hold the data flow
3643
+ $this->numObj++;
3644
+ $this->o_contents($this->numObj, 'new');
3645
+ $this->currentContents = $this->numObj;
3646
+ $this->looseObjects[$this->numObj] = 1;
3647
+
3648
+ return $this->numObj;
3649
+ }
3650
+
3651
+ /**
3652
+ * open an existing object for editing
3653
+ */
3654
+ function reopenObject($id) {
3655
+ $this->nStack++;
3656
+ $this->stack[$this->nStack] = array('c' => $this->currentContents, 'p' => $this->currentPage);
3657
+ $this->currentContents = $id;
3658
+
3659
+ // also if this object is the primary contents for a page, then set the current page to its parent
3660
+ if (isset($this->objects[$id]['onPage'])) {
3661
+ $this->currentPage = $this->objects[$id]['onPage'];
3662
+ }
3663
+ }
3664
+
3665
+ /**
3666
+ * close an object
3667
+ */
3668
+ function closeObject() {
3669
+ // close the object, as long as there was one open in the first place, which will be indicated by
3670
+ // an objectId on the stack.
3671
+ if ($this->nStack>0) {
3672
+ $this->currentContents = $this->stack[$this->nStack]['c'];
3673
+ $this->currentPage = $this->stack[$this->nStack]['p'];
3674
+ $this->nStack--;
3675
+ // easier to probably not worry about removing the old entries, they will be overwritten
3676
+ // if there are new ones.
3677
+ }
3678
+ }
3679
+
3680
+ /**
3681
+ * stop an object from appearing on pages from this point on
3682
+ */
3683
+ function stopObject($id) {
3684
+ // if an object has been appearing on pages up to now, then stop it, this page will
3685
+ // be the last one that could contian it.
3686
+ if (isset($this->addLooseObjects[$id])) {
3687
+ $this->addLooseObjects[$id] = '';
3688
+ }
3689
+ }
3690
+
3691
+ /**
3692
+ * after an object has been created, it wil only show if it has been added, using this function.
3693
+ */
3694
+ function addObject($id, $options = 'add') {
3695
+ // add the specified object to the page
3696
+ if (isset($this->looseObjects[$id]) && $this->currentContents != $id) {
3697
+ // then it is a valid object, and it is not being added to itself
3698
+ switch ($options) {
3699
+ case 'all':
3700
+ // then this object is to be added to this page (done in the next block) and
3701
+ // all future new pages.
3702
+ $this->addLooseObjects[$id] = 'all';
3703
+
3704
+ case 'add':
3705
+ if (isset($this->objects[$this->currentContents]['onPage'])) {
3706
+ // then the destination contents is the primary for the page
3707
+ // (though this object is actually added to that page)
3708
+ $this->o_page($this->objects[$this->currentContents]['onPage'], 'content', $id);
3709
+ }
3710
+ break;
3711
+
3712
+ case 'even':
3713
+ $this->addLooseObjects[$id] = 'even';
3714
+ $pageObjectId = $this->objects[$this->currentContents]['onPage'];
3715
+ if ($this->objects[$pageObjectId]['info']['pageNum']%2 == 0) {
3716
+ $this->addObject($id);
3717
+ // hacky huh :)
3718
+ }
3719
+ break;
3720
+
3721
+ case 'odd':
3722
+ $this->addLooseObjects[$id] = 'odd';
3723
+ $pageObjectId = $this->objects[$this->currentContents]['onPage'];
3724
+ if ($this->objects[$pageObjectId]['info']['pageNum']%2 == 1) {
3725
+ $this->addObject($id);
3726
+ // hacky huh :)
3727
+ }
3728
+ break;
3729
+
3730
+ case 'next':
3731
+ $this->addLooseObjects[$id] = 'all';
3732
+ break;
3733
+
3734
+ case 'nexteven':
3735
+ $this->addLooseObjects[$id] = 'even';
3736
+ break;
3737
+
3738
+ case 'nextodd':
3739
+ $this->addLooseObjects[$id] = 'odd';
3740
+ break;
3741
+ }
3742
+ }
3743
+ }
3744
+
3745
+ /**
3746
+ * return a storable representation of a specific object
3747
+ */
3748
+ function serializeObject($id) {
3749
+ if ( array_key_exists($id, $this->objects)) {
3750
+ return serialize($this->objects[$id]);
3751
+ }
3752
+ }
3753
+
3754
+ /**
3755
+ * restore an object from its stored representation. returns its new object id.
3756
+ */
3757
+ function restoreSerializedObject($obj) {
3758
+ $obj_id = $this->openObject();
3759
+ $this->objects[$obj_id] = unserialize($obj);
3760
+ $this->closeObject();
3761
+ return $obj_id;
3762
+ }
3763
+
3764
+ /**
3765
+ * add content to the documents info object
3766
+ */
3767
+ function addInfo($label, $value = 0) {
3768
+ // this will only work if the label is one of the valid ones.
3769
+ // modify this so that arrays can be passed as well.
3770
+ // if $label is an array then assume that it is key => value pairs
3771
+ // else assume that they are both scalar, anything else will probably error
3772
+ if (is_array($label)) {
3773
+ foreach ($label as $l => $v) {
3774
+ $this->o_info($this->infoObject, $l, $v);
3775
+ }
3776
+ } else {
3777
+ $this->o_info($this->infoObject, $label, $value);
3778
+ }
3779
+ }
3780
+
3781
+ /**
3782
+ * set the viewer preferences of the document, it is up to the browser to obey these.
3783
+ */
3784
+ function setPreferences($label, $value = 0) {
3785
+ // this will only work if the label is one of the valid ones.
3786
+ if (is_array($label)) {
3787
+ foreach ($label as $l => $v) {
3788
+ $this->o_catalog($this->catalogId, 'viewerPreferences', array($l => $v));
3789
+ }
3790
+ } else {
3791
+ $this->o_catalog($this->catalogId, 'viewerPreferences', array($label => $value));
3792
+ }
3793
+ }
3794
+
3795
+ /**
3796
+ * extract an integer from a position in a byte stream
3797
+ */
3798
+ private function getBytes(&$data, $pos, $num) {
3799
+ // return the integer represented by $num bytes from $pos within $data
3800
+ $ret = 0;
3801
+ for ($i = 0; $i < $num; $i++) {
3802
+ $ret *= 256;
3803
+ $ret += ord($data[$pos+$i]);
3804
+ }
3805
+
3806
+ return $ret;
3807
+ }
3808
+
3809
+ /**
3810
+ * Check if image already added to pdf image directory.
3811
+ * If yes, need not to create again (pass empty data)
3812
+ */
3813
+ function image_iscached($imgname) {
3814
+ return isset($this->imagelist[$imgname]);
3815
+ }
3816
+
3817
+ /**
3818
+ * add a PNG image into the document, from a GD object
3819
+ * this should work with remote files
3820
+ *
3821
+ * @param string $file The PNG file
3822
+ * @param float $x X position
3823
+ * @param float $y Y position
3824
+ * @param float $w Width
3825
+ * @param float $h Height
3826
+ * @param resource $img A GD resource
3827
+ * @param bool $is_mask true if the image is a mask
3828
+ * @param bool $mask true if the image is masked
3829
+ */
3830
+ function addImagePng($file, $x, $y, $w = 0.0, $h = 0.0, &$img, $is_mask = false, $mask = null) {
3831
+ //if already cached, need not to read again
3832
+ if ( isset($this->imagelist[$file]) ) {
3833
+ $data = null;
3834
+ }
3835
+ else {
3836
+ // Example for transparency handling on new image. Retain for current image
3837
+ // $tIndex = imagecolortransparent($img);
3838
+ // if ($tIndex > 0) {
3839
+ // $tColor = imagecolorsforindex($img, $tIndex);
3840
+ // $new_tIndex = imagecolorallocate($new_img, $tColor['red'], $tColor['green'], $tColor['blue']);
3841
+ // imagefill($new_img, 0, 0, $new_tIndex);
3842
+ // imagecolortransparent($new_img, $new_tIndex);
3843
+ // }
3844
+ // blending mode (literal/blending) on drawing into current image. not relevant when not saved or not drawn
3845
+ //imagealphablending($img, true);
3846
+
3847
+ //default, but explicitely set to ensure pdf compatibility
3848
+ imagesavealpha($img, false/*!$is_mask && !$mask*/);
3849
+
3850
+ $error = 0;
3851
+ //DEBUG_IMG_TEMP
3852
+ //debugpng
3853
+ if (DEBUGPNG) print '[addImagePng '.$file.']';
3854
+
3855
+ ob_start();
3856
+ @imagepng($img);
3857
+ $data = ob_get_clean();
3858
+
3859
+ if ($data == '') {
3860
+ $error = 1;
3861
+ $errormsg = 'trouble writing file from GD';
3862
+ //DEBUG_IMG_TEMP
3863
+ //debugpng
3864
+ if (DEBUGPNG) print 'trouble writing file from GD';
3865
+ }
3866
+
3867
+ if ($error) {
3868
+ $this->addMessage('PNG error - ('.$file.') '.$errormsg);
3869
+ return;
3870
+ }
3871
+ } //End isset($this->imagelist[$file]) (png Duplicate removal)
3872
+
3873
+ $this->addPngFromBuf($file, $x, $y, $w, $h, $data, $is_mask, $mask);
3874
+ }
3875
+
3876
+ protected function addImagePngAlpha($file, $x, $y, $w, $h, $byte) {
3877
+ // generate images
3878
+ $img = imagecreatefrompng($file);
3879
+
3880
+ if ($img === false) {
3881
+ return;
3882
+ }
3883
+
3884
+ // FIXME The pixel transformation doesn't work well with 8bit PNGs
3885
+ $eight_bit = ($byte & 4) !== 4;
3886
+
3887
+ $wpx = imagesx($img);
3888
+ $hpx = imagesy($img);
3889
+
3890
+ imagesavealpha($img, false);
3891
+
3892
+ // create temp alpha file
3893
+ $tempfile_alpha = tempnam($this->tmp, "cpdf_img_");
3894
+ @unlink($tempfile_alpha);
3895
+ $tempfile_alpha = "$tempfile_alpha.png";
3896
+
3897
+ // create temp plain file
3898
+ $tempfile_plain = tempnam($this->tmp, "cpdf_img_");
3899
+ @unlink($tempfile_plain);
3900
+ $tempfile_plain = "$tempfile_plain.png";
3901
+
3902
+ $imgalpha = imagecreate($wpx, $hpx);
3903
+ imagesavealpha($imgalpha, false);
3904
+
3905
+ // generate gray scale palette (0 -> 255)
3906
+ for ($c = 0; $c < 256; ++$c) {
3907
+ imagecolorallocate($imgalpha, $c, $c, $c);
3908
+ }
3909
+
3910
+ // Use PECL gmagick + Graphics Magic to process transparent PNG images
3911
+ if (extension_loaded("gmagick")) {
3912
+ $gmagick = new Gmagick($file);
3913
+ $gmagick->setimageformat('png');
3914
+
3915
+ // Get opacity channel (negative of alpha channel)
3916
+ $alpha_channel_neg = clone $gmagick;
3917
+ $alpha_channel_neg->separateimagechannel(Gmagick::CHANNEL_OPACITY);
3918
+
3919
+ // Negate opacity channel
3920
+ $alpha_channel = new Gmagick();
3921
+ $alpha_channel->newimage($wpx, $hpx, "#FFFFFF", "png");
3922
+ $alpha_channel->compositeimage($alpha_channel_neg, Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
3923
+ $alpha_channel->separateimagechannel(Gmagick::CHANNEL_RED);
3924
+ $alpha_channel->writeimage($tempfile_alpha);
3925
+
3926
+ // Cast to 8bit+palette
3927
+ $imgalpha_ = imagecreatefrompng($tempfile_alpha);
3928
+ imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
3929
+ imagedestroy($imgalpha_);
3930
+ imagepng($imgalpha, $tempfile_alpha);
3931
+
3932
+ // Make opaque image
3933
+ $color_channels = new Gmagick();
3934
+ $color_channels->newimage($wpx, $hpx, "#FFFFFF", "png");
3935
+ $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYRED, 0, 0);
3936
+ $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYGREEN, 0, 0);
3937
+ $color_channels->compositeimage($gmagick, Gmagick::COMPOSITE_COPYBLUE, 0, 0);
3938
+ $color_channels->writeimage($tempfile_plain);
3939
+
3940
+ $imgplain = imagecreatefrompng($tempfile_plain);
3941
+ }
3942
+
3943
+ // Use PECL imagick + ImageMagic to process transparent PNG images
3944
+ elseif (extension_loaded("imagick")) {
3945
+ $imagick = new Imagick($file);
3946
+ $imagick->setFormat('png');
3947
+
3948
+ // Get opacity channel (negative of alpha channel)
3949
+ $alpha_channel = clone $imagick;
3950
+ $alpha_channel->separateImageChannel(Imagick::CHANNEL_ALPHA);
3951
+ $alpha_channel->negateImage(true);
3952
+ $alpha_channel->writeImage($tempfile_alpha);
3953
+
3954
+ // Cast to 8bit+palette
3955
+ $imgalpha_ = imagecreatefrompng($tempfile_alpha);
3956
+ imagecopy($imgalpha, $imgalpha_, 0, 0, 0, 0, $wpx, $hpx);
3957
+ imagedestroy($imgalpha_);
3958
+ imagepng($imgalpha, $tempfile_alpha);
3959
+
3960
+ // Make opaque image
3961
+ $color_channels = new Imagick();
3962
+ $color_channels->newImage($wpx, $hpx, "#FFFFFF", "png");
3963
+ $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYRED, 0, 0);
3964
+ $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYGREEN, 0, 0);
3965
+ $color_channels->compositeImage($imagick, Imagick::COMPOSITE_COPYBLUE, 0, 0);
3966
+ $color_channels->writeImage($tempfile_plain);
3967
+
3968
+ $imgplain = imagecreatefrompng($tempfile_plain);
3969
+ }
3970
+ else {
3971
+ // allocated colors cache
3972
+ $allocated_colors = array();
3973
+
3974
+ // extract alpha channel
3975
+ for ($xpx = 0; $xpx < $wpx; ++$xpx) {
3976
+ for ($ypx = 0; $ypx < $hpx; ++$ypx) {
3977
+ $color = imagecolorat($img, $xpx, $ypx);
3978
+ $col = imagecolorsforindex($img, $color);
3979
+ $alpha = $col['alpha'];
3980
+
3981
+ if ($eight_bit) {
3982
+ // with gamma correction
3983
+ $gammacorr = 2.2;
3984
+ $pixel = pow((((127 - $alpha) * 255 / 127) / 255), $gammacorr) * 255;
3985
+ }
3986
+
3987
+ else {
3988
+ // without gamma correction
3989
+ $pixel = (127 - $alpha) * 2;
3990
+
3991
+ $key = $col['red'].$col['green'].$col['blue'];
3992
+
3993
+ if (!isset($allocated_colors[$key])) {
3994
+ $pixel_img = imagecolorallocate($img, $col['red'], $col['green'], $col['blue']);
3995
+ $allocated_colors[$key] = $pixel_img;
3996
+ }
3997
+ else {
3998
+ $pixel_img = $allocated_colors[$key];
3999
+ }
4000
+
4001
+ imagesetpixel($img, $xpx, $ypx, $pixel_img);
4002
+ }
4003
+
4004
+ imagesetpixel($imgalpha, $xpx, $ypx, $pixel);
4005
+ }
4006
+ }
4007
+
4008
+ // extract image without alpha channel
4009
+ $imgplain = imagecreatetruecolor($wpx, $hpx);
4010
+ imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
4011
+ imagedestroy($img);
4012
+
4013
+ imagepng($imgalpha, $tempfile_alpha);
4014
+ imagepng($imgplain, $tempfile_plain);
4015
+ }
4016
+
4017
+ // embed mask image
4018
+ $this->addImagePng($tempfile_alpha, $x, $y, $w, $h, $imgalpha, true);
4019
+ imagedestroy($imgalpha);
4020
+
4021
+ // embed image, masked with previously embedded mask
4022
+ $this->addImagePng($tempfile_plain, $x, $y, $w, $h, $imgplain, false, true);
4023
+ imagedestroy($imgplain);
4024
+
4025
+ // remove temp files
4026
+ unlink($tempfile_alpha);
4027
+ unlink($tempfile_plain);
4028
+ }
4029
+
4030
+ /**
4031
+ * add a PNG image into the document, from a file
4032
+ * this should work with remote files
4033
+ */
4034
+ function addPngFromFile($file, $x, $y, $w = 0, $h = 0) {
4035
+ //if already cached, need not to read again
4036
+ if ( isset($this->imagelist[$file]) ) {
4037
+ $img = null;
4038
+ }
4039
+
4040
+ else {
4041
+ $info = file_get_contents ($file, false, null, 24, 5);
4042
+ $meta = unpack("CbitDepth/CcolorType/CcompressionMethod/CfilterMethod/CinterlaceMethod", $info);
4043
+ $bit_depth = $meta["bitDepth"];
4044
+ $color_type = $meta["colorType"];
4045
+
4046
+ // http://www.w3.org/TR/PNG/#11IHDR
4047
+ // 3 => indexed
4048
+ // 4 => greyscale with alpha
4049
+ // 6 => fullcolor with alpha
4050
+ $is_alpha = in_array($color_type, array(4, 6)) || ($color_type == 3 && $bit_depth != 4);
4051
+
4052
+ if ($is_alpha) { // exclude grayscale alpha
4053
+ return $this->addImagePngAlpha($file, $x, $y, $w, $h, $color_type);
4054
+ }
4055
+
4056
+ //png files typically contain an alpha channel.
4057
+ //pdf file format or class.pdf does not support alpha blending.
4058
+ //on alpha blended images, more transparent areas have a color near black.
4059
+ //This appears in the result on not storing the alpha channel.
4060
+ //Correct would be the box background image or its parent when transparent.
4061
+ //But this would make the image dependent on the background.
4062
+ //Therefore create an image with white background and copy in
4063
+ //A more natural background than black is white.
4064
+ //Therefore create an empty image with white background and merge the
4065
+ //image in with alpha blending.
4066
+ $imgtmp = @imagecreatefrompng($file);
4067
+ if (!$imgtmp) {
4068
+ return;
4069
+ }
4070
+ $sx = imagesx($imgtmp);
4071
+ $sy = imagesy($imgtmp);
4072
+ $img = imagecreatetruecolor($sx,$sy);
4073
+ imagealphablending($img, true);
4074
+
4075
+ // @todo is it still needed ??
4076
+ $ti = imagecolortransparent($imgtmp);
4077
+ if ($ti >= 0) {
4078
+ $tc = imagecolorsforindex($imgtmp,$ti);
4079
+ $ti = imagecolorallocate($img,$tc['red'],$tc['green'],$tc['blue']);
4080
+ imagefill($img,0,0,$ti);
4081
+ imagecolortransparent($img, $ti);
4082
+ } else {
4083
+ imagefill($img,1,1,imagecolorallocate($img,255,255,255));
4084
+ }
4085
+
4086
+ imagecopy($img,$imgtmp,0,0,0,0,$sx,$sy);
4087
+ imagedestroy($imgtmp);
4088
+ }
4089
+ $this->addImagePng($file, $x, $y, $w, $h, $img);
4090
+
4091
+ if ( $img ) {
4092
+ imagedestroy($img);
4093
+ }
4094
+ }
4095
+
4096
+ /**
4097
+ * add a PNG image into the document, from a memory buffer of the file
4098
+ */
4099
+ function addPngFromBuf($file, $x, $y, $w = 0.0, $h = 0.0, &$data, $is_mask = false, $mask = null) {
4100
+ if ( isset($this->imagelist[$file]) ) {
4101
+ $data = null;
4102
+ $info['width'] = $this->imagelist[$file]['w'];
4103
+ $info['height'] = $this->imagelist[$file]['h'];
4104
+ $label = $this->imagelist[$file]['label'];
4105
+ }
4106
+
4107
+ else {
4108
+ if ($data == null) {
4109
+ $this->addMessage('addPngFromBuf error - data not present!');
4110
+ return;
4111
+ }
4112
+
4113
+ $error = 0;
4114
+
4115
+ if (!$error) {
4116
+ $header = chr(137) .chr(80) .chr(78) .chr(71) .chr(13) .chr(10) .chr(26) .chr(10);
4117
+
4118
+ if (mb_substr($data, 0, 8, '8bit') != $header) {
4119
+ $error = 1;
4120
+
4121
+ if (DEBUGPNG) print '[addPngFromFile this file does not have a valid header '.$file.']';
4122
+
4123
+ $errormsg = 'this file does not have a valid header';
4124
+ }
4125
+ }
4126
+
4127
+ if (!$error) {
4128
+ // set pointer
4129
+ $p = 8;
4130
+ $len = mb_strlen($data, '8bit');
4131
+
4132
+ // cycle through the file, identifying chunks
4133
+ $haveHeader = 0;
4134
+ $info = array();
4135
+ $idata = '';
4136
+ $pdata = '';
4137
+
4138
+ while ($p < $len) {
4139
+ $chunkLen = $this->getBytes($data, $p, 4);
4140
+ $chunkType = mb_substr($data, $p+4, 4, '8bit');
4141
+
4142
+ switch ($chunkType) {
4143
+ case 'IHDR':
4144
+ // this is where all the file information comes from
4145
+ $info['width'] = $this->getBytes($data, $p+8, 4);
4146
+ $info['height'] = $this->getBytes($data, $p+12, 4);
4147
+ $info['bitDepth'] = ord($data[$p+16]);
4148
+ $info['colorType'] = ord($data[$p+17]);
4149
+ $info['compressionMethod'] = ord($data[$p+18]);
4150
+ $info['filterMethod'] = ord($data[$p+19]);
4151
+ $info['interlaceMethod'] = ord($data[$p+20]);
4152
+
4153
+ //print_r($info);
4154
+ $haveHeader = 1;
4155
+ if ($info['compressionMethod'] != 0) {
4156
+ $error = 1;
4157
+
4158
+ //debugpng
4159
+ if (DEBUGPNG) print '[addPngFromFile unsupported compression method '.$file.']';
4160
+
4161
+ $errormsg = 'unsupported compression method';
4162
+ }
4163
+
4164
+ if ($info['filterMethod'] != 0) {
4165
+ $error = 1;
4166
+
4167
+ //debugpng
4168
+ if (DEBUGPNG) print '[addPngFromFile unsupported filter method '.$file.']';
4169
+
4170
+ $errormsg = 'unsupported filter method';
4171
+ }
4172
+ break;
4173
+
4174
+ case 'PLTE':
4175
+ $pdata.= mb_substr($data, $p+8, $chunkLen, '8bit');
4176
+ break;
4177
+
4178
+ case 'IDAT':
4179
+ $idata.= mb_substr($data, $p+8, $chunkLen, '8bit');
4180
+ break;
4181
+
4182
+ case 'tRNS':
4183
+ //this chunk can only occur once and it must occur after the PLTE chunk and before IDAT chunk
4184
+ //print "tRNS found, color type = ".$info['colorType']."\n";
4185
+ $transparency = array();
4186
+
4187
+ switch ($info['colorType']) {
4188
+ // indexed color, rbg
4189
+ case 3:
4190
+ /* corresponding to entries in the plte chunk
4191
+ Alpha for palette index 0: 1 byte
4192
+ Alpha for palette index 1: 1 byte
4193
+ ...etc...
4194
+ */
4195
+ // there will be one entry for each palette entry. up until the last non-opaque entry.
4196
+ // set up an array, stretching over all palette entries which will be o (opaque) or 1 (transparent)
4197
+ $transparency['type'] = 'indexed';
4198
+ $trans = 0;
4199
+
4200
+ for ($i = $chunkLen;$i >= 0;$i--) {
4201
+ if (ord($data[$p+8+$i]) == 0) {
4202
+ $trans = $i;
4203
+ }
4204
+ }
4205
+
4206
+ $transparency['data'] = $trans;
4207
+ break;
4208
+
4209
+ // grayscale
4210
+ case 0:
4211
+ /* corresponding to entries in the plte chunk
4212
+ Gray: 2 bytes, range 0 .. (2^bitdepth)-1
4213
+ */
4214
+ // $transparency['grayscale'] = $this->PRVT_getBytes($data,$p+8,2); // g = grayscale
4215
+ $transparency['type'] = 'indexed';
4216
+ $transparency['data'] = ord($data[$p+8+1]);
4217
+ break;
4218
+
4219
+ // truecolor
4220
+ case 2:
4221
+ /* corresponding to entries in the plte chunk
4222
+ Red: 2 bytes, range 0 .. (2^bitdepth)-1
4223
+ Green: 2 bytes, range 0 .. (2^bitdepth)-1
4224
+ Blue: 2 bytes, range 0 .. (2^bitdepth)-1
4225
+ */
4226
+ $transparency['r'] = $this->getBytes($data, $p+8, 2);
4227
+ // r from truecolor
4228
+ $transparency['g'] = $this->getBytes($data, $p+10, 2);
4229
+ // g from truecolor
4230
+ $transparency['b'] = $this->getBytes($data, $p+12, 2);
4231
+ // b from truecolor
4232
+
4233
+ $transparency['type'] = 'color-key';
4234
+ break;
4235
+
4236
+ //unsupported transparency type
4237
+ default:
4238
+ if (DEBUGPNG) print '[addPngFromFile unsupported transparency type '.$file.']';
4239
+ break;
4240
+ }
4241
+
4242
+ // KS End new code
4243
+ break;
4244
+
4245
+ default:
4246
+ break;
4247
+ }
4248
+
4249
+ $p += $chunkLen+12;
4250
+ }
4251
+
4252
+ if (!$haveHeader) {
4253
+ $error = 1;
4254
+
4255
+ //debugpng
4256
+ if (DEBUGPNG) print '[addPngFromFile information header is missing '.$file.']';
4257
+
4258
+ $errormsg = 'information header is missing';
4259
+ }
4260
+
4261
+ if (isset($info['interlaceMethod']) && $info['interlaceMethod']) {
4262
+ $error = 1;
4263
+
4264
+ //debugpng
4265
+ if (DEBUGPNG) print '[addPngFromFile no support for interlaced images in pdf '.$file.']';
4266
+
4267
+ $errormsg = 'There appears to be no support for interlaced images in pdf.';
4268
+ }
4269
+ }
4270
+
4271
+ if (!$error && $info['bitDepth'] > 8) {
4272
+ $error = 1;
4273
+
4274
+ //debugpng
4275
+ if (DEBUGPNG) print '[addPngFromFile bit depth of 8 or less is supported '.$file.']';
4276
+
4277
+ $errormsg = 'only bit depth of 8 or less is supported';
4278
+ }
4279
+
4280
+ if (!$error) {
4281
+ switch ($info['colorType']) {
4282
+ case 3:
4283
+ $color = 'DeviceRGB';
4284
+ $ncolor = 1;
4285
+ break;
4286
+
4287
+ case 2:
4288
+ $color = 'DeviceRGB';
4289
+ $ncolor = 3;
4290
+ break;
4291
+
4292
+ case 0:
4293
+ $color = 'DeviceGray';
4294
+ $ncolor = 1;
4295
+ break;
4296
+
4297
+ default:
4298
+ $error = 1;
4299
+
4300
+ //debugpng
4301
+ if (DEBUGPNG) print '[addPngFromFile alpha channel not supported: '.$info['colorType'].' '.$file.']';
4302
+
4303
+ $errormsg = 'transparancey alpha channel not supported, transparency only supported for palette images.';
4304
+ }
4305
+ }
4306
+
4307
+ if ($error) {
4308
+ $this->addMessage('PNG error - ('.$file.') '.$errormsg);
4309
+ return;
4310
+ }
4311
+
4312
+ //print_r($info);
4313
+ // so this image is ok... add it in.
4314
+ $this->numImages++;
4315
+ $im = $this->numImages;
4316
+ $label = "I$im";
4317
+ $this->numObj++;
4318
+
4319
+ // $this->o_image($this->numObj,'new',array('label' => $label,'data' => $idata,'iw' => $w,'ih' => $h,'type' => 'png','ic' => $info['width']));
4320
+ $options = array(
4321
+ 'label' => $label,
4322
+ 'data' => $idata,
4323
+ 'bitsPerComponent' => $info['bitDepth'],
4324
+ 'pdata' => $pdata,
4325
+ 'iw' => $info['width'],
4326
+ 'ih' => $info['height'],
4327
+ 'type' => 'png',
4328
+ 'color' => $color,
4329
+ 'ncolor' => $ncolor,
4330
+ 'masked' => $mask,
4331
+ 'isMask' => $is_mask,
4332
+ );
4333
+
4334
+ if (isset($transparency)) {
4335
+ $options['transparency'] = $transparency;
4336
+ }
4337
+
4338
+ $this->o_image($this->numObj, 'new', $options);
4339
+ $this->imagelist[$file] = array('label' =>$label, 'w' => $info['width'], 'h' => $info['height']);
4340
+ }
4341
+
4342
+ if ($is_mask) {
4343
+ return;
4344
+ }
4345
+
4346
+ if ($w <= 0 && $h <= 0) {
4347
+ $w = $info['width'];
4348
+ $h = $info['height'];
4349
+ }
4350
+
4351
+ if ($w <= 0) {
4352
+ $w = $h/$info['height']*$info['width'];
4353
+ }
4354
+
4355
+ if ($h <= 0) {
4356
+ $h = $w*$info['height']/$info['width'];
4357
+ }
4358
+
4359
+ $this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ", $w, $h, $x, $y, $label));
4360
+ }
4361
+
4362
+ /**
4363
+ * add a JPEG image into the document, from a file
4364
+ */
4365
+ function addJpegFromFile($img, $x, $y, $w = 0, $h = 0) {
4366
+ // attempt to add a jpeg image straight from a file, using no GD commands
4367
+ // note that this function is unable to operate on a remote file.
4368
+
4369
+ if (!file_exists($img)) {
4370
+ return;
4371
+ }
4372
+
4373
+ if ( $this->image_iscached($img) ) {
4374
+ $data = null;
4375
+ $imageWidth = $this->imagelist[$img]['w'];
4376
+ $imageHeight = $this->imagelist[$img]['h'];
4377
+ $channels = $this->imagelist[$img]['c'];
4378
+ }
4379
+ else {
4380
+ $tmp = getimagesize($img);
4381
+ $imageWidth = $tmp[0];
4382
+ $imageHeight = $tmp[1];
4383
+
4384
+ if ( isset($tmp['channels']) ) {
4385
+ $channels = $tmp['channels'];
4386
+ } else {
4387
+ $channels = 3;
4388
+ }
4389
+
4390
+ $data = file_get_contents($img);
4391
+ }
4392
+
4393
+ if ($w <= 0 && $h <= 0) {
4394
+ $w = $imageWidth;
4395
+ }
4396
+
4397
+ if ($w == 0) {
4398
+ $w = $h/$imageHeight*$imageWidth;
4399
+ }
4400
+
4401
+ if ($h == 0) {
4402
+ $h = $w*$imageHeight/$imageWidth;
4403
+ }
4404
+
4405
+ $this->addJpegImage_common($data, $x, $y, $w, $h, $imageWidth, $imageHeight, $channels, $img);
4406
+ }
4407
+
4408
+ /**
4409
+ * common code used by the two JPEG adding functions
4410
+ */
4411
+ private function addJpegImage_common(&$data, $x, $y, $w = 0, $h = 0, $imageWidth, $imageHeight, $channels = 3, $imgname) {
4412
+ if ( $this->image_iscached($imgname) ) {
4413
+ $label = $this->imagelist[$imgname]['label'];
4414
+ //debugpng
4415
+ //if (DEBUGPNG) print '[addJpegImage_common Duplicate '.$imgname.']';
4416
+
4417
+ } else {
4418
+ if ($data == null) {
4419
+ $this->addMessage('addJpegImage_common error - ('.$imgname.') data not present!');
4420
+ return;
4421
+ }
4422
+
4423
+ // note that this function is not to be called externally
4424
+ // it is just the common code between the GD and the file options
4425
+ $this->numImages++;
4426
+ $im = $this->numImages;
4427
+ $label = "I$im";
4428
+ $this->numObj++;
4429
+
4430
+ $this->o_image($this->numObj, 'new', array(
4431
+ 'label' => $label,
4432
+ 'data' => &$data,
4433
+ 'iw' => $imageWidth,
4434
+ 'ih' => $imageHeight,
4435
+ 'channels' => $channels
4436
+ ));
4437
+
4438
+ $this->imagelist[$imgname] = array('label' =>$label, 'w' => $imageWidth, 'h' => $imageHeight, 'c'=> $channels );
4439
+ }
4440
+
4441
+ $this->addContent(sprintf("\nq\n%.3F 0 0 %.3F %.3F %.3F cm /%s Do\nQ ", $w, $h, $x, $y, $label));
4442
+ }
4443
+
4444
+ /**
4445
+ * specify where the document should open when it first starts
4446
+ */
4447
+ function openHere($style, $a = 0, $b = 0, $c = 0) {
4448
+ // this function will open the document at a specified page, in a specified style
4449
+ // the values for style, and the required paramters are:
4450
+ // 'XYZ' left, top, zoom
4451
+ // 'Fit'
4452
+ // 'FitH' top
4453
+ // 'FitV' left
4454
+ // 'FitR' left,bottom,right
4455
+ // 'FitB'
4456
+ // 'FitBH' top
4457
+ // 'FitBV' left
4458
+ $this->numObj++;
4459
+ $this->o_destination($this->numObj, 'new', array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c));
4460
+ $id = $this->catalogId;
4461
+ $this->o_catalog($id, 'openHere', $this->numObj);
4462
+ }
4463
+
4464
+ /**
4465
+ * Add JavaScript code to the PDF document
4466
+ *
4467
+ * @param string $code
4468
+ * @return void
4469
+ */
4470
+ function addJavascript($code) {
4471
+ $this->javascript .= $code;
4472
+ }
4473
+
4474
+ /**
4475
+ * create a labelled destination within the document
4476
+ */
4477
+ function addDestination($label, $style, $a = 0, $b = 0, $c = 0) {
4478
+ // associates the given label with the destination, it is done this way so that a destination can be specified after
4479
+ // it has been linked to
4480
+ // styles are the same as the 'openHere' function
4481
+ $this->numObj++;
4482
+ $this->o_destination($this->numObj, 'new', array('page' => $this->currentPage, 'type' => $style, 'p1' => $a, 'p2' => $b, 'p3' => $c));
4483
+ $id = $this->numObj;
4484
+
4485
+ // store the label->idf relationship, note that this means that labels can be used only once
4486
+ $this->destinations["$label"] = $id;
4487
+ }
4488
+
4489
+ /**
4490
+ * define font families, this is used to initialize the font families for the default fonts
4491
+ * and for the user to add new ones for their fonts. The default bahavious can be overridden should
4492
+ * that be desired.
4493
+ */
4494
+ function setFontFamily($family, $options = '') {
4495
+ if (!is_array($options)) {
4496
+ if ($family === 'init') {
4497
+ // set the known family groups
4498
+ // these font families will be used to enable bold and italic markers to be included
4499
+ // within text streams. html forms will be used... <b></b> <i></i>
4500
+ $this->fontFamilies['Helvetica.afm'] =
4501
+ array(
4502
+ 'b' => 'Helvetica-Bold.afm',
4503
+ 'i' => 'Helvetica-Oblique.afm',
4504
+ 'bi' => 'Helvetica-BoldOblique.afm',
4505
+ 'ib' => 'Helvetica-BoldOblique.afm',
4506
+ );
4507
+
4508
+ $this->fontFamilies['Courier.afm'] =
4509
+ array(
4510
+ 'b' => 'Courier-Bold.afm',
4511
+ 'i' => 'Courier-Oblique.afm',
4512
+ 'bi' => 'Courier-BoldOblique.afm',
4513
+ 'ib' => 'Courier-BoldOblique.afm',
4514
+ );
4515
+
4516
+ $this->fontFamilies['Times-Roman.afm'] =
4517
+ array(
4518
+ 'b' => 'Times-Bold.afm',
4519
+ 'i' => 'Times-Italic.afm',
4520
+ 'bi' => 'Times-BoldItalic.afm',
4521
+ 'ib' => 'Times-BoldItalic.afm',
4522
+ );
4523
+ }
4524
+ } else {
4525
+
4526
+ // the user is trying to set a font family
4527
+ // note that this can also be used to set the base ones to something else
4528
+ if (mb_strlen($family)) {
4529
+ $this->fontFamilies[$family] = $options;
4530
+ }
4531
+ }
4532
+ }
4533
+
4534
+ /**
4535
+ * used to add messages for use in debugging
4536
+ */
4537
+ function addMessage($message) {
4538
+ $this->messages.= $message."\n";
4539
+ }
4540
+
4541
+ /**
4542
+ * a few functions which should allow the document to be treated transactionally.
4543
+ */
4544
+ function transaction($action) {
4545
+ switch ($action) {
4546
+ case 'start':
4547
+ // store all the data away into the checkpoint variable
4548
+ $data = get_object_vars($this);
4549
+ $this->checkpoint = $data;
4550
+ unset($data);
4551
+ break;
4552
+
4553
+ case 'commit':
4554
+ if (is_array($this->checkpoint) && isset($this->checkpoint['checkpoint'])) {
4555
+ $tmp = $this->checkpoint['checkpoint'];
4556
+ $this->checkpoint = $tmp;
4557
+ unset($tmp);
4558
+ } else {
4559
+ $this->checkpoint = '';
4560
+ }
4561
+ break;
4562
+
4563
+ case 'rewind':
4564
+ // do not destroy the current checkpoint, but move us back to the state then, so that we can try again
4565
+ if (is_array($this->checkpoint)) {
4566
+ // can only abort if were inside a checkpoint
4567
+ $tmp = $this->checkpoint;
4568
+
4569
+ foreach ($tmp as $k => $v) {
4570
+ if ($k !== 'checkpoint') {
4571
+ $this->$k = $v;
4572
+ }
4573
+ }
4574
+ unset($tmp);
4575
+ }
4576
+ break;
4577
+
4578
+ case 'abort':
4579
+ if (is_array($this->checkpoint)) {
4580
+ // can only abort if were inside a checkpoint
4581
+ $tmp = $this->checkpoint;
4582
+ foreach ($tmp as $k => $v) {
4583
+ $this->$k = $v;
4584
+ }
4585
+ unset($tmp);
4586
+ }
4587
+ break;
4588
+ }
4589
+ }
4590
+ }
dompdf/lib/fonts/Courier-Bold.afm ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ StartFontMetrics 4.1
2
+ Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
3
+ Comment Creation Date: Mon Jun 23 16:28:00 0:00:00
4
+ Comment UniqueID 43048
5
+ Comment VMusage 41139 52164
6
+ FontName Courier-Bold
7
+ FullName Courier Bold
8
+ FamilyName Courier
9
+ Weight Bold
10
+ ItalicAngle 0
11
+ IsFixedPitch true
12
+ CharacterSet ExtendedRoman
13
+ FontBBox -113 -250 749 801
14
+ UnderlinePosition -100
15
+ UnderlineThickness 50
16
+ Version 003.000
17
+ Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
18
+ EncodingScheme WinAnsiEncoding
19
+ CapHeight 562
20
+ XHeight 439
21
+ Ascender 629
22
+ Descender -157
23
+ StdHW 84
24
+ StdVW 106
25
+ StartCharMetrics 317
26
+ C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
27
+ C 160 ; WX 600 ; N space ; B 0 0 0 0 ;
28
+ C 33 ; WX 600 ; N exclam ; B 202 -15 398 572 ;
29
+ C 34 ; WX 600 ; N quotedbl ; B 135 277 465 562 ;
30
+ C 35 ; WX 600 ; N numbersign ; B 56 -45 544 651 ;
31
+ C 36 ; WX 600 ; N dollar ; B 82 -126 519 666 ;
32
+ C 37 ; WX 600 ; N percent ; B 5 -15 595 616 ;
33
+ C 38 ; WX 600 ; N ampersand ; B 36 -15 546 543 ;
34
+ C 146 ; WX 600 ; N quoteright ; B 171 277 423 562 ;
35
+ C 40 ; WX 600 ; N parenleft ; B 219 -102 461 616 ;
36
+ C 41 ; WX 600 ; N parenright ; B 139 -102 381 616 ;
37
+ C 42 ; WX 600 ; N asterisk ; B 91 219 509 601 ;
38
+ C 43 ; WX 600 ; N plus ; B 71 39 529 478 ;
39
+ C 44 ; WX 600 ; N comma ; B 123 -111 393 174 ;
40
+ C 45 ; WX 600 ; N hyphen ; B 100 203 500 313 ;
41
+ C 173 ; WX 600 ; N hyphen ; B 100 203 500 313 ;
42
+ C 46 ; WX 600 ; N period ; B 192 -15 408 171 ;
43
+ C 47 ; WX 600 ; N slash ; B 98 -77 502 626 ;
44
+ C 48 ; WX 600 ; N zero ; B 87 -15 513 616 ;
45
+ C 49 ; WX 600 ; N one ; B 81 0 539 616 ;
46
+ C 50 ; WX 600 ; N two ; B 61 0 499 616 ;
47
+ C 51 ; WX 600 ; N three ; B 63 -15 501 616 ;
48
+ C 52 ; WX 600 ; N four ; B 53 0 507 616 ;
49
+ C 53 ; WX 600 ; N five ; B 70 -15 521 601 ;
50
+ C 54 ; WX 600 ; N six ; B 90 -15 521 616 ;
51
+ C 55 ; WX 600 ; N seven ; B 55 0 494 601 ;
52
+ C 56 ; WX 600 ; N eight ; B 83 -15 517 616 ;
53
+ C 57 ; WX 600 ; N nine ; B 79 -15 510 616 ;
54
+ C 58 ; WX 600 ; N colon ; B 191 -15 407 425 ;
55
+ C 59 ; WX 600 ; N semicolon ; B 123 -111 408 425 ;
56
+ C 60 ; WX 600 ; N less ; B 66 15 523 501 ;
57
+ C 61 ; WX 600 ; N equal ; B 71 118 529 398 ;
58
+ C 62 ; WX 600 ; N greater ; B 77 15 534 501 ;
59
+ C 63 ; WX 600 ; N question ; B 98 -14 501 580 ;
60
+ C 64 ; WX 600 ; N at ; B 16 -15 584 616 ;
61
+ C 65 ; WX 600 ; N A ; B -9 0 609 562 ;
62
+ C 66 ; WX 600 ; N B ; B 30 0 573 562 ;
63
+ C 67 ; WX 600 ; N C ; B 22 -18 560 580 ;
64
+ C 68 ; WX 600 ; N D ; B 30 0 594 562 ;
65
+ C 69 ; WX 600 ; N E ; B 25 0 560 562 ;
66
+ C 70 ; WX 600 ; N F ; B 39 0 570 562 ;
67
+ C 71 ; WX 600 ; N G ; B 22 -18 594 580 ;
68
+ C 72 ; WX 600 ; N H ; B 20 0 580 562 ;
69
+ C 73 ; WX 600 ; N I ; B 77 0 523 562 ;
70
+ C 74 ; WX 600 ; N J ; B 37 -18 601 562 ;
71
+ C 75 ; WX 600 ; N K ; B 21 0 599 562 ;
72
+ C 76 ; WX 600 ; N L ; B 39 0 578 562 ;
73
+ C 77 ; WX 600 ; N M ; B -2 0 602 562 ;
74
+ C 78 ; WX 600 ; N N ; B 8 -12 610 562 ;
75
+ C 79 ; WX 600 ; N O ; B 22 -18 578 580 ;
76
+ C 80 ; WX 600 ; N P ; B 48 0 559 562 ;
77
+ C 81 ; WX 600 ; N Q ; B 32 -138 578 580 ;
78
+ C 82 ; WX 600 ; N R ; B 24 0 599 562 ;
79
+ C 83 ; WX 600 ; N S ; B 47 -22 553 582 ;
80
+ C 84 ; WX 600 ; N T ; B 21 0 579 562 ;
81
+ C 85 ; WX 600 ; N U ; B 4 -18 596 562 ;
82
+ C 86 ; WX 600 ; N V ; B -13 0 613 562 ;
83
+ C 87 ; WX 600 ; N W ; B -18 0 618 562 ;
84
+ C 88 ; WX 600 ; N X ; B 12 0 588 562 ;
85
+ C 89 ; WX 600 ; N Y ; B 12 0 589 562 ;
86
+ C 90 ; WX 600 ; N Z ; B 62 0 539 562 ;
87
+ C 91 ; WX 600 ; N bracketleft ; B 245 -102 475 616 ;
88
+ C 92 ; WX 600 ; N backslash ; B 99 -77 503 626 ;
89
+ C 93 ; WX 600 ; N bracketright ; B 125 -102 355 616 ;
90
+ C 94 ; WX 600 ; N asciicircum ; B 108 250 492 616 ;
91
+ C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
92
+ C 145 ; WX 600 ; N quoteleft ; B 178 277 428 562 ;
93
+ C 97 ; WX 600 ; N a ; B 35 -15 570 454 ;
94
+ C 98 ; WX 600 ; N b ; B 0 -15 584 626 ;
95
+ C 99 ; WX 600 ; N c ; B 40 -15 545 459 ;
96
+ C 100 ; WX 600 ; N d ; B 20 -15 591 626 ;
97
+ C 101 ; WX 600 ; N e ; B 40 -15 563 454 ;
98
+ C 102 ; WX 600 ; N f ; B 83 0 547 626 ; L i fi ; L l fl ;
99
+ C 103 ; WX 600 ; N g ; B 30 -146 580 454 ;
100
+ C 104 ; WX 600 ; N h ; B 5 0 592 626 ;
101
+ C 105 ; WX 600 ; N i ; B 77 0 523 658 ;
102
+ C 106 ; WX 600 ; N j ; B 63 -146 440 658 ;
103
+ C 107 ; WX 600 ; N k ; B 20 0 585 626 ;
104
+ C 108 ; WX 600 ; N l ; B 77 0 523 626 ;
105
+ C 109 ; WX 600 ; N m ; B -22 0 626 454 ;
106
+ C 110 ; WX 600 ; N n ; B 18 0 592 454 ;
107
+ C 111 ; WX 600 ; N o ; B 30 -15 570 454 ;
108
+ C 112 ; WX 600 ; N p ; B -1 -142 570 454 ;
109
+ C 113 ; WX 600 ; N q ; B 20 -142 591 454 ;
110
+ C 114 ; WX 600 ; N r ; B 47 0 580 454 ;
111
+ C 115 ; WX 600 ; N s ; B 68 -17 535 459 ;
112
+ C 116 ; WX 600 ; N t ; B 47 -15 532 562 ;
113
+ C 117 ; WX 600 ; N u ; B -1 -15 569 439 ;
114
+ C 118 ; WX 600 ; N v ; B -1 0 601 439 ;
115
+ C 119 ; WX 600 ; N w ; B -18 0 618 439 ;
116
+ C 120 ; WX 600 ; N x ; B 6 0 594 439 ;
117
+ C 121 ; WX 600 ; N y ; B -4 -142 601 439 ;
118
+ C 122 ; WX 600 ; N z ; B 81 0 520 439 ;
119
+ C 123 ; WX 600 ; N braceleft ; B 160 -102 464 616 ;
120
+ C 124 ; WX 600 ; N bar ; B 255 -250 345 750 ;
121
+ C 125 ; WX 600 ; N braceright ; B 136 -102 440 616 ;
122
+ C 126 ; WX 600 ; N asciitilde ; B 71 153 530 356 ;
123
+ C 161 ; WX 600 ; N exclamdown ; B 202 -146 398 449 ;
124
+ C 162 ; WX 600 ; N cent ; B 66 -49 518 614 ;
125
+ C 163 ; WX 600 ; N sterling ; B 72 -28 558 611 ;
126
+ C -1 ; WX 600 ; N fraction ; B 25 -60 576 661 ;
127
+ C 165 ; WX 600 ; N yen ; B 10 0 590 562 ;
128
+ C 131 ; WX 600 ; N florin ; B -30 -131 572 616 ;
129
+ C 167 ; WX 600 ; N section ; B 83 -70 517 580 ;
130
+ C 164 ; WX 600 ; N currency ; B 54 49 546 517 ;
131
+ C 39 ; WX 600 ; N quotesingle ; B 227 277 373 562 ;
132
+ C 147 ; WX 600 ; N quotedblleft ; B 71 277 535 562 ;
133
+ C 170 ; WX 600 ; N guillemotleft ; B 8 70 553 446 ;
134
+ C 139 ; WX 600 ; N guilsinglleft ; B 141 70 459 446 ;
135
+ C 155 ; WX 600 ; N guilsinglright ; B 141 70 459 446 ;
136
+ C -1 ; WX 600 ; N fi ; B 12 0 593 626 ;
137
+ C -1 ; WX 600 ; N fl ; B 12 0 593 626 ;
138
+ C 150 ; WX 600 ; N endash ; B 65 203 535 313 ;
139
+ C 134 ; WX 600 ; N dagger ; B 106 -70 494 580 ;
140
+ C 135 ; WX 600 ; N daggerdbl ; B 106 -70 494 580 ;
141
+ C 183 ; WX 600 ; N periodcentered ; B 196 165 404 351 ;
142
+ C 182 ; WX 600 ; N paragraph ; B 6 -70 576 580 ;
143
+ C 149 ; WX 600 ; N bullet ; B 140 132 460 430 ;
144
+ C 130 ; WX 600 ; N quotesinglbase ; B 175 -142 427 143 ;
145
+ C 132 ; WX 600 ; N quotedblbase ; B 65 -142 529 143 ;
146
+ C 148 ; WX 600 ; N quotedblright ; B 61 277 525 562 ;
147
+ C 187 ; WX 600 ; N guillemotright ; B 47 70 592 446 ;
148
+ C 133 ; WX 600 ; N ellipsis ; B 26 -15 574 116 ;
149
+ C 137 ; WX 600 ; N perthousand ; B -113 -15 713 616 ;
150
+ C 191 ; WX 600 ; N questiondown ; B 99 -146 502 449 ;
151
+ C 96 ; WX 600 ; N grave ; B 132 508 395 661 ;
152
+ C 180 ; WX 600 ; N acute ; B 205 508 468 661 ;
153
+ C 136 ; WX 600 ; N circumflex ; B 103 483 497 657 ;
154
+ C 152 ; WX 600 ; N tilde ; B 89 493 512 636 ;
155
+ C 175 ; WX 600 ; N macron ; B 88 505 512 585 ;
156
+ C -1 ; WX 600 ; N breve ; B 83 468 517 631 ;
157
+ C -1 ; WX 600 ; N dotaccent ; B 230 498 370 638 ;
158
+ C 168 ; WX 600 ; N dieresis ; B 128 498 472 638 ;
159
+ C -1 ; WX 600 ; N ring ; B 198 481 402 678 ;
160
+ C 184 ; WX 600 ; N cedilla ; B 205 -206 387 0 ;
161
+ C -1 ; WX 600 ; N hungarumlaut ; B 68 488 588 661 ;
162
+ C -1 ; WX 600 ; N ogonek ; B 169 -199 400 0 ;
163
+ C -1 ; WX 600 ; N caron ; B 103 493 497 667 ;
164
+ C 151 ; WX 600 ; N emdash ; B -10 203 610 313 ;
165
+ C 198 ; WX 600 ; N AE ; B -29 0 602 562 ;
166
+ C 170 ; WX 600 ; N ordfeminine ; B 147 196 453 580 ;
167
+ C -1 ; WX 600 ; N Lslash ; B 39 0 578 562 ;
168
+ C 216 ; WX 600 ; N Oslash ; B 22 -22 578 584 ;
169
+ C 140 ; WX 600 ; N OE ; B -25 0 595 562 ;
170
+ C 186 ; WX 600 ; N ordmasculine ; B 147 196 453 580 ;
171
+ C 230 ; WX 600 ; N ae ; B -4 -15 601 454 ;
172
+ C -1 ; WX 600 ; N dotlessi ; B 77 0 523 439 ;
173
+ C -1 ; WX 600 ; N lslash ; B 77 0 523 626 ;
174
+ C 248 ; WX 600 ; N oslash ; B 30 -24 570 463 ;
175
+ C 156 ; WX 600 ; N oe ; B -18 -15 611 454 ;
176
+ C 223 ; WX 600 ; N germandbls ; B 22 -15 596 626 ;
177
+ C 207 ; WX 600 ; N Idieresis ; B 77 0 523 761 ;
178
+ C 233 ; WX 600 ; N eacute ; B 40 -15 563 661 ;
179
+ C -1 ; WX 600 ; N abreve ; B 35 -15 570 661 ;
180
+ C -1 ; WX 600 ; N uhungarumlaut ; B -1 -15 628 661 ;
181
+ C -1 ; WX 600 ; N ecaron ; B 40 -15 563 667 ;
182
+ C 159 ; WX 600 ; N Ydieresis ; B 12 0 589 761 ;
183
+ C 247 ; WX 600 ; N divide ; B 71 16 529 500 ;
184
+ C 221 ; WX 600 ; N Yacute ; B 12 0 589 784 ;
185
+ C 194 ; WX 600 ; N Acircumflex ; B -9 0 609 780 ;
186
+ C 225 ; WX 600 ; N aacute ; B 35 -15 570 661 ;
187
+ C 219 ; WX 600 ; N Ucircumflex ; B 4 -18 596 780 ;
188
+ C 253 ; WX 600 ; N yacute ; B -4 -142 601 661 ;
189
+ C -1 ; WX 600 ; N scommaaccent ; B 68 -250 535 459 ;
190
+ C 234 ; WX 600 ; N ecircumflex ; B 40 -15 563 657 ;
191
+ C -1 ; WX 600 ; N Uring ; B 4 -18 596 801 ;
192
+ C 220 ; WX 600 ; N Udieresis ; B 4 -18 596 761 ;
193
+ C -1 ; WX 600 ; N aogonek ; B 35 -199 586 454 ;
194
+ C 218 ; WX 600 ; N Uacute ; B 4 -18 596 784 ;
195
+ C -1 ; WX 600 ; N uogonek ; B -1 -199 585 439 ;
196
+ C 203 ; WX 600 ; N Edieresis ; B 25 0 560 761 ;
197
+ C -1 ; WX 600 ; N Dcroat ; B 30 0 594 562 ;
198
+ C -1 ; WX 600 ; N commaaccent ; B 205 -250 397 -57 ;
199
+ C 169 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
200
+ C -1 ; WX 600 ; N Emacron ; B 25 0 560 708 ;
201
+ C -1 ; WX 600 ; N ccaron ; B 40 -15 545 667 ;
202
+ C 229 ; WX 600 ; N aring ; B 35 -15 570 678 ;
203
+ C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 610 562 ;
204
+ C -1 ; WX 600 ; N lacute ; B 77 0 523 801 ;
205
+ C 224 ; WX 600 ; N agrave ; B 35 -15 570 661 ;
206
+ C -1 ; WX 600 ; N Tcommaaccent ; B 21 -250 579 562 ;
207
+ C -1 ; WX 600 ; N Cacute ; B 22 -18 560 784 ;
208
+ C 227 ; WX 600 ; N atilde ; B 35 -15 570 636 ;
209
+ C -1 ; WX 600 ; N Edotaccent ; B 25 0 560 761 ;
210
+ C 154 ; WX 600 ; N scaron ; B 68 -17 535 667 ;
211
+ C -1 ; WX 600 ; N scedilla ; B 68 -206 535 459 ;
212
+ C 237 ; WX 600 ; N iacute ; B 77 0 523 661 ;
213
+ C -1 ; WX 600 ; N lozenge ; B 66 0 534 740 ;
214
+ C -1 ; WX 600 ; N Rcaron ; B 24 0 599 790 ;
215
+ C -1 ; WX 600 ; N Gcommaaccent ; B 22 -250 594 580 ;
216
+ C 251 ; WX 600 ; N ucircumflex ; B -1 -15 569 657 ;
217
+ C 226 ; WX 600 ; N acircumflex ; B 35 -15 570 657 ;
218
+ C -1 ; WX 600 ; N Amacron ; B -9 0 609 708 ;
219
+ C -1 ; WX 600 ; N rcaron ; B 47 0 580 667 ;
220
+ C 231 ; WX 600 ; N ccedilla ; B 40 -206 545 459 ;
221
+ C -1 ; WX 600 ; N Zdotaccent ; B 62 0 539 761 ;
222
+ C 222 ; WX 600 ; N Thorn ; B 48 0 557 562 ;
223
+ C -1 ; WX 600 ; N Omacron ; B 22 -18 578 708 ;
224
+ C -1 ; WX 600 ; N Racute ; B 24 0 599 784 ;
225
+ C -1 ; WX 600 ; N Sacute ; B 47 -22 553 784 ;
226
+ C -1 ; WX 600 ; N dcaron ; B 20 -15 727 626 ;
227
+ C -1 ; WX 600 ; N Umacron ; B 4 -18 596 708 ;
228
+ C -1 ; WX 600 ; N uring ; B -1 -15 569 678 ;
229
+ C 179 ; WX 600 ; N threesuperior ; B 138 222 433 616 ;
230
+ C 210 ; WX 600 ; N Ograve ; B 22 -18 578 784 ;
231
+ C 192 ; WX 600 ; N Agrave ; B -9 0 609 784 ;
232
+ C -1 ; WX 600 ; N Abreve ; B -9 0 609 784 ;
233
+ C 215 ; WX 600 ; N multiply ; B 81 39 520 478 ;
234
+ C 250 ; WX 600 ; N uacute ; B -1 -15 569 661 ;
235
+ C -1 ; WX 600 ; N Tcaron ; B 21 0 579 790 ;
236
+ C -1 ; WX 600 ; N partialdiff ; B 63 -38 537 728 ;
237
+ C 255 ; WX 600 ; N ydieresis ; B -4 -142 601 638 ;
238
+ C -1 ; WX 600 ; N Nacute ; B 8 -12 610 784 ;
239
+ C 238 ; WX 600 ; N icircumflex ; B 73 0 523 657 ;
240
+ C 202 ; WX 600 ; N Ecircumflex ; B 25 0 560 780 ;
241
+ C 228 ; WX 600 ; N adieresis ; B 35 -15 570 638 ;
242
+ C 235 ; WX 600 ; N edieresis ; B 40 -15 563 638 ;
243
+ C -1 ; WX 600 ; N cacute ; B 40 -15 545 661 ;
244
+ C -1 ; WX 600 ; N nacute ; B 18 0 592 661 ;
245
+ C -1 ; WX 600 ; N umacron ; B -1 -15 569 585 ;
246
+ C -1 ; WX 600 ; N Ncaron ; B 8 -12 610 790 ;
247
+ C 205 ; WX 600 ; N Iacute ; B 77 0 523 784 ;
248
+ C 177 ; WX 600 ; N plusminus ; B 71 24 529 515 ;
249
+ C 166 ; WX 600 ; N brokenbar ; B 255 -175 345 675 ;
250
+ C 174 ; WX 600 ; N registered ; B 0 -18 600 580 ;
251
+ C -1 ; WX 600 ; N Gbreve ; B 22 -18 594 784 ;
252
+ C -1 ; WX 600 ; N Idotaccent ; B 77 0 523 761 ;
253
+ C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ;
254
+ C 200 ; WX 600 ; N Egrave ; B 25 0 560 784 ;
255
+ C -1 ; WX 600 ; N racute ; B 47 0 580 661 ;
256
+ C -1 ; WX 600 ; N omacron ; B 30 -15 570 585 ;
257
+ C -1 ; WX 600 ; N Zacute ; B 62 0 539 784 ;
258
+ C 142 ; WX 600 ; N Zcaron ; B 62 0 539 790 ;
259
+ C -1 ; WX 600 ; N greaterequal ; B 26 0 523 696 ;
260
+ C 208 ; WX 600 ; N Eth ; B 30 0 594 562 ;
261
+ C 199 ; WX 600 ; N Ccedilla ; B 22 -206 560 580 ;
262
+ C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 523 626 ;
263
+ C -1 ; WX 600 ; N tcaron ; B 47 -15 532 703 ;
264
+ C -1 ; WX 600 ; N eogonek ; B 40 -199 563 454 ;
265
+ C -1 ; WX 600 ; N Uogonek ; B 4 -199 596 562 ;
266
+ C 193 ; WX 600 ; N Aacute ; B -9 0 609 784 ;
267
+ C 196 ; WX 600 ; N Adieresis ; B -9 0 609 761 ;
268
+ C 232 ; WX 600 ; N egrave ; B 40 -15 563 661 ;
269
+ C -1 ; WX 600 ; N zacute ; B 81 0 520 661 ;
270
+ C -1 ; WX 600 ; N iogonek ; B 77 -199 523 658 ;
271
+ C 211 ; WX 600 ; N Oacute ; B 22 -18 578 784 ;
272
+ C 243 ; WX 600 ; N oacute ; B 30 -15 570 661 ;
273
+ C -1 ; WX 600 ; N amacron ; B 35 -15 570 585 ;
274
+ C -1 ; WX 600 ; N sacute ; B 68 -17 535 661 ;
275
+ C 239 ; WX 600 ; N idieresis ; B 77 0 523 618 ;
276
+ C 212 ; WX 600 ; N Ocircumflex ; B 22 -18 578 780 ;
277
+ C 217 ; WX 600 ; N Ugrave ; B 4 -18 596 784 ;
278
+ C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ;
279
+ C 254 ; WX 600 ; N thorn ; B -14 -142 570 626 ;
280
+ C 178 ; WX 600 ; N twosuperior ; B 143 230 436 616 ;
281
+ C 214 ; WX 600 ; N Odieresis ; B 22 -18 578 761 ;
282
+ C 181 ; WX 600 ; N mu ; B -1 -142 569 439 ;
283
+ C 236 ; WX 600 ; N igrave ; B 77 0 523 661 ;
284
+ C -1 ; WX 600 ; N ohungarumlaut ; B 30 -15 668 661 ;
285
+ C -1 ; WX 600 ; N Eogonek ; B 25 -199 576 562 ;
286
+ C -1 ; WX 600 ; N dcroat ; B 20 -15 591 626 ;
287
+ C 190 ; WX 600 ; N threequarters ; B -47 -60 648 661 ;
288
+ C -1 ; WX 600 ; N Scedilla ; B 47 -206 553 582 ;
289
+ C -1 ; WX 600 ; N lcaron ; B 77 0 597 626 ;
290
+ C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 599 562 ;
291
+ C -1 ; WX 600 ; N Lacute ; B 39 0 578 784 ;
292
+ C 153 ; WX 600 ; N trademark ; B -9 230 749 562 ;
293
+ C -1 ; WX 600 ; N edotaccent ; B 40 -15 563 638 ;
294
+ C 204 ; WX 600 ; N Igrave ; B 77 0 523 784 ;
295
+ C -1 ; WX 600 ; N Imacron ; B 77 0 523 708 ;
296
+ C -1 ; WX 600 ; N Lcaron ; B 39 0 637 562 ;
297
+ C 189 ; WX 600 ; N onehalf ; B -47 -60 648 661 ;
298
+ C -1 ; WX 600 ; N lessequal ; B 26 0 523 696 ;
299
+ C 244 ; WX 600 ; N ocircumflex ; B 30 -15 570 657 ;
300
+ C 241 ; WX 600 ; N ntilde ; B 18 0 592 636 ;
301
+ C -1 ; WX 600 ; N Uhungarumlaut ; B 4 -18 638 784 ;
302
+ C 201 ; WX 600 ; N Eacute ; B 25 0 560 784 ;
303
+ C -1 ; WX 600 ; N emacron ; B 40 -15 563 585 ;
304
+ C -1 ; WX 600 ; N gbreve ; B 30 -146 580 661 ;
305
+ C 188 ; WX 600 ; N onequarter ; B -56 -60 656 661 ;
306
+ C 138 ; WX 600 ; N Scaron ; B 47 -22 553 790 ;
307
+ C -1 ; WX 600 ; N Scommaaccent ; B 47 -250 553 582 ;
308
+ C -1 ; WX 600 ; N Ohungarumlaut ; B 22 -18 628 784 ;
309
+ C 176 ; WX 600 ; N degree ; B 86 243 474 616 ;
310
+ C 242 ; WX 600 ; N ograve ; B 30 -15 570 661 ;
311
+ C -1 ; WX 600 ; N Ccaron ; B 22 -18 560 790 ;
312
+ C 249 ; WX 600 ; N ugrave ; B -1 -15 569 661 ;
313
+ C -1 ; WX 600 ; N radical ; B -19 -104 473 778 ;
314
+ C -1 ; WX 600 ; N Dcaron ; B 30 0 594 790 ;
315
+ C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 580 454 ;
316
+ C 209 ; WX 600 ; N Ntilde ; B 8 -12 610 759 ;
317
+ C 245 ; WX 600 ; N otilde ; B 30 -15 570 636 ;
318
+ C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 599 562 ;
319
+ C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 578 562 ;
320
+ C 195 ; WX 600 ; N Atilde ; B -9 0 609 759 ;
321
+ C -1 ; WX 600 ; N Aogonek ; B -9 -199 625 562 ;
322
+ C 197 ; WX 600 ; N Aring ; B -9 0 609 801 ;
323
+ C 213 ; WX 600 ; N Otilde ; B 22 -18 578 759 ;
324
+ C -1 ; WX 600 ; N zdotaccent ; B 81 0 520 638 ;
325
+ C -1 ; WX 600 ; N Ecaron ; B 25 0 560 790 ;
326
+ C -1 ; WX 600 ; N Iogonek ; B 77 -199 523 562 ;
327
+ C -1 ; WX 600 ; N kcommaaccent ; B 20 -250 585 626 ;
328
+ C -1 ; WX 600 ; N minus ; B 71 203 529 313 ;
329
+ C 206 ; WX 600 ; N Icircumflex ; B 77 0 523 780 ;
330
+ C -1 ; WX 600 ; N ncaron ; B 18 0 592 667 ;
331
+ C -1 ; WX 600 ; N tcommaaccent ; B 47 -250 532 562 ;
332
+ C 172 ; WX 600 ; N logicalnot ; B 71 103 529 413 ;
333
+ C 246 ; WX 600 ; N odieresis ; B 30 -15 570 638 ;
334
+ C 252 ; WX 600 ; N udieresis ; B -1 -15 569 638 ;
335
+ C -1 ; WX 600 ; N notequal ; B 12 -47 537 563 ;
336
+ C -1 ; WX 600 ; N gcommaaccent ; B 30 -146 580 714 ;
337
+ C 240 ; WX 600 ; N eth ; B 58 -27 543 626 ;
338
+ C 158 ; WX 600 ; N zcaron ; B 81 0 520 667 ;
339
+ C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 592 454 ;
340
+ C 185 ; WX 600 ; N onesuperior ; B 153 230 447 616 ;
341
+ C -1 ; WX 600 ; N imacron ; B 77 0 523 585 ;
342
+ C 128 ; WX 600 ; N Euro ; B 0 0 0 0 ;
343
+ EndCharMetrics
344
+ EndFontMetrics
dompdf/lib/fonts/Courier-BoldOblique.afm ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ StartFontMetrics 4.1
2
+ Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
3
+ Comment Creation Date: Mon Jun 23 16:28:46 0:00:00
4
+ Comment UniqueID 43049
5
+ Comment VMusage 17529 79244
6
+ FontName Courier-BoldOblique
7
+ FullName Courier Bold Oblique
8
+ FamilyName Courier
9
+ Weight Bold
10
+ ItalicAngle -12
11
+ IsFixedPitch true
12
+ CharacterSet ExtendedRoman
13
+ FontBBox -57 -250 869 801
14
+ UnderlinePosition -100
15
+ UnderlineThickness 50
16
+ Version 3
17
+ Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
18
+ EncodingScheme WinAnsiEncoding
19
+ CapHeight 562
20
+ XHeight 439
21
+ Ascender 629
22
+ Descender -157
23
+ StdHW 84
24
+ StdVW 106
25
+ StartCharMetrics 317
26
+ C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
27
+ C 160 ; WX 600 ; N space ; B 0 0 0 0 ;
28
+ C 33 ; WX 600 ; N exclam ; B 215 -15 495 572 ;
29
+ C 34 ; WX 600 ; N quotedbl ; B 211 277 585 562 ;
30
+ C 35 ; WX 600 ; N numbersign ; B 88 -45 641 651 ;
31
+ C 36 ; WX 600 ; N dollar ; B 87 -126 630 666 ;
32
+ C 37 ; WX 600 ; N percent ; B 101 -15 625 616 ;
33
+ C 38 ; WX 600 ; N ampersand ; B 61 -15 595 543 ;
34
+ C 146 ; WX 600 ; N quoteright ; B 229 277 543 562 ;
35
+ C 40 ; WX 600 ; N parenleft ; B 265 -102 592 616 ;
36
+ C 41 ; WX 600 ; N parenright ; B 117 -102 444 616 ;
37
+ C 42 ; WX 600 ; N asterisk ; B 179 219 598 601 ;
38
+ C 43 ; WX 600 ; N plus ; B 114 39 596 478 ;
39
+ C 44 ; WX 600 ; N comma ; B 99 -111 430 174 ;
40
+ C 45 ; WX 600 ; N hyphen ; B 143 203 567 313 ;
41
+ C 173 ; WX 600 ; N hyphen ; B 143 203 567 313 ;
42
+ C 46 ; WX 600 ; N period ; B 206 -15 427 171 ;
43
+ C 47 ; WX 600 ; N slash ; B 90 -77 626 626 ;
44
+ C 48 ; WX 600 ; N zero ; B 135 -15 593 616 ;
45
+ C 49 ; WX 600 ; N one ; B 93 0 562 616 ;
46
+ C 50 ; WX 600 ; N two ; B 61 0 594 616 ;
47
+ C 51 ; WX 600 ; N three ; B 71 -15 571 616 ;
48
+ C 52 ; WX 600 ; N four ; B 81 0 559 616 ;
49
+ C 53 ; WX 600 ; N five ; B 77 -15 621 601 ;
50
+ C 54 ; WX 600 ; N six ; B 135 -15 652 616 ;
51
+ C 55 ; WX 600 ; N seven ; B 147 0 622 601 ;
52
+ C 56 ; WX 600 ; N eight ; B 115 -15 604 616 ;
53
+ C 57 ; WX 600 ; N nine ; B 75 -15 592 616 ;
54
+ C 58 ; WX 600 ; N colon ; B 205 -15 480 425 ;
55
+ C 59 ; WX 600 ; N semicolon ; B 99 -111 481 425 ;
56
+ C 60 ; WX 600 ; N less ; B 120 15 613 501 ;
57
+ C 61 ; WX 600 ; N equal ; B 96 118 614 398 ;
58
+ C 62 ; WX 600 ; N greater ; B 97 15 589 501 ;
59
+ C 63 ; WX 600 ; N question ; B 183 -14 592 580 ;
60
+ C 64 ; WX 600 ; N at ; B 65 -15 642 616 ;
61
+ C 65 ; WX 600 ; N A ; B -9 0 632 562 ;
62
+ C 66 ; WX 600 ; N B ; B 30 0 630 562 ;
63
+ C 67 ; WX 600 ; N C ; B 74 -18 675 580 ;
64
+ C 68 ; WX 600 ; N D ; B 30 0 664 562 ;
65
+ C 69 ; WX 600 ; N E ; B 25 0 670 562 ;
66
+ C 70 ; WX 600 ; N F ; B 39 0 684 562 ;
67
+ C 71 ; WX 600 ; N G ; B 74 -18 675 580 ;
68
+ C 72 ; WX 600 ; N H ; B 20 0 700 562 ;
69
+ C 73 ; WX 600 ; N I ; B 77 0 643 562 ;
70
+ C 74 ; WX 600 ; N J ; B 58 -18 721 562 ;
71
+ C 75 ; WX 600 ; N K ; B 21 0 692 562 ;
72
+ C 76 ; WX 600 ; N L ; B 39 0 636 562 ;
73
+ C 77 ; WX 600 ; N M ; B -2 0 722 562 ;
74
+ C 78 ; WX 600 ; N N ; B 8 -12 730 562 ;
75
+ C 79 ; WX 600 ; N O ; B 74 -18 645 580 ;
76
+ C 80 ; WX 600 ; N P ; B 48 0 643 562 ;
77
+ C 81 ; WX 600 ; N Q ; B 83 -138 636 580 ;
78
+ C 82 ; WX 600 ; N R ; B 24 0 617 562 ;
79
+ C 83 ; WX 600 ; N S ; B 54 -22 673 582 ;
80
+ C 84 ; WX 600 ; N T ; B 86 0 679 562 ;
81
+ C 85 ; WX 600 ; N U ; B 101 -18 716 562 ;
82
+ C 86 ; WX 600 ; N V ; B 84 0 733 562 ;
83
+ C 87 ; WX 600 ; N W ; B 79 0 738 562 ;
84
+ C 88 ; WX 600 ; N X ; B 12 0 690 562 ;
85
+ C 89 ; WX 600 ; N Y ; B 109 0 709 562 ;
86
+ C 90 ; WX 600 ; N Z ; B 62 0 637 562 ;
87
+ C 91 ; WX 600 ; N bracketleft ; B 223 -102 606 616 ;
88
+ C 92 ; WX 600 ; N backslash ; B 222 -77 496 626 ;
89
+ C 93 ; WX 600 ; N bracketright ; B 103 -102 486 616 ;
90
+ C 94 ; WX 600 ; N asciicircum ; B 171 250 556 616 ;
91
+ C 95 ; WX 600 ; N underscore ; B -27 -125 585 -75 ;
92
+ C 145 ; WX 600 ; N quoteleft ; B 297 277 487 562 ;
93
+ C 97 ; WX 600 ; N a ; B 61 -15 593 454 ;
94
+ C 98 ; WX 600 ; N b ; B 13 -15 636 626 ;
95
+ C 99 ; WX 600 ; N c ; B 81 -15 631 459 ;
96
+ C 100 ; WX 600 ; N d ; B 60 -15 645 626 ;
97
+ C 101 ; WX 600 ; N e ; B 81 -15 605 454 ;
98
+ C 102 ; WX 600 ; N f ; B 83 0 677 626 ; L i fi ; L l fl ;
99
+ C 103 ; WX 600 ; N g ; B 40 -146 674 454 ;
100
+ C 104 ; WX 600 ; N h ; B 18 0 615 626 ;
101
+ C 105 ; WX 600 ; N i ; B 77 0 546 658 ;
102
+ C 106 ; WX 600 ; N j ; B 36 -146 580 658 ;
103
+ C 107 ; WX 600 ; N k ; B 33 0 643 626 ;
104
+ C 108 ; WX 600 ; N l ; B 77 0 546 626 ;
105
+ C 109 ; WX 600 ; N m ; B -22 0 649 454 ;
106
+ C 110 ; WX 600 ; N n ; B 18 0 615 454 ;
107
+ C 111 ; WX 600 ; N o ; B 71 -15 622 454 ;
108
+ C 112 ; WX 600 ; N p ; B -32 -142 622 454 ;
109
+ C 113 ; WX 600 ; N q ; B 60 -142 685 454 ;
110
+ C 114 ; WX 600 ; N r ; B 47 0 655 454 ;
111
+ C 115 ; WX 600 ; N s ; B 66 -17 608 459 ;
112
+ C 116 ; WX 600 ; N t ; B 118 -15 567 562 ;
113
+ C 117 ; WX 600 ; N u ; B 70 -15 592 439 ;
114
+ C 118 ; WX 600 ; N v ; B 70 0 695 439 ;
115
+ C 119 ; WX 600 ; N w ; B 53 0 712 439 ;
116
+ C 120 ; WX 600 ; N x ; B 6 0 671 439 ;
117
+ C 121 ; WX 600 ; N y ; B -21 -142 695 439 ;
118
+ C 122 ; WX 600 ; N z ; B 81 0 614 439 ;
119
+ C 123 ; WX 600 ; N braceleft ; B 203 -102 595 616 ;
120
+ C 124 ; WX 600 ; N bar ; B 201 -250 505 750 ;
121
+ C 125 ; WX 600 ; N braceright ; B 114 -102 506 616 ;
122
+ C 126 ; WX 600 ; N asciitilde ; B 120 153 590 356 ;
123
+ C 161 ; WX 600 ; N exclamdown ; B 196 -146 477 449 ;
124
+ C 162 ; WX 600 ; N cent ; B 121 -49 605 614 ;
125
+ C 163 ; WX 600 ; N sterling ; B 106 -28 650 611 ;
126
+ C -1 ; WX 600 ; N fraction ; B 22 -60 708 661 ;
127
+ C 165 ; WX 600 ; N yen ; B 98 0 710 562 ;
128
+ C 131 ; WX 600 ; N florin ; B -57 -131 702 616 ;
129
+ C 167 ; WX 600 ; N section ; B 74 -70 620 580 ;
130
+ C 164 ; WX 600 ; N currency ; B 77 49 644 517 ;
131
+ C 39 ; WX 600 ; N quotesingle ; B 303 277 493 562 ;
132
+ C 147 ; WX 600 ; N quotedblleft ; B 190 277 594 562 ;
133
+ C 170 ; WX 600 ; N guillemotleft ; B 62 70 639 446 ;
134
+ C 139 ; WX 600 ; N guilsinglleft ; B 195 70 545 446 ;
135
+ C 155 ; WX 600 ; N guilsinglright ; B 165 70 514 446 ;
136
+ C -1 ; WX 600 ; N fi ; B 12 0 644 626 ;
137
+ C -1 ; WX 600 ; N fl ; B 12 0 644 626 ;
138
+ C 150 ; WX 600 ; N endash ; B 108 203 602 313 ;
139
+ C 134 ; WX 600 ; N dagger ; B 175 -70 586 580 ;
140
+ C 135 ; WX 600 ; N daggerdbl ; B 121 -70 587 580 ;
141
+ C 183 ; WX 600 ; N periodcentered ; B 248 165 461 351 ;
142
+ C 182 ; WX 600 ; N paragraph ; B 61 -70 700 580 ;
143
+ C 149 ; WX 600 ; N bullet ; B 196 132 523 430 ;
144
+ C 130 ; WX 600 ; N quotesinglbase ; B 144 -142 458 143 ;
145
+ C 132 ; WX 600 ; N quotedblbase ; B 34 -142 560 143 ;
146
+ C 148 ; WX 600 ; N quotedblright ; B 119 277 645 562 ;
147
+ C 187 ; WX 600 ; N guillemotright ; B 71 70 647 446 ;
148
+ C 133 ; WX 600 ; N ellipsis ; B 35 -15 587 116 ;
149
+ C 137 ; WX 600 ; N perthousand ; B -45 -15 743 616 ;
150
+ C 191 ; WX 600 ; N questiondown ; B 100 -146 509 449 ;
151
+ C 96 ; WX 600 ; N grave ; B 272 508 503 661 ;
152
+ C 180 ; WX 600 ; N acute ; B 312 508 609 661 ;
153
+ C 136 ; WX 600 ; N circumflex ; B 212 483 607 657 ;
154
+ C 152 ; WX 600 ; N tilde ; B 199 493 643 636 ;
155
+ C 175 ; WX 600 ; N macron ; B 195 505 637 585 ;
156
+ C -1 ; WX 600 ; N breve ; B 217 468 652 631 ;
157
+ C -1 ; WX 600 ; N dotaccent ; B 348 498 493 638 ;
158
+ C 168 ; WX 600 ; N dieresis ; B 246 498 595 638 ;
159
+ C -1 ; WX 600 ; N ring ; B 319 481 528 678 ;
160
+ C 184 ; WX 600 ; N cedilla ; B 168 -206 368 0 ;
161
+ C -1 ; WX 600 ; N hungarumlaut ; B 171 488 729 661 ;
162
+ C -1 ; WX 600 ; N ogonek ; B 143 -199 367 0 ;
163
+ C -1 ; WX 600 ; N caron ; B 238 493 633 667 ;
164
+ C 151 ; WX 600 ; N emdash ; B 33 203 677 313 ;
165
+ C 198 ; WX 600 ; N AE ; B -29 0 708 562 ;
166
+ C 170 ; WX 600 ; N ordfeminine ; B 188 196 526 580 ;
167
+ C -1 ; WX 600 ; N Lslash ; B 39 0 636 562 ;
168
+ C 216 ; WX 600 ; N Oslash ; B 48 -22 673 584 ;
169
+ C 140 ; WX 600 ; N OE ; B 26 0 701 562 ;
170
+ C 186 ; WX 600 ; N ordmasculine ; B 188 196 543 580 ;
171
+ C 230 ; WX 600 ; N ae ; B 21 -15 652 454 ;
172
+ C -1 ; WX 600 ; N dotlessi ; B 77 0 546 439 ;
173
+ C -1 ; WX 600 ; N lslash ; B 77 0 587 626 ;
174
+ C 248 ; WX 600 ; N oslash ; B 54 -24 638 463 ;
175
+ C 156 ; WX 600 ; N oe ; B 18 -15 662 454 ;
176
+ C 223 ; WX 600 ; N germandbls ; B 22 -15 629 626 ;
177
+ C 207 ; WX 600 ; N Idieresis ; B 77 0 643 761 ;
178
+ C 233 ; WX 600 ; N eacute ; B 81 -15 609 661 ;
179
+ C -1 ; WX 600 ; N abreve ; B 61 -15 658 661 ;
180
+ C -1 ; WX 600 ; N uhungarumlaut ; B 70 -15 769 661 ;
181
+ C -1 ; WX 600 ; N ecaron ; B 81 -15 633 667 ;
182
+ C 159 ; WX 600 ; N Ydieresis ; B 109 0 709 761 ;
183
+ C 247 ; WX 600 ; N divide ; B 114 16 596 500 ;
184
+ C 221 ; WX 600 ; N Yacute ; B 109 0 709 784 ;
185
+ C 194 ; WX 600 ; N Acircumflex ; B -9 0 632 780 ;
186
+ C 225 ; WX 600 ; N aacute ; B 61 -15 609 661 ;
187
+ C 219 ; WX 600 ; N Ucircumflex ; B 101 -18 716 780 ;
188
+ C 253 ; WX 600 ; N yacute ; B -21 -142 695 661 ;
189
+ C -1 ; WX 600 ; N scommaaccent ; B 66 -250 608 459 ;
190
+ C 234 ; WX 600 ; N ecircumflex ; B 81 -15 607 657 ;
191
+ C -1 ; WX 600 ; N Uring ; B 101 -18 716 801 ;
192
+ C 220 ; WX 600 ; N Udieresis ; B 101 -18 716 761 ;
193
+ C -1 ; WX 600 ; N aogonek ; B 61 -199 593 454 ;
194
+ C 218 ; WX 600 ; N Uacute ; B 101 -18 716 784 ;
195
+ C -1 ; WX 600 ; N uogonek ; B 70 -199 592 439 ;
196
+ C 203 ; WX 600 ; N Edieresis ; B 25 0 670 761 ;
197
+ C -1 ; WX 600 ; N Dcroat ; B 30 0 664 562 ;
198
+ C -1 ; WX 600 ; N commaaccent ; B 151 -250 385 -57 ;
199
+ C 169 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
200
+ C -1 ; WX 600 ; N Emacron ; B 25 0 670 708 ;
201
+ C -1 ; WX 600 ; N ccaron ; B 81 -15 633 667 ;
202
+ C 229 ; WX 600 ; N aring ; B 61 -15 593 678 ;
203
+ C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 730 562 ;
204
+ C -1 ; WX 600 ; N lacute ; B 77 0 639 801 ;
205
+ C 224 ; WX 600 ; N agrave ; B 61 -15 593 661 ;
206
+ C -1 ; WX 600 ; N Tcommaaccent ; B 86 -250 679 562 ;
207
+ C -1 ; WX 600 ; N Cacute ; B 74 -18 675 784 ;
208
+ C 227 ; WX 600 ; N atilde ; B 61 -15 643 636 ;
209
+ C -1 ; WX 600 ; N Edotaccent ; B 25 0 670 761 ;
210
+ C 154 ; WX 600 ; N scaron ; B 66 -17 633 667 ;
211
+ C -1 ; WX 600 ; N scedilla ; B 66 -206 608 459 ;
212
+ C 237 ; WX 600 ; N iacute ; B 77 0 609 661 ;
213
+ C -1 ; WX 600 ; N lozenge ; B 145 0 614 740 ;
214
+ C -1 ; WX 600 ; N Rcaron ; B 24 0 659 790 ;
215
+ C -1 ; WX 600 ; N Gcommaaccent ; B 74 -250 675 580 ;
216
+ C 251 ; WX 600 ; N ucircumflex ; B 70 -15 597 657 ;
217
+ C 226 ; WX 600 ; N acircumflex ; B 61 -15 607 657 ;
218
+ C -1 ; WX 600 ; N Amacron ; B -9 0 633 708 ;
219
+ C -1 ; WX 600 ; N rcaron ; B 47 0 655 667 ;
220
+ C 231 ; WX 600 ; N ccedilla ; B 81 -206 631 459 ;
221
+ C -1 ; WX 600 ; N Zdotaccent ; B 62 0 637 761 ;
222
+ C 222 ; WX 600 ; N Thorn ; B 48 0 620 562 ;
223
+ C -1 ; WX 600 ; N Omacron ; B 74 -18 663 708 ;
224
+ C -1 ; WX 600 ; N Racute ; B 24 0 665 784 ;
225
+ C -1 ; WX 600 ; N Sacute ; B 54 -22 673 784 ;
226
+ C -1 ; WX 600 ; N dcaron ; B 60 -15 861 626 ;
227
+ C -1 ; WX 600 ; N Umacron ; B 101 -18 716 708 ;
228
+ C -1 ; WX 600 ; N uring ; B 70 -15 592 678 ;
229
+ C 179 ; WX 600 ; N threesuperior ; B 193 222 526 616 ;
230
+ C 210 ; WX 600 ; N Ograve ; B 74 -18 645 784 ;
231
+ C 192 ; WX 600 ; N Agrave ; B -9 0 632 784 ;
232
+ C -1 ; WX 600 ; N Abreve ; B -9 0 684 784 ;
233
+ C 215 ; WX 600 ; N multiply ; B 104 39 606 478 ;
234
+ C 250 ; WX 600 ; N uacute ; B 70 -15 599 661 ;
235
+ C -1 ; WX 600 ; N Tcaron ; B 86 0 679 790 ;
236
+ C -1 ; WX 600 ; N partialdiff ; B 91 -38 627 728 ;
237
+ C 255 ; WX 600 ; N ydieresis ; B -21 -142 695 638 ;
238
+ C -1 ; WX 600 ; N Nacute ; B 8 -12 730 784 ;
239
+ C 238 ; WX 600 ; N icircumflex ; B 77 0 577 657 ;
240
+ C 202 ; WX 600 ; N Ecircumflex ; B 25 0 670 780 ;
241
+ C 228 ; WX 600 ; N adieresis ; B 61 -15 595 638 ;
242
+ C 235 ; WX 600 ; N edieresis ; B 81 -15 605 638 ;
243
+ C -1 ; WX 600 ; N cacute ; B 81 -15 649 661 ;
244
+ C -1 ; WX 600 ; N nacute ; B 18 0 639 661 ;
245
+ C -1 ; WX 600 ; N umacron ; B 70 -15 637 585 ;
246
+ C -1 ; WX 600 ; N Ncaron ; B 8 -12 730 790 ;
247
+ C 205 ; WX 600 ; N Iacute ; B 77 0 643 784 ;
248
+ C 177 ; WX 600 ; N plusminus ; B 76 24 614 515 ;
249
+ C 166 ; WX 600 ; N brokenbar ; B 217 -175 489 675 ;
250
+ C 174 ; WX 600 ; N registered ; B 53 -18 667 580 ;
251
+ C -1 ; WX 600 ; N Gbreve ; B 74 -18 684 784 ;
252
+ C -1 ; WX 600 ; N Idotaccent ; B 77 0 643 761 ;
253
+ C -1 ; WX 600 ; N summation ; B 15 -10 672 706 ;
254
+ C 200 ; WX 600 ; N Egrave ; B 25 0 670 784 ;
255
+ C -1 ; WX 600 ; N racute ; B 47 0 655 661 ;
256
+ C -1 ; WX 600 ; N omacron ; B 71 -15 637 585 ;
257
+ C -1 ; WX 600 ; N Zacute ; B 62 0 665 784 ;
258
+ C 142 ; WX 600 ; N Zcaron ; B 62 0 659 790 ;
259
+ C -1 ; WX 600 ; N greaterequal ; B 26 0 627 696 ;
260
+ C 208 ; WX 600 ; N Eth ; B 30 0 664 562 ;
261
+ C 199 ; WX 600 ; N Ccedilla ; B 74 -206 675 580 ;
262
+ C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 546 626 ;
263
+ C -1 ; WX 600 ; N tcaron ; B 118 -15 627 703 ;
264
+ C -1 ; WX 600 ; N eogonek ; B 81 -199 605 454 ;
265
+ C -1 ; WX 600 ; N Uogonek ; B 101 -199 716 562 ;
266
+ C 193 ; WX 600 ; N Aacute ; B -9 0 655 784 ;
267
+ C 196 ; WX 600 ; N Adieresis ; B -9 0 632 761 ;
268
+ C 232 ; WX 600 ; N egrave ; B 81 -15 605 661 ;
269
+ C -1 ; WX 600 ; N zacute ; B 81 0 614 661 ;
270
+ C -1 ; WX 600 ; N iogonek ; B 77 -199 546 658 ;
271
+ C 211 ; WX 600 ; N Oacute ; B 74 -18 645 784 ;
272
+ C 243 ; WX 600 ; N oacute ; B 71 -15 649 661 ;
273
+ C -1 ; WX 600 ; N amacron ; B 61 -15 637 585 ;
274
+ C -1 ; WX 600 ; N sacute ; B 66 -17 609 661 ;
275
+ C 239 ; WX 600 ; N idieresis ; B 77 0 561 618 ;
276
+ C 212 ; WX 600 ; N Ocircumflex ; B 74 -18 645 780 ;
277
+ C 217 ; WX 600 ; N Ugrave ; B 101 -18 716 784 ;
278
+ C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ;
279
+ C 254 ; WX 600 ; N thorn ; B -32 -142 622 626 ;
280
+ C 178 ; WX 600 ; N twosuperior ; B 191 230 542 616 ;
281
+ C 214 ; WX 600 ; N Odieresis ; B 74 -18 645 761 ;
282
+ C 181 ; WX 600 ; N mu ; B 49 -142 592 439 ;
283
+ C 236 ; WX 600 ; N igrave ; B 77 0 546 661 ;
284
+ C -1 ; WX 600 ; N ohungarumlaut ; B 71 -15 809 661 ;
285
+ C -1 ; WX 600 ; N Eogonek ; B 25 -199 670 562 ;
286
+ C -1 ; WX 600 ; N dcroat ; B 60 -15 712 626 ;
287
+ C 190 ; WX 600 ; N threequarters ; B 8 -60 699 661 ;
288
+ C -1 ; WX 600 ; N Scedilla ; B 54 -206 673 582 ;
289
+ C -1 ; WX 600 ; N lcaron ; B 77 0 731 626 ;
290
+ C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 692 562 ;
291
+ C -1 ; WX 600 ; N Lacute ; B 39 0 636 784 ;
292
+ C 153 ; WX 600 ; N trademark ; B 86 230 869 562 ;
293
+ C -1 ; WX 600 ; N edotaccent ; B 81 -15 605 638 ;
294
+ C 204 ; WX 600 ; N Igrave ; B 77 0 643 784 ;
295
+ C -1 ; WX 600 ; N Imacron ; B 77 0 663 708 ;
296
+ C -1 ; WX 600 ; N Lcaron ; B 39 0 757 562 ;
297
+ C 189 ; WX 600 ; N onehalf ; B 22 -60 716 661 ;
298
+ C -1 ; WX 600 ; N lessequal ; B 26 0 671 696 ;
299
+ C 244 ; WX 600 ; N ocircumflex ; B 71 -15 622 657 ;
300
+ C 241 ; WX 600 ; N ntilde ; B 18 0 643 636 ;
301
+ C -1 ; WX 600 ; N Uhungarumlaut ; B 101 -18 805 784 ;
302
+ C 201 ; WX 600 ; N Eacute ; B 25 0 670 784 ;
303
+ C -1 ; WX 600 ; N emacron ; B 81 -15 637 585 ;
304
+ C -1 ; WX 600 ; N gbreve ; B 40 -146 674 661 ;
305
+ C 188 ; WX 600 ; N onequarter ; B 13 -60 707 661 ;
306
+ C 138 ; WX 600 ; N Scaron ; B 54 -22 689 790 ;
307
+ C -1 ; WX 600 ; N Scommaaccent ; B 54 -250 673 582 ;
308
+ C -1 ; WX 600 ; N Ohungarumlaut ; B 74 -18 795 784 ;
309
+ C 176 ; WX 600 ; N degree ; B 173 243 570 616 ;
310
+ C 242 ; WX 600 ; N ograve ; B 71 -15 622 661 ;
311
+ C -1 ; WX 600 ; N Ccaron ; B 74 -18 689 790 ;
312
+ C 249 ; WX 600 ; N ugrave ; B 70 -15 592 661 ;
313
+ C -1 ; WX 600 ; N radical ; B 67 -104 635 778 ;
314
+ C -1 ; WX 600 ; N Dcaron ; B 30 0 664 790 ;
315
+ C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 655 454 ;
316
+ C 209 ; WX 600 ; N Ntilde ; B 8 -12 730 759 ;
317
+ C 245 ; WX 600 ; N otilde ; B 71 -15 643 636 ;
318
+ C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 617 562 ;
319
+ C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 636 562 ;
320
+ C 195 ; WX 600 ; N Atilde ; B -9 0 669 759 ;
321
+ C -1 ; WX 600 ; N Aogonek ; B -9 -199 632 562 ;
322
+ C 197 ; WX 600 ; N Aring ; B -9 0 632 801 ;
323
+ C 213 ; WX 600 ; N Otilde ; B 74 -18 669 759 ;
324
+ C -1 ; WX 600 ; N zdotaccent ; B 81 0 614 638 ;
325
+ C -1 ; WX 600 ; N Ecaron ; B 25 0 670 790 ;
326
+ C -1 ; WX 600 ; N Iogonek ; B 77 -199 643 562 ;
327
+ C -1 ; WX 600 ; N kcommaaccent ; B 33 -250 643 626 ;
328
+ C -1 ; WX 600 ; N minus ; B 114 203 596 313 ;
329
+ C 206 ; WX 600 ; N Icircumflex ; B 77 0 643 780 ;
330
+ C -1 ; WX 600 ; N ncaron ; B 18 0 633 667 ;
331
+ C -1 ; WX 600 ; N tcommaaccent ; B 118 -250 567 562 ;
332
+ C 172 ; WX 600 ; N logicalnot ; B 135 103 617 413 ;
333
+ C 246 ; WX 600 ; N odieresis ; B 71 -15 622 638 ;
334
+ C 252 ; WX 600 ; N udieresis ; B 70 -15 595 638 ;
335
+ C -1 ; WX 600 ; N notequal ; B 30 -47 626 563 ;
336
+ C -1 ; WX 600 ; N gcommaaccent ; B 40 -146 674 714 ;
337
+ C 240 ; WX 600 ; N eth ; B 93 -27 661 626 ;
338
+ C 158 ; WX 600 ; N zcaron ; B 81 0 643 667 ;
339
+ C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 615 454 ;
340
+ C 185 ; WX 600 ; N onesuperior ; B 212 230 514 616 ;
341
+ C -1 ; WX 600 ; N imacron ; B 77 0 575 585 ;
342
+ C 128 ; WX 600 ; N Euro ; B 0 0 0 0 ;
343
+ EndCharMetrics
344
+ EndFontMetrics
dompdf/lib/fonts/Courier-Oblique.afm ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ StartFontMetrics 4.1
2
+ Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
3
+ Comment Creation Date: Thu May 0:00:00 17:37:52 1997
4
+ Comment UniqueID 43051
5
+ Comment VMusage 16248 75829
6
+ FontName Courier-Oblique
7
+ FullName Courier Oblique
8
+ FamilyName Courier
9
+ Weight Medium
10
+ ItalicAngle -12
11
+ IsFixedPitch true
12
+ CharacterSet ExtendedRoman
13
+ FontBBox -27 -250 849 805
14
+ UnderlinePosition -100
15
+ UnderlineThickness 50
16
+ Version 003.000
17
+ Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
18
+ EncodingScheme WinAnsiEncoding
19
+ CapHeight 562
20
+ XHeight 426
21
+ Ascender 629
22
+ Descender -157
23
+ StdHW 51
24
+ StdVW 51
25
+ StartCharMetrics 317
26
+ C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
27
+ C 160 ; WX 600 ; N space ; B 0 0 0 0 ;
28
+ C 33 ; WX 600 ; N exclam ; B 243 -15 464 572 ;
29
+ C 34 ; WX 600 ; N quotedbl ; B 273 328 532 562 ;
30
+ C 35 ; WX 600 ; N numbersign ; B 133 -32 596 639 ;
31
+ C 36 ; WX 600 ; N dollar ; B 108 -126 596 662 ;
32
+ C 37 ; WX 600 ; N percent ; B 134 -15 599 622 ;
33
+ C 38 ; WX 600 ; N ampersand ; B 87 -15 580 543 ;
34
+ C 146 ; WX 600 ; N quoteright ; B 283 328 495 562 ;
35
+ C 40 ; WX 600 ; N parenleft ; B 313 -108 572 622 ;
36
+ C 41 ; WX 600 ; N parenright ; B 137 -108 396 622 ;
37
+ C 42 ; WX 600 ; N asterisk ; B 212 257 580 607 ;
38
+ C 43 ; WX 600 ; N plus ; B 129 44 580 470 ;
39
+ C 44 ; WX 600 ; N comma ; B 157 -112 370 122 ;
40
+ C 45 ; WX 600 ; N hyphen ; B 152 231 558 285 ;
41
+ C 173 ; WX 600 ; N hyphen ; B 152 231 558 285 ;
42
+ C 46 ; WX 600 ; N period ; B 238 -15 382 109 ;
43
+ C 47 ; WX 600 ; N slash ; B 112 -80 604 629 ;
44
+ C 48 ; WX 600 ; N zero ; B 154 -15 575 622 ;
45
+ C 49 ; WX 600 ; N one ; B 98 0 515 622 ;
46
+ C 50 ; WX 600 ; N two ; B 70 0 568 622 ;
47
+ C 51 ; WX 600 ; N three ; B 82 -15 538 622 ;
48
+ C 52 ; WX 600 ; N four ; B 108 0 541 622 ;
49
+ C 53 ; WX 600 ; N five ; B 99 -15 589 607 ;
50
+ C 54 ; WX 600 ; N six ; B 155 -15 629 622 ;
51
+ C 55 ; WX 600 ; N seven ; B 182 0 612 607 ;
52
+ C 56 ; WX 600 ; N eight ; B 132 -15 588 622 ;
53
+ C 57 ; WX 600 ; N nine ; B 93 -15 574 622 ;
54
+ C 58 ; WX 600 ; N colon ; B 238 -15 441 385 ;
55
+ C 59 ; WX 600 ; N semicolon ; B 157 -112 441 385 ;
56
+ C 60 ; WX 600 ; N less ; B 96 42 610 472 ;
57
+ C 61 ; WX 600 ; N equal ; B 109 138 600 376 ;
58
+ C 62 ; WX 600 ; N greater ; B 85 42 599 472 ;
59
+ C 63 ; WX 600 ; N question ; B 222 -15 583 572 ;
60
+ C 64 ; WX 600 ; N at ; B 127 -15 582 622 ;
61
+ C 65 ; WX 600 ; N A ; B 3 0 607 562 ;
62
+ C 66 ; WX 600 ; N B ; B 43 0 616 562 ;
63
+ C 67 ; WX 600 ; N C ; B 93 -18 655 580 ;
64
+ C 68 ; WX 600 ; N D ; B 43 0 645 562 ;
65
+ C 69 ; WX 600 ; N E ; B 53 0 660 562 ;
66
+ C 70 ; WX 600 ; N F ; B 53 0 660 562 ;
67
+ C 71 ; WX 600 ; N G ; B 83 -18 645 580 ;
68
+ C 72 ; WX 600 ; N H ; B 32 0 687 562 ;
69
+ C 73 ; WX 600 ; N I ; B 96 0 623 562 ;
70
+ C 74 ; WX 600 ; N J ; B 52 -18 685 562 ;
71
+ C 75 ; WX 600 ; N K ; B 38 0 671 562 ;
72
+ C 76 ; WX 600 ; N L ; B 47 0 607 562 ;
73
+ C 77 ; WX 600 ; N M ; B 4 0 715 562 ;
74
+ C 78 ; WX 600 ; N N ; B 7 -13 712 562 ;
75
+ C 79 ; WX 600 ; N O ; B 94 -18 625 580 ;
76
+ C 80 ; WX 600 ; N P ; B 79 0 644 562 ;
77
+ C 81 ; WX 600 ; N Q ; B 95 -138 625 580 ;
78
+ C 82 ; WX 600 ; N R ; B 38 0 598 562 ;
79
+ C 83 ; WX 600 ; N S ; B 76 -20 650 580 ;
80
+ C 84 ; WX 600 ; N T ; B 108 0 665 562 ;
81
+ C 85 ; WX 600 ; N U ; B 125 -18 702 562 ;
82
+ C 86 ; WX 600 ; N V ; B 105 -13 723 562 ;
83
+ C 87 ; WX 600 ; N W ; B 106 -13 722 562 ;
84
+ C 88 ; WX 600 ; N X ; B 23 0 675 562 ;
85
+ C 89 ; WX 600 ; N Y ; B 133 0 695 562 ;
86
+ C 90 ; WX 600 ; N Z ; B 86 0 610 562 ;
87
+ C 91 ; WX 600 ; N bracketleft ; B 246 -108 574 622 ;
88
+ C 92 ; WX 600 ; N backslash ; B 249 -80 468 629 ;
89
+ C 93 ; WX 600 ; N bracketright ; B 135 -108 463 622 ;
90
+ C 94 ; WX 600 ; N asciicircum ; B 175 354 587 622 ;
91
+ C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ;
92
+ C 145 ; WX 600 ; N quoteleft ; B 343 328 457 562 ;
93
+ C 97 ; WX 600 ; N a ; B 76 -15 569 441 ;
94
+ C 98 ; WX 600 ; N b ; B 29 -15 625 629 ;
95
+ C 99 ; WX 600 ; N c ; B 106 -15 608 441 ;
96
+ C 100 ; WX 600 ; N d ; B 85 -15 640 629 ;
97
+ C 101 ; WX 600 ; N e ; B 106 -15 598 441 ;
98
+ C 102 ; WX 600 ; N f ; B 114 0 662 629 ; L i fi ; L l fl ;
99
+ C 103 ; WX 600 ; N g ; B 61 -157 657 441 ;
100
+ C 104 ; WX 600 ; N h ; B 33 0 592 629 ;
101
+ C 105 ; WX 600 ; N i ; B 95 0 515 657 ;
102
+ C 106 ; WX 600 ; N j ; B 52 -157 550 657 ;
103
+ C 107 ; WX 600 ; N k ; B 58 0 633 629 ;
104
+ C 108 ; WX 600 ; N l ; B 95 0 515 629 ;
105
+ C 109 ; WX 600 ; N m ; B -5 0 615 441 ;
106
+ C 110 ; WX 600 ; N n ; B 26 0 585 441 ;
107
+ C 111 ; WX 600 ; N o ; B 102 -15 588 441 ;
108
+ C 112 ; WX 600 ; N p ; B -24 -157 605 441 ;
109
+ C 113 ; WX 600 ; N q ; B 85 -157 682 441 ;
110
+ C 114 ; WX 600 ; N r ; B 60 0 636 441 ;
111
+ C 115 ; WX 600 ; N s ; B 78 -15 584 441 ;
112
+ C 116 ; WX 600 ; N t ; B 167 -15 561 561 ;
113
+ C 117 ; WX 600 ; N u ; B 101 -15 572 426 ;
114
+ C 118 ; WX 600 ; N v ; B 90 -10 681 426 ;
115
+ C 119 ; WX 600 ; N w ; B 76 -10 695 426 ;
116
+ C 120 ; WX 600 ; N x ; B 20 0 655 426 ;
117
+ C 121 ; WX 600 ; N y ; B -4 -157 683 426 ;
118
+ C 122 ; WX 600 ; N z ; B 99 0 593 426 ;
119
+ C 123 ; WX 600 ; N braceleft ; B 233 -108 569 622 ;
120
+ C 124 ; WX 600 ; N bar ; B 222 -250 485 750 ;
121
+ C 125 ; WX 600 ; N braceright ; B 140 -108 477 622 ;
122
+ C 126 ; WX 600 ; N asciitilde ; B 116 197 600 320 ;
123
+ C 161 ; WX 600 ; N exclamdown ; B 225 -157 445 430 ;
124
+ C 162 ; WX 600 ; N cent ; B 151 -49 588 614 ;
125
+ C 163 ; WX 600 ; N sterling ; B 124 -21 621 611 ;
126
+ C -1 ; WX 600 ; N fraction ; B 84 -57 646 665 ;
127
+ C 165 ; WX 600 ; N yen ; B 120 0 693 562 ;
128
+ C 131 ; WX 600 ; N florin ; B -26 -143 671 622 ;
129
+ C 167 ; WX 600 ; N section ; B 104 -78 590 580 ;
130
+ C 164 ; WX 600 ; N currency ; B 94 58 628 506 ;
131
+ C 39 ; WX 600 ; N quotesingle ; B 345 328 460 562 ;
132
+ C 147 ; WX 600 ; N quotedblleft ; B 262 328 541 562 ;
133
+ C 170 ; WX 600 ; N guillemotleft ; B 92 70 652 446 ;
134
+ C 139 ; WX 600 ; N guilsinglleft ; B 204 70 540 446 ;
135
+ C 155 ; WX 600 ; N guilsinglright ; B 170 70 506 446 ;
136
+ C -1 ; WX 600 ; N fi ; B 3 0 619 629 ;
137
+ C -1 ; WX 600 ; N fl ; B 3 0 619 629 ;
138
+ C 150 ; WX 600 ; N endash ; B 124 231 586 285 ;
139
+ C 134 ; WX 600 ; N dagger ; B 217 -78 546 580 ;
140
+ C 135 ; WX 600 ; N daggerdbl ; B 163 -78 546 580 ;
141
+ C 183 ; WX 600 ; N periodcentered ; B 275 189 434 327 ;
142
+ C 182 ; WX 600 ; N paragraph ; B 100 -78 630 562 ;
143
+ C 149 ; WX 600 ; N bullet ; B 224 130 485 383 ;
144
+ C 130 ; WX 600 ; N quotesinglbase ; B 185 -134 397 100 ;
145
+ C 132 ; WX 600 ; N quotedblbase ; B 115 -134 478 100 ;
146
+ C 148 ; WX 600 ; N quotedblright ; B 213 328 576 562 ;
147
+ C 187 ; WX 600 ; N guillemotright ; B 58 70 618 446 ;
148
+ C 133 ; WX 600 ; N ellipsis ; B 46 -15 575 111 ;
149
+ C 137 ; WX 600 ; N perthousand ; B 59 -15 627 622 ;
150
+ C 191 ; WX 600 ; N questiondown ; B 105 -157 466 430 ;
151
+ C 96 ; WX 600 ; N grave ; B 294 497 484 672 ;
152
+ C 180 ; WX 600 ; N acute ; B 348 497 612 672 ;
153
+ C 136 ; WX 600 ; N circumflex ; B 229 477 581 654 ;
154
+ C 152 ; WX 600 ; N tilde ; B 212 489 629 606 ;
155
+ C 175 ; WX 600 ; N macron ; B 232 525 600 565 ;
156
+ C -1 ; WX 600 ; N breve ; B 279 501 576 609 ;
157
+ C -1 ; WX 600 ; N dotaccent ; B 373 537 478 640 ;
158
+ C 168 ; WX 600 ; N dieresis ; B 272 537 579 640 ;
159
+ C -1 ; WX 600 ; N ring ; B 332 463 500 627 ;
160
+ C 184 ; WX 600 ; N cedilla ; B 197 -151 344 10 ;
161
+ C -1 ; WX 600 ; N hungarumlaut ; B 239 497 683 672 ;
162
+ C -1 ; WX 600 ; N ogonek ; B 189 -172 377 4 ;
163
+ C -1 ; WX 600 ; N caron ; B 262 492 614 669 ;
164
+ C 151 ; WX 600 ; N emdash ; B 49 231 661 285 ;
165
+ C 198 ; WX 600 ; N AE ; B 3 0 655 562 ;
166
+ C 170 ; WX 600 ; N ordfeminine ; B 209 249 512 580 ;
167
+ C -1 ; WX 600 ; N Lslash ; B 47 0 607 562 ;
168
+ C 216 ; WX 600 ; N Oslash ; B 94 -80 625 629 ;
169
+ C 140 ; WX 600 ; N OE ; B 59 0 672 562 ;
170
+ C 186 ; WX 600 ; N ordmasculine ; B 210 249 535 580 ;
171
+ C 230 ; WX 600 ; N ae ; B 41 -15 626 441 ;
172
+ C -1 ; WX 600 ; N dotlessi ; B 95 0 515 426 ;
173
+ C -1 ; WX 600 ; N lslash ; B 95 0 587 629 ;
174
+ C 248 ; WX 600 ; N oslash ; B 102 -80 588 506 ;
175
+ C 156 ; WX 600 ; N oe ; B 54 -15 615 441 ;
176
+ C 223 ; WX 600 ; N germandbls ; B 48 -15 617 629 ;
177
+ C 207 ; WX 600 ; N Idieresis ; B 96 0 623 753 ;
178
+ C 233 ; WX 600 ; N eacute ; B 106 -15 612 672 ;
179
+ C -1 ; WX 600 ; N abreve ; B 76 -15 576 609 ;
180
+ C -1 ; WX 600 ; N uhungarumlaut ; B 101 -15 723 672 ;
181
+ C -1 ; WX 600 ; N ecaron ; B 106 -15 614 669 ;
182
+ C 159 ; WX 600 ; N Ydieresis ; B 133 0 695 753 ;
183
+ C 247 ; WX 600 ; N divide ; B 136 48 573 467 ;
184
+ C 221 ; WX 600 ; N Yacute ; B 133 0 695 805 ;
185
+ C 194 ; WX 600 ; N Acircumflex ; B 3 0 607 787 ;
186
+ C 225 ; WX 600 ; N aacute ; B 76 -15 612 672 ;
187
+ C 219 ; WX 600 ; N Ucircumflex ; B 125 -18 702 787 ;
188
+ C 253 ; WX 600 ; N yacute ; B -4 -157 683 672 ;
189
+ C -1 ; WX 600 ; N scommaaccent ; B 78 -250 584 441 ;
190
+ C 234 ; WX 600 ; N ecircumflex ; B 106 -15 598 654 ;
191
+ C -1 ; WX 600 ; N Uring ; B 125 -18 702 760 ;
192
+ C 220 ; WX 600 ; N Udieresis ; B 125 -18 702 753 ;
193
+ C -1 ; WX 600 ; N aogonek ; B 76 -172 569 441 ;
194
+ C 218 ; WX 600 ; N Uacute ; B 125 -18 702 805 ;
195
+ C -1 ; WX 600 ; N uogonek ; B 101 -172 572 426 ;
196
+ C 203 ; WX 600 ; N Edieresis ; B 53 0 660 753 ;
197
+ C -1 ; WX 600 ; N Dcroat ; B 43 0 645 562 ;
198
+ C -1 ; WX 600 ; N commaaccent ; B 145 -250 323 -58 ;
199
+ C 169 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
200
+ C -1 ; WX 600 ; N Emacron ; B 53 0 660 698 ;
201
+ C -1 ; WX 600 ; N ccaron ; B 106 -15 614 669 ;
202
+ C 229 ; WX 600 ; N aring ; B 76 -15 569 627 ;
203
+ C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 712 562 ;
204
+ C -1 ; WX 600 ; N lacute ; B 95 0 640 805 ;
205
+ C 224 ; WX 600 ; N agrave ; B 76 -15 569 672 ;
206
+ C -1 ; WX 600 ; N Tcommaaccent ; B 108 -250 665 562 ;
207
+ C -1 ; WX 600 ; N Cacute ; B 93 -18 655 805 ;
208
+ C 227 ; WX 600 ; N atilde ; B 76 -15 629 606 ;
209
+ C -1 ; WX 600 ; N Edotaccent ; B 53 0 660 753 ;
210
+ C 154 ; WX 600 ; N scaron ; B 78 -15 614 669 ;
211
+ C -1 ; WX 600 ; N scedilla ; B 78 -151 584 441 ;
212
+ C 237 ; WX 600 ; N iacute ; B 95 0 612 672 ;
213
+ C -1 ; WX 600 ; N lozenge ; B 94 0 519 706 ;
214
+ C -1 ; WX 600 ; N Rcaron ; B 38 0 642 802 ;
215
+ C -1 ; WX 600 ; N Gcommaaccent ; B 83 -250 645 580 ;
216
+ C 251 ; WX 600 ; N ucircumflex ; B 101 -15 572 654 ;
217
+ C 226 ; WX 600 ; N acircumflex ; B 76 -15 581 654 ;
218
+ C -1 ; WX 600 ; N Amacron ; B 3 0 607 698 ;
219
+ C -1 ; WX 600 ; N rcaron ; B 60 0 636 669 ;
220
+ C 231 ; WX 600 ; N ccedilla ; B 106 -151 614 441 ;
221
+ C -1 ; WX 600 ; N Zdotaccent ; B 86 0 610 753 ;
222
+ C 222 ; WX 600 ; N Thorn ; B 79 0 606 562 ;
223
+ C -1 ; WX 600 ; N Omacron ; B 94 -18 628 698 ;
224
+ C -1 ; WX 600 ; N Racute ; B 38 0 670 805 ;
225
+ C -1 ; WX 600 ; N Sacute ; B 76 -20 650 805 ;
226
+ C -1 ; WX 600 ; N dcaron ; B 85 -15 849 629 ;
227
+ C -1 ; WX 600 ; N Umacron ; B 125 -18 702 698 ;
228
+ C -1 ; WX 600 ; N uring ; B 101 -15 572 627 ;
229
+ C 179 ; WX 600 ; N threesuperior ; B 213 240 501 622 ;
230
+ C 210 ; WX 600 ; N Ograve ; B 94 -18 625 805 ;
231
+ C 192 ; WX 600 ; N Agrave ; B 3 0 607 805 ;
232
+ C -1 ; WX 600 ; N Abreve ; B 3 0 607 732 ;
233
+ C 215 ; WX 600 ; N multiply ; B 103 43 607 470 ;
234
+ C 250 ; WX 600 ; N uacute ; B 101 -15 602 672 ;
235
+ C -1 ; WX 600 ; N Tcaron ; B 108 0 665 802 ;
236
+ C -1 ; WX 600 ; N partialdiff ; B 45 -38 546 710 ;
237
+ C 255 ; WX 600 ; N ydieresis ; B -4 -157 683 620 ;
238
+ C -1 ; WX 600 ; N Nacute ; B 7 -13 712 805 ;
239
+ C 238 ; WX 600 ; N icircumflex ; B 95 0 551 654 ;
240
+ C 202 ; WX 600 ; N Ecircumflex ; B 53 0 660 787 ;
241
+ C 228 ; WX 600 ; N adieresis ; B 76 -15 575 620 ;
242
+ C 235 ; WX 600 ; N edieresis ; B 106 -15 598 620 ;
243
+ C -1 ; WX 600 ; N cacute ; B 106 -15 612 672 ;
244
+ C -1 ; WX 600 ; N nacute ; B 26 0 602 672 ;
245
+ C -1 ; WX 600 ; N umacron ; B 101 -15 600 565 ;
246
+ C -1 ; WX 600 ; N Ncaron ; B 7 -13 712 802 ;
247
+ C 205 ; WX 600 ; N Iacute ; B 96 0 640 805 ;
248
+ C 177 ; WX 600 ; N plusminus ; B 96 44 594 558 ;
249
+ C 166 ; WX 600 ; N brokenbar ; B 238 -175 469 675 ;
250
+ C 174 ; WX 600 ; N registered ; B 53 -18 667 580 ;
251
+ C -1 ; WX 600 ; N Gbreve ; B 83 -18 645 732 ;
252
+ C -1 ; WX 600 ; N Idotaccent ; B 96 0 623 753 ;
253
+ C -1 ; WX 600 ; N summation ; B 15 -10 670 706 ;
254
+ C 200 ; WX 600 ; N Egrave ; B 53 0 660 805 ;
255
+ C -1 ; WX 600 ; N racute ; B 60 0 636 672 ;
256
+ C -1 ; WX 600 ; N omacron ; B 102 -15 600 565 ;
257
+ C -1 ; WX 600 ; N Zacute ; B 86 0 670 805 ;
258
+ C 142 ; WX 600 ; N Zcaron ; B 86 0 642 802 ;
259
+ C -1 ; WX 600 ; N greaterequal ; B 98 0 594 710 ;
260
+ C 208 ; WX 600 ; N Eth ; B 43 0 645 562 ;
261
+ C 199 ; WX 600 ; N Ccedilla ; B 93 -151 658 580 ;
262
+ C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 515 629 ;
263
+ C -1 ; WX 600 ; N tcaron ; B 167 -15 587 717 ;
264
+ C -1 ; WX 600 ; N eogonek ; B 106 -172 598 441 ;
265
+ C -1 ; WX 600 ; N Uogonek ; B 124 -172 702 562 ;
266
+ C 193 ; WX 600 ; N Aacute ; B 3 0 660 805 ;
267
+ C 196 ; WX 600 ; N Adieresis ; B 3 0 607 753 ;
268
+ C 232 ; WX 600 ; N egrave ; B 106 -15 598 672 ;
269
+ C -1 ; WX 600 ; N zacute ; B 99 0 612 672 ;
270
+ C -1 ; WX 600 ; N iogonek ; B 95 -172 515 657 ;
271
+ C 211 ; WX 600 ; N Oacute ; B 94 -18 640 805 ;
272
+ C 243 ; WX 600 ; N oacute ; B 102 -15 612 672 ;
273
+ C -1 ; WX 600 ; N amacron ; B 76 -15 600 565 ;
274
+ C -1 ; WX 600 ; N sacute ; B 78 -15 612 672 ;
275
+ C 239 ; WX 600 ; N idieresis ; B 95 0 545 620 ;
276
+ C 212 ; WX 600 ; N Ocircumflex ; B 94 -18 625 787 ;
277
+ C 217 ; WX 600 ; N Ugrave ; B 125 -18 702 805 ;
278
+ C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;
279
+ C 254 ; WX 600 ; N thorn ; B -24 -157 605 629 ;
280
+ C 178 ; WX 600 ; N twosuperior ; B 230 249 535 622 ;
281
+ C 214 ; WX 600 ; N Odieresis ; B 94 -18 625 753 ;
282
+ C 181 ; WX 600 ; N mu ; B 72 -157 572 426 ;
283
+ C 236 ; WX 600 ; N igrave ; B 95 0 515 672 ;
284
+ C -1 ; WX 600 ; N ohungarumlaut ; B 102 -15 723 672 ;
285
+ C -1 ; WX 600 ; N Eogonek ; B 53 -172 660 562 ;
286
+ C -1 ; WX 600 ; N dcroat ; B 85 -15 704 629 ;
287
+ C 190 ; WX 600 ; N threequarters ; B 73 -56 659 666 ;
288
+ C -1 ; WX 600 ; N Scedilla ; B 76 -151 650 580 ;
289
+ C -1 ; WX 600 ; N lcaron ; B 95 0 667 629 ;
290
+ C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 671 562 ;
291
+ C -1 ; WX 600 ; N Lacute ; B 47 0 607 805 ;
292
+ C 153 ; WX 600 ; N trademark ; B 75 263 742 562 ;
293
+ C -1 ; WX 600 ; N edotaccent ; B 106 -15 598 620 ;
294
+ C 204 ; WX 600 ; N Igrave ; B 96 0 623 805 ;
295
+ C -1 ; WX 600 ; N Imacron ; B 96 0 628 698 ;
296
+ C -1 ; WX 600 ; N Lcaron ; B 47 0 632 562 ;
297
+ C 189 ; WX 600 ; N onehalf ; B 65 -57 669 665 ;
298
+ C -1 ; WX 600 ; N lessequal ; B 98 0 645 710 ;
299
+ C 244 ; WX 600 ; N ocircumflex ; B 102 -15 588 654 ;
300
+ C 241 ; WX 600 ; N ntilde ; B 26 0 629 606 ;
301
+ C -1 ; WX 600 ; N Uhungarumlaut ; B 125 -18 761 805 ;
302
+ C 201 ; WX 600 ; N Eacute ; B 53 0 670 805 ;
303
+ C -1 ; WX 600 ; N emacron ; B 106 -15 600 565 ;
304
+ C -1 ; WX 600 ; N gbreve ; B 61 -157 657 609 ;
305
+ C 188 ; WX 600 ; N onequarter ; B 65 -57 674 665 ;
306
+ C 138 ; WX 600 ; N Scaron ; B 76 -20 672 802 ;
307
+ C -1 ; WX 600 ; N Scommaaccent ; B 76 -250 650 580 ;
308
+ C -1 ; WX 600 ; N Ohungarumlaut ; B 94 -18 751 805 ;
309
+ C 176 ; WX 600 ; N degree ; B 214 269 576 622 ;
310
+ C 242 ; WX 600 ; N ograve ; B 102 -15 588 672 ;
311
+ C -1 ; WX 600 ; N Ccaron ; B 93 -18 672 802 ;
312
+ C 249 ; WX 600 ; N ugrave ; B 101 -15 572 672 ;
313
+ C -1 ; WX 600 ; N radical ; B 85 -15 765 792 ;
314
+ C -1 ; WX 600 ; N Dcaron ; B 43 0 645 802 ;
315
+ C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 636 441 ;
316
+ C 209 ; WX 600 ; N Ntilde ; B 7 -13 712 729 ;
317
+ C 245 ; WX 600 ; N otilde ; B 102 -15 629 606 ;
318
+ C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 598 562 ;
319
+ C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 607 562 ;
320
+ C 195 ; WX 600 ; N Atilde ; B 3 0 655 729 ;
321
+ C -1 ; WX 600 ; N Aogonek ; B 3 -172 607 562 ;
322
+ C 197 ; WX 600 ; N Aring ; B 3 0 607 750 ;
323
+ C 213 ; WX 600 ; N Otilde ; B 94 -18 655 729 ;
324
+ C -1 ; WX 600 ; N zdotaccent ; B 99 0 593 620 ;
325
+ C -1 ; WX 600 ; N Ecaron ; B 53 0 660 802 ;
326
+ C -1 ; WX 600 ; N Iogonek ; B 96 -172 623 562 ;
327
+ C -1 ; WX 600 ; N kcommaaccent ; B 58 -250 633 629 ;
328
+ C -1 ; WX 600 ; N minus ; B 129 232 580 283 ;
329
+ C 206 ; WX 600 ; N Icircumflex ; B 96 0 623 787 ;
330
+ C -1 ; WX 600 ; N ncaron ; B 26 0 614 669 ;
331
+ C -1 ; WX 600 ; N tcommaaccent ; B 165 -250 561 561 ;
332
+ C 172 ; WX 600 ; N logicalnot ; B 155 108 591 369 ;
333
+ C 246 ; WX 600 ; N odieresis ; B 102 -15 588 620 ;
334
+ C 252 ; WX 600 ; N udieresis ; B 101 -15 575 620 ;
335
+ C -1 ; WX 600 ; N notequal ; B 43 -16 621 529 ;
336
+ C -1 ; WX 600 ; N gcommaaccent ; B 61 -157 657 708 ;
337
+ C 240 ; WX 600 ; N eth ; B 102 -15 639 629 ;
338
+ C 158 ; WX 600 ; N zcaron ; B 99 0 624 669 ;
339
+ C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 585 441 ;
340
+ C 185 ; WX 600 ; N onesuperior ; B 231 249 491 622 ;
341
+ C -1 ; WX 600 ; N imacron ; B 95 0 543 565 ;
342
+ C 128 ; WX 600 ; N Euro ; B 0 0 0 0 ;
343
+ EndCharMetrics
344
+ EndFontMetrics
dompdf/lib/fonts/Courier.afm ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ StartFontMetrics 4.1
2
+ Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
3
+ Comment Creation Date: Thu May 1 17:27:09 1997
4
+ Comment UniqueID 43050
5
+ Comment VMusage 39754 50779
6
+ FontName Courier
7
+ FullName Courier
8
+ FamilyName Courier
9
+ Weight Medium
10
+ ItalicAngle 0
11
+ IsFixedPitch true
12
+ CharacterSet ExtendedRoman
13
+ FontBBox -23 -250 715 805
14
+ UnderlinePosition -100
15
+ UnderlineThickness 50
16
+ Version 003.000
17
+ Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
18
+ EncodingScheme WinAnsiEncoding
19
+ CapHeight 562
20
+ XHeight 426
21
+ Ascender 629
22
+ Descender -157
23
+ StdHW 51
24
+ StdVW 51
25
+ StartCharMetrics 317
26
+ C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
27
+ C 160 ; WX 600 ; N space ; B 0 0 0 0 ;
28
+ C 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ;
29
+ C 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ;
30
+ C 35 ; WX 600 ; N numbersign ; B 93 -32 507 639 ;
31
+ C 36 ; WX 600 ; N dollar ; B 105 -126 496 662 ;
32
+ C 37 ; WX 600 ; N percent ; B 81 -15 518 622 ;
33
+ C 38 ; WX 600 ; N ampersand ; B 63 -15 538 543 ;
34
+ C 146 ; WX 600 ; N quoteright ; B 213 328 376 562 ;
35
+ C 40 ; WX 600 ; N parenleft ; B 269 -108 440 622 ;
36
+ C 41 ; WX 600 ; N parenright ; B 160 -108 331 622 ;
37
+ C 42 ; WX 600 ; N asterisk ; B 116 257 484 607 ;
38
+ C 43 ; WX 600 ; N plus ; B 80 44 520 470 ;
39
+ C 44 ; WX 600 ; N comma ; B 181 -112 344 122 ;
40
+ C 45 ; WX 600 ; N hyphen ; B 103 231 497 285 ;
41
+ C 173 ; WX 600 ; N hyphen ; B 103 231 497 285 ;
42
+ C 46 ; WX 600 ; N period ; B 229 -15 371 109 ;
43
+ C 47 ; WX 600 ; N slash ; B 125 -80 475 629 ;
44
+ C 48 ; WX 600 ; N zero ; B 106 -15 494 622 ;
45
+ C 49 ; WX 600 ; N one ; B 96 0 505 622 ;
46
+ C 50 ; WX 600 ; N two ; B 70 0 471 622 ;
47
+ C 51 ; WX 600 ; N three ; B 75 -15 466 622 ;
48
+ C 52 ; WX 600 ; N four ; B 78 0 500 622 ;
49
+ C 53 ; WX 600 ; N five ; B 92 -15 497 607 ;
50
+ C 54 ; WX 600 ; N six ; B 111 -15 497 622 ;
51
+ C 55 ; WX 600 ; N seven ; B 82 0 483 607 ;
52
+ C 56 ; WX 600 ; N eight ; B 102 -15 498 622 ;
53
+ C 57 ; WX 600 ; N nine ; B 96 -15 489 622 ;
54
+ C 58 ; WX 600 ; N colon ; B 229 -15 371 385 ;
55
+ C 59 ; WX 600 ; N semicolon ; B 181 -112 371 385 ;
56
+ C 60 ; WX 600 ; N less ; B 41 42 519 472 ;
57
+ C 61 ; WX 600 ; N equal ; B 80 138 520 376 ;
58
+ C 62 ; WX 600 ; N greater ; B 66 42 544 472 ;
59
+ C 63 ; WX 600 ; N question ; B 129 -15 492 572 ;
60
+ C 64 ; WX 600 ; N at ; B 77 -15 533 622 ;
61
+ C 65 ; WX 600 ; N A ; B 3 0 597 562 ;
62
+ C 66 ; WX 600 ; N B ; B 43 0 559 562 ;
63
+ C 67 ; WX 600 ; N C ; B 41 -18 540 580 ;
64
+ C 68 ; WX 600 ; N D ; B 43 0 574 562 ;
65
+ C 69 ; WX 600 ; N E ; B 53 0 550 562 ;
66
+ C 70 ; WX 600 ; N F ; B 53 0 545 562 ;
67
+ C 71 ; WX 600 ; N G ; B 31 -18 575 580 ;
68
+ C 72 ; WX 600 ; N H ; B 32 0 568 562 ;
69
+ C 73 ; WX 600 ; N I ; B 96 0 504 562 ;
70
+ C 74 ; WX 600 ; N J ; B 34 -18 566 562 ;
71
+ C 75 ; WX 600 ; N K ; B 38 0 582 562 ;
72
+ C 76 ; WX 600 ; N L ; B 47 0 554 562 ;
73
+ C 77 ; WX 600 ; N M ; B 4 0 596 562 ;
74
+ C 78 ; WX 600 ; N N ; B 7 -13 593 562 ;
75
+ C 79 ; WX 600 ; N O ; B 43 -18 557 580 ;
76
+ C 80 ; WX 600 ; N P ; B 79 0 558 562 ;
77
+ C 81 ; WX 600 ; N Q ; B 43 -138 557 580 ;
78
+ C 82 ; WX 600 ; N R ; B 38 0 588 562 ;
79
+ C 83 ; WX 600 ; N S ; B 72 -20 529 580 ;
80
+ C 84 ; WX 600 ; N T ; B 38 0 563 562 ;
81
+ C 85 ; WX 600 ; N U ; B 17 -18 583 562 ;
82
+ C 86 ; WX 600 ; N V ; B -4 -13 604 562 ;
83
+ C 87 ; WX 600 ; N W ; B -3 -13 603 562 ;
84
+ C 88 ; WX 600 ; N X ; B 23 0 577 562 ;
85
+ C 89 ; WX 600 ; N Y ; B 24 0 576 562 ;
86
+ C 90 ; WX 600 ; N Z ; B 86 0 514 562 ;
87
+ C 91 ; WX 600 ; N bracketleft ; B 269 -108 442 622 ;
88
+ C 92 ; WX 600 ; N backslash ; B 118 -80 482 629 ;
89
+ C 93 ; WX 600 ; N bracketright ; B 158 -108 331 622 ;
90
+ C 94 ; WX 600 ; N asciicircum ; B 94 354 506 622 ;
91
+ C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
92
+ C 145 ; WX 600 ; N quoteleft ; B 224 328 387 562 ;
93
+ C 97 ; WX 600 ; N a ; B 53 -15 559 441 ;
94
+ C 98 ; WX 600 ; N b ; B 14 -15 575 629 ;
95
+ C 99 ; WX 600 ; N c ; B 66 -15 529 441 ;
96
+ C 100 ; WX 600 ; N d ; B 45 -15 591 629 ;
97
+ C 101 ; WX 600 ; N e ; B 66 -15 548 441 ;
98
+ C 102 ; WX 600 ; N f ; B 114 0 531 629 ; L i fi ; L l fl ;
99
+ C 103 ; WX 600 ; N g ; B 45 -157 566 441 ;
100
+ C 104 ; WX 600 ; N h ; B 18 0 582 629 ;
101
+ C 105 ; WX 600 ; N i ; B 95 0 505 657 ;
102
+ C 106 ; WX 600 ; N j ; B 82 -157 410 657 ;
103
+ C 107 ; WX 600 ; N k ; B 43 0 580 629 ;
104
+ C 108 ; WX 600 ; N l ; B 95 0 505 629 ;
105
+ C 109 ; WX 600 ; N m ; B -5 0 605 441 ;
106
+ C 110 ; WX 600 ; N n ; B 26 0 575 441 ;
107
+ C 111 ; WX 600 ; N o ; B 62 -15 538 441 ;
108
+ C 112 ; WX 600 ; N p ; B 9 -157 555 441 ;
109
+ C 113 ; WX 600 ; N q ; B 45 -157 591 441 ;
110
+ C 114 ; WX 600 ; N r ; B 60 0 559 441 ;
111
+ C 115 ; WX 600 ; N s ; B 80 -15 513 441 ;
112
+ C 116 ; WX 600 ; N t ; B 87 -15 530 561 ;
113
+ C 117 ; WX 600 ; N u ; B 21 -15 562 426 ;
114
+ C 118 ; WX 600 ; N v ; B 10 -10 590 426 ;
115
+ C 119 ; WX 600 ; N w ; B -4 -10 604 426 ;
116
+ C 120 ; WX 600 ; N x ; B 20 0 580 426 ;
117
+ C 121 ; WX 600 ; N y ; B 7 -157 592 426 ;
118
+ C 122 ; WX 600 ; N z ; B 99 0 502 426 ;
119
+ C 123 ; WX 600 ; N braceleft ; B 182 -108 437 622 ;
120
+ C 124 ; WX 600 ; N bar ; B 275 -250 326 750 ;
121
+ C 125 ; WX 600 ; N braceright ; B 163 -108 418 622 ;
122
+ C 126 ; WX 600 ; N asciitilde ; B 63 197 540 320 ;
123
+ C 161 ; WX 600 ; N exclamdown ; B 236 -157 364 430 ;
124
+ C 162 ; WX 600 ; N cent ; B 96 -49 500 614 ;
125
+ C 163 ; WX 600 ; N sterling ; B 84 -21 521 611 ;
126
+ C -1 ; WX 600 ; N fraction ; B 92 -57 509 665 ;
127
+ C 165 ; WX 600 ; N yen ; B 26 0 574 562 ;
128
+ C 131 ; WX 600 ; N florin ; B 4 -143 539 622 ;
129
+ C 167 ; WX 600 ; N section ; B 113 -78 488 580 ;
130
+ C 164 ; WX 600 ; N currency ; B 73 58 527 506 ;
131
+ C 39 ; WX 600 ; N quotesingle ; B 259 328 341 562 ;
132
+ C 147 ; WX 600 ; N quotedblleft ; B 143 328 471 562 ;
133
+ C 170 ; WX 600 ; N guillemotleft ; B 37 70 563 446 ;
134
+ C 139 ; WX 600 ; N guilsinglleft ; B 149 70 451 446 ;
135
+ C 155 ; WX 600 ; N guilsinglright ; B 149 70 451 446 ;
136
+ C -1 ; WX 600 ; N fi ; B 3 0 597 629 ;
137
+ C -1 ; WX 600 ; N fl ; B 3 0 597 629 ;
138
+ C 150 ; WX 600 ; N endash ; B 75 231 525 285 ;
139
+ C 134 ; WX 600 ; N dagger ; B 141 -78 459 580 ;
140
+ C 135 ; WX 600 ; N daggerdbl ; B 141 -78 459 580 ;
141
+ C 183 ; WX 600 ; N periodcentered ; B 222 189 378 327 ;
142
+ C 182 ; WX 600 ; N paragraph ; B 50 -78 511 562 ;
143
+ C 149 ; WX 600 ; N bullet ; B 172 130 428 383 ;
144
+ C 130 ; WX 600 ; N quotesinglbase ; B 213 -134 376 100 ;
145
+ C 132 ; WX 600 ; N quotedblbase ; B 143 -134 457 100 ;
146
+ C 148 ; WX 600 ; N quotedblright ; B 143 328 457 562 ;
147
+ C 187 ; WX 600 ; N guillemotright ; B 37 70 563 446 ;
148
+ C 133 ; WX 600 ; N ellipsis ; B 37 -15 563 111 ;
149
+ C 137 ; WX 600 ; N perthousand ; B 3 -15 600 622 ;
150
+ C 191 ; WX 600 ; N questiondown ; B 108 -157 471 430 ;
151
+ C 96 ; WX 600 ; N grave ; B 151 497 378 672 ;
152
+ C 180 ; WX 600 ; N acute ; B 242 497 469 672 ;
153
+ C 136 ; WX 600 ; N circumflex ; B 124 477 476 654 ;
154
+ C 152 ; WX 600 ; N tilde ; B 105 489 503 606 ;
155
+ C 175 ; WX 600 ; N macron ; B 120 525 480 565 ;
156
+ C -1 ; WX 600 ; N breve ; B 153 501 447 609 ;
157
+ C -1 ; WX 600 ; N dotaccent ; B 249 537 352 640 ;
158
+ C 168 ; WX 600 ; N dieresis ; B 148 537 453 640 ;
159
+ C -1 ; WX 600 ; N ring ; B 218 463 382 627 ;
160
+ C 184 ; WX 600 ; N cedilla ; B 224 -151 362 10 ;
161
+ C -1 ; WX 600 ; N hungarumlaut ; B 133 497 540 672 ;
162
+ C -1 ; WX 600 ; N ogonek ; B 211 -172 407 4 ;
163
+ C -1 ; WX 600 ; N caron ; B 124 492 476 669 ;
164
+ C 151 ; WX 600 ; N emdash ; B 0 231 600 285 ;
165
+ C 198 ; WX 600 ; N AE ; B 3 0 550 562 ;
166
+ C 170 ; WX 600 ; N ordfeminine ; B 156 249 442 580 ;
167
+ C -1 ; WX 600 ; N Lslash ; B 47 0 554 562 ;
168
+ C 216 ; WX 600 ; N Oslash ; B 43 -80 557 629 ;
169
+ C 140 ; WX 600 ; N OE ; B 7 0 567 562 ;
170
+ C 186 ; WX 600 ; N ordmasculine ; B 157 249 443 580 ;
171
+ C 230 ; WX 600 ; N ae ; B 19 -15 570 441 ;
172
+ C -1 ; WX 600 ; N dotlessi ; B 95 0 505 426 ;
173
+ C -1 ; WX 600 ; N lslash ; B 95 0 505 629 ;
174
+ C 248 ; WX 600 ; N oslash ; B 62 -80 538 506 ;
175
+ C 156 ; WX 600 ; N oe ; B 19 -15 559 441 ;
176
+ C 223 ; WX 600 ; N germandbls ; B 48 -15 588 629 ;
177
+ C 207 ; WX 600 ; N Idieresis ; B 96 0 504 753 ;
178
+ C 233 ; WX 600 ; N eacute ; B 66 -15 548 672 ;
179
+ C -1 ; WX 600 ; N abreve ; B 53 -15 559 609 ;
180
+ C -1 ; WX 600 ; N uhungarumlaut ; B 21 -15 580 672 ;
181
+ C -1 ; WX 600 ; N ecaron ; B 66 -15 548 669 ;
182
+ C 159 ; WX 600 ; N Ydieresis ; B 24 0 576 753 ;
183
+ C 247 ; WX 600 ; N divide ; B 87 48 513 467 ;
184
+ C 221 ; WX 600 ; N Yacute ; B 24 0 576 805 ;
185
+ C 194 ; WX 600 ; N Acircumflex ; B 3 0 597 787 ;
186
+ C 225 ; WX 600 ; N aacute ; B 53 -15 559 672 ;
187
+ C 219 ; WX 600 ; N Ucircumflex ; B 17 -18 583 787 ;
188
+ C 253 ; WX 600 ; N yacute ; B 7 -157 592 672 ;
189
+ C -1 ; WX 600 ; N scommaaccent ; B 80 -250 513 441 ;
190
+ C 234 ; WX 600 ; N ecircumflex ; B 66 -15 548 654 ;
191
+ C -1 ; WX 600 ; N Uring ; B 17 -18 583 760 ;
192
+ C 220 ; WX 600 ; N Udieresis ; B 17 -18 583 753 ;
193
+ C -1 ; WX 600 ; N aogonek ; B 53 -172 587 441 ;
194
+ C 218 ; WX 600 ; N Uacute ; B 17 -18 583 805 ;
195
+ C -1 ; WX 600 ; N uogonek ; B 21 -172 590 426 ;
196
+ C 203 ; WX 600 ; N Edieresis ; B 53 0 550 753 ;
197
+ C -1 ; WX 600 ; N Dcroat ; B 30 0 574 562 ;
198
+ C -1 ; WX 600 ; N commaaccent ; B 198 -250 335 -58 ;
199
+ C 169 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
200
+ C -1 ; WX 600 ; N Emacron ; B 53 0 550 698 ;
201
+ C -1 ; WX 600 ; N ccaron ; B 66 -15 529 669 ;
202
+ C 229 ; WX 600 ; N aring ; B 53 -15 559 627 ;
203
+ C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 593 562 ;
204
+ C -1 ; WX 600 ; N lacute ; B 95 0 505 805 ;
205
+ C 224 ; WX 600 ; N agrave ; B 53 -15 559 672 ;
206
+ C -1 ; WX 600 ; N Tcommaaccent ; B 38 -250 563 562 ;
207
+ C -1 ; WX 600 ; N Cacute ; B 41 -18 540 805 ;
208
+ C 227 ; WX 600 ; N atilde ; B 53 -15 559 606 ;
209
+ C -1 ; WX 600 ; N Edotaccent ; B 53 0 550 753 ;
210
+ C 154 ; WX 600 ; N scaron ; B 80 -15 513 669 ;
211
+ C -1 ; WX 600 ; N scedilla ; B 80 -151 513 441 ;
212
+ C 237 ; WX 600 ; N iacute ; B 95 0 505 672 ;
213
+ C -1 ; WX 600 ; N lozenge ; B 18 0 443 706 ;
214
+ C -1 ; WX 600 ; N Rcaron ; B 38 0 588 802 ;
215
+ C -1 ; WX 600 ; N Gcommaaccent ; B 31 -250 575 580 ;
216
+ C 251 ; WX 600 ; N ucircumflex ; B 21 -15 562 654 ;
217
+ C 226 ; WX 600 ; N acircumflex ; B 53 -15 559 654 ;
218
+ C -1 ; WX 600 ; N Amacron ; B 3 0 597 698 ;
219
+ C -1 ; WX 600 ; N rcaron ; B 60 0 559 669 ;
220
+ C 231 ; WX 600 ; N ccedilla ; B 66 -151 529 441 ;
221
+ C -1 ; WX 600 ; N Zdotaccent ; B 86 0 514 753 ;
222
+ C 222 ; WX 600 ; N Thorn ; B 79 0 538 562 ;
223
+ C -1 ; WX 600 ; N Omacron ; B 43 -18 557 698 ;
224
+ C -1 ; WX 600 ; N Racute ; B 38 0 588 805 ;
225
+ C -1 ; WX 600 ; N Sacute ; B 72 -20 529 805 ;
226
+ C -1 ; WX 600 ; N dcaron ; B 45 -15 715 629 ;
227
+ C -1 ; WX 600 ; N Umacron ; B 17 -18 583 698 ;
228
+ C -1 ; WX 600 ; N uring ; B 21 -15 562 627 ;
229
+ C 179 ; WX 600 ; N threesuperior ; B 155 240 406 622 ;
230
+ C 210 ; WX 600 ; N Ograve ; B 43 -18 557 805 ;
231
+ C 192 ; WX 600 ; N Agrave ; B 3 0 597 805 ;
232
+ C -1 ; WX 600 ; N Abreve ; B 3 0 597 732 ;
233
+ C 215 ; WX 600 ; N multiply ; B 87 43 515 470 ;
234
+ C 250 ; WX 600 ; N uacute ; B 21 -15 562 672 ;
235
+ C -1 ; WX 600 ; N Tcaron ; B 38 0 563 802 ;
236
+ C -1 ; WX 600 ; N partialdiff ; B 17 -38 459 710 ;
237
+ C 255 ; WX 600 ; N ydieresis ; B 7 -157 592 620 ;
238
+ C -1 ; WX 600 ; N Nacute ; B 7 -13 593 805 ;
239
+ C 238 ; WX 600 ; N icircumflex ; B 94 0 505 654 ;
240
+ C 202 ; WX 600 ; N Ecircumflex ; B 53 0 550 787 ;
241
+ C 228 ; WX 600 ; N adieresis ; B 53 -15 559 620 ;
242
+ C 235 ; WX 600 ; N edieresis ; B 66 -15 548 620 ;
243
+ C -1 ; WX 600 ; N cacute ; B 66 -15 529 672 ;
244
+ C -1 ; WX 600 ; N nacute ; B 26 0 575 672 ;
245
+ C -1 ; WX 600 ; N umacron ; B 21 -15 562 565 ;
246
+ C -1 ; WX 600 ; N Ncaron ; B 7 -13 593 802 ;
247
+ C 205 ; WX 600 ; N Iacute ; B 96 0 504 805 ;
248
+ C 177 ; WX 600 ; N plusminus ; B 87 44 513 558 ;
249
+ C 166 ; WX 600 ; N brokenbar ; B 275 -175 326 675 ;
250
+ C 174 ; WX 600 ; N registered ; B 0 -18 600 580 ;
251
+ C -1 ; WX 600 ; N Gbreve ; B 31 -18 575 732 ;
252
+ C -1 ; WX 600 ; N Idotaccent ; B 96 0 504 753 ;
253
+ C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ;
254
+ C 200 ; WX 600 ; N Egrave ; B 53 0 550 805 ;
255
+ C -1 ; WX 600 ; N racute ; B 60 0 559 672 ;
256
+ C -1 ; WX 600 ; N omacron ; B 62 -15 538 565 ;
257
+ C -1 ; WX 600 ; N Zacute ; B 86 0 514 805 ;
258
+ C 142 ; WX 600 ; N Zcaron ; B 86 0 514 802 ;
259
+ C -1 ; WX 600 ; N greaterequal ; B 98 0 502 710 ;
260
+ C 208 ; WX 600 ; N Eth ; B 30 0 574 562 ;
261
+ C 199 ; WX 600 ; N Ccedilla ; B 41 -151 540 580 ;
262
+ C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 505 629 ;
263
+ C -1 ; WX 600 ; N tcaron ; B 87 -15 530 717 ;
264
+ C -1 ; WX 600 ; N eogonek ; B 66 -172 548 441 ;
265
+ C -1 ; WX 600 ; N Uogonek ; B 17 -172 583 562 ;
266
+ C 193 ; WX 600 ; N Aacute ; B 3 0 597 805 ;
267
+ C 196 ; WX 600 ; N Adieresis ; B 3 0 597 753 ;
268
+ C 232 ; WX 600 ; N egrave ; B 66 -15 548 672 ;
269
+ C -1 ; WX 600 ; N zacute ; B 99 0 502 672 ;
270
+ C -1 ; WX 600 ; N iogonek ; B 95 -172 505 657 ;
271
+ C 211 ; WX 600 ; N Oacute ; B 43 -18 557 805 ;
272
+ C 243 ; WX 600 ; N oacute ; B 62 -15 538 672 ;
273
+ C -1 ; WX 600 ; N amacron ; B 53 -15 559 565 ;
274
+ C -1 ; WX 600 ; N sacute ; B 80 -15 513 672 ;
275
+ C 239 ; WX 600 ; N idieresis ; B 95 0 505 620 ;
276
+ C 212 ; WX 600 ; N Ocircumflex ; B 43 -18 557 787 ;
277
+ C 217 ; WX 600 ; N Ugrave ; B 17 -18 583 805 ;
278
+ C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;
279
+ C 254 ; WX 600 ; N thorn ; B -6 -157 555 629 ;
280
+ C 178 ; WX 600 ; N twosuperior ; B 177 249 424 622 ;
281
+ C 214 ; WX 600 ; N Odieresis ; B 43 -18 557 753 ;
282
+ C 181 ; WX 600 ; N mu ; B 21 -157 562 426 ;
283
+ C 236 ; WX 600 ; N igrave ; B 95 0 505 672 ;
284
+ C -1 ; WX 600 ; N ohungarumlaut ; B 62 -15 580 672 ;
285
+ C -1 ; WX 600 ; N Eogonek ; B 53 -172 561 562 ;
286
+ C -1 ; WX 600 ; N dcroat ; B 45 -15 591 629 ;
287
+ C 190 ; WX 600 ; N threequarters ; B 8 -56 593 666 ;
288
+ C -1 ; WX 600 ; N Scedilla ; B 72 -151 529 580 ;
289
+ C -1 ; WX 600 ; N lcaron ; B 95 0 533 629 ;
290
+ C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 582 562 ;
291
+ C -1 ; WX 600 ; N Lacute ; B 47 0 554 805 ;
292
+ C 153 ; WX 600 ; N trademark ; B -23 263 623 562 ;
293
+ C -1 ; WX 600 ; N edotaccent ; B 66 -15 548 620 ;
294
+ C 204 ; WX 600 ; N Igrave ; B 96 0 504 805 ;
295
+ C -1 ; WX 600 ; N Imacron ; B 96 0 504 698 ;
296
+ C -1 ; WX 600 ; N Lcaron ; B 47 0 554 562 ;
297
+ C 189 ; WX 600 ; N onehalf ; B 0 -57 611 665 ;
298
+ C -1 ; WX 600 ; N lessequal ; B 98 0 502 710 ;
299
+ C 244 ; WX 600 ; N ocircumflex ; B 62 -15 538 654 ;
300
+ C 241 ; WX 600 ; N ntilde ; B 26 0 575 606 ;
301
+ C -1 ; WX 600 ; N Uhungarumlaut ; B 17 -18 590 805 ;
302
+ C 201 ; WX 600 ; N Eacute ; B 53 0 550 805 ;
303
+ C -1 ; WX 600 ; N emacron ; B 66 -15 548 565 ;
304
+ C -1 ; WX 600 ; N gbreve ; B 45 -157 566 609 ;
305
+ C 188 ; WX 600 ; N onequarter ; B 0 -57 600 665 ;
306
+ C 138 ; WX 600 ; N Scaron ; B 72 -20 529 802 ;
307
+ C -1 ; WX 600 ; N Scommaaccent ; B 72 -250 529 580 ;
308
+ C -1 ; WX 600 ; N Ohungarumlaut ; B 43 -18 580 805 ;
309
+ C 176 ; WX 600 ; N degree ; B 123 269 477 622 ;
310
+ C 242 ; WX 600 ; N ograve ; B 62 -15 538 672 ;
311
+ C -1 ; WX 600 ; N Ccaron ; B 41 -18 540 802 ;
312
+ C 249 ; WX 600 ; N ugrave ; B 21 -15 562 672 ;
313
+ C -1 ; WX 600 ; N radical ; B 3 -15 597 792 ;
314
+ C -1 ; WX 600 ; N Dcaron ; B 43 0 574 802 ;
315
+ C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 559 441 ;
316
+ C 209 ; WX 600 ; N Ntilde ; B 7 -13 593 729 ;
317
+ C 245 ; WX 600 ; N otilde ; B 62 -15 538 606 ;
318
+ C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 588 562 ;
319
+ C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 554 562 ;
320
+ C 195 ; WX 600 ; N Atilde ; B 3 0 597 729 ;
321
+ C -1 ; WX 600 ; N Aogonek ; B 3 -172 608 562 ;
322
+ C 197 ; WX 600 ; N Aring ; B 3 0 597 750 ;
323
+ C 213 ; WX 600 ; N Otilde ; B 43 -18 557 729 ;
324
+ C -1 ; WX 600 ; N zdotaccent ; B 99 0 502 620 ;
325
+ C -1 ; WX 600 ; N Ecaron ; B 53 0 550 802 ;
326
+ C -1 ; WX 600 ; N Iogonek ; B 96 -172 504 562 ;
327
+ C -1 ; WX 600 ; N kcommaaccent ; B 43 -250 580 629 ;
328
+ C -1 ; WX 600 ; N minus ; B 80 232 520 283 ;
329
+ C 206 ; WX 600 ; N Icircumflex ; B 96 0 504 787 ;
330
+ C -1 ; WX 600 ; N ncaron ; B 26 0 575 669 ;
331
+ C -1 ; WX 600 ; N tcommaaccent ; B 87 -250 530 561 ;
332
+ C 172 ; WX 600 ; N logicalnot ; B 87 108 513 369 ;
333
+ C 246 ; WX 600 ; N odieresis ; B 62 -15 538 620 ;
334
+ C 252 ; WX 600 ; N udieresis ; B 21 -15 562 620 ;
335
+ C -1 ; WX 600 ; N notequal ; B 15 -16 540 529 ;
336
+ C -1 ; WX 600 ; N gcommaaccent ; B 45 -157 566 708 ;
337
+ C 240 ; WX 600 ; N eth ; B 62 -15 538 629 ;
338
+ C 158 ; WX 600 ; N zcaron ; B 99 0 502 669 ;
339
+ C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 575 441 ;
340
+ C 185 ; WX 600 ; N onesuperior ; B 172 249 428 622 ;
341
+ C -1 ; WX 600 ; N imacron ; B 95 0 505 565 ;
342
+ C 128 ; WX 600 ; N Euro ; B 0 0 0 0 ;
343
+ EndCharMetrics
344
+ EndFontMetrics
{mPDF/ttfonts → dompdf/lib/fonts}/DejaVuSans-Bold.ttf RENAMED
Binary file
dompdf/lib/fonts/DejaVuSans-Bold.ufm ADDED
@@ -0,0 +1,5734 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ StartFontMetrics 4.1
2
+ Notice Converted by PHP-font-lib
3
+ Comment http://php-font-lib.googlecode.com/
4
+ EncodingScheme FontSpecific
5
+ FontName DejaVu Sans
6
+ FontSubfamily Bold
7
+ UniqueID DejaVu Sans Bold
8
+ FullName DejaVu Sans Bold
9
+ Version Version 2.32
10
+ PostScriptName DejaVuSans-Bold
11
+ Manufacturer DejaVu fonts team
12
+ FontVendorURL http://dejavu.sourceforge.net
13
+ LicenseURL http://dejavu.sourceforge.net/wiki/index.php/License
14
+ PreferredFamily DejaVu Sans
15
+ PreferredSubfamily Bold
16
+ Weight Bold
17
+ ItalicAngle 0
18
+ IsFixedPitch false
19
+ UnderlineThickness 44
20
+ UnderlinePosition -63
21
+ FontHeightOffset 0
22
+ Ascender 928
23
+ Descender -236
24
+ FontBBox -1069 -415 1975 1174
25
+ StartCharMetrics 5690
26
+ U 32 ; WX 348 ; N space ; G 3
27
+ U 33 ; WX 456 ; N exclam ; G 4
28
+ U 34 ; WX 521 ; N quotedbl ; G 5
29
+ U 35 ; WX 838 ; N numbersign ; G 6
30
+ U 36 ; WX 696 ; N dollar ; G 7
31
+ U 37 ; WX 1002 ; N percent ; G 8
32
+ U 38 ; WX 872 ; N ampersand ; G 9
33
+ U 39 ; WX 306 ; N quotesingle ; G 10
34
+ U 40 ; WX 457 ; N parenleft ; G 11
35
+ U 41 ; WX 457 ; N parenright ; G 12
36
+ U 42 ; WX 523 ; N asterisk ; G 13
37
+ U 43 ; WX 838 ; N plus ; G 14
38
+ U 44 ; WX 380 ; N comma ; G 15
39
+ U 45 ; WX 415 ; N hyphen ; G 16
40
+ U 46 ; WX 380 ; N period ; G 17
41
+ U 47 ; WX 365 ; N slash ; G 18
42
+ U 48 ; WX 696 ; N zero ; G 19
43
+ U 49 ; WX 696 ; N one ; G 20
44
+ U 50 ; WX 696 ; N two ; G 21
45
+ U 51 ; WX 696 ; N three ; G 22
46
+ U 52 ; WX 696 ; N four ; G 23
47
+ U 53 ; WX 696 ; N five ; G 24
48
+ U 54 ; WX 696 ; N six ; G 25
49
+ U 55 ; WX 696 ; N seven ; G 26
50
+ U 56 ; WX 696 ; N eight ; G 27
51
+ U 57 ; WX 696 ; N nine ; G 28
52
+ U 58 ; WX 400 ; N colon ; G 29
53
+ U 59 ; WX 400 ; N semicolon ; G 30
54
+ U 60 ; WX 838 ; N less ; G 31
55
+ U 61 ; WX 838 ; N equal ; G 32
56
+ U 62 ; WX 838 ; N greater ; G 33
57
+ U 63 ; WX 580 ; N question ; G 34
58
+ U 64 ; WX 1000 ; N at ; G 35
59
+ U 65 ; WX 774 ; N A ; G 36
60
+ U 66 ; WX 762 ; N B ; G 37
61
+ U 67 ; WX 734 ; N C ; G 38
62
+ U 68 ; WX 830 ; N D ; G 39
63
+ U 69 ; WX 683 ; N E ; G 40
64
+ U 70 ; WX 683 ; N F ; G 41
65
+ U 71 ; WX 821 ; N G ; G 42
66
+ U 72 ; WX 837 ; N H ; G 43
67
+ U 73 ; WX 372 ; N I ; G 44
68
+ U 74 ; WX 372 ; N J ; G 45
69
+ U 75 ; WX 775 ; N K ; G 46
70
+ U 76 ; WX 637 ; N L ; G 47
71
+ U 77 ; WX 995 ; N M ; G 48
72
+ U 78 ; WX 837 ; N N ; G 49
73
+ U 79 ; WX 850 ; N O ; G 50
74
+ U 80 ; WX 733 ; N P ; G 51
75
+ U 81 ; WX 850 ; N Q ; G 52
76
+ U 82 ; WX 770 ; N R ; G 53
77
+ U 83 ; WX 720 ; N S ; G 54
78
+ U 84 ; WX 682 ; N T ; G 55
79
+ U 85 ; WX 812 ; N U ; G 56
80
+ U 86 ; WX 774 ; N V ; G 57
81
+ U 87 ; WX 1103 ; N W ; G 58
82
+ U 88 ; WX 771 ; N X ; G 59
83
+ U 89 ; WX 724 ; N Y ; G 60
84
+ U 90 ; WX 725 ; N Z ; G 61
85
+ U 91 ; WX 457 ; N bracketleft ; G 62
86
+ U 92 ; WX 365 ; N backslash ; G 63
87
+ U 93 ; WX 457 ; N bracketright ; G 64
88
+ U 94 ; WX 838 ; N asciicircum ; G 65
89
+ U 95 ; WX 500 ; N underscore ; G 66
90
+ U 96 ; WX 500 ; N grave ; G 67
91
+ U 97 ; WX 675 ; N a ; G 68
92
+ U 98 ; WX 716 ; N b ; G 69
93
+ U 99 ; WX 593 ; N c ; G 70
94
+ U 100 ; WX 716 ; N d ; G 71
95
+ U 101 ; WX 678 ; N e ; G 72
96
+ U 102 ; WX 435 ; N f ; G 73
97
+ U 103 ; WX 716 ; N g ; G 74
98
+ U 104 ; WX 712 ; N h ; G 75
99
+ U 105 ; WX 343 ; N i ; G 76
100
+ U 106 ; WX 343 ; N j ; G 77
101
+ U 107 ; WX 665 ; N k ; G 78
102
+ U 108 ; WX 343 ; N l ; G 79
103
+ U 109 ; WX 1042 ; N m ; G 80
104
+ U 110 ; WX 712 ; N n ; G 81
105
+ U 111 ; WX 687 ; N o ; G 82
106
+ U 112 ; WX 716 ; N p ; G 83
107
+ U 113 ; WX 716 ; N q ; G 84
108
+ U 114 ; WX 493 ; N r ; G 85
109
+ U 115 ; WX 595 ; N s ; G 86
110
+ U 116 ; WX 478 ; N t ; G 87
111
+ U 117 ; WX 712 ; N u ; G 88
112
+ U 118 ; WX 652 ; N v ; G 89
113
+ U 119 ; WX 924 ; N w ; G 90
114
+ U 120 ; WX 645 ; N x ; G 91
115
+ U 121 ; WX 652 ; N y ; G 92
116
+ U 122 ; WX 582 ; N z ; G 93
117
+ U 123 ; WX 712 ; N braceleft ; G 94
118
+ U 124 ; WX 365 ; N bar ; G 95
119
+ U 125 ; WX 712 ; N braceright ; G 96
120
+ U 126 ; WX 838 ; N asciitilde ; G 97
121
+ U 160 ; WX 348 ; N nbspace ; G 98
122
+ U 161 ; WX 456 ; N exclamdown ; G 99
123
+ U 162 ; WX 696 ; N cent ; G 100
124
+ U 163 ; WX 696 ; N sterling ; G 101
125
+ U 164 ; WX 636 ; N currency ; G 102
126
+ U 165 ; WX 696 ; N yen ; G 103
127
+ U 166 ; WX 365 ; N brokenbar ; G 104
128
+ U 167 ; WX 500 ; N section ; G 105
129
+ U 168 ; WX 500 ; N dieresis ; G 106
130
+ U 169 ; WX 1000 ; N copyright ; G 107
131
+ U 170 ; WX 564 ; N ordfeminine ; G 108
132
+ U 171 ; WX 646 ; N guillemotleft ; G 109
133
+ U 172 ; WX 838 ; N logicalnot ; G 110
134
+ U 173 ; WX 415 ; N sfthyphen ; G 111
135
+ U 174 ; WX 1000 ; N registered ; G 112
136
+ U 175 ; WX 500 ; N macron ; G 113
137
+ U 176 ; WX 500 ; N degree ; G 114
138
+ U 177 ; WX 838 ; N plusminus ; G 115
139
+ U 178 ; WX 438 ; N twosuperior ; G 116
140
+ U 179 ; WX 438 ; N threesuperior ; G 117
141
+ U 180 ; WX 500 ; N acute ; G 118
142
+ U 181 ; WX 736 ; N mu ; G 119
143
+ U 182 ; WX 636 ; N paragraph ; G 120
144
+ U 183 ; WX 380 ; N periodcentered ; G 121
145
+ U 184 ; WX 500 ; N cedilla ; G 122
146
+ U 185 ; WX 438 ; N onesuperior ; G 123
147
+ U 186 ; WX 564 ; N ordmasculine ; G 124
148
+ U 187 ; WX 646 ; N guillemotright ; G 125
149
+ U 188 ; WX 1035 ; N onequarter ; G 126
150
+ U 189 ; WX 1035 ; N onehalf ; G 127
151
+ U 190 ; WX 1035 ; N threequarters ; G 128
152
+ U 191 ; WX 580 ; N questiondown ; G 129
153
+ U 192 ; WX 774 ; N Agrave ; G 130
154
+ U 193 ; WX 774 ; N Aacute ; G 131
155
+ U 194 ; WX 774 ; N Acircumflex ; G 132
156
+ U 195 ; WX 774 ; N Atilde ; G 133
157
+ U 196 ; WX 774 ; N Adieresis ; G 134
158
+ U 197 ; WX 774 ; N Aring ; G 135
159
+ U 198 ; WX 1085 ; N AE ; G 136
160
+ U 199 ; WX 734 ; N Ccedilla ; G 137
161
+ U 200 ; WX 683 ; N Egrave ; G 138
162
+ U 201 ; WX 683 ; N Eacute ; G 139
163
+ U 202 ; WX 683 ; N Ecircumflex ; G 140
164
+ U 203 ; WX 683 ; N Edieresis ; G 141
165
+ U 204 ; WX 372 ; N Igrave ; G 142
166
+ U 205 ; WX 372 ; N Iacute ; G 143
167
+ U 206 ; WX 372 ; N Icircumflex ; G 144
168
+ U 207 ; WX 372 ; N Idieresis ; G 145
169
+ U 208 ; WX 838 ; N Eth ; G 146
170
+ U 209 ; WX 837 ; N Ntilde ; G 147
171
+ U 210 ; WX 850 ; N Ograve ; G 148
172
+ U 211 ; WX 850 ; N Oacute ; G 149
173
+ U 212 ; WX 850 ; N Ocircumflex ; G 150
174
+ U 213 ; WX 850 ; N Otilde ; G 151
175
+ U 214 ; WX 850 ; N Odieresis ; G 152
176
+ U 215 ; WX 838 ; N multiply ; G 153
177
+ U 216 ; WX 850 ; N Oslash ; G 154
178
+ U 217 ; WX 812 ; N Ugrave ; G 155
179
+ U 218 ; WX 812 ; N Uacute ; G 156
180
+ U 219 ; WX 812 ; N Ucircumflex ; G 157
181
+ U 220 ; WX 812 ; N Udieresis ; G 158
182
+ U 221 ; WX 724 ; N Yacute ; G 159
183
+ U 222 ; WX 738 ; N Thorn ; G 160
184
+ U 223 ; WX 719 ; N germandbls ; G 161
185
+ U 224 ; WX 675 ; N agrave ; G 162
186
+ U 225 ; WX 675 ; N aacute ; G 163
187
+ U 226 ; WX 675 ; N acircumflex ; G 164
188
+ U 227 ; WX 675 ; N atilde ; G 165
189
+ U 228 ; WX 675 ; N adieresis ; G 166
190
+ U 229 ; WX 675 ; N aring ; G 167
191
+ U 230 ; WX 1048 ; N ae ; G 168
192
+ U 231 ; WX 593 ; N ccedilla ; G 169
193
+ U 232 ; WX 678 ; N egrave ; G 170
194
+ U 233 ; WX 678 ; N eacute ; G 171
195
+ U 234 ; WX 678 ; N ecircumflex ; G 172
196
+ U 235 ; WX 678 ; N edieresis ; G 173
197
+ U 236 ; WX 343 ; N igrave ; G 174
198
+ U 237 ; WX 343 ; N iacute ; G 175
199
+ U 238 ; WX 343 ; N icircumflex ; G 176
200
+ U 239 ; WX 343 ; N idieresis ; G 177
201
+ U 240 ; WX 687 ; N eth ; G 178
202
+ U 241 ; WX 712 ; N ntilde ; G 179
203
+ U 242 ; WX 687 ; N ograve ; G 180
204
+ U 243 ; WX 687 ; N oacute ; G 181
205
+ U 244 ; WX 687 ; N ocircumflex ; G 182
206
+ U 245 ; WX 687 ; N otilde ; G 183
207
+ U 246 ; WX 687 ; N odieresis ; G 184
208
+ U 247 ; WX 838 ; N divide ; G 185
209
+ U 248 ; WX 687 ; N oslash ; G 186
210
+ U 249 ; WX 712 ; N ugrave ; G 187
211
+ U 250 ; WX 712 ; N uacute ; G 188
212
+ U 251 ; WX 712 ; N ucircumflex ; G 189
213
+ U 252 ; WX 712 ; N udieresis ; G 190
214
+ U 253 ; WX 652 ; N yacute ; G 191
215
+ U 254 ; WX 716 ; N thorn ; G 192
216
+ U 255 ; WX 652 ; N ydieresis ; G 193
217
+ U 256 ; WX 774 ; N Amacron ; G 194
218
+ U 257 ; WX 675 ; N amacron ; G 195
219
+ U 258 ; WX 774 ; N Abreve ; G 196
220
+ U 259 ; WX 675 ; N abreve ; G 197
221
+ U 260 ; WX 774 ; N Aogonek ; G 198
222
+ U 261 ; WX 675 ; N aogonek ; G 199
223
+ U 262 ; WX 734 ; N Cacute ; G 200
224
+ U 263 ; WX 593 ; N cacute ; G 201
225
+ U 264 ; WX 734 ; N Ccircumflex ; G 202
226
+ U 265 ; WX 593 ; N ccircumflex ; G 203
227
+ U 266 ; WX 734 ; N Cdotaccent ; G 204
228
+ U 267 ; WX 593 ; N cdotaccent ; G 205
229
+ U 268 ; WX 734 ; N Ccaron ; G 206
230
+ U 269 ; WX 593 ; N ccaron ; G 207
231
+ U 270 ; WX 830 ; N Dcaron ; G 208
232
+ U 271 ; WX 716 ; N dcaron ; G 209
233
+ U 272 ; WX 838 ; N Dcroat ; G 210
234
+ U 273 ; WX 716 ; N dmacron ; G 211
235
+ U 274 ; WX 683 ; N Emacron ; G 212
236
+ U 275 ; WX 678 ; N emacron ; G 213
237
+ U 276 ; WX 683 ; N Ebreve ; G 214
238
+ U 277 ; WX 678 ; N ebreve ; G 215
239
+ U 278 ; WX 683 ; N Edotaccent ; G 216
240
+ U 279 ; WX 678 ; N edotaccent ; G 217
241
+ U 280 ; WX 683 ; N Eogonek ; G 218
242
+ U 281 ; WX 678 ; N eogonek ; G 219
243
+ U 282 ; WX 683 ; N Ecaron ; G 220
244
+ U 283 ; WX 678 ; N ecaron ; G 221
245
+ U 284 ; WX 821 ; N Gcircumflex ; G 222
246
+ U 285 ; WX 716 ; N gcircumflex ; G 223
247
+ U 286 ; WX 821 ; N Gbreve ; G 224
248
+ U 287 ; WX 716 ; N gbreve ; G 225
249
+ U 288 ; WX 821 ; N Gdotaccent ; G 226
250
+ U 289 ; WX 716 ; N gdotaccent ; G 227
251
+ U 290 ; WX 821 ; N Gcommaaccent ; G 228
252
+ U 291 ; WX 716 ; N gcommaaccent ; G 229
253
+ U 292 ; WX 837 ; N Hcircumflex ; G 230
254
+ U 293 ; WX 712 ; N hcircumflex ; G 231
255
+ U 294 ; WX 974 ; N Hbar ; G 232
256
+ U 295 ; WX 790 ; N hbar ; G 233
257
+ U 296 ; WX 372 ; N Itilde ; G 234
258
+ U 297 ; WX 343 ; N itilde ; G 235
259
+ U 298 ; WX 372 ; N Imacron ; G 236
260
+ U 299 ; WX 343 ; N imacron ; G 237
261
+ U 300 ; WX 372 ; N Ibreve ; G 238
262
+ U 301 ; WX 343 ; N ibreve ; G 239
263
+ U 302 ; WX 372 ; N Iogonek ; G 240
264
+ U 303 ; WX 343 ; N iogonek ; G 241
265
+ U 304 ; WX 372 ; N Idot ; G 242
266
+ U 305 ; WX 343 ; N dotlessi ; G 243
267
+ U 306 ; WX 744 ; N IJ ; G 244
268
+ U 307 ; WX 686 ; N ij ; G 245
269
+ U 308 ; WX 372 ; N Jcircumflex ; G 246
270
+ U 309 ; WX 343 ; N jcircumflex ; G 247
271
+ U 310 ; WX 775 ; N Kcommaaccent ; G 248
272
+ U 311 ; WX 665 ; N kcommaaccent ; G 249
273
+ U 312 ; WX 665 ; N kgreenlandic ; G 250
274
+ U 313 ; WX 637 ; N Lacute ; G 251
275
+ U 314 ; WX 343 ; N lacute ; G 252
276
+ U 315 ; WX 637 ; N Lcommaaccent ; G 253
277
+ U 316 ; WX 343 ; N lcommaaccent ; G 254
278
+ U 317 ; WX 637 ; N Lcaron ; G 255
279
+ U 318 ; WX 479 ; N lcaron ; G 256
280
+ U 319 ; WX 637 ; N Ldot ; G 257
281
+ U 320 ; WX 557 ; N ldot ; G 258
282
+ U 321 ; WX 642 ; N Lslash ; G 259
283
+ U 322 ; WX 371 ; N lslash ; G 260
284
+ U 323 ; WX 837 ; N Nacute ; G 261
285
+ U 324 ; WX 712 ; N nacute ; G 262
286
+ U 325 ; WX 837 ; N Ncommaaccent ; G 263
287
+ U 326 ; WX 712 ; N ncommaaccent ; G 264
288
+ U 327 ; WX 837 ; N Ncaron ; G 265
289
+ U 328 ; WX 712 ; N ncaron ; G 266
290
+ U 329 ; WX 983 ; N napostrophe ; G 267
291
+ U 330 ; WX 837 ; N Eng ; G 268
292
+ U 331 ; WX 712 ; N eng ; G 269
293
+ U 332 ; WX 850 ; N Omacron ; G 270
294
+ U 333 ; WX 687 ; N omacron ; G 271
295
+ U 334 ; WX 850 ; N Obreve ; G 272
296
+ U 335 ; WX 687 ; N obreve ; G 273
297
+ U 336 ; WX 850 ; N Ohungarumlaut ; G 274
298
+ U 337 ; WX 687 ; N ohungarumlaut ; G 275
299
+ U 338 ; WX 1167 ; N OE ; G 276
300
+ U 339 ; WX 1094 ; N oe ; G 277
301
+ U 340 ; WX 770 ; N Racute ; G 278
302
+ U 341 ; WX 493 ; N racute ; G 279
303
+ U 342 ; WX 770 ; N Rcommaaccent ; G 280
304
+ U 343 ; WX 493 ; N rcommaaccent ; G 281
305
+ U 344 ; WX 770 ; N Rcaron ; G 282
306
+ U 345 ; WX 493 ; N rcaron ; G 283
307
+ U 346 ; WX 720 ; N Sacute ; G 284
308
+ U 347 ; WX 595 ; N sacute ; G 285
309
+ U 348 ; WX 720 ; N Scircumflex ; G 286
310
+ U 349 ; WX 595 ; N scircumflex ; G 287
311
+ U 350 ; WX 720 ; N Scedilla ; G 288
312
+ U 351 ; WX 595 ; N scedilla ; G 289
313
+ U 352 ; WX 720 ; N Scaron ; G 290
314
+ U 353 ; WX 595 ; N scaron ; G 291
315
+ U 354 ; WX 682 ; N Tcommaaccent ; G 292
316
+ U 355 ; WX 478 ; N tcommaaccent ; G 293
317
+ U 356 ; WX 682 ; N Tcaron ; G 294
318
+ U 357 ; WX 478 ; N tcaron ; G 295
319
+ U 358 ; WX 682 ; N Tbar ; G 296
320
+ U 359 ; WX 478 ; N tbar ; G 297
321
+ U 360 ; WX 812 ; N Utilde ; G 298
322
+ U 361 ; WX 712 ; N utilde ; G 299
323
+ U 362 ; WX 812 ; N Umacron ; G 300
324
+ U 363 ; WX 712 ; N umacron ; G 301
325
+ U 364 ; WX 812 ; N Ubreve ; G 302
326
+ U 365 ; WX 712 ; N ubreve ; G 303
327
+ U 366 ; WX 812 ; N Uring ; G 304
328
+ U 367 ; WX 712 ; N uring ; G 305
329
+ U 368 ; WX 812 ; N Uhungarumlaut ; G 306
330
+ U 369 ; WX 712 ; N uhungarumlaut ; G 307
331
+ U 370 ; WX 812 ; N Uogonek ; G 308
332
+ U 371 ; WX 712 ; N uogonek ; G 309
333
+ U 372 ; WX 1103 ; N Wcircumflex ; G 310
334
+ U 373 ; WX 924 ; N wcircumflex ; G 311
335
+ U 374 ; WX 724 ; N Ycircumflex ; G 312
336
+ U 375 ; WX 652 ; N ycircumflex ; G 313
337
+ U 376 ; WX 724 ; N Ydieresis ; G 314
338
+ U 377 ; WX 725 ; N Zacute ; G 315
339
+ U 378 ; WX 582 ; N zacute ; G 316
340
+ U 379 ; WX 725 ; N Zdotaccent ; G 317
341
+ U 380 ; WX 582 ; N zdotaccent ; G 318
342
+ U 381 ; WX 725 ; N Zcaron ; G 319
343
+ U 382 ; WX 582 ; N zcaron ; G 320
344
+ U 383 ; WX 435 ; N longs ; G 321
345
+ U 384 ; WX 716 ; N uni0180 ; G 322
346
+ U 385 ; WX 811 ; N uni0181 ; G 323
347
+ U 386 ; WX 762 ; N uni0182 ; G 324
348
+ U 387 ; WX 716 ; N uni0183 ; G 325
349
+ U 388 ; WX 762 ; N uni0184 ; G 326
350
+ U 389 ; WX 716 ; N uni0185 ; G 327
351
+ U 390 ; WX 734 ; N uni0186 ; G 328
352
+ U 391 ; WX 734 ; N uni0187 ; G 329
353
+ U 392 ; WX 593 ; N uni0188 ; G 330
354
+ U 393 ; WX 838 ; N uni0189 ; G 331
355
+ U 394 ; WX 879 ; N uni018A ; G 332
356
+ U 395 ; WX 757 ; N uni018B ; G 333
357
+ U 396 ; WX 716 ; N uni018C ; G 334
358
+ U 397 ; WX 688 ; N uni018D ; G 335
359
+ U 398 ; WX 683 ; N uni018E ; G 336
360
+ U 399 ; WX 849 ; N uni018F ; G 337
361
+ U 400 ; WX 696 ; N uni0190 ; G 338
362
+ U 401 ; WX 683 ; N uni0191 ; G 339
363
+ U 402 ; WX 435 ; N florin ; G 340
364
+ U 403 ; WX 821 ; N uni0193 ; G 341
365
+ U 404 ; WX 793 ; N uni0194 ; G 342
366
+ U 405 ; WX 1045 ; N uni0195 ; G 343
367
+ U 406 ; WX 436 ; N uni0196 ; G 344
368
+ U 407 ; WX 389 ; N uni0197 ; G 345
369
+ U 408 ; WX 775 ; N uni0198 ; G 346
370
+ U 409 ; WX 665 ; N uni0199 ; G 347
371
+ U 410 ; WX 360 ; N uni019A ; G 348
372
+ U 411 ; WX 592 ; N uni019B ; G 349
373
+ U 412 ; WX 1042 ; N uni019C ; G 350
374
+ U 413 ; WX 837 ; N uni019D ; G 351
375
+ U 414 ; WX 712 ; N uni019E ; G 352
376
+ U 415 ; WX 850 ; N uni019F ; G 353
377
+ U 416 ; WX 874 ; N Ohorn ; G 354
378
+ U 417 ; WX 687 ; N ohorn ; G 355
379
+ U 418 ; WX 1083 ; N uni01A2 ; G 356
380
+ U 419 ; WX 912 ; N uni01A3 ; G 357
381
+ U 420 ; WX 782 ; N uni01A4 ; G 358
382
+ U 421 ; WX 716 ; N uni01A5 ; G 359
383
+ U 422 ; WX 770 ; N uni01A6 ; G 360
384
+ U 423 ; WX 720 ; N uni01A7 ; G 361
385
+ U 424 ; WX 595 ; N uni01A8 ; G 362
386
+ U 425 ; WX 683 ; N uni01A9 ; G 363
387
+ U 426 ; WX 552 ; N uni01AA ; G 364
388
+ U 427 ; WX 478 ; N uni01AB ; G 365
389
+ U 428 ; WX 707 ; N uni01AC ; G 366
390
+ U 429 ; WX 478 ; N uni01AD ; G 367
391
+ U 430 ; WX 682 ; N uni01AE ; G 368
392
+ U 431 ; WX 835 ; N Uhorn ; G 369
393
+ U 432 ; WX 712 ; N uhorn ; G 370
394
+ U 433 ; WX 850 ; N uni01B1 ; G 371
395
+ U 434 ; WX 813 ; N uni01B2 ; G 372
396
+ U 435 ; WX 797 ; N uni01B3 ; G 373
397
+ U 436 ; WX 778 ; N uni01B4 ; G 374
398
+ U 437 ; WX 725 ; N uni01B5 ; G 375
399
+ U 438 ; WX 582 ; N uni01B6 ; G 376
400
+ U 439 ; WX 772 ; N uni01B7 ; G 377
401
+ U 440 ; WX 772 ; N uni01B8 ; G 378
402
+ U 441 ; WX 641 ; N uni01B9 ; G 379
403
+ U 442 ; WX 582 ; N uni01BA ; G 380
404
+ U 443 ; WX 696 ; N uni01BB ; G 381
405
+ U 444 ; WX 772 ; N uni01BC ; G 382
406
+ U 445 ; WX 641 ; N uni01BD ; G 383
407
+ U 446 ; WX 573 ; N uni01BE ; G 384
408
+ U 447 ; WX 716 ; N uni01BF ; G 385
409
+ U 448 ; WX 372 ; N uni01C0 ; G 386
410
+ U 449 ; WX 659 ; N uni01C1 ; G 387
411
+ U 450 ; WX 544 ; N uni01C2 ; G 388
412
+ U 451 ; WX 372 ; N uni01C3 ; G 389
413
+ U 452 ; WX 1555 ; N uni01C4 ; G 390
414
+ U 453 ; WX 1412 ; N uni01C5 ; G 391
415
+ U 454 ; WX 1298 ; N uni01C6 ; G 392
416
+ U 455 ; WX 1009 ; N uni01C7 ; G 393
417
+ U 456 ; WX 980 ; N uni01C8 ; G 394
418
+ U 457 ; WX 686 ; N uni01C9 ; G 395
419
+ U 458 ; WX 1209 ; N uni01CA ; G 396
420
+ U 459 ; WX 1180 ; N uni01CB ; G 397
421
+ U 460 ; WX 1055 ; N uni01CC ; G 398
422
+ U 461 ; WX 774 ; N uni01CD ; G 399
423
+ U 462 ; WX 675 ; N uni01CE ; G 400
424
+ U 463 ; WX 372 ; N uni01CF ; G 401
425
+ U 464 ; WX 343 ; N uni01D0 ; G 402
426
+ U 465 ; WX 850 ; N uni01D1 ; G 403
427
+ U 466 ; WX 687 ; N uni01D2 ; G 404
428
+ U 467 ; WX 812 ; N uni01D3 ; G 405
429
+ U 468 ; WX 712 ; N uni01D4 ; G 406
430
+ U 469 ; WX 812 ; N uni01D5 ; G 407
431
+ U 470 ; WX 712 ; N uni01D6 ; G 408
432
+ U 471 ; WX 812 ; N uni01D7 ; G 409
433
+ U 472 ; WX 712 ; N uni01D8 ; G 410
434
+ U 473 ; WX 812 ; N uni01D9 ; G 411
435
+ U 474 ; WX 712 ; N uni01DA ; G 412
436
+ U 475 ; WX 812 ; N uni01DB ; G 413
437
+ U 476 ; WX 712 ; N uni01DC ; G 414
438
+ U 477 ; WX 678 ; N uni01DD ; G 415
439
+ U 478 ; WX 774 ; N uni01DE ; G 416
440
+ U 479 ; WX 675 ; N uni01DF ; G 417
441
+ U 480 ; WX 774 ; N uni01E0 ; G 418
442
+ U 481 ; WX 675 ; N uni01E1 ; G 419
443
+ U 482 ; WX 1085 ; N uni01E2 ; G 420
444
+ U 483 ; WX 1048 ; N uni01E3 ; G 421
445
+ U 484 ; WX 821 ; N uni01E4 ; G 422
446
+ U 485 ; WX 716 ; N uni01E5 ; G 423
447
+ U 486 ; WX 821 ; N Gcaron ; G 424
448
+ U 487 ; WX 716 ; N gcaron ; G 425
449
+ U 488 ; WX 775 ; N uni01E8 ; G 426
450
+ U 489 ; WX 665 ; N uni01E9 ; G 427
451
+ U 490 ; WX 850 ; N uni01EA ; G 428
452
+ U 491 ; WX 687 ; N uni01EB ; G 429
453
+ U 492 ; WX 850 ; N uni01EC ; G 430
454
+ U 493 ; WX 687 ; N uni01ED ; G 431
455
+ U 494 ; WX 772 ; N uni01EE ; G 432
456
+ U 495 ; WX 582 ; N uni01EF ; G 433
457
+ U 496 ; WX 343 ; N uni01F0 ; G 434
458
+ U 497 ; WX 1555 ; N uni01F1 ; G 435
459
+ U 498 ; WX 1412 ; N uni01F2 ; G 436
460
+ U 499 ; WX 1298 ; N uni01F3 ; G 437
461
+ U 500 ; WX 821 ; N uni01F4 ; G 438
462
+ U 501 ; WX 716 ; N uni01F5 ; G 439
463
+ U 502 ; WX 1289 ; N uni01F6 ; G 440
464
+ U 503 ; WX 787 ; N uni01F7 ; G 441
465
+ U 504 ; WX 837 ; N uni01F8 ; G 442
466
+ U 505 ; WX 712 ; N uni01F9 ; G 443
467
+ U 506 ; WX 774 ; N Aringacute ; G 444
468
+ U 507 ; WX 675 ; N aringacute ; G 445
469
+ U 508 ; WX 1085 ; N AEacute ; G 446
470
+ U 509 ; WX 1048 ; N aeacute ; G 447
471
+ U 510 ; WX 850 ; N Oslashacute ; G 448
472
+ U 511 ; WX 687 ; N oslashacute ; G 449
473
+ U 512 ; WX 774 ; N uni0200 ; G 450
474
+ U 513 ; WX 675 ; N uni0201 ; G 451
475
+ U 514 ; WX 774 ; N uni0202 ; G 452
476
+ U 515 ; WX 675 ; N uni0203 ; G 453
477
+ U 516 ; WX 683 ; N uni0204 ; G 454
478
+ U 517 ; WX 678 ; N uni0205 ; G 455
479
+ U 518 ; WX 683 ; N uni0206 ; G 456
480
+ U 519 ; WX 678 ; N uni0207 ; G 457
481
+ U 520 ; WX 372 ; N uni0208 ; G 458
482
+ U 521 ; WX 343 ; N uni0209 ; G 459
483
+ U 522 ; WX 372 ; N uni020A ; G 460
484
+ U 523 ; WX 343 ; N uni020B ; G 461
485
+ U 524 ; WX 850 ; N uni020C ; G 462
486
+ U 525 ; WX 687 ; N uni020D ; G 463
487
+ U 526 ; WX 850 ; N uni020E ; G 464
488
+ U 527 ; WX 687 ; N uni020F ; G 465
489
+ U 528 ; WX 770 ; N uni0210 ; G 466
490
+ U 529 ; WX 493 ; N uni0211 ; G 467
491
+ U 530 ; WX 770 ; N uni0212 ; G 468
492
+ U 531 ; WX 493 ; N uni0213 ; G 469
493
+ U 532 ; WX 812 ; N uni0214 ; G 470
494
+ U 533 ; WX 712 ; N uni0215 ; G 471
495
+ U 534 ; WX 812 ; N uni0216 ; G 472
496
+ U 535 ; WX 712 ; N uni0217 ; G 473
497
+ U 536 ; WX 720 ; N Scommaaccent ; G 474
498
+ U 537 ; WX 595 ; N scommaaccent ; G 475
499
+ U 538 ; WX 682 ; N uni021A ; G 476
500
+ U 539 ; WX 478 ; N uni021B ; G 477
501
+ U 540 ; WX 690 ; N uni021C ; G 478
502
+ U 541 ; WX 607 ; N uni021D ; G 479
503
+ U 542 ; WX 837 ; N uni021E ; G 480
504
+ U 543 ; WX 712 ; N uni021F ; G 481
505
+ U 544 ; WX 837 ; N uni0220 ; G 482
506
+ U 545 ; WX 865 ; N uni0221 ; G 483
507
+ U 546 ; WX 809 ; N uni0222 ; G 484
508
+ U 547 ; WX 659 ; N uni0223 ; G 485
509
+ U 548 ; WX 725 ; N uni0224 ; G 486
510
+ U 549 ; WX 582 ; N uni0225 ; G 487
511
+ U 550 ; WX 774 ; N uni0226 ; G 488
512
+ U 551 ; WX 675 ; N uni0227 ; G 489
513
+ U 552 ; WX 683 ; N uni0228 ; G 490
514
+ U 553 ; WX 678 ; N uni0229 ; G 491
515
+ U 554 ; WX 850 ; N uni022A ; G 492
516
+ U 555 ; WX 687 ; N uni022B ; G 493
517
+ U 556 ; WX 850 ; N uni022C ; G 494
518
+ U 557 ; WX 687 ; N uni022D ; G 495
519
+ U 558 ; WX 850 ; N uni022E ; G 496
520
+ U 559 ; WX 687 ; N uni022F ; G 497
521
+ U 560 ; WX 850 ; N uni0230 ; G 498
522
+ U 561 ; WX 687 ; N