From 793468f14075b64d8c4f992b6fe2b1d3bcd3341b Mon Sep 17 00:00:00 2001 From: Adnan Date: Thu, 3 Jun 2010 15:28:00 +0500 Subject: [PATCH 01/48] classes structure --- classes/diaSchema.class.php | 7 +++++++ classes/epsSchema.class.php | 7 +++++++ classes/pdfSchema.class.php | 7 +++++++ classes/relationSchema.abstract.class.php | 18 ++++++++++++++++++ classes/svgSchema.class.php | 7 +++++++ classes/visioSchema.class.php | 7 +++++++ 6 files changed, 53 insertions(+) create mode 100644 classes/diaSchema.class.php create mode 100644 classes/epsSchema.class.php create mode 100644 classes/pdfSchema.class.php create mode 100644 classes/relationSchema.abstract.class.php create mode 100644 classes/svgSchema.class.php create mode 100644 classes/visioSchema.class.php diff --git a/classes/diaSchema.class.php b/classes/diaSchema.class.php new file mode 100644 index 000000000..e0fb1ccd2 --- /dev/null +++ b/classes/diaSchema.class.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/classes/epsSchema.class.php b/classes/epsSchema.class.php new file mode 100644 index 000000000..3e0b942a0 --- /dev/null +++ b/classes/epsSchema.class.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/classes/pdfSchema.class.php b/classes/pdfSchema.class.php new file mode 100644 index 000000000..65fa8a043 --- /dev/null +++ b/classes/pdfSchema.class.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/classes/relationSchema.abstract.class.php b/classes/relationSchema.abstract.class.php new file mode 100644 index 000000000..ae8f62d02 --- /dev/null +++ b/classes/relationSchema.abstract.class.php @@ -0,0 +1,18 @@ +pageTitle=$title; + } + + public function createPage() + { + + } +} +?> \ No newline at end of file diff --git a/classes/svgSchema.class.php b/classes/svgSchema.class.php new file mode 100644 index 000000000..61ccc3bfc --- /dev/null +++ b/classes/svgSchema.class.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/classes/visioSchema.class.php b/classes/visioSchema.class.php new file mode 100644 index 000000000..167d5220b --- /dev/null +++ b/classes/visioSchema.class.php @@ -0,0 +1,7 @@ + \ No newline at end of file From 14e6af7d8aeb05ea32ee7defa9aee335041bd3cc Mon Sep 17 00:00:00 2001 From: Adnan Date: Thu, 3 Jun 2010 16:01:12 +0500 Subject: [PATCH 02/48] done --- classes/relationSchema.abstract.class.php | 35 +++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/classes/relationSchema.abstract.class.php b/classes/relationSchema.abstract.class.php index ae8f62d02..a30e9d2a1 100644 --- a/classes/relationSchema.abstract.class.php +++ b/classes/relationSchema.abstract.class.php @@ -14,5 +14,40 @@ abstract class exportRelationSchema { } + public function createPageHTML() + { + ?> +
+
+ + + + + + + + + + + + + +
+
+ +
+ +
+
+
+ +
+
+ \ No newline at end of file From 7d125e01a822a26571e4938eb1e97beac6056a73 Mon Sep 17 00:00:00 2001 From: Adnan Date: Thu, 3 Jun 2010 17:07:01 +0500 Subject: [PATCH 03/48] moved classes to libraries directory --- classes/diaSchema.class.php | 7 --- classes/epsSchema.class.php | 7 --- classes/pdfSchema.class.php | 7 --- classes/relationSchema.abstract.class.php | 53 ----------------------- classes/svgSchema.class.php | 7 --- classes/visioSchema.class.php | 7 --- 6 files changed, 88 deletions(-) delete mode 100644 classes/diaSchema.class.php delete mode 100644 classes/epsSchema.class.php delete mode 100644 classes/pdfSchema.class.php delete mode 100644 classes/relationSchema.abstract.class.php delete mode 100644 classes/svgSchema.class.php delete mode 100644 classes/visioSchema.class.php diff --git a/classes/diaSchema.class.php b/classes/diaSchema.class.php deleted file mode 100644 index e0fb1ccd2..000000000 --- a/classes/diaSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file diff --git a/classes/epsSchema.class.php b/classes/epsSchema.class.php deleted file mode 100644 index 3e0b942a0..000000000 --- a/classes/epsSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file diff --git a/classes/pdfSchema.class.php b/classes/pdfSchema.class.php deleted file mode 100644 index 65fa8a043..000000000 --- a/classes/pdfSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file diff --git a/classes/relationSchema.abstract.class.php b/classes/relationSchema.abstract.class.php deleted file mode 100644 index a30e9d2a1..000000000 --- a/classes/relationSchema.abstract.class.php +++ /dev/null @@ -1,53 +0,0 @@ -pageTitle=$title; - } - - public function createPage() - { - - } - public function createPageHTML() - { - ?> -
-
- - - - - - - - - - - - - -
-
- -
- -
-
-
- -
-
- \ No newline at end of file diff --git a/classes/svgSchema.class.php b/classes/svgSchema.class.php deleted file mode 100644 index 61ccc3bfc..000000000 --- a/classes/svgSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file diff --git a/classes/visioSchema.class.php b/classes/visioSchema.class.php deleted file mode 100644 index 167d5220b..000000000 --- a/classes/visioSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file From 5d7998984d8dbf6d2065d06bb9c93668a852ec4a Mon Sep 17 00:00:00 2001 From: Adnan Date: Thu, 3 Jun 2010 17:09:57 +0500 Subject: [PATCH 04/48] adding classes to libraries dir --- libraries/classes/diaSchema.class.php | 7 +++ libraries/classes/epsSchema.class.php | 7 +++ libraries/classes/pdfSchema.class.php | 9 ++++ .../classes/relationSchema.abstract.class.php | 53 +++++++++++++++++++ libraries/classes/svgSchema.class.php | 7 +++ libraries/classes/visioSchema.class.php | 7 +++ 6 files changed, 90 insertions(+) create mode 100644 libraries/classes/diaSchema.class.php create mode 100644 libraries/classes/epsSchema.class.php create mode 100644 libraries/classes/pdfSchema.class.php create mode 100644 libraries/classes/relationSchema.abstract.class.php create mode 100644 libraries/classes/svgSchema.class.php create mode 100644 libraries/classes/visioSchema.class.php diff --git a/libraries/classes/diaSchema.class.php b/libraries/classes/diaSchema.class.php new file mode 100644 index 000000000..e0fb1ccd2 --- /dev/null +++ b/libraries/classes/diaSchema.class.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/libraries/classes/epsSchema.class.php b/libraries/classes/epsSchema.class.php new file mode 100644 index 000000000..3e0b942a0 --- /dev/null +++ b/libraries/classes/epsSchema.class.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/libraries/classes/pdfSchema.class.php b/libraries/classes/pdfSchema.class.php new file mode 100644 index 000000000..cd9cfe14b --- /dev/null +++ b/libraries/classes/pdfSchema.class.php @@ -0,0 +1,9 @@ +createPageHTML(); +?> \ No newline at end of file diff --git a/libraries/classes/relationSchema.abstract.class.php b/libraries/classes/relationSchema.abstract.class.php new file mode 100644 index 000000000..a30e9d2a1 --- /dev/null +++ b/libraries/classes/relationSchema.abstract.class.php @@ -0,0 +1,53 @@ +pageTitle=$title; + } + + public function createPage() + { + + } + public function createPageHTML() + { + ?> +
+
+ + + + + + + + + + + + + +
+
+ +
+ +
+
+
+ +
+
+ \ No newline at end of file diff --git a/libraries/classes/svgSchema.class.php b/libraries/classes/svgSchema.class.php new file mode 100644 index 000000000..61ccc3bfc --- /dev/null +++ b/libraries/classes/svgSchema.class.php @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/libraries/classes/visioSchema.class.php b/libraries/classes/visioSchema.class.php new file mode 100644 index 000000000..167d5220b --- /dev/null +++ b/libraries/classes/visioSchema.class.php @@ -0,0 +1,7 @@ + \ No newline at end of file From 3805338674202055aa7724e42b23c894b06bd4c0 Mon Sep 17 00:00:00 2001 From: Adnan Date: Thu, 3 Jun 2010 17:51:05 +0500 Subject: [PATCH 05/48] Rename libraries/classes to libraries/schema --- libraries/{classes => schema}/diaSchema.class.php | 0 libraries/{classes => schema}/epsSchema.class.php | 0 libraries/{classes => schema}/pdfSchema.class.php | 0 libraries/{classes => schema}/relationSchema.abstract.class.php | 0 libraries/{classes => schema}/svgSchema.class.php | 0 libraries/{classes => schema}/visioSchema.class.php | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename libraries/{classes => schema}/diaSchema.class.php (100%) rename libraries/{classes => schema}/epsSchema.class.php (100%) rename libraries/{classes => schema}/pdfSchema.class.php (100%) rename libraries/{classes => schema}/relationSchema.abstract.class.php (100%) rename libraries/{classes => schema}/svgSchema.class.php (100%) rename libraries/{classes => schema}/visioSchema.class.php (100%) diff --git a/libraries/classes/diaSchema.class.php b/libraries/schema/diaSchema.class.php similarity index 100% rename from libraries/classes/diaSchema.class.php rename to libraries/schema/diaSchema.class.php diff --git a/libraries/classes/epsSchema.class.php b/libraries/schema/epsSchema.class.php similarity index 100% rename from libraries/classes/epsSchema.class.php rename to libraries/schema/epsSchema.class.php diff --git a/libraries/classes/pdfSchema.class.php b/libraries/schema/pdfSchema.class.php similarity index 100% rename from libraries/classes/pdfSchema.class.php rename to libraries/schema/pdfSchema.class.php diff --git a/libraries/classes/relationSchema.abstract.class.php b/libraries/schema/relationSchema.abstract.class.php similarity index 100% rename from libraries/classes/relationSchema.abstract.class.php rename to libraries/schema/relationSchema.abstract.class.php diff --git a/libraries/classes/svgSchema.class.php b/libraries/schema/svgSchema.class.php similarity index 100% rename from libraries/classes/svgSchema.class.php rename to libraries/schema/svgSchema.class.php diff --git a/libraries/classes/visioSchema.class.php b/libraries/schema/visioSchema.class.php similarity index 100% rename from libraries/classes/visioSchema.class.php rename to libraries/schema/visioSchema.class.php From 589eef328048df3e74e3a713c8de06e304688c96 Mon Sep 17 00:00:00 2001 From: Adnan Date: Thu, 24 Jun 2010 18:31:42 +0500 Subject: [PATCH 06/48] schema classes structure --- db_operations.php | 7 + js/colorpicker/css/colorpicker.css | 322 +- js/colorpicker/css/layout.css | 436 +- js/colorpicker/js/colorpicker.js | 966 +- js/functions.js | 29 +- .../PHPExcel/CachedObjectStorage/APC.php | 386 +- .../CachedObjectStorage/CacheBase.php | 306 +- .../PHPExcel/CachedObjectStorage/DiscISAM.php | 286 +- .../PHPExcel/CachedObjectStorage/Memcache.php | 428 +- .../PHPExcel/CachedObjectStorage/Memory.php | 170 +- .../CachedObjectStorage/MemoryGZip.php | 214 +- .../CachedObjectStorage/MemorySerialized.php | 214 +- .../PHPExcel/CachedObjectStorage/PHPTemp.php | 274 +- .../PHPExcel/CachedObjectStorage/Wincache.php | 400 +- .../PHPExcel/CachedObjectStorageFactory.php | 260 +- .../PHPExcel/PHPExcel/Reader/Excel2007.php | 6 +- libraries/PHPExcel/PHPExcel/Reader/Excel5.php | 12354 ++++++++-------- .../PHPExcel/PHPExcel/Shared/OLE/PPS/Root.php | 10 +- libraries/config.default.php | 2 +- libraries/schema/diaSchema.class.php | 7 - libraries/schema/epsSchema.class.php | 7 - libraries/schema/pdfSchema.class.php | 9 - .../schema/relationSchema.abstract.class.php | 53 - libraries/schema/svgSchema.class.php | 7 - libraries/schema/visioSchema.class.php | 7 - 25 files changed, 8551 insertions(+), 8609 deletions(-) delete mode 100644 libraries/schema/diaSchema.class.php delete mode 100644 libraries/schema/epsSchema.class.php delete mode 100644 libraries/schema/pdfSchema.class.php delete mode 100644 libraries/schema/relationSchema.abstract.class.php delete mode 100644 libraries/schema/svgSchema.class.php delete mode 100644 libraries/schema/visioSchema.class.php diff --git a/db_operations.php b/db_operations.php index 40ec65272..6ca9461cb 100644 --- a/db_operations.php +++ b/db_operations.php @@ -646,6 +646,13 @@ if ($cfgRelation['pdfwork'] && $num_tables > 0) { ?> .' alt="" width="16" height="16" />'; } echo __('Edit PDF Pages') . ''; + // Export Relational Schema View + echo '
'; + if ($cfg['PropertiesIconic']) { + echo ''; + } + echo __('Export Relational Schema View') . '
'; } // end if /** diff --git a/js/colorpicker/css/colorpicker.css b/js/colorpicker/css/colorpicker.css index 05b02b485..0b3d5d936 100644 --- a/js/colorpicker/css/colorpicker.css +++ b/js/colorpicker/css/colorpicker.css @@ -1,161 +1,161 @@ -.colorpicker { - width: 356px; - height: 176px; - overflow: hidden; - position: absolute; - background: url(../images/colorpicker_background.png); - font-family: Arial, Helvetica, sans-serif; - display: none; -} -.colorpicker_color { - width: 150px; - height: 150px; - left: 14px; - top: 13px; - position: absolute; - background: #f00; - overflow: hidden; - cursor: crosshair; -} -.colorpicker_color div { - position: absolute; - top: 0; - left: 0; - width: 150px; - height: 150px; - background: url(../images/colorpicker_overlay.png); -} -.colorpicker_color div div { - position: absolute; - top: 0; - left: 0; - width: 11px; - height: 11px; - overflow: hidden; - background: url(../images/colorpicker_select.gif); - margin: -5px 0 0 -5px; -} -.colorpicker_hue { - position: absolute; - top: 13px; - left: 171px; - width: 35px; - height: 150px; - cursor: n-resize; -} -.colorpicker_hue div { - position: absolute; - width: 35px; - height: 9px; - overflow: hidden; - background: url(../images/colorpicker_indic.gif) left top; - margin: -4px 0 0 0; - left: 0px; -} -.colorpicker_new_color { - position: absolute; - width: 60px; - height: 30px; - left: 213px; - top: 13px; - background: #f00; -} -.colorpicker_current_color { - position: absolute; - width: 60px; - height: 30px; - left: 283px; - top: 13px; - background: #f00; -} -.colorpicker input { - background-color: transparent; - border: 1px solid transparent; - position: absolute; - font-size: 10px; - font-family: Arial, Helvetica, sans-serif; - color: #898989; - top: 4px; - right: 11px; - text-align: right; - margin: 0; - padding: 0; - height: 11px; -} -.colorpicker_hex { - position: absolute; - width: 72px; - height: 22px; - background: url(../images/colorpicker_hex.png) top; - left: 212px; - top: 142px; -} -.colorpicker_hex input { - right: 6px; -} -.colorpicker_field { - height: 22px; - width: 62px; - background-position: top; - position: absolute; -} -.colorpicker_field span { - position: absolute; - width: 12px; - height: 22px; - overflow: hidden; - top: 0; - right: 0; - cursor: n-resize; -} -.colorpicker_rgb_r { - background-image: url(../images/colorpicker_rgb_r.png); - top: 52px; - left: 212px; -} -.colorpicker_rgb_g { - background-image: url(../images/colorpicker_rgb_g.png); - top: 82px; - left: 212px; -} -.colorpicker_rgb_b { - background-image: url(../images/colorpicker_rgb_b.png); - top: 112px; - left: 212px; -} -.colorpicker_hsb_h { - background-image: url(../images/colorpicker_hsb_h.png); - top: 52px; - left: 282px; -} -.colorpicker_hsb_s { - background-image: url(../images/colorpicker_hsb_s.png); - top: 82px; - left: 282px; -} -.colorpicker_hsb_b { - background-image: url(../images/colorpicker_hsb_b.png); - top: 112px; - left: 282px; -} -.colorpicker_submit { - position: absolute; - width: 22px; - height: 22px; - background: url(../images/colorpicker_submit.png) top; - left: 322px; - top: 142px; - overflow: hidden; -} -.colorpicker_focus { - background-position: center; -} -.colorpicker_hex.colorpicker_focus { - background-position: bottom; -} -.colorpicker_submit.colorpicker_focus { - background-position: bottom; -} -.colorpicker_slider { - background-position: bottom; -} +.colorpicker { + width: 356px; + height: 176px; + overflow: hidden; + position: absolute; + background: url(../images/colorpicker_background.png); + font-family: Arial, Helvetica, sans-serif; + display: none; +} +.colorpicker_color { + width: 150px; + height: 150px; + left: 14px; + top: 13px; + position: absolute; + background: #f00; + overflow: hidden; + cursor: crosshair; +} +.colorpicker_color div { + position: absolute; + top: 0; + left: 0; + width: 150px; + height: 150px; + background: url(../images/colorpicker_overlay.png); +} +.colorpicker_color div div { + position: absolute; + top: 0; + left: 0; + width: 11px; + height: 11px; + overflow: hidden; + background: url(../images/colorpicker_select.gif); + margin: -5px 0 0 -5px; +} +.colorpicker_hue { + position: absolute; + top: 13px; + left: 171px; + width: 35px; + height: 150px; + cursor: n-resize; +} +.colorpicker_hue div { + position: absolute; + width: 35px; + height: 9px; + overflow: hidden; + background: url(../images/colorpicker_indic.gif) left top; + margin: -4px 0 0 0; + left: 0px; +} +.colorpicker_new_color { + position: absolute; + width: 60px; + height: 30px; + left: 213px; + top: 13px; + background: #f00; +} +.colorpicker_current_color { + position: absolute; + width: 60px; + height: 30px; + left: 283px; + top: 13px; + background: #f00; +} +.colorpicker input { + background-color: transparent; + border: 1px solid transparent; + position: absolute; + font-size: 10px; + font-family: Arial, Helvetica, sans-serif; + color: #898989; + top: 4px; + right: 11px; + text-align: right; + margin: 0; + padding: 0; + height: 11px; +} +.colorpicker_hex { + position: absolute; + width: 72px; + height: 22px; + background: url(../images/colorpicker_hex.png) top; + left: 212px; + top: 142px; +} +.colorpicker_hex input { + right: 6px; +} +.colorpicker_field { + height: 22px; + width: 62px; + background-position: top; + position: absolute; +} +.colorpicker_field span { + position: absolute; + width: 12px; + height: 22px; + overflow: hidden; + top: 0; + right: 0; + cursor: n-resize; +} +.colorpicker_rgb_r { + background-image: url(../images/colorpicker_rgb_r.png); + top: 52px; + left: 212px; +} +.colorpicker_rgb_g { + background-image: url(../images/colorpicker_rgb_g.png); + top: 82px; + left: 212px; +} +.colorpicker_rgb_b { + background-image: url(../images/colorpicker_rgb_b.png); + top: 112px; + left: 212px; +} +.colorpicker_hsb_h { + background-image: url(../images/colorpicker_hsb_h.png); + top: 52px; + left: 282px; +} +.colorpicker_hsb_s { + background-image: url(../images/colorpicker_hsb_s.png); + top: 82px; + left: 282px; +} +.colorpicker_hsb_b { + background-image: url(../images/colorpicker_hsb_b.png); + top: 112px; + left: 282px; +} +.colorpicker_submit { + position: absolute; + width: 22px; + height: 22px; + background: url(../images/colorpicker_submit.png) top; + left: 322px; + top: 142px; + overflow: hidden; +} +.colorpicker_focus { + background-position: center; +} +.colorpicker_hex.colorpicker_focus { + background-position: bottom; +} +.colorpicker_submit.colorpicker_focus { + background-position: bottom; +} +.colorpicker_slider { + background-position: bottom; +} diff --git a/js/colorpicker/css/layout.css b/js/colorpicker/css/layout.css index 396e671c6..92675bcf0 100644 --- a/js/colorpicker/css/layout.css +++ b/js/colorpicker/css/layout.css @@ -1,218 +1,218 @@ -/*body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td { - margin:0; - padding:0; -} -table { - border-collapse:collapse; - border-spacing:0; -} -fieldset,img { - border:0; -} -address,caption,cite,code,dfn,em,strong,th,var { - font-style:normal; - font-weight:normal; -} -ol,ul { - list-style:none; -} -caption,th { - text-align:left; -} -h1,h2,h3,h4,h5,h6 { - font-size:100%; - font-weight:normal; -} -q:before,q:after { - content:''; -} -abbr,acronym { border:0; -} -html, body { - background-color: #fff; - font-family: Arial, Helvetica, sans-serif; - font-size: 12px; - line-height: 18px; - color: #52697E; -} -body { - text-align: center; - overflow: auto; -} -.wrapper { - width: 700px; - margin: 0 auto; - text-align: left; -} -h1 { - font-size: 21px; - height: 47px; - line-height: 47px; - text-transform: uppercase; -} -.navigationTabs { - height: 23px; - line-height: 23px; - border-bottom: 1px solid #ccc; -} -.navigationTabs li { - float: left; - height: 23px; - line-height: 23px; - padding-right: 3px; -} -.navigationTabs li a{ - float: left; - dispaly: block; - height: 23px; - line-height: 23px; - padding: 0 10px; - overflow: hidden; - color: #52697E; - background-color: #eee; - position: relative; - text-decoration: none; -} -.navigationTabs li a:hover { - background-color: #f0f0f0; -} -.navigationTabs li a.active { - background-color: #fff; - border: 1px solid #ccc; - border-bottom: 0px solid; -} -.tabsContent { - border: 1px solid #ccc; - border-top: 0px solid; - width: 698px; - overflow: hidden; -} -.tab { - padding: 16px; - display: none; -} -.tab h2 { - font-weight: bold; - font-size: 16px; -} -.tab h3 { - font-weight: bold; - font-size: 14px; - margin-top: 20px; -} -.tab p { - margin-top: 16px; - clear: both; -} -.tab ul { - margin-top: 16px; - list-style: disc; -} -.tab li { - margin: 10px 0 0 35px; -} -.tab a { - color: #8FB0CF; -} -.tab strong { - font-weight: bold; -} -.tab pre { - font-size: 11px; - margin-top: 20px; - width: 668px; - overflow: auto; - clear: both; -} -.tab table { - width: 100%; -} -.tab table td { - padding: 6px 10px 6px 0; - vertical-align: top; -} -.tab dt { - margin-top: 16px; -} -*/ -#colorSelector { - position: relative; - width: 36px; - height: 36px; - background: url(../images/select.png); -} -#colorSelector div { - position: absolute; - top: 3px; - left: 3px; - width: 30px; - height: 30px; - background: url(../images/select.png) center; -} -#colorSelector2 { - position: absolute; - top: 0; - left: 0; - width: 36px; - height: 36px; - background: url(../images/select2.png); -} -#colorSelector2 div { - position: absolute; - top: 4px; - left: 4px; - width: 28px; - height: 28px; - background: url(../images/select2.png) center; -} -#colorpickerHolder2 { - top: 32px; - left: 0; - width: 356px; - height: 0; - overflow: hidden; - position: absolute; -} -#colorpickerHolder2 .colorpicker { - background-image: url(../images/custom_background.png); - position: absolute; - bottom: 0; - left: 0; -} -#colorpickerHolder2 .colorpicker_hue div { - background-image: url(../images/custom_indic.gif); -} -#colorpickerHolder2 .colorpicker_hex { - background-image: url(../images/custom_hex.png); -} -#colorpickerHolder2 .colorpicker_rgb_r { - background-image: url(../images/custom_rgb_r.png); -} -#colorpickerHolder2 .colorpicker_rgb_g { - background-image: url(../images/custom_rgb_g.png); -} -#colorpickerHolder2 .colorpicker_rgb_b { - background-image: url(../images/custom_rgb_b.png); -} -#colorpickerHolder2 .colorpicker_hsb_s { - background-image: url(../images/custom_hsb_s.png); - display: none; -} -#colorpickerHolder2 .colorpicker_hsb_h { - background-image: url(../images/custom_hsb_h.png); - display: none; -} -#colorpickerHolder2 .colorpicker_hsb_b { - background-image: url(../images/custom_hsb_b.png); - display: none; -} -#colorpickerHolder2 .colorpicker_submit { - background-image: url(../images/custom_submit.png); -} -#colorpickerHolder2 .colorpicker input { - color: #778398; -} -#customWidget { - position: relative; - height: 36px; -} +/*body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td { + margin:0; + padding:0; +} +table { + border-collapse:collapse; + border-spacing:0; +} +fieldset,img { + border:0; +} +address,caption,cite,code,dfn,em,strong,th,var { + font-style:normal; + font-weight:normal; +} +ol,ul { + list-style:none; +} +caption,th { + text-align:left; +} +h1,h2,h3,h4,h5,h6 { + font-size:100%; + font-weight:normal; +} +q:before,q:after { + content:''; +} +abbr,acronym { border:0; +} +html, body { + background-color: #fff; + font-family: Arial, Helvetica, sans-serif; + font-size: 12px; + line-height: 18px; + color: #52697E; +} +body { + text-align: center; + overflow: auto; +} +.wrapper { + width: 700px; + margin: 0 auto; + text-align: left; +} +h1 { + font-size: 21px; + height: 47px; + line-height: 47px; + text-transform: uppercase; +} +.navigationTabs { + height: 23px; + line-height: 23px; + border-bottom: 1px solid #ccc; +} +.navigationTabs li { + float: left; + height: 23px; + line-height: 23px; + padding-right: 3px; +} +.navigationTabs li a{ + float: left; + dispaly: block; + height: 23px; + line-height: 23px; + padding: 0 10px; + overflow: hidden; + color: #52697E; + background-color: #eee; + position: relative; + text-decoration: none; +} +.navigationTabs li a:hover { + background-color: #f0f0f0; +} +.navigationTabs li a.active { + background-color: #fff; + border: 1px solid #ccc; + border-bottom: 0px solid; +} +.tabsContent { + border: 1px solid #ccc; + border-top: 0px solid; + width: 698px; + overflow: hidden; +} +.tab { + padding: 16px; + display: none; +} +.tab h2 { + font-weight: bold; + font-size: 16px; +} +.tab h3 { + font-weight: bold; + font-size: 14px; + margin-top: 20px; +} +.tab p { + margin-top: 16px; + clear: both; +} +.tab ul { + margin-top: 16px; + list-style: disc; +} +.tab li { + margin: 10px 0 0 35px; +} +.tab a { + color: #8FB0CF; +} +.tab strong { + font-weight: bold; +} +.tab pre { + font-size: 11px; + margin-top: 20px; + width: 668px; + overflow: auto; + clear: both; +} +.tab table { + width: 100%; +} +.tab table td { + padding: 6px 10px 6px 0; + vertical-align: top; +} +.tab dt { + margin-top: 16px; +} +*/ +#colorSelector { + position: relative; + width: 36px; + height: 36px; + background: url(../images/select.png); +} +#colorSelector div { + position: absolute; + top: 3px; + left: 3px; + width: 30px; + height: 30px; + background: url(../images/select.png) center; +} +#colorSelector2 { + position: absolute; + top: 0; + left: 0; + width: 36px; + height: 36px; + background: url(../images/select2.png); +} +#colorSelector2 div { + position: absolute; + top: 4px; + left: 4px; + width: 28px; + height: 28px; + background: url(../images/select2.png) center; +} +#colorpickerHolder2 { + top: 32px; + left: 0; + width: 356px; + height: 0; + overflow: hidden; + position: absolute; +} +#colorpickerHolder2 .colorpicker { + background-image: url(../images/custom_background.png); + position: absolute; + bottom: 0; + left: 0; +} +#colorpickerHolder2 .colorpicker_hue div { + background-image: url(../images/custom_indic.gif); +} +#colorpickerHolder2 .colorpicker_hex { + background-image: url(../images/custom_hex.png); +} +#colorpickerHolder2 .colorpicker_rgb_r { + background-image: url(../images/custom_rgb_r.png); +} +#colorpickerHolder2 .colorpicker_rgb_g { + background-image: url(../images/custom_rgb_g.png); +} +#colorpickerHolder2 .colorpicker_rgb_b { + background-image: url(../images/custom_rgb_b.png); +} +#colorpickerHolder2 .colorpicker_hsb_s { + background-image: url(../images/custom_hsb_s.png); + display: none; +} +#colorpickerHolder2 .colorpicker_hsb_h { + background-image: url(../images/custom_hsb_h.png); + display: none; +} +#colorpickerHolder2 .colorpicker_hsb_b { + background-image: url(../images/custom_hsb_b.png); + display: none; +} +#colorpickerHolder2 .colorpicker_submit { + background-image: url(../images/custom_submit.png); +} +#colorpickerHolder2 .colorpicker input { + color: #778398; +} +#customWidget { + position: relative; + height: 36px; +} diff --git a/js/colorpicker/js/colorpicker.js b/js/colorpicker/js/colorpicker.js index 10a2b2244..45f56ced7 100644 --- a/js/colorpicker/js/colorpicker.js +++ b/js/colorpicker/js/colorpicker.js @@ -1,484 +1,484 @@ -/** - * - * Color picker - * Author: Stefan Petre www.eyecon.ro - * - * Dual licensed under the MIT and GPL licenses - * - */ -(function ($) { - var ColorPicker = function () { - var - ids = {}, - inAction, - charMin = 65, - visible, - tpl = '
', - defaults = { - eventName: 'click', - onShow: function () {}, - onBeforeShow: function(){}, - onHide: function () {}, - onChange: function () {}, - onSubmit: function () {}, - color: 'ff0000', - livePreview: true, - flat: false - }, - fillRGBFields = function (hsb, cal) { - var rgb = HSBToRGB(hsb); - $(cal).data('colorpicker').fields - .eq(1).val(rgb.r).end() - .eq(2).val(rgb.g).end() - .eq(3).val(rgb.b).end(); - }, - fillHSBFields = function (hsb, cal) { - $(cal).data('colorpicker').fields - .eq(4).val(hsb.h).end() - .eq(5).val(hsb.s).end() - .eq(6).val(hsb.b).end(); - }, - fillHexFields = function (hsb, cal) { - $(cal).data('colorpicker').fields - .eq(0).val(HSBToHex(hsb)).end(); - }, - setSelector = function (hsb, cal) { - $(cal).data('colorpicker').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100})); - $(cal).data('colorpicker').selectorIndic.css({ - left: parseInt(150 * hsb.s/100, 10), - top: parseInt(150 * (100-hsb.b)/100, 10) - }); - }, - setHue = function (hsb, cal) { - $(cal).data('colorpicker').hue.css('top', parseInt(150 - 150 * hsb.h/360, 10)); - }, - setCurrentColor = function (hsb, cal) { - $(cal).data('colorpicker').currentColor.css('backgroundColor', '#' + HSBToHex(hsb)); - }, - setNewColor = function (hsb, cal) { - $(cal).data('colorpicker').newColor.css('backgroundColor', '#' + HSBToHex(hsb)); - }, - keyDown = function (ev) { - var pressedKey = ev.charCode || ev.keyCode || -1; - if ((pressedKey > charMin && pressedKey <= 90) || pressedKey == 32) { - return false; - } - var cal = $(this).parent().parent(); - if (cal.data('colorpicker').livePreview === true) { - change.apply(this); - } - }, - change = function (ev) { - var cal = $(this).parent().parent(), col; - if (this.parentNode.className.indexOf('_hex') > 0) { - cal.data('colorpicker').color = col = HexToHSB(fixHex(this.value)); - } else if (this.parentNode.className.indexOf('_hsb') > 0) { - cal.data('colorpicker').color = col = fixHSB({ - h: parseInt(cal.data('colorpicker').fields.eq(4).val(), 10), - s: parseInt(cal.data('colorpicker').fields.eq(5).val(), 10), - b: parseInt(cal.data('colorpicker').fields.eq(6).val(), 10) - }); - } else { - cal.data('colorpicker').color = col = RGBToHSB(fixRGB({ - r: parseInt(cal.data('colorpicker').fields.eq(1).val(), 10), - g: parseInt(cal.data('colorpicker').fields.eq(2).val(), 10), - b: parseInt(cal.data('colorpicker').fields.eq(3).val(), 10) - })); - } - if (ev) { - fillRGBFields(col, cal.get(0)); - fillHexFields(col, cal.get(0)); - fillHSBFields(col, cal.get(0)); - } - setSelector(col, cal.get(0)); - setHue(col, cal.get(0)); - setNewColor(col, cal.get(0)); - cal.data('colorpicker').onChange.apply(cal, [col, HSBToHex(col), HSBToRGB(col)]); - }, - blur = function (ev) { - var cal = $(this).parent().parent(); - cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus'); - }, - focus = function () { - charMin = this.parentNode.className.indexOf('_hex') > 0 ? 70 : 65; - $(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus'); - $(this).parent().addClass('colorpicker_focus'); - }, - downIncrement = function (ev) { - var field = $(this).parent().find('input').focus(); - var current = { - el: $(this).parent().addClass('colorpicker_slider'), - max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), - y: ev.pageY, - field: field, - val: parseInt(field.val(), 10), - preview: $(this).parent().parent().data('colorpicker').livePreview - }; - $(document).bind('mouseup', current, upIncrement); - $(document).bind('mousemove', current, moveIncrement); - }, - moveIncrement = function (ev) { - ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val + ev.pageY - ev.data.y, 10)))); - if (ev.data.preview) { - change.apply(ev.data.field.get(0), [true]); - } - return false; - }, - upIncrement = function (ev) { - change.apply(ev.data.field.get(0), [true]); - ev.data.el.removeClass('colorpicker_slider').find('input').focus(); - $(document).unbind('mouseup', upIncrement); - $(document).unbind('mousemove', moveIncrement); - return false; - }, - downHue = function (ev) { - var current = { - cal: $(this).parent(), - y: $(this).offset().top - }; - current.preview = current.cal.data('colorpicker').livePreview; - $(document).bind('mouseup', current, upHue); - $(document).bind('mousemove', current, moveHue); - }, - moveHue = function (ev) { - change.apply( - ev.data.cal.data('colorpicker') - .fields - .eq(4) - .val(parseInt(360*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.y))))/150, 10)) - .get(0), - [ev.data.preview] - ); - return false; - }, - upHue = function (ev) { - fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); - fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); - $(document).unbind('mouseup', upHue); - $(document).unbind('mousemove', moveHue); - return false; - }, - downSelector = function (ev) { - var current = { - cal: $(this).parent(), - pos: $(this).offset() - }; - current.preview = current.cal.data('colorpicker').livePreview; - $(document).bind('mouseup', current, upSelector); - $(document).bind('mousemove', current, moveSelector); - }, - moveSelector = function (ev) { - change.apply( - ev.data.cal.data('colorpicker') - .fields - .eq(6) - .val(parseInt(100*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.pos.top))))/150, 10)) - .end() - .eq(5) - .val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX - ev.data.pos.left))))/150, 10)) - .get(0), - [ev.data.preview] - ); - return false; - }, - upSelector = function (ev) { - fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); - fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); - $(document).unbind('mouseup', upSelector); - $(document).unbind('mousemove', moveSelector); - return false; - }, - enterSubmit = function (ev) { - $(this).addClass('colorpicker_focus'); - }, - leaveSubmit = function (ev) { - $(this).removeClass('colorpicker_focus'); - }, - clickSubmit = function (ev) { - var cal = $(this).parent(); - var col = cal.data('colorpicker').color; - cal.data('colorpicker').origColor = col; - setCurrentColor(col, cal.get(0)); - cal.data('colorpicker').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colorpicker').el); - }, - show = function (ev) { - var cal = $('#' + $(this).data('colorpickerId')); - cal.data('colorpicker').onBeforeShow.apply(this, [cal.get(0)]); - var pos = $(this).offset(); - var viewPort = getViewport(); - var top = pos.top + this.offsetHeight; - var left = pos.left; - if (top + 176 > viewPort.t + viewPort.h) { - top -= this.offsetHeight + 176; - } - if (left + 356 > viewPort.l + viewPort.w) { - left -= 356; - } - cal.css({left: left + 'px', top: top + 'px'}); - if (cal.data('colorpicker').onShow.apply(this, [cal.get(0)]) != false) { - cal.show(); - } - $(document).bind('mousedown', {cal: cal}, hide); - return false; - }, - hide = function (ev) { - if (!isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) { - if (ev.data.cal.data('colorpicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) { - ev.data.cal.hide(); - } - $(document).unbind('mousedown', hide); - } - }, - isChildOf = function(parentEl, el, container) { - if (parentEl == el) { - return true; - } - if (parentEl.contains) { - return parentEl.contains(el); - } - if ( parentEl.compareDocumentPosition ) { - return !!(parentEl.compareDocumentPosition(el) & 16); - } - var prEl = el.parentNode; - while(prEl && prEl != container) { - if (prEl == parentEl) - return true; - prEl = prEl.parentNode; - } - return false; - }, - getViewport = function () { - var m = document.compatMode == 'CSS1Compat'; - return { - l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft), - t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop), - w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth), - h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight) - }; - }, - fixHSB = function (hsb) { - return { - h: Math.min(360, Math.max(0, hsb.h)), - s: Math.min(100, Math.max(0, hsb.s)), - b: Math.min(100, Math.max(0, hsb.b)) - }; - }, - fixRGB = function (rgb) { - return { - r: Math.min(255, Math.max(0, rgb.r)), - g: Math.min(255, Math.max(0, rgb.g)), - b: Math.min(255, Math.max(0, rgb.b)) - }; - }, - fixHex = function (hex) { - var len = 6 - hex.length; - if (len > 0) { - var o = []; - for (var i=0; i -1) ? hex.substring(1) : hex), 16); - return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)}; - }, - HexToHSB = function (hex) { - return RGBToHSB(HexToRGB(hex)); - }, - RGBToHSB = function (rgb) { - var hsb = { - h: 0, - s: 0, - b: 0 - }; - var min = Math.min(rgb.r, rgb.g, rgb.b); - var max = Math.max(rgb.r, rgb.g, rgb.b); - var delta = max - min; - hsb.b = max; - if (max != 0) { - - } - hsb.s = max != 0 ? 255 * delta / max : 0; - if (hsb.s != 0) { - if (rgb.r == max) { - hsb.h = (rgb.g - rgb.b) / delta; - } else if (rgb.g == max) { - hsb.h = 2 + (rgb.b - rgb.r) / delta; - } else { - hsb.h = 4 + (rgb.r - rgb.g) / delta; - } - } else { - hsb.h = -1; - } - hsb.h *= 60; - if (hsb.h < 0) { - hsb.h += 360; - } - hsb.s *= 100/255; - hsb.b *= 100/255; - return hsb; - }, - HSBToRGB = function (hsb) { - var rgb = {}; - var h = Math.round(hsb.h); - var s = Math.round(hsb.s*255/100); - var v = Math.round(hsb.b*255/100); - if(s == 0) { - rgb.r = rgb.g = rgb.b = v; - } else { - var t1 = v; - var t2 = (255-s)*v/255; - var t3 = (t1-t2)*(h%60)/60; - if(h==360) h = 0; - if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3} - else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} - else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} - else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} - else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} - else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} - else {rgb.r=0; rgb.g=0; rgb.b=0} - } - return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)}; - }, - RGBToHex = function (rgb) { - var hex = [ - rgb.r.toString(16), - rgb.g.toString(16), - rgb.b.toString(16) - ]; - $.each(hex, function (nr, val) { - if (val.length == 1) { - hex[nr] = '0' + val; - } - }); - return hex.join(''); - }, - HSBToHex = function (hsb) { - return RGBToHex(HSBToRGB(hsb)); - }, - restoreOriginal = function () { - var cal = $(this).parent(); - var col = cal.data('colorpicker').origColor; - cal.data('colorpicker').color = col; - fillRGBFields(col, cal.get(0)); - fillHexFields(col, cal.get(0)); - fillHSBFields(col, cal.get(0)); - setSelector(col, cal.get(0)); - setHue(col, cal.get(0)); - setNewColor(col, cal.get(0)); - }; - return { - init: function (opt) { - opt = $.extend({}, defaults, opt||{}); - if (typeof opt.color == 'string') { - opt.color = HexToHSB(opt.color); - } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) { - opt.color = RGBToHSB(opt.color); - } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) { - opt.color = fixHSB(opt.color); - } else { - return this; - } - return this.each(function () { - if (!$(this).data('colorpickerId')) { - var options = $.extend({}, opt); - options.origColor = opt.color; - var id = 'collorpicker_' + parseInt(Math.random() * 1000); - $(this).data('colorpickerId', id); - var cal = $(tpl).attr('id', id); - if (options.flat) { - cal.appendTo(this).show(); - } else { - cal.appendTo(document.body); - } - options.fields = cal - .find('input') - .bind('keyup', keyDown) - .bind('change', change) - .bind('blur', blur) - .bind('focus', focus); - cal - .find('span').bind('mousedown', downIncrement).end() - .find('>div.colorpicker_current_color').bind('click', restoreOriginal); - options.selector = cal.find('div.colorpicker_color').bind('mousedown', downSelector); - options.selectorIndic = options.selector.find('div div'); - options.el = this; - options.hue = cal.find('div.colorpicker_hue div'); - cal.find('div.colorpicker_hue').bind('mousedown', downHue); - options.newColor = cal.find('div.colorpicker_new_color'); - options.currentColor = cal.find('div.colorpicker_current_color'); - cal.data('colorpicker', options); - cal.find('div.colorpicker_submit') - .bind('mouseenter', enterSubmit) - .bind('mouseleave', leaveSubmit) - .bind('click', clickSubmit); - fillRGBFields(options.color, cal.get(0)); - fillHSBFields(options.color, cal.get(0)); - fillHexFields(options.color, cal.get(0)); - setHue(options.color, cal.get(0)); - setSelector(options.color, cal.get(0)); - setCurrentColor(options.color, cal.get(0)); - setNewColor(options.color, cal.get(0)); - if (options.flat) { - cal.css({ - position: 'relative', - display: 'block' - }); - } else { - $(this).bind(options.eventName, show); - } - } - }); - }, - showPicker: function() { - return this.each( function () { - if ($(this).data('colorpickerId')) { - show.apply(this); - } - }); - }, - hidePicker: function() { - return this.each( function () { - if ($(this).data('colorpickerId')) { - $('#' + $(this).data('colorpickerId')).hide(); - } - }); - }, - setColor: function(col) { - if (typeof col == 'string') { - col = HexToHSB(col); - } else if (col.r != undefined && col.g != undefined && col.b != undefined) { - col = RGBToHSB(col); - } else if (col.h != undefined && col.s != undefined && col.b != undefined) { - col = fixHSB(col); - } else { - return this; - } - return this.each(function(){ - if ($(this).data('colorpickerId')) { - var cal = $('#' + $(this).data('colorpickerId')); - cal.data('colorpicker').color = col; - cal.data('colorpicker').origColor = col; - fillRGBFields(col, cal.get(0)); - fillHSBFields(col, cal.get(0)); - fillHexFields(col, cal.get(0)); - setHue(col, cal.get(0)); - setSelector(col, cal.get(0)); - setCurrentColor(col, cal.get(0)); - setNewColor(col, cal.get(0)); - } - }); - } - }; - }(); - $.fn.extend({ - ColorPicker: ColorPicker.init, - ColorPickerHide: ColorPicker.hidePicker, - ColorPickerShow: ColorPicker.showPicker, - ColorPickerSetColor: ColorPicker.setColor - }); +/** + * + * Color picker + * Author: Stefan Petre www.eyecon.ro + * + * Dual licensed under the MIT and GPL licenses + * + */ +(function ($) { + var ColorPicker = function () { + var + ids = {}, + inAction, + charMin = 65, + visible, + tpl = '
', + defaults = { + eventName: 'click', + onShow: function () {}, + onBeforeShow: function(){}, + onHide: function () {}, + onChange: function () {}, + onSubmit: function () {}, + color: 'ff0000', + livePreview: true, + flat: false + }, + fillRGBFields = function (hsb, cal) { + var rgb = HSBToRGB(hsb); + $(cal).data('colorpicker').fields + .eq(1).val(rgb.r).end() + .eq(2).val(rgb.g).end() + .eq(3).val(rgb.b).end(); + }, + fillHSBFields = function (hsb, cal) { + $(cal).data('colorpicker').fields + .eq(4).val(hsb.h).end() + .eq(5).val(hsb.s).end() + .eq(6).val(hsb.b).end(); + }, + fillHexFields = function (hsb, cal) { + $(cal).data('colorpicker').fields + .eq(0).val(HSBToHex(hsb)).end(); + }, + setSelector = function (hsb, cal) { + $(cal).data('colorpicker').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100})); + $(cal).data('colorpicker').selectorIndic.css({ + left: parseInt(150 * hsb.s/100, 10), + top: parseInt(150 * (100-hsb.b)/100, 10) + }); + }, + setHue = function (hsb, cal) { + $(cal).data('colorpicker').hue.css('top', parseInt(150 - 150 * hsb.h/360, 10)); + }, + setCurrentColor = function (hsb, cal) { + $(cal).data('colorpicker').currentColor.css('backgroundColor', '#' + HSBToHex(hsb)); + }, + setNewColor = function (hsb, cal) { + $(cal).data('colorpicker').newColor.css('backgroundColor', '#' + HSBToHex(hsb)); + }, + keyDown = function (ev) { + var pressedKey = ev.charCode || ev.keyCode || -1; + if ((pressedKey > charMin && pressedKey <= 90) || pressedKey == 32) { + return false; + } + var cal = $(this).parent().parent(); + if (cal.data('colorpicker').livePreview === true) { + change.apply(this); + } + }, + change = function (ev) { + var cal = $(this).parent().parent(), col; + if (this.parentNode.className.indexOf('_hex') > 0) { + cal.data('colorpicker').color = col = HexToHSB(fixHex(this.value)); + } else if (this.parentNode.className.indexOf('_hsb') > 0) { + cal.data('colorpicker').color = col = fixHSB({ + h: parseInt(cal.data('colorpicker').fields.eq(4).val(), 10), + s: parseInt(cal.data('colorpicker').fields.eq(5).val(), 10), + b: parseInt(cal.data('colorpicker').fields.eq(6).val(), 10) + }); + } else { + cal.data('colorpicker').color = col = RGBToHSB(fixRGB({ + r: parseInt(cal.data('colorpicker').fields.eq(1).val(), 10), + g: parseInt(cal.data('colorpicker').fields.eq(2).val(), 10), + b: parseInt(cal.data('colorpicker').fields.eq(3).val(), 10) + })); + } + if (ev) { + fillRGBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + } + setSelector(col, cal.get(0)); + setHue(col, cal.get(0)); + setNewColor(col, cal.get(0)); + cal.data('colorpicker').onChange.apply(cal, [col, HSBToHex(col), HSBToRGB(col)]); + }, + blur = function (ev) { + var cal = $(this).parent().parent(); + cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus'); + }, + focus = function () { + charMin = this.parentNode.className.indexOf('_hex') > 0 ? 70 : 65; + $(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus'); + $(this).parent().addClass('colorpicker_focus'); + }, + downIncrement = function (ev) { + var field = $(this).parent().find('input').focus(); + var current = { + el: $(this).parent().addClass('colorpicker_slider'), + max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), + y: ev.pageY, + field: field, + val: parseInt(field.val(), 10), + preview: $(this).parent().parent().data('colorpicker').livePreview + }; + $(document).bind('mouseup', current, upIncrement); + $(document).bind('mousemove', current, moveIncrement); + }, + moveIncrement = function (ev) { + ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val + ev.pageY - ev.data.y, 10)))); + if (ev.data.preview) { + change.apply(ev.data.field.get(0), [true]); + } + return false; + }, + upIncrement = function (ev) { + change.apply(ev.data.field.get(0), [true]); + ev.data.el.removeClass('colorpicker_slider').find('input').focus(); + $(document).unbind('mouseup', upIncrement); + $(document).unbind('mousemove', moveIncrement); + return false; + }, + downHue = function (ev) { + var current = { + cal: $(this).parent(), + y: $(this).offset().top + }; + current.preview = current.cal.data('colorpicker').livePreview; + $(document).bind('mouseup', current, upHue); + $(document).bind('mousemove', current, moveHue); + }, + moveHue = function (ev) { + change.apply( + ev.data.cal.data('colorpicker') + .fields + .eq(4) + .val(parseInt(360*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.y))))/150, 10)) + .get(0), + [ev.data.preview] + ); + return false; + }, + upHue = function (ev) { + fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + $(document).unbind('mouseup', upHue); + $(document).unbind('mousemove', moveHue); + return false; + }, + downSelector = function (ev) { + var current = { + cal: $(this).parent(), + pos: $(this).offset() + }; + current.preview = current.cal.data('colorpicker').livePreview; + $(document).bind('mouseup', current, upSelector); + $(document).bind('mousemove', current, moveSelector); + }, + moveSelector = function (ev) { + change.apply( + ev.data.cal.data('colorpicker') + .fields + .eq(6) + .val(parseInt(100*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.pos.top))))/150, 10)) + .end() + .eq(5) + .val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX - ev.data.pos.left))))/150, 10)) + .get(0), + [ev.data.preview] + ); + return false; + }, + upSelector = function (ev) { + fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + $(document).unbind('mouseup', upSelector); + $(document).unbind('mousemove', moveSelector); + return false; + }, + enterSubmit = function (ev) { + $(this).addClass('colorpicker_focus'); + }, + leaveSubmit = function (ev) { + $(this).removeClass('colorpicker_focus'); + }, + clickSubmit = function (ev) { + var cal = $(this).parent(); + var col = cal.data('colorpicker').color; + cal.data('colorpicker').origColor = col; + setCurrentColor(col, cal.get(0)); + cal.data('colorpicker').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colorpicker').el); + }, + show = function (ev) { + var cal = $('#' + $(this).data('colorpickerId')); + cal.data('colorpicker').onBeforeShow.apply(this, [cal.get(0)]); + var pos = $(this).offset(); + var viewPort = getViewport(); + var top = pos.top + this.offsetHeight; + var left = pos.left; + if (top + 176 > viewPort.t + viewPort.h) { + top -= this.offsetHeight + 176; + } + if (left + 356 > viewPort.l + viewPort.w) { + left -= 356; + } + cal.css({left: left + 'px', top: top + 'px'}); + if (cal.data('colorpicker').onShow.apply(this, [cal.get(0)]) != false) { + cal.show(); + } + $(document).bind('mousedown', {cal: cal}, hide); + return false; + }, + hide = function (ev) { + if (!isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) { + if (ev.data.cal.data('colorpicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) { + ev.data.cal.hide(); + } + $(document).unbind('mousedown', hide); + } + }, + isChildOf = function(parentEl, el, container) { + if (parentEl == el) { + return true; + } + if (parentEl.contains) { + return parentEl.contains(el); + } + if ( parentEl.compareDocumentPosition ) { + return !!(parentEl.compareDocumentPosition(el) & 16); + } + var prEl = el.parentNode; + while(prEl && prEl != container) { + if (prEl == parentEl) + return true; + prEl = prEl.parentNode; + } + return false; + }, + getViewport = function () { + var m = document.compatMode == 'CSS1Compat'; + return { + l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft), + t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop), + w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth), + h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight) + }; + }, + fixHSB = function (hsb) { + return { + h: Math.min(360, Math.max(0, hsb.h)), + s: Math.min(100, Math.max(0, hsb.s)), + b: Math.min(100, Math.max(0, hsb.b)) + }; + }, + fixRGB = function (rgb) { + return { + r: Math.min(255, Math.max(0, rgb.r)), + g: Math.min(255, Math.max(0, rgb.g)), + b: Math.min(255, Math.max(0, rgb.b)) + }; + }, + fixHex = function (hex) { + var len = 6 - hex.length; + if (len > 0) { + var o = []; + for (var i=0; i -1) ? hex.substring(1) : hex), 16); + return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)}; + }, + HexToHSB = function (hex) { + return RGBToHSB(HexToRGB(hex)); + }, + RGBToHSB = function (rgb) { + var hsb = { + h: 0, + s: 0, + b: 0 + }; + var min = Math.min(rgb.r, rgb.g, rgb.b); + var max = Math.max(rgb.r, rgb.g, rgb.b); + var delta = max - min; + hsb.b = max; + if (max != 0) { + + } + hsb.s = max != 0 ? 255 * delta / max : 0; + if (hsb.s != 0) { + if (rgb.r == max) { + hsb.h = (rgb.g - rgb.b) / delta; + } else if (rgb.g == max) { + hsb.h = 2 + (rgb.b - rgb.r) / delta; + } else { + hsb.h = 4 + (rgb.r - rgb.g) / delta; + } + } else { + hsb.h = -1; + } + hsb.h *= 60; + if (hsb.h < 0) { + hsb.h += 360; + } + hsb.s *= 100/255; + hsb.b *= 100/255; + return hsb; + }, + HSBToRGB = function (hsb) { + var rgb = {}; + var h = Math.round(hsb.h); + var s = Math.round(hsb.s*255/100); + var v = Math.round(hsb.b*255/100); + if(s == 0) { + rgb.r = rgb.g = rgb.b = v; + } else { + var t1 = v; + var t2 = (255-s)*v/255; + var t3 = (t1-t2)*(h%60)/60; + if(h==360) h = 0; + if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3} + else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} + else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} + else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} + else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} + else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} + else {rgb.r=0; rgb.g=0; rgb.b=0} + } + return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)}; + }, + RGBToHex = function (rgb) { + var hex = [ + rgb.r.toString(16), + rgb.g.toString(16), + rgb.b.toString(16) + ]; + $.each(hex, function (nr, val) { + if (val.length == 1) { + hex[nr] = '0' + val; + } + }); + return hex.join(''); + }, + HSBToHex = function (hsb) { + return RGBToHex(HSBToRGB(hsb)); + }, + restoreOriginal = function () { + var cal = $(this).parent(); + var col = cal.data('colorpicker').origColor; + cal.data('colorpicker').color = col; + fillRGBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + setSelector(col, cal.get(0)); + setHue(col, cal.get(0)); + setNewColor(col, cal.get(0)); + }; + return { + init: function (opt) { + opt = $.extend({}, defaults, opt||{}); + if (typeof opt.color == 'string') { + opt.color = HexToHSB(opt.color); + } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) { + opt.color = RGBToHSB(opt.color); + } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) { + opt.color = fixHSB(opt.color); + } else { + return this; + } + return this.each(function () { + if (!$(this).data('colorpickerId')) { + var options = $.extend({}, opt); + options.origColor = opt.color; + var id = 'collorpicker_' + parseInt(Math.random() * 1000); + $(this).data('colorpickerId', id); + var cal = $(tpl).attr('id', id); + if (options.flat) { + cal.appendTo(this).show(); + } else { + cal.appendTo(document.body); + } + options.fields = cal + .find('input') + .bind('keyup', keyDown) + .bind('change', change) + .bind('blur', blur) + .bind('focus', focus); + cal + .find('span').bind('mousedown', downIncrement).end() + .find('>div.colorpicker_current_color').bind('click', restoreOriginal); + options.selector = cal.find('div.colorpicker_color').bind('mousedown', downSelector); + options.selectorIndic = options.selector.find('div div'); + options.el = this; + options.hue = cal.find('div.colorpicker_hue div'); + cal.find('div.colorpicker_hue').bind('mousedown', downHue); + options.newColor = cal.find('div.colorpicker_new_color'); + options.currentColor = cal.find('div.colorpicker_current_color'); + cal.data('colorpicker', options); + cal.find('div.colorpicker_submit') + .bind('mouseenter', enterSubmit) + .bind('mouseleave', leaveSubmit) + .bind('click', clickSubmit); + fillRGBFields(options.color, cal.get(0)); + fillHSBFields(options.color, cal.get(0)); + fillHexFields(options.color, cal.get(0)); + setHue(options.color, cal.get(0)); + setSelector(options.color, cal.get(0)); + setCurrentColor(options.color, cal.get(0)); + setNewColor(options.color, cal.get(0)); + if (options.flat) { + cal.css({ + position: 'relative', + display: 'block' + }); + } else { + $(this).bind(options.eventName, show); + } + } + }); + }, + showPicker: function() { + return this.each( function () { + if ($(this).data('colorpickerId')) { + show.apply(this); + } + }); + }, + hidePicker: function() { + return this.each( function () { + if ($(this).data('colorpickerId')) { + $('#' + $(this).data('colorpickerId')).hide(); + } + }); + }, + setColor: function(col) { + if (typeof col == 'string') { + col = HexToHSB(col); + } else if (col.r != undefined && col.g != undefined && col.b != undefined) { + col = RGBToHSB(col); + } else if (col.h != undefined && col.s != undefined && col.b != undefined) { + col = fixHSB(col); + } else { + return this; + } + return this.each(function(){ + if ($(this).data('colorpickerId')) { + var cal = $('#' + $(this).data('colorpickerId')); + cal.data('colorpicker').color = col; + cal.data('colorpicker').origColor = col; + fillRGBFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + setHue(col, cal.get(0)); + setSelector(col, cal.get(0)); + setCurrentColor(col, cal.get(0)); + setNewColor(col, cal.get(0)); + } + }); + } + }; + }(); + $.fn.extend({ + ColorPicker: ColorPicker.init, + ColorPickerHide: ColorPicker.hidePicker, + ColorPickerShow: ColorPicker.showPicker, + ColorPickerSetColor: ColorPicker.setColor + }); })(jQuery) \ No newline at end of file diff --git a/js/functions.js b/js/functions.js index 908ced29f..56c2c6e8f 100644 --- a/js/functions.js +++ b/js/functions.js @@ -1434,8 +1434,14 @@ function refreshDragOption(e) { function refreshLayout() { var elm = $('#pdflayout') var orientation = $('#orientation_opt').val(); - var paper = $('#paper_opt').val(); - + if($('#paper_opt').length==1) + { + var paper = $('#paper_opt').val(); + } + else + { + var paper = 'A4'; + } if (orientation == 'P') { posa = 'x'; posb = 'y'; @@ -1725,5 +1731,24 @@ $(document).ready(function(){ insertQuery(evt.target.id); return false; }); + + $("#export_type").change(function(){ + + if($("#export_type").val()!='pdf') + { + $("#show_grid_opt").attr("disabled","disabled"); + $("#orientation_opt").attr("disabled","disabled"); + $("#with_doc").attr("disabled","disabled"); + $(this).css("background-color","yellow"); + + } + if($("#export_type").val()=='pdf') + { + $("#show_grid_opt").removeAttr("disabled"); + $("#orientation_opt").removeAttr("disabled"); + $("#with_doc").removeAttr("disabled","disabled"); + } + + }); }); diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/APC.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/APC.php index 62e3a1459..bf12551c7 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/APC.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/APC.php @@ -1,193 +1,193 @@ -_currentObject->detach(); - - if (!apc_store($this->_cachePrefix.$this->_currentObjectID.'.cache',serialize($this->_currentObject),$this->_cacheTime)) { - $this->__destruct(); - throw new Exception('Failed to store cell in APC'); - } - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - $this->_cellCache[$pCoord] = true; - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * @return void - * @return boolean - */ - public function isDataSet($pCoord) { - // Check if the requested entry is the current object, or exists in the cache - if (parent::isDataSet($pCoord)) { - if ($this->_currentObjectID == $pCoord) { - return true; - } - // Check if the requested entry still exists in apc - $success = apc_fetch($this->_cachePrefix.$pCoord.'.cache'); - if ($success === false) { - // Entry no longer exists in APC, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in APC'); - } - return true; - } - return false; - } // function isDataSet() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (parent::isDataSet($pCoord)) { - $obj = apc_fetch($this->_cachePrefix.$pCoord.'.cache'); - if ($obj === false) { - // Entry no longer exists in APC, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in APC'); - } - } else { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize($obj); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - /** - * Delete a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to delete - * @throws Exception - */ - public function deleteCacheData($pCoord) { - // Delete the entry from APC - apc_delete($this->_cachePrefix.$pCoord.'.cache'); - - // Delete the entry from our cell address array - parent::deleteCacheData($pCoord); - } // function deleteCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - - // Flush the APC cache - $this->__destruct(); - - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - - - public function __construct(PHPExcel_Worksheet $parent, $arguments) { - $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; - - if (is_null($this->_cachePrefix)) { - if (function_exists('posix_getpid')) { - $baseUnique = posix_getpid(); - } else { - $baseUnique = mt_rand(); - } - $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; - $this->_cacheTime = $cacheTime; - - parent::__construct($parent); - } - } // function __construct() - - - public function __destruct() { - $cacheList = $this->getCellList(); - foreach($cacheList as $cellID) { - apc_delete($this->_cachePrefix.$cellID.'.cache'); - } - } // function __destruct() - -} +_currentObject->detach(); + + if (!apc_store($this->_cachePrefix.$this->_currentObjectID.'.cache',serialize($this->_currentObject),$this->_cacheTime)) { + $this->__destruct(); + throw new Exception('Failed to store cell in APC'); + } + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + $this->_cellCache[$pCoord] = true; + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? + * + * @param string $pCoord Coordinate address of the cell to check + * @return void + * @return boolean + */ + public function isDataSet($pCoord) { + // Check if the requested entry is the current object, or exists in the cache + if (parent::isDataSet($pCoord)) { + if ($this->_currentObjectID == $pCoord) { + return true; + } + // Check if the requested entry still exists in apc + $success = apc_fetch($this->_cachePrefix.$pCoord.'.cache'); + if ($success === false) { + // Entry no longer exists in APC, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in APC'); + } + return true; + } + return false; + } // function isDataSet() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (parent::isDataSet($pCoord)) { + $obj = apc_fetch($this->_cachePrefix.$pCoord.'.cache'); + if ($obj === false) { + // Entry no longer exists in APC, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in APC'); + } + } else { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + $this->_currentObject = unserialize($obj); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + /** + * Delete a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to delete + * @throws Exception + */ + public function deleteCacheData($pCoord) { + // Delete the entry from APC + apc_delete($this->_cachePrefix.$pCoord.'.cache'); + + // Delete the entry from our cell address array + parent::deleteCacheData($pCoord); + } // function deleteCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + + // Flush the APC cache + $this->__destruct(); + + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent, $arguments) { + $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; + + if (is_null($this->_cachePrefix)) { + if (function_exists('posix_getpid')) { + $baseUnique = posix_getpid(); + } else { + $baseUnique = mt_rand(); + } + $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; + $this->_cacheTime = $cacheTime; + + parent::__construct($parent); + } + } // function __construct() + + + public function __destruct() { + $cacheList = $this->getCellList(); + foreach($cacheList as $cellID) { + apc_delete($this->_cachePrefix.$cellID.'.cache'); + } + } // function __destruct() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/CacheBase.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/CacheBase.php index 9871d90dd..8a2c25049 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/CacheBase.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/CacheBase.php @@ -1,153 +1,153 @@ -_parent = $parent; - } // function __construct() - - - /** - * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * @return void - * @return boolean - */ - public function isDataSet($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return true; - } - // Check if the requested entry exists in the cache - return isset($this->_cellCache[$pCoord]); - } // function isDataSet() - - - /** - * Add or Update a cell in cache - * - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function updateCacheData(PHPExcel_Cell $cell) { - $pCoord = $cell->getCoordinate(); - - return $this->addCacheData($pCoord,$cell); - } // function updateCacheData() - - - /** - * Delete a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to delete - * @throws Exception - */ - public function deleteCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - $this->_currentObject->detach(); - $this->_currentObjectID = $this->_currentObject = null; - } - - if (isset($this->_cellCache[$pCoord])) { - $this->_cellCache[$pCoord]->detach(); - unset($this->_cellCache[$pCoord]); - } - } // function deleteCacheData() - - - /** - * Get a list of all cell addresses currently held in cache - * - * @return array of string - */ - public function getCellList() { - return array_keys($this->_cellCache); - } // function getCellList() - - - /** - * Sort the list of all cell addresses currently held in cache by row and column - * - * @return void - */ - public function getSortedCellList() { - $sortKeys = array(); - foreach ($this->_cellCache as $coord => $value) { - preg_match('/^(\w+)(\d+)$/U',$coord,$matches); - list(,$colNum,$rowNum) = $matches; - $sortKeys[$coord] = str_pad($rowNum . str_pad($colNum,3,'@',STR_PAD_LEFT),12,'0',STR_PAD_LEFT); - } - asort($sortKeys); - - return array_keys($sortKeys); - } // function sortCellList() - -} +_parent = $parent; + } // function __construct() + + + /** + * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? + * + * @param string $pCoord Coordinate address of the cell to check + * @return void + * @return boolean + */ + public function isDataSet($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return true; + } + // Check if the requested entry exists in the cache + return isset($this->_cellCache[$pCoord]); + } // function isDataSet() + + + /** + * Add or Update a cell in cache + * + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function updateCacheData(PHPExcel_Cell $cell) { + $pCoord = $cell->getCoordinate(); + + return $this->addCacheData($pCoord,$cell); + } // function updateCacheData() + + + /** + * Delete a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to delete + * @throws Exception + */ + public function deleteCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + $this->_currentObject->detach(); + $this->_currentObjectID = $this->_currentObject = null; + } + + if (isset($this->_cellCache[$pCoord])) { + $this->_cellCache[$pCoord]->detach(); + unset($this->_cellCache[$pCoord]); + } + } // function deleteCacheData() + + + /** + * Get a list of all cell addresses currently held in cache + * + * @return array of string + */ + public function getCellList() { + return array_keys($this->_cellCache); + } // function getCellList() + + + /** + * Sort the list of all cell addresses currently held in cache by row and column + * + * @return void + */ + public function getSortedCellList() { + $sortKeys = array(); + foreach ($this->_cellCache as $coord => $value) { + preg_match('/^(\w+)(\d+)$/U',$coord,$matches); + list(,$colNum,$rowNum) = $matches; + $sortKeys[$coord] = str_pad($rowNum . str_pad($colNum,3,'@',STR_PAD_LEFT),12,'0',STR_PAD_LEFT); + } + asort($sortKeys); + + return array_keys($sortKeys); + } // function sortCellList() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/DiscISAM.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/DiscISAM.php index e685607f7..f00c377c2 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/DiscISAM.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/DiscISAM.php @@ -1,143 +1,143 @@ -_currentObject->detach(); - - fseek($this->_fileHandle,0,SEEK_END); - $offset = ftell($this->_fileHandle); - fwrite($this->_fileHandle, serialize($this->_currentObject)); - $this->_cellCache[$this->_currentObjectID] = array('ptr' => $offset, - 'sz' => ftell($this->_fileHandle) - $offset - ); - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->_cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - fseek($this->_fileHandle,$this->_cellCache[$pCoord]['ptr']); - $this->_currentObject = unserialize(fread($this->_fileHandle,$this->_cellCache[$pCoord]['sz'])); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - - // Close down the temporary cache file - $this->__destruct(); - } // function unsetWorksheetCells() - - - public function __construct(PHPExcel_Worksheet $parent) { - parent::__construct($parent); - if (is_null($this->_fileHandle)) { - if (function_exists('posix_getpid')) { - $baseUnique = posix_getpid(); - } else { - $baseUnique = mt_rand(); - } - $this->_fileName = sys_get_temp_dir().'/PHPExcel.'.uniqid($baseUnique,true).'.cache'; - $this->_fileHandle = fopen($this->_fileName,'a+'); - } - } // function __construct() - - - public function __destruct() { - if (!is_null($this->_fileHandle)) { - fclose($this->_fileHandle); - unlink($this->_fileName); - } - $this->_fileHandle = null; - } // function __destruct() - -} +_currentObject->detach(); + + fseek($this->_fileHandle,0,SEEK_END); + $offset = ftell($this->_fileHandle); + fwrite($this->_fileHandle, serialize($this->_currentObject)); + $this->_cellCache[$this->_currentObjectID] = array('ptr' => $offset, + 'sz' => ftell($this->_fileHandle) - $offset + ); + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (!isset($this->_cellCache[$pCoord])) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + fseek($this->_fileHandle,$this->_cellCache[$pCoord]['ptr']); + $this->_currentObject = unserialize(fread($this->_fileHandle,$this->_cellCache[$pCoord]['sz'])); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + + // Close down the temporary cache file + $this->__destruct(); + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent) { + parent::__construct($parent); + if (is_null($this->_fileHandle)) { + if (function_exists('posix_getpid')) { + $baseUnique = posix_getpid(); + } else { + $baseUnique = mt_rand(); + } + $this->_fileName = sys_get_temp_dir().'/PHPExcel.'.uniqid($baseUnique,true).'.cache'; + $this->_fileHandle = fopen($this->_fileName,'a+'); + } + } // function __construct() + + + public function __destruct() { + if (!is_null($this->_fileHandle)) { + fclose($this->_fileHandle); + unlink($this->_fileName); + } + $this->_fileHandle = null; + } // function __destruct() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memcache.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memcache.php index f1fc43c40..d5e9ab1a1 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memcache.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memcache.php @@ -1,214 +1,214 @@ -_currentObject->detach(); - - $obj = serialize($this->_currentObject); - if (!$this->_memcache->replace($this->_cachePrefix.$this->_currentObjectID.'.cache',$obj,NULL,$this->_cacheTime)) { - if (!$this->_memcache->add($this->_cachePrefix.$this->_currentObjectID.'.cache',$obj,NULL,$this->_cacheTime)) { - $this->__destruct(); - throw new Exception('Failed to store cell in Memcache'); - } - } - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - $this->_cellCache[$pCoord] = true; - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * @return void - * @return boolean - */ - public function isDataSet($pCoord) { - // Check if the requested entry is the current object, or exists in the cache - if (parent::isDataSet($pCoord)) { - if ($this->_currentObjectID == $pCoord) { - return true; - } - // Check if the requested entry still exists in apc - $success = $this->_memcache->get($this->_cachePrefix.$pCoord.'.cache'); - if ($success === false) { - // Entry no longer exists in Memcache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in Memcache'); - } - return true; - } - return false; - } // function isDataSet() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (parent::isDataSet($pCoord)) { - $obj = $this->_memcache->get($this->_cachePrefix.$pCoord.'.cache'); - if ($obj === false) { - // Entry no longer exists in Memcache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in Memcache'); - } - } else { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize($obj); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - /** - * Delete a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to delete - * @throws Exception - */ - public function deleteCacheData($pCoord) { - // Delete the entry from Memcache - $this->_memcache->delete($this->_cachePrefix.$pCoord.'.cache'); - - // Delete the entry from our cell address array - parent::deleteCacheData($pCoord); - } // function deleteCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - - // Flush the Memcache cache - $this->__destruct(); - - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - - - public function __construct(PHPExcel_Worksheet $parent, $arguments) { - $memcacheServer = (isset($arguments['memcacheServer'])) ? $arguments['memcacheServer'] : 'localhost'; - $memcachePort = (isset($arguments['memcachePort'])) ? $arguments['memcachePort'] : 11211; - $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; - - if (is_null($this->_cachePrefix)) { - if (function_exists('posix_getpid')) { - $baseUnique = posix_getpid(); - } else { - $baseUnique = mt_rand(); - } - $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; - - // Set a new Memcache object and connect to the Memcache server - $this->_memcache = new Memcache(); - if (!$this->_memcache->addServer($memcacheServer, $memcachePort, false, 50, 5, 5, true, array($this, 'failureCallback')) { - throw new Exception('Could not connect to Memcache server at '.$memcacheServer.':'.$memcachePort); - } - $this->_cacheTime = $cacheTime; - - parent::__construct($parent); - } - } // function __construct() - - - public function failureCallback($host, $port) { - throw new Exception('memcache '.$host.':'.$port' failed'); - } - - - public function __destruct() { - $cacheList = $this->getCellList(); - foreach($cacheList as $cellID) { - $this->_memcache->delete($this->_cachePrefix.$cellID.'.cache'); - } - } // function __destruct() - -} - - -?> +_currentObject->detach(); + + $obj = serialize($this->_currentObject); + if (!$this->_memcache->replace($this->_cachePrefix.$this->_currentObjectID.'.cache',$obj,NULL,$this->_cacheTime)) { + if (!$this->_memcache->add($this->_cachePrefix.$this->_currentObjectID.'.cache',$obj,NULL,$this->_cacheTime)) { + $this->__destruct(); + throw new Exception('Failed to store cell in Memcache'); + } + } + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + $this->_cellCache[$pCoord] = true; + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? + * + * @param string $pCoord Coordinate address of the cell to check + * @return void + * @return boolean + */ + public function isDataSet($pCoord) { + // Check if the requested entry is the current object, or exists in the cache + if (parent::isDataSet($pCoord)) { + if ($this->_currentObjectID == $pCoord) { + return true; + } + // Check if the requested entry still exists in apc + $success = $this->_memcache->get($this->_cachePrefix.$pCoord.'.cache'); + if ($success === false) { + // Entry no longer exists in Memcache, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in Memcache'); + } + return true; + } + return false; + } // function isDataSet() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (parent::isDataSet($pCoord)) { + $obj = $this->_memcache->get($this->_cachePrefix.$pCoord.'.cache'); + if ($obj === false) { + // Entry no longer exists in Memcache, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in Memcache'); + } + } else { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + $this->_currentObject = unserialize($obj); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + /** + * Delete a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to delete + * @throws Exception + */ + public function deleteCacheData($pCoord) { + // Delete the entry from Memcache + $this->_memcache->delete($this->_cachePrefix.$pCoord.'.cache'); + + // Delete the entry from our cell address array + parent::deleteCacheData($pCoord); + } // function deleteCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + + // Flush the Memcache cache + $this->__destruct(); + + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent, $arguments) { + $memcacheServer = (isset($arguments['memcacheServer'])) ? $arguments['memcacheServer'] : 'localhost'; + $memcachePort = (isset($arguments['memcachePort'])) ? $arguments['memcachePort'] : 11211; + $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; + + if (is_null($this->_cachePrefix)) { + if (function_exists('posix_getpid')) { + $baseUnique = posix_getpid(); + } else { + $baseUnique = mt_rand(); + } + $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; + + // Set a new Memcache object and connect to the Memcache server + $this->_memcache = new Memcache(); + if (!$this->_memcache->addServer($memcacheServer, $memcachePort, false, 50, 5, 5, true, array($this, 'failureCallback')) { + throw new Exception('Could not connect to Memcache server at '.$memcacheServer.':'.$memcachePort); + } + $this->_cacheTime = $cacheTime; + + parent::__construct($parent); + } + } // function __construct() + + + public function failureCallback($host, $port) { + throw new Exception('memcache '.$host.':'.$port' failed'); + } + + + public function __destruct() { + $cacheList = $this->getCellList(); + foreach($cacheList as $cellID) { + $this->_memcache->delete($this->_cachePrefix.$cellID.'.cache'); + } + } // function __destruct() + +} + + +?> diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memory.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memory.php index be0fadea6..bbb7cb069 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memory.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memory.php @@ -1,85 +1,85 @@ -_cellCache[$pCoord] = $cell; - return $cell; - } // function addCacheData() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - // Check if the entry that has been requested actually exists - if (!isset($this->_cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Return requested entry - return $this->_cellCache[$pCoord]; - } // function getCacheData() - - - public function unsetWorksheetCells() { - // Because cells are all stored as intact objects in memory, we need to detach each one from the parent - foreach($this->_cellCache as $k => &$cell) { - $cell->detach(); - $this->_cellCache[$k] = null; - } - unset($cell); - - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - -} +_cellCache[$pCoord] = $cell; + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + // Check if the entry that has been requested actually exists + if (!isset($this->_cellCache[$pCoord])) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Return requested entry + return $this->_cellCache[$pCoord]; + } // function getCacheData() + + + public function unsetWorksheetCells() { + // Because cells are all stored as intact objects in memory, we need to detach each one from the parent + foreach($this->_cellCache as $k => &$cell) { + $cell->detach(); + $this->_cellCache[$k] = null; + } + unset($cell); + + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemoryGZip.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemoryGZip.php index c28138907..529b6bf27 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemoryGZip.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemoryGZip.php @@ -1,107 +1,107 @@ -_currentObject->detach(); - - $this->_cellCache[$this->_currentObjectID] = gzdeflate(serialize($this->_currentObject)); - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->_cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize(gzinflate($this->_cellCache[$pCoord])); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - -} +_currentObject->detach(); + + $this->_cellCache[$this->_currentObjectID] = gzdeflate(serialize($this->_currentObject)); + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (!isset($this->_cellCache[$pCoord])) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + $this->_currentObject = unserialize(gzinflate($this->_cellCache[$pCoord])); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemorySerialized.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemorySerialized.php index 9c29ea245..cbc1dc152 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemorySerialized.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemorySerialized.php @@ -1,107 +1,107 @@ -_currentObject->detach(); - - $this->_cellCache[$this->_currentObjectID] = serialize($this->_currentObject); - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->_cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize($this->_cellCache[$pCoord]); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - -} +_currentObject->detach(); + + $this->_cellCache[$this->_currentObjectID] = serialize($this->_currentObject); + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (!isset($this->_cellCache[$pCoord])) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + $this->_currentObject = unserialize($this->_cellCache[$pCoord]); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/PHPTemp.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/PHPTemp.php index 98cd9524c..3c426a76f 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/PHPTemp.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/PHPTemp.php @@ -1,137 +1,137 @@ -_currentObject->detach(); - - fseek($this->_fileHandle,0,SEEK_END); - $offset = ftell($this->_fileHandle); - fwrite($this->_fileHandle, serialize($this->_currentObject)); - $this->_cellCache[$this->_currentObjectID] = array('ptr' => $offset, - 'sz' => ftell($this->_fileHandle) - $offset - ); - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->_cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - fseek($this->_fileHandle,$this->_cellCache[$pCoord]['ptr']); - $this->_currentObject = unserialize(fread($this->_fileHandle,$this->_cellCache[$pCoord]['sz'])); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - - // Close down the php://temp file - $this->__destruct(); - } // function unsetWorksheetCells() - - - public function __construct(PHPExcel_Worksheet $parent, $memoryCacheSize = '1MB') { - $memoryCacheSize = (isset($arguments['memoryCacheSize'])) ? $arguments['memoryCacheSize'] : '1MB'; - - parent::__construct($parent); - if (is_null($this->_fileHandle)) { - $this->_fileHandle = fopen('php://temp/maxmemory:'.$memoryCacheSize,'a+'); - } - } // function __construct() - - - public function __destruct() { - if (!is_null($this->_fileHandle)) { - fclose($this->_fileHandle); - } - $this->_fileHandle = null; - } // function __destruct() - -} +_currentObject->detach(); + + fseek($this->_fileHandle,0,SEEK_END); + $offset = ftell($this->_fileHandle); + fwrite($this->_fileHandle, serialize($this->_currentObject)); + $this->_cellCache[$this->_currentObjectID] = array('ptr' => $offset, + 'sz' => ftell($this->_fileHandle) - $offset + ); + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (!isset($this->_cellCache[$pCoord])) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + fseek($this->_fileHandle,$this->_cellCache[$pCoord]['ptr']); + $this->_currentObject = unserialize(fread($this->_fileHandle,$this->_cellCache[$pCoord]['sz'])); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + + // Close down the php://temp file + $this->__destruct(); + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent, $memoryCacheSize = '1MB') { + $memoryCacheSize = (isset($arguments['memoryCacheSize'])) ? $arguments['memoryCacheSize'] : '1MB'; + + parent::__construct($parent); + if (is_null($this->_fileHandle)) { + $this->_fileHandle = fopen('php://temp/maxmemory:'.$memoryCacheSize,'a+'); + } + } // function __construct() + + + public function __destruct() { + if (!is_null($this->_fileHandle)) { + fclose($this->_fileHandle); + } + $this->_fileHandle = null; + } // function __destruct() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Wincache.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Wincache.php index d759d7c40..c14bb1a37 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Wincache.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Wincache.php @@ -1,201 +1,201 @@ -_currentObject->detach(); - - $obj = serialize($this->_currentObject); - if (wincache_ucache_exists($this->_cachePrefix.$this->_currentObjectID.'.cache')) { - wincache_ucache_set($this->_cachePrefix.$this->_currentObjectID.'.cache', $obj, $this->_cacheTime); - } else { - wincache_ucache_add($this->_cachePrefix.$this->_currentObjectID.'.cache', $obj, $this->_cacheTime); - } - - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - $this->_cellCache[$pCoord] = true; - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * @return void - * @return boolean - */ - public function isDataSet($pCoord) { - // Check if the requested entry is the current object, or exists in the cache - if (parent::isDataSet($pCoord)) { - if ($this->_currentObjectID == $pCoord) { - return true; - } - // Check if the requested entry still exists in cache - $success = wincache_ucache_exists($this->_cachePrefix.$pCoord.'.cache'); - if ($success === false) { - // Entry no longer exists in Wincache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in Wincache'); - } - return true; - } - return false; - } // function isDataSet() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - $obj = null; - if (parent::isDataSet($pCoord)) { - $success = false; - $obj = wincache_ucache_get($this->_cachePrefix.$pCoord.'.cache', $success); - if ($success === false) { - // Entry no longer exists in Wincache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in Wincache'); - } - } else { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize($obj); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - /** - * Delete a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to delete - * @throws Exception - */ - public function deleteCacheData($pCoord) { - // Delete the entry from Wincache - wincache_ucache_delete($this->_cachePrefix.$pCoord.'.cache'); - - // Delete the entry from our cell address array - parent::deleteCacheData($pCoord); - } // function deleteCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - - // Flush the Wincache cache - $this->__destruct(); - - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - - - public function __construct(PHPExcel_Worksheet $parent, $arguments) { - $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; - - if (is_null($this->_cachePrefix)) { - if (function_exists('posix_getpid')) { - $baseUnique = posix_getpid(); - } else { - $baseUnique = mt_rand(); - } - $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; - $this->_cacheTime = $cacheTime; - - parent::__construct($parent); - } - } // function __construct() - - - public function __destruct() { - $cacheList = $this->getCellList(); - foreach($cacheList as $cellID) { - wincache_ucache_delete($this->_cachePrefix.$cellID.'.cache'); - } - } // function __destruct() - -} - - +_currentObject->detach(); + + $obj = serialize($this->_currentObject); + if (wincache_ucache_exists($this->_cachePrefix.$this->_currentObjectID.'.cache')) { + wincache_ucache_set($this->_cachePrefix.$this->_currentObjectID.'.cache', $obj, $this->_cacheTime); + } else { + wincache_ucache_add($this->_cachePrefix.$this->_currentObjectID.'.cache', $obj, $this->_cacheTime); + } + + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + $this->_cellCache[$pCoord] = true; + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? + * + * @param string $pCoord Coordinate address of the cell to check + * @return void + * @return boolean + */ + public function isDataSet($pCoord) { + // Check if the requested entry is the current object, or exists in the cache + if (parent::isDataSet($pCoord)) { + if ($this->_currentObjectID == $pCoord) { + return true; + } + // Check if the requested entry still exists in cache + $success = wincache_ucache_exists($this->_cachePrefix.$pCoord.'.cache'); + if ($success === false) { + // Entry no longer exists in Wincache, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in Wincache'); + } + return true; + } + return false; + } // function isDataSet() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + $obj = null; + if (parent::isDataSet($pCoord)) { + $success = false; + $obj = wincache_ucache_get($this->_cachePrefix.$pCoord.'.cache', $success); + if ($success === false) { + // Entry no longer exists in Wincache, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in Wincache'); + } + } else { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + $this->_currentObject = unserialize($obj); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + /** + * Delete a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to delete + * @throws Exception + */ + public function deleteCacheData($pCoord) { + // Delete the entry from Wincache + wincache_ucache_delete($this->_cachePrefix.$pCoord.'.cache'); + + // Delete the entry from our cell address array + parent::deleteCacheData($pCoord); + } // function deleteCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + + // Flush the Wincache cache + $this->__destruct(); + + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent, $arguments) { + $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; + + if (is_null($this->_cachePrefix)) { + if (function_exists('posix_getpid')) { + $baseUnique = posix_getpid(); + } else { + $baseUnique = mt_rand(); + } + $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; + $this->_cacheTime = $cacheTime; + + parent::__construct($parent); + } + } // function __construct() + + + public function __destruct() { + $cacheList = $this->getCellList(); + foreach($cacheList as $cellID) { + wincache_ucache_delete($this->_cachePrefix.$cellID.'.cache'); + } + } // function __destruct() + +} + + ?> \ No newline at end of file diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorageFactory.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorageFactory.php index d4c947976..1486ad11a 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorageFactory.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorageFactory.php @@ -1,131 +1,131 @@ - array( - ), - self::cache_in_memory_gzip => array( - ), - self::cache_in_memory_serialized => array( - ), - self::cache_to_phpTemp => array( 'memoryCacheSize' => '1MB' - ), - self::cache_to_discISAM => array( - ), - self::cache_to_apc => array( 'cacheTime' => 600 - ), - self::cache_to_memcache => array( 'memcacheServer' => 'localhost', - 'memcachePort' => 11211, - 'cacheTime' => 600 - ), - self::cache_to_wincache => array( 'cacheTime' => 600 - ) - ); - - - private static $_storageMethodParameters = array(); - - - public static function getCacheStorageMethod() { - if (!is_null(self::$_cacheStorageMethod)) { - return self::$_cacheStorageMethod; - } - return null; - } // function getCacheStorageMethod() - - - public static function getCacheStorageClass() { - if (!is_null(self::$_cacheStorageClass)) { - return self::$_cacheStorageClass; - } - return null; - } // function getCacheStorageClass() - - - public static function getCacheStorageMethods() { - return self::$_storageMethods; - } // function getCacheStorageMethods() - - - public static function initialize($method = self::cache_in_memory, $arguments = array()) { - if (!in_array($method,self::$_storageMethods)) { - return false; - } - - switch($method) { - case self::cache_to_apc : - if (!function_exists('apc_store')) { - return false; - } - if (apc_sma_info() === false) { - return false; - } - break; - case self::cache_to_memcache : - if (!function_exists('memcache_add')) { - return false; - } - break; - case self::cache_to_wincache : - if (!function_exists('wincache_ucache_add')) { - return false; - } - break; - } - - self::$_storageMethodParameters[$method] = self::$_storageMethodDefaultParameters[$method]; - foreach($arguments as $k => $v) { - if (isset(self::$_storageMethodParameters[$method][$k])) { - self::$_storageMethodParameters[$method][$k] = $v; - } - } - - if (is_null(self::$_cacheStorageMethod)) { - self::$_cacheStorageClass = 'PHPExcel_CachedObjectStorage_'.$method; - self::$_cacheStorageMethod = $method; - } - return true; - } // function initialize() - - - public static function getInstance(PHPExcel_Worksheet $parent) { - if (is_null(self::$_cacheStorageMethod)) { - self::initialize(); - } - - $instance = new self::$_cacheStorageClass($parent,self::$_storageMethodParameters[self::$_cacheStorageMethod]); - if (!is_null($instance)) { - return $instance; - } - - return false; - } // function getInstance() - + array( + ), + self::cache_in_memory_gzip => array( + ), + self::cache_in_memory_serialized => array( + ), + self::cache_to_phpTemp => array( 'memoryCacheSize' => '1MB' + ), + self::cache_to_discISAM => array( + ), + self::cache_to_apc => array( 'cacheTime' => 600 + ), + self::cache_to_memcache => array( 'memcacheServer' => 'localhost', + 'memcachePort' => 11211, + 'cacheTime' => 600 + ), + self::cache_to_wincache => array( 'cacheTime' => 600 + ) + ); + + + private static $_storageMethodParameters = array(); + + + public static function getCacheStorageMethod() { + if (!is_null(self::$_cacheStorageMethod)) { + return self::$_cacheStorageMethod; + } + return null; + } // function getCacheStorageMethod() + + + public static function getCacheStorageClass() { + if (!is_null(self::$_cacheStorageClass)) { + return self::$_cacheStorageClass; + } + return null; + } // function getCacheStorageClass() + + + public static function getCacheStorageMethods() { + return self::$_storageMethods; + } // function getCacheStorageMethods() + + + public static function initialize($method = self::cache_in_memory, $arguments = array()) { + if (!in_array($method,self::$_storageMethods)) { + return false; + } + + switch($method) { + case self::cache_to_apc : + if (!function_exists('apc_store')) { + return false; + } + if (apc_sma_info() === false) { + return false; + } + break; + case self::cache_to_memcache : + if (!function_exists('memcache_add')) { + return false; + } + break; + case self::cache_to_wincache : + if (!function_exists('wincache_ucache_add')) { + return false; + } + break; + } + + self::$_storageMethodParameters[$method] = self::$_storageMethodDefaultParameters[$method]; + foreach($arguments as $k => $v) { + if (isset(self::$_storageMethodParameters[$method][$k])) { + self::$_storageMethodParameters[$method][$k] = $v; + } + } + + if (is_null(self::$_cacheStorageMethod)) { + self::$_cacheStorageClass = 'PHPExcel_CachedObjectStorage_'.$method; + self::$_cacheStorageMethod = $method; + } + return true; + } // function initialize() + + + public static function getInstance(PHPExcel_Worksheet $parent) { + if (is_null(self::$_cacheStorageMethod)) { + self::initialize(); + } + + $instance = new self::$_cacheStorageClass($parent,self::$_storageMethodParameters[self::$_cacheStorageMethod]); + if (!is_null($instance)) { + return $instance; + } + + return false; + } // function getInstance() + } \ No newline at end of file diff --git a/libraries/PHPExcel/PHPExcel/Reader/Excel2007.php b/libraries/PHPExcel/PHPExcel/Reader/Excel2007.php index 031dee559..43826396a 100644 --- a/libraries/PHPExcel/PHPExcel/Reader/Excel2007.php +++ b/libraries/PHPExcel/PHPExcel/Reader/Excel2007.php @@ -160,9 +160,9 @@ class PHPExcel_Reader_Excel2007 implements PHPExcel_Reader_IReader */ public function canRead($pFilename) { - // Check if zip class exists - if (!class_exists('ZipArchive')) { - return false; + // Check if zip class exists + if (!class_exists('ZipArchive')) { + return false; } // Check if file exists diff --git a/libraries/PHPExcel/PHPExcel/Reader/Excel5.php b/libraries/PHPExcel/PHPExcel/Reader/Excel5.php index a4f20e7a4..0b52918c4 100644 --- a/libraries/PHPExcel/PHPExcel/Reader/Excel5.php +++ b/libraries/PHPExcel/PHPExcel/Reader/Excel5.php @@ -1,6177 +1,6177 @@ -_data - * - * @var int - */ - private $_dataSize; - - /** - * Current position in stream - * - * @var integer - */ - private $_pos; - - /** - * Workbook to be returned by the reader. - * - * @var PHPExcel - */ - private $_phpExcel; - - /** - * Worksheet that is currently being built by the reader. - * - * @var PHPExcel_Worksheet - */ - private $_phpSheet; - - /** - * BIFF version - * - * @var int - */ - private $_version; - - /** - * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95) - * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE' - * - * @var string - */ - private $_codepage; - - /** - * Shared formats - * - * @var array - */ - private $_formats; - - /** - * Shared fonts - * - * @var array - */ - private $_objFonts; - - /** - * Color palette - * - * @var array - */ - private $_palette; - - /** - * Worksheets - * - * @var array - */ - private $_sheets; - - /** - * External books - * - * @var array - */ - private $_externalBooks; - - /** - * REF structures. Only applies to BIFF8. - * - * @var array - */ - private $_ref; - - /** - * External names - * - * @var array - */ - private $_externalNames; - - /** - * Defined names - * - * @var array - */ - private $_definedname; - - /** - * Shared strings. Only applies to BIFF8. - * - * @var array - */ - private $_sst; - - /** - * Panes are frozen? (in sheet currently being read). See WINDOW2 record. - * - * @var boolean - */ - private $_frozen; - - /** - * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record. - * - * @var boolean - */ - private $_isFitToPages; - - /** - * Objects. One OBJ record contributes with one entry. - * - * @var array - */ - private $_objs; - - /** - * The combined MSODRAWINGGROUP data - * - * @var string - */ - private $_drawingGroupData; - - /** - * The combined MSODRAWING data (per sheet) - * - * @var string - */ - private $_drawingData; - - /** - * Keep track of XF index - * - * @var int - */ - private $_xfIndex; - - /** - * Mapping of XF index (that is a cell XF) to final index in cellXf collection - * - * @var array - */ - private $_mapCellXfIndex; - - /** - * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection - * - * @var array - */ - private $_mapCellStyleXfIndex; - - /** - * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value. - * - * @var array - */ - private $_sharedFormulas; - - /** - * The shared formula parts in a sheet. One FORMULA record contributes with one value if it - * refers to a shared formula. - * - * @var array - */ - private $_sharedFormulaParts; - - /** - * Read data only? - * - * @return boolean - */ - public function getReadDataOnly() - { - return $this->_readDataOnly; - } - - /** - * Set read data only - * - * @param boolean $pValue - * @return PHPExcel_Reader_Excel5 - */ - public function setReadDataOnly($pValue = false) - { - $this->_readDataOnly = $pValue; - return $this; - } - - /** - * Get which sheets to load - * - * @return mixed - */ - public function getLoadSheetsOnly() - { - return $this->_loadSheetsOnly; - } - - /** - * Set which sheets to load - * - * @param mixed $value - * @return PHPExcel_Reader_Excel5 - */ - public function setLoadSheetsOnly($value = null) - { - $this->_loadSheetsOnly = is_array($value) ? - $value : array($value); - return $this; - } - - /** - * Set all sheets to load - * - * @return PHPExcel_Reader_Excel5 - */ - public function setLoadAllSheets() - { - $this->_loadSheetsOnly = null; - return $this; - } - - /** - * Read filter - * - * @return PHPExcel_Reader_IReadFilter - */ - public function getReadFilter() { - return $this->_readFilter; - } - - /** - * Set read filter - * - * @param PHPExcel_Reader_IReadFilter $pValue - * @return PHPExcel_Reader_Excel5 - */ - public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) { - $this->_readFilter = $pValue; - return $this; - } - - /** - * Create a new PHPExcel_Reader_Excel5 instance - */ - public function __construct() { - $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter(); - } - - /** - * Can the current PHPExcel_Reader_IReader read the file? - * - * @param string $pFileName - * @return boolean - */ - public function canRead($pFilename) - { - // Check if file exists - if (!file_exists($pFilename)) { - throw new Exception("Could not open " . $pFilename . " for reading! File does not exist."); - } - - try { - // Use ParseXL for the hard work. - $ole = new PHPExcel_Shared_OLERead(); - - // get excel data - $res = $ole->read($pFilename); - return true; - - } catch (Exception $e) { - return false; - } - } - - /** - * Loads PHPExcel from file - * - * @param string $pFilename - * @return PHPExcel - * @throws Exception - */ - public function load($pFilename) - { - // Read the OLE file - $this->_loadOLE($pFilename); - - // Initialisations - $this->_phpExcel = new PHPExcel; - $this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet - if (!$this->_readDataOnly) { - $this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style - $this->_phpExcel->removeCellXfByIndex(0); // remove the default style - } - - // Read the summary information stream (containing meta data) - $this->_readSummaryInformation(); - - // total byte size of Excel data (workbook global substream + sheet substreams) - $this->_dataSize = strlen($this->_data); - - // initialize - $this->_pos = 0; - $this->_codepage = 'CP1252'; - $this->_formats = array(); - $this->_objFonts = array(); - $this->_palette = array(); - $this->_sheets = array(); - $this->_externalBooks = array(); - $this->_ref = array(); - $this->_definedname = array(); - $this->_sst = array(); - $this->_drawingGroupData = ''; - $this->_xfIndex = ''; - $this->_mapCellXfIndex = array(); - $this->_mapCellStyleXfIndex = array(); - - // Parse Workbook Global Substream - while ($this->_pos < $this->_dataSize) { - $code = $this->_GetInt2d($this->_data, $this->_pos); - - switch ($code) { - case self::XLS_Type_BOF: $this->_readBof(); break; - case self::XLS_Type_FILEPASS: $this->_readFilepass(); break; - case self::XLS_Type_CODEPAGE: $this->_readCodepage(); break; - case self::XLS_Type_DATEMODE: $this->_readDateMode(); break; - case self::XLS_Type_FONT: $this->_readFont(); break; - case self::XLS_Type_FORMAT: $this->_readFormat(); break; - case self::XLS_Type_XF: $this->_readXf(); break; - case self::XLS_Type_XFEXT: $this->_readXfExt(); break; - case self::XLS_Type_STYLE: $this->_readStyle(); break; - case self::XLS_Type_PALETTE: $this->_readPalette(); break; - case self::XLS_Type_SHEET: $this->_readSheet(); break; - case self::XLS_Type_EXTERNALBOOK: $this->_readExternalBook(); break; - case self::XLS_Type_EXTERNNAME: $this->_readExternName(); break; - case self::XLS_Type_EXTERNSHEET: $this->_readExternSheet(); break; - case self::XLS_Type_DEFINEDNAME: $this->_readDefinedName(); break; - case self::XLS_Type_MSODRAWINGGROUP: $this->_readMsoDrawingGroup(); break; - case self::XLS_Type_SST: $this->_readSst(); break; - case self::XLS_Type_EOF: $this->_readDefault(); break 2; - default: $this->_readDefault(); break; - } - } - - // Resolve indexed colors for font, fill, and border colors - // Cannot be resolved already in XF record, because PALETTE record comes afterwards - if (!$this->_readDataOnly) { - foreach ($this->_objFonts as $objFont) { - if (isset($objFont->colorIndex)) { - $color = $this->_readColor($objFont->colorIndex); - $objFont->getColor()->setRGB($color['rgb']); - } - } - - foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) { - // fill start and end color - $fill = $objStyle->getFill(); - - if (isset($fill->startcolorIndex)) { - $startColor = $this->_readColor($fill->startcolorIndex); - $fill->getStartColor()->setRGB($startColor['rgb']); - } - - if (isset($fill->endcolorIndex)) { - $endColor = $this->_readColor($fill->endcolorIndex); - $fill->getEndColor()->setRGB($endColor['rgb']); - } - - // border colors - $top = $objStyle->getBorders()->getTop(); - $right = $objStyle->getBorders()->getRight(); - $bottom = $objStyle->getBorders()->getBottom(); - $left = $objStyle->getBorders()->getLeft(); - $diagonal = $objStyle->getBorders()->getDiagonal(); - - if (isset($top->colorIndex)) { - $borderTopColor = $this->_readColor($top->colorIndex); - $top->getColor()->setRGB($borderTopColor['rgb']); - } - - if (isset($right->colorIndex)) { - $borderRightColor = $this->_readColor($right->colorIndex); - $right->getColor()->setRGB($borderRightColor['rgb']); - } - - if (isset($bottom->colorIndex)) { - $borderBottomColor = $this->_readColor($bottom->colorIndex); - $bottom->getColor()->setRGB($borderBottomColor['rgb']); - } - - if (isset($left->colorIndex)) { - $borderLeftColor = $this->_readColor($left->colorIndex); - $left->getColor()->setRGB($borderLeftColor['rgb']); - } - - if (isset($diagonal->colorIndex)) { - $borderDiagonalColor = $this->_readColor($diagonal->colorIndex); - $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']); - } - } - } - - // treat MSODRAWINGGROUP records, workbook-level Escher - if (!$this->_readDataOnly && $this->_drawingGroupData) { - $escherWorkbook = new PHPExcel_Shared_Escher(); - $reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook); - $escherWorkbook = $reader->load($this->_drawingGroupData); - - // debug Escher stream - //$debug = new Debug_Escher(new PHPExcel_Shared_Escher()); - //$debug->load($this->_drawingGroupData); - } - - // Parse the individual sheets - foreach ($this->_sheets as $sheet) { - - if ($sheet['sheetType'] != 0x00) { - // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module - continue; - } - - // check if sheet should be skipped - if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) { - continue; - } - - // add sheet to PHPExcel object - $this->_phpSheet = $this->_phpExcel->createSheet(); - $this->_phpSheet->setTitle($sheet['name']); - $this->_phpSheet->setSheetState($sheet['sheetState']); - - $this->_pos = $sheet['offset']; - - // Initialize isFitToPages. May change after reading SHEETPR record. - $this->_isFitToPages = false; - - // Initialize drawingData - $this->_drawingData = ''; - - // Initialize objs - $this->_objs = array(); - - // Initialize shared formula parts - $this->_sharedFormulaParts = array(); - - // Initialize shared formulas - $this->_sharedFormulas = array(); - - while ($this->_pos <= $this->_dataSize - 4) { - $code = $this->_GetInt2d($this->_data, $this->_pos); - - switch ($code) { - case self::XLS_Type_BOF: $this->_readBof(); break; - case self::XLS_Type_PRINTGRIDLINES: $this->_readPrintGridlines(); break; - case self::XLS_Type_DEFAULTROWHEIGHT: $this->_readDefaultRowHeight(); break; - case self::XLS_Type_SHEETPR: $this->_readSheetPr(); break; - case self::XLS_Type_HORIZONTALPAGEBREAKS: $this->_readHorizontalPageBreaks(); break; - case self::XLS_Type_VERTICALPAGEBREAKS: $this->_readVerticalPageBreaks(); break; - case self::XLS_Type_HEADER: $this->_readHeader(); break; - case self::XLS_Type_FOOTER: $this->_readFooter(); break; - case self::XLS_Type_HCENTER: $this->_readHcenter(); break; - case self::XLS_Type_VCENTER: $this->_readVcenter(); break; - case self::XLS_Type_LEFTMARGIN: $this->_readLeftMargin(); break; - case self::XLS_Type_RIGHTMARGIN: $this->_readRightMargin(); break; - case self::XLS_Type_TOPMARGIN: $this->_readTopMargin(); break; - case self::XLS_Type_BOTTOMMARGIN: $this->_readBottomMargin(); break; - case self::XLS_Type_PAGESETUP: $this->_readPageSetup(); break; - case self::XLS_Type_PROTECT: $this->_readProtect(); break; - case self::XLS_Type_SCENPROTECT: $this->_readScenProtect(); break; - case self::XLS_Type_OBJECTPROTECT: $this->_readObjectProtect(); break; - case self::XLS_Type_PASSWORD: $this->_readPassword(); break; - case self::XLS_Type_DEFCOLWIDTH: $this->_readDefColWidth(); break; - case self::XLS_Type_COLINFO: $this->_readColInfo(); break; - case self::XLS_Type_DIMENSION: $this->_readDefault(); break; - case self::XLS_Type_ROW: $this->_readRow(); break; - case self::XLS_Type_DBCELL: $this->_readDefault(); break; - case self::XLS_Type_RK: $this->_readRk(); break; - case self::XLS_Type_LABELSST: $this->_readLabelSst(); break; - case self::XLS_Type_MULRK: $this->_readMulRk(); break; - case self::XLS_Type_NUMBER: $this->_readNumber(); break; - case self::XLS_Type_FORMULA: $this->_readFormula(); break; - case self::XLS_Type_SHAREDFMLA: $this->_readSharedFmla(); break; - case self::XLS_Type_BOOLERR: $this->_readBoolErr(); break; - case self::XLS_Type_MULBLANK: $this->_readMulBlank(); break; - case self::XLS_Type_LABEL: $this->_readLabel(); break; - case self::XLS_Type_BLANK: $this->_readBlank(); break; - case self::XLS_Type_MSODRAWING: $this->_readMsoDrawing(); break; - case self::XLS_Type_OBJ: $this->_readObj(); break; - case self::XLS_Type_WINDOW2: $this->_readWindow2(); break; - case self::XLS_Type_SCL: $this->_readScl(); break; - case self::XLS_Type_PANE: $this->_readPane(); break; - case self::XLS_Type_SELECTION: $this->_readSelection(); break; - case self::XLS_Type_MERGEDCELLS: $this->_readMergedCells(); break; - case self::XLS_Type_HYPERLINK: $this->_readHyperLink(); break; - case self::XLS_Type_DATAVALIDATIONS: $this->_readDataValidations(); break; - case self::XLS_Type_DATAVALIDATION: $this->_readDataValidation(); break; - case self::XLS_Type_SHEETLAYOUT: $this->_readSheetLayout(); break; - case self::XLS_Type_SHEETPROTECTION: $this->_readSheetProtection(); break; - case self::XLS_Type_RANGEPROTECTION: $this->_readRangeProtection(); break; - //case self::XLS_Type_IMDATA: $this->_readImData(); break; - case self::XLS_Type_CONTINUE: $this->_readContinue(); break; - case self::XLS_Type_EOF: $this->_readDefault(); break 2; - default: $this->_readDefault(); break; - } - - } - - // treat MSODRAWING records, sheet-level Escher - if (!$this->_readDataOnly && $this->_drawingData) { - $escherWorksheet = new PHPExcel_Shared_Escher(); - $reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet); - $escherWorksheet = $reader->load($this->_drawingData); - - // debug Escher stream - //$debug = new Debug_Escher(new PHPExcel_Shared_Escher()); - //$debug->load($this->_drawingData); - - // get all spContainers in one long array, so they can be mapped to OBJ records - $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers(); - } - - // treat OBJ records - foreach ($this->_objs as $n => $obj) { - - // the first shape container never has a corresponding OBJ record, hence $n + 1 - $spContainer = $allSpContainers[$n + 1]; - - // we skip all spContainers that are a part of a group shape since we cannot yet handle those - if ($spContainer->getNestingLevel() > 1) { - continue; - } - - // calculate the width and height of the shape - list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates()); - list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates()); - - $startOffsetX = $spContainer->getStartOffsetX(); - $startOffsetY = $spContainer->getStartOffsetY(); - $endOffsetX = $spContainer->getEndOffsetX(); - $endOffsetY = $spContainer->getEndOffsetY(); - - $width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX); - $height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY); - - // calculate offsetX and offsetY of the shape - $offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024; - $offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256; - - switch ($obj['type']) { - - case 0x08: - // picture - - // get index to BSE entry (1-based) - $BSEindex = $spContainer->getOPT(0x0104); - $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection(); - $BSE = $BSECollection[$BSEindex - 1]; - $blipType = $BSE->getBlipType(); - - // need check because some blip types are not supported by Escher reader such as EMF - if ($blip = $BSE->getBlip()) { - $ih = imagecreatefromstring($blip->getData()); - $drawing = new PHPExcel_Worksheet_MemoryDrawing(); - $drawing->setImageResource($ih); - - // width, height, offsetX, offsetY - $drawing->setResizeProportional(false); - $drawing->setWidth($width); - $drawing->setHeight($height); - $drawing->setOffsetX($offsetX); - $drawing->setOffsetY($offsetY); - - switch ($blipType) { - - case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG: - $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG); - $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG); - break; - - case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG: - $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG); - $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG); - break; - } - - $drawing->setWorksheet($this->_phpSheet); - $drawing->setCoordinates($spContainer->getStartCoordinates()); - } - - break; - - default: - // other object type - break; - - } - } - - // treat SHAREDFMLA records - if ($this->_version == self::XLS_BIFF8) { - foreach ($this->_sharedFormulaParts as $cell => $baseCell) { - $formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell); - $this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA); - } - } - } - - // add the named ranges (defined names) - foreach ($this->_definedname as $definedName) { - if ($definedName['isBuiltInName']) { - switch ($definedName['name']) { - - case pack('C', 0x06): - // print area - // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2 - - $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma? - - $extractedRanges = array(); - foreach ($ranges as $range) { - // $range should look like one of these - // Foo!$C$7:$J$66 - // Bar!$A$1:$IV$2 - - $explodes = explode('!', $range); - $sheetName = $explodes[0]; - - if (count($explodes) == 2) { - $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66 - } - } - if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) { - $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2 - } - break; - - case pack('C', 0x07): - // print titles (repeating rows) - // Assuming BIFF8, there are 3 cases - // 1. repeating rows - // formula looks like this: Sheet!$A$1:$IV$2 - // rows 1-2 repeat - // 2. repeating columns - // formula looks like this: Sheet!$A$1:$B$65536 - // columns A-B repeat - // 3. both repeating rows and repeating columns - // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2 - - $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma? - - foreach ($ranges as $range) { - // $range should look like this one of these - // Sheet!$A$1:$B$65536 - // Sheet!$A$1:$IV$2 - - $explodes = explode('!', $range); - - if (count($explodes) == 2) { - if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) { - - $extractedRange = $explodes[1]; - $extractedRange = str_replace('$', '', $extractedRange); - - $coordinateStrings = explode(':', $extractedRange); - if (count($coordinateStrings) == 2) { - list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]); - list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]); - - if ($firstColumn == 'A' and $lastColumn == 'IV') { - // then we have repeating rows - $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow)); - } elseif ($firstRow == 1 and $lastRow == 65536) { - // then we have repeating columns - $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn)); - } - } - } - } - } - break; - - } - } else { - // Extract range - $explodes = explode('!', $definedName['formula']); - - if (count($explodes) == 2) { - if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) { - $extractedRange = $explodes[1]; - $extractedRange = str_replace('$', '', $extractedRange); - - $localOnly = ($definedName['scope'] == 0) ? false : true; - $scope = ($definedName['scope'] == 0) ? - null : $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'] - 1]['name']); - - $this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, $localOnly, $scope) ); - } - } - } - } - - return $this->_phpExcel; - } - - /** - * Use OLE reader to extract the relevant data streams from the OLE file - * - * @param string $pFilename - */ - private function _loadOLE($pFilename) - { - // OLE reader - $ole = new PHPExcel_Shared_OLERead(); - - // get excel data - $res = $ole->read($pFilename); - $this->_data = $ole->getWorkBook(); - - // Get summary information data - $this->_summaryInformation = $ole->getSummaryInformation(); - } - - /** - * Read summary information - */ - private function _readSummaryInformation() - { - if (!isset($this->_summaryInformation)) { - return; - } - - // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark) - // offset: 2; size: 2; - // offset: 4; size: 2; OS version - // offset: 6; size: 2; OS indicator - // offset: 8; size: 16 - // offset: 24; size: 4; section count - - // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9 - // offset: 44; size: 4 - - // section header - // offset: 48; size: 4; section length - $secLength = $this->_GetInt4d($this->_summaryInformation, 48); - - // offset: 52; size: 4; property count - $countProperties = $this->_GetInt4d($this->_summaryInformation, 52); - - // initialize code page (used to resolve string values) - $codePage = 'CP1252'; - - // offset: 56; size: var - // loop through property decarations and properties - for ($i = 0; $i < $countProperties; ++$i) { - - // offset: 56 + 8 * $i; size: 4; property ID - $id = $this->_GetInt4d($this->_summaryInformation, 56 + 8 * $i); - - // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48) - $offset = $this->_GetInt4d($this->_summaryInformation, 60 + 8 * $i); - - $type = $this->_GetInt4d($this->_summaryInformation, 48 + $offset); - - // initialize property value - $value = null; - - // extract property value based on property type - switch ($type) { - case 0x02: // 2 byte signed integer - $value = $this->_GetInt2d($this->_summaryInformation, 52 + $offset); - break; - - case 0x03: // 4 byte signed integer - $value = $this->_GetInt4d($this->_summaryInformation, 52 + $offset); - break; - - case 0x13: // 4 byte unsigned integer - // not needed yet, fix later if necessary - break; - - case 0x1E: // null-terminated string prepended by dword string length - $byteLength = $this->_GetInt4d($this->_summaryInformation, 52 + $offset); - $value = substr($this->_summaryInformation, 56 + $offset, $byteLength); - $value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage); - $value = rtrim($value); - break; - - case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601) - // PHP-time - $value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation, 52 + $offset, 8)); - break; - - case 0x47: // Clipboard format - // not needed yet, fix later if necessary - break; - } - - // Use value of property id as appropriate - switch ($id) { - case 0x01: // Code Page - $codePage = PHPExcel_Shared_CodePage::NumberToName($value); - break; - - case 0x02: // Title - $this->_phpExcel->getProperties()->setTitle($value); - break; - - case 0x03: // Subject - $this->_phpExcel->getProperties()->setSubject($value); - break; - - case 0x04: // Author (Creator) - $this->_phpExcel->getProperties()->setCreator($value); - break; - - case 0x05: // Keywords - $this->_phpExcel->getProperties()->setKeywords($value); - break; - - case 0x06: // Comments (Description) - $this->_phpExcel->getProperties()->setDescription($value); - break; - - case 0x08: // Last Saved By (LastModifiedBy) - $this->_phpExcel->getProperties()->setLastModifiedBy($value); - break; - - case 0x09: // Revision - // not supported by PHPExcel - break; - - case 0x0C: // Created - $this->_phpExcel->getProperties()->setCreated($value); - break; - - case 0x0D: // Modified - $this->_phpExcel->getProperties()->setModified($value); - break; - - case 0x12: // Name of creating application - // not supported by PHPExcel - break; - } - } - } - - /** - * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record. - */ - private function _readDefault() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - } - - /** - * Read BOF - */ - private function _readBof() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 2; size: 2; type of the following data - $substreamType = $this->_GetInt2d($recordData, 2); - - switch ($substreamType) { - case self::XLS_WorkbookGlobals: - $version = $this->_GetInt2d($recordData, 0); - if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) { - throw new Exception('Cannot read this Excel file. Version is too old.'); - } - $this->_version = $version; - break; - - case self::XLS_Worksheet: - // do not use this version information for anything - // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream - break; - - default: - // substream, e.g. chart - // just skip the entire substream - do { - $code = $this->_GetInt2d($this->_data, $this->_pos); - $this->_readDefault(); - } while ($code != self::XLS_Type_EOF && $this->_pos < $this->_dataSize); - break; - } - } - - /** - * FILEPASS - * - * This record is part of the File Protection Block. It - * contains information about the read/write password of the - * file. All record contents following this record will be - * encrypted. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readFilepass() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - throw new Exception('Cannot read encrypted file'); - } - - /** - * CODEPAGE - * - * This record stores the text encoding used to write byte - * strings, stored as MS Windows code page identifier. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readCodepage() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; code page identifier - $codepage = $this->_GetInt2d($recordData, 0); - - $this->_codepage = PHPExcel_Shared_CodePage::NumberToName($codepage); - } - - /** - * DATEMODE - * - * This record specifies the base date for displaying date - * values. All dates are stored as count of days past this - * base date. In BIFF2-BIFF4 this record is part of the - * Calculation Settings Block. In BIFF5-BIFF8 it is - * stored in the Workbook Globals Substream. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readDateMode() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; 0 = base 1900, 1 = base 1904 - PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900); - if (ord($recordData{0}) == 1) { - PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904); - } - } - - /** - * Read a FONT record - */ - private function _readFont() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - $objFont = new PHPExcel_Style_Font(); - - // offset: 0; size: 2; height of the font (in twips = 1/20 of a point) - $size = $this->_GetInt2d($recordData, 0); - $objFont->setSize($size / 20); - - // offset: 2; size: 2; option flags - // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8) - // bit: 1; mask 0x0002; italic - $isItalic = (0x0002 & $this->_GetInt2d($recordData, 2)) >> 1; - if ($isItalic) $objFont->setItalic(true); - - // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8) - // bit: 3; mask 0x0008; strike - $isStrike = (0x0008 & $this->_GetInt2d($recordData, 2)) >> 3; - if ($isStrike) $objFont->setStrikethrough(true); - - // offset: 4; size: 2; colour index - $colorIndex = $this->_GetInt2d($recordData, 4); - $objFont->colorIndex = $colorIndex; - - // offset: 6; size: 2; font weight - $weight = $this->_GetInt2d($recordData, 6); - switch ($weight) { - case 0x02BC: - $objFont->setBold(true); - break; - } - - // offset: 8; size: 2; escapement type - $escapement = $this->_GetInt2d($recordData, 8); - switch ($escapement) { - case 0x0001: - $objFont->setSuperScript(true); - break; - case 0x0002: - $objFont->setSubScript(true); - break; - } - - // offset: 10; size: 1; underline type - $underlineType = ord($recordData{10}); - switch ($underlineType) { - case 0x00: - break; // no underline - case 0x01: - $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE); - break; - case 0x02: - $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLE); - break; - case 0x21: - $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING); - break; - case 0x22: - $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING); - break; - } - - // offset: 11; size: 1; font family - // offset: 12; size: 1; character set - // offset: 13; size: 1; not used - // offset: 14; size: var; font name - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringShort(substr($recordData, 14)); - } else { - $string = $this->_readByteStringShort(substr($recordData, 14)); - } - $objFont->setName($string['value']); - - $this->_objFonts[] = $objFont; - } - } - - /** - * FORMAT - * - * This record contains information about a number format. - * All FORMAT records occur together in a sequential list. - * - * In BIFF2-BIFF4 other records referencing a FORMAT record - * contain a zero-based index into this list. From BIFF5 on - * the FORMAT record contains the index itself that will be - * used by other records. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readFormat() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - $indexCode = $this->_GetInt2d($recordData, 0); - - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringLong(substr($recordData, 2)); - } else { - // BIFF7 - $string = $this->_readByteStringShort(substr($recordData, 2)); - } - - $formatString = $string['value']; - $this->_formats[$indexCode] = $formatString; - } - } - - /** - * XF - Extended Format - * - * This record contains formatting information for cells, rows, columns or styles. - * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF - * and 1 cell XF. - * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF - * and XF record 15 is a cell XF - * We only read the first cell style XF and skip the remaining cell style XF records - * We read all cell XF records. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readXf() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - $objStyle = new PHPExcel_Style(); - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; Index to FONT record - if ($this->_GetInt2d($recordData, 0) < 4) { - $fontIndex = $this->_GetInt2d($recordData, 0); - } else { - // this has to do with that index 4 is omitted in all BIFF versions for some strange reason - // check the OpenOffice documentation of the FONT record - $fontIndex = $this->_GetInt2d($recordData, 0) - 1; - } - $objStyle->setFont($this->_objFonts[$fontIndex]); - - // offset: 2; size: 2; Index to FORMAT record - $numberFormatIndex = $this->_GetInt2d($recordData, 2); - if (isset($this->_formats[$numberFormatIndex])) { - // then we have user-defined format code - $numberformat = array('code' => $this->_formats[$numberFormatIndex]); - } elseif (($code = PHPExcel_Style_NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') { - // then we have built-in format code - $numberformat = array('code' => $code); - } else { - // we set the general format code - $numberformat = array('code' => 'General'); - } - $objStyle->getNumberFormat()->setFormatCode($numberformat['code']); - - // offset: 4; size: 2; XF type, cell protection, and parent style XF - // bit 2-0; mask 0x0007; XF_TYPE_PROT - $xfTypeProt = $this->_GetInt2d($recordData, 4); - // bit 0; mask 0x01; 1 = cell is locked - $isLocked = (0x01 & $xfTypeProt) >> 0; - $objStyle->getProtection()->setLocked($isLocked ? - PHPExcel_Style_Protection::PROTECTION_INHERIT : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED); - - // bit 1; mask 0x02; 1 = Formula is hidden - $isHidden = (0x02 & $xfTypeProt) >> 1; - $objStyle->getProtection()->setHidden($isHidden ? - PHPExcel_Style_Protection::PROTECTION_PROTECTED : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED); - - // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF - $isCellStyleXf = (0x04 & $xfTypeProt) >> 2; - - // offset: 6; size: 1; Alignment and text break - // bit 2-0, mask 0x07; horizontal alignment - $horAlign = (0x07 & ord($recordData{6})) >> 0; - switch ($horAlign) { - case 0: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_GENERAL); - break; - case 1: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT); - break; - case 2: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER); - break; - case 3: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT); - break; - case 5: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY); - break; - case 6: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS); - break; - } - // bit 3, mask 0x08; wrap text - $wrapText = (0x08 & ord($recordData{6})) >> 3; - switch ($wrapText) { - case 0: - $objStyle->getAlignment()->setWrapText(false); - break; - case 1: - $objStyle->getAlignment()->setWrapText(true); - break; - } - // bit 6-4, mask 0x70; vertical alignment - $vertAlign = (0x70 & ord($recordData{6})) >> 4; - switch ($vertAlign) { - case 0: - $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP); - break; - case 1: - $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER); - break; - case 2: - $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_BOTTOM); - break; - case 3: - $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_JUSTIFY); - break; - } - - if ($this->_version == self::XLS_BIFF8) { - // offset: 7; size: 1; XF_ROTATION: Text rotation angle - $angle = ord($recordData{7}); - $rotation = 0; - if ($angle <= 90) { - $rotation = $angle; - } else if ($angle <= 180) { - $rotation = 90 - $angle; - } else if ($angle == 255) { - $rotation = -165; - } - $objStyle->getAlignment()->setTextRotation($rotation); - - // offset: 8; size: 1; Indentation, shrink to cell size, and text direction - // bit: 3-0; mask: 0x0F; indent level - $indent = (0x0F & ord($recordData{8})) >> 0; - $objStyle->getAlignment()->setIndent($indent); - - // bit: 4; mask: 0x10; 1 = shrink content to fit into cell - $shrinkToFit = (0x10 & ord($recordData{8})) >> 4; - switch ($shrinkToFit) { - case 0: - $objStyle->getAlignment()->setShrinkToFit(false); - break; - case 1: - $objStyle->getAlignment()->setShrinkToFit(true); - break; - } - - // offset: 9; size: 1; Flags used for attribute groups - - // offset: 10; size: 4; Cell border lines and background area - // bit: 3-0; mask: 0x0000000F; left style - if ($bordersLeftStyle = $this->_mapBorderStyle((0x0000000F & $this->_GetInt4d($recordData, 10)) >> 0)) { - $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle); - } - // bit: 7-4; mask: 0x000000F0; right style - if ($bordersRightStyle = $this->_mapBorderStyle((0x000000F0 & $this->_GetInt4d($recordData, 10)) >> 4)) { - $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle); - } - // bit: 11-8; mask: 0x00000F00; top style - if ($bordersTopStyle = $this->_mapBorderStyle((0x00000F00 & $this->_GetInt4d($recordData, 10)) >> 8)) { - $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle); - } - // bit: 15-12; mask: 0x0000F000; bottom style - if ($bordersBottomStyle = $this->_mapBorderStyle((0x0000F000 & $this->_GetInt4d($recordData, 10)) >> 12)) { - $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle); - } - // bit: 22-16; mask: 0x007F0000; left color - $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $this->_GetInt4d($recordData, 10)) >> 16; - - // bit: 29-23; mask: 0x3F800000; right color - $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $this->_GetInt4d($recordData, 10)) >> 23; - - // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom - $diagonalDown = (0x40000000 & $this->_GetInt4d($recordData, 10)) >> 30 ? - true : false; - - // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right - $diagonalUp = (0x80000000 & $this->_GetInt4d($recordData, 10)) >> 31 ? - true : false; - - if ($diagonalUp == false && $diagonalDown == false) { - $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_NONE); - } elseif ($diagonalUp == true && $diagonalDown == false) { - $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_UP); - } elseif ($diagonalUp == false && $diagonalDown == true) { - $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_DOWN); - } elseif ($diagonalUp == true && $diagonalDown == true) { - $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_BOTH); - } - - // offset: 14; size: 4; - // bit: 6-0; mask: 0x0000007F; top color - $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & $this->_GetInt4d($recordData, 14)) >> 0; - - // bit: 13-7; mask: 0x00003F80; bottom color - $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & $this->_GetInt4d($recordData, 14)) >> 7; - - // bit: 20-14; mask: 0x001FC000; diagonal color - $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & $this->_GetInt4d($recordData, 14)) >> 14; - - // bit: 24-21; mask: 0x01E00000; diagonal style - if ($bordersDiagonalStyle = $this->_mapBorderStyle((0x01E00000 & $this->_GetInt4d($recordData, 14)) >> 21)) { - $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle); - } - - // bit: 31-26; mask: 0xFC000000 fill pattern - if ($fillType = $this->_mapFillPattern((0xFC000000 & $this->_GetInt4d($recordData, 14)) >> 26)) { - $objStyle->getFill()->setFillType($fillType); - } - // offset: 18; size: 2; pattern and background colour - // bit: 6-0; mask: 0x007F; color index for pattern color - $objStyle->getFill()->startcolorIndex = (0x007F & $this->_GetInt2d($recordData, 18)) >> 0; - - // bit: 13-7; mask: 0x3F80; color index for pattern background - $objStyle->getFill()->endcolorIndex = (0x3F80 & $this->_GetInt2d($recordData, 18)) >> 7; - } else { - // BIFF5 - - // offset: 7; size: 1; Text orientation and flags - $orientationAndFlags = ord($recordData{7}); - - // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation - $xfOrientation = (0x03 & $orientationAndFlags) >> 0; - switch ($xfOrientation) { - case 0: - $objStyle->getAlignment()->setTextRotation(0); - break; - case 1: - $objStyle->getAlignment()->setTextRotation(-165); - break; - case 2: - $objStyle->getAlignment()->setTextRotation(90); - break; - case 3: - $objStyle->getAlignment()->setTextRotation(-90); - break; - } - - // offset: 8; size: 4; cell border lines and background area - $borderAndBackground = $this->_GetInt4d($recordData, 8); - - // bit: 6-0; mask: 0x0000007F; color index for pattern color - $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0; - - // bit: 13-7; mask: 0x00003F80; color index for pattern background - $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7; - - // bit: 21-16; mask: 0x003F0000; fill pattern - $objStyle->getFill()->setFillType($this->_mapFillPattern((0x003F0000 & $borderAndBackground) >> 16)); - - // bit: 24-22; mask: 0x01C00000; bottom line style - $objStyle->getBorders()->getBottom()->setBorderStyle($this->_mapBorderStyle((0x01C00000 & $borderAndBackground) >> 22)); - - // bit: 31-25; mask: 0xFE000000; bottom line color - $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25; - - // offset: 12; size: 4; cell border lines - $borderLines = $this->_GetInt4d($recordData, 12); - - // bit: 2-0; mask: 0x00000007; top line style - $objStyle->getBorders()->getTop()->setBorderStyle($this->_mapBorderStyle((0x00000007 & $borderLines) >> 0)); - - // bit: 5-3; mask: 0x00000038; left line style - $objStyle->getBorders()->getLeft()->setBorderStyle($this->_mapBorderStyle((0x00000038 & $borderLines) >> 3)); - - // bit: 8-6; mask: 0x000001C0; right line style - $objStyle->getBorders()->getRight()->setBorderStyle($this->_mapBorderStyle((0x000001C0 & $borderLines) >> 6)); - - // bit: 15-9; mask: 0x0000FE00; top line color index - $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9; - - // bit: 22-16; mask: 0x007F0000; left line color index - $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16; - - // bit: 29-23; mask: 0x3F800000; right line color index - $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23; - } - - // add cellStyleXf or cellXf and update mapping - if ($isCellStyleXf) { - // we only read one style XF record which is always the first - if ($this->_xfIndex == 0) { - $this->_phpExcel->addCellStyleXf($objStyle); - $this->_mapCellStyleXfIndex[$this->_xfIndex] = 0; - } - } else { - // we read all cell XF records - $this->_phpExcel->addCellXf($objStyle); - $this->_mapCellXfIndex[$this->_xfIndex] = count($this->_phpExcel->getCellXfCollection()) - 1; - } - - // update XF index for when we read next record - ++$this->_xfIndex; - } - } - - /** - * - */ - private function _readXfExt() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; 0x087D = repeated header - - // offset: 2; size: 2 - - // offset: 4; size: 8; not used - - // offset: 12; size: 2; record version - - // offset: 14; size: 2; index to XF record which this record modifies - $ixfe = $this->_GetInt2d($recordData, 14); - - // offset: 16; size: 2; not used - - // offset: 18; size: 2; number of extension properties that follow - $cexts = $this->_GetInt2d($recordData, 18); - - // start reading the actual extension data - $offset = 20; - while ($offset < $length) { - // extension type - $extType = $this->_GetInt2d($recordData, $offset); - - // extension length - $cb = $this->_GetInt2d($recordData, $offset + 2); - - // extension data - $extData = substr($recordData, $offset + 4, $cb); - - switch ($extType) { - case 4: // fill start color - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill(); - $fill->getStartColor()->setRGB($rgb); - unset($fill->startcolorIndex); // normal color index does not apply, discard - } - } - break; - - case 5: // fill end color - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill(); - $fill->getEndColor()->setRGB($rgb); - unset($fill->endcolorIndex); // normal color index does not apply, discard - } - } - break; - - case 7: // border color top - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $top = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getTop(); - $top->getColor()->setRGB($rgb); - unset($top->colorIndex); // normal color index does not apply, discard - } - } - break; - - case 8: // border color bottom - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $bottom = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getBottom(); - $bottom->getColor()->setRGB($rgb); - unset($bottom->colorIndex); // normal color index does not apply, discard - } - } - break; - - case 9: // border color left - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $left = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getLeft(); - $left->getColor()->setRGB($rgb); - unset($left->colorIndex); // normal color index does not apply, discard - } - } - break; - - case 10: // border color right - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $right = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getRight(); - $right->getColor()->setRGB($rgb); - unset($right->colorIndex); // normal color index does not apply, discard - } - } - break; - - case 11: // border color diagonal - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $diagonal = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getDiagonal(); - $diagonal->getColor()->setRGB($rgb); - unset($diagonal->colorIndex); // normal color index does not apply, discard - } - } - break; - - case 13: // font color - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $font = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFont(); - $font->getColor()->setRGB($rgb); - unset($font->colorIndex); // normal color index does not apply, discard - } - } - break; - } - - $offset += $cb; - } - } - - } - - /** - * Read STYLE record - */ - private function _readStyle() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; index to XF record and flag for built-in style - $ixfe = $this->_GetInt2d($recordData, 0); - - // bit: 11-0; mask 0x0FFF; index to XF record - $xfIndex = (0x0FFF & $ixfe) >> 0; - - // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style - $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15); - - if ($isBuiltIn) { - // offset: 2; size: 1; identifier for built-in style - $builtInId = ord($recordData{2}); - - switch ($builtInId) { - case 0x00: - // currently, we are not using this for anything - break; - - default: - break; - } - - } else { - // user-defined; not supported by PHPExcel - } - } - } - - /** - * Read PALETTE record - */ - private function _readPalette() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; number of following colors - $nm = $this->_GetInt2d($recordData, 0); - - // list of RGB colors - for ($i = 0; $i < $nm; ++$i) { - $rgb = substr($recordData, 2 + 4 * $i, 4); - $this->_palette[] = $this->_readRGB($rgb); - } - } - } - - /** - * SHEET - * - * This record is located in the Workbook Globals - * Substream and represents a sheet inside the workbook. - * One SHEET record is written for each sheet. It stores the - * sheet name and a stream offset to the BOF record of the - * respective Sheet Substream within the Workbook Stream. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readSheet() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 4; absolute stream position of the BOF record of the sheet - $rec_offset = $this->_GetInt4d($recordData, 0); - - // offset: 4; size: 1; sheet state - switch (ord($recordData{4})) { - case 0x00: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break; - case 0x01: $sheetState = PHPExcel_Worksheet::SHEETSTATE_HIDDEN; break; - case 0x02: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VERYHIDDEN; break; - default: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break; - } - - // offset: 5; size: 1; sheet type - $sheetType = ord($recordData{5}); - - // offset: 6; size: var; sheet name - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringShort(substr($recordData, 6)); - $rec_name = $string['value']; - } elseif ($this->_version == self::XLS_BIFF7) { - $string = $this->_readByteStringShort(substr($recordData, 6)); - $rec_name = $string['value']; - } - - $this->_sheets[] = array( - 'name' => $rec_name, - 'offset' => $rec_offset, - 'sheetState' => $sheetState, - 'sheetType' => $sheetType, - ); - } - - /** - * Read EXTERNALBOOK record - */ - private function _readExternalBook() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset within record data - $offset = 0; - - // there are 4 types of records - if (strlen($recordData) > 4) { - // external reference - // offset: 0; size: 2; number of sheet names ($nm) - $nm = $this->_GetInt2d($recordData, 0); - $offset += 2; - - // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length) - $encodedUrlString = $this->_readUnicodeStringLong(substr($recordData, 2)); - $offset += $encodedUrlString['size']; - - // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length) - $externalSheetNames = array(); - for ($i = 0; $i < $nm; ++$i) { - $externalSheetNameString = $this->_readUnicodeStringLong(substr($recordData, $offset)); - $externalSheetNames[] = $externalSheetNameString['value']; - $offset += $externalSheetNameString['size']; - } - - // store the record data - $this->_externalBooks[] = array( - 'type' => 'external', - 'encodedUrl' => $encodedUrlString['value'], - 'externalSheetNames' => $externalSheetNames, - ); - - } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) { - // internal reference - // offset: 0; size: 2; number of sheet in this document - // offset: 2; size: 2; 0x01 0x04 - $this->_externalBooks[] = array( - 'type' => 'internal', - ); - } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) { - // add-in function - // offset: 0; size: 2; 0x0001 - $this->_externalBooks[] = array( - 'type' => 'addInFunction', - ); - } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) { - // DDE links, OLE links - // offset: 0; size: 2; 0x0000 - // offset: 2; size: var; encoded source document name - $this->_externalBooks[] = array( - 'type' => 'DDEorOLE', - ); - } - } - - /** - * Read EXTERNNAME record. - */ - private function _readExternName() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // external sheet references provided for named cells - if ($this->_version == self::XLS_BIFF8) { - // offset: 0; size: 2; options - $options = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; - - // offset: 4; size: 2; not used - - // offset: 6; size: var - $nameString = $this->_readUnicodeStringShort(substr($recordData, 6)); - - // offset: var; size: var; formula data - $offset = 6 + $nameString['size']; - $formula = $this->_getFormulaFromStructure(substr($recordData, $offset)); - - $this->_externalNames[] = array( - 'name' => $nameString['value'], - 'formula' => $formula, - ); - } - } - - /** - * Read EXTERNSHEET record - */ - private function _readExternSheet() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // external sheet references provided for named cells - if ($this->_version == self::XLS_BIFF8) { - // offset: 0; size: 2; number of following ref structures - $nm = $this->_GetInt2d($recordData, 0); - for ($i = 0; $i < $nm; ++$i) { - $this->_ref[] = array( - // offset: 2 + 6 * $i; index to EXTERNALBOOK record - 'externalBookIndex' => $this->_GetInt2d($recordData, 2 + 6 * $i), - // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record - 'firstSheetIndex' => $this->_GetInt2d($recordData, 4 + 6 * $i), - // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record - 'lastSheetIndex' => $this->_GetInt2d($recordData, 6 + 6 * $i), - ); - } - } - } - - /** - * DEFINEDNAME - * - * This record is part of a Link Table. It contains the name - * and the token array of an internal defined name. Token - * arrays of defined names contain tokens with aberrant - * token classes. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readDefinedName() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8) { - // retrieves named cells - - // offset: 0; size: 2; option flags - $opts = $this->_GetInt2d($recordData, 0); - - // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name - $isBuiltInName = (0x0020 & $opts) >> 5; - - // offset: 2; size: 1; keyboard shortcut - - // offset: 3; size: 1; length of the name (character count) - $nlen = ord($recordData{3}); - - // offset: 4; size: 2; size of the formula data (it can happen that this is zero) - // note: there can also be additional data, this is not included in $flen - $flen = $this->_GetInt2d($recordData, 4); - - // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based) - $scope = $this->_GetInt2d($recordData, 8); - - // offset: 14; size: var; Name (Unicode string without length field) - $string = $this->_readUnicodeString(substr($recordData, 14), $nlen); - - // offset: var; size: $flen; formula data - $offset = 14 + $string['size']; - $formulaStructure = pack('v', $flen) . substr($recordData, $offset); - - try { - $formula = $this->_getFormulaFromStructure($formulaStructure); - } catch (Exception $e) { - $formula = ''; - } - - $this->_definedname[] = array( - 'isBuiltInName' => $isBuiltInName, - 'name' => $string['value'], - 'formula' => $formula, - 'scope' => $scope, - ); - } - } - - /** - * Read MSODRAWINGGROUP record - */ - private function _readMsoDrawingGroup() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - - // get spliced record data - $splicedRecordData = $this->_getSplicedRecordData(); - $recordData = $splicedRecordData['recordData']; - - $this->_drawingGroupData .= $recordData; - } - - /** - * SST - Shared String Table - * - * This record contains a list of all strings used anywhere - * in the workbook. Each string occurs only once. The - * workbook uses indexes into the list to reference the - * strings. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - **/ - private function _readSst() - { - // offset within (spliced) record data - $pos = 0; - - // get spliced record data - $splicedRecordData = $this->_getSplicedRecordData(); - - $recordData = $splicedRecordData['recordData']; - $spliceOffsets = $splicedRecordData['spliceOffsets']; - - // offset: 0; size: 4; total number of strings in the workbook - $pos += 4; - - // offset: 4; size: 4; number of following strings ($nm) - $nm = $this->_GetInt4d($recordData, 4); - $pos += 4; - - // loop through the Unicode strings (16-bit length) - for ($i = 0; $i < $nm; ++$i) { - - // number of characters in the Unicode string - $numChars = $this->_GetInt2d($recordData, $pos); - $pos += 2; - - // option flags - $optionFlags = ord($recordData{$pos}); - ++$pos; - - // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed - $isCompressed = (($optionFlags & 0x01) == 0) ; - - // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic - $hasAsian = (($optionFlags & 0x04) != 0); - - // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text - $hasRichText = (($optionFlags & 0x08) != 0); - - if ($hasRichText) { - // number of Rich-Text formatting runs - $formattingRuns = $this->_GetInt2d($recordData, $pos); - $pos += 2; - } - - if ($hasAsian) { - // size of Asian phonetic setting - $extendedRunLength = $this->_GetInt4d($recordData, $pos); - $pos += 4; - } - - // expected byte length of character array if not split - $len = ($isCompressed) ? $numChars : $numChars * 2; - - // look up limit position - foreach ($spliceOffsets as $spliceOffset) { - // it can happen that the string is empty, therefore we need - // <= and not just < - if ($pos <= $spliceOffset) { - $limitpos = $spliceOffset; - break; - } - } - - if ($pos + $len <= $limitpos) { - // character array is not split between records - - $retstr = substr($recordData, $pos, $len); - $pos += $len; - - } else { - // character array is split between records - - // first part of character array - $retstr = substr($recordData, $pos, $limitpos - $pos); - - $bytesRead = $limitpos - $pos; - - // remaining characters in Unicode string - $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2)); - - $pos = $limitpos; - - // keep reading the characters - while ($charsLeft > 0) { - - // look up next limit position, in case the string span more than one continue record - foreach ($spliceOffsets as $spliceOffset) { - if ($pos < $spliceOffset) { - $limitpos = $spliceOffset; - break; - } - } - - // repeated option flags - // OpenOffice.org documentation 5.21 - $option = ord($recordData{$pos}); - ++$pos; - - if ($isCompressed && ($option == 0)) { - // 1st fragment compressed - // this fragment compressed - $len = min($charsLeft, $limitpos - $pos); - $retstr .= substr($recordData, $pos, $len); - $charsLeft -= $len; - $isCompressed = true; - - } elseif (!$isCompressed && ($option != 0)) { - // 1st fragment uncompressed - // this fragment uncompressed - $len = min($charsLeft * 2, $limitpos - $pos); - $retstr .= substr($recordData, $pos, $len); - $charsLeft -= $len / 2; - $isCompressed = false; - - } elseif (!$isCompressed && ($option == 0)) { - // 1st fragment uncompressed - // this fragment compressed - $len = min($charsLeft, $limitpos - $pos); - for ($j = 0; $j < $len; ++$j) { - $retstr .= $recordData{$pos + $j} . chr(0); - } - $charsLeft -= $len; - $isCompressed = false; - - } else { - // 1st fragment compressed - // this fragment uncompressed - $newstr = ''; - for ($j = 0; $j < strlen($retstr); ++$j) { - $newstr .= $retstr[$j] . chr(0); - } - $retstr = $newstr; - $len = min($charsLeft * 2, $limitpos - $pos); - $retstr .= substr($recordData, $pos, $len); - $charsLeft -= $len / 2; - $isCompressed = false; - } - - $pos += $len; - } - } - - // convert to UTF-8 - $retstr = $this->_encodeUTF16($retstr, $isCompressed); - - // read additional Rich-Text information, if any - $fmtRuns = array(); - if ($hasRichText) { - // list of formatting runs - for ($j = 0; $j < $formattingRuns; ++$j) { - // first formatted character; zero-based - $charPos = $this->_GetInt2d($recordData, $pos + $j * 4); - - // index to font record - $fontIndex = $this->_GetInt2d($recordData, $pos + 2 + $j * 4); - - $fmtRuns[] = array( - 'charPos' => $charPos, - 'fontIndex' => $fontIndex, - ); - } - $pos += 4 * $formattingRuns; - } - - // read additional Asian phonetics information, if any - if ($hasAsian) { - // For Asian phonetic settings, we skip the extended string data - $pos += $extendedRunLength; - } - - // store the shared sting - $this->_sst[] = array( - 'value' => $retstr, - 'fmtRuns' => $fmtRuns, - ); - } - - // _getSplicedRecordData() takes care of moving current position in data stream - } - - /** - * Read PRINTGRIDLINES record - */ - private function _readPrintGridlines() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { - // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines - $printGridlines = (bool) $this->_GetInt2d($recordData, 0); - $this->_phpSheet->setPrintGridlines($printGridlines); - } - } - - /** - * Read DEFAULTROWHEIGHT record - */ - private function _readDefaultRowHeight() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; option flags - // offset: 2; size: 2; default height for unused rows, (twips 1/20 point) - $height = $this->_GetInt2d($recordData, 2); - $this->_phpSheet->getDefaultRowDimension()->setRowHeight($height / 20); - } - - /** - * Read SHEETPR record - */ - private function _readSheetPr() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2 - - // bit: 6; mask: 0x0040; 0 = outline buttons above outline group - $isSummaryBelow = (0x0040 & $this->_GetInt2d($recordData, 0)) >> 6; - $this->_phpSheet->setShowSummaryBelow($isSummaryBelow); - - // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group - $isSummaryRight = (0x0080 & $this->_GetInt2d($recordData, 0)) >> 7; - $this->_phpSheet->setShowSummaryRight($isSummaryRight); - - // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages - // this corresponds to radio button setting in page setup dialog in Excel - $this->_isFitToPages = (bool) ((0x0100 & $this->_GetInt2d($recordData, 0)) >> 8); - } - - /** - * Read HORIZONTALPAGEBREAKS record - */ - private function _readHorizontalPageBreaks() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { - - // offset: 0; size: 2; number of the following row index structures - $nm = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 6 * $nm; list of $nm row index structures - for ($i = 0; $i < $nm; ++$i) { - $r = $this->_GetInt2d($recordData, 2 + 6 * $i); - $cf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2); - $cl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4); - - // not sure why two column indexes are necessary? - $this->_phpSheet->setBreakByColumnAndRow($cf, $r, PHPExcel_Worksheet::BREAK_ROW); - } - } - } - - /** - * Read VERTICALPAGEBREAKS record - */ - private function _readVerticalPageBreaks() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { - // offset: 0; size: 2; number of the following column index structures - $nm = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 6 * $nm; list of $nm row index structures - for ($i = 0; $i < $nm; ++$i) { - $c = $this->_GetInt2d($recordData, 2 + 6 * $i); - $rf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2); - $rl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4); - - // not sure why two row indexes are necessary? - $this->_phpSheet->setBreakByColumnAndRow($c, $rf, PHPExcel_Worksheet::BREAK_COLUMN); - } - } - } - - /** - * Read HEADER record - */ - private function _readHeader() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: var - // realized that $recordData can be empty even when record exists - if ($recordData) { - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringLong($recordData); - } else { - $string = $this->_readByteStringShort($recordData); - } - - $this->_phpSheet->getHeaderFooter()->setOddHeader($string['value']); - $this->_phpSheet->getHeaderFooter()->setEvenHeader($string['value']); - } - } - } - - /** - * Read FOOTER record - */ - private function _readFooter() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: var - // realized that $recordData can be empty even when record exists - if ($recordData) { - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringLong($recordData); - } else { - $string = $this->_readByteStringShort($recordData); - } - $this->_phpSheet->getHeaderFooter()->setOddFooter($string['value']); - $this->_phpSheet->getHeaderFooter()->setEvenFooter($string['value']); - } - } - } - - /** - * Read HCENTER record - */ - private function _readHcenter() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally - $isHorizontalCentered = (bool) $this->_GetInt2d($recordData, 0); - - $this->_phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered); - } - } - - /** - * Read VCENTER record - */ - private function _readVcenter() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered - $isVerticalCentered = (bool) $this->_GetInt2d($recordData, 0); - - $this->_phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered); - } - } - - /** - * Read LEFTMARGIN record - */ - private function _readLeftMargin() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 8 - $this->_phpSheet->getPageMargins()->setLeft($this->_extractNumber($recordData)); - } - } - - /** - * Read RIGHTMARGIN record - */ - private function _readRightMargin() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 8 - $this->_phpSheet->getPageMargins()->setRight($this->_extractNumber($recordData)); - } - } - - /** - * Read TOPMARGIN record - */ - private function _readTopMargin() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 8 - $this->_phpSheet->getPageMargins()->setTop($this->_extractNumber($recordData)); - } - } - - /** - * Read BOTTOMMARGIN record - */ - private function _readBottomMargin() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 8 - $this->_phpSheet->getPageMargins()->setBottom($this->_extractNumber($recordData)); - } - } - - /** - * Read PAGESETUP record - */ - private function _readPageSetup() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; paper size - $paperSize = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; scaling factor - $scale = $this->_GetInt2d($recordData, 2); - - // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed - $fitToWidth = $this->_GetInt2d($recordData, 6); - - // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed - $fitToHeight = $this->_GetInt2d($recordData, 8); - - // offset: 10; size: 2; option flags - - // bit: 1; mask: 0x0002; 0=landscape, 1=portrait - $isPortrait = (0x0002 & $this->_GetInt2d($recordData, 10)) >> 1; - - // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init - // when this bit is set, do not use flags for those properties - $isNotInit = (0x0004 & $this->_GetInt2d($recordData, 10)) >> 2; - - if (!$isNotInit) { - $this->_phpSheet->getPageSetup()->setPaperSize($paperSize); - switch ($isPortrait) { - case 0: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE); break; - case 1: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_PORTRAIT); break; - } - - $this->_phpSheet->getPageSetup()->setScale($scale, false); - $this->_phpSheet->getPageSetup()->setFitToPage((bool) $this->_isFitToPages); - $this->_phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false); - $this->_phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false); - } - - // offset: 16; size: 8; header margin (IEEE 754 floating-point value) - $marginHeader = $this->_extractNumber(substr($recordData, 16, 8)); - $this->_phpSheet->getPageMargins()->setHeader($marginHeader); - - // offset: 24; size: 8; footer margin (IEEE 754 floating-point value) - $marginFooter = $this->_extractNumber(substr($recordData, 24, 8)); - $this->_phpSheet->getPageMargins()->setFooter($marginFooter); - } - } - - /** - * PROTECT - Sheet protection (BIFF2 through BIFF8) - * if this record is omitted, then it also means no sheet protection - */ - private function _readProtect() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly) { - return; - } - - // offset: 0; size: 2; - - // bit 0, mask 0x01; 1 = sheet is protected - $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; - $this->_phpSheet->getProtection()->setSheet((bool)$bool); - } - - /** - * SCENPROTECT - */ - private function _readScenProtect() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly) { - return; - } - - // offset: 0; size: 2; - - // bit: 0, mask 0x01; 1 = scenarios are protected - $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; - - $this->_phpSheet->getProtection()->setScenarios((bool)$bool); - } - - /** - * OBJECTPROTECT - */ - private function _readObjectProtect() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly) { - return; - } - - // offset: 0; size: 2; - - // bit: 0, mask 0x01; 1 = objects are protected - $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; - - $this->_phpSheet->getProtection()->setObjects((bool)$bool); - } - - /** - * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8) - */ - private function _readPassword() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; 16-bit hash value of password - $password = strtoupper(dechex($this->_GetInt2d($recordData, 0))); // the hashed password - $this->_phpSheet->getProtection()->setPassword($password, true); - } - } - - /** - * Read DEFCOLWIDTH record - */ - private function _readDefColWidth() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; default column width - $width = $this->_GetInt2d($recordData, 0); - if ($width != 8) { - $this->_phpSheet->getDefaultColumnDimension()->setWidth($width); - } - } - - /** - * Read COLINFO record - */ - private function _readColInfo() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; index to first column in range - $fc = $this->_GetInt2d($recordData, 0); // first column index - - // offset: 2; size: 2; index to last column in range - $lc = $this->_GetInt2d($recordData, 2); // first column index - - // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character - $width = $this->_GetInt2d($recordData, 4); - - // offset: 6; size: 2; index to XF record for default column formatting - $xfIndex = $this->_GetInt2d($recordData, 6); - - // offset: 8; size: 2; option flags - - // bit: 0; mask: 0x0001; 1= columns are hidden - $isHidden = (0x0001 & $this->_GetInt2d($recordData, 8)) >> 0; - - // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline) - $level = (0x0700 & $this->_GetInt2d($recordData, 8)) >> 8; - - // bit: 12; mask: 0x1000; 1 = collapsed - $isCollapsed = (0x1000 & $this->_GetInt2d($recordData, 8)) >> 12; - - // offset: 10; size: 2; not used - - for ($i = $fc; $i <= $lc; ++$i) { - if ($lc == 255 || $lc == 256) { - $this->_phpSheet->getDefaultColumnDimension()->setWidth($width / 256); - break; - } - $this->_phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256); - $this->_phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden); - $this->_phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level); - $this->_phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed); - $this->_phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - /** - * ROW - * - * This record contains the properties of a single row in a - * sheet. Rows and cells in a sheet are divided into blocks - * of 32 rows. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readRow() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; index of this row - $r = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to column of the first cell which is described by a cell record - - // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1 - - // offset: 6; size: 2; - - // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point - $height = (0x7FFF & $this->_GetInt2d($recordData, 6)) >> 0; - - // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height - $useDefaultHeight = (0x8000 & $this->_GetInt2d($recordData, 6)) >> 15; - - if (!$useDefaultHeight) { - $this->_phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20); - } - - // offset: 8; size: 2; not used - - // offset: 10; size: 2; not used in BIFF5-BIFF8 - - // offset: 12; size: 4; option flags and default row formatting - - // bit: 2-0: mask: 0x00000007; outline level of the row - $level = (0x00000007 & $this->_GetInt4d($recordData, 12)) >> 0; - $this->_phpSheet->getRowDimension($r + 1)->setOutlineLevel($level); - - // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed - $isCollapsed = (0x00000010 & $this->_GetInt4d($recordData, 12)) >> 4; - $this->_phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed); - - // bit: 5; mask: 0x00000020; 1 = row is hidden - $isHidden = (0x00000020 & $this->_GetInt4d($recordData, 12)) >> 5; - $this->_phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden); - - // bit: 7; mask: 0x00000080; 1 = row has explicit format - $hasExplicitFormat = (0x00000080 & $this->_GetInt4d($recordData, 12)) >> 7; - - // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record - $xfIndex = (0x0FFF0000 & $this->_GetInt4d($recordData, 12)) >> 16; - - if ($hasExplicitFormat) { - $this->_phpSheet->getRowDimension($r + 1)->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - /** - * Read RK record - * This record represents a cell that contains an RK value - * (encoded integer or floating-point value). If a - * floating-point value cannot be encoded to an RK value, - * a NUMBER record will be written. This record replaces the - * record INTEGER written in BIFF2. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readRk() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to column - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset: 4; size: 2; index to XF record - $xfIndex = $this->_GetInt2d($recordData, 4); - - // offset: 6; size: 4; RK value - $rknum = $this->_GetInt4d($recordData, 6); - $numValue = $this->_GetIEEE754($rknum); - - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - if (!$this->_readDataOnly) { - // add style information - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - - // add cell - $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); - } - } - - /** - * Read LABELSST record - * This record represents a cell that contains a string. It - * replaces the LABEL record and RSTRING record used in - * BIFF2-BIFF5. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readLabelSst() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to column - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset: 4; size: 2; index to XF record - $xfIndex = $this->_GetInt2d($recordData, 4); - - // offset: 6; size: 4; index to SST record - $index = $this->_GetInt4d($recordData, 6); - - // add cell - if (($fmtRuns = $this->_sst[$index]['fmtRuns']) && !$this->_readDataOnly) { - // then we should treat as rich text - $richText = new PHPExcel_RichText(); - $charPos = 0; - for ($i = 0; $i <= count($this->_sst[$index]['fmtRuns']); ++$i) { - if (isset($fmtRuns[$i])) { - $text = PHPExcel_Shared_String::Substring($this->_sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos); - $charPos = $fmtRuns[$i]['charPos']; - } else { - $text = PHPExcel_Shared_String::Substring($this->_sst[$index]['value'], $charPos, PHPExcel_Shared_String::CountCharacters($this->_sst[$index]['value'])); - } - - if (PHPExcel_Shared_String::CountCharacters($text) > 0) { - if ($i == 0) { // first text run, no style - $richText->createText($text); - } else { - $textRun = $richText->createTextRun($text); - if (isset($fmtRuns[$i - 1])) { - if ($fmtRuns[$i - 1]['fontIndex'] < 4) { - $fontIndex = $fmtRuns[$i - 1]['fontIndex']; - } else { - // this has to do with that index 4 is omitted in all BIFF versions for some strange reason - // check the OpenOffice documentation of the FONT record - $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1; - } - $textRun->setFont(clone $this->_objFonts[$fontIndex]); - } - } - } - } - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - $cell->setValueExplicit($richText, PHPExcel_Cell_DataType::TYPE_STRING); - } else { - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - $cell->setValueExplicit($this->_sst[$index]['value'], PHPExcel_Cell_DataType::TYPE_STRING); - } - - if (!$this->_readDataOnly) { - // add style information - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - /** - * Read MULRK record - * This record represents a cell range containing RK value - * cells. All cells are located in the same row. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readMulRk() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to first column - $colFirst = $this->_GetInt2d($recordData, 2); - - // offset: var; size: 2; index to last column - $colLast = $this->_GetInt2d($recordData, $length - 2); - $columns = $colLast - $colFirst + 1; - - // offset within record data - $offset = 4; - - for ($i = 0; $i < $columns; ++$i) { - $columnString = PHPExcel_Cell::stringFromColumnIndex($colFirst + $i); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - - // offset: var; size: 2; index to XF record - $xfIndex = $this->_GetInt2d($recordData, $offset); - - // offset: var; size: 4; RK value - $numValue = $this->_GetIEEE754($this->_GetInt4d($recordData, $offset + 2)); - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - if (!$this->_readDataOnly) { - // add style - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - - // add cell value - $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); - } - - $offset += 6; - } - } - - /** - * Read NUMBER record - * This record represents a cell that contains a - * floating-point value. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readNumber() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size 2; index to column - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset 4; size: 2; index to XF record - $xfIndex = $this->_GetInt2d($recordData, 4); - - $numValue = $this->_extractNumber(substr($recordData, 6, 8)); - - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - if (!$this->_readDataOnly) { - // add cell style - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - - // add cell value - $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); - } - } - - /** - * Read FORMULA record + perhaps a following STRING record if formula result is a string - * This record contains the token array and the result of a - * formula cell. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readFormula() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; row index - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; col index - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // offset: 20: size: variable; formula structure - $formulaStructure = substr($recordData, 20); - - // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc. - $options = $this->_GetInt2d($recordData, 14); - - // bit: 0; mask: 0x0001; 1 = recalculate always - // bit: 1; mask: 0x0002; 1 = calculate on open - // bit: 2; mask: 0x0008; 1 = part of a shared formula - $isPartOfSharedFormula = (bool) (0x0008 & $options); - - // WARNING: - // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true - // the formula data may be ordinary formula data, therefore we need to check - // explicitly for the tExp token (0x01) - $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure{2}) == 0x01; - - if ($isPartOfSharedFormula) { - // part of shared formula which means there will be a formula with a tExp token and nothing else - // get the base cell, grab tExp token - $baseRow = $this->_GetInt2d($formulaStructure, 3); - $baseCol = $this->_GetInt2d($formulaStructure, 5); - $this->_baseCell = PHPExcel_Cell::stringFromColumnIndex($baseCol). ($baseRow + 1); - } - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - - if ($isPartOfSharedFormula) { - // formula is added to this cell after the sheet has been read - $this->_sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell; - } - - // offset: 16: size: 4; not used - - // offset: 4; size: 2; XF index - $xfIndex = $this->_GetInt2d($recordData, 4); - - // offset: 6; size: 8; result of the formula - if ( (ord($recordData{6}) == 0) - && (ord($recordData{12}) == 255) - && (ord($recordData{13}) == 255) ) { - - // String formula. Result follows in appended STRING record - $dataType = PHPExcel_Cell_DataType::TYPE_STRING; - - // read possible SHAREDFMLA record - $code = $this->_GetInt2d($this->_data, $this->_pos); - if ($code == self::XLS_Type_SHAREDFMLA) { - $this->_readSharedFmla(); - } - - // read STRING record - $value = $this->_readString(); - - } elseif ((ord($recordData{6}) == 1) - && (ord($recordData{12}) == 255) - && (ord($recordData{13}) == 255)) { - - // Boolean formula. Result is in +2; 0=false, 1=true - $dataType = PHPExcel_Cell_DataType::TYPE_BOOL; - $value = (bool) ord($recordData{8}); - - } elseif ((ord($recordData{6}) == 2) - && (ord($recordData{12}) == 255) - && (ord($recordData{13}) == 255)) { - - // Error formula. Error code is in +2 - $dataType = PHPExcel_Cell_DataType::TYPE_ERROR; - $value = $this->_mapErrorCode(ord($recordData{8})); - - } elseif ((ord($recordData{6}) == 3) - && (ord($recordData{12}) == 255) - && (ord($recordData{13}) == 255)) { - - // Formula result is a null string - $dataType = PHPExcel_Cell_DataType::TYPE_NULL; - $value = ''; - - } else { - - // forumla result is a number, first 14 bytes like _NUMBER record - $dataType = PHPExcel_Cell_DataType::TYPE_NUMERIC; - $value = $this->_extractNumber(substr($recordData, 6, 8)); - - } - - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - if (!$this->_readDataOnly) { - // add cell style - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - - // store the formula - if (!$isPartOfSharedFormula) { - // not part of shared formula - // add cell value. If we can read formula, populate with formula, otherwise just used cached value - try { - if ($this->_version != self::XLS_BIFF8) { - throw new Exception('Not BIFF8. Can only read BIFF8 formulas'); - } - $formula = $this->_getFormulaFromStructure($formulaStructure); // get formula in human language - $cell->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA); - - } catch (Exception $e) { - $cell->setValueExplicit($value, $dataType); - } - } else { - if ($this->_version == self::XLS_BIFF8) { - // do nothing at this point, formula id added later in the code - } else { - $cell->setValueExplicit($value, $dataType); - } - } - - // store the cached calculated value - $cell->setCalculatedValue($value); - } - } - - /** - * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader, - * which usually contains relative references. - * These will be used to construct the formula in each shared formula part after the sheet is read. - */ - private function _readSharedFmla() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything - $cellRange = substr($recordData, 0, 6); - $cellRange = $this->_readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax - - // offset: 6, size: 1; not used - - // offset: 7, size: 1; number of existing FORMULA records for this shared formula - $no = ord($recordData{7}); - - // offset: 8, size: var; Binary token array of the shared formula - $formula = substr($recordData, 8); - - // at this point we only store the shared formula for later use - $this->_sharedFormulas[$this->_baseCell] = $formula; - - } - - /** - * Read a STRING record from current stream position and advance the stream pointer to next record - * This record is used for storing result from FORMULA record when it is a string, and - * it occurs directly after the FORMULA record - * - * @return string The string contents as UTF-8 - */ - private function _readString() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringLong($recordData); - $value = $string['value']; - } else { - $string = $this->_readByteStringLong($recordData); - $value = $string['value']; - } - - return $value; - } - - /** - * Read BOOLERR record - * This record represents a Boolean value or error value - * cell. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readBoolErr() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; row index - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; column index - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset: 4; size: 2; index to XF record - $xfIndex = $this->_GetInt2d($recordData, 4); - - // offset: 6; size: 1; the boolean value or error value - $boolErr = ord($recordData{6}); - - // offset: 7; size: 1; 0=boolean; 1=error - $isError = ord($recordData{7}); - - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - switch ($isError) { - case 0: // boolean - $value = (bool) $boolErr; - - // add cell value - $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_BOOL); - break; - - case 1: // error type - $value = $this->_mapErrorCode($boolErr); - - // add cell value - $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_ERROR); - break; - } - - if (!$this->_readDataOnly) { - // add cell style - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - /** - * Read MULBLANK record - * This record represents a cell range of empty cells. All - * cells are located in the same row - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readMulBlank() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to first column - $fc = $this->_GetInt2d($recordData, 2); - - // offset: 4; size: 2 x nc; list of indexes to XF records - // add style information - if (!$this->_readDataOnly) { - for ($i = 0; $i < $length / 2 - 3; ++$i) { - $columnString = PHPExcel_Cell::stringFromColumnIndex($fc + $i); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - $xfIndex = $this->_GetInt2d($recordData, 4 + 2 * $i); - $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - // offset: 6; size 2; index to last column (not needed) - } - - /** - * Read LABEL record - * This record represents a cell that contains a string. In - * BIFF8 it is usually replaced by the LABELSST record. - * Excel still uses this record, if it copies unformatted - * text cells to the clipboard. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readLabel() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to column - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset: 4; size: 2; XF index - $xfIndex = $this->_GetInt2d($recordData, 4); - - // add cell value - // todo: what if string is very long? continue record - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringLong(substr($recordData, 6)); - $value = $string['value']; - } else { - $string = $this->_readByteStringLong(substr($recordData, 6)); - $value = $string['value']; - } - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING); - - if (!$this->_readDataOnly) { - // add cell style - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - /** - * Read BLANK record - */ - private function _readBlank() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; row index - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; col index - $col = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($col); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset: 4; size: 2; XF index - $xfIndex = $this->_GetInt2d($recordData, 4); - - // add style information - if (!$this->_readDataOnly) { - $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - - } - - /** - * Read MSODRAWING record - */ - private function _readMsoDrawing() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - - // get spliced record data - $splicedRecordData = $this->_getSplicedRecordData(); - $recordData = $splicedRecordData['recordData']; - - $this->_drawingData .= $recordData; - } - - /** - * Read OBJ record - */ - private function _readObj() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly || $this->_version != self::XLS_BIFF8) { - return; - } - - // recordData consists of an array of subrecords looking like this: - // ft: 2 bytes; id number - // cb: 2 bytes; size in bytes of following data - // data: var; subrecord data - - // for now, we are just interested in the second subrecord containing the object type - $ot = $this->_GetInt2d($recordData, 4); - - $this->_objs[] = array( - 'type' => $ot, - ); - } - - /** - * Read WINDOW2 record - */ - private function _readWindow2() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; option flags - $options = $this->_GetInt2d($recordData, 0); - - // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines - $showGridlines = (bool) ((0x0002 & $options) >> 1); - $this->_phpSheet->setShowGridlines($showGridlines); - - // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers - $showRowColHeaders = (bool) ((0x0004 & $options) >> 2); - $this->_phpSheet->setShowRowColHeaders($showRowColHeaders); - - // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen - $this->_frozen = (bool) ((0x0008 & $options) >> 3); - - // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left - $this->_phpSheet->setRightToLeft((bool)((0x0040 & $options) >> 6)); - - // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active - $isActive = (bool) ((0x0400 & $options) >> 10); - if ($isActive) { - $this->_phpExcel->setActiveSheetIndex($this->_phpExcel->getIndex($this->_phpSheet)); - } - } - - /** - * Read SCL record - */ - private function _readScl() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; numerator of the view magnification - $numerator = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; numerator of the view magnification - $denumerator = $this->_GetInt2d($recordData, 2); - - // set the zoom scale (in percent) - $this->_phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator); - } - - /** - * Read PANE record - */ - private function _readPane() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; position of vertical split - $px = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; position of horizontal split - $py = $this->_GetInt2d($recordData, 2); - - if ($this->_frozen) { - // frozen panes - $this->_phpSheet->freezePane(PHPExcel_Cell::stringFromColumnIndex($px) . ($py + 1)); - } else { - // unfrozen panes; split windows; not supported by PHPExcel core - } - } - } - - /** - * Read SELECTION record. There is one such record for each pane in the sheet. - */ - private function _readSelection() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 1; pane identifier - $paneId = ord($recordData{0}); - - // offset: 1; size: 2; index to row of the active cell - $r = $this->_GetInt2d($recordData, 1); - - // offset: 3; size: 2; index to column of the active cell - $c = $this->_GetInt2d($recordData, 3); - - // offset: 5; size: 2; index into the following cell range list to the - // entry that contains the active cell - $index = $this->_GetInt2d($recordData, 5); - - // offset: 7; size: var; cell range address list containing all selected cell ranges - $data = substr($recordData, 7); - $cellRangeAddressList = $this->_readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax - - $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0]; - - // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!) - if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) { - $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells); - } - - // first row '1' + last row '65536' indicates that full column is selected - if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) { - $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells); - } - - // first column 'A' + last column 'IV' indicates that full row is selected - if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) { - $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells); - } - - $this->_phpSheet->setSelectedCells($selectedCells); - } - } - - /** - * MERGEDCELLS - * - * This record contains the addresses of merged cell ranges - * in the current sheet. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readMergedCells() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { - $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($recordData); - foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) { - $this->_phpSheet->mergeCells($cellRangeAddress); - } - } - } - - /** - * Read HYPERLINK record - */ - private function _readHyperLink() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer forward to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 8; cell range address of all cells containing this hyperlink - try { - $cellRange = $this->_readBIFF8CellRangeAddressFixed($recordData, 0, 8); - } catch (Exception $e) { - return; - } - - // offset: 8, size: 16; GUID of StdLink - - // offset: 24, size: 4; unknown value - - // offset: 28, size: 4; option flags - - // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL - $isFileLinkOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 0; - - // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL - $isAbsPathOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 1; - - // bit: 2 (and 4); mask: 0x00000014; 0 = no description - $hasDesc = (0x00000014 & $this->_GetInt2d($recordData, 28)) >> 2; - - // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text - $hasText = (0x00000008 & $this->_GetInt2d($recordData, 28)) >> 3; - - // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame - $hasFrame = (0x00000080 & $this->_GetInt2d($recordData, 28)) >> 7; - - // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name) - $isUNC = (0x00000100 & $this->_GetInt2d($recordData, 28)) >> 8; - - // offset within record data - $offset = 32; - - if ($hasDesc) { - // offset: 32; size: var; character count of description text - $dl = $this->_GetInt4d($recordData, 32); - // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated - $desc = $this->_encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false); - $offset += 4 + 2 * $dl; - } - if ($hasFrame) { - $fl = $this->_GetInt4d($recordData, $offset); - $offset += 4 + 2 * $fl; - } - - // detect type of hyperlink (there are 4 types) - $hyperlinkType = null; - - if ($isUNC) { - $hyperlinkType = 'UNC'; - } else if (!$isFileLinkOrUrl) { - $hyperlinkType = 'workbook'; - } else if (ord($recordData{$offset}) == 0x03) { - $hyperlinkType = 'local'; - } else if (ord($recordData{$offset}) == 0xE0) { - $hyperlinkType = 'URL'; - } - - switch ($hyperlinkType) { - case 'URL': - // section 5.58.2: Hyperlink containing a URL - // e.g. http://example.org/index.php - - // offset: var; size: 16; GUID of URL Moniker - $offset += 16; - // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word - $us = $this->_GetInt4d($recordData, $offset); - $offset += 4; - // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated - $url = $this->_encodeUTF16(substr($recordData, $offset, $us - 2), false); - $url .= $hasText ? '#' : ''; - $offset += $us; - break; - - case 'local': - // section 5.58.3: Hyperlink to local file - // examples: - // mydoc.txt - // ../../somedoc.xls#Sheet!A1 - - // offset: var; size: 16; GUI of File Moniker - $offset += 16; - - // offset: var; size: 2; directory up-level count. - $upLevelCount = $this->_GetInt2d($recordData, $offset); - $offset += 2; - - // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word - $sl = $this->_GetInt4d($recordData, $offset); - $offset += 4; - - // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string) - $shortenedFilePath = substr($recordData, $offset, $sl); - $shortenedFilePath = $this->_encodeUTF16($shortenedFilePath, true); - $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero - - $offset += $sl; - - // offset: var; size: 24; unknown sequence - $offset += 24; - - // extended file path - // offset: var; size: 4; size of the following file link field including string lenth mark - $sz = $this->_GetInt4d($recordData, $offset); - $offset += 4; - - // only present if $sz > 0 - if ($sz > 0) { - // offset: var; size: 4; size of the character array of the extended file path and name - $xl = $this->_GetInt4d($recordData, $offset); - $offset += 4; - - // offset: var; size 2; unknown - $offset += 2; - - // offset: var; size $xl; character array of the extended file path and name. - $extendedFilePath = substr($recordData, $offset, $xl); - $extendedFilePath = $this->_encodeUTF16($extendedFilePath, false); - $offset += $xl; - } - - // construct the path - $url = str_repeat('..\\', $upLevelCount); - $url .= ($sz > 0) ? - $extendedFilePath : $shortenedFilePath; // use extended path if available - $url .= $hasText ? '#' : ''; - - break; - - - case 'UNC': - // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path - // todo: implement - return; - - case 'workbook': - // section 5.58.5: Hyperlink to the Current Workbook - // e.g. Sheet2!B1:C2, stored in text mark field - $url = 'sheet://'; - break; - - default: - return; - - } - - if ($hasText) { - // offset: var; size: 4; character count of text mark including trailing zero word - $tl = $this->_GetInt4d($recordData, $offset); - $offset += 4; - // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated - $text = $this->_encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false); - $url .= $text; - } - - // apply the hyperlink to all the relevant cells - foreach (PHPExcel_Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) { - $this->_phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url); - } - } - } - - /** - * Read DATAVALIDATIONS record - */ - private function _readDataValidations() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer forward to next record - $this->_pos += 4 + $length; - } - - /** - * Read DATAVALIDATION record - */ - private function _readDataValidation() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer forward to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly) { - return; - } - - // offset: 0; size: 4; Options - $options = $this->_GetInt4d($recordData, 0); - - // bit: 0-3; mask: 0x0000000F; type - $type = (0x0000000F & $options) >> 0; - switch ($type) { - case 0x00: $type = PHPExcel_Cell_DataValidation::TYPE_NONE; break; - case 0x01: $type = PHPExcel_Cell_DataValidation::TYPE_WHOLE; break; - case 0x02: $type = PHPExcel_Cell_DataValidation::TYPE_DECIMAL; break; - case 0x03: $type = PHPExcel_Cell_DataValidation::TYPE_LIST; break; - case 0x04: $type = PHPExcel_Cell_DataValidation::TYPE_DATE; break; - case 0x05: $type = PHPExcel_Cell_DataValidation::TYPE_TIME; break; - case 0x06: $type = PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH; break; - case 0x07: $type = PHPExcel_Cell_DataValidation::TYPE_CUSTOM; break; - } - - // bit: 4-6; mask: 0x00000070; error type - $errorStyle = (0x00000070 & $options) >> 4; - switch ($errorStyle) { - case 0x00: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_STOP; break; - case 0x01: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_WARNING; break; - case 0x02: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_INFORMATION; break; - } - - // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list) - // I have only seen cases where this is 1 - $explicitFormula = (0x00000080 & $options) >> 7; - - // bit: 8; mask: 0x00000100; 1= empty cells allowed - $allowBlank = (0x00000100 & $options) >> 8; - - // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity - $suppressDropDown = (0x00000200 & $options) >> 9; - - // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected - $showInputMessage = (0x00040000 & $options) >> 18; - - // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered - $showErrorMessage = (0x00080000 & $options) >> 19; - - // bit: 20-23; mask: 0x00F00000; condition operator - $operator = (0x00F00000 & $options) >> 20; - switch ($operator) { - case 0x00: $operator = PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN ; break; - case 0x01: $operator = PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN ; break; - case 0x02: $operator = PHPExcel_Cell_DataValidation::OPERATOR_EQUAL ; break; - case 0x03: $operator = PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL ; break; - case 0x04: $operator = PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN ; break; - case 0x05: $operator = PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN ; break; - case 0x06: $operator = PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL; break; - case 0x07: $operator = PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL ; break; - } - - // offset: 4; size: var; title of the prompt box - $offset = 4; - $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); - $promptTitle = $string['value'] !== chr(0) ? - $string['value'] : ''; - $offset += $string['size']; - - // offset: var; size: var; title of the error box - $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); - $errorTitle = $string['value'] !== chr(0) ? - $string['value'] : ''; - $offset += $string['size']; - - // offset: var; size: var; text of the prompt box - $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); - $prompt = $string['value'] !== chr(0) ? - $string['value'] : ''; - $offset += $string['size']; - - // offset: var; size: var; text of the error box - $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); - $error = $string['value'] !== chr(0) ? - $string['value'] : ''; - $offset += $string['size']; - - // offset: var; size: 2; size of the formula data for the first condition - $sz1 = $this->_GetInt2d($recordData, $offset); - $offset += 2; - - // offset: var; size: 2; not used - $offset += 2; - - // offset: var; size: $sz1; formula data for first condition (without size field) - $formula1 = substr($recordData, $offset, $sz1); - $formula1 = pack('v', $sz1) . $formula1; // prepend the length - try { - $formula1 = $this->_getFormulaFromStructure($formula1); - - // in list type validity, null characters are used as item separators - if ($type == PHPExcel_Cell_DataValidation::TYPE_LIST) { - $formula1 = str_replace(chr(0), ',', $formula1); - } - } catch (Exception $e) { - return; - } - $offset += $sz1; - - // offset: var; size: 2; size of the formula data for the first condition - $sz2 = $this->_GetInt2d($recordData, $offset); - $offset += 2; - - // offset: var; size: 2; not used - $offset += 2; - - // offset: var; size: $sz2; formula data for second condition (without size field) - $formula2 = substr($recordData, $offset, $sz2); - $formula2 = pack('v', $sz2) . $formula2; // prepend the length - try { - $formula2 = $this->_getFormulaFromStructure($formula2); - } catch (Exception $e) { - return; - } - $offset += $sz2; - - // offset: var; size: var; cell range address list with - $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList(substr($recordData, $offset)); - $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses']; - - foreach ($cellRangeAddresses as $cellRange) { - $stRange = $this->_phpSheet->shrinkRangeToFit($cellRange); - $stRange = PHPExcel_Cell::extractAllCellReferencesInRange($stRange); - foreach ($stRange as $coordinate) { - $objValidation = $this->_phpSheet->getCell($coordinate)->getDataValidation(); - $objValidation->setType($type); - $objValidation->setErrorStyle($errorStyle); - $objValidation->setAllowBlank((bool)$allowBlank); - $objValidation->setShowInputMessage((bool)$showInputMessage); - $objValidation->setShowErrorMessage((bool)$showErrorMessage); - $objValidation->setShowDropDown(!$suppressDropDown); - $objValidation->setOperator($operator); - $objValidation->setErrorTitle($errorTitle); - $objValidation->setError($error); - $objValidation->setPromptTitle($promptTitle); - $objValidation->setPrompt($prompt); - $objValidation->setFormula1($formula1); - $objValidation->setFormula2($formula2); - } - } - - } - - /** - * Read SHEETLAYOUT record. Stores sheet tab color information. - */ - private function _readSheetLayout() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // local pointer in record data - $offset = 0; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; repeated record identifier 0x0862 - - // offset: 2; size: 10; not used - - // offset: 12; size: 4; size of record data - // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?) - $sz = $this->_GetInt4d($recordData, 12); - - switch ($sz) { - case 0x14: - // offset: 16; size: 2; color index for sheet tab - $colorIndex = $this->_GetInt2d($recordData, 16); - $color = $this->_readColor($colorIndex); - $this->_phpSheet->getTabColor()->setRGB($color['rgb']); - break; - - case 0x28: - // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007 - return; - break; - } - } - } - - /** - * Read SHEETPROTECTION record (FEATHEADR) - */ - private function _readSheetProtection() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly) { - return; - } - - // offset: 0; size: 2; repeated record header - - // offset: 2; size: 2; FRT cell reference flag (=0 currently) - - // offset: 4; size: 8; Currently not used and set to 0 - - // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag) - $isf = $this->_GetInt2d($recordData, 12); - if ($isf != 2) { - return; - } - - // offset: 14; size: 1; =1 since this is a feat header - - // offset: 15; size: 4; size of rgbHdrSData - - // rgbHdrSData, assume "Enhanced Protection" - // offset: 19; size: 2; option flags - $options = $this->_GetInt2d($recordData, 19); - - // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects - $bool = (0x0001 & $options) >> 0; - $this->_phpSheet->getProtection()->setObjects(!$bool); - - // bit: 1; mask 0x0002; edit scenarios - $bool = (0x0002 & $options) >> 1; - $this->_phpSheet->getProtection()->setScenarios(!$bool); - - // bit: 2; mask 0x0004; format cells - $bool = (0x0004 & $options) >> 2; - $this->_phpSheet->getProtection()->setFormatCells(!$bool); - - // bit: 3; mask 0x0008; format columns - $bool = (0x0008 & $options) >> 3; - $this->_phpSheet->getProtection()->setFormatColumns(!$bool); - - // bit: 4; mask 0x0010; format rows - $bool = (0x0010 & $options) >> 4; - $this->_phpSheet->getProtection()->setFormatRows(!$bool); - - // bit: 5; mask 0x0020; insert columns - $bool = (0x0020 & $options) >> 5; - $this->_phpSheet->getProtection()->setInsertColumns(!$bool); - - // bit: 6; mask 0x0040; insert rows - $bool = (0x0040 & $options) >> 6; - $this->_phpSheet->getProtection()->setInsertRows(!$bool); - - // bit: 7; mask 0x0080; insert hyperlinks - $bool = (0x0080 & $options) >> 7; - $this->_phpSheet->getProtection()->setInsertHyperlinks(!$bool); - - // bit: 8; mask 0x0100; delete columns - $bool = (0x0100 & $options) >> 8; - $this->_phpSheet->getProtection()->setDeleteColumns(!$bool); - - // bit: 9; mask 0x0200; delete rows - $bool = (0x0200 & $options) >> 9; - $this->_phpSheet->getProtection()->setDeleteRows(!$bool); - - // bit: 10; mask 0x0400; select locked cells - $bool = (0x0400 & $options) >> 10; - $this->_phpSheet->getProtection()->setSelectLockedCells(!$bool); - - // bit: 11; mask 0x0800; sort cell range - $bool = (0x0800 & $options) >> 11; - $this->_phpSheet->getProtection()->setSort(!$bool); - - // bit: 12; mask 0x1000; auto filter - $bool = (0x1000 & $options) >> 12; - $this->_phpSheet->getProtection()->setAutoFilter(!$bool); - - // bit: 13; mask 0x2000; pivot tables - $bool = (0x2000 & $options) >> 13; - $this->_phpSheet->getProtection()->setPivotTables(!$bool); - - // bit: 14; mask 0x4000; select unlocked cells - $bool = (0x4000 & $options) >> 14; - $this->_phpSheet->getProtection()->setSelectUnlockedCells(!$bool); - - // offset: 21; size: 2; not used - } - - /** - * Read RANGEPROTECTION record - * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification, - * where it is referred to as FEAT record - */ - private function _readRangeProtection() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // local pointer in record data - $offset = 0; - - if (!$this->_readDataOnly) { - $offset += 12; - - // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag - $isf = $this->_GetInt2d($recordData, 12); - if ($isf != 2) { - // we only read FEAT records of type 2 - return; - } - $offset += 2; - - $offset += 5; - - // offset: 19; size: 2; count of ref ranges this feature is on - $cref = $this->_GetInt2d($recordData, 19); - $offset += 2; - - $offset += 6; - - // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record) - $cellRanges = array(); - for ($i = 0; $i < $cref; ++$i) { - try { - $cellRange = $this->_readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8)); - } catch (Exception $e) { - return; - } - $cellRanges[] = $cellRange; - $offset += 8; - } - - // offset: var; size: var; variable length of feature specific data - $rgbFeat = substr($recordData, $offset); - $offset += 4; - - // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit) - $wPassword = $this->_GetInt4d($recordData, $offset); - $offset += 4; - - // Apply range protection to sheet - if ($cellRanges) { - $this->_phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true); - } - } - } - - /** - * Read IMDATA record - */ - private function _readImData() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - - // get spliced record data - $splicedRecordData = $this->_getSplicedRecordData(); - $recordData = $splicedRecordData['recordData']; - - // UNDER CONSTRUCTION - - // offset: 0; size: 2; image format - $cf = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; environment from which the file was written - $env = $this->_GetInt2d($recordData, 2); - - // offset: 4; size: 4; length of the image data - $lcb = $this->_GetInt4d($recordData, 4); - - // offset: 8; size: var; image data - $iData = substr($recordData, 8); - - switch ($cf) { - case 0x09: // Windows bitmap format - // BITMAPCOREINFO - // 1. BITMAPCOREHEADER - // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure - $bcSize = $this->_GetInt4d($iData, 0); - var_dump($bcSize); - - // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels - $bcWidth = $this->_GetInt2d($iData, 4); - var_dump($bcWidth); - - // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels. - $bcHeight = $this->_GetInt2d($iData, 6); - var_dump($bcHeight); - $ih = imagecreatetruecolor($bcWidth, $bcHeight); - - // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1 - - // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24 - $bcBitCount = $this->_GetInt2d($iData, 10); - var_dump($bcBitCount); - - $rgbString = substr($iData, 12); - $rgbTriples = array(); - while (strlen($rgbString) > 0) { - $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString); - $rgbString = substr($rgbString, 3); - } - $x = 0; - $y = 0; - foreach ($rgbTriples as $i => $rgbTriple) { - $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']); - imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color); - $x = ($x + 1) % $bcWidth; - $y = $y + floor(($x + 1) / $bcWidth); - } - //imagepng($ih, 'image.png'); - - $drawing = new PHPExcel_Worksheet_Drawing(); - $drawing->setPath($filename); - $drawing->setWorksheet($this->_phpSheet); - - break; - - case 0x02: // Windows metafile or Macintosh PICT format - case 0x0e: // native format - default; - break; - - } - - // _getSplicedRecordData() takes care of moving current position in data stream - } - - /** - * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record - * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented. - * In this case, we must treat the CONTINUE record as a MSODRAWING record - */ - private function _readContinue() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // check if we are reading drawing data - // this is in case a free CONTINUE record occurs in other circumstances we are unaware of - if ($this->_drawingData == '') { - // move stream pointer to next record - $this->_pos += 4 + $length; - - return; - } - - // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data - if ($length < 4) { - // move stream pointer to next record - $this->_pos += 4 + $length; - - return; - } - - // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record - // look inside CONTINUE record to see if it looks like a part of an Escher stream - // we know that Escher stream may be split at least at - // 0xF003 MsofbtSpgrContainer - // 0xF004 MsofbtSpContainer - // 0xF00D MsofbtClientTextbox - $validSplitPoints = array(0xF003, 0xF004, 0xF00D); // add identifiers if we find more - - $splitPoint = $this->_GetInt2d($recordData, 2); - if (in_array($splitPoint, $validSplitPoints)) { - // get spliced record data (and move pointer to next record) - $splicedRecordData = $this->_getSplicedRecordData(); - $this->_drawingData .= $splicedRecordData['recordData']; - - return; - } - - // move stream pointer to next record - $this->_pos += 4 + $length; - - } - - - /** - * Reads a record from current position in data stream and continues reading data as long as CONTINUE - * records are found. Splices the record data pieces and returns the combined string as if record data - * is in one piece. - * Moves to next current position in data stream to start of next record different from a CONtINUE record - * - * @return array - */ - private function _getSplicedRecordData() - { - $data = ''; - $spliceOffsets = array(); - - $i = 0; - $spliceOffsets[0] = 0; - - do { - ++$i; - - // offset: 0; size: 2; identifier - $identifier = $this->_GetInt2d($this->_data, $this->_pos); - // offset: 2; size: 2; length - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $data .= substr($this->_data, $this->_pos + 4, $length); - - $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length; - - $this->_pos += 4 + $length; - $nextIdentifier = $this->_GetInt2d($this->_data, $this->_pos); - } - while ($nextIdentifier == self::XLS_Type_CONTINUE); - - $splicedData = array( - 'recordData' => $data, - 'spliceOffsets' => $spliceOffsets, - ); - - return $splicedData; - - } - - /** - * Convert formula structure into human readable Excel formula like 'A3+A5*5' - * - * @param string $formulaStructure The complete binary data for the formula - * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas - * @return string Human readable formula - */ - private function _getFormulaFromStructure($formulaStructure, $baseCell = 'A1') - { - // offset: 0; size: 2; size of the following formula data - $sz = $this->_GetInt2d($formulaStructure, 0); - - // offset: 2; size: sz - $formulaData = substr($formulaStructure, 2, $sz); - - // for debug: dump the formula data - //echo ''; - //echo 'size: ' . $sz . "\n"; - //echo 'the entire formula data: '; - //Debug::dump($formulaData); - //echo "\n----\n"; - - // offset: 2 + sz; size: variable (optional) - if (strlen($formulaStructure) > 2 + $sz) { - $additionalData = substr($formulaStructure, 2 + $sz); - - // for debug: dump the additional data - //echo 'the entire additional data: '; - //Debug::dump($additionalData); - //echo "\n----\n"; - - } else { - $additionalData = ''; - } - - return $this->_getFormulaFromData($formulaData, $additionalData, $baseCell); - } - - /** - * Take formula data and additional data for formula and return human readable formula - * - * @param string $formulaData The binary data for the formula itself - * @param string $additionalData Additional binary data going with the formula - * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas - * @return string Human readable formula - */ - private function _getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1') - { - // start parsing the formula data - $tokens = array(); - - while (strlen($formulaData) > 0 and $token = $this->_getNextToken($formulaData, $baseCell)) { - $tokens[] = $token; - $formulaData = substr($formulaData, $token['size']); - - // for debug: dump the token - //var_dump($token); - } - - $formulaString = $this->_createFormulaFromTokens($tokens, $additionalData); - - return $formulaString; - } - - /** - * Take array of tokens together with additional data for formula and return human readable formula - * - * @param array $tokens - * @param array $additionalData Additional binary data going with the formula - * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas - * @return string Human readable formula - */ - private function _createFormulaFromTokens($tokens, $additionalData) - { - // empty formula? - if (count($tokens) == 0) { - return ''; - } - - $formulaStrings = array(); - foreach ($tokens as $token) { - // initialize spaces - $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen - $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen - $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis - $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis - $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis - $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis - - switch ($token['name']) { - case 'tAdd': // addition - case 'tConcat': // addition - case 'tDiv': // division - case 'tEQ': // equaltiy - case 'tGE': // greater than or equal - case 'tGT': // greater than - case 'tIsect': // intersection - case 'tLE': // less than or equal - case 'tList': // less than or equal - case 'tLT': // less than - case 'tMul': // multiplication - case 'tNE': // multiplication - case 'tPower': // power - case 'tRange': // range - case 'tSub': // subtraction - $op2 = array_pop($formulaStrings); - $op1 = array_pop($formulaStrings); - $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2"; - unset($space0, $space1); - break; - case 'tUplus': // unary plus - case 'tUminus': // unary minus - $op = array_pop($formulaStrings); - $formulaStrings[] = "$space1$space0{$token['data']}$op"; - unset($space0, $space1); - break; - case 'tPercent': // percent sign - $op = array_pop($formulaStrings); - $formulaStrings[] = "$op$space1$space0{$token['data']}"; - unset($space0, $space1); - break; - case 'tAttrVolatile': // indicates volatile function - case 'tAttrIf': - case 'tAttrSkip': - case 'tAttrChoose': - // token is only important for Excel formula evaluator - // do nothing - break; - case 'tAttrSpace': // space / carriage return - // space will be used when next token arrives, do not alter formulaString stack - switch ($token['data']['spacetype']) { - case 'type0': - $space0 = str_repeat(' ', $token['data']['spacecount']); - break; - case 'type1': - $space1 = str_repeat("\n", $token['data']['spacecount']); - break; - case 'type2': - $space2 = str_repeat(' ', $token['data']['spacecount']); - break; - case 'type3': - $space3 = str_repeat("\n", $token['data']['spacecount']); - break; - case 'type4': - $space4 = str_repeat(' ', $token['data']['spacecount']); - break; - case 'type5': - $space5 = str_repeat("\n", $token['data']['spacecount']); - break; - } - break; - case 'tAttrSum': // SUM function with one parameter - $op = array_pop($formulaStrings); - $formulaStrings[] = "{$space1}{$space0}SUM($op)"; - unset($space0, $space1); - break; - case 'tFunc': // function with fixed number of arguments - case 'tFuncV': // function with variable number of arguments - if ($token['data']['function'] != '') { - // normal function - $ops = array(); // array of operators - for ($i = 0; $i < $token['data']['args']; ++$i) { - $ops[] = array_pop($formulaStrings); - } - $ops = array_reverse($ops); - $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ")"; - unset($space0, $space1); - } else { - // add-in function - $ops = array(); // array of operators - for ($i = 0; $i < $token['data']['args'] - 1; ++$i) { - $ops[] = array_pop($formulaStrings); - } - $ops = array_reverse($ops); - $function = array_pop($formulaStrings); - $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ")"; - unset($space0, $space1); - } - break; - case 'tParen': // parenthesis - $expression = array_pop($formulaStrings); - $formulaStrings[] = "$space3$space2($expression$space5$space4)"; - unset($space2, $space3, $space4, $space5); - break; - case 'tArray': // array constant - $constantArray = $this->_readBIFF8ConstantArray($additionalData); - $formulaStrings[] = $space1 . $space0 . $constantArray['value']; - $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data - unset($space0, $space1); - break; - case 'tMemArea': - // bite off chunk of additional data - $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($additionalData); - $additionalData = substr($additionalData, $cellRangeAddressList['size']); - $formulaStrings[] = "$space1$space0{$token['data']}"; - unset($space0, $space1); - break; - case 'tArea': // cell range address - case 'tBool': // boolean - case 'tErr': // error code - case 'tInt': // integer - case 'tMemErr': - case 'tMemFunc': - case 'tMissArg': - case 'tName': - case 'tNameX': - case 'tNum': // number - case 'tRef': // single cell reference - case 'tRef3d': // 3d cell reference - case 'tArea3d': // 3d cell range reference - case 'tRefN': - case 'tAreaN': - case 'tStr': // string - $formulaStrings[] = "$space1$space0{$token['data']}"; - unset($space0, $space1); - break; - } - } - $formulaString = $formulaStrings[0]; - - // for debug: dump the human readable formula - //echo '----' . "\n"; - //echo 'Formula: ' . $formulaString; - - return $formulaString; - } - - /** - * Fetch next token from binary formula data - * - * @param string Formula data - * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas - * @return array - * @throws Exception - */ - private function _getNextToken($formulaData, $baseCell = 'A1') - { - // offset: 0; size: 1; token id - $id = ord($formulaData[0]); // token id - $name = false; // initialize token name - - switch ($id) { - case 0x03: $name = 'tAdd'; $size = 1; $data = '+'; break; - case 0x04: $name = 'tSub'; $size = 1; $data = '-'; break; - case 0x05: $name = 'tMul'; $size = 1; $data = '*'; break; - case 0x06: $name = 'tDiv'; $size = 1; $data = '/'; break; - case 0x07: $name = 'tPower'; $size = 1; $data = '^'; break; - case 0x08: $name = 'tConcat'; $size = 1; $data = '&'; break; - case 0x09: $name = 'tLT'; $size = 1; $data = '<'; break; - case 0x0A: $name = 'tLE'; $size = 1; $data = '<='; break; - case 0x0B: $name = 'tEQ'; $size = 1; $data = '='; break; - case 0x0C: $name = 'tGE'; $size = 1; $data = '>='; break; - case 0x0D: $name = 'tGT'; $size = 1; $data = '>'; break; - case 0x0E: $name = 'tNE'; $size = 1; $data = '<>'; break; - case 0x0F: $name = 'tIsect'; $size = 1; $data = ' '; break; - case 0x10: $name = 'tList'; $size = 1; $data = ','; break; - case 0x11: $name = 'tRange'; $size = 1; $data = ':'; break; - case 0x12: $name = 'tUplus'; $size = 1; $data = '+'; break; - case 0x13: $name = 'tUminus'; $size = 1; $data = '-'; break; - case 0x14: $name = 'tPercent'; $size = 1; $data = '%'; break; - case 0x15: // parenthesis - $name = 'tParen'; - $size = 1; - $data = null; - break; - case 0x16: // missing argument - $name = 'tMissArg'; - $size = 1; - $data = ''; - break; - case 0x17: // string - $name = 'tStr'; - // offset: 1; size: var; Unicode string, 8-bit string length - $string = $this->_readUnicodeStringShort(substr($formulaData, 1)); - $size = 1 + $string['size']; - $data = $this->_UTF8toExcelDoubleQuoted($string['value']); - break; - case 0x19: // Special attribute - // offset: 1; size: 1; attribute type flags: - switch (ord($formulaData[1])) { - case 0x01: - $name = 'tAttrVolatile'; - $size = 4; - $data = null; - break; - case 0x02: - $name = 'tAttrIf'; - $size = 4; - $data = null; - break; - case 0x04: - $name = 'tAttrChoose'; - // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1) - $nc = $this->_GetInt2d($formulaData, 2); - // offset: 4; size: 2 * $nc - // offset: 4 + 2 * $nc; size: 2 - $size = 2 * $nc + 6; - $data = null; - break; - case 0x08: - $name = 'tAttrSkip'; - $size = 4; - $data = null; - break; - case 0x10: - $name = 'tAttrSum'; - $size = 4; - $data = null; - break; - case 0x40: - case 0x41: - $name = 'tAttrSpace'; - $size = 4; - // offset: 2; size: 2; space type and position - switch (ord($formulaData[2])) { - case 0x00: - $spacetype = 'type0'; - break; - case 0x01: - $spacetype = 'type1'; - break; - case 0x02: - $spacetype = 'type2'; - break; - case 0x03: - $spacetype = 'type3'; - break; - case 0x04: - $spacetype = 'type4'; - break; - case 0x05: - $spacetype = 'type5'; - break; - default: - throw new Exception('Unrecognized space type in tAttrSpace token'); - break; - } - // offset: 3; size: 1; number of inserted spaces/carriage returns - $spacecount = ord($formulaData[3]); - - $data = array('spacetype' => $spacetype, 'spacecount' => $spacecount); - break; - default: - throw new Exception('Unrecognized attribute flag in tAttr token'); - break; - } - break; - case 0x1C: // error code - // offset: 1; size: 1; error code - $name = 'tErr'; - $size = 2; - $data = $this->_mapErrorCode(ord($formulaData[1])); - break; - case 0x1D: // boolean - // offset: 1; size: 1; 0 = false, 1 = true; - $name = 'tBool'; - $size = 2; - $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE'; - break; - case 0x1E: // integer - // offset: 1; size: 2; unsigned 16-bit integer - $name = 'tInt'; - $size = 3; - $data = $this->_GetInt2d($formulaData, 1); - break; - case 0x1F: // number - // offset: 1; size: 8; - $name = 'tNum'; - $size = 9; - $data = $this->_extractNumber(substr($formulaData, 1)); - $data = str_replace(',', '.', (string)$data); // in case non-English locale - break; - case 0x40: // array constant - case 0x60: // array constant - // offset: 1; size: 7; not used - $name = 'tArray'; - $size = 8; - $data = null; - break; - case 0x41: // function with fixed number of arguments - $name = 'tFunc'; - $size = 3; - // offset: 1; size: 2; index to built-in sheet function - switch ($this->_GetInt2d($formulaData, 1)) { - case 2: $function = 'ISNA'; $args = 1; break; - case 3: $function = 'ISERROR'; $args = 1; break; - case 10: $function = 'NA'; $args = 0; break; - case 15: $function = 'SIN'; $args = 1; break; - case 16: $function = 'COS'; $args = 1; break; - case 17: $function = 'TAN'; $args = 1; break; - case 18: $function = 'ATAN'; $args = 1; break; - case 19: $function = 'PI'; $args = 0; break; - case 20: $function = 'SQRT'; $args = 1; break; - case 21: $function = 'EXP'; $args = 1; break; - case 22: $function = 'LN'; $args = 1; break; - case 23: $function = 'LOG10'; $args = 1; break; - case 24: $function = 'ABS'; $args = 1; break; - case 25: $function = 'INT'; $args = 1; break; - case 26: $function = 'SIGN'; $args = 1; break; - case 27: $function = 'ROUND'; $args = 2; break; - case 30: $function = 'REPT'; $args = 2; break; - case 31: $function = 'MID'; $args = 3; break; - case 32: $function = 'LEN'; $args = 1; break; - case 33: $function = 'VALUE'; $args = 1; break; - case 34: $function = 'TRUE'; $args = 0; break; - case 35: $function = 'FALSE'; $args = 0; break; - case 38: $function = 'NOT'; $args = 1; break; - case 39: $function = 'MOD'; $args = 2; break; - case 40: $function = 'DCOUNT'; $args = 3; break; - case 41: $function = 'DSUM'; $args = 3; break; - case 42: $function = 'DAVERAGE'; $args = 3; break; - case 43: $function = 'DMIN'; $args = 3; break; - case 44: $function = 'DMAX'; $args = 3; break; - case 45: $function = 'DSTDEV'; $args = 3; break; - case 48: $function = 'TEXT'; $args = 2; break; - case 61: $function = 'MIRR'; $args = 3; break; - case 63: $function = 'RAND'; $args = 0; break; - case 65: $function = 'DATE'; $args = 3; break; - case 66: $function = 'TIME'; $args = 3; break; - case 67: $function = 'DAY'; $args = 1; break; - case 68: $function = 'MONTH'; $args = 1; break; - case 69: $function = 'YEAR'; $args = 1; break; - case 71: $function = 'HOUR'; $args = 1; break; - case 72: $function = 'MINUTE'; $args = 1; break; - case 73: $function = 'SECOND'; $args = 1; break; - case 74: $function = 'NOW'; $args = 0; break; - case 75: $function = 'AREAS'; $args = 1; break; - case 76: $function = 'ROWS'; $args = 1; break; - case 77: $function = 'COLUMNS'; $args = 1; break; - case 83: $function = 'TRANSPOSE'; $args = 1; break; - case 86: $function = 'TYPE'; $args = 1; break; - case 97: $function = 'ATAN2'; $args = 2; break; - case 98: $function = 'ASIN'; $args = 1; break; - case 99: $function = 'ACOS'; $args = 1; break; - case 105: $function = 'ISREF'; $args = 1; break; - case 111: $function = 'CHAR'; $args = 1; break; - case 112: $function = 'LOWER'; $args = 1; break; - case 113: $function = 'UPPER'; $args = 1; break; - case 114: $function = 'PROPER'; $args = 1; break; - case 117: $function = 'EXACT'; $args = 2; break; - case 118: $function = 'TRIM'; $args = 1; break; - case 119: $function = 'REPLACE'; $args = 4; break; - case 121: $function = 'CODE'; $args = 1; break; - case 126: $function = 'ISERR'; $args = 1; break; - case 127: $function = 'ISTEXT'; $args = 1; break; - case 128: $function = 'ISNUMBER'; $args = 1; break; - case 129: $function = 'ISBLANK'; $args = 1; break; - case 130: $function = 'T'; $args = 1; break; - case 131: $function = 'N'; $args = 1; break; - case 140: $function = 'DATEVALUE'; $args = 1; break; - case 141: $function = 'TIMEVALUE'; $args = 1; break; - case 142: $function = 'SLN'; $args = 3; break; - case 143: $function = 'SYD'; $args = 4; break; - case 162: $function = 'CLEAN'; $args = 1; break; - case 163: $function = 'MDETERM'; $args = 1; break; - case 164: $function = 'MINVERSE'; $args = 1; break; - case 165: $function = 'MMULT'; $args = 2; break; - case 184: $function = 'FACT'; $args = 1; break; - case 189: $function = 'DPRODUCT'; $args = 3; break; - case 190: $function = 'ISNONTEXT'; $args = 1; break; - case 195: $function = 'DSTDEVP'; $args = 3; break; - case 196: $function = 'DVARP'; $args = 3; break; - case 198: $function = 'ISLOGICAL'; $args = 1; break; - case 199: $function = 'DCOUNTA'; $args = 3; break; - case 207: $function = 'REPLACEB'; $args = 4; break; - case 210: $function = 'MIDB'; $args = 3; break; - case 211: $function = 'LENB'; $args = 1; break; - case 212: $function = 'ROUNDUP'; $args = 2; break; - case 213: $function = 'ROUNDDOWN'; $args = 2; break; - case 214: $function = 'ASC'; $args = 1; break; - case 215: $function = 'DBCS'; $args = 1; break; - case 221: $function = 'TODAY'; $args = 0; break; - case 229: $function = 'SINH'; $args = 1; break; - case 230: $function = 'COSH'; $args = 1; break; - case 231: $function = 'TANH'; $args = 1; break; - case 232: $function = 'ASINH'; $args = 1; break; - case 233: $function = 'ACOSH'; $args = 1; break; - case 234: $function = 'ATANH'; $args = 1; break; - case 235: $function = 'DGET'; $args = 3; break; - case 244: $function = 'INFO'; $args = 1; break; - case 252: $function = 'FREQUENCY'; $args = 2; break; - case 261: $function = 'ERROR.TYPE'; $args = 1; break; - case 271: $function = 'GAMMALN'; $args = 1; break; - case 273: $function = 'BINOMDIST'; $args = 4; break; - case 274: $function = 'CHIDIST'; $args = 2; break; - case 275: $function = 'CHIINV'; $args = 2; break; - case 276: $function = 'COMBIN'; $args = 2; break; - case 277: $function = 'CONFIDENCE'; $args = 3; break; - case 278: $function = 'CRITBINOM'; $args = 3; break; - case 279: $function = 'EVEN'; $args = 1; break; - case 280: $function = 'EXPONDIST'; $args = 3; break; - case 281: $function = 'FDIST'; $args = 3; break; - case 282: $function = 'FINV'; $args = 3; break; - case 283: $function = 'FISHER'; $args = 1; break; - case 284: $function = 'FISHERINV'; $args = 1; break; - case 285: $function = 'FLOOR'; $args = 2; break; - case 286: $function = 'GAMMADIST'; $args = 4; break; - case 287: $function = 'GAMMAINV'; $args = 3; break; - case 288: $function = 'CEILING'; $args = 2; break; - case 289: $function = 'HYPGEOMDIST'; $args = 4; break; - case 290: $function = 'LOGNORMDIST'; $args = 3; break; - case 291: $function = 'LOGINV'; $args = 3; break; - case 292: $function = 'NEGBINOMDIST'; $args = 3; break; - case 293: $function = 'NORMDIST'; $args = 4; break; - case 294: $function = 'NORMSDIST'; $args = 1; break; - case 295: $function = 'NORMINV'; $args = 3; break; - case 296: $function = 'NORMSINV'; $args = 1; break; - case 297: $function = 'STANDARDIZE'; $args = 3; break; - case 298: $function = 'ODD'; $args = 1; break; - case 299: $function = 'PERMUT'; $args = 2; break; - case 300: $function = 'POISSON'; $args = 3; break; - case 301: $function = 'TDIST'; $args = 3; break; - case 302: $function = 'WEIBULL'; $args = 4; break; - case 303: $function = 'SUMXMY2'; $args = 2; break; - case 304: $function = 'SUMX2MY2'; $args = 2; break; - case 305: $function = 'SUMX2PY2'; $args = 2; break; - case 306: $function = 'CHITEST'; $args = 2; break; - case 307: $function = 'CORREL'; $args = 2; break; - case 308: $function = 'COVAR'; $args = 2; break; - case 309: $function = 'FORECAST'; $args = 3; break; - case 310: $function = 'FTEST'; $args = 2; break; - case 311: $function = 'INTERCEPT'; $args = 2; break; - case 312: $function = 'PEARSON'; $args = 2; break; - case 313: $function = 'RSQ'; $args = 2; break; - case 314: $function = 'STEYX'; $args = 2; break; - case 315: $function = 'SLOPE'; $args = 2; break; - case 316: $function = 'TTEST'; $args = 4; break; - case 325: $function = 'LARGE'; $args = 2; break; - case 326: $function = 'SMALL'; $args = 2; break; - case 327: $function = 'QUARTILE'; $args = 2; break; - case 328: $function = 'PERCENTILE'; $args = 2; break; - case 331: $function = 'TRIMMEAN'; $args = 2; break; - case 332: $function = 'TINV'; $args = 2; break; - case 337: $function = 'POWER'; $args = 2; break; - case 342: $function = 'RADIANS'; $args = 1; break; - case 343: $function = 'DEGREES'; $args = 1; break; - case 346: $function = 'COUNTIF'; $args = 2; break; - case 347: $function = 'COUNTBLANK'; $args = 1; break; - case 350: $function = 'ISPMT'; $args = 4; break; - case 351: $function = 'DATEDIF'; $args = 3; break; - case 352: $function = 'DATESTRING'; $args = 1; break; - case 353: $function = 'NUMBERSTRING'; $args = 2; break; - case 360: $function = 'PHONETIC'; $args = 1; break; - default: - throw new Exception('Unrecognized function in formula'); - break; - } - $data = array('function' => $function, 'args' => $args); - break; - case 0x22: // function with variable number of arguments - case 0x42: // function with variable number of arguments - case 0x62: // function with variable number of arguments - $name = 'tFuncV'; - $size = 4; - // offset: 1; size: 1; number of arguments - $args = ord($formulaData[1]); - // offset: 2: size: 2; index to built-in sheet function - $index = $this->_GetInt2d($formulaData, 2); - switch ($index) { - case 0: $function = 'COUNT'; break; - case 1: $function = 'IF'; break; - case 4: $function = 'SUM'; break; - case 5: $function = 'AVERAGE'; break; - case 6: $function = 'MIN'; break; - case 7: $function = 'MAX'; break; - case 8: $function = 'ROW'; break; - case 9: $function = 'COLUMN'; break; - case 11: $function = 'NPV'; break; - case 12: $function = 'STDEV'; break; - case 13: $function = 'DOLLAR'; break; - case 14: $function = 'FIXED'; break; - case 28: $function = 'LOOKUP'; break; - case 29: $function = 'INDEX'; break; - case 36: $function = 'AND'; break; - case 37: $function = 'OR'; break; - case 46: $function = 'VAR'; break; - case 49: $function = 'LINEST'; break; - case 50: $function = 'TREND'; break; - case 51: $function = 'LOGEST'; break; - case 52: $function = 'GROWTH'; break; - case 56: $function = 'PV'; break; - case 57: $function = 'FV'; break; - case 58: $function = 'NPER'; break; - case 59: $function = 'PMT'; break; - case 60: $function = 'RATE'; break; - case 62: $function = 'IRR'; break; - case 64: $function = 'MATCH'; break; - case 70: $function = 'WEEKDAY'; break; - case 78: $function = 'OFFSET'; break; - case 82: $function = 'SEARCH'; break; - case 100: $function = 'CHOOSE'; break; - case 101: $function = 'HLOOKUP'; break; - case 102: $function = 'VLOOKUP'; break; - case 109: $function = 'LOG'; break; - case 115: $function = 'LEFT'; break; - case 116: $function = 'RIGHT'; break; - case 120: $function = 'SUBSTITUTE'; break; - case 124: $function = 'FIND'; break; - case 125: $function = 'CELL'; break; - case 144: $function = 'DDB'; break; - case 148: $function = 'INDIRECT'; break; - case 167: $function = 'IPMT'; break; - case 168: $function = 'PPMT'; break; - case 169: $function = 'COUNTA'; break; - case 183: $function = 'PRODUCT'; break; - case 193: $function = 'STDEVP'; break; - case 194: $function = 'VARP'; break; - case 197: $function = 'TRUNC'; break; - case 204: $function = 'USDOLLAR'; break; - case 205: $function = 'FINDB'; break; - case 206: $function = 'SEARCHB'; break; - case 208: $function = 'LEFTB'; break; - case 209: $function = 'RIGHTB'; break; - case 216: $function = 'RANK'; break; - case 219: $function = 'ADDRESS'; break; - case 220: $function = 'DAYS360'; break; - case 222: $function = 'VDB'; break; - case 227: $function = 'MEDIAN'; break; - case 228: $function = 'SUMPRODUCT'; break; - case 247: $function = 'DB'; break; - case 255: $function = ''; break; - case 269: $function = 'AVEDEV'; break; - case 270: $function = 'BETADIST'; break; - case 272: $function = 'BETAINV'; break; - case 317: $function = 'PROB'; break; - case 318: $function = 'DEVSQ'; break; - case 319: $function = 'GEOMEAN'; break; - case 320: $function = 'HARMEAN'; break; - case 321: $function = 'SUMSQ'; break; - case 322: $function = 'KURT'; break; - case 323: $function = 'SKEW'; break; - case 324: $function = 'ZTEST'; break; - case 329: $function = 'PERCENTRANK'; break; - case 330: $function = 'MODE'; break; - case 336: $function = 'CONCATENATE'; break; - case 344: $function = 'SUBTOTAL'; break; - case 345: $function = 'SUMIF'; break; - case 354: $function = 'ROMAN'; break; - case 358: $function = 'GETPIVOTDATA'; break; - case 359: $function = 'HYPERLINK'; break; - case 361: $function = 'AVERAGEA'; break; - case 362: $function = 'MAXA'; break; - case 363: $function = 'MINA'; break; - case 364: $function = 'STDEVPA'; break; - case 365: $function = 'VARPA'; break; - case 366: $function = 'STDEVA'; break; - case 367: $function = 'VARA'; break; - default: - throw new Exception('Unrecognized function in formula'); - break; - } - $data = array('function' => $function, 'args' => $args); - break; - case 0x23: // index to defined name - case 0x43: - $name = 'tName'; - $size = 5; - // offset: 1; size: 2; one-based index to definedname record - $definedNameIndex = $this->_GetInt2d($formulaData, 1) - 1; - // offset: 2; size: 2; not used - $data = $this->_definedname[$definedNameIndex]['name']; - break; - case 0x24: // single cell reference e.g. A5 - case 0x44: - case 0x64: - $name = 'tRef'; - $size = 5; - $data = $this->_readBIFF8CellAddress(substr($formulaData, 1, 4)); - break; - case 0x25: // cell range reference to cells in the same sheet - case 0x45: - case 0x65: - $name = 'tArea'; - $size = 9; - $data = $this->_readBIFF8CellRangeAddress(substr($formulaData, 1, 8)); - break; - case 0x26: - case 0x46: - $name = 'tMemArea'; - // offset: 1; size: 4; not used - // offset: 5; size: 2; size of the following subexpression - $subSize = $this->_GetInt2d($formulaData, 5); - $size = 7 + $subSize; - $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize)); - break; - case 0x47: - $name = 'tMemErr'; - // offset: 1; size: 4; not used - // offset: 5; size: 2; size of the following subexpression - $subSize = $this->_GetInt2d($formulaData, 5); - $size = 7 + $subSize; - $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize)); - break; - case 0x29: - case 0x49: - $name = 'tMemFunc'; - // offset: 1; size: 2; size of the following subexpression - $subSize = $this->_GetInt2d($formulaData, 1); - $size = 3 + $subSize; - $data = $this->_getFormulaFromData(substr($formulaData, 3, $subSize)); - break; - - case 0x2C: // Relative reference, used in shared formulas and some other places - case 0x4C: - case 0x6C: - $name = 'tRefN'; - $size = 5; - $data = $this->_readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell); - break; - - case 0x2D: - case 0x4D: - case 0x6D: - $name = 'tAreaN'; - $size = 9; - $data = $this->_readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell); - break; - - case 0x39: - case 0x59: - case 0x79: - $name = 'tNameX'; - $size = 7; - // offset: 1; size: 2; index to REF entry in EXTERNSHEET record - // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record - $index = $this->_GetInt2d($formulaData, 3); - // assume index is to EXTERNNAME record - $data = $this->_externalNames[$index - 1]['name']; - // offset: 5; size: 2; not used - break; - - case 0x3A: // 3d reference to cell - case 0x5A: - $name = 'tRef3d'; - $size = 7; - - try { - // offset: 1; size: 2; index to REF entry - $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1)); - // offset: 3; size: 4; cell address - $cellAddress = $this->_readBIFF8CellAddress(substr($formulaData, 3, 4)); - - $data = "$sheetRange!$cellAddress"; - - } catch (Exception $e) { - // deleted sheet reference - $data = '#REF!'; - } - - break; - case 0x3B: // 3d reference to cell range - case 0x5B: - $name = 'tArea3d'; - $size = 11; - - try { - // offset: 1; size: 2; index to REF entry - $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1)); - // offset: 3; size: 8; cell address - $cellRangeAddress = $this->_readBIFF8CellRangeAddress(substr($formulaData, 3, 8)); - - $data = "$sheetRange!$cellRangeAddress"; - - } catch (Exception $e) { - // deleted sheet reference - $data = '#REF!'; - - } - - break; - // case 0x39: // don't know how to deal with - default: - throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula'); - break; - } - - return array( - 'id' => $id, - 'name' => $name, - 'size' => $size, - 'data' => $data, - ); - } - - /** - * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2' - * section 3.3.4 - * - * @param string $cellAddressStructure - * @return string - */ - private function _readBIFF8CellAddress($cellAddressStructure) - { - // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767)) - $row = $this->_GetInt2d($cellAddressStructure, 0) + 1; - - // offset: 2; size: 2; index to column or column offset + relative flags - - // bit: 7-0; mask 0x00FF; column index - $column = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($cellAddressStructure, 2)); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) { - $column = '$' . $column; - } - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) { - $row = '$' . $row; - } - - return $column . $row; - } - - /** - * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column - * to indicate offsets from a base cell - * section 3.3.4 - * - * @param string $cellAddressStructure - * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas - * @return string - */ - private function _readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1') - { - list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell); - $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1; - - // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767)) - $rowIndex = $this->_GetInt2d($cellAddressStructure, 0); - $row = $this->_GetInt2d($cellAddressStructure, 0) + 1; - - // offset: 2; size: 2; index to column or column offset + relative flags - - // bit: 7-0; mask 0x00FF; column index - $colIndex = 0x00FF & $this->_GetInt2d($cellAddressStructure, 2); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) { - $column = PHPExcel_Cell::stringFromColumnIndex($colIndex); - $column = '$' . $column; - } else { - $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256; - $column = PHPExcel_Cell::stringFromColumnIndex($baseCol + $colIndex); - } - - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) { - $row = '$' . $row; - } else { - $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536; - $row = $baseRow + $rowIndex; - } - - return $column . $row; - } - - /** - * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1' - * always fixed range - * section 2.5.14 - * - * @param string $subData - * @return string - * @throws Exception - */ - private function _readBIFF5CellRangeAddressFixed($subData) - { - // offset: 0; size: 2; index to first row - $fr = $this->_GetInt2d($subData, 0) + 1; - - // offset: 2; size: 2; index to last row - $lr = $this->_GetInt2d($subData, 2) + 1; - - // offset: 4; size: 1; index to first column - $fc = ord($subData{4}); - - // offset: 5; size: 1; index to last column - $lc = ord($subData{5}); - - // check values - if ($fr > $lr || $fc > $lc) { - throw new Exception('Not a cell range address'); - } - - // column index to letter - $fc = PHPExcel_Cell::stringFromColumnIndex($fc); - $lc = PHPExcel_Cell::stringFromColumnIndex($lc); - - if ($fr == $lr and $fc == $lc) { - return "$fc$fr"; - } - return "$fc$fr:$lc$lr"; - } - - /** - * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1' - * always fixed range - * section 2.5.14 - * - * @param string $subData - * @return string - * @throws Exception - */ - private function _readBIFF8CellRangeAddressFixed($subData) - { - // offset: 0; size: 2; index to first row - $fr = $this->_GetInt2d($subData, 0) + 1; - - // offset: 2; size: 2; index to last row - $lr = $this->_GetInt2d($subData, 2) + 1; - - // offset: 4; size: 2; index to first column - $fc = $this->_GetInt2d($subData, 4); - - // offset: 6; size: 2; index to last column - $lc = $this->_GetInt2d($subData, 6); - - // check values - if ($fr > $lr || $fc > $lc) { - throw new Exception('Not a cell range address'); - } - - // column index to letter - $fc = PHPExcel_Cell::stringFromColumnIndex($fc); - $lc = PHPExcel_Cell::stringFromColumnIndex($lc); - - if ($fr == $lr and $fc == $lc) { - return "$fc$fr"; - } - return "$fc$fr:$lc$lr"; - } - - /** - * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6' - * there are flags indicating whether column/row index is relative - * section 3.3.4 - * - * @param string $subData - * @return string - */ - private function _readBIFF8CellRangeAddress($subData) - { - // todo: if cell range is just a single cell, should this funciton - // not just return e.g. 'A1' and not 'A1:A1' ? - - // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767)) - $fr = $this->_GetInt2d($subData, 0) + 1; - - // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767)) - $lr = $this->_GetInt2d($subData, 2) + 1; - - // offset: 4; size: 2; index to first column or column offset + relative flags - - // bit: 7-0; mask 0x00FF; column index - $fc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 4)); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($subData, 4))) { - $fc = '$' . $fc; - } - - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($subData, 4))) { - $fr = '$' . $fr; - } - - // offset: 6; size: 2; index to last column or column offset + relative flags - - // bit: 7-0; mask 0x00FF; column index - $lc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 6)); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($subData, 6))) { - $lc = '$' . $lc; - } - - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($subData, 6))) { - $lr = '$' . $lr; - } - - return "$fc$fr:$lc$lr"; - } - - /** - * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column - * to indicate offsets from a base cell - * section 3.3.4 - * - * @param string $subData - * @param string $baseCell Base cell - * @return string Cell range address - */ - private function _readBIFF8CellRangeAddressB($subData, $baseCell = 'A1') - { - list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell); - $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1; - - // TODO: if cell range is just a single cell, should this funciton - // not just return e.g. 'A1' and not 'A1:A1' ? - - // offset: 0; size: 2; first row - $frIndex = $this->_GetInt2d($subData, 0); // adjust below - - // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767) - $lrIndex = $this->_GetInt2d($subData, 2); // adjust below - - // offset: 4; size: 2; first column with relative/absolute flags - - // bit: 7-0; mask 0x00FF; column index - $fcIndex = 0x00FF & $this->_GetInt2d($subData, 4); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($subData, 4))) { - // absolute column index - $fc = PHPExcel_Cell::stringFromColumnIndex($fcIndex); - $fc = '$' . $fc; - } else { - // column offset - $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256; - $fc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $fcIndex); - } - - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($subData, 4))) { - // absolute row index - $fr = $frIndex + 1; - $fr = '$' . $fr; - } else { - // row offset - $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536; - $fr = $baseRow + $frIndex; - } - - // offset: 6; size: 2; last column with relative/absolute flags - - // bit: 7-0; mask 0x00FF; column index - $lcIndex = 0x00FF & $this->_GetInt2d($subData, 6); - $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256; - $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($subData, 6))) { - // absolute column index - $lc = PHPExcel_Cell::stringFromColumnIndex($lcIndex); - $lc = '$' . $lc; - } else { - // column offset - $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256; - $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex); - } - - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($subData, 6))) { - // absolute row index - $lr = $lrIndex + 1; - $lr = '$' . $lr; - } else { - // row offset - $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536; - $lr = $baseRow + $lrIndex; - } - - return "$fc$fr:$lc$lr"; - } - - /** - * Read BIFF8 cell range address list - * section 2.5.15 - * - * @param string $subData - * @return array - */ - private function _readBIFF8CellRangeAddressList($subData) - { - $cellRangeAddresses = array(); - - // offset: 0; size: 2; number of the following cell range addresses - $nm = $this->_GetInt2d($subData, 0); - - $offset = 2; - // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses - for ($i = 0; $i < $nm; ++$i) { - $cellRangeAddresses[] = $this->_readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8)); - $offset += 8; - } - - return array( - 'size' => 2 + 8 * $nm, - 'cellRangeAddresses' => $cellRangeAddresses, - ); - } - - /** - * Read BIFF5 cell range address list - * section 2.5.15 - * - * @param string $subData - * @return array - */ - private function _readBIFF5CellRangeAddressList($subData) - { - $cellRangeAddresses = array(); - - // offset: 0; size: 2; number of the following cell range addresses - $nm = $this->_GetInt2d($subData, 0); - - $offset = 2; - // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses - for ($i = 0; $i < $nm; ++$i) { - $cellRangeAddresses[] = $this->_readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6)); - $offset += 6; - } - - return array( - 'size' => 2 + 6 * $nm, - 'cellRangeAddresses' => $cellRangeAddresses, - ); - } - - /** - * Get a sheet range like Sheet1:Sheet3 from REF index - * Note: If there is only one sheet in the range, one gets e.g Sheet1 - * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets, - * in which case an exception is thrown - * - * @param int $index - * @return string|false - * @throws Exception - */ - private function _readSheetRangeByRefIndex($index) - { - if (isset($this->_ref[$index])) { - - $type = $this->_externalBooks[$this->_ref[$index]['externalBookIndex']]['type']; - - switch ($type) { - case 'internal': - // check if we have a deleted 3d reference - if ($this->_ref[$index]['firstSheetIndex'] == 0xFFFF or $this->_ref[$index]['lastSheetIndex'] == 0xFFFF) { - throw new Exception('Deleted sheet reference'); - } - - // we have normal sheet range (collapsed or uncollapsed) - $firstSheetName = $this->_sheets[$this->_ref[$index]['firstSheetIndex']]['name']; - $lastSheetName = $this->_sheets[$this->_ref[$index]['lastSheetIndex']]['name']; - - if ($firstSheetName == $lastSheetName) { - // collapsed sheet range - $sheetRange = $firstSheetName; - } else { - $sheetRange = "$firstSheetName:$lastSheetName"; - } - - // escape the single-quotes - $sheetRange = str_replace("'", "''", $sheetRange); - - // if there are special characters, we need to enclose the range in single-quotes - // todo: check if we have identified the whole set of special characters - // it seems that the following characters are not accepted for sheet names - // and we may assume that they are not present: []*/:\? - if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) { - $sheetRange = "'$sheetRange'"; - } - - return $sheetRange; - break; - - default: - // TODO: external sheet support - throw new Exception('Excel5 reader only supports internal sheets in fomulas'); - break; - } - } - return false; - } - - /** - * read BIFF8 constant value array from array data - * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40} - * section 2.5.8 - * - * @param string $arrayData - * @return array - */ - private function _readBIFF8ConstantArray($arrayData) - { - // offset: 0; size: 1; number of columns decreased by 1 - $nc = ord($arrayData[0]); - - // offset: 1; size: 2; number of rows decreased by 1 - $nr = $this->_GetInt2d($arrayData, 1); - $size = 3; // initialize - $arrayData = substr($arrayData, 3); - - // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values - $matrixChunks = array(); - for ($r = 1; $r <= $nr + 1; ++$r) { - $items = array(); - for ($c = 1; $c <= $nc + 1; ++$c) { - $constant = $this->_readBIFF8Constant($arrayData); - $items[] = $constant['value']; - $arrayData = substr($arrayData, $constant['size']); - $size += $constant['size']; - } - $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"' - } - $matrix = '{' . implode(';', $matrixChunks) . '}'; - - return array( - 'value' => $matrix, - 'size' => $size, - ); - } - - /** - * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value' - * section 2.5.7 - * returns e.g. array('value' => '5', 'size' => 9) - * - * @param string $valueData - * @return array - */ - private function _readBIFF8Constant($valueData) - { - // offset: 0; size: 1; identifier for type of constant - $identifier = ord($valueData[0]); - - switch ($identifier) { - case 0x00: // empty constant (what is this?) - $value = ''; - $size = 9; - break; - case 0x01: // number - // offset: 1; size: 8; IEEE 754 floating-point value - $value = $this->_extractNumber(substr($valueData, 1, 8)); - $size = 9; - break; - case 0x02: // string value - // offset: 1; size: var; Unicode string, 16-bit string length - $string = $this->_readUnicodeStringLong(substr($valueData, 1)); - $value = '"' . $string['value'] . '"'; - $size = 1 + $string['size']; - break; - case 0x04: // boolean - // offset: 1; size: 1; 0 = FALSE, 1 = TRUE - if (ord($valueData[1])) { - $value = 'TRUE'; - } else { - $value = 'FALSE'; - } - $size = 9; - break; - case 0x10: // error code - // offset: 1; size: 1; error code - $value = $this->_mapErrorCode(ord($valueData[1])); - $size = 9; - break; - } - return array( - 'value' => $value, - 'size' => $size, - ); - } - - /** - * Extract RGB color - * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4 - * - * @param string $rgb Encoded RGB value (4 bytes) - * @return array - */ - private function _readRGB($rgb) - { - // offset: 0; size 1; Red component - $r = ord($rgb{0}); - - // offset: 1; size: 1; Green component - $g = ord($rgb{1}); - - // offset: 2; size: 1; Blue component - $b = ord($rgb{2}); - - // HEX notation, e.g. 'FF00FC' - $rgb = sprintf('%02X', $r) . sprintf('%02X', $g) . sprintf('%02X', $b); - - return array('rgb' => $rgb); - } - - /** - * Read byte string (8-bit string length) - * OpenOffice documentation: 2.5.2 - * - * @param string $subData - * @return array - */ - private function _readByteStringShort($subData) - { - // offset: 0; size: 1; length of the string (character count) - $ln = ord($subData[0]); - - // offset: 1: size: var; character array (8-bit characters) - $value = $this->_decodeCodepage(substr($subData, 1, $ln)); - - return array( - 'value' => $value, - 'size' => 1 + $ln, // size in bytes of data structure - ); - } - - /** - * Read byte string (16-bit string length) - * OpenOffice documentation: 2.5.2 - * - * @param string $subData - * @return array - */ - private function _readByteStringLong($subData) - { - // offset: 0; size: 2; length of the string (character count) - $ln = $this->_GetInt2d($subData, 0); - - // offset: 2: size: var; character array (8-bit characters) - $value = $this->_decodeCodepage(substr($subData, 2)); - - //return $string; - return array( - 'value' => $value, - 'size' => 2 + $ln, // size in bytes of data structure - ); - } - - /** - * Extracts an Excel Unicode short string (8-bit string length) - * OpenOffice documentation: 2.5.3 - * function will automatically find out where the Unicode string ends. - * - * @param string $subData - * @return array - */ - private function _readUnicodeStringShort($subData) - { - $value = ''; - - // offset: 0: size: 1; length of the string (character count) - $characterCount = ord($subData[0]); - - $string = $this->_readUnicodeString(substr($subData, 1), $characterCount); - - // add 1 for the string length - $string['size'] += 1; - - return $string; - } - - /** - * Extracts an Excel Unicode long string (16-bit string length) - * OpenOffice documentation: 2.5.3 - * this function is under construction, needs to support rich text, and Asian phonetic settings - * - * @param string $subData - * @return array - */ - private function _readUnicodeStringLong($subData) - { - $value = ''; - - // offset: 0: size: 2; length of the string (character count) - $characterCount = $this->_GetInt2d($subData, 0); - - $string = $this->_readUnicodeString(substr($subData, 2), $characterCount); - - // add 2 for the string length - $string['size'] += 2; - - return $string; - } - - /** - * Read Unicode string with no string length field, but with known character count - * this function is under construction, needs to support rich text, and Asian phonetic settings - * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3 - * - * @param string $subData - * @param int $characterCount - * @return array - */ - private function _readUnicodeString($subData, $characterCount) - { - $value = ''; - - // offset: 0: size: 1; option flags - - // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit) - $isCompressed = !((0x01 & ord($subData[0])) >> 0); - - // bit: 2; mask: 0x04; Asian phonetic settings - $hasAsian = (0x04) & ord($subData[0]) >> 2; - - // bit: 3; mask: 0x08; Rich-Text settings - $hasRichText = (0x08) & ord($subData[0]) >> 3; - - // offset: 1: size: var; character array - // this offset assumes richtext and Asian phonetic settings are off which is generally wrong - // needs to be fixed - $value = $this->_encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed); - - return array( - 'value' => $value, - 'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags - ); - } - - /** - * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas. - * Example: hello"world --> "hello""world" - * - * @param string $value UTF-8 encoded string - * @return string - */ - private function _UTF8toExcelDoubleQuoted($value) - { - return '"' . str_replace('"', '""', $value) . '"'; - } - - /** - * Reads first 8 bytes of a string and return IEEE 754 float - * - * @param string $data Binary string that is at least 8 bytes long - * @return float - */ - private function _extractNumber($data) - { - $rknumhigh = $this->_GetInt4d($data, 4); - $rknumlow = $this->_GetInt4d($data, 0); - $sign = ($rknumhigh & 0x80000000) >> 31; - $exp = ($rknumhigh & 0x7ff00000) >> 20; - $mantissa = (0x100000 | ($rknumhigh & 0x000fffff)); - $mantissalow1 = ($rknumlow & 0x80000000) >> 31; - $mantissalow2 = ($rknumlow & 0x7fffffff); - $value = $mantissa / pow( 2 , (20 - ($exp - 1023))); - - if ($mantissalow1 != 0) { - $value += 1 / pow (2 , (21 - ($exp - 1023))); - } - - $value += $mantissalow2 / pow (2 , (52 - ($exp - 1023))); - if ($sign) { - $value = -1 * $value; - } - - return $value; - } - - private function _GetIEEE754($rknum) - { - if (($rknum & 0x02) != 0) { - $value = $rknum >> 2; - } - else { - // changes by mmp, info on IEEE754 encoding from - // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html - // The RK format calls for using only the most significant 30 bits - // of the 64 bit floating point value. The other 34 bits are assumed - // to be 0 so we use the upper 30 bits of $rknum as follows... - $sign = ($rknum & 0x80000000) >> 31; - $exp = ($rknum & 0x7ff00000) >> 20; - $mantissa = (0x100000 | ($rknum & 0x000ffffc)); - $value = $mantissa / pow( 2 , (20- ($exp - 1023))); - if ($sign) { - $value = -1 * $value; - } - //end of changes by mmp - } - if (($rknum & 0x01) != 0) { - $value /= 100; - } - return $value; - } - - /** - * Get UTF-8 string from (compressed or uncompressed) UTF-16 string - * - * @param string $string - * @param bool $compressed - * @return string - */ - private function _encodeUTF16($string, $compressed = '') - { - if ($compressed) { - $string = $this->_uncompressByteString($string); - } - - $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', 'UTF-16LE'); - - return $result; - } - - /** - * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8. - * - * @param string $string - * @return string - */ - private function _uncompressByteString($string) - { - $uncompressedString = ''; - for ($i = 0; $i < strlen($string); ++$i) { - $uncompressedString .= $string[$i] . "\0"; - } - - return $uncompressedString; - } - - /** - * Convert string to UTF-8. Only used for BIFF5. - * - * @param string $string - * @return string - */ - private function _decodeCodepage($string) - { - $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', $this->_codepage); - return $result; - } - - /** - * Read 16-bit unsigned integer - * - * @param string $data - * @param int $pos - * @return int - */ - private function _GetInt2d($data, $pos) - { - return ord($data[$pos]) | (ord($data[$pos + 1]) << 8); - } - - /** - * Read 32-bit signed integer - * - * @param string $data - * @param int $pos - * @return int - */ - private function _GetInt4d($data, $pos) - { - //return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | - // (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24); - - // FIX: represent numbers correctly on 64-bit system - // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334 - $_or_24 = ord($data[$pos + 3]); - if ($_or_24 >= 128) { - // negative number - $_ord_24 = -abs((256 - $_or_24) << 24); - } else { - $_ord_24 = ($_or_24 & 127) << 24; - } - return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24; - } - - /** - * Read color - * - * @param int $color Indexed color - * @return array RGB color value, example: array('rgb' => 'FF0000') - */ - private function _readColor($color) - { - if ($color <= 0x07 || $color >= 0x40) { - // special built-in color - $color = $this->_mapBuiltInColor($color); - } else if (isset($this->_palette) && isset($this->_palette[$color - 8])) { - // palette color, color index 0x08 maps to pallete index 0 - $color = $this->_palette[$color - 8]; - } else { - // default color table - if ($this->_version == self::XLS_BIFF8) { - $color = $this->_mapColor($color); - } else { - // BIFF5 - $color = $this->_mapColorBIFF5($color); - } - } - - return $color; - } - - - /** - * Map border style - * OpenOffice documentation: 2.5.11 - * - * @param int $index - * @return string - */ - private function _mapBorderStyle($index) - { - switch ($index) { - case 0x00: return PHPExcel_Style_Border::BORDER_NONE; - case 0x01: return PHPExcel_Style_Border::BORDER_THIN; - case 0x02: return PHPExcel_Style_Border::BORDER_MEDIUM; - case 0x03: return PHPExcel_Style_Border::BORDER_DASHED; - case 0x04: return PHPExcel_Style_Border::BORDER_DOTTED; - case 0x05: return PHPExcel_Style_Border::BORDER_THICK; - case 0x06: return PHPExcel_Style_Border::BORDER_DOUBLE; - case 0x07: return PHPExcel_Style_Border::BORDER_HAIR; - case 0x08: return PHPExcel_Style_Border::BORDER_MEDIUMDASHED; - case 0x09: return PHPExcel_Style_Border::BORDER_DASHDOT; - case 0x0A: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT; - case 0x0B: return PHPExcel_Style_Border::BORDER_DASHDOTDOT; - case 0x0C: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT; - case 0x0D: return PHPExcel_Style_Border::BORDER_SLANTDASHDOT; - default: return PHPExcel_Style_Border::BORDER_NONE; - } - } - - /** - * Get fill pattern from index - * OpenOffice documentation: 2.5.12 - * - * @param int $index - * @return string - */ - private function _mapFillPattern($index) - { - switch ($index) { - case 0x00: return PHPExcel_Style_Fill::FILL_NONE; - case 0x01: return PHPExcel_Style_Fill::FILL_SOLID; - case 0x02: return PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY; - case 0x03: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY; - case 0x04: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY; - case 0x05: return PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL; - case 0x06: return PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL; - case 0x07: return PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN; - case 0x08: return PHPExcel_Style_Fill::FILL_PATTERN_DARKUP; - case 0x09: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID; - case 0x0A: return PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS; - case 0x0B: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL; - case 0x0C: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL; - case 0x0D: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN; - case 0x0E: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP; - case 0x0F: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID; - case 0x10: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS; - case 0x11: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY125; - case 0x12: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625; - default: return PHPExcel_Style_Fill::FILL_NONE; - } - } - - /** - * Map error code, e.g. '#N/A' - * - * @param int $subData - * @return string - */ - private function _mapErrorCode($subData) - { - switch ($subData) { - case 0x00: return '#NULL!'; break; - case 0x07: return '#DIV/0!'; break; - case 0x0F: return '#VALUE!'; break; - case 0x17: return '#REF!'; break; - case 0x1D: return '#NAME?'; break; - case 0x24: return '#NUM!'; break; - case 0x2A: return '#N/A'; break; - default: return false; - } - } - - /** - * Map built-in color to RGB value - * - * @param int $color Indexed color - * @return array - */ - private function _mapBuiltInColor($color) - { - switch ($color) { - case 0x00: return array('rgb' => '000000'); - case 0x01: return array('rgb' => 'FFFFFF'); - case 0x02: return array('rgb' => 'FF0000'); - case 0x03: return array('rgb' => '00FF00'); - case 0x04: return array('rgb' => '0000FF'); - case 0x05: return array('rgb' => 'FFFF00'); - case 0x06: return array('rgb' => 'FF00FF'); - case 0x07: return array('rgb' => '00FFFF'); - case 0x40: return array('rgb' => '000000'); // system window text color - case 0x41: return array('rgb' => 'FFFFFF'); // system window background color - default: return array('rgb' => '000000'); - } - } - - /** - * Map color array from BIFF5 built-in color index - * - * @param int $subData - * @return array - */ - private function _mapColorBIFF5($subData) - { - switch ($subData) { - case 0x08: return array('rgb' => '000000'); - case 0x09: return array('rgb' => 'FFFFFF'); - case 0x0A: return array('rgb' => 'FF0000'); - case 0x0B: return array('rgb' => '00FF00'); - case 0x0C: return array('rgb' => '0000FF'); - case 0x0D: return array('rgb' => 'FFFF00'); - case 0x0E: return array('rgb' => 'FF00FF'); - case 0x0F: return array('rgb' => '00FFFF'); - case 0x10: return array('rgb' => '800000'); - case 0x11: return array('rgb' => '008000'); - case 0x12: return array('rgb' => '000080'); - case 0x13: return array('rgb' => '808000'); - case 0x14: return array('rgb' => '800080'); - case 0x15: return array('rgb' => '008080'); - case 0x16: return array('rgb' => 'C0C0C0'); - case 0x17: return array('rgb' => '808080'); - case 0x18: return array('rgb' => '8080FF'); - case 0x19: return array('rgb' => '802060'); - case 0x1A: return array('rgb' => 'FFFFC0'); - case 0x1B: return array('rgb' => 'A0E0F0'); - case 0x1C: return array('rgb' => '600080'); - case 0x1D: return array('rgb' => 'FF8080'); - case 0x1E: return array('rgb' => '0080C0'); - case 0x1F: return array('rgb' => 'C0C0FF'); - case 0x20: return array('rgb' => '000080'); - case 0x21: return array('rgb' => 'FF00FF'); - case 0x22: return array('rgb' => 'FFFF00'); - case 0x23: return array('rgb' => '00FFFF'); - case 0x24: return array('rgb' => '800080'); - case 0x25: return array('rgb' => '800000'); - case 0x26: return array('rgb' => '008080'); - case 0x27: return array('rgb' => '0000FF'); - case 0x28: return array('rgb' => '00CFFF'); - case 0x29: return array('rgb' => '69FFFF'); - case 0x2A: return array('rgb' => 'E0FFE0'); - case 0x2B: return array('rgb' => 'FFFF80'); - case 0x2C: return array('rgb' => 'A6CAF0'); - case 0x2D: return array('rgb' => 'DD9CB3'); - case 0x2E: return array('rgb' => 'B38FEE'); - case 0x2F: return array('rgb' => 'E3E3E3'); - case 0x30: return array('rgb' => '2A6FF9'); - case 0x31: return array('rgb' => '3FB8CD'); - case 0x32: return array('rgb' => '488436'); - case 0x33: return array('rgb' => '958C41'); - case 0x34: return array('rgb' => '8E5E42'); - case 0x35: return array('rgb' => 'A0627A'); - case 0x36: return array('rgb' => '624FAC'); - case 0x37: return array('rgb' => '969696'); - case 0x38: return array('rgb' => '1D2FBE'); - case 0x39: return array('rgb' => '286676'); - case 0x3A: return array('rgb' => '004500'); - case 0x3B: return array('rgb' => '453E01'); - case 0x3C: return array('rgb' => '6A2813'); - case 0x3D: return array('rgb' => '85396A'); - case 0x3E: return array('rgb' => '4A3285'); - case 0x3F: return array('rgb' => '424242'); - default: return array('rgb' => '000000'); - } - } - - /** - * Map color array from BIFF8 built-in color index - * - * @param int $subData - * @return array - */ - private function _mapColor($subData) - { - switch ($subData) { - case 0x08: return array('rgb' => '000000'); - case 0x09: return array('rgb' => 'FFFFFF'); - case 0x0A: return array('rgb' => 'FF0000'); - case 0x0B: return array('rgb' => '00FF00'); - case 0x0C: return array('rgb' => '0000FF'); - case 0x0D: return array('rgb' => 'FFFF00'); - case 0x0E: return array('rgb' => 'FF00FF'); - case 0x0F: return array('rgb' => '00FFFF'); - case 0x10: return array('rgb' => '800000'); - case 0x11: return array('rgb' => '008000'); - case 0x12: return array('rgb' => '000080'); - case 0x13: return array('rgb' => '808000'); - case 0x14: return array('rgb' => '800080'); - case 0x15: return array('rgb' => '008080'); - case 0x16: return array('rgb' => 'C0C0C0'); - case 0x17: return array('rgb' => '808080'); - case 0x18: return array('rgb' => '9999FF'); - case 0x19: return array('rgb' => '993366'); - case 0x1A: return array('rgb' => 'FFFFCC'); - case 0x1B: return array('rgb' => 'CCFFFF'); - case 0x1C: return array('rgb' => '660066'); - case 0x1D: return array('rgb' => 'FF8080'); - case 0x1E: return array('rgb' => '0066CC'); - case 0x1F: return array('rgb' => 'CCCCFF'); - case 0x20: return array('rgb' => '000080'); - case 0x21: return array('rgb' => 'FF00FF'); - case 0x22: return array('rgb' => 'FFFF00'); - case 0x23: return array('rgb' => '00FFFF'); - case 0x24: return array('rgb' => '800080'); - case 0x25: return array('rgb' => '800000'); - case 0x26: return array('rgb' => '008080'); - case 0x27: return array('rgb' => '0000FF'); - case 0x28: return array('rgb' => '00CCFF'); - case 0x29: return array('rgb' => 'CCFFFF'); - case 0x2A: return array('rgb' => 'CCFFCC'); - case 0x2B: return array('rgb' => 'FFFF99'); - case 0x2C: return array('rgb' => '99CCFF'); - case 0x2D: return array('rgb' => 'FF99CC'); - case 0x2E: return array('rgb' => 'CC99FF'); - case 0x2F: return array('rgb' => 'FFCC99'); - case 0x30: return array('rgb' => '3366FF'); - case 0x31: return array('rgb' => '33CCCC'); - case 0x32: return array('rgb' => '99CC00'); - case 0x33: return array('rgb' => 'FFCC00'); - case 0x34: return array('rgb' => 'FF9900'); - case 0x35: return array('rgb' => 'FF6600'); - case 0x36: return array('rgb' => '666699'); - case 0x37: return array('rgb' => '969696'); - case 0x38: return array('rgb' => '003366'); - case 0x39: return array('rgb' => '339966'); - case 0x3A: return array('rgb' => '003300'); - case 0x3B: return array('rgb' => '333300'); - case 0x3C: return array('rgb' => '993300'); - case 0x3D: return array('rgb' => '993366'); - case 0x3E: return array('rgb' => '333399'); - case 0x3F: return array('rgb' => '333333'); - default: return array('rgb' => '000000'); - } - } - -} +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_Reader_Excel5 + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + +// Original file header of ParseXL (used as the base for this class): +// -------------------------------------------------------------------------------- +// Adapted from Excel_Spreadsheet_Reader developed by users bizon153, +// trex005, and mmp11 (SourceForge.net) +// http://sourceforge.net/projects/phpexcelreader/ +// Primary changes made by canyoncasa (dvc) for ParseXL 1.00 ... +// Modelled moreso after Perl Excel Parse/Write modules +// Added Parse_Excel_Spreadsheet object +// Reads a whole worksheet or tab as row,column array or as +// associated hash of indexed rows and named column fields +// Added variables for worksheet (tab) indexes and names +// Added an object call for loading individual woorksheets +// Changed default indexing defaults to 0 based arrays +// Fixed date/time and percent formats +// Includes patches found at SourceForge... +// unicode patch by nobody +// unpack("d") machine depedency patch by matchy +// boundsheet utf16 patch by bjaenichen +// Renamed functions for shorter names +// General code cleanup and rigor, including <80 column width +// Included a testcase Excel file and PHP example calls +// Code works for PHP 5.x + +// Primary changes made by canyoncasa (dvc) for ParseXL 1.10 ... +// http://sourceforge.net/tracker/index.php?func=detail&aid=1466964&group_id=99160&atid=623334 +// Decoding of formula conditions, results, and tokens. +// Support for user-defined named cells added as an array "namedcells" +// Patch code for user-defined named cells supports single cells only. +// NOTE: this patch only works for BIFF8 as BIFF5-7 use a different +// external sheet reference structure + + +/** PHPExcel root directory */ +if (!defined('PHPEXCEL_ROOT')) { + /** + * @ignore + */ + define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../'); + require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php'); + PHPExcel_Autoloader::Register(); + PHPExcel_Shared_ZipStreamWrapper::register(); + // check mbstring.func_overload + if (ini_get('mbstring.func_overload') & 2) { + throw new Exception('Multibyte function overloading in PHP must be disabled for string functions (2).'); + } +} + +/** + * PHPExcel_Reader_Excel5 + * + * This class uses {@link http://sourceforge.net/projects/phpexcelreader/parseXL} + * + * @category PHPExcel + * @package PHPExcel_Reader_Excel5 + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_Reader_Excel5 implements PHPExcel_Reader_IReader +{ + // ParseXL definitions + const XLS_BIFF8 = 0x0600; + const XLS_BIFF7 = 0x0500; + const XLS_WorkbookGlobals = 0x0005; + const XLS_Worksheet = 0x0010; + + // record identifiers + const XLS_Type_FORMULA = 0x0006; + const XLS_Type_EOF = 0x000a; + const XLS_Type_PROTECT = 0x0012; + const XLS_Type_OBJECTPROTECT = 0x0063; + const XLS_Type_SCENPROTECT = 0x00dd; + const XLS_Type_PASSWORD = 0x0013; + const XLS_Type_HEADER = 0x0014; + const XLS_Type_FOOTER = 0x0015; + const XLS_Type_EXTERNSHEET = 0x0017; + const XLS_Type_DEFINEDNAME = 0x0018; + const XLS_Type_VERTICALPAGEBREAKS = 0x001a; + const XLS_Type_HORIZONTALPAGEBREAKS = 0x001b; + const XLS_Type_NOTE = 0x001c; + const XLS_Type_SELECTION = 0x001d; + const XLS_Type_DATEMODE = 0x0022; + const XLS_Type_EXTERNNAME = 0x0023; + const XLS_Type_LEFTMARGIN = 0x0026; + const XLS_Type_RIGHTMARGIN = 0x0027; + const XLS_Type_TOPMARGIN = 0x0028; + const XLS_Type_BOTTOMMARGIN = 0x0029; + const XLS_Type_PRINTGRIDLINES = 0x002b; + const XLS_Type_FILEPASS = 0x002f; + const XLS_Type_FONT = 0x0031; + const XLS_Type_CONTINUE = 0x003c; + const XLS_Type_PANE = 0x0041; + const XLS_Type_CODEPAGE = 0x0042; + const XLS_Type_DEFCOLWIDTH = 0x0055; + const XLS_Type_OBJ = 0x005d; + const XLS_Type_COLINFO = 0x007d; + const XLS_Type_IMDATA = 0x007f; + const XLS_Type_SHEETPR = 0x0081; + const XLS_Type_HCENTER = 0x0083; + const XLS_Type_VCENTER = 0x0084; + const XLS_Type_SHEET = 0x0085; + const XLS_Type_PALETTE = 0x0092; + const XLS_Type_SCL = 0x00a0; + const XLS_Type_PAGESETUP = 0x00a1; + const XLS_Type_MULRK = 0x00bd; + const XLS_Type_MULBLANK = 0x00be; + const XLS_Type_DBCELL = 0x00d7; + const XLS_Type_XF = 0x00e0; + const XLS_Type_MERGEDCELLS = 0x00e5; + const XLS_Type_MSODRAWINGGROUP = 0x00eb; + const XLS_Type_MSODRAWING = 0x00ec; + const XLS_Type_SST = 0x00fc; + const XLS_Type_LABELSST = 0x00fd; + const XLS_Type_EXTSST = 0x00ff; + const XLS_Type_EXTERNALBOOK = 0x01ae; + const XLS_Type_DATAVALIDATIONS = 0x01b2; + const XLS_Type_TXO = 0x01b6; + const XLS_Type_HYPERLINK = 0x01b8; + const XLS_Type_DATAVALIDATION = 0x01be; + const XLS_Type_DIMENSION = 0x0200; + const XLS_Type_BLANK = 0x0201; + const XLS_Type_NUMBER = 0x0203; + const XLS_Type_LABEL = 0x0204; + const XLS_Type_BOOLERR = 0x0205; + const XLS_Type_STRING = 0x0207; + const XLS_Type_ROW = 0x0208; + const XLS_Type_INDEX = 0x020b; + const XLS_Type_ARRAY = 0x0221; + const XLS_Type_DEFAULTROWHEIGHT = 0x0225; + const XLS_Type_WINDOW2 = 0x023e; + const XLS_Type_RK = 0x027e; + const XLS_Type_STYLE = 0x0293; + const XLS_Type_FORMAT = 0x041e; + const XLS_Type_SHAREDFMLA = 0x04bc; + const XLS_Type_BOF = 0x0809; + const XLS_Type_SHEETPROTECTION = 0x0867; + const XLS_Type_RANGEPROTECTION = 0x0868; + const XLS_Type_SHEETLAYOUT = 0x0862; + const XLS_Type_XFEXT = 0x087d; + const XLS_Type_UNKNOWN = 0xffff; + + /** + * Read data only? + * + * @var boolean + */ + private $_readDataOnly = false; + + /** + * Restict which sheets should be loaded? + * + * @var array + */ + private $_loadSheetsOnly = null; + + /** + * PHPExcel_Reader_IReadFilter instance + * + * @var PHPExcel_Reader_IReadFilter + */ + private $_readFilter = null; + + /** + * Summary Information stream data. + * + * @var string + */ + private $_summaryInformation; + + /** + * Workbook stream data. (Includes workbook globals substream as well as sheet substreams) + * + * @var string + */ + private $_data; + + /** + * Size in bytes of $this->_data + * + * @var int + */ + private $_dataSize; + + /** + * Current position in stream + * + * @var integer + */ + private $_pos; + + /** + * Workbook to be returned by the reader. + * + * @var PHPExcel + */ + private $_phpExcel; + + /** + * Worksheet that is currently being built by the reader. + * + * @var PHPExcel_Worksheet + */ + private $_phpSheet; + + /** + * BIFF version + * + * @var int + */ + private $_version; + + /** + * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95) + * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE' + * + * @var string + */ + private $_codepage; + + /** + * Shared formats + * + * @var array + */ + private $_formats; + + /** + * Shared fonts + * + * @var array + */ + private $_objFonts; + + /** + * Color palette + * + * @var array + */ + private $_palette; + + /** + * Worksheets + * + * @var array + */ + private $_sheets; + + /** + * External books + * + * @var array + */ + private $_externalBooks; + + /** + * REF structures. Only applies to BIFF8. + * + * @var array + */ + private $_ref; + + /** + * External names + * + * @var array + */ + private $_externalNames; + + /** + * Defined names + * + * @var array + */ + private $_definedname; + + /** + * Shared strings. Only applies to BIFF8. + * + * @var array + */ + private $_sst; + + /** + * Panes are frozen? (in sheet currently being read). See WINDOW2 record. + * + * @var boolean + */ + private $_frozen; + + /** + * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record. + * + * @var boolean + */ + private $_isFitToPages; + + /** + * Objects. One OBJ record contributes with one entry. + * + * @var array + */ + private $_objs; + + /** + * The combined MSODRAWINGGROUP data + * + * @var string + */ + private $_drawingGroupData; + + /** + * The combined MSODRAWING data (per sheet) + * + * @var string + */ + private $_drawingData; + + /** + * Keep track of XF index + * + * @var int + */ + private $_xfIndex; + + /** + * Mapping of XF index (that is a cell XF) to final index in cellXf collection + * + * @var array + */ + private $_mapCellXfIndex; + + /** + * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection + * + * @var array + */ + private $_mapCellStyleXfIndex; + + /** + * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value. + * + * @var array + */ + private $_sharedFormulas; + + /** + * The shared formula parts in a sheet. One FORMULA record contributes with one value if it + * refers to a shared formula. + * + * @var array + */ + private $_sharedFormulaParts; + + /** + * Read data only? + * + * @return boolean + */ + public function getReadDataOnly() + { + return $this->_readDataOnly; + } + + /** + * Set read data only + * + * @param boolean $pValue + * @return PHPExcel_Reader_Excel5 + */ + public function setReadDataOnly($pValue = false) + { + $this->_readDataOnly = $pValue; + return $this; + } + + /** + * Get which sheets to load + * + * @return mixed + */ + public function getLoadSheetsOnly() + { + return $this->_loadSheetsOnly; + } + + /** + * Set which sheets to load + * + * @param mixed $value + * @return PHPExcel_Reader_Excel5 + */ + public function setLoadSheetsOnly($value = null) + { + $this->_loadSheetsOnly = is_array($value) ? + $value : array($value); + return $this; + } + + /** + * Set all sheets to load + * + * @return PHPExcel_Reader_Excel5 + */ + public function setLoadAllSheets() + { + $this->_loadSheetsOnly = null; + return $this; + } + + /** + * Read filter + * + * @return PHPExcel_Reader_IReadFilter + */ + public function getReadFilter() { + return $this->_readFilter; + } + + /** + * Set read filter + * + * @param PHPExcel_Reader_IReadFilter $pValue + * @return PHPExcel_Reader_Excel5 + */ + public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) { + $this->_readFilter = $pValue; + return $this; + } + + /** + * Create a new PHPExcel_Reader_Excel5 instance + */ + public function __construct() { + $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter(); + } + + /** + * Can the current PHPExcel_Reader_IReader read the file? + * + * @param string $pFileName + * @return boolean + */ + public function canRead($pFilename) + { + // Check if file exists + if (!file_exists($pFilename)) { + throw new Exception("Could not open " . $pFilename . " for reading! File does not exist."); + } + + try { + // Use ParseXL for the hard work. + $ole = new PHPExcel_Shared_OLERead(); + + // get excel data + $res = $ole->read($pFilename); + return true; + + } catch (Exception $e) { + return false; + } + } + + /** + * Loads PHPExcel from file + * + * @param string $pFilename + * @return PHPExcel + * @throws Exception + */ + public function load($pFilename) + { + // Read the OLE file + $this->_loadOLE($pFilename); + + // Initialisations + $this->_phpExcel = new PHPExcel; + $this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet + if (!$this->_readDataOnly) { + $this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style + $this->_phpExcel->removeCellXfByIndex(0); // remove the default style + } + + // Read the summary information stream (containing meta data) + $this->_readSummaryInformation(); + + // total byte size of Excel data (workbook global substream + sheet substreams) + $this->_dataSize = strlen($this->_data); + + // initialize + $this->_pos = 0; + $this->_codepage = 'CP1252'; + $this->_formats = array(); + $this->_objFonts = array(); + $this->_palette = array(); + $this->_sheets = array(); + $this->_externalBooks = array(); + $this->_ref = array(); + $this->_definedname = array(); + $this->_sst = array(); + $this->_drawingGroupData = ''; + $this->_xfIndex = ''; + $this->_mapCellXfIndex = array(); + $this->_mapCellStyleXfIndex = array(); + + // Parse Workbook Global Substream + while ($this->_pos < $this->_dataSize) { + $code = $this->_GetInt2d($this->_data, $this->_pos); + + switch ($code) { + case self::XLS_Type_BOF: $this->_readBof(); break; + case self::XLS_Type_FILEPASS: $this->_readFilepass(); break; + case self::XLS_Type_CODEPAGE: $this->_readCodepage(); break; + case self::XLS_Type_DATEMODE: $this->_readDateMode(); break; + case self::XLS_Type_FONT: $this->_readFont(); break; + case self::XLS_Type_FORMAT: $this->_readFormat(); break; + case self::XLS_Type_XF: $this->_readXf(); break; + case self::XLS_Type_XFEXT: $this->_readXfExt(); break; + case self::XLS_Type_STYLE: $this->_readStyle(); break; + case self::XLS_Type_PALETTE: $this->_readPalette(); break; + case self::XLS_Type_SHEET: $this->_readSheet(); break; + case self::XLS_Type_EXTERNALBOOK: $this->_readExternalBook(); break; + case self::XLS_Type_EXTERNNAME: $this->_readExternName(); break; + case self::XLS_Type_EXTERNSHEET: $this->_readExternSheet(); break; + case self::XLS_Type_DEFINEDNAME: $this->_readDefinedName(); break; + case self::XLS_Type_MSODRAWINGGROUP: $this->_readMsoDrawingGroup(); break; + case self::XLS_Type_SST: $this->_readSst(); break; + case self::XLS_Type_EOF: $this->_readDefault(); break 2; + default: $this->_readDefault(); break; + } + } + + // Resolve indexed colors for font, fill, and border colors + // Cannot be resolved already in XF record, because PALETTE record comes afterwards + if (!$this->_readDataOnly) { + foreach ($this->_objFonts as $objFont) { + if (isset($objFont->colorIndex)) { + $color = $this->_readColor($objFont->colorIndex); + $objFont->getColor()->setRGB($color['rgb']); + } + } + + foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) { + // fill start and end color + $fill = $objStyle->getFill(); + + if (isset($fill->startcolorIndex)) { + $startColor = $this->_readColor($fill->startcolorIndex); + $fill->getStartColor()->setRGB($startColor['rgb']); + } + + if (isset($fill->endcolorIndex)) { + $endColor = $this->_readColor($fill->endcolorIndex); + $fill->getEndColor()->setRGB($endColor['rgb']); + } + + // border colors + $top = $objStyle->getBorders()->getTop(); + $right = $objStyle->getBorders()->getRight(); + $bottom = $objStyle->getBorders()->getBottom(); + $left = $objStyle->getBorders()->getLeft(); + $diagonal = $objStyle->getBorders()->getDiagonal(); + + if (isset($top->colorIndex)) { + $borderTopColor = $this->_readColor($top->colorIndex); + $top->getColor()->setRGB($borderTopColor['rgb']); + } + + if (isset($right->colorIndex)) { + $borderRightColor = $this->_readColor($right->colorIndex); + $right->getColor()->setRGB($borderRightColor['rgb']); + } + + if (isset($bottom->colorIndex)) { + $borderBottomColor = $this->_readColor($bottom->colorIndex); + $bottom->getColor()->setRGB($borderBottomColor['rgb']); + } + + if (isset($left->colorIndex)) { + $borderLeftColor = $this->_readColor($left->colorIndex); + $left->getColor()->setRGB($borderLeftColor['rgb']); + } + + if (isset($diagonal->colorIndex)) { + $borderDiagonalColor = $this->_readColor($diagonal->colorIndex); + $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']); + } + } + } + + // treat MSODRAWINGGROUP records, workbook-level Escher + if (!$this->_readDataOnly && $this->_drawingGroupData) { + $escherWorkbook = new PHPExcel_Shared_Escher(); + $reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook); + $escherWorkbook = $reader->load($this->_drawingGroupData); + + // debug Escher stream + //$debug = new Debug_Escher(new PHPExcel_Shared_Escher()); + //$debug->load($this->_drawingGroupData); + } + + // Parse the individual sheets + foreach ($this->_sheets as $sheet) { + + if ($sheet['sheetType'] != 0x00) { + // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module + continue; + } + + // check if sheet should be skipped + if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) { + continue; + } + + // add sheet to PHPExcel object + $this->_phpSheet = $this->_phpExcel->createSheet(); + $this->_phpSheet->setTitle($sheet['name']); + $this->_phpSheet->setSheetState($sheet['sheetState']); + + $this->_pos = $sheet['offset']; + + // Initialize isFitToPages. May change after reading SHEETPR record. + $this->_isFitToPages = false; + + // Initialize drawingData + $this->_drawingData = ''; + + // Initialize objs + $this->_objs = array(); + + // Initialize shared formula parts + $this->_sharedFormulaParts = array(); + + // Initialize shared formulas + $this->_sharedFormulas = array(); + + while ($this->_pos <= $this->_dataSize - 4) { + $code = $this->_GetInt2d($this->_data, $this->_pos); + + switch ($code) { + case self::XLS_Type_BOF: $this->_readBof(); break; + case self::XLS_Type_PRINTGRIDLINES: $this->_readPrintGridlines(); break; + case self::XLS_Type_DEFAULTROWHEIGHT: $this->_readDefaultRowHeight(); break; + case self::XLS_Type_SHEETPR: $this->_readSheetPr(); break; + case self::XLS_Type_HORIZONTALPAGEBREAKS: $this->_readHorizontalPageBreaks(); break; + case self::XLS_Type_VERTICALPAGEBREAKS: $this->_readVerticalPageBreaks(); break; + case self::XLS_Type_HEADER: $this->_readHeader(); break; + case self::XLS_Type_FOOTER: $this->_readFooter(); break; + case self::XLS_Type_HCENTER: $this->_readHcenter(); break; + case self::XLS_Type_VCENTER: $this->_readVcenter(); break; + case self::XLS_Type_LEFTMARGIN: $this->_readLeftMargin(); break; + case self::XLS_Type_RIGHTMARGIN: $this->_readRightMargin(); break; + case self::XLS_Type_TOPMARGIN: $this->_readTopMargin(); break; + case self::XLS_Type_BOTTOMMARGIN: $this->_readBottomMargin(); break; + case self::XLS_Type_PAGESETUP: $this->_readPageSetup(); break; + case self::XLS_Type_PROTECT: $this->_readProtect(); break; + case self::XLS_Type_SCENPROTECT: $this->_readScenProtect(); break; + case self::XLS_Type_OBJECTPROTECT: $this->_readObjectProtect(); break; + case self::XLS_Type_PASSWORD: $this->_readPassword(); break; + case self::XLS_Type_DEFCOLWIDTH: $this->_readDefColWidth(); break; + case self::XLS_Type_COLINFO: $this->_readColInfo(); break; + case self::XLS_Type_DIMENSION: $this->_readDefault(); break; + case self::XLS_Type_ROW: $this->_readRow(); break; + case self::XLS_Type_DBCELL: $this->_readDefault(); break; + case self::XLS_Type_RK: $this->_readRk(); break; + case self::XLS_Type_LABELSST: $this->_readLabelSst(); break; + case self::XLS_Type_MULRK: $this->_readMulRk(); break; + case self::XLS_Type_NUMBER: $this->_readNumber(); break; + case self::XLS_Type_FORMULA: $this->_readFormula(); break; + case self::XLS_Type_SHAREDFMLA: $this->_readSharedFmla(); break; + case self::XLS_Type_BOOLERR: $this->_readBoolErr(); break; + case self::XLS_Type_MULBLANK: $this->_readMulBlank(); break; + case self::XLS_Type_LABEL: $this->_readLabel(); break; + case self::XLS_Type_BLANK: $this->_readBlank(); break; + case self::XLS_Type_MSODRAWING: $this->_readMsoDrawing(); break; + case self::XLS_Type_OBJ: $this->_readObj(); break; + case self::XLS_Type_WINDOW2: $this->_readWindow2(); break; + case self::XLS_Type_SCL: $this->_readScl(); break; + case self::XLS_Type_PANE: $this->_readPane(); break; + case self::XLS_Type_SELECTION: $this->_readSelection(); break; + case self::XLS_Type_MERGEDCELLS: $this->_readMergedCells(); break; + case self::XLS_Type_HYPERLINK: $this->_readHyperLink(); break; + case self::XLS_Type_DATAVALIDATIONS: $this->_readDataValidations(); break; + case self::XLS_Type_DATAVALIDATION: $this->_readDataValidation(); break; + case self::XLS_Type_SHEETLAYOUT: $this->_readSheetLayout(); break; + case self::XLS_Type_SHEETPROTECTION: $this->_readSheetProtection(); break; + case self::XLS_Type_RANGEPROTECTION: $this->_readRangeProtection(); break; + //case self::XLS_Type_IMDATA: $this->_readImData(); break; + case self::XLS_Type_CONTINUE: $this->_readContinue(); break; + case self::XLS_Type_EOF: $this->_readDefault(); break 2; + default: $this->_readDefault(); break; + } + + } + + // treat MSODRAWING records, sheet-level Escher + if (!$this->_readDataOnly && $this->_drawingData) { + $escherWorksheet = new PHPExcel_Shared_Escher(); + $reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet); + $escherWorksheet = $reader->load($this->_drawingData); + + // debug Escher stream + //$debug = new Debug_Escher(new PHPExcel_Shared_Escher()); + //$debug->load($this->_drawingData); + + // get all spContainers in one long array, so they can be mapped to OBJ records + $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers(); + } + + // treat OBJ records + foreach ($this->_objs as $n => $obj) { + + // the first shape container never has a corresponding OBJ record, hence $n + 1 + $spContainer = $allSpContainers[$n + 1]; + + // we skip all spContainers that are a part of a group shape since we cannot yet handle those + if ($spContainer->getNestingLevel() > 1) { + continue; + } + + // calculate the width and height of the shape + list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates()); + list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates()); + + $startOffsetX = $spContainer->getStartOffsetX(); + $startOffsetY = $spContainer->getStartOffsetY(); + $endOffsetX = $spContainer->getEndOffsetX(); + $endOffsetY = $spContainer->getEndOffsetY(); + + $width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX); + $height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY); + + // calculate offsetX and offsetY of the shape + $offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024; + $offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256; + + switch ($obj['type']) { + + case 0x08: + // picture + + // get index to BSE entry (1-based) + $BSEindex = $spContainer->getOPT(0x0104); + $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection(); + $BSE = $BSECollection[$BSEindex - 1]; + $blipType = $BSE->getBlipType(); + + // need check because some blip types are not supported by Escher reader such as EMF + if ($blip = $BSE->getBlip()) { + $ih = imagecreatefromstring($blip->getData()); + $drawing = new PHPExcel_Worksheet_MemoryDrawing(); + $drawing->setImageResource($ih); + + // width, height, offsetX, offsetY + $drawing->setResizeProportional(false); + $drawing->setWidth($width); + $drawing->setHeight($height); + $drawing->setOffsetX($offsetX); + $drawing->setOffsetY($offsetY); + + switch ($blipType) { + + case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG: + $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG); + $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG); + break; + + case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG: + $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG); + $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG); + break; + } + + $drawing->setWorksheet($this->_phpSheet); + $drawing->setCoordinates($spContainer->getStartCoordinates()); + } + + break; + + default: + // other object type + break; + + } + } + + // treat SHAREDFMLA records + if ($this->_version == self::XLS_BIFF8) { + foreach ($this->_sharedFormulaParts as $cell => $baseCell) { + $formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell); + $this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA); + } + } + } + + // add the named ranges (defined names) + foreach ($this->_definedname as $definedName) { + if ($definedName['isBuiltInName']) { + switch ($definedName['name']) { + + case pack('C', 0x06): + // print area + // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2 + + $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma? + + $extractedRanges = array(); + foreach ($ranges as $range) { + // $range should look like one of these + // Foo!$C$7:$J$66 + // Bar!$A$1:$IV$2 + + $explodes = explode('!', $range); + $sheetName = $explodes[0]; + + if (count($explodes) == 2) { + $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66 + } + } + if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) { + $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2 + } + break; + + case pack('C', 0x07): + // print titles (repeating rows) + // Assuming BIFF8, there are 3 cases + // 1. repeating rows + // formula looks like this: Sheet!$A$1:$IV$2 + // rows 1-2 repeat + // 2. repeating columns + // formula looks like this: Sheet!$A$1:$B$65536 + // columns A-B repeat + // 3. both repeating rows and repeating columns + // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2 + + $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma? + + foreach ($ranges as $range) { + // $range should look like this one of these + // Sheet!$A$1:$B$65536 + // Sheet!$A$1:$IV$2 + + $explodes = explode('!', $range); + + if (count($explodes) == 2) { + if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) { + + $extractedRange = $explodes[1]; + $extractedRange = str_replace('$', '', $extractedRange); + + $coordinateStrings = explode(':', $extractedRange); + if (count($coordinateStrings) == 2) { + list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]); + list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]); + + if ($firstColumn == 'A' and $lastColumn == 'IV') { + // then we have repeating rows + $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow)); + } elseif ($firstRow == 1 and $lastRow == 65536) { + // then we have repeating columns + $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn)); + } + } + } + } + } + break; + + } + } else { + // Extract range + $explodes = explode('!', $definedName['formula']); + + if (count($explodes) == 2) { + if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) { + $extractedRange = $explodes[1]; + $extractedRange = str_replace('$', '', $extractedRange); + + $localOnly = ($definedName['scope'] == 0) ? false : true; + $scope = ($definedName['scope'] == 0) ? + null : $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'] - 1]['name']); + + $this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, $localOnly, $scope) ); + } + } + } + } + + return $this->_phpExcel; + } + + /** + * Use OLE reader to extract the relevant data streams from the OLE file + * + * @param string $pFilename + */ + private function _loadOLE($pFilename) + { + // OLE reader + $ole = new PHPExcel_Shared_OLERead(); + + // get excel data + $res = $ole->read($pFilename); + $this->_data = $ole->getWorkBook(); + + // Get summary information data + $this->_summaryInformation = $ole->getSummaryInformation(); + } + + /** + * Read summary information + */ + private function _readSummaryInformation() + { + if (!isset($this->_summaryInformation)) { + return; + } + + // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark) + // offset: 2; size: 2; + // offset: 4; size: 2; OS version + // offset: 6; size: 2; OS indicator + // offset: 8; size: 16 + // offset: 24; size: 4; section count + + // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9 + // offset: 44; size: 4 + + // section header + // offset: 48; size: 4; section length + $secLength = $this->_GetInt4d($this->_summaryInformation, 48); + + // offset: 52; size: 4; property count + $countProperties = $this->_GetInt4d($this->_summaryInformation, 52); + + // initialize code page (used to resolve string values) + $codePage = 'CP1252'; + + // offset: 56; size: var + // loop through property decarations and properties + for ($i = 0; $i < $countProperties; ++$i) { + + // offset: 56 + 8 * $i; size: 4; property ID + $id = $this->_GetInt4d($this->_summaryInformation, 56 + 8 * $i); + + // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48) + $offset = $this->_GetInt4d($this->_summaryInformation, 60 + 8 * $i); + + $type = $this->_GetInt4d($this->_summaryInformation, 48 + $offset); + + // initialize property value + $value = null; + + // extract property value based on property type + switch ($type) { + case 0x02: // 2 byte signed integer + $value = $this->_GetInt2d($this->_summaryInformation, 52 + $offset); + break; + + case 0x03: // 4 byte signed integer + $value = $this->_GetInt4d($this->_summaryInformation, 52 + $offset); + break; + + case 0x13: // 4 byte unsigned integer + // not needed yet, fix later if necessary + break; + + case 0x1E: // null-terminated string prepended by dword string length + $byteLength = $this->_GetInt4d($this->_summaryInformation, 52 + $offset); + $value = substr($this->_summaryInformation, 56 + $offset, $byteLength); + $value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage); + $value = rtrim($value); + break; + + case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601) + // PHP-time + $value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation, 52 + $offset, 8)); + break; + + case 0x47: // Clipboard format + // not needed yet, fix later if necessary + break; + } + + // Use value of property id as appropriate + switch ($id) { + case 0x01: // Code Page + $codePage = PHPExcel_Shared_CodePage::NumberToName($value); + break; + + case 0x02: // Title + $this->_phpExcel->getProperties()->setTitle($value); + break; + + case 0x03: // Subject + $this->_phpExcel->getProperties()->setSubject($value); + break; + + case 0x04: // Author (Creator) + $this->_phpExcel->getProperties()->setCreator($value); + break; + + case 0x05: // Keywords + $this->_phpExcel->getProperties()->setKeywords($value); + break; + + case 0x06: // Comments (Description) + $this->_phpExcel->getProperties()->setDescription($value); + break; + + case 0x08: // Last Saved By (LastModifiedBy) + $this->_phpExcel->getProperties()->setLastModifiedBy($value); + break; + + case 0x09: // Revision + // not supported by PHPExcel + break; + + case 0x0C: // Created + $this->_phpExcel->getProperties()->setCreated($value); + break; + + case 0x0D: // Modified + $this->_phpExcel->getProperties()->setModified($value); + break; + + case 0x12: // Name of creating application + // not supported by PHPExcel + break; + } + } + } + + /** + * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record. + */ + private function _readDefault() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + } + + /** + * Read BOF + */ + private function _readBof() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 2; size: 2; type of the following data + $substreamType = $this->_GetInt2d($recordData, 2); + + switch ($substreamType) { + case self::XLS_WorkbookGlobals: + $version = $this->_GetInt2d($recordData, 0); + if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) { + throw new Exception('Cannot read this Excel file. Version is too old.'); + } + $this->_version = $version; + break; + + case self::XLS_Worksheet: + // do not use this version information for anything + // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream + break; + + default: + // substream, e.g. chart + // just skip the entire substream + do { + $code = $this->_GetInt2d($this->_data, $this->_pos); + $this->_readDefault(); + } while ($code != self::XLS_Type_EOF && $this->_pos < $this->_dataSize); + break; + } + } + + /** + * FILEPASS + * + * This record is part of the File Protection Block. It + * contains information about the read/write password of the + * file. All record contents following this record will be + * encrypted. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readFilepass() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + throw new Exception('Cannot read encrypted file'); + } + + /** + * CODEPAGE + * + * This record stores the text encoding used to write byte + * strings, stored as MS Windows code page identifier. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readCodepage() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; code page identifier + $codepage = $this->_GetInt2d($recordData, 0); + + $this->_codepage = PHPExcel_Shared_CodePage::NumberToName($codepage); + } + + /** + * DATEMODE + * + * This record specifies the base date for displaying date + * values. All dates are stored as count of days past this + * base date. In BIFF2-BIFF4 this record is part of the + * Calculation Settings Block. In BIFF5-BIFF8 it is + * stored in the Workbook Globals Substream. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readDateMode() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; 0 = base 1900, 1 = base 1904 + PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900); + if (ord($recordData{0}) == 1) { + PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904); + } + } + + /** + * Read a FONT record + */ + private function _readFont() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + $objFont = new PHPExcel_Style_Font(); + + // offset: 0; size: 2; height of the font (in twips = 1/20 of a point) + $size = $this->_GetInt2d($recordData, 0); + $objFont->setSize($size / 20); + + // offset: 2; size: 2; option flags + // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8) + // bit: 1; mask 0x0002; italic + $isItalic = (0x0002 & $this->_GetInt2d($recordData, 2)) >> 1; + if ($isItalic) $objFont->setItalic(true); + + // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8) + // bit: 3; mask 0x0008; strike + $isStrike = (0x0008 & $this->_GetInt2d($recordData, 2)) >> 3; + if ($isStrike) $objFont->setStrikethrough(true); + + // offset: 4; size: 2; colour index + $colorIndex = $this->_GetInt2d($recordData, 4); + $objFont->colorIndex = $colorIndex; + + // offset: 6; size: 2; font weight + $weight = $this->_GetInt2d($recordData, 6); + switch ($weight) { + case 0x02BC: + $objFont->setBold(true); + break; + } + + // offset: 8; size: 2; escapement type + $escapement = $this->_GetInt2d($recordData, 8); + switch ($escapement) { + case 0x0001: + $objFont->setSuperScript(true); + break; + case 0x0002: + $objFont->setSubScript(true); + break; + } + + // offset: 10; size: 1; underline type + $underlineType = ord($recordData{10}); + switch ($underlineType) { + case 0x00: + break; // no underline + case 0x01: + $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE); + break; + case 0x02: + $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLE); + break; + case 0x21: + $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING); + break; + case 0x22: + $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING); + break; + } + + // offset: 11; size: 1; font family + // offset: 12; size: 1; character set + // offset: 13; size: 1; not used + // offset: 14; size: var; font name + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringShort(substr($recordData, 14)); + } else { + $string = $this->_readByteStringShort(substr($recordData, 14)); + } + $objFont->setName($string['value']); + + $this->_objFonts[] = $objFont; + } + } + + /** + * FORMAT + * + * This record contains information about a number format. + * All FORMAT records occur together in a sequential list. + * + * In BIFF2-BIFF4 other records referencing a FORMAT record + * contain a zero-based index into this list. From BIFF5 on + * the FORMAT record contains the index itself that will be + * used by other records. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readFormat() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + $indexCode = $this->_GetInt2d($recordData, 0); + + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringLong(substr($recordData, 2)); + } else { + // BIFF7 + $string = $this->_readByteStringShort(substr($recordData, 2)); + } + + $formatString = $string['value']; + $this->_formats[$indexCode] = $formatString; + } + } + + /** + * XF - Extended Format + * + * This record contains formatting information for cells, rows, columns or styles. + * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF + * and 1 cell XF. + * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF + * and XF record 15 is a cell XF + * We only read the first cell style XF and skip the remaining cell style XF records + * We read all cell XF records. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readXf() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + $objStyle = new PHPExcel_Style(); + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; Index to FONT record + if ($this->_GetInt2d($recordData, 0) < 4) { + $fontIndex = $this->_GetInt2d($recordData, 0); + } else { + // this has to do with that index 4 is omitted in all BIFF versions for some strange reason + // check the OpenOffice documentation of the FONT record + $fontIndex = $this->_GetInt2d($recordData, 0) - 1; + } + $objStyle->setFont($this->_objFonts[$fontIndex]); + + // offset: 2; size: 2; Index to FORMAT record + $numberFormatIndex = $this->_GetInt2d($recordData, 2); + if (isset($this->_formats[$numberFormatIndex])) { + // then we have user-defined format code + $numberformat = array('code' => $this->_formats[$numberFormatIndex]); + } elseif (($code = PHPExcel_Style_NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') { + // then we have built-in format code + $numberformat = array('code' => $code); + } else { + // we set the general format code + $numberformat = array('code' => 'General'); + } + $objStyle->getNumberFormat()->setFormatCode($numberformat['code']); + + // offset: 4; size: 2; XF type, cell protection, and parent style XF + // bit 2-0; mask 0x0007; XF_TYPE_PROT + $xfTypeProt = $this->_GetInt2d($recordData, 4); + // bit 0; mask 0x01; 1 = cell is locked + $isLocked = (0x01 & $xfTypeProt) >> 0; + $objStyle->getProtection()->setLocked($isLocked ? + PHPExcel_Style_Protection::PROTECTION_INHERIT : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED); + + // bit 1; mask 0x02; 1 = Formula is hidden + $isHidden = (0x02 & $xfTypeProt) >> 1; + $objStyle->getProtection()->setHidden($isHidden ? + PHPExcel_Style_Protection::PROTECTION_PROTECTED : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED); + + // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF + $isCellStyleXf = (0x04 & $xfTypeProt) >> 2; + + // offset: 6; size: 1; Alignment and text break + // bit 2-0, mask 0x07; horizontal alignment + $horAlign = (0x07 & ord($recordData{6})) >> 0; + switch ($horAlign) { + case 0: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_GENERAL); + break; + case 1: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT); + break; + case 2: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER); + break; + case 3: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT); + break; + case 5: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY); + break; + case 6: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS); + break; + } + // bit 3, mask 0x08; wrap text + $wrapText = (0x08 & ord($recordData{6})) >> 3; + switch ($wrapText) { + case 0: + $objStyle->getAlignment()->setWrapText(false); + break; + case 1: + $objStyle->getAlignment()->setWrapText(true); + break; + } + // bit 6-4, mask 0x70; vertical alignment + $vertAlign = (0x70 & ord($recordData{6})) >> 4; + switch ($vertAlign) { + case 0: + $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP); + break; + case 1: + $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER); + break; + case 2: + $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_BOTTOM); + break; + case 3: + $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_JUSTIFY); + break; + } + + if ($this->_version == self::XLS_BIFF8) { + // offset: 7; size: 1; XF_ROTATION: Text rotation angle + $angle = ord($recordData{7}); + $rotation = 0; + if ($angle <= 90) { + $rotation = $angle; + } else if ($angle <= 180) { + $rotation = 90 - $angle; + } else if ($angle == 255) { + $rotation = -165; + } + $objStyle->getAlignment()->setTextRotation($rotation); + + // offset: 8; size: 1; Indentation, shrink to cell size, and text direction + // bit: 3-0; mask: 0x0F; indent level + $indent = (0x0F & ord($recordData{8})) >> 0; + $objStyle->getAlignment()->setIndent($indent); + + // bit: 4; mask: 0x10; 1 = shrink content to fit into cell + $shrinkToFit = (0x10 & ord($recordData{8})) >> 4; + switch ($shrinkToFit) { + case 0: + $objStyle->getAlignment()->setShrinkToFit(false); + break; + case 1: + $objStyle->getAlignment()->setShrinkToFit(true); + break; + } + + // offset: 9; size: 1; Flags used for attribute groups + + // offset: 10; size: 4; Cell border lines and background area + // bit: 3-0; mask: 0x0000000F; left style + if ($bordersLeftStyle = $this->_mapBorderStyle((0x0000000F & $this->_GetInt4d($recordData, 10)) >> 0)) { + $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle); + } + // bit: 7-4; mask: 0x000000F0; right style + if ($bordersRightStyle = $this->_mapBorderStyle((0x000000F0 & $this->_GetInt4d($recordData, 10)) >> 4)) { + $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle); + } + // bit: 11-8; mask: 0x00000F00; top style + if ($bordersTopStyle = $this->_mapBorderStyle((0x00000F00 & $this->_GetInt4d($recordData, 10)) >> 8)) { + $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle); + } + // bit: 15-12; mask: 0x0000F000; bottom style + if ($bordersBottomStyle = $this->_mapBorderStyle((0x0000F000 & $this->_GetInt4d($recordData, 10)) >> 12)) { + $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle); + } + // bit: 22-16; mask: 0x007F0000; left color + $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $this->_GetInt4d($recordData, 10)) >> 16; + + // bit: 29-23; mask: 0x3F800000; right color + $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $this->_GetInt4d($recordData, 10)) >> 23; + + // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom + $diagonalDown = (0x40000000 & $this->_GetInt4d($recordData, 10)) >> 30 ? + true : false; + + // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right + $diagonalUp = (0x80000000 & $this->_GetInt4d($recordData, 10)) >> 31 ? + true : false; + + if ($diagonalUp == false && $diagonalDown == false) { + $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_NONE); + } elseif ($diagonalUp == true && $diagonalDown == false) { + $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_UP); + } elseif ($diagonalUp == false && $diagonalDown == true) { + $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_DOWN); + } elseif ($diagonalUp == true && $diagonalDown == true) { + $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_BOTH); + } + + // offset: 14; size: 4; + // bit: 6-0; mask: 0x0000007F; top color + $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & $this->_GetInt4d($recordData, 14)) >> 0; + + // bit: 13-7; mask: 0x00003F80; bottom color + $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & $this->_GetInt4d($recordData, 14)) >> 7; + + // bit: 20-14; mask: 0x001FC000; diagonal color + $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & $this->_GetInt4d($recordData, 14)) >> 14; + + // bit: 24-21; mask: 0x01E00000; diagonal style + if ($bordersDiagonalStyle = $this->_mapBorderStyle((0x01E00000 & $this->_GetInt4d($recordData, 14)) >> 21)) { + $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle); + } + + // bit: 31-26; mask: 0xFC000000 fill pattern + if ($fillType = $this->_mapFillPattern((0xFC000000 & $this->_GetInt4d($recordData, 14)) >> 26)) { + $objStyle->getFill()->setFillType($fillType); + } + // offset: 18; size: 2; pattern and background colour + // bit: 6-0; mask: 0x007F; color index for pattern color + $objStyle->getFill()->startcolorIndex = (0x007F & $this->_GetInt2d($recordData, 18)) >> 0; + + // bit: 13-7; mask: 0x3F80; color index for pattern background + $objStyle->getFill()->endcolorIndex = (0x3F80 & $this->_GetInt2d($recordData, 18)) >> 7; + } else { + // BIFF5 + + // offset: 7; size: 1; Text orientation and flags + $orientationAndFlags = ord($recordData{7}); + + // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation + $xfOrientation = (0x03 & $orientationAndFlags) >> 0; + switch ($xfOrientation) { + case 0: + $objStyle->getAlignment()->setTextRotation(0); + break; + case 1: + $objStyle->getAlignment()->setTextRotation(-165); + break; + case 2: + $objStyle->getAlignment()->setTextRotation(90); + break; + case 3: + $objStyle->getAlignment()->setTextRotation(-90); + break; + } + + // offset: 8; size: 4; cell border lines and background area + $borderAndBackground = $this->_GetInt4d($recordData, 8); + + // bit: 6-0; mask: 0x0000007F; color index for pattern color + $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0; + + // bit: 13-7; mask: 0x00003F80; color index for pattern background + $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7; + + // bit: 21-16; mask: 0x003F0000; fill pattern + $objStyle->getFill()->setFillType($this->_mapFillPattern((0x003F0000 & $borderAndBackground) >> 16)); + + // bit: 24-22; mask: 0x01C00000; bottom line style + $objStyle->getBorders()->getBottom()->setBorderStyle($this->_mapBorderStyle((0x01C00000 & $borderAndBackground) >> 22)); + + // bit: 31-25; mask: 0xFE000000; bottom line color + $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25; + + // offset: 12; size: 4; cell border lines + $borderLines = $this->_GetInt4d($recordData, 12); + + // bit: 2-0; mask: 0x00000007; top line style + $objStyle->getBorders()->getTop()->setBorderStyle($this->_mapBorderStyle((0x00000007 & $borderLines) >> 0)); + + // bit: 5-3; mask: 0x00000038; left line style + $objStyle->getBorders()->getLeft()->setBorderStyle($this->_mapBorderStyle((0x00000038 & $borderLines) >> 3)); + + // bit: 8-6; mask: 0x000001C0; right line style + $objStyle->getBorders()->getRight()->setBorderStyle($this->_mapBorderStyle((0x000001C0 & $borderLines) >> 6)); + + // bit: 15-9; mask: 0x0000FE00; top line color index + $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9; + + // bit: 22-16; mask: 0x007F0000; left line color index + $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16; + + // bit: 29-23; mask: 0x3F800000; right line color index + $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23; + } + + // add cellStyleXf or cellXf and update mapping + if ($isCellStyleXf) { + // we only read one style XF record which is always the first + if ($this->_xfIndex == 0) { + $this->_phpExcel->addCellStyleXf($objStyle); + $this->_mapCellStyleXfIndex[$this->_xfIndex] = 0; + } + } else { + // we read all cell XF records + $this->_phpExcel->addCellXf($objStyle); + $this->_mapCellXfIndex[$this->_xfIndex] = count($this->_phpExcel->getCellXfCollection()) - 1; + } + + // update XF index for when we read next record + ++$this->_xfIndex; + } + } + + /** + * + */ + private function _readXfExt() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; 0x087D = repeated header + + // offset: 2; size: 2 + + // offset: 4; size: 8; not used + + // offset: 12; size: 2; record version + + // offset: 14; size: 2; index to XF record which this record modifies + $ixfe = $this->_GetInt2d($recordData, 14); + + // offset: 16; size: 2; not used + + // offset: 18; size: 2; number of extension properties that follow + $cexts = $this->_GetInt2d($recordData, 18); + + // start reading the actual extension data + $offset = 20; + while ($offset < $length) { + // extension type + $extType = $this->_GetInt2d($recordData, $offset); + + // extension length + $cb = $this->_GetInt2d($recordData, $offset + 2); + + // extension data + $extData = substr($recordData, $offset + 4, $cb); + + switch ($extType) { + case 4: // fill start color + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill(); + $fill->getStartColor()->setRGB($rgb); + unset($fill->startcolorIndex); // normal color index does not apply, discard + } + } + break; + + case 5: // fill end color + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill(); + $fill->getEndColor()->setRGB($rgb); + unset($fill->endcolorIndex); // normal color index does not apply, discard + } + } + break; + + case 7: // border color top + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $top = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getTop(); + $top->getColor()->setRGB($rgb); + unset($top->colorIndex); // normal color index does not apply, discard + } + } + break; + + case 8: // border color bottom + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $bottom = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getBottom(); + $bottom->getColor()->setRGB($rgb); + unset($bottom->colorIndex); // normal color index does not apply, discard + } + } + break; + + case 9: // border color left + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $left = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getLeft(); + $left->getColor()->setRGB($rgb); + unset($left->colorIndex); // normal color index does not apply, discard + } + } + break; + + case 10: // border color right + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $right = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getRight(); + $right->getColor()->setRGB($rgb); + unset($right->colorIndex); // normal color index does not apply, discard + } + } + break; + + case 11: // border color diagonal + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $diagonal = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getDiagonal(); + $diagonal->getColor()->setRGB($rgb); + unset($diagonal->colorIndex); // normal color index does not apply, discard + } + } + break; + + case 13: // font color + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $font = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFont(); + $font->getColor()->setRGB($rgb); + unset($font->colorIndex); // normal color index does not apply, discard + } + } + break; + } + + $offset += $cb; + } + } + + } + + /** + * Read STYLE record + */ + private function _readStyle() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; index to XF record and flag for built-in style + $ixfe = $this->_GetInt2d($recordData, 0); + + // bit: 11-0; mask 0x0FFF; index to XF record + $xfIndex = (0x0FFF & $ixfe) >> 0; + + // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style + $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15); + + if ($isBuiltIn) { + // offset: 2; size: 1; identifier for built-in style + $builtInId = ord($recordData{2}); + + switch ($builtInId) { + case 0x00: + // currently, we are not using this for anything + break; + + default: + break; + } + + } else { + // user-defined; not supported by PHPExcel + } + } + } + + /** + * Read PALETTE record + */ + private function _readPalette() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; number of following colors + $nm = $this->_GetInt2d($recordData, 0); + + // list of RGB colors + for ($i = 0; $i < $nm; ++$i) { + $rgb = substr($recordData, 2 + 4 * $i, 4); + $this->_palette[] = $this->_readRGB($rgb); + } + } + } + + /** + * SHEET + * + * This record is located in the Workbook Globals + * Substream and represents a sheet inside the workbook. + * One SHEET record is written for each sheet. It stores the + * sheet name and a stream offset to the BOF record of the + * respective Sheet Substream within the Workbook Stream. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readSheet() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 4; absolute stream position of the BOF record of the sheet + $rec_offset = $this->_GetInt4d($recordData, 0); + + // offset: 4; size: 1; sheet state + switch (ord($recordData{4})) { + case 0x00: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break; + case 0x01: $sheetState = PHPExcel_Worksheet::SHEETSTATE_HIDDEN; break; + case 0x02: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VERYHIDDEN; break; + default: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break; + } + + // offset: 5; size: 1; sheet type + $sheetType = ord($recordData{5}); + + // offset: 6; size: var; sheet name + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringShort(substr($recordData, 6)); + $rec_name = $string['value']; + } elseif ($this->_version == self::XLS_BIFF7) { + $string = $this->_readByteStringShort(substr($recordData, 6)); + $rec_name = $string['value']; + } + + $this->_sheets[] = array( + 'name' => $rec_name, + 'offset' => $rec_offset, + 'sheetState' => $sheetState, + 'sheetType' => $sheetType, + ); + } + + /** + * Read EXTERNALBOOK record + */ + private function _readExternalBook() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset within record data + $offset = 0; + + // there are 4 types of records + if (strlen($recordData) > 4) { + // external reference + // offset: 0; size: 2; number of sheet names ($nm) + $nm = $this->_GetInt2d($recordData, 0); + $offset += 2; + + // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length) + $encodedUrlString = $this->_readUnicodeStringLong(substr($recordData, 2)); + $offset += $encodedUrlString['size']; + + // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length) + $externalSheetNames = array(); + for ($i = 0; $i < $nm; ++$i) { + $externalSheetNameString = $this->_readUnicodeStringLong(substr($recordData, $offset)); + $externalSheetNames[] = $externalSheetNameString['value']; + $offset += $externalSheetNameString['size']; + } + + // store the record data + $this->_externalBooks[] = array( + 'type' => 'external', + 'encodedUrl' => $encodedUrlString['value'], + 'externalSheetNames' => $externalSheetNames, + ); + + } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) { + // internal reference + // offset: 0; size: 2; number of sheet in this document + // offset: 2; size: 2; 0x01 0x04 + $this->_externalBooks[] = array( + 'type' => 'internal', + ); + } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) { + // add-in function + // offset: 0; size: 2; 0x0001 + $this->_externalBooks[] = array( + 'type' => 'addInFunction', + ); + } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) { + // DDE links, OLE links + // offset: 0; size: 2; 0x0000 + // offset: 2; size: var; encoded source document name + $this->_externalBooks[] = array( + 'type' => 'DDEorOLE', + ); + } + } + + /** + * Read EXTERNNAME record. + */ + private function _readExternName() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // external sheet references provided for named cells + if ($this->_version == self::XLS_BIFF8) { + // offset: 0; size: 2; options + $options = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; + + // offset: 4; size: 2; not used + + // offset: 6; size: var + $nameString = $this->_readUnicodeStringShort(substr($recordData, 6)); + + // offset: var; size: var; formula data + $offset = 6 + $nameString['size']; + $formula = $this->_getFormulaFromStructure(substr($recordData, $offset)); + + $this->_externalNames[] = array( + 'name' => $nameString['value'], + 'formula' => $formula, + ); + } + } + + /** + * Read EXTERNSHEET record + */ + private function _readExternSheet() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // external sheet references provided for named cells + if ($this->_version == self::XLS_BIFF8) { + // offset: 0; size: 2; number of following ref structures + $nm = $this->_GetInt2d($recordData, 0); + for ($i = 0; $i < $nm; ++$i) { + $this->_ref[] = array( + // offset: 2 + 6 * $i; index to EXTERNALBOOK record + 'externalBookIndex' => $this->_GetInt2d($recordData, 2 + 6 * $i), + // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record + 'firstSheetIndex' => $this->_GetInt2d($recordData, 4 + 6 * $i), + // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record + 'lastSheetIndex' => $this->_GetInt2d($recordData, 6 + 6 * $i), + ); + } + } + } + + /** + * DEFINEDNAME + * + * This record is part of a Link Table. It contains the name + * and the token array of an internal defined name. Token + * arrays of defined names contain tokens with aberrant + * token classes. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readDefinedName() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8) { + // retrieves named cells + + // offset: 0; size: 2; option flags + $opts = $this->_GetInt2d($recordData, 0); + + // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name + $isBuiltInName = (0x0020 & $opts) >> 5; + + // offset: 2; size: 1; keyboard shortcut + + // offset: 3; size: 1; length of the name (character count) + $nlen = ord($recordData{3}); + + // offset: 4; size: 2; size of the formula data (it can happen that this is zero) + // note: there can also be additional data, this is not included in $flen + $flen = $this->_GetInt2d($recordData, 4); + + // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based) + $scope = $this->_GetInt2d($recordData, 8); + + // offset: 14; size: var; Name (Unicode string without length field) + $string = $this->_readUnicodeString(substr($recordData, 14), $nlen); + + // offset: var; size: $flen; formula data + $offset = 14 + $string['size']; + $formulaStructure = pack('v', $flen) . substr($recordData, $offset); + + try { + $formula = $this->_getFormulaFromStructure($formulaStructure); + } catch (Exception $e) { + $formula = ''; + } + + $this->_definedname[] = array( + 'isBuiltInName' => $isBuiltInName, + 'name' => $string['value'], + 'formula' => $formula, + 'scope' => $scope, + ); + } + } + + /** + * Read MSODRAWINGGROUP record + */ + private function _readMsoDrawingGroup() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + + // get spliced record data + $splicedRecordData = $this->_getSplicedRecordData(); + $recordData = $splicedRecordData['recordData']; + + $this->_drawingGroupData .= $recordData; + } + + /** + * SST - Shared String Table + * + * This record contains a list of all strings used anywhere + * in the workbook. Each string occurs only once. The + * workbook uses indexes into the list to reference the + * strings. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + **/ + private function _readSst() + { + // offset within (spliced) record data + $pos = 0; + + // get spliced record data + $splicedRecordData = $this->_getSplicedRecordData(); + + $recordData = $splicedRecordData['recordData']; + $spliceOffsets = $splicedRecordData['spliceOffsets']; + + // offset: 0; size: 4; total number of strings in the workbook + $pos += 4; + + // offset: 4; size: 4; number of following strings ($nm) + $nm = $this->_GetInt4d($recordData, 4); + $pos += 4; + + // loop through the Unicode strings (16-bit length) + for ($i = 0; $i < $nm; ++$i) { + + // number of characters in the Unicode string + $numChars = $this->_GetInt2d($recordData, $pos); + $pos += 2; + + // option flags + $optionFlags = ord($recordData{$pos}); + ++$pos; + + // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed + $isCompressed = (($optionFlags & 0x01) == 0) ; + + // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic + $hasAsian = (($optionFlags & 0x04) != 0); + + // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text + $hasRichText = (($optionFlags & 0x08) != 0); + + if ($hasRichText) { + // number of Rich-Text formatting runs + $formattingRuns = $this->_GetInt2d($recordData, $pos); + $pos += 2; + } + + if ($hasAsian) { + // size of Asian phonetic setting + $extendedRunLength = $this->_GetInt4d($recordData, $pos); + $pos += 4; + } + + // expected byte length of character array if not split + $len = ($isCompressed) ? $numChars : $numChars * 2; + + // look up limit position + foreach ($spliceOffsets as $spliceOffset) { + // it can happen that the string is empty, therefore we need + // <= and not just < + if ($pos <= $spliceOffset) { + $limitpos = $spliceOffset; + break; + } + } + + if ($pos + $len <= $limitpos) { + // character array is not split between records + + $retstr = substr($recordData, $pos, $len); + $pos += $len; + + } else { + // character array is split between records + + // first part of character array + $retstr = substr($recordData, $pos, $limitpos - $pos); + + $bytesRead = $limitpos - $pos; + + // remaining characters in Unicode string + $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2)); + + $pos = $limitpos; + + // keep reading the characters + while ($charsLeft > 0) { + + // look up next limit position, in case the string span more than one continue record + foreach ($spliceOffsets as $spliceOffset) { + if ($pos < $spliceOffset) { + $limitpos = $spliceOffset; + break; + } + } + + // repeated option flags + // OpenOffice.org documentation 5.21 + $option = ord($recordData{$pos}); + ++$pos; + + if ($isCompressed && ($option == 0)) { + // 1st fragment compressed + // this fragment compressed + $len = min($charsLeft, $limitpos - $pos); + $retstr .= substr($recordData, $pos, $len); + $charsLeft -= $len; + $isCompressed = true; + + } elseif (!$isCompressed && ($option != 0)) { + // 1st fragment uncompressed + // this fragment uncompressed + $len = min($charsLeft * 2, $limitpos - $pos); + $retstr .= substr($recordData, $pos, $len); + $charsLeft -= $len / 2; + $isCompressed = false; + + } elseif (!$isCompressed && ($option == 0)) { + // 1st fragment uncompressed + // this fragment compressed + $len = min($charsLeft, $limitpos - $pos); + for ($j = 0; $j < $len; ++$j) { + $retstr .= $recordData{$pos + $j} . chr(0); + } + $charsLeft -= $len; + $isCompressed = false; + + } else { + // 1st fragment compressed + // this fragment uncompressed + $newstr = ''; + for ($j = 0; $j < strlen($retstr); ++$j) { + $newstr .= $retstr[$j] . chr(0); + } + $retstr = $newstr; + $len = min($charsLeft * 2, $limitpos - $pos); + $retstr .= substr($recordData, $pos, $len); + $charsLeft -= $len / 2; + $isCompressed = false; + } + + $pos += $len; + } + } + + // convert to UTF-8 + $retstr = $this->_encodeUTF16($retstr, $isCompressed); + + // read additional Rich-Text information, if any + $fmtRuns = array(); + if ($hasRichText) { + // list of formatting runs + for ($j = 0; $j < $formattingRuns; ++$j) { + // first formatted character; zero-based + $charPos = $this->_GetInt2d($recordData, $pos + $j * 4); + + // index to font record + $fontIndex = $this->_GetInt2d($recordData, $pos + 2 + $j * 4); + + $fmtRuns[] = array( + 'charPos' => $charPos, + 'fontIndex' => $fontIndex, + ); + } + $pos += 4 * $formattingRuns; + } + + // read additional Asian phonetics information, if any + if ($hasAsian) { + // For Asian phonetic settings, we skip the extended string data + $pos += $extendedRunLength; + } + + // store the shared sting + $this->_sst[] = array( + 'value' => $retstr, + 'fmtRuns' => $fmtRuns, + ); + } + + // _getSplicedRecordData() takes care of moving current position in data stream + } + + /** + * Read PRINTGRIDLINES record + */ + private function _readPrintGridlines() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { + // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines + $printGridlines = (bool) $this->_GetInt2d($recordData, 0); + $this->_phpSheet->setPrintGridlines($printGridlines); + } + } + + /** + * Read DEFAULTROWHEIGHT record + */ + private function _readDefaultRowHeight() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; option flags + // offset: 2; size: 2; default height for unused rows, (twips 1/20 point) + $height = $this->_GetInt2d($recordData, 2); + $this->_phpSheet->getDefaultRowDimension()->setRowHeight($height / 20); + } + + /** + * Read SHEETPR record + */ + private function _readSheetPr() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2 + + // bit: 6; mask: 0x0040; 0 = outline buttons above outline group + $isSummaryBelow = (0x0040 & $this->_GetInt2d($recordData, 0)) >> 6; + $this->_phpSheet->setShowSummaryBelow($isSummaryBelow); + + // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group + $isSummaryRight = (0x0080 & $this->_GetInt2d($recordData, 0)) >> 7; + $this->_phpSheet->setShowSummaryRight($isSummaryRight); + + // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages + // this corresponds to radio button setting in page setup dialog in Excel + $this->_isFitToPages = (bool) ((0x0100 & $this->_GetInt2d($recordData, 0)) >> 8); + } + + /** + * Read HORIZONTALPAGEBREAKS record + */ + private function _readHorizontalPageBreaks() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { + + // offset: 0; size: 2; number of the following row index structures + $nm = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 6 * $nm; list of $nm row index structures + for ($i = 0; $i < $nm; ++$i) { + $r = $this->_GetInt2d($recordData, 2 + 6 * $i); + $cf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2); + $cl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4); + + // not sure why two column indexes are necessary? + $this->_phpSheet->setBreakByColumnAndRow($cf, $r, PHPExcel_Worksheet::BREAK_ROW); + } + } + } + + /** + * Read VERTICALPAGEBREAKS record + */ + private function _readVerticalPageBreaks() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { + // offset: 0; size: 2; number of the following column index structures + $nm = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 6 * $nm; list of $nm row index structures + for ($i = 0; $i < $nm; ++$i) { + $c = $this->_GetInt2d($recordData, 2 + 6 * $i); + $rf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2); + $rl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4); + + // not sure why two row indexes are necessary? + $this->_phpSheet->setBreakByColumnAndRow($c, $rf, PHPExcel_Worksheet::BREAK_COLUMN); + } + } + } + + /** + * Read HEADER record + */ + private function _readHeader() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: var + // realized that $recordData can be empty even when record exists + if ($recordData) { + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringLong($recordData); + } else { + $string = $this->_readByteStringShort($recordData); + } + + $this->_phpSheet->getHeaderFooter()->setOddHeader($string['value']); + $this->_phpSheet->getHeaderFooter()->setEvenHeader($string['value']); + } + } + } + + /** + * Read FOOTER record + */ + private function _readFooter() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: var + // realized that $recordData can be empty even when record exists + if ($recordData) { + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringLong($recordData); + } else { + $string = $this->_readByteStringShort($recordData); + } + $this->_phpSheet->getHeaderFooter()->setOddFooter($string['value']); + $this->_phpSheet->getHeaderFooter()->setEvenFooter($string['value']); + } + } + } + + /** + * Read HCENTER record + */ + private function _readHcenter() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally + $isHorizontalCentered = (bool) $this->_GetInt2d($recordData, 0); + + $this->_phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered); + } + } + + /** + * Read VCENTER record + */ + private function _readVcenter() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered + $isVerticalCentered = (bool) $this->_GetInt2d($recordData, 0); + + $this->_phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered); + } + } + + /** + * Read LEFTMARGIN record + */ + private function _readLeftMargin() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 8 + $this->_phpSheet->getPageMargins()->setLeft($this->_extractNumber($recordData)); + } + } + + /** + * Read RIGHTMARGIN record + */ + private function _readRightMargin() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 8 + $this->_phpSheet->getPageMargins()->setRight($this->_extractNumber($recordData)); + } + } + + /** + * Read TOPMARGIN record + */ + private function _readTopMargin() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 8 + $this->_phpSheet->getPageMargins()->setTop($this->_extractNumber($recordData)); + } + } + + /** + * Read BOTTOMMARGIN record + */ + private function _readBottomMargin() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 8 + $this->_phpSheet->getPageMargins()->setBottom($this->_extractNumber($recordData)); + } + } + + /** + * Read PAGESETUP record + */ + private function _readPageSetup() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; paper size + $paperSize = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; scaling factor + $scale = $this->_GetInt2d($recordData, 2); + + // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed + $fitToWidth = $this->_GetInt2d($recordData, 6); + + // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed + $fitToHeight = $this->_GetInt2d($recordData, 8); + + // offset: 10; size: 2; option flags + + // bit: 1; mask: 0x0002; 0=landscape, 1=portrait + $isPortrait = (0x0002 & $this->_GetInt2d($recordData, 10)) >> 1; + + // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init + // when this bit is set, do not use flags for those properties + $isNotInit = (0x0004 & $this->_GetInt2d($recordData, 10)) >> 2; + + if (!$isNotInit) { + $this->_phpSheet->getPageSetup()->setPaperSize($paperSize); + switch ($isPortrait) { + case 0: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE); break; + case 1: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_PORTRAIT); break; + } + + $this->_phpSheet->getPageSetup()->setScale($scale, false); + $this->_phpSheet->getPageSetup()->setFitToPage((bool) $this->_isFitToPages); + $this->_phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false); + $this->_phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false); + } + + // offset: 16; size: 8; header margin (IEEE 754 floating-point value) + $marginHeader = $this->_extractNumber(substr($recordData, 16, 8)); + $this->_phpSheet->getPageMargins()->setHeader($marginHeader); + + // offset: 24; size: 8; footer margin (IEEE 754 floating-point value) + $marginFooter = $this->_extractNumber(substr($recordData, 24, 8)); + $this->_phpSheet->getPageMargins()->setFooter($marginFooter); + } + } + + /** + * PROTECT - Sheet protection (BIFF2 through BIFF8) + * if this record is omitted, then it also means no sheet protection + */ + private function _readProtect() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly) { + return; + } + + // offset: 0; size: 2; + + // bit 0, mask 0x01; 1 = sheet is protected + $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; + $this->_phpSheet->getProtection()->setSheet((bool)$bool); + } + + /** + * SCENPROTECT + */ + private function _readScenProtect() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly) { + return; + } + + // offset: 0; size: 2; + + // bit: 0, mask 0x01; 1 = scenarios are protected + $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; + + $this->_phpSheet->getProtection()->setScenarios((bool)$bool); + } + + /** + * OBJECTPROTECT + */ + private function _readObjectProtect() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly) { + return; + } + + // offset: 0; size: 2; + + // bit: 0, mask 0x01; 1 = objects are protected + $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; + + $this->_phpSheet->getProtection()->setObjects((bool)$bool); + } + + /** + * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8) + */ + private function _readPassword() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; 16-bit hash value of password + $password = strtoupper(dechex($this->_GetInt2d($recordData, 0))); // the hashed password + $this->_phpSheet->getProtection()->setPassword($password, true); + } + } + + /** + * Read DEFCOLWIDTH record + */ + private function _readDefColWidth() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; default column width + $width = $this->_GetInt2d($recordData, 0); + if ($width != 8) { + $this->_phpSheet->getDefaultColumnDimension()->setWidth($width); + } + } + + /** + * Read COLINFO record + */ + private function _readColInfo() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; index to first column in range + $fc = $this->_GetInt2d($recordData, 0); // first column index + + // offset: 2; size: 2; index to last column in range + $lc = $this->_GetInt2d($recordData, 2); // first column index + + // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character + $width = $this->_GetInt2d($recordData, 4); + + // offset: 6; size: 2; index to XF record for default column formatting + $xfIndex = $this->_GetInt2d($recordData, 6); + + // offset: 8; size: 2; option flags + + // bit: 0; mask: 0x0001; 1= columns are hidden + $isHidden = (0x0001 & $this->_GetInt2d($recordData, 8)) >> 0; + + // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline) + $level = (0x0700 & $this->_GetInt2d($recordData, 8)) >> 8; + + // bit: 12; mask: 0x1000; 1 = collapsed + $isCollapsed = (0x1000 & $this->_GetInt2d($recordData, 8)) >> 12; + + // offset: 10; size: 2; not used + + for ($i = $fc; $i <= $lc; ++$i) { + if ($lc == 255 || $lc == 256) { + $this->_phpSheet->getDefaultColumnDimension()->setWidth($width / 256); + break; + } + $this->_phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256); + $this->_phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden); + $this->_phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level); + $this->_phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed); + $this->_phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * ROW + * + * This record contains the properties of a single row in a + * sheet. Rows and cells in a sheet are divided into blocks + * of 32 rows. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readRow() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; index of this row + $r = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to column of the first cell which is described by a cell record + + // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1 + + // offset: 6; size: 2; + + // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point + $height = (0x7FFF & $this->_GetInt2d($recordData, 6)) >> 0; + + // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height + $useDefaultHeight = (0x8000 & $this->_GetInt2d($recordData, 6)) >> 15; + + if (!$useDefaultHeight) { + $this->_phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20); + } + + // offset: 8; size: 2; not used + + // offset: 10; size: 2; not used in BIFF5-BIFF8 + + // offset: 12; size: 4; option flags and default row formatting + + // bit: 2-0: mask: 0x00000007; outline level of the row + $level = (0x00000007 & $this->_GetInt4d($recordData, 12)) >> 0; + $this->_phpSheet->getRowDimension($r + 1)->setOutlineLevel($level); + + // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed + $isCollapsed = (0x00000010 & $this->_GetInt4d($recordData, 12)) >> 4; + $this->_phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed); + + // bit: 5; mask: 0x00000020; 1 = row is hidden + $isHidden = (0x00000020 & $this->_GetInt4d($recordData, 12)) >> 5; + $this->_phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden); + + // bit: 7; mask: 0x00000080; 1 = row has explicit format + $hasExplicitFormat = (0x00000080 & $this->_GetInt4d($recordData, 12)) >> 7; + + // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record + $xfIndex = (0x0FFF0000 & $this->_GetInt4d($recordData, 12)) >> 16; + + if ($hasExplicitFormat) { + $this->_phpSheet->getRowDimension($r + 1)->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read RK record + * This record represents a cell that contains an RK value + * (encoded integer or floating-point value). If a + * floating-point value cannot be encoded to an RK value, + * a NUMBER record will be written. This record replaces the + * record INTEGER written in BIFF2. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readRk() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to column + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset: 4; size: 2; index to XF record + $xfIndex = $this->_GetInt2d($recordData, 4); + + // offset: 6; size: 4; RK value + $rknum = $this->_GetInt4d($recordData, 6); + $numValue = $this->_GetIEEE754($rknum); + + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + if (!$this->_readDataOnly) { + // add style information + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + + // add cell + $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); + } + } + + /** + * Read LABELSST record + * This record represents a cell that contains a string. It + * replaces the LABEL record and RSTRING record used in + * BIFF2-BIFF5. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readLabelSst() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to column + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset: 4; size: 2; index to XF record + $xfIndex = $this->_GetInt2d($recordData, 4); + + // offset: 6; size: 4; index to SST record + $index = $this->_GetInt4d($recordData, 6); + + // add cell + if (($fmtRuns = $this->_sst[$index]['fmtRuns']) && !$this->_readDataOnly) { + // then we should treat as rich text + $richText = new PHPExcel_RichText(); + $charPos = 0; + for ($i = 0; $i <= count($this->_sst[$index]['fmtRuns']); ++$i) { + if (isset($fmtRuns[$i])) { + $text = PHPExcel_Shared_String::Substring($this->_sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos); + $charPos = $fmtRuns[$i]['charPos']; + } else { + $text = PHPExcel_Shared_String::Substring($this->_sst[$index]['value'], $charPos, PHPExcel_Shared_String::CountCharacters($this->_sst[$index]['value'])); + } + + if (PHPExcel_Shared_String::CountCharacters($text) > 0) { + if ($i == 0) { // first text run, no style + $richText->createText($text); + } else { + $textRun = $richText->createTextRun($text); + if (isset($fmtRuns[$i - 1])) { + if ($fmtRuns[$i - 1]['fontIndex'] < 4) { + $fontIndex = $fmtRuns[$i - 1]['fontIndex']; + } else { + // this has to do with that index 4 is omitted in all BIFF versions for some strange reason + // check the OpenOffice documentation of the FONT record + $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1; + } + $textRun->setFont(clone $this->_objFonts[$fontIndex]); + } + } + } + } + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + $cell->setValueExplicit($richText, PHPExcel_Cell_DataType::TYPE_STRING); + } else { + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + $cell->setValueExplicit($this->_sst[$index]['value'], PHPExcel_Cell_DataType::TYPE_STRING); + } + + if (!$this->_readDataOnly) { + // add style information + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read MULRK record + * This record represents a cell range containing RK value + * cells. All cells are located in the same row. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readMulRk() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to first column + $colFirst = $this->_GetInt2d($recordData, 2); + + // offset: var; size: 2; index to last column + $colLast = $this->_GetInt2d($recordData, $length - 2); + $columns = $colLast - $colFirst + 1; + + // offset within record data + $offset = 4; + + for ($i = 0; $i < $columns; ++$i) { + $columnString = PHPExcel_Cell::stringFromColumnIndex($colFirst + $i); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + + // offset: var; size: 2; index to XF record + $xfIndex = $this->_GetInt2d($recordData, $offset); + + // offset: var; size: 4; RK value + $numValue = $this->_GetIEEE754($this->_GetInt4d($recordData, $offset + 2)); + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + if (!$this->_readDataOnly) { + // add style + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + + // add cell value + $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); + } + + $offset += 6; + } + } + + /** + * Read NUMBER record + * This record represents a cell that contains a + * floating-point value. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readNumber() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size 2; index to column + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset 4; size: 2; index to XF record + $xfIndex = $this->_GetInt2d($recordData, 4); + + $numValue = $this->_extractNumber(substr($recordData, 6, 8)); + + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + if (!$this->_readDataOnly) { + // add cell style + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + + // add cell value + $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); + } + } + + /** + * Read FORMULA record + perhaps a following STRING record if formula result is a string + * This record contains the token array and the result of a + * formula cell. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readFormula() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; row index + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; col index + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // offset: 20: size: variable; formula structure + $formulaStructure = substr($recordData, 20); + + // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc. + $options = $this->_GetInt2d($recordData, 14); + + // bit: 0; mask: 0x0001; 1 = recalculate always + // bit: 1; mask: 0x0002; 1 = calculate on open + // bit: 2; mask: 0x0008; 1 = part of a shared formula + $isPartOfSharedFormula = (bool) (0x0008 & $options); + + // WARNING: + // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true + // the formula data may be ordinary formula data, therefore we need to check + // explicitly for the tExp token (0x01) + $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure{2}) == 0x01; + + if ($isPartOfSharedFormula) { + // part of shared formula which means there will be a formula with a tExp token and nothing else + // get the base cell, grab tExp token + $baseRow = $this->_GetInt2d($formulaStructure, 3); + $baseCol = $this->_GetInt2d($formulaStructure, 5); + $this->_baseCell = PHPExcel_Cell::stringFromColumnIndex($baseCol). ($baseRow + 1); + } + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + + if ($isPartOfSharedFormula) { + // formula is added to this cell after the sheet has been read + $this->_sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell; + } + + // offset: 16: size: 4; not used + + // offset: 4; size: 2; XF index + $xfIndex = $this->_GetInt2d($recordData, 4); + + // offset: 6; size: 8; result of the formula + if ( (ord($recordData{6}) == 0) + && (ord($recordData{12}) == 255) + && (ord($recordData{13}) == 255) ) { + + // String formula. Result follows in appended STRING record + $dataType = PHPExcel_Cell_DataType::TYPE_STRING; + + // read possible SHAREDFMLA record + $code = $this->_GetInt2d($this->_data, $this->_pos); + if ($code == self::XLS_Type_SHAREDFMLA) { + $this->_readSharedFmla(); + } + + // read STRING record + $value = $this->_readString(); + + } elseif ((ord($recordData{6}) == 1) + && (ord($recordData{12}) == 255) + && (ord($recordData{13}) == 255)) { + + // Boolean formula. Result is in +2; 0=false, 1=true + $dataType = PHPExcel_Cell_DataType::TYPE_BOOL; + $value = (bool) ord($recordData{8}); + + } elseif ((ord($recordData{6}) == 2) + && (ord($recordData{12}) == 255) + && (ord($recordData{13}) == 255)) { + + // Error formula. Error code is in +2 + $dataType = PHPExcel_Cell_DataType::TYPE_ERROR; + $value = $this->_mapErrorCode(ord($recordData{8})); + + } elseif ((ord($recordData{6}) == 3) + && (ord($recordData{12}) == 255) + && (ord($recordData{13}) == 255)) { + + // Formula result is a null string + $dataType = PHPExcel_Cell_DataType::TYPE_NULL; + $value = ''; + + } else { + + // forumla result is a number, first 14 bytes like _NUMBER record + $dataType = PHPExcel_Cell_DataType::TYPE_NUMERIC; + $value = $this->_extractNumber(substr($recordData, 6, 8)); + + } + + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + if (!$this->_readDataOnly) { + // add cell style + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + + // store the formula + if (!$isPartOfSharedFormula) { + // not part of shared formula + // add cell value. If we can read formula, populate with formula, otherwise just used cached value + try { + if ($this->_version != self::XLS_BIFF8) { + throw new Exception('Not BIFF8. Can only read BIFF8 formulas'); + } + $formula = $this->_getFormulaFromStructure($formulaStructure); // get formula in human language + $cell->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA); + + } catch (Exception $e) { + $cell->setValueExplicit($value, $dataType); + } + } else { + if ($this->_version == self::XLS_BIFF8) { + // do nothing at this point, formula id added later in the code + } else { + $cell->setValueExplicit($value, $dataType); + } + } + + // store the cached calculated value + $cell->setCalculatedValue($value); + } + } + + /** + * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader, + * which usually contains relative references. + * These will be used to construct the formula in each shared formula part after the sheet is read. + */ + private function _readSharedFmla() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything + $cellRange = substr($recordData, 0, 6); + $cellRange = $this->_readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax + + // offset: 6, size: 1; not used + + // offset: 7, size: 1; number of existing FORMULA records for this shared formula + $no = ord($recordData{7}); + + // offset: 8, size: var; Binary token array of the shared formula + $formula = substr($recordData, 8); + + // at this point we only store the shared formula for later use + $this->_sharedFormulas[$this->_baseCell] = $formula; + + } + + /** + * Read a STRING record from current stream position and advance the stream pointer to next record + * This record is used for storing result from FORMULA record when it is a string, and + * it occurs directly after the FORMULA record + * + * @return string The string contents as UTF-8 + */ + private function _readString() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringLong($recordData); + $value = $string['value']; + } else { + $string = $this->_readByteStringLong($recordData); + $value = $string['value']; + } + + return $value; + } + + /** + * Read BOOLERR record + * This record represents a Boolean value or error value + * cell. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readBoolErr() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; row index + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; column index + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset: 4; size: 2; index to XF record + $xfIndex = $this->_GetInt2d($recordData, 4); + + // offset: 6; size: 1; the boolean value or error value + $boolErr = ord($recordData{6}); + + // offset: 7; size: 1; 0=boolean; 1=error + $isError = ord($recordData{7}); + + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + switch ($isError) { + case 0: // boolean + $value = (bool) $boolErr; + + // add cell value + $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_BOOL); + break; + + case 1: // error type + $value = $this->_mapErrorCode($boolErr); + + // add cell value + $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_ERROR); + break; + } + + if (!$this->_readDataOnly) { + // add cell style + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read MULBLANK record + * This record represents a cell range of empty cells. All + * cells are located in the same row + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readMulBlank() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to first column + $fc = $this->_GetInt2d($recordData, 2); + + // offset: 4; size: 2 x nc; list of indexes to XF records + // add style information + if (!$this->_readDataOnly) { + for ($i = 0; $i < $length / 2 - 3; ++$i) { + $columnString = PHPExcel_Cell::stringFromColumnIndex($fc + $i); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + $xfIndex = $this->_GetInt2d($recordData, 4 + 2 * $i); + $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + // offset: 6; size 2; index to last column (not needed) + } + + /** + * Read LABEL record + * This record represents a cell that contains a string. In + * BIFF8 it is usually replaced by the LABELSST record. + * Excel still uses this record, if it copies unformatted + * text cells to the clipboard. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readLabel() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to column + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset: 4; size: 2; XF index + $xfIndex = $this->_GetInt2d($recordData, 4); + + // add cell value + // todo: what if string is very long? continue record + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringLong(substr($recordData, 6)); + $value = $string['value']; + } else { + $string = $this->_readByteStringLong(substr($recordData, 6)); + $value = $string['value']; + } + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING); + + if (!$this->_readDataOnly) { + // add cell style + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read BLANK record + */ + private function _readBlank() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; row index + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; col index + $col = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($col); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset: 4; size: 2; XF index + $xfIndex = $this->_GetInt2d($recordData, 4); + + // add style information + if (!$this->_readDataOnly) { + $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + + } + + /** + * Read MSODRAWING record + */ + private function _readMsoDrawing() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + + // get spliced record data + $splicedRecordData = $this->_getSplicedRecordData(); + $recordData = $splicedRecordData['recordData']; + + $this->_drawingData .= $recordData; + } + + /** + * Read OBJ record + */ + private function _readObj() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly || $this->_version != self::XLS_BIFF8) { + return; + } + + // recordData consists of an array of subrecords looking like this: + // ft: 2 bytes; id number + // cb: 2 bytes; size in bytes of following data + // data: var; subrecord data + + // for now, we are just interested in the second subrecord containing the object type + $ot = $this->_GetInt2d($recordData, 4); + + $this->_objs[] = array( + 'type' => $ot, + ); + } + + /** + * Read WINDOW2 record + */ + private function _readWindow2() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; option flags + $options = $this->_GetInt2d($recordData, 0); + + // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines + $showGridlines = (bool) ((0x0002 & $options) >> 1); + $this->_phpSheet->setShowGridlines($showGridlines); + + // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers + $showRowColHeaders = (bool) ((0x0004 & $options) >> 2); + $this->_phpSheet->setShowRowColHeaders($showRowColHeaders); + + // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen + $this->_frozen = (bool) ((0x0008 & $options) >> 3); + + // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left + $this->_phpSheet->setRightToLeft((bool)((0x0040 & $options) >> 6)); + + // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active + $isActive = (bool) ((0x0400 & $options) >> 10); + if ($isActive) { + $this->_phpExcel->setActiveSheetIndex($this->_phpExcel->getIndex($this->_phpSheet)); + } + } + + /** + * Read SCL record + */ + private function _readScl() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; numerator of the view magnification + $numerator = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; numerator of the view magnification + $denumerator = $this->_GetInt2d($recordData, 2); + + // set the zoom scale (in percent) + $this->_phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator); + } + + /** + * Read PANE record + */ + private function _readPane() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; position of vertical split + $px = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; position of horizontal split + $py = $this->_GetInt2d($recordData, 2); + + if ($this->_frozen) { + // frozen panes + $this->_phpSheet->freezePane(PHPExcel_Cell::stringFromColumnIndex($px) . ($py + 1)); + } else { + // unfrozen panes; split windows; not supported by PHPExcel core + } + } + } + + /** + * Read SELECTION record. There is one such record for each pane in the sheet. + */ + private function _readSelection() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 1; pane identifier + $paneId = ord($recordData{0}); + + // offset: 1; size: 2; index to row of the active cell + $r = $this->_GetInt2d($recordData, 1); + + // offset: 3; size: 2; index to column of the active cell + $c = $this->_GetInt2d($recordData, 3); + + // offset: 5; size: 2; index into the following cell range list to the + // entry that contains the active cell + $index = $this->_GetInt2d($recordData, 5); + + // offset: 7; size: var; cell range address list containing all selected cell ranges + $data = substr($recordData, 7); + $cellRangeAddressList = $this->_readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax + + $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0]; + + // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!) + if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) { + $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells); + } + + // first row '1' + last row '65536' indicates that full column is selected + if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) { + $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells); + } + + // first column 'A' + last column 'IV' indicates that full row is selected + if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) { + $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells); + } + + $this->_phpSheet->setSelectedCells($selectedCells); + } + } + + /** + * MERGEDCELLS + * + * This record contains the addresses of merged cell ranges + * in the current sheet. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readMergedCells() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { + $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($recordData); + foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) { + $this->_phpSheet->mergeCells($cellRangeAddress); + } + } + } + + /** + * Read HYPERLINK record + */ + private function _readHyperLink() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer forward to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 8; cell range address of all cells containing this hyperlink + try { + $cellRange = $this->_readBIFF8CellRangeAddressFixed($recordData, 0, 8); + } catch (Exception $e) { + return; + } + + // offset: 8, size: 16; GUID of StdLink + + // offset: 24, size: 4; unknown value + + // offset: 28, size: 4; option flags + + // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL + $isFileLinkOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 0; + + // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL + $isAbsPathOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 1; + + // bit: 2 (and 4); mask: 0x00000014; 0 = no description + $hasDesc = (0x00000014 & $this->_GetInt2d($recordData, 28)) >> 2; + + // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text + $hasText = (0x00000008 & $this->_GetInt2d($recordData, 28)) >> 3; + + // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame + $hasFrame = (0x00000080 & $this->_GetInt2d($recordData, 28)) >> 7; + + // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name) + $isUNC = (0x00000100 & $this->_GetInt2d($recordData, 28)) >> 8; + + // offset within record data + $offset = 32; + + if ($hasDesc) { + // offset: 32; size: var; character count of description text + $dl = $this->_GetInt4d($recordData, 32); + // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated + $desc = $this->_encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false); + $offset += 4 + 2 * $dl; + } + if ($hasFrame) { + $fl = $this->_GetInt4d($recordData, $offset); + $offset += 4 + 2 * $fl; + } + + // detect type of hyperlink (there are 4 types) + $hyperlinkType = null; + + if ($isUNC) { + $hyperlinkType = 'UNC'; + } else if (!$isFileLinkOrUrl) { + $hyperlinkType = 'workbook'; + } else if (ord($recordData{$offset}) == 0x03) { + $hyperlinkType = 'local'; + } else if (ord($recordData{$offset}) == 0xE0) { + $hyperlinkType = 'URL'; + } + + switch ($hyperlinkType) { + case 'URL': + // section 5.58.2: Hyperlink containing a URL + // e.g. http://example.org/index.php + + // offset: var; size: 16; GUID of URL Moniker + $offset += 16; + // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word + $us = $this->_GetInt4d($recordData, $offset); + $offset += 4; + // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated + $url = $this->_encodeUTF16(substr($recordData, $offset, $us - 2), false); + $url .= $hasText ? '#' : ''; + $offset += $us; + break; + + case 'local': + // section 5.58.3: Hyperlink to local file + // examples: + // mydoc.txt + // ../../somedoc.xls#Sheet!A1 + + // offset: var; size: 16; GUI of File Moniker + $offset += 16; + + // offset: var; size: 2; directory up-level count. + $upLevelCount = $this->_GetInt2d($recordData, $offset); + $offset += 2; + + // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word + $sl = $this->_GetInt4d($recordData, $offset); + $offset += 4; + + // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string) + $shortenedFilePath = substr($recordData, $offset, $sl); + $shortenedFilePath = $this->_encodeUTF16($shortenedFilePath, true); + $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero + + $offset += $sl; + + // offset: var; size: 24; unknown sequence + $offset += 24; + + // extended file path + // offset: var; size: 4; size of the following file link field including string lenth mark + $sz = $this->_GetInt4d($recordData, $offset); + $offset += 4; + + // only present if $sz > 0 + if ($sz > 0) { + // offset: var; size: 4; size of the character array of the extended file path and name + $xl = $this->_GetInt4d($recordData, $offset); + $offset += 4; + + // offset: var; size 2; unknown + $offset += 2; + + // offset: var; size $xl; character array of the extended file path and name. + $extendedFilePath = substr($recordData, $offset, $xl); + $extendedFilePath = $this->_encodeUTF16($extendedFilePath, false); + $offset += $xl; + } + + // construct the path + $url = str_repeat('..\\', $upLevelCount); + $url .= ($sz > 0) ? + $extendedFilePath : $shortenedFilePath; // use extended path if available + $url .= $hasText ? '#' : ''; + + break; + + + case 'UNC': + // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path + // todo: implement + return; + + case 'workbook': + // section 5.58.5: Hyperlink to the Current Workbook + // e.g. Sheet2!B1:C2, stored in text mark field + $url = 'sheet://'; + break; + + default: + return; + + } + + if ($hasText) { + // offset: var; size: 4; character count of text mark including trailing zero word + $tl = $this->_GetInt4d($recordData, $offset); + $offset += 4; + // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated + $text = $this->_encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false); + $url .= $text; + } + + // apply the hyperlink to all the relevant cells + foreach (PHPExcel_Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) { + $this->_phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url); + } + } + } + + /** + * Read DATAVALIDATIONS record + */ + private function _readDataValidations() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer forward to next record + $this->_pos += 4 + $length; + } + + /** + * Read DATAVALIDATION record + */ + private function _readDataValidation() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer forward to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly) { + return; + } + + // offset: 0; size: 4; Options + $options = $this->_GetInt4d($recordData, 0); + + // bit: 0-3; mask: 0x0000000F; type + $type = (0x0000000F & $options) >> 0; + switch ($type) { + case 0x00: $type = PHPExcel_Cell_DataValidation::TYPE_NONE; break; + case 0x01: $type = PHPExcel_Cell_DataValidation::TYPE_WHOLE; break; + case 0x02: $type = PHPExcel_Cell_DataValidation::TYPE_DECIMAL; break; + case 0x03: $type = PHPExcel_Cell_DataValidation::TYPE_LIST; break; + case 0x04: $type = PHPExcel_Cell_DataValidation::TYPE_DATE; break; + case 0x05: $type = PHPExcel_Cell_DataValidation::TYPE_TIME; break; + case 0x06: $type = PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH; break; + case 0x07: $type = PHPExcel_Cell_DataValidation::TYPE_CUSTOM; break; + } + + // bit: 4-6; mask: 0x00000070; error type + $errorStyle = (0x00000070 & $options) >> 4; + switch ($errorStyle) { + case 0x00: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_STOP; break; + case 0x01: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_WARNING; break; + case 0x02: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_INFORMATION; break; + } + + // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list) + // I have only seen cases where this is 1 + $explicitFormula = (0x00000080 & $options) >> 7; + + // bit: 8; mask: 0x00000100; 1= empty cells allowed + $allowBlank = (0x00000100 & $options) >> 8; + + // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity + $suppressDropDown = (0x00000200 & $options) >> 9; + + // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected + $showInputMessage = (0x00040000 & $options) >> 18; + + // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered + $showErrorMessage = (0x00080000 & $options) >> 19; + + // bit: 20-23; mask: 0x00F00000; condition operator + $operator = (0x00F00000 & $options) >> 20; + switch ($operator) { + case 0x00: $operator = PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN ; break; + case 0x01: $operator = PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN ; break; + case 0x02: $operator = PHPExcel_Cell_DataValidation::OPERATOR_EQUAL ; break; + case 0x03: $operator = PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL ; break; + case 0x04: $operator = PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN ; break; + case 0x05: $operator = PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN ; break; + case 0x06: $operator = PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL; break; + case 0x07: $operator = PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL ; break; + } + + // offset: 4; size: var; title of the prompt box + $offset = 4; + $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); + $promptTitle = $string['value'] !== chr(0) ? + $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: var; title of the error box + $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); + $errorTitle = $string['value'] !== chr(0) ? + $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: var; text of the prompt box + $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); + $prompt = $string['value'] !== chr(0) ? + $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: var; text of the error box + $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); + $error = $string['value'] !== chr(0) ? + $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: 2; size of the formula data for the first condition + $sz1 = $this->_GetInt2d($recordData, $offset); + $offset += 2; + + // offset: var; size: 2; not used + $offset += 2; + + // offset: var; size: $sz1; formula data for first condition (without size field) + $formula1 = substr($recordData, $offset, $sz1); + $formula1 = pack('v', $sz1) . $formula1; // prepend the length + try { + $formula1 = $this->_getFormulaFromStructure($formula1); + + // in list type validity, null characters are used as item separators + if ($type == PHPExcel_Cell_DataValidation::TYPE_LIST) { + $formula1 = str_replace(chr(0), ',', $formula1); + } + } catch (Exception $e) { + return; + } + $offset += $sz1; + + // offset: var; size: 2; size of the formula data for the first condition + $sz2 = $this->_GetInt2d($recordData, $offset); + $offset += 2; + + // offset: var; size: 2; not used + $offset += 2; + + // offset: var; size: $sz2; formula data for second condition (without size field) + $formula2 = substr($recordData, $offset, $sz2); + $formula2 = pack('v', $sz2) . $formula2; // prepend the length + try { + $formula2 = $this->_getFormulaFromStructure($formula2); + } catch (Exception $e) { + return; + } + $offset += $sz2; + + // offset: var; size: var; cell range address list with + $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList(substr($recordData, $offset)); + $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses']; + + foreach ($cellRangeAddresses as $cellRange) { + $stRange = $this->_phpSheet->shrinkRangeToFit($cellRange); + $stRange = PHPExcel_Cell::extractAllCellReferencesInRange($stRange); + foreach ($stRange as $coordinate) { + $objValidation = $this->_phpSheet->getCell($coordinate)->getDataValidation(); + $objValidation->setType($type); + $objValidation->setErrorStyle($errorStyle); + $objValidation->setAllowBlank((bool)$allowBlank); + $objValidation->setShowInputMessage((bool)$showInputMessage); + $objValidation->setShowErrorMessage((bool)$showErrorMessage); + $objValidation->setShowDropDown(!$suppressDropDown); + $objValidation->setOperator($operator); + $objValidation->setErrorTitle($errorTitle); + $objValidation->setError($error); + $objValidation->setPromptTitle($promptTitle); + $objValidation->setPrompt($prompt); + $objValidation->setFormula1($formula1); + $objValidation->setFormula2($formula2); + } + } + + } + + /** + * Read SHEETLAYOUT record. Stores sheet tab color information. + */ + private function _readSheetLayout() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // local pointer in record data + $offset = 0; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; repeated record identifier 0x0862 + + // offset: 2; size: 10; not used + + // offset: 12; size: 4; size of record data + // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?) + $sz = $this->_GetInt4d($recordData, 12); + + switch ($sz) { + case 0x14: + // offset: 16; size: 2; color index for sheet tab + $colorIndex = $this->_GetInt2d($recordData, 16); + $color = $this->_readColor($colorIndex); + $this->_phpSheet->getTabColor()->setRGB($color['rgb']); + break; + + case 0x28: + // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007 + return; + break; + } + } + } + + /** + * Read SHEETPROTECTION record (FEATHEADR) + */ + private function _readSheetProtection() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly) { + return; + } + + // offset: 0; size: 2; repeated record header + + // offset: 2; size: 2; FRT cell reference flag (=0 currently) + + // offset: 4; size: 8; Currently not used and set to 0 + + // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag) + $isf = $this->_GetInt2d($recordData, 12); + if ($isf != 2) { + return; + } + + // offset: 14; size: 1; =1 since this is a feat header + + // offset: 15; size: 4; size of rgbHdrSData + + // rgbHdrSData, assume "Enhanced Protection" + // offset: 19; size: 2; option flags + $options = $this->_GetInt2d($recordData, 19); + + // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects + $bool = (0x0001 & $options) >> 0; + $this->_phpSheet->getProtection()->setObjects(!$bool); + + // bit: 1; mask 0x0002; edit scenarios + $bool = (0x0002 & $options) >> 1; + $this->_phpSheet->getProtection()->setScenarios(!$bool); + + // bit: 2; mask 0x0004; format cells + $bool = (0x0004 & $options) >> 2; + $this->_phpSheet->getProtection()->setFormatCells(!$bool); + + // bit: 3; mask 0x0008; format columns + $bool = (0x0008 & $options) >> 3; + $this->_phpSheet->getProtection()->setFormatColumns(!$bool); + + // bit: 4; mask 0x0010; format rows + $bool = (0x0010 & $options) >> 4; + $this->_phpSheet->getProtection()->setFormatRows(!$bool); + + // bit: 5; mask 0x0020; insert columns + $bool = (0x0020 & $options) >> 5; + $this->_phpSheet->getProtection()->setInsertColumns(!$bool); + + // bit: 6; mask 0x0040; insert rows + $bool = (0x0040 & $options) >> 6; + $this->_phpSheet->getProtection()->setInsertRows(!$bool); + + // bit: 7; mask 0x0080; insert hyperlinks + $bool = (0x0080 & $options) >> 7; + $this->_phpSheet->getProtection()->setInsertHyperlinks(!$bool); + + // bit: 8; mask 0x0100; delete columns + $bool = (0x0100 & $options) >> 8; + $this->_phpSheet->getProtection()->setDeleteColumns(!$bool); + + // bit: 9; mask 0x0200; delete rows + $bool = (0x0200 & $options) >> 9; + $this->_phpSheet->getProtection()->setDeleteRows(!$bool); + + // bit: 10; mask 0x0400; select locked cells + $bool = (0x0400 & $options) >> 10; + $this->_phpSheet->getProtection()->setSelectLockedCells(!$bool); + + // bit: 11; mask 0x0800; sort cell range + $bool = (0x0800 & $options) >> 11; + $this->_phpSheet->getProtection()->setSort(!$bool); + + // bit: 12; mask 0x1000; auto filter + $bool = (0x1000 & $options) >> 12; + $this->_phpSheet->getProtection()->setAutoFilter(!$bool); + + // bit: 13; mask 0x2000; pivot tables + $bool = (0x2000 & $options) >> 13; + $this->_phpSheet->getProtection()->setPivotTables(!$bool); + + // bit: 14; mask 0x4000; select unlocked cells + $bool = (0x4000 & $options) >> 14; + $this->_phpSheet->getProtection()->setSelectUnlockedCells(!$bool); + + // offset: 21; size: 2; not used + } + + /** + * Read RANGEPROTECTION record + * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification, + * where it is referred to as FEAT record + */ + private function _readRangeProtection() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // local pointer in record data + $offset = 0; + + if (!$this->_readDataOnly) { + $offset += 12; + + // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag + $isf = $this->_GetInt2d($recordData, 12); + if ($isf != 2) { + // we only read FEAT records of type 2 + return; + } + $offset += 2; + + $offset += 5; + + // offset: 19; size: 2; count of ref ranges this feature is on + $cref = $this->_GetInt2d($recordData, 19); + $offset += 2; + + $offset += 6; + + // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record) + $cellRanges = array(); + for ($i = 0; $i < $cref; ++$i) { + try { + $cellRange = $this->_readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8)); + } catch (Exception $e) { + return; + } + $cellRanges[] = $cellRange; + $offset += 8; + } + + // offset: var; size: var; variable length of feature specific data + $rgbFeat = substr($recordData, $offset); + $offset += 4; + + // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit) + $wPassword = $this->_GetInt4d($recordData, $offset); + $offset += 4; + + // Apply range protection to sheet + if ($cellRanges) { + $this->_phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true); + } + } + } + + /** + * Read IMDATA record + */ + private function _readImData() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + + // get spliced record data + $splicedRecordData = $this->_getSplicedRecordData(); + $recordData = $splicedRecordData['recordData']; + + // UNDER CONSTRUCTION + + // offset: 0; size: 2; image format + $cf = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; environment from which the file was written + $env = $this->_GetInt2d($recordData, 2); + + // offset: 4; size: 4; length of the image data + $lcb = $this->_GetInt4d($recordData, 4); + + // offset: 8; size: var; image data + $iData = substr($recordData, 8); + + switch ($cf) { + case 0x09: // Windows bitmap format + // BITMAPCOREINFO + // 1. BITMAPCOREHEADER + // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure + $bcSize = $this->_GetInt4d($iData, 0); + var_dump($bcSize); + + // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels + $bcWidth = $this->_GetInt2d($iData, 4); + var_dump($bcWidth); + + // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels. + $bcHeight = $this->_GetInt2d($iData, 6); + var_dump($bcHeight); + $ih = imagecreatetruecolor($bcWidth, $bcHeight); + + // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1 + + // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24 + $bcBitCount = $this->_GetInt2d($iData, 10); + var_dump($bcBitCount); + + $rgbString = substr($iData, 12); + $rgbTriples = array(); + while (strlen($rgbString) > 0) { + $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString); + $rgbString = substr($rgbString, 3); + } + $x = 0; + $y = 0; + foreach ($rgbTriples as $i => $rgbTriple) { + $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']); + imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color); + $x = ($x + 1) % $bcWidth; + $y = $y + floor(($x + 1) / $bcWidth); + } + //imagepng($ih, 'image.png'); + + $drawing = new PHPExcel_Worksheet_Drawing(); + $drawing->setPath($filename); + $drawing->setWorksheet($this->_phpSheet); + + break; + + case 0x02: // Windows metafile or Macintosh PICT format + case 0x0e: // native format + default; + break; + + } + + // _getSplicedRecordData() takes care of moving current position in data stream + } + + /** + * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record + * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented. + * In this case, we must treat the CONTINUE record as a MSODRAWING record + */ + private function _readContinue() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // check if we are reading drawing data + // this is in case a free CONTINUE record occurs in other circumstances we are unaware of + if ($this->_drawingData == '') { + // move stream pointer to next record + $this->_pos += 4 + $length; + + return; + } + + // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data + if ($length < 4) { + // move stream pointer to next record + $this->_pos += 4 + $length; + + return; + } + + // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record + // look inside CONTINUE record to see if it looks like a part of an Escher stream + // we know that Escher stream may be split at least at + // 0xF003 MsofbtSpgrContainer + // 0xF004 MsofbtSpContainer + // 0xF00D MsofbtClientTextbox + $validSplitPoints = array(0xF003, 0xF004, 0xF00D); // add identifiers if we find more + + $splitPoint = $this->_GetInt2d($recordData, 2); + if (in_array($splitPoint, $validSplitPoints)) { + // get spliced record data (and move pointer to next record) + $splicedRecordData = $this->_getSplicedRecordData(); + $this->_drawingData .= $splicedRecordData['recordData']; + + return; + } + + // move stream pointer to next record + $this->_pos += 4 + $length; + + } + + + /** + * Reads a record from current position in data stream and continues reading data as long as CONTINUE + * records are found. Splices the record data pieces and returns the combined string as if record data + * is in one piece. + * Moves to next current position in data stream to start of next record different from a CONtINUE record + * + * @return array + */ + private function _getSplicedRecordData() + { + $data = ''; + $spliceOffsets = array(); + + $i = 0; + $spliceOffsets[0] = 0; + + do { + ++$i; + + // offset: 0; size: 2; identifier + $identifier = $this->_GetInt2d($this->_data, $this->_pos); + // offset: 2; size: 2; length + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $data .= substr($this->_data, $this->_pos + 4, $length); + + $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length; + + $this->_pos += 4 + $length; + $nextIdentifier = $this->_GetInt2d($this->_data, $this->_pos); + } + while ($nextIdentifier == self::XLS_Type_CONTINUE); + + $splicedData = array( + 'recordData' => $data, + 'spliceOffsets' => $spliceOffsets, + ); + + return $splicedData; + + } + + /** + * Convert formula structure into human readable Excel formula like 'A3+A5*5' + * + * @param string $formulaStructure The complete binary data for the formula + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * @return string Human readable formula + */ + private function _getFormulaFromStructure($formulaStructure, $baseCell = 'A1') + { + // offset: 0; size: 2; size of the following formula data + $sz = $this->_GetInt2d($formulaStructure, 0); + + // offset: 2; size: sz + $formulaData = substr($formulaStructure, 2, $sz); + + // for debug: dump the formula data + //echo '<xmp>'; + //echo 'size: ' . $sz . "\n"; + //echo 'the entire formula data: '; + //Debug::dump($formulaData); + //echo "\n----\n"; + + // offset: 2 + sz; size: variable (optional) + if (strlen($formulaStructure) > 2 + $sz) { + $additionalData = substr($formulaStructure, 2 + $sz); + + // for debug: dump the additional data + //echo 'the entire additional data: '; + //Debug::dump($additionalData); + //echo "\n----\n"; + + } else { + $additionalData = ''; + } + + return $this->_getFormulaFromData($formulaData, $additionalData, $baseCell); + } + + /** + * Take formula data and additional data for formula and return human readable formula + * + * @param string $formulaData The binary data for the formula itself + * @param string $additionalData Additional binary data going with the formula + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * @return string Human readable formula + */ + private function _getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1') + { + // start parsing the formula data + $tokens = array(); + + while (strlen($formulaData) > 0 and $token = $this->_getNextToken($formulaData, $baseCell)) { + $tokens[] = $token; + $formulaData = substr($formulaData, $token['size']); + + // for debug: dump the token + //var_dump($token); + } + + $formulaString = $this->_createFormulaFromTokens($tokens, $additionalData); + + return $formulaString; + } + + /** + * Take array of tokens together with additional data for formula and return human readable formula + * + * @param array $tokens + * @param array $additionalData Additional binary data going with the formula + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * @return string Human readable formula + */ + private function _createFormulaFromTokens($tokens, $additionalData) + { + // empty formula? + if (count($tokens) == 0) { + return ''; + } + + $formulaStrings = array(); + foreach ($tokens as $token) { + // initialize spaces + $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen + $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen + $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis + $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis + $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis + $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis + + switch ($token['name']) { + case 'tAdd': // addition + case 'tConcat': // addition + case 'tDiv': // division + case 'tEQ': // equaltiy + case 'tGE': // greater than or equal + case 'tGT': // greater than + case 'tIsect': // intersection + case 'tLE': // less than or equal + case 'tList': // less than or equal + case 'tLT': // less than + case 'tMul': // multiplication + case 'tNE': // multiplication + case 'tPower': // power + case 'tRange': // range + case 'tSub': // subtraction + $op2 = array_pop($formulaStrings); + $op1 = array_pop($formulaStrings); + $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2"; + unset($space0, $space1); + break; + case 'tUplus': // unary plus + case 'tUminus': // unary minus + $op = array_pop($formulaStrings); + $formulaStrings[] = "$space1$space0{$token['data']}$op"; + unset($space0, $space1); + break; + case 'tPercent': // percent sign + $op = array_pop($formulaStrings); + $formulaStrings[] = "$op$space1$space0{$token['data']}"; + unset($space0, $space1); + break; + case 'tAttrVolatile': // indicates volatile function + case 'tAttrIf': + case 'tAttrSkip': + case 'tAttrChoose': + // token is only important for Excel formula evaluator + // do nothing + break; + case 'tAttrSpace': // space / carriage return + // space will be used when next token arrives, do not alter formulaString stack + switch ($token['data']['spacetype']) { + case 'type0': + $space0 = str_repeat(' ', $token['data']['spacecount']); + break; + case 'type1': + $space1 = str_repeat("\n", $token['data']['spacecount']); + break; + case 'type2': + $space2 = str_repeat(' ', $token['data']['spacecount']); + break; + case 'type3': + $space3 = str_repeat("\n", $token['data']['spacecount']); + break; + case 'type4': + $space4 = str_repeat(' ', $token['data']['spacecount']); + break; + case 'type5': + $space5 = str_repeat("\n", $token['data']['spacecount']); + break; + } + break; + case 'tAttrSum': // SUM function with one parameter + $op = array_pop($formulaStrings); + $formulaStrings[] = "{$space1}{$space0}SUM($op)"; + unset($space0, $space1); + break; + case 'tFunc': // function with fixed number of arguments + case 'tFuncV': // function with variable number of arguments + if ($token['data']['function'] != '') { + // normal function + $ops = array(); // array of operators + for ($i = 0; $i < $token['data']['args']; ++$i) { + $ops[] = array_pop($formulaStrings); + } + $ops = array_reverse($ops); + $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ")"; + unset($space0, $space1); + } else { + // add-in function + $ops = array(); // array of operators + for ($i = 0; $i < $token['data']['args'] - 1; ++$i) { + $ops[] = array_pop($formulaStrings); + } + $ops = array_reverse($ops); + $function = array_pop($formulaStrings); + $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ")"; + unset($space0, $space1); + } + break; + case 'tParen': // parenthesis + $expression = array_pop($formulaStrings); + $formulaStrings[] = "$space3$space2($expression$space5$space4)"; + unset($space2, $space3, $space4, $space5); + break; + case 'tArray': // array constant + $constantArray = $this->_readBIFF8ConstantArray($additionalData); + $formulaStrings[] = $space1 . $space0 . $constantArray['value']; + $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data + unset($space0, $space1); + break; + case 'tMemArea': + // bite off chunk of additional data + $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($additionalData); + $additionalData = substr($additionalData, $cellRangeAddressList['size']); + $formulaStrings[] = "$space1$space0{$token['data']}"; + unset($space0, $space1); + break; + case 'tArea': // cell range address + case 'tBool': // boolean + case 'tErr': // error code + case 'tInt': // integer + case 'tMemErr': + case 'tMemFunc': + case 'tMissArg': + case 'tName': + case 'tNameX': + case 'tNum': // number + case 'tRef': // single cell reference + case 'tRef3d': // 3d cell reference + case 'tArea3d': // 3d cell range reference + case 'tRefN': + case 'tAreaN': + case 'tStr': // string + $formulaStrings[] = "$space1$space0{$token['data']}"; + unset($space0, $space1); + break; + } + } + $formulaString = $formulaStrings[0]; + + // for debug: dump the human readable formula + //echo '----' . "\n"; + //echo 'Formula: ' . $formulaString; + + return $formulaString; + } + + /** + * Fetch next token from binary formula data + * + * @param string Formula data + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * @return array + * @throws Exception + */ + private function _getNextToken($formulaData, $baseCell = 'A1') + { + // offset: 0; size: 1; token id + $id = ord($formulaData[0]); // token id + $name = false; // initialize token name + + switch ($id) { + case 0x03: $name = 'tAdd'; $size = 1; $data = '+'; break; + case 0x04: $name = 'tSub'; $size = 1; $data = '-'; break; + case 0x05: $name = 'tMul'; $size = 1; $data = '*'; break; + case 0x06: $name = 'tDiv'; $size = 1; $data = '/'; break; + case 0x07: $name = 'tPower'; $size = 1; $data = '^'; break; + case 0x08: $name = 'tConcat'; $size = 1; $data = '&'; break; + case 0x09: $name = 'tLT'; $size = 1; $data = '<'; break; + case 0x0A: $name = 'tLE'; $size = 1; $data = '<='; break; + case 0x0B: $name = 'tEQ'; $size = 1; $data = '='; break; + case 0x0C: $name = 'tGE'; $size = 1; $data = '>='; break; + case 0x0D: $name = 'tGT'; $size = 1; $data = '>'; break; + case 0x0E: $name = 'tNE'; $size = 1; $data = '<>'; break; + case 0x0F: $name = 'tIsect'; $size = 1; $data = ' '; break; + case 0x10: $name = 'tList'; $size = 1; $data = ','; break; + case 0x11: $name = 'tRange'; $size = 1; $data = ':'; break; + case 0x12: $name = 'tUplus'; $size = 1; $data = '+'; break; + case 0x13: $name = 'tUminus'; $size = 1; $data = '-'; break; + case 0x14: $name = 'tPercent'; $size = 1; $data = '%'; break; + case 0x15: // parenthesis + $name = 'tParen'; + $size = 1; + $data = null; + break; + case 0x16: // missing argument + $name = 'tMissArg'; + $size = 1; + $data = ''; + break; + case 0x17: // string + $name = 'tStr'; + // offset: 1; size: var; Unicode string, 8-bit string length + $string = $this->_readUnicodeStringShort(substr($formulaData, 1)); + $size = 1 + $string['size']; + $data = $this->_UTF8toExcelDoubleQuoted($string['value']); + break; + case 0x19: // Special attribute + // offset: 1; size: 1; attribute type flags: + switch (ord($formulaData[1])) { + case 0x01: + $name = 'tAttrVolatile'; + $size = 4; + $data = null; + break; + case 0x02: + $name = 'tAttrIf'; + $size = 4; + $data = null; + break; + case 0x04: + $name = 'tAttrChoose'; + // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1) + $nc = $this->_GetInt2d($formulaData, 2); + // offset: 4; size: 2 * $nc + // offset: 4 + 2 * $nc; size: 2 + $size = 2 * $nc + 6; + $data = null; + break; + case 0x08: + $name = 'tAttrSkip'; + $size = 4; + $data = null; + break; + case 0x10: + $name = 'tAttrSum'; + $size = 4; + $data = null; + break; + case 0x40: + case 0x41: + $name = 'tAttrSpace'; + $size = 4; + // offset: 2; size: 2; space type and position + switch (ord($formulaData[2])) { + case 0x00: + $spacetype = 'type0'; + break; + case 0x01: + $spacetype = 'type1'; + break; + case 0x02: + $spacetype = 'type2'; + break; + case 0x03: + $spacetype = 'type3'; + break; + case 0x04: + $spacetype = 'type4'; + break; + case 0x05: + $spacetype = 'type5'; + break; + default: + throw new Exception('Unrecognized space type in tAttrSpace token'); + break; + } + // offset: 3; size: 1; number of inserted spaces/carriage returns + $spacecount = ord($formulaData[3]); + + $data = array('spacetype' => $spacetype, 'spacecount' => $spacecount); + break; + default: + throw new Exception('Unrecognized attribute flag in tAttr token'); + break; + } + break; + case 0x1C: // error code + // offset: 1; size: 1; error code + $name = 'tErr'; + $size = 2; + $data = $this->_mapErrorCode(ord($formulaData[1])); + break; + case 0x1D: // boolean + // offset: 1; size: 1; 0 = false, 1 = true; + $name = 'tBool'; + $size = 2; + $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE'; + break; + case 0x1E: // integer + // offset: 1; size: 2; unsigned 16-bit integer + $name = 'tInt'; + $size = 3; + $data = $this->_GetInt2d($formulaData, 1); + break; + case 0x1F: // number + // offset: 1; size: 8; + $name = 'tNum'; + $size = 9; + $data = $this->_extractNumber(substr($formulaData, 1)); + $data = str_replace(',', '.', (string)$data); // in case non-English locale + break; + case 0x40: // array constant + case 0x60: // array constant + // offset: 1; size: 7; not used + $name = 'tArray'; + $size = 8; + $data = null; + break; + case 0x41: // function with fixed number of arguments + $name = 'tFunc'; + $size = 3; + // offset: 1; size: 2; index to built-in sheet function + switch ($this->_GetInt2d($formulaData, 1)) { + case 2: $function = 'ISNA'; $args = 1; break; + case 3: $function = 'ISERROR'; $args = 1; break; + case 10: $function = 'NA'; $args = 0; break; + case 15: $function = 'SIN'; $args = 1; break; + case 16: $function = 'COS'; $args = 1; break; + case 17: $function = 'TAN'; $args = 1; break; + case 18: $function = 'ATAN'; $args = 1; break; + case 19: $function = 'PI'; $args = 0; break; + case 20: $function = 'SQRT'; $args = 1; break; + case 21: $function = 'EXP'; $args = 1; break; + case 22: $function = 'LN'; $args = 1; break; + case 23: $function = 'LOG10'; $args = 1; break; + case 24: $function = 'ABS'; $args = 1; break; + case 25: $function = 'INT'; $args = 1; break; + case 26: $function = 'SIGN'; $args = 1; break; + case 27: $function = 'ROUND'; $args = 2; break; + case 30: $function = 'REPT'; $args = 2; break; + case 31: $function = 'MID'; $args = 3; break; + case 32: $function = 'LEN'; $args = 1; break; + case 33: $function = 'VALUE'; $args = 1; break; + case 34: $function = 'TRUE'; $args = 0; break; + case 35: $function = 'FALSE'; $args = 0; break; + case 38: $function = 'NOT'; $args = 1; break; + case 39: $function = 'MOD'; $args = 2; break; + case 40: $function = 'DCOUNT'; $args = 3; break; + case 41: $function = 'DSUM'; $args = 3; break; + case 42: $function = 'DAVERAGE'; $args = 3; break; + case 43: $function = 'DMIN'; $args = 3; break; + case 44: $function = 'DMAX'; $args = 3; break; + case 45: $function = 'DSTDEV'; $args = 3; break; + case 48: $function = 'TEXT'; $args = 2; break; + case 61: $function = 'MIRR'; $args = 3; break; + case 63: $function = 'RAND'; $args = 0; break; + case 65: $function = 'DATE'; $args = 3; break; + case 66: $function = 'TIME'; $args = 3; break; + case 67: $function = 'DAY'; $args = 1; break; + case 68: $function = 'MONTH'; $args = 1; break; + case 69: $function = 'YEAR'; $args = 1; break; + case 71: $function = 'HOUR'; $args = 1; break; + case 72: $function = 'MINUTE'; $args = 1; break; + case 73: $function = 'SECOND'; $args = 1; break; + case 74: $function = 'NOW'; $args = 0; break; + case 75: $function = 'AREAS'; $args = 1; break; + case 76: $function = 'ROWS'; $args = 1; break; + case 77: $function = 'COLUMNS'; $args = 1; break; + case 83: $function = 'TRANSPOSE'; $args = 1; break; + case 86: $function = 'TYPE'; $args = 1; break; + case 97: $function = 'ATAN2'; $args = 2; break; + case 98: $function = 'ASIN'; $args = 1; break; + case 99: $function = 'ACOS'; $args = 1; break; + case 105: $function = 'ISREF'; $args = 1; break; + case 111: $function = 'CHAR'; $args = 1; break; + case 112: $function = 'LOWER'; $args = 1; break; + case 113: $function = 'UPPER'; $args = 1; break; + case 114: $function = 'PROPER'; $args = 1; break; + case 117: $function = 'EXACT'; $args = 2; break; + case 118: $function = 'TRIM'; $args = 1; break; + case 119: $function = 'REPLACE'; $args = 4; break; + case 121: $function = 'CODE'; $args = 1; break; + case 126: $function = 'ISERR'; $args = 1; break; + case 127: $function = 'ISTEXT'; $args = 1; break; + case 128: $function = 'ISNUMBER'; $args = 1; break; + case 129: $function = 'ISBLANK'; $args = 1; break; + case 130: $function = 'T'; $args = 1; break; + case 131: $function = 'N'; $args = 1; break; + case 140: $function = 'DATEVALUE'; $args = 1; break; + case 141: $function = 'TIMEVALUE'; $args = 1; break; + case 142: $function = 'SLN'; $args = 3; break; + case 143: $function = 'SYD'; $args = 4; break; + case 162: $function = 'CLEAN'; $args = 1; break; + case 163: $function = 'MDETERM'; $args = 1; break; + case 164: $function = 'MINVERSE'; $args = 1; break; + case 165: $function = 'MMULT'; $args = 2; break; + case 184: $function = 'FACT'; $args = 1; break; + case 189: $function = 'DPRODUCT'; $args = 3; break; + case 190: $function = 'ISNONTEXT'; $args = 1; break; + case 195: $function = 'DSTDEVP'; $args = 3; break; + case 196: $function = 'DVARP'; $args = 3; break; + case 198: $function = 'ISLOGICAL'; $args = 1; break; + case 199: $function = 'DCOUNTA'; $args = 3; break; + case 207: $function = 'REPLACEB'; $args = 4; break; + case 210: $function = 'MIDB'; $args = 3; break; + case 211: $function = 'LENB'; $args = 1; break; + case 212: $function = 'ROUNDUP'; $args = 2; break; + case 213: $function = 'ROUNDDOWN'; $args = 2; break; + case 214: $function = 'ASC'; $args = 1; break; + case 215: $function = 'DBCS'; $args = 1; break; + case 221: $function = 'TODAY'; $args = 0; break; + case 229: $function = 'SINH'; $args = 1; break; + case 230: $function = 'COSH'; $args = 1; break; + case 231: $function = 'TANH'; $args = 1; break; + case 232: $function = 'ASINH'; $args = 1; break; + case 233: $function = 'ACOSH'; $args = 1; break; + case 234: $function = 'ATANH'; $args = 1; break; + case 235: $function = 'DGET'; $args = 3; break; + case 244: $function = 'INFO'; $args = 1; break; + case 252: $function = 'FREQUENCY'; $args = 2; break; + case 261: $function = 'ERROR.TYPE'; $args = 1; break; + case 271: $function = 'GAMMALN'; $args = 1; break; + case 273: $function = 'BINOMDIST'; $args = 4; break; + case 274: $function = 'CHIDIST'; $args = 2; break; + case 275: $function = 'CHIINV'; $args = 2; break; + case 276: $function = 'COMBIN'; $args = 2; break; + case 277: $function = 'CONFIDENCE'; $args = 3; break; + case 278: $function = 'CRITBINOM'; $args = 3; break; + case 279: $function = 'EVEN'; $args = 1; break; + case 280: $function = 'EXPONDIST'; $args = 3; break; + case 281: $function = 'FDIST'; $args = 3; break; + case 282: $function = 'FINV'; $args = 3; break; + case 283: $function = 'FISHER'; $args = 1; break; + case 284: $function = 'FISHERINV'; $args = 1; break; + case 285: $function = 'FLOOR'; $args = 2; break; + case 286: $function = 'GAMMADIST'; $args = 4; break; + case 287: $function = 'GAMMAINV'; $args = 3; break; + case 288: $function = 'CEILING'; $args = 2; break; + case 289: $function = 'HYPGEOMDIST'; $args = 4; break; + case 290: $function = 'LOGNORMDIST'; $args = 3; break; + case 291: $function = 'LOGINV'; $args = 3; break; + case 292: $function = 'NEGBINOMDIST'; $args = 3; break; + case 293: $function = 'NORMDIST'; $args = 4; break; + case 294: $function = 'NORMSDIST'; $args = 1; break; + case 295: $function = 'NORMINV'; $args = 3; break; + case 296: $function = 'NORMSINV'; $args = 1; break; + case 297: $function = 'STANDARDIZE'; $args = 3; break; + case 298: $function = 'ODD'; $args = 1; break; + case 299: $function = 'PERMUT'; $args = 2; break; + case 300: $function = 'POISSON'; $args = 3; break; + case 301: $function = 'TDIST'; $args = 3; break; + case 302: $function = 'WEIBULL'; $args = 4; break; + case 303: $function = 'SUMXMY2'; $args = 2; break; + case 304: $function = 'SUMX2MY2'; $args = 2; break; + case 305: $function = 'SUMX2PY2'; $args = 2; break; + case 306: $function = 'CHITEST'; $args = 2; break; + case 307: $function = 'CORREL'; $args = 2; break; + case 308: $function = 'COVAR'; $args = 2; break; + case 309: $function = 'FORECAST'; $args = 3; break; + case 310: $function = 'FTEST'; $args = 2; break; + case 311: $function = 'INTERCEPT'; $args = 2; break; + case 312: $function = 'PEARSON'; $args = 2; break; + case 313: $function = 'RSQ'; $args = 2; break; + case 314: $function = 'STEYX'; $args = 2; break; + case 315: $function = 'SLOPE'; $args = 2; break; + case 316: $function = 'TTEST'; $args = 4; break; + case 325: $function = 'LARGE'; $args = 2; break; + case 326: $function = 'SMALL'; $args = 2; break; + case 327: $function = 'QUARTILE'; $args = 2; break; + case 328: $function = 'PERCENTILE'; $args = 2; break; + case 331: $function = 'TRIMMEAN'; $args = 2; break; + case 332: $function = 'TINV'; $args = 2; break; + case 337: $function = 'POWER'; $args = 2; break; + case 342: $function = 'RADIANS'; $args = 1; break; + case 343: $function = 'DEGREES'; $args = 1; break; + case 346: $function = 'COUNTIF'; $args = 2; break; + case 347: $function = 'COUNTBLANK'; $args = 1; break; + case 350: $function = 'ISPMT'; $args = 4; break; + case 351: $function = 'DATEDIF'; $args = 3; break; + case 352: $function = 'DATESTRING'; $args = 1; break; + case 353: $function = 'NUMBERSTRING'; $args = 2; break; + case 360: $function = 'PHONETIC'; $args = 1; break; + default: + throw new Exception('Unrecognized function in formula'); + break; + } + $data = array('function' => $function, 'args' => $args); + break; + case 0x22: // function with variable number of arguments + case 0x42: // function with variable number of arguments + case 0x62: // function with variable number of arguments + $name = 'tFuncV'; + $size = 4; + // offset: 1; size: 1; number of arguments + $args = ord($formulaData[1]); + // offset: 2: size: 2; index to built-in sheet function + $index = $this->_GetInt2d($formulaData, 2); + switch ($index) { + case 0: $function = 'COUNT'; break; + case 1: $function = 'IF'; break; + case 4: $function = 'SUM'; break; + case 5: $function = 'AVERAGE'; break; + case 6: $function = 'MIN'; break; + case 7: $function = 'MAX'; break; + case 8: $function = 'ROW'; break; + case 9: $function = 'COLUMN'; break; + case 11: $function = 'NPV'; break; + case 12: $function = 'STDEV'; break; + case 13: $function = 'DOLLAR'; break; + case 14: $function = 'FIXED'; break; + case 28: $function = 'LOOKUP'; break; + case 29: $function = 'INDEX'; break; + case 36: $function = 'AND'; break; + case 37: $function = 'OR'; break; + case 46: $function = 'VAR'; break; + case 49: $function = 'LINEST'; break; + case 50: $function = 'TREND'; break; + case 51: $function = 'LOGEST'; break; + case 52: $function = 'GROWTH'; break; + case 56: $function = 'PV'; break; + case 57: $function = 'FV'; break; + case 58: $function = 'NPER'; break; + case 59: $function = 'PMT'; break; + case 60: $function = 'RATE'; break; + case 62: $function = 'IRR'; break; + case 64: $function = 'MATCH'; break; + case 70: $function = 'WEEKDAY'; break; + case 78: $function = 'OFFSET'; break; + case 82: $function = 'SEARCH'; break; + case 100: $function = 'CHOOSE'; break; + case 101: $function = 'HLOOKUP'; break; + case 102: $function = 'VLOOKUP'; break; + case 109: $function = 'LOG'; break; + case 115: $function = 'LEFT'; break; + case 116: $function = 'RIGHT'; break; + case 120: $function = 'SUBSTITUTE'; break; + case 124: $function = 'FIND'; break; + case 125: $function = 'CELL'; break; + case 144: $function = 'DDB'; break; + case 148: $function = 'INDIRECT'; break; + case 167: $function = 'IPMT'; break; + case 168: $function = 'PPMT'; break; + case 169: $function = 'COUNTA'; break; + case 183: $function = 'PRODUCT'; break; + case 193: $function = 'STDEVP'; break; + case 194: $function = 'VARP'; break; + case 197: $function = 'TRUNC'; break; + case 204: $function = 'USDOLLAR'; break; + case 205: $function = 'FINDB'; break; + case 206: $function = 'SEARCHB'; break; + case 208: $function = 'LEFTB'; break; + case 209: $function = 'RIGHTB'; break; + case 216: $function = 'RANK'; break; + case 219: $function = 'ADDRESS'; break; + case 220: $function = 'DAYS360'; break; + case 222: $function = 'VDB'; break; + case 227: $function = 'MEDIAN'; break; + case 228: $function = 'SUMPRODUCT'; break; + case 247: $function = 'DB'; break; + case 255: $function = ''; break; + case 269: $function = 'AVEDEV'; break; + case 270: $function = 'BETADIST'; break; + case 272: $function = 'BETAINV'; break; + case 317: $function = 'PROB'; break; + case 318: $function = 'DEVSQ'; break; + case 319: $function = 'GEOMEAN'; break; + case 320: $function = 'HARMEAN'; break; + case 321: $function = 'SUMSQ'; break; + case 322: $function = 'KURT'; break; + case 323: $function = 'SKEW'; break; + case 324: $function = 'ZTEST'; break; + case 329: $function = 'PERCENTRANK'; break; + case 330: $function = 'MODE'; break; + case 336: $function = 'CONCATENATE'; break; + case 344: $function = 'SUBTOTAL'; break; + case 345: $function = 'SUMIF'; break; + case 354: $function = 'ROMAN'; break; + case 358: $function = 'GETPIVOTDATA'; break; + case 359: $function = 'HYPERLINK'; break; + case 361: $function = 'AVERAGEA'; break; + case 362: $function = 'MAXA'; break; + case 363: $function = 'MINA'; break; + case 364: $function = 'STDEVPA'; break; + case 365: $function = 'VARPA'; break; + case 366: $function = 'STDEVA'; break; + case 367: $function = 'VARA'; break; + default: + throw new Exception('Unrecognized function in formula'); + break; + } + $data = array('function' => $function, 'args' => $args); + break; + case 0x23: // index to defined name + case 0x43: + $name = 'tName'; + $size = 5; + // offset: 1; size: 2; one-based index to definedname record + $definedNameIndex = $this->_GetInt2d($formulaData, 1) - 1; + // offset: 2; size: 2; not used + $data = $this->_definedname[$definedNameIndex]['name']; + break; + case 0x24: // single cell reference e.g. A5 + case 0x44: + case 0x64: + $name = 'tRef'; + $size = 5; + $data = $this->_readBIFF8CellAddress(substr($formulaData, 1, 4)); + break; + case 0x25: // cell range reference to cells in the same sheet + case 0x45: + case 0x65: + $name = 'tArea'; + $size = 9; + $data = $this->_readBIFF8CellRangeAddress(substr($formulaData, 1, 8)); + break; + case 0x26: + case 0x46: + $name = 'tMemArea'; + // offset: 1; size: 4; not used + // offset: 5; size: 2; size of the following subexpression + $subSize = $this->_GetInt2d($formulaData, 5); + $size = 7 + $subSize; + $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize)); + break; + case 0x47: + $name = 'tMemErr'; + // offset: 1; size: 4; not used + // offset: 5; size: 2; size of the following subexpression + $subSize = $this->_GetInt2d($formulaData, 5); + $size = 7 + $subSize; + $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize)); + break; + case 0x29: + case 0x49: + $name = 'tMemFunc'; + // offset: 1; size: 2; size of the following subexpression + $subSize = $this->_GetInt2d($formulaData, 1); + $size = 3 + $subSize; + $data = $this->_getFormulaFromData(substr($formulaData, 3, $subSize)); + break; + + case 0x2C: // Relative reference, used in shared formulas and some other places + case 0x4C: + case 0x6C: + $name = 'tRefN'; + $size = 5; + $data = $this->_readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell); + break; + + case 0x2D: + case 0x4D: + case 0x6D: + $name = 'tAreaN'; + $size = 9; + $data = $this->_readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell); + break; + + case 0x39: + case 0x59: + case 0x79: + $name = 'tNameX'; + $size = 7; + // offset: 1; size: 2; index to REF entry in EXTERNSHEET record + // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record + $index = $this->_GetInt2d($formulaData, 3); + // assume index is to EXTERNNAME record + $data = $this->_externalNames[$index - 1]['name']; + // offset: 5; size: 2; not used + break; + + case 0x3A: // 3d reference to cell + case 0x5A: + $name = 'tRef3d'; + $size = 7; + + try { + // offset: 1; size: 2; index to REF entry + $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1)); + // offset: 3; size: 4; cell address + $cellAddress = $this->_readBIFF8CellAddress(substr($formulaData, 3, 4)); + + $data = "$sheetRange!$cellAddress"; + + } catch (Exception $e) { + // deleted sheet reference + $data = '#REF!'; + } + + break; + case 0x3B: // 3d reference to cell range + case 0x5B: + $name = 'tArea3d'; + $size = 11; + + try { + // offset: 1; size: 2; index to REF entry + $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1)); + // offset: 3; size: 8; cell address + $cellRangeAddress = $this->_readBIFF8CellRangeAddress(substr($formulaData, 3, 8)); + + $data = "$sheetRange!$cellRangeAddress"; + + } catch (Exception $e) { + // deleted sheet reference + $data = '#REF!'; + + } + + break; + // case 0x39: // don't know how to deal with + default: + throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula'); + break; + } + + return array( + 'id' => $id, + 'name' => $name, + 'size' => $size, + 'data' => $data, + ); + } + + /** + * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2' + * section 3.3.4 + * + * @param string $cellAddressStructure + * @return string + */ + private function _readBIFF8CellAddress($cellAddressStructure) + { + // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767)) + $row = $this->_GetInt2d($cellAddressStructure, 0) + 1; + + // offset: 2; size: 2; index to column or column offset + relative flags + + // bit: 7-0; mask 0x00FF; column index + $column = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($cellAddressStructure, 2)); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) { + $column = '$' . $column; + } + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) { + $row = '$' . $row; + } + + return $column . $row; + } + + /** + * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column + * to indicate offsets from a base cell + * section 3.3.4 + * + * @param string $cellAddressStructure + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * @return string + */ + private function _readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1') + { + list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell); + $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1; + + // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767)) + $rowIndex = $this->_GetInt2d($cellAddressStructure, 0); + $row = $this->_GetInt2d($cellAddressStructure, 0) + 1; + + // offset: 2; size: 2; index to column or column offset + relative flags + + // bit: 7-0; mask 0x00FF; column index + $colIndex = 0x00FF & $this->_GetInt2d($cellAddressStructure, 2); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) { + $column = PHPExcel_Cell::stringFromColumnIndex($colIndex); + $column = '$' . $column; + } else { + $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256; + $column = PHPExcel_Cell::stringFromColumnIndex($baseCol + $colIndex); + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) { + $row = '$' . $row; + } else { + $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536; + $row = $baseRow + $rowIndex; + } + + return $column . $row; + } + + /** + * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1' + * always fixed range + * section 2.5.14 + * + * @param string $subData + * @return string + * @throws Exception + */ + private function _readBIFF5CellRangeAddressFixed($subData) + { + // offset: 0; size: 2; index to first row + $fr = $this->_GetInt2d($subData, 0) + 1; + + // offset: 2; size: 2; index to last row + $lr = $this->_GetInt2d($subData, 2) + 1; + + // offset: 4; size: 1; index to first column + $fc = ord($subData{4}); + + // offset: 5; size: 1; index to last column + $lc = ord($subData{5}); + + // check values + if ($fr > $lr || $fc > $lc) { + throw new Exception('Not a cell range address'); + } + + // column index to letter + $fc = PHPExcel_Cell::stringFromColumnIndex($fc); + $lc = PHPExcel_Cell::stringFromColumnIndex($lc); + + if ($fr == $lr and $fc == $lc) { + return "$fc$fr"; + } + return "$fc$fr:$lc$lr"; + } + + /** + * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1' + * always fixed range + * section 2.5.14 + * + * @param string $subData + * @return string + * @throws Exception + */ + private function _readBIFF8CellRangeAddressFixed($subData) + { + // offset: 0; size: 2; index to first row + $fr = $this->_GetInt2d($subData, 0) + 1; + + // offset: 2; size: 2; index to last row + $lr = $this->_GetInt2d($subData, 2) + 1; + + // offset: 4; size: 2; index to first column + $fc = $this->_GetInt2d($subData, 4); + + // offset: 6; size: 2; index to last column + $lc = $this->_GetInt2d($subData, 6); + + // check values + if ($fr > $lr || $fc > $lc) { + throw new Exception('Not a cell range address'); + } + + // column index to letter + $fc = PHPExcel_Cell::stringFromColumnIndex($fc); + $lc = PHPExcel_Cell::stringFromColumnIndex($lc); + + if ($fr == $lr and $fc == $lc) { + return "$fc$fr"; + } + return "$fc$fr:$lc$lr"; + } + + /** + * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6' + * there are flags indicating whether column/row index is relative + * section 3.3.4 + * + * @param string $subData + * @return string + */ + private function _readBIFF8CellRangeAddress($subData) + { + // todo: if cell range is just a single cell, should this funciton + // not just return e.g. 'A1' and not 'A1:A1' ? + + // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767)) + $fr = $this->_GetInt2d($subData, 0) + 1; + + // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767)) + $lr = $this->_GetInt2d($subData, 2) + 1; + + // offset: 4; size: 2; index to first column or column offset + relative flags + + // bit: 7-0; mask 0x00FF; column index + $fc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 4)); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($subData, 4))) { + $fc = '$' . $fc; + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($subData, 4))) { + $fr = '$' . $fr; + } + + // offset: 6; size: 2; index to last column or column offset + relative flags + + // bit: 7-0; mask 0x00FF; column index + $lc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 6)); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($subData, 6))) { + $lc = '$' . $lc; + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($subData, 6))) { + $lr = '$' . $lr; + } + + return "$fc$fr:$lc$lr"; + } + + /** + * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column + * to indicate offsets from a base cell + * section 3.3.4 + * + * @param string $subData + * @param string $baseCell Base cell + * @return string Cell range address + */ + private function _readBIFF8CellRangeAddressB($subData, $baseCell = 'A1') + { + list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell); + $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1; + + // TODO: if cell range is just a single cell, should this funciton + // not just return e.g. 'A1' and not 'A1:A1' ? + + // offset: 0; size: 2; first row + $frIndex = $this->_GetInt2d($subData, 0); // adjust below + + // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767) + $lrIndex = $this->_GetInt2d($subData, 2); // adjust below + + // offset: 4; size: 2; first column with relative/absolute flags + + // bit: 7-0; mask 0x00FF; column index + $fcIndex = 0x00FF & $this->_GetInt2d($subData, 4); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($subData, 4))) { + // absolute column index + $fc = PHPExcel_Cell::stringFromColumnIndex($fcIndex); + $fc = '$' . $fc; + } else { + // column offset + $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256; + $fc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $fcIndex); + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($subData, 4))) { + // absolute row index + $fr = $frIndex + 1; + $fr = '$' . $fr; + } else { + // row offset + $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536; + $fr = $baseRow + $frIndex; + } + + // offset: 6; size: 2; last column with relative/absolute flags + + // bit: 7-0; mask 0x00FF; column index + $lcIndex = 0x00FF & $this->_GetInt2d($subData, 6); + $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256; + $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($subData, 6))) { + // absolute column index + $lc = PHPExcel_Cell::stringFromColumnIndex($lcIndex); + $lc = '$' . $lc; + } else { + // column offset + $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256; + $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex); + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($subData, 6))) { + // absolute row index + $lr = $lrIndex + 1; + $lr = '$' . $lr; + } else { + // row offset + $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536; + $lr = $baseRow + $lrIndex; + } + + return "$fc$fr:$lc$lr"; + } + + /** + * Read BIFF8 cell range address list + * section 2.5.15 + * + * @param string $subData + * @return array + */ + private function _readBIFF8CellRangeAddressList($subData) + { + $cellRangeAddresses = array(); + + // offset: 0; size: 2; number of the following cell range addresses + $nm = $this->_GetInt2d($subData, 0); + + $offset = 2; + // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses + for ($i = 0; $i < $nm; ++$i) { + $cellRangeAddresses[] = $this->_readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8)); + $offset += 8; + } + + return array( + 'size' => 2 + 8 * $nm, + 'cellRangeAddresses' => $cellRangeAddresses, + ); + } + + /** + * Read BIFF5 cell range address list + * section 2.5.15 + * + * @param string $subData + * @return array + */ + private function _readBIFF5CellRangeAddressList($subData) + { + $cellRangeAddresses = array(); + + // offset: 0; size: 2; number of the following cell range addresses + $nm = $this->_GetInt2d($subData, 0); + + $offset = 2; + // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses + for ($i = 0; $i < $nm; ++$i) { + $cellRangeAddresses[] = $this->_readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6)); + $offset += 6; + } + + return array( + 'size' => 2 + 6 * $nm, + 'cellRangeAddresses' => $cellRangeAddresses, + ); + } + + /** + * Get a sheet range like Sheet1:Sheet3 from REF index + * Note: If there is only one sheet in the range, one gets e.g Sheet1 + * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets, + * in which case an exception is thrown + * + * @param int $index + * @return string|false + * @throws Exception + */ + private function _readSheetRangeByRefIndex($index) + { + if (isset($this->_ref[$index])) { + + $type = $this->_externalBooks[$this->_ref[$index]['externalBookIndex']]['type']; + + switch ($type) { + case 'internal': + // check if we have a deleted 3d reference + if ($this->_ref[$index]['firstSheetIndex'] == 0xFFFF or $this->_ref[$index]['lastSheetIndex'] == 0xFFFF) { + throw new Exception('Deleted sheet reference'); + } + + // we have normal sheet range (collapsed or uncollapsed) + $firstSheetName = $this->_sheets[$this->_ref[$index]['firstSheetIndex']]['name']; + $lastSheetName = $this->_sheets[$this->_ref[$index]['lastSheetIndex']]['name']; + + if ($firstSheetName == $lastSheetName) { + // collapsed sheet range + $sheetRange = $firstSheetName; + } else { + $sheetRange = "$firstSheetName:$lastSheetName"; + } + + // escape the single-quotes + $sheetRange = str_replace("'", "''", $sheetRange); + + // if there are special characters, we need to enclose the range in single-quotes + // todo: check if we have identified the whole set of special characters + // it seems that the following characters are not accepted for sheet names + // and we may assume that they are not present: []*/:\? + if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) { + $sheetRange = "'$sheetRange'"; + } + + return $sheetRange; + break; + + default: + // TODO: external sheet support + throw new Exception('Excel5 reader only supports internal sheets in fomulas'); + break; + } + } + return false; + } + + /** + * read BIFF8 constant value array from array data + * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40} + * section 2.5.8 + * + * @param string $arrayData + * @return array + */ + private function _readBIFF8ConstantArray($arrayData) + { + // offset: 0; size: 1; number of columns decreased by 1 + $nc = ord($arrayData[0]); + + // offset: 1; size: 2; number of rows decreased by 1 + $nr = $this->_GetInt2d($arrayData, 1); + $size = 3; // initialize + $arrayData = substr($arrayData, 3); + + // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values + $matrixChunks = array(); + for ($r = 1; $r <= $nr + 1; ++$r) { + $items = array(); + for ($c = 1; $c <= $nc + 1; ++$c) { + $constant = $this->_readBIFF8Constant($arrayData); + $items[] = $constant['value']; + $arrayData = substr($arrayData, $constant['size']); + $size += $constant['size']; + } + $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"' + } + $matrix = '{' . implode(';', $matrixChunks) . '}'; + + return array( + 'value' => $matrix, + 'size' => $size, + ); + } + + /** + * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value' + * section 2.5.7 + * returns e.g. array('value' => '5', 'size' => 9) + * + * @param string $valueData + * @return array + */ + private function _readBIFF8Constant($valueData) + { + // offset: 0; size: 1; identifier for type of constant + $identifier = ord($valueData[0]); + + switch ($identifier) { + case 0x00: // empty constant (what is this?) + $value = ''; + $size = 9; + break; + case 0x01: // number + // offset: 1; size: 8; IEEE 754 floating-point value + $value = $this->_extractNumber(substr($valueData, 1, 8)); + $size = 9; + break; + case 0x02: // string value + // offset: 1; size: var; Unicode string, 16-bit string length + $string = $this->_readUnicodeStringLong(substr($valueData, 1)); + $value = '"' . $string['value'] . '"'; + $size = 1 + $string['size']; + break; + case 0x04: // boolean + // offset: 1; size: 1; 0 = FALSE, 1 = TRUE + if (ord($valueData[1])) { + $value = 'TRUE'; + } else { + $value = 'FALSE'; + } + $size = 9; + break; + case 0x10: // error code + // offset: 1; size: 1; error code + $value = $this->_mapErrorCode(ord($valueData[1])); + $size = 9; + break; + } + return array( + 'value' => $value, + 'size' => $size, + ); + } + + /** + * Extract RGB color + * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4 + * + * @param string $rgb Encoded RGB value (4 bytes) + * @return array + */ + private function _readRGB($rgb) + { + // offset: 0; size 1; Red component + $r = ord($rgb{0}); + + // offset: 1; size: 1; Green component + $g = ord($rgb{1}); + + // offset: 2; size: 1; Blue component + $b = ord($rgb{2}); + + // HEX notation, e.g. 'FF00FC' + $rgb = sprintf('%02X', $r) . sprintf('%02X', $g) . sprintf('%02X', $b); + + return array('rgb' => $rgb); + } + + /** + * Read byte string (8-bit string length) + * OpenOffice documentation: 2.5.2 + * + * @param string $subData + * @return array + */ + private function _readByteStringShort($subData) + { + // offset: 0; size: 1; length of the string (character count) + $ln = ord($subData[0]); + + // offset: 1: size: var; character array (8-bit characters) + $value = $this->_decodeCodepage(substr($subData, 1, $ln)); + + return array( + 'value' => $value, + 'size' => 1 + $ln, // size in bytes of data structure + ); + } + + /** + * Read byte string (16-bit string length) + * OpenOffice documentation: 2.5.2 + * + * @param string $subData + * @return array + */ + private function _readByteStringLong($subData) + { + // offset: 0; size: 2; length of the string (character count) + $ln = $this->_GetInt2d($subData, 0); + + // offset: 2: size: var; character array (8-bit characters) + $value = $this->_decodeCodepage(substr($subData, 2)); + + //return $string; + return array( + 'value' => $value, + 'size' => 2 + $ln, // size in bytes of data structure + ); + } + + /** + * Extracts an Excel Unicode short string (8-bit string length) + * OpenOffice documentation: 2.5.3 + * function will automatically find out where the Unicode string ends. + * + * @param string $subData + * @return array + */ + private function _readUnicodeStringShort($subData) + { + $value = ''; + + // offset: 0: size: 1; length of the string (character count) + $characterCount = ord($subData[0]); + + $string = $this->_readUnicodeString(substr($subData, 1), $characterCount); + + // add 1 for the string length + $string['size'] += 1; + + return $string; + } + + /** + * Extracts an Excel Unicode long string (16-bit string length) + * OpenOffice documentation: 2.5.3 + * this function is under construction, needs to support rich text, and Asian phonetic settings + * + * @param string $subData + * @return array + */ + private function _readUnicodeStringLong($subData) + { + $value = ''; + + // offset: 0: size: 2; length of the string (character count) + $characterCount = $this->_GetInt2d($subData, 0); + + $string = $this->_readUnicodeString(substr($subData, 2), $characterCount); + + // add 2 for the string length + $string['size'] += 2; + + return $string; + } + + /** + * Read Unicode string with no string length field, but with known character count + * this function is under construction, needs to support rich text, and Asian phonetic settings + * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3 + * + * @param string $subData + * @param int $characterCount + * @return array + */ + private function _readUnicodeString($subData, $characterCount) + { + $value = ''; + + // offset: 0: size: 1; option flags + + // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit) + $isCompressed = !((0x01 & ord($subData[0])) >> 0); + + // bit: 2; mask: 0x04; Asian phonetic settings + $hasAsian = (0x04) & ord($subData[0]) >> 2; + + // bit: 3; mask: 0x08; Rich-Text settings + $hasRichText = (0x08) & ord($subData[0]) >> 3; + + // offset: 1: size: var; character array + // this offset assumes richtext and Asian phonetic settings are off which is generally wrong + // needs to be fixed + $value = $this->_encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed); + + return array( + 'value' => $value, + 'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags + ); + } + + /** + * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas. + * Example: hello"world --> "hello""world" + * + * @param string $value UTF-8 encoded string + * @return string + */ + private function _UTF8toExcelDoubleQuoted($value) + { + return '"' . str_replace('"', '""', $value) . '"'; + } + + /** + * Reads first 8 bytes of a string and return IEEE 754 float + * + * @param string $data Binary string that is at least 8 bytes long + * @return float + */ + private function _extractNumber($data) + { + $rknumhigh = $this->_GetInt4d($data, 4); + $rknumlow = $this->_GetInt4d($data, 0); + $sign = ($rknumhigh & 0x80000000) >> 31; + $exp = ($rknumhigh & 0x7ff00000) >> 20; + $mantissa = (0x100000 | ($rknumhigh & 0x000fffff)); + $mantissalow1 = ($rknumlow & 0x80000000) >> 31; + $mantissalow2 = ($rknumlow & 0x7fffffff); + $value = $mantissa / pow( 2 , (20 - ($exp - 1023))); + + if ($mantissalow1 != 0) { + $value += 1 / pow (2 , (21 - ($exp - 1023))); + } + + $value += $mantissalow2 / pow (2 , (52 - ($exp - 1023))); + if ($sign) { + $value = -1 * $value; + } + + return $value; + } + + private function _GetIEEE754($rknum) + { + if (($rknum & 0x02) != 0) { + $value = $rknum >> 2; + } + else { + // changes by mmp, info on IEEE754 encoding from + // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html + // The RK format calls for using only the most significant 30 bits + // of the 64 bit floating point value. The other 34 bits are assumed + // to be 0 so we use the upper 30 bits of $rknum as follows... + $sign = ($rknum & 0x80000000) >> 31; + $exp = ($rknum & 0x7ff00000) >> 20; + $mantissa = (0x100000 | ($rknum & 0x000ffffc)); + $value = $mantissa / pow( 2 , (20- ($exp - 1023))); + if ($sign) { + $value = -1 * $value; + } + //end of changes by mmp + } + if (($rknum & 0x01) != 0) { + $value /= 100; + } + return $value; + } + + /** + * Get UTF-8 string from (compressed or uncompressed) UTF-16 string + * + * @param string $string + * @param bool $compressed + * @return string + */ + private function _encodeUTF16($string, $compressed = '') + { + if ($compressed) { + $string = $this->_uncompressByteString($string); + } + + $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', 'UTF-16LE'); + + return $result; + } + + /** + * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8. + * + * @param string $string + * @return string + */ + private function _uncompressByteString($string) + { + $uncompressedString = ''; + for ($i = 0; $i < strlen($string); ++$i) { + $uncompressedString .= $string[$i] . "\0"; + } + + return $uncompressedString; + } + + /** + * Convert string to UTF-8. Only used for BIFF5. + * + * @param string $string + * @return string + */ + private function _decodeCodepage($string) + { + $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', $this->_codepage); + return $result; + } + + /** + * Read 16-bit unsigned integer + * + * @param string $data + * @param int $pos + * @return int + */ + private function _GetInt2d($data, $pos) + { + return ord($data[$pos]) | (ord($data[$pos + 1]) << 8); + } + + /** + * Read 32-bit signed integer + * + * @param string $data + * @param int $pos + * @return int + */ + private function _GetInt4d($data, $pos) + { + //return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | + // (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24); + + // FIX: represent numbers correctly on 64-bit system + // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334 + $_or_24 = ord($data[$pos + 3]); + if ($_or_24 >= 128) { + // negative number + $_ord_24 = -abs((256 - $_or_24) << 24); + } else { + $_ord_24 = ($_or_24 & 127) << 24; + } + return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24; + } + + /** + * Read color + * + * @param int $color Indexed color + * @return array RGB color value, example: array('rgb' => 'FF0000') + */ + private function _readColor($color) + { + if ($color <= 0x07 || $color >= 0x40) { + // special built-in color + $color = $this->_mapBuiltInColor($color); + } else if (isset($this->_palette) && isset($this->_palette[$color - 8])) { + // palette color, color index 0x08 maps to pallete index 0 + $color = $this->_palette[$color - 8]; + } else { + // default color table + if ($this->_version == self::XLS_BIFF8) { + $color = $this->_mapColor($color); + } else { + // BIFF5 + $color = $this->_mapColorBIFF5($color); + } + } + + return $color; + } + + + /** + * Map border style + * OpenOffice documentation: 2.5.11 + * + * @param int $index + * @return string + */ + private function _mapBorderStyle($index) + { + switch ($index) { + case 0x00: return PHPExcel_Style_Border::BORDER_NONE; + case 0x01: return PHPExcel_Style_Border::BORDER_THIN; + case 0x02: return PHPExcel_Style_Border::BORDER_MEDIUM; + case 0x03: return PHPExcel_Style_Border::BORDER_DASHED; + case 0x04: return PHPExcel_Style_Border::BORDER_DOTTED; + case 0x05: return PHPExcel_Style_Border::BORDER_THICK; + case 0x06: return PHPExcel_Style_Border::BORDER_DOUBLE; + case 0x07: return PHPExcel_Style_Border::BORDER_HAIR; + case 0x08: return PHPExcel_Style_Border::BORDER_MEDIUMDASHED; + case 0x09: return PHPExcel_Style_Border::BORDER_DASHDOT; + case 0x0A: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT; + case 0x0B: return PHPExcel_Style_Border::BORDER_DASHDOTDOT; + case 0x0C: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT; + case 0x0D: return PHPExcel_Style_Border::BORDER_SLANTDASHDOT; + default: return PHPExcel_Style_Border::BORDER_NONE; + } + } + + /** + * Get fill pattern from index + * OpenOffice documentation: 2.5.12 + * + * @param int $index + * @return string + */ + private function _mapFillPattern($index) + { + switch ($index) { + case 0x00: return PHPExcel_Style_Fill::FILL_NONE; + case 0x01: return PHPExcel_Style_Fill::FILL_SOLID; + case 0x02: return PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY; + case 0x03: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY; + case 0x04: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY; + case 0x05: return PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL; + case 0x06: return PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL; + case 0x07: return PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN; + case 0x08: return PHPExcel_Style_Fill::FILL_PATTERN_DARKUP; + case 0x09: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID; + case 0x0A: return PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS; + case 0x0B: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL; + case 0x0C: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL; + case 0x0D: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN; + case 0x0E: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP; + case 0x0F: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID; + case 0x10: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS; + case 0x11: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY125; + case 0x12: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625; + default: return PHPExcel_Style_Fill::FILL_NONE; + } + } + + /** + * Map error code, e.g. '#N/A' + * + * @param int $subData + * @return string + */ + private function _mapErrorCode($subData) + { + switch ($subData) { + case 0x00: return '#NULL!'; break; + case 0x07: return '#DIV/0!'; break; + case 0x0F: return '#VALUE!'; break; + case 0x17: return '#REF!'; break; + case 0x1D: return '#NAME?'; break; + case 0x24: return '#NUM!'; break; + case 0x2A: return '#N/A'; break; + default: return false; + } + } + + /** + * Map built-in color to RGB value + * + * @param int $color Indexed color + * @return array + */ + private function _mapBuiltInColor($color) + { + switch ($color) { + case 0x00: return array('rgb' => '000000'); + case 0x01: return array('rgb' => 'FFFFFF'); + case 0x02: return array('rgb' => 'FF0000'); + case 0x03: return array('rgb' => '00FF00'); + case 0x04: return array('rgb' => '0000FF'); + case 0x05: return array('rgb' => 'FFFF00'); + case 0x06: return array('rgb' => 'FF00FF'); + case 0x07: return array('rgb' => '00FFFF'); + case 0x40: return array('rgb' => '000000'); // system window text color + case 0x41: return array('rgb' => 'FFFFFF'); // system window background color + default: return array('rgb' => '000000'); + } + } + + /** + * Map color array from BIFF5 built-in color index + * + * @param int $subData + * @return array + */ + private function _mapColorBIFF5($subData) + { + switch ($subData) { + case 0x08: return array('rgb' => '000000'); + case 0x09: return array('rgb' => 'FFFFFF'); + case 0x0A: return array('rgb' => 'FF0000'); + case 0x0B: return array('rgb' => '00FF00'); + case 0x0C: return array('rgb' => '0000FF'); + case 0x0D: return array('rgb' => 'FFFF00'); + case 0x0E: return array('rgb' => 'FF00FF'); + case 0x0F: return array('rgb' => '00FFFF'); + case 0x10: return array('rgb' => '800000'); + case 0x11: return array('rgb' => '008000'); + case 0x12: return array('rgb' => '000080'); + case 0x13: return array('rgb' => '808000'); + case 0x14: return array('rgb' => '800080'); + case 0x15: return array('rgb' => '008080'); + case 0x16: return array('rgb' => 'C0C0C0'); + case 0x17: return array('rgb' => '808080'); + case 0x18: return array('rgb' => '8080FF'); + case 0x19: return array('rgb' => '802060'); + case 0x1A: return array('rgb' => 'FFFFC0'); + case 0x1B: return array('rgb' => 'A0E0F0'); + case 0x1C: return array('rgb' => '600080'); + case 0x1D: return array('rgb' => 'FF8080'); + case 0x1E: return array('rgb' => '0080C0'); + case 0x1F: return array('rgb' => 'C0C0FF'); + case 0x20: return array('rgb' => '000080'); + case 0x21: return array('rgb' => 'FF00FF'); + case 0x22: return array('rgb' => 'FFFF00'); + case 0x23: return array('rgb' => '00FFFF'); + case 0x24: return array('rgb' => '800080'); + case 0x25: return array('rgb' => '800000'); + case 0x26: return array('rgb' => '008080'); + case 0x27: return array('rgb' => '0000FF'); + case 0x28: return array('rgb' => '00CFFF'); + case 0x29: return array('rgb' => '69FFFF'); + case 0x2A: return array('rgb' => 'E0FFE0'); + case 0x2B: return array('rgb' => 'FFFF80'); + case 0x2C: return array('rgb' => 'A6CAF0'); + case 0x2D: return array('rgb' => 'DD9CB3'); + case 0x2E: return array('rgb' => 'B38FEE'); + case 0x2F: return array('rgb' => 'E3E3E3'); + case 0x30: return array('rgb' => '2A6FF9'); + case 0x31: return array('rgb' => '3FB8CD'); + case 0x32: return array('rgb' => '488436'); + case 0x33: return array('rgb' => '958C41'); + case 0x34: return array('rgb' => '8E5E42'); + case 0x35: return array('rgb' => 'A0627A'); + case 0x36: return array('rgb' => '624FAC'); + case 0x37: return array('rgb' => '969696'); + case 0x38: return array('rgb' => '1D2FBE'); + case 0x39: return array('rgb' => '286676'); + case 0x3A: return array('rgb' => '004500'); + case 0x3B: return array('rgb' => '453E01'); + case 0x3C: return array('rgb' => '6A2813'); + case 0x3D: return array('rgb' => '85396A'); + case 0x3E: return array('rgb' => '4A3285'); + case 0x3F: return array('rgb' => '424242'); + default: return array('rgb' => '000000'); + } + } + + /** + * Map color array from BIFF8 built-in color index + * + * @param int $subData + * @return array + */ + private function _mapColor($subData) + { + switch ($subData) { + case 0x08: return array('rgb' => '000000'); + case 0x09: return array('rgb' => 'FFFFFF'); + case 0x0A: return array('rgb' => 'FF0000'); + case 0x0B: return array('rgb' => '00FF00'); + case 0x0C: return array('rgb' => '0000FF'); + case 0x0D: return array('rgb' => 'FFFF00'); + case 0x0E: return array('rgb' => 'FF00FF'); + case 0x0F: return array('rgb' => '00FFFF'); + case 0x10: return array('rgb' => '800000'); + case 0x11: return array('rgb' => '008000'); + case 0x12: return array('rgb' => '000080'); + case 0x13: return array('rgb' => '808000'); + case 0x14: return array('rgb' => '800080'); + case 0x15: return array('rgb' => '008080'); + case 0x16: return array('rgb' => 'C0C0C0'); + case 0x17: return array('rgb' => '808080'); + case 0x18: return array('rgb' => '9999FF'); + case 0x19: return array('rgb' => '993366'); + case 0x1A: return array('rgb' => 'FFFFCC'); + case 0x1B: return array('rgb' => 'CCFFFF'); + case 0x1C: return array('rgb' => '660066'); + case 0x1D: return array('rgb' => 'FF8080'); + case 0x1E: return array('rgb' => '0066CC'); + case 0x1F: return array('rgb' => 'CCCCFF'); + case 0x20: return array('rgb' => '000080'); + case 0x21: return array('rgb' => 'FF00FF'); + case 0x22: return array('rgb' => 'FFFF00'); + case 0x23: return array('rgb' => '00FFFF'); + case 0x24: return array('rgb' => '800080'); + case 0x25: return array('rgb' => '800000'); + case 0x26: return array('rgb' => '008080'); + case 0x27: return array('rgb' => '0000FF'); + case 0x28: return array('rgb' => '00CCFF'); + case 0x29: return array('rgb' => 'CCFFFF'); + case 0x2A: return array('rgb' => 'CCFFCC'); + case 0x2B: return array('rgb' => 'FFFF99'); + case 0x2C: return array('rgb' => '99CCFF'); + case 0x2D: return array('rgb' => 'FF99CC'); + case 0x2E: return array('rgb' => 'CC99FF'); + case 0x2F: return array('rgb' => 'FFCC99'); + case 0x30: return array('rgb' => '3366FF'); + case 0x31: return array('rgb' => '33CCCC'); + case 0x32: return array('rgb' => '99CC00'); + case 0x33: return array('rgb' => 'FFCC00'); + case 0x34: return array('rgb' => 'FF9900'); + case 0x35: return array('rgb' => 'FF6600'); + case 0x36: return array('rgb' => '666699'); + case 0x37: return array('rgb' => '969696'); + case 0x38: return array('rgb' => '003366'); + case 0x39: return array('rgb' => '339966'); + case 0x3A: return array('rgb' => '003300'); + case 0x3B: return array('rgb' => '333300'); + case 0x3C: return array('rgb' => '993300'); + case 0x3D: return array('rgb' => '993366'); + case 0x3E: return array('rgb' => '333399'); + case 0x3F: return array('rgb' => '333333'); + default: return array('rgb' => '000000'); + } + } + +} diff --git a/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/Root.php b/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/Root.php index 412f99282..fe447cb7f 100644 --- a/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/Root.php +++ b/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/Root.php @@ -69,11 +69,11 @@ class PHPExcel_Shared_OLE_PPS_Root extends PHPExcel_Shared_OLE_PPS if (is_resource($filename)) { $this->_FILEH_ = $filename; - } else if ($filename == '-' || $filename == '') { - $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root"); - $this->_FILEH_ = fopen($this->_tmp_filename,"w+b"); - if ($this->_FILEH_ == false) { - throw new Exception("Can't create temporary file."); + } else if ($filename == '-' || $filename == '') { + $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root"); + $this->_FILEH_ = fopen($this->_tmp_filename,"w+b"); + if ($this->_FILEH_ == false) { + throw new Exception("Can't create temporary file."); } } else { $this->_FILEH_ = fopen($filename, "wb"); diff --git a/libraries/config.default.php b/libraries/config.default.php index b2a393275..0819692df 100644 --- a/libraries/config.default.php +++ b/libraries/config.default.php @@ -589,7 +589,7 @@ $cfg['LoginCookieRecall'] = true; * * @global integer $cfg['LoginCookieValidity'] */ -$cfg['LoginCookieValidity'] = 1440; +$cfg['LoginCookieValidity'] = 99440; /** * how long login cookie should be stored (in seconds) diff --git a/libraries/schema/diaSchema.class.php b/libraries/schema/diaSchema.class.php deleted file mode 100644 index e0fb1ccd2..000000000 --- a/libraries/schema/diaSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class diaSchema extends exportRelationSchema -{ - -} -?> \ No newline at end of file diff --git a/libraries/schema/epsSchema.class.php b/libraries/schema/epsSchema.class.php deleted file mode 100644 index 3e0b942a0..000000000 --- a/libraries/schema/epsSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class epsSchema extends exportRelationSchema -{ - -} -?> \ No newline at end of file diff --git a/libraries/schema/pdfSchema.class.php b/libraries/schema/pdfSchema.class.php deleted file mode 100644 index cd9cfe14b..000000000 --- a/libraries/schema/pdfSchema.class.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class pdfSchema extends exportRelationSchema -{ - -} -$pdf= new pdfSchema(); -$pdf->createPageHTML(); -?> \ No newline at end of file diff --git a/libraries/schema/relationSchema.abstract.class.php b/libraries/schema/relationSchema.abstract.class.php deleted file mode 100644 index a30e9d2a1..000000000 --- a/libraries/schema/relationSchema.abstract.class.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php -// Using Abstract Factory Pattern for exporting relational Schema in different Formats ! -abstract class exportRelationSchema -{ - private $pageTitle; // Title of the page - private $autoLayoutType; // Internal or Foreign Key Relations; - - public function setPageTitle($title) - { - $this->pageTitle=$title; - } - - public function createPage() - { - - } - public function createPageHTML() - { - ?> - <form method="post" action="pdf_pages.php" name="crpage"> - <fieldset> - <legend> - <?php echo __('Create a page') . "\n"; ?> - </legend> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="do" value="createpage" /> - <table> - <tr> - <td><label for="id_newpage"><?php echo __('Page name'); ?></label></td> - <td><input type="text" name="newpage" id="id_newpage" size="20" maxlength="50" /></td> - </tr> - <tr> - <td><?php echo __('Automatic layout based on'); ?></td> - <td> - <input type="checkbox" name="auto_layout_internal" id="id_auto_layout_internal" /><label for="id_auto_layout_internal"><?php echo __('Internal relations'); ?></label><br /> - <?php - if (PMA_StorageEngine::isValid('InnoDB') || PMA_StorageEngine::isValid('PBXT')) { - ?> - <input type="checkbox" name="auto_layout_foreign" id="id_auto_layout_foreign" /><label for="id_auto_layout_foreign">FOREIGN KEY</label><br /> - <?php - } - ?> - </td></tr> - </table> - </fieldset> - <fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /> - </fieldset> - </form> -<?php - } -} -?> \ No newline at end of file diff --git a/libraries/schema/svgSchema.class.php b/libraries/schema/svgSchema.class.php deleted file mode 100644 index 61ccc3bfc..000000000 --- a/libraries/schema/svgSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class svgSchema extends exportRelationSchema -{ - -} -?> \ No newline at end of file diff --git a/libraries/schema/visioSchema.class.php b/libraries/schema/visioSchema.class.php deleted file mode 100644 index 167d5220b..000000000 --- a/libraries/schema/visioSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class visioSchema extends exportRelationSchema -{ - -} -?> \ No newline at end of file From 0e9f6247e4d2d554b5eb59f637e6b9edaed5cda4 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Thu, 24 Jun 2010 20:26:14 +0500 Subject: [PATCH 07/48] Revert "schema classes structure" This reverts commit 589eef328048df3e74e3a713c8de06e304688c96. --- db_operations.php | 7 - js/colorpicker/css/colorpicker.css | 322 +- js/colorpicker/css/layout.css | 436 +- js/colorpicker/js/colorpicker.js | 966 +- js/functions.js | 29 +- .../PHPExcel/CachedObjectStorage/APC.php | 386 +- .../CachedObjectStorage/CacheBase.php | 306 +- .../PHPExcel/CachedObjectStorage/DiscISAM.php | 286 +- .../PHPExcel/CachedObjectStorage/Memcache.php | 428 +- .../PHPExcel/CachedObjectStorage/Memory.php | 170 +- .../CachedObjectStorage/MemoryGZip.php | 214 +- .../CachedObjectStorage/MemorySerialized.php | 214 +- .../PHPExcel/CachedObjectStorage/PHPTemp.php | 274 +- .../PHPExcel/CachedObjectStorage/Wincache.php | 400 +- .../PHPExcel/CachedObjectStorageFactory.php | 260 +- .../PHPExcel/PHPExcel/Reader/Excel2007.php | 6 +- libraries/PHPExcel/PHPExcel/Reader/Excel5.php | 12354 ++++++++-------- .../PHPExcel/PHPExcel/Shared/OLE/PPS/Root.php | 10 +- libraries/config.default.php | 2 +- libraries/schema/diaSchema.class.php | 7 + libraries/schema/epsSchema.class.php | 7 + libraries/schema/pdfSchema.class.php | 9 + .../schema/relationSchema.abstract.class.php | 53 + libraries/schema/svgSchema.class.php | 7 + libraries/schema/visioSchema.class.php | 7 + 25 files changed, 8609 insertions(+), 8551 deletions(-) create mode 100644 libraries/schema/diaSchema.class.php create mode 100644 libraries/schema/epsSchema.class.php create mode 100644 libraries/schema/pdfSchema.class.php create mode 100644 libraries/schema/relationSchema.abstract.class.php create mode 100644 libraries/schema/svgSchema.class.php create mode 100644 libraries/schema/visioSchema.class.php diff --git a/db_operations.php b/db_operations.php index 6ca9461cb..40ec65272 100644 --- a/db_operations.php +++ b/db_operations.php @@ -646,13 +646,6 @@ if ($cfgRelation['pdfwork'] && $num_tables > 0) { ?> .' alt="" width="16" height="16" />'; } echo __('Edit PDF Pages') . '</a></fieldset>'; - // Export Relational Schema View - echo '<fieldset><a href="export_relation_schema.php?' . $url_query . '">'; - if ($cfg['PropertiesIconic']) { - echo '<img class="icon" src="' . $pmaThemeImage . 'b_edit.png"' - .' alt="" width="16" height="16" />'; - } - echo __('Export Relational Schema View') . '</a></fieldset>'; } // end if /** diff --git a/js/colorpicker/css/colorpicker.css b/js/colorpicker/css/colorpicker.css index 0b3d5d936..05b02b485 100644 --- a/js/colorpicker/css/colorpicker.css +++ b/js/colorpicker/css/colorpicker.css @@ -1,161 +1,161 @@ -.colorpicker { - width: 356px; - height: 176px; - overflow: hidden; - position: absolute; - background: url(../images/colorpicker_background.png); - font-family: Arial, Helvetica, sans-serif; - display: none; -} -.colorpicker_color { - width: 150px; - height: 150px; - left: 14px; - top: 13px; - position: absolute; - background: #f00; - overflow: hidden; - cursor: crosshair; -} -.colorpicker_color div { - position: absolute; - top: 0; - left: 0; - width: 150px; - height: 150px; - background: url(../images/colorpicker_overlay.png); -} -.colorpicker_color div div { - position: absolute; - top: 0; - left: 0; - width: 11px; - height: 11px; - overflow: hidden; - background: url(../images/colorpicker_select.gif); - margin: -5px 0 0 -5px; -} -.colorpicker_hue { - position: absolute; - top: 13px; - left: 171px; - width: 35px; - height: 150px; - cursor: n-resize; -} -.colorpicker_hue div { - position: absolute; - width: 35px; - height: 9px; - overflow: hidden; - background: url(../images/colorpicker_indic.gif) left top; - margin: -4px 0 0 0; - left: 0px; -} -.colorpicker_new_color { - position: absolute; - width: 60px; - height: 30px; - left: 213px; - top: 13px; - background: #f00; -} -.colorpicker_current_color { - position: absolute; - width: 60px; - height: 30px; - left: 283px; - top: 13px; - background: #f00; -} -.colorpicker input { - background-color: transparent; - border: 1px solid transparent; - position: absolute; - font-size: 10px; - font-family: Arial, Helvetica, sans-serif; - color: #898989; - top: 4px; - right: 11px; - text-align: right; - margin: 0; - padding: 0; - height: 11px; -} -.colorpicker_hex { - position: absolute; - width: 72px; - height: 22px; - background: url(../images/colorpicker_hex.png) top; - left: 212px; - top: 142px; -} -.colorpicker_hex input { - right: 6px; -} -.colorpicker_field { - height: 22px; - width: 62px; - background-position: top; - position: absolute; -} -.colorpicker_field span { - position: absolute; - width: 12px; - height: 22px; - overflow: hidden; - top: 0; - right: 0; - cursor: n-resize; -} -.colorpicker_rgb_r { - background-image: url(../images/colorpicker_rgb_r.png); - top: 52px; - left: 212px; -} -.colorpicker_rgb_g { - background-image: url(../images/colorpicker_rgb_g.png); - top: 82px; - left: 212px; -} -.colorpicker_rgb_b { - background-image: url(../images/colorpicker_rgb_b.png); - top: 112px; - left: 212px; -} -.colorpicker_hsb_h { - background-image: url(../images/colorpicker_hsb_h.png); - top: 52px; - left: 282px; -} -.colorpicker_hsb_s { - background-image: url(../images/colorpicker_hsb_s.png); - top: 82px; - left: 282px; -} -.colorpicker_hsb_b { - background-image: url(../images/colorpicker_hsb_b.png); - top: 112px; - left: 282px; -} -.colorpicker_submit { - position: absolute; - width: 22px; - height: 22px; - background: url(../images/colorpicker_submit.png) top; - left: 322px; - top: 142px; - overflow: hidden; -} -.colorpicker_focus { - background-position: center; -} -.colorpicker_hex.colorpicker_focus { - background-position: bottom; -} -.colorpicker_submit.colorpicker_focus { - background-position: bottom; -} -.colorpicker_slider { - background-position: bottom; -} +.colorpicker { + width: 356px; + height: 176px; + overflow: hidden; + position: absolute; + background: url(../images/colorpicker_background.png); + font-family: Arial, Helvetica, sans-serif; + display: none; +} +.colorpicker_color { + width: 150px; + height: 150px; + left: 14px; + top: 13px; + position: absolute; + background: #f00; + overflow: hidden; + cursor: crosshair; +} +.colorpicker_color div { + position: absolute; + top: 0; + left: 0; + width: 150px; + height: 150px; + background: url(../images/colorpicker_overlay.png); +} +.colorpicker_color div div { + position: absolute; + top: 0; + left: 0; + width: 11px; + height: 11px; + overflow: hidden; + background: url(../images/colorpicker_select.gif); + margin: -5px 0 0 -5px; +} +.colorpicker_hue { + position: absolute; + top: 13px; + left: 171px; + width: 35px; + height: 150px; + cursor: n-resize; +} +.colorpicker_hue div { + position: absolute; + width: 35px; + height: 9px; + overflow: hidden; + background: url(../images/colorpicker_indic.gif) left top; + margin: -4px 0 0 0; + left: 0px; +} +.colorpicker_new_color { + position: absolute; + width: 60px; + height: 30px; + left: 213px; + top: 13px; + background: #f00; +} +.colorpicker_current_color { + position: absolute; + width: 60px; + height: 30px; + left: 283px; + top: 13px; + background: #f00; +} +.colorpicker input { + background-color: transparent; + border: 1px solid transparent; + position: absolute; + font-size: 10px; + font-family: Arial, Helvetica, sans-serif; + color: #898989; + top: 4px; + right: 11px; + text-align: right; + margin: 0; + padding: 0; + height: 11px; +} +.colorpicker_hex { + position: absolute; + width: 72px; + height: 22px; + background: url(../images/colorpicker_hex.png) top; + left: 212px; + top: 142px; +} +.colorpicker_hex input { + right: 6px; +} +.colorpicker_field { + height: 22px; + width: 62px; + background-position: top; + position: absolute; +} +.colorpicker_field span { + position: absolute; + width: 12px; + height: 22px; + overflow: hidden; + top: 0; + right: 0; + cursor: n-resize; +} +.colorpicker_rgb_r { + background-image: url(../images/colorpicker_rgb_r.png); + top: 52px; + left: 212px; +} +.colorpicker_rgb_g { + background-image: url(../images/colorpicker_rgb_g.png); + top: 82px; + left: 212px; +} +.colorpicker_rgb_b { + background-image: url(../images/colorpicker_rgb_b.png); + top: 112px; + left: 212px; +} +.colorpicker_hsb_h { + background-image: url(../images/colorpicker_hsb_h.png); + top: 52px; + left: 282px; +} +.colorpicker_hsb_s { + background-image: url(../images/colorpicker_hsb_s.png); + top: 82px; + left: 282px; +} +.colorpicker_hsb_b { + background-image: url(../images/colorpicker_hsb_b.png); + top: 112px; + left: 282px; +} +.colorpicker_submit { + position: absolute; + width: 22px; + height: 22px; + background: url(../images/colorpicker_submit.png) top; + left: 322px; + top: 142px; + overflow: hidden; +} +.colorpicker_focus { + background-position: center; +} +.colorpicker_hex.colorpicker_focus { + background-position: bottom; +} +.colorpicker_submit.colorpicker_focus { + background-position: bottom; +} +.colorpicker_slider { + background-position: bottom; +} diff --git a/js/colorpicker/css/layout.css b/js/colorpicker/css/layout.css index 92675bcf0..396e671c6 100644 --- a/js/colorpicker/css/layout.css +++ b/js/colorpicker/css/layout.css @@ -1,218 +1,218 @@ -/*body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td { - margin:0; - padding:0; -} -table { - border-collapse:collapse; - border-spacing:0; -} -fieldset,img { - border:0; -} -address,caption,cite,code,dfn,em,strong,th,var { - font-style:normal; - font-weight:normal; -} -ol,ul { - list-style:none; -} -caption,th { - text-align:left; -} -h1,h2,h3,h4,h5,h6 { - font-size:100%; - font-weight:normal; -} -q:before,q:after { - content:''; -} -abbr,acronym { border:0; -} -html, body { - background-color: #fff; - font-family: Arial, Helvetica, sans-serif; - font-size: 12px; - line-height: 18px; - color: #52697E; -} -body { - text-align: center; - overflow: auto; -} -.wrapper { - width: 700px; - margin: 0 auto; - text-align: left; -} -h1 { - font-size: 21px; - height: 47px; - line-height: 47px; - text-transform: uppercase; -} -.navigationTabs { - height: 23px; - line-height: 23px; - border-bottom: 1px solid #ccc; -} -.navigationTabs li { - float: left; - height: 23px; - line-height: 23px; - padding-right: 3px; -} -.navigationTabs li a{ - float: left; - dispaly: block; - height: 23px; - line-height: 23px; - padding: 0 10px; - overflow: hidden; - color: #52697E; - background-color: #eee; - position: relative; - text-decoration: none; -} -.navigationTabs li a:hover { - background-color: #f0f0f0; -} -.navigationTabs li a.active { - background-color: #fff; - border: 1px solid #ccc; - border-bottom: 0px solid; -} -.tabsContent { - border: 1px solid #ccc; - border-top: 0px solid; - width: 698px; - overflow: hidden; -} -.tab { - padding: 16px; - display: none; -} -.tab h2 { - font-weight: bold; - font-size: 16px; -} -.tab h3 { - font-weight: bold; - font-size: 14px; - margin-top: 20px; -} -.tab p { - margin-top: 16px; - clear: both; -} -.tab ul { - margin-top: 16px; - list-style: disc; -} -.tab li { - margin: 10px 0 0 35px; -} -.tab a { - color: #8FB0CF; -} -.tab strong { - font-weight: bold; -} -.tab pre { - font-size: 11px; - margin-top: 20px; - width: 668px; - overflow: auto; - clear: both; -} -.tab table { - width: 100%; -} -.tab table td { - padding: 6px 10px 6px 0; - vertical-align: top; -} -.tab dt { - margin-top: 16px; -} -*/ -#colorSelector { - position: relative; - width: 36px; - height: 36px; - background: url(../images/select.png); -} -#colorSelector div { - position: absolute; - top: 3px; - left: 3px; - width: 30px; - height: 30px; - background: url(../images/select.png) center; -} -#colorSelector2 { - position: absolute; - top: 0; - left: 0; - width: 36px; - height: 36px; - background: url(../images/select2.png); -} -#colorSelector2 div { - position: absolute; - top: 4px; - left: 4px; - width: 28px; - height: 28px; - background: url(../images/select2.png) center; -} -#colorpickerHolder2 { - top: 32px; - left: 0; - width: 356px; - height: 0; - overflow: hidden; - position: absolute; -} -#colorpickerHolder2 .colorpicker { - background-image: url(../images/custom_background.png); - position: absolute; - bottom: 0; - left: 0; -} -#colorpickerHolder2 .colorpicker_hue div { - background-image: url(../images/custom_indic.gif); -} -#colorpickerHolder2 .colorpicker_hex { - background-image: url(../images/custom_hex.png); -} -#colorpickerHolder2 .colorpicker_rgb_r { - background-image: url(../images/custom_rgb_r.png); -} -#colorpickerHolder2 .colorpicker_rgb_g { - background-image: url(../images/custom_rgb_g.png); -} -#colorpickerHolder2 .colorpicker_rgb_b { - background-image: url(../images/custom_rgb_b.png); -} -#colorpickerHolder2 .colorpicker_hsb_s { - background-image: url(../images/custom_hsb_s.png); - display: none; -} -#colorpickerHolder2 .colorpicker_hsb_h { - background-image: url(../images/custom_hsb_h.png); - display: none; -} -#colorpickerHolder2 .colorpicker_hsb_b { - background-image: url(../images/custom_hsb_b.png); - display: none; -} -#colorpickerHolder2 .colorpicker_submit { - background-image: url(../images/custom_submit.png); -} -#colorpickerHolder2 .colorpicker input { - color: #778398; -} -#customWidget { - position: relative; - height: 36px; -} +/*body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td { + margin:0; + padding:0; +} +table { + border-collapse:collapse; + border-spacing:0; +} +fieldset,img { + border:0; +} +address,caption,cite,code,dfn,em,strong,th,var { + font-style:normal; + font-weight:normal; +} +ol,ul { + list-style:none; +} +caption,th { + text-align:left; +} +h1,h2,h3,h4,h5,h6 { + font-size:100%; + font-weight:normal; +} +q:before,q:after { + content:''; +} +abbr,acronym { border:0; +} +html, body { + background-color: #fff; + font-family: Arial, Helvetica, sans-serif; + font-size: 12px; + line-height: 18px; + color: #52697E; +} +body { + text-align: center; + overflow: auto; +} +.wrapper { + width: 700px; + margin: 0 auto; + text-align: left; +} +h1 { + font-size: 21px; + height: 47px; + line-height: 47px; + text-transform: uppercase; +} +.navigationTabs { + height: 23px; + line-height: 23px; + border-bottom: 1px solid #ccc; +} +.navigationTabs li { + float: left; + height: 23px; + line-height: 23px; + padding-right: 3px; +} +.navigationTabs li a{ + float: left; + dispaly: block; + height: 23px; + line-height: 23px; + padding: 0 10px; + overflow: hidden; + color: #52697E; + background-color: #eee; + position: relative; + text-decoration: none; +} +.navigationTabs li a:hover { + background-color: #f0f0f0; +} +.navigationTabs li a.active { + background-color: #fff; + border: 1px solid #ccc; + border-bottom: 0px solid; +} +.tabsContent { + border: 1px solid #ccc; + border-top: 0px solid; + width: 698px; + overflow: hidden; +} +.tab { + padding: 16px; + display: none; +} +.tab h2 { + font-weight: bold; + font-size: 16px; +} +.tab h3 { + font-weight: bold; + font-size: 14px; + margin-top: 20px; +} +.tab p { + margin-top: 16px; + clear: both; +} +.tab ul { + margin-top: 16px; + list-style: disc; +} +.tab li { + margin: 10px 0 0 35px; +} +.tab a { + color: #8FB0CF; +} +.tab strong { + font-weight: bold; +} +.tab pre { + font-size: 11px; + margin-top: 20px; + width: 668px; + overflow: auto; + clear: both; +} +.tab table { + width: 100%; +} +.tab table td { + padding: 6px 10px 6px 0; + vertical-align: top; +} +.tab dt { + margin-top: 16px; +} +*/ +#colorSelector { + position: relative; + width: 36px; + height: 36px; + background: url(../images/select.png); +} +#colorSelector div { + position: absolute; + top: 3px; + left: 3px; + width: 30px; + height: 30px; + background: url(../images/select.png) center; +} +#colorSelector2 { + position: absolute; + top: 0; + left: 0; + width: 36px; + height: 36px; + background: url(../images/select2.png); +} +#colorSelector2 div { + position: absolute; + top: 4px; + left: 4px; + width: 28px; + height: 28px; + background: url(../images/select2.png) center; +} +#colorpickerHolder2 { + top: 32px; + left: 0; + width: 356px; + height: 0; + overflow: hidden; + position: absolute; +} +#colorpickerHolder2 .colorpicker { + background-image: url(../images/custom_background.png); + position: absolute; + bottom: 0; + left: 0; +} +#colorpickerHolder2 .colorpicker_hue div { + background-image: url(../images/custom_indic.gif); +} +#colorpickerHolder2 .colorpicker_hex { + background-image: url(../images/custom_hex.png); +} +#colorpickerHolder2 .colorpicker_rgb_r { + background-image: url(../images/custom_rgb_r.png); +} +#colorpickerHolder2 .colorpicker_rgb_g { + background-image: url(../images/custom_rgb_g.png); +} +#colorpickerHolder2 .colorpicker_rgb_b { + background-image: url(../images/custom_rgb_b.png); +} +#colorpickerHolder2 .colorpicker_hsb_s { + background-image: url(../images/custom_hsb_s.png); + display: none; +} +#colorpickerHolder2 .colorpicker_hsb_h { + background-image: url(../images/custom_hsb_h.png); + display: none; +} +#colorpickerHolder2 .colorpicker_hsb_b { + background-image: url(../images/custom_hsb_b.png); + display: none; +} +#colorpickerHolder2 .colorpicker_submit { + background-image: url(../images/custom_submit.png); +} +#colorpickerHolder2 .colorpicker input { + color: #778398; +} +#customWidget { + position: relative; + height: 36px; +} diff --git a/js/colorpicker/js/colorpicker.js b/js/colorpicker/js/colorpicker.js index 45f56ced7..10a2b2244 100644 --- a/js/colorpicker/js/colorpicker.js +++ b/js/colorpicker/js/colorpicker.js @@ -1,484 +1,484 @@ -/** - * - * Color picker - * Author: Stefan Petre www.eyecon.ro - * - * Dual licensed under the MIT and GPL licenses - * - */ -(function ($) { - var ColorPicker = function () { - var - ids = {}, - inAction, - charMin = 65, - visible, - tpl = '<div class="colorpicker"><div class="colorpicker_color"><div><div></div></div></div><div class="colorpicker_hue"><div></div></div><div class="colorpicker_new_color"></div><div class="colorpicker_current_color"></div><div class="colorpicker_hex"><input type="text" maxlength="6" size="6" /></div><div class="colorpicker_rgb_r colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_rgb_g colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_rgb_b colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_h colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_s colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_b colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_submit"></div></div>', - defaults = { - eventName: 'click', - onShow: function () {}, - onBeforeShow: function(){}, - onHide: function () {}, - onChange: function () {}, - onSubmit: function () {}, - color: 'ff0000', - livePreview: true, - flat: false - }, - fillRGBFields = function (hsb, cal) { - var rgb = HSBToRGB(hsb); - $(cal).data('colorpicker').fields - .eq(1).val(rgb.r).end() - .eq(2).val(rgb.g).end() - .eq(3).val(rgb.b).end(); - }, - fillHSBFields = function (hsb, cal) { - $(cal).data('colorpicker').fields - .eq(4).val(hsb.h).end() - .eq(5).val(hsb.s).end() - .eq(6).val(hsb.b).end(); - }, - fillHexFields = function (hsb, cal) { - $(cal).data('colorpicker').fields - .eq(0).val(HSBToHex(hsb)).end(); - }, - setSelector = function (hsb, cal) { - $(cal).data('colorpicker').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100})); - $(cal).data('colorpicker').selectorIndic.css({ - left: parseInt(150 * hsb.s/100, 10), - top: parseInt(150 * (100-hsb.b)/100, 10) - }); - }, - setHue = function (hsb, cal) { - $(cal).data('colorpicker').hue.css('top', parseInt(150 - 150 * hsb.h/360, 10)); - }, - setCurrentColor = function (hsb, cal) { - $(cal).data('colorpicker').currentColor.css('backgroundColor', '#' + HSBToHex(hsb)); - }, - setNewColor = function (hsb, cal) { - $(cal).data('colorpicker').newColor.css('backgroundColor', '#' + HSBToHex(hsb)); - }, - keyDown = function (ev) { - var pressedKey = ev.charCode || ev.keyCode || -1; - if ((pressedKey > charMin && pressedKey <= 90) || pressedKey == 32) { - return false; - } - var cal = $(this).parent().parent(); - if (cal.data('colorpicker').livePreview === true) { - change.apply(this); - } - }, - change = function (ev) { - var cal = $(this).parent().parent(), col; - if (this.parentNode.className.indexOf('_hex') > 0) { - cal.data('colorpicker').color = col = HexToHSB(fixHex(this.value)); - } else if (this.parentNode.className.indexOf('_hsb') > 0) { - cal.data('colorpicker').color = col = fixHSB({ - h: parseInt(cal.data('colorpicker').fields.eq(4).val(), 10), - s: parseInt(cal.data('colorpicker').fields.eq(5).val(), 10), - b: parseInt(cal.data('colorpicker').fields.eq(6).val(), 10) - }); - } else { - cal.data('colorpicker').color = col = RGBToHSB(fixRGB({ - r: parseInt(cal.data('colorpicker').fields.eq(1).val(), 10), - g: parseInt(cal.data('colorpicker').fields.eq(2).val(), 10), - b: parseInt(cal.data('colorpicker').fields.eq(3).val(), 10) - })); - } - if (ev) { - fillRGBFields(col, cal.get(0)); - fillHexFields(col, cal.get(0)); - fillHSBFields(col, cal.get(0)); - } - setSelector(col, cal.get(0)); - setHue(col, cal.get(0)); - setNewColor(col, cal.get(0)); - cal.data('colorpicker').onChange.apply(cal, [col, HSBToHex(col), HSBToRGB(col)]); - }, - blur = function (ev) { - var cal = $(this).parent().parent(); - cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus'); - }, - focus = function () { - charMin = this.parentNode.className.indexOf('_hex') > 0 ? 70 : 65; - $(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus'); - $(this).parent().addClass('colorpicker_focus'); - }, - downIncrement = function (ev) { - var field = $(this).parent().find('input').focus(); - var current = { - el: $(this).parent().addClass('colorpicker_slider'), - max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), - y: ev.pageY, - field: field, - val: parseInt(field.val(), 10), - preview: $(this).parent().parent().data('colorpicker').livePreview - }; - $(document).bind('mouseup', current, upIncrement); - $(document).bind('mousemove', current, moveIncrement); - }, - moveIncrement = function (ev) { - ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val + ev.pageY - ev.data.y, 10)))); - if (ev.data.preview) { - change.apply(ev.data.field.get(0), [true]); - } - return false; - }, - upIncrement = function (ev) { - change.apply(ev.data.field.get(0), [true]); - ev.data.el.removeClass('colorpicker_slider').find('input').focus(); - $(document).unbind('mouseup', upIncrement); - $(document).unbind('mousemove', moveIncrement); - return false; - }, - downHue = function (ev) { - var current = { - cal: $(this).parent(), - y: $(this).offset().top - }; - current.preview = current.cal.data('colorpicker').livePreview; - $(document).bind('mouseup', current, upHue); - $(document).bind('mousemove', current, moveHue); - }, - moveHue = function (ev) { - change.apply( - ev.data.cal.data('colorpicker') - .fields - .eq(4) - .val(parseInt(360*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.y))))/150, 10)) - .get(0), - [ev.data.preview] - ); - return false; - }, - upHue = function (ev) { - fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); - fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); - $(document).unbind('mouseup', upHue); - $(document).unbind('mousemove', moveHue); - return false; - }, - downSelector = function (ev) { - var current = { - cal: $(this).parent(), - pos: $(this).offset() - }; - current.preview = current.cal.data('colorpicker').livePreview; - $(document).bind('mouseup', current, upSelector); - $(document).bind('mousemove', current, moveSelector); - }, - moveSelector = function (ev) { - change.apply( - ev.data.cal.data('colorpicker') - .fields - .eq(6) - .val(parseInt(100*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.pos.top))))/150, 10)) - .end() - .eq(5) - .val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX - ev.data.pos.left))))/150, 10)) - .get(0), - [ev.data.preview] - ); - return false; - }, - upSelector = function (ev) { - fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); - fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); - $(document).unbind('mouseup', upSelector); - $(document).unbind('mousemove', moveSelector); - return false; - }, - enterSubmit = function (ev) { - $(this).addClass('colorpicker_focus'); - }, - leaveSubmit = function (ev) { - $(this).removeClass('colorpicker_focus'); - }, - clickSubmit = function (ev) { - var cal = $(this).parent(); - var col = cal.data('colorpicker').color; - cal.data('colorpicker').origColor = col; - setCurrentColor(col, cal.get(0)); - cal.data('colorpicker').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colorpicker').el); - }, - show = function (ev) { - var cal = $('#' + $(this).data('colorpickerId')); - cal.data('colorpicker').onBeforeShow.apply(this, [cal.get(0)]); - var pos = $(this).offset(); - var viewPort = getViewport(); - var top = pos.top + this.offsetHeight; - var left = pos.left; - if (top + 176 > viewPort.t + viewPort.h) { - top -= this.offsetHeight + 176; - } - if (left + 356 > viewPort.l + viewPort.w) { - left -= 356; - } - cal.css({left: left + 'px', top: top + 'px'}); - if (cal.data('colorpicker').onShow.apply(this, [cal.get(0)]) != false) { - cal.show(); - } - $(document).bind('mousedown', {cal: cal}, hide); - return false; - }, - hide = function (ev) { - if (!isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) { - if (ev.data.cal.data('colorpicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) { - ev.data.cal.hide(); - } - $(document).unbind('mousedown', hide); - } - }, - isChildOf = function(parentEl, el, container) { - if (parentEl == el) { - return true; - } - if (parentEl.contains) { - return parentEl.contains(el); - } - if ( parentEl.compareDocumentPosition ) { - return !!(parentEl.compareDocumentPosition(el) & 16); - } - var prEl = el.parentNode; - while(prEl && prEl != container) { - if (prEl == parentEl) - return true; - prEl = prEl.parentNode; - } - return false; - }, - getViewport = function () { - var m = document.compatMode == 'CSS1Compat'; - return { - l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft), - t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop), - w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth), - h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight) - }; - }, - fixHSB = function (hsb) { - return { - h: Math.min(360, Math.max(0, hsb.h)), - s: Math.min(100, Math.max(0, hsb.s)), - b: Math.min(100, Math.max(0, hsb.b)) - }; - }, - fixRGB = function (rgb) { - return { - r: Math.min(255, Math.max(0, rgb.r)), - g: Math.min(255, Math.max(0, rgb.g)), - b: Math.min(255, Math.max(0, rgb.b)) - }; - }, - fixHex = function (hex) { - var len = 6 - hex.length; - if (len > 0) { - var o = []; - for (var i=0; i<len; i++) { - o.push('0'); - } - o.push(hex); - hex = o.join(''); - } - return hex; - }, - HexToRGB = function (hex) { - var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); - return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)}; - }, - HexToHSB = function (hex) { - return RGBToHSB(HexToRGB(hex)); - }, - RGBToHSB = function (rgb) { - var hsb = { - h: 0, - s: 0, - b: 0 - }; - var min = Math.min(rgb.r, rgb.g, rgb.b); - var max = Math.max(rgb.r, rgb.g, rgb.b); - var delta = max - min; - hsb.b = max; - if (max != 0) { - - } - hsb.s = max != 0 ? 255 * delta / max : 0; - if (hsb.s != 0) { - if (rgb.r == max) { - hsb.h = (rgb.g - rgb.b) / delta; - } else if (rgb.g == max) { - hsb.h = 2 + (rgb.b - rgb.r) / delta; - } else { - hsb.h = 4 + (rgb.r - rgb.g) / delta; - } - } else { - hsb.h = -1; - } - hsb.h *= 60; - if (hsb.h < 0) { - hsb.h += 360; - } - hsb.s *= 100/255; - hsb.b *= 100/255; - return hsb; - }, - HSBToRGB = function (hsb) { - var rgb = {}; - var h = Math.round(hsb.h); - var s = Math.round(hsb.s*255/100); - var v = Math.round(hsb.b*255/100); - if(s == 0) { - rgb.r = rgb.g = rgb.b = v; - } else { - var t1 = v; - var t2 = (255-s)*v/255; - var t3 = (t1-t2)*(h%60)/60; - if(h==360) h = 0; - if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3} - else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} - else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} - else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} - else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} - else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} - else {rgb.r=0; rgb.g=0; rgb.b=0} - } - return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)}; - }, - RGBToHex = function (rgb) { - var hex = [ - rgb.r.toString(16), - rgb.g.toString(16), - rgb.b.toString(16) - ]; - $.each(hex, function (nr, val) { - if (val.length == 1) { - hex[nr] = '0' + val; - } - }); - return hex.join(''); - }, - HSBToHex = function (hsb) { - return RGBToHex(HSBToRGB(hsb)); - }, - restoreOriginal = function () { - var cal = $(this).parent(); - var col = cal.data('colorpicker').origColor; - cal.data('colorpicker').color = col; - fillRGBFields(col, cal.get(0)); - fillHexFields(col, cal.get(0)); - fillHSBFields(col, cal.get(0)); - setSelector(col, cal.get(0)); - setHue(col, cal.get(0)); - setNewColor(col, cal.get(0)); - }; - return { - init: function (opt) { - opt = $.extend({}, defaults, opt||{}); - if (typeof opt.color == 'string') { - opt.color = HexToHSB(opt.color); - } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) { - opt.color = RGBToHSB(opt.color); - } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) { - opt.color = fixHSB(opt.color); - } else { - return this; - } - return this.each(function () { - if (!$(this).data('colorpickerId')) { - var options = $.extend({}, opt); - options.origColor = opt.color; - var id = 'collorpicker_' + parseInt(Math.random() * 1000); - $(this).data('colorpickerId', id); - var cal = $(tpl).attr('id', id); - if (options.flat) { - cal.appendTo(this).show(); - } else { - cal.appendTo(document.body); - } - options.fields = cal - .find('input') - .bind('keyup', keyDown) - .bind('change', change) - .bind('blur', blur) - .bind('focus', focus); - cal - .find('span').bind('mousedown', downIncrement).end() - .find('>div.colorpicker_current_color').bind('click', restoreOriginal); - options.selector = cal.find('div.colorpicker_color').bind('mousedown', downSelector); - options.selectorIndic = options.selector.find('div div'); - options.el = this; - options.hue = cal.find('div.colorpicker_hue div'); - cal.find('div.colorpicker_hue').bind('mousedown', downHue); - options.newColor = cal.find('div.colorpicker_new_color'); - options.currentColor = cal.find('div.colorpicker_current_color'); - cal.data('colorpicker', options); - cal.find('div.colorpicker_submit') - .bind('mouseenter', enterSubmit) - .bind('mouseleave', leaveSubmit) - .bind('click', clickSubmit); - fillRGBFields(options.color, cal.get(0)); - fillHSBFields(options.color, cal.get(0)); - fillHexFields(options.color, cal.get(0)); - setHue(options.color, cal.get(0)); - setSelector(options.color, cal.get(0)); - setCurrentColor(options.color, cal.get(0)); - setNewColor(options.color, cal.get(0)); - if (options.flat) { - cal.css({ - position: 'relative', - display: 'block' - }); - } else { - $(this).bind(options.eventName, show); - } - } - }); - }, - showPicker: function() { - return this.each( function () { - if ($(this).data('colorpickerId')) { - show.apply(this); - } - }); - }, - hidePicker: function() { - return this.each( function () { - if ($(this).data('colorpickerId')) { - $('#' + $(this).data('colorpickerId')).hide(); - } - }); - }, - setColor: function(col) { - if (typeof col == 'string') { - col = HexToHSB(col); - } else if (col.r != undefined && col.g != undefined && col.b != undefined) { - col = RGBToHSB(col); - } else if (col.h != undefined && col.s != undefined && col.b != undefined) { - col = fixHSB(col); - } else { - return this; - } - return this.each(function(){ - if ($(this).data('colorpickerId')) { - var cal = $('#' + $(this).data('colorpickerId')); - cal.data('colorpicker').color = col; - cal.data('colorpicker').origColor = col; - fillRGBFields(col, cal.get(0)); - fillHSBFields(col, cal.get(0)); - fillHexFields(col, cal.get(0)); - setHue(col, cal.get(0)); - setSelector(col, cal.get(0)); - setCurrentColor(col, cal.get(0)); - setNewColor(col, cal.get(0)); - } - }); - } - }; - }(); - $.fn.extend({ - ColorPicker: ColorPicker.init, - ColorPickerHide: ColorPicker.hidePicker, - ColorPickerShow: ColorPicker.showPicker, - ColorPickerSetColor: ColorPicker.setColor - }); +/** + * + * Color picker + * Author: Stefan Petre www.eyecon.ro + * + * Dual licensed under the MIT and GPL licenses + * + */ +(function ($) { + var ColorPicker = function () { + var + ids = {}, + inAction, + charMin = 65, + visible, + tpl = '<div class="colorpicker"><div class="colorpicker_color"><div><div></div></div></div><div class="colorpicker_hue"><div></div></div><div class="colorpicker_new_color"></div><div class="colorpicker_current_color"></div><div class="colorpicker_hex"><input type="text" maxlength="6" size="6" /></div><div class="colorpicker_rgb_r colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_rgb_g colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_rgb_b colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_h colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_s colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_b colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_submit"></div></div>', + defaults = { + eventName: 'click', + onShow: function () {}, + onBeforeShow: function(){}, + onHide: function () {}, + onChange: function () {}, + onSubmit: function () {}, + color: 'ff0000', + livePreview: true, + flat: false + }, + fillRGBFields = function (hsb, cal) { + var rgb = HSBToRGB(hsb); + $(cal).data('colorpicker').fields + .eq(1).val(rgb.r).end() + .eq(2).val(rgb.g).end() + .eq(3).val(rgb.b).end(); + }, + fillHSBFields = function (hsb, cal) { + $(cal).data('colorpicker').fields + .eq(4).val(hsb.h).end() + .eq(5).val(hsb.s).end() + .eq(6).val(hsb.b).end(); + }, + fillHexFields = function (hsb, cal) { + $(cal).data('colorpicker').fields + .eq(0).val(HSBToHex(hsb)).end(); + }, + setSelector = function (hsb, cal) { + $(cal).data('colorpicker').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100})); + $(cal).data('colorpicker').selectorIndic.css({ + left: parseInt(150 * hsb.s/100, 10), + top: parseInt(150 * (100-hsb.b)/100, 10) + }); + }, + setHue = function (hsb, cal) { + $(cal).data('colorpicker').hue.css('top', parseInt(150 - 150 * hsb.h/360, 10)); + }, + setCurrentColor = function (hsb, cal) { + $(cal).data('colorpicker').currentColor.css('backgroundColor', '#' + HSBToHex(hsb)); + }, + setNewColor = function (hsb, cal) { + $(cal).data('colorpicker').newColor.css('backgroundColor', '#' + HSBToHex(hsb)); + }, + keyDown = function (ev) { + var pressedKey = ev.charCode || ev.keyCode || -1; + if ((pressedKey > charMin && pressedKey <= 90) || pressedKey == 32) { + return false; + } + var cal = $(this).parent().parent(); + if (cal.data('colorpicker').livePreview === true) { + change.apply(this); + } + }, + change = function (ev) { + var cal = $(this).parent().parent(), col; + if (this.parentNode.className.indexOf('_hex') > 0) { + cal.data('colorpicker').color = col = HexToHSB(fixHex(this.value)); + } else if (this.parentNode.className.indexOf('_hsb') > 0) { + cal.data('colorpicker').color = col = fixHSB({ + h: parseInt(cal.data('colorpicker').fields.eq(4).val(), 10), + s: parseInt(cal.data('colorpicker').fields.eq(5).val(), 10), + b: parseInt(cal.data('colorpicker').fields.eq(6).val(), 10) + }); + } else { + cal.data('colorpicker').color = col = RGBToHSB(fixRGB({ + r: parseInt(cal.data('colorpicker').fields.eq(1).val(), 10), + g: parseInt(cal.data('colorpicker').fields.eq(2).val(), 10), + b: parseInt(cal.data('colorpicker').fields.eq(3).val(), 10) + })); + } + if (ev) { + fillRGBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + } + setSelector(col, cal.get(0)); + setHue(col, cal.get(0)); + setNewColor(col, cal.get(0)); + cal.data('colorpicker').onChange.apply(cal, [col, HSBToHex(col), HSBToRGB(col)]); + }, + blur = function (ev) { + var cal = $(this).parent().parent(); + cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus'); + }, + focus = function () { + charMin = this.parentNode.className.indexOf('_hex') > 0 ? 70 : 65; + $(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus'); + $(this).parent().addClass('colorpicker_focus'); + }, + downIncrement = function (ev) { + var field = $(this).parent().find('input').focus(); + var current = { + el: $(this).parent().addClass('colorpicker_slider'), + max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), + y: ev.pageY, + field: field, + val: parseInt(field.val(), 10), + preview: $(this).parent().parent().data('colorpicker').livePreview + }; + $(document).bind('mouseup', current, upIncrement); + $(document).bind('mousemove', current, moveIncrement); + }, + moveIncrement = function (ev) { + ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val + ev.pageY - ev.data.y, 10)))); + if (ev.data.preview) { + change.apply(ev.data.field.get(0), [true]); + } + return false; + }, + upIncrement = function (ev) { + change.apply(ev.data.field.get(0), [true]); + ev.data.el.removeClass('colorpicker_slider').find('input').focus(); + $(document).unbind('mouseup', upIncrement); + $(document).unbind('mousemove', moveIncrement); + return false; + }, + downHue = function (ev) { + var current = { + cal: $(this).parent(), + y: $(this).offset().top + }; + current.preview = current.cal.data('colorpicker').livePreview; + $(document).bind('mouseup', current, upHue); + $(document).bind('mousemove', current, moveHue); + }, + moveHue = function (ev) { + change.apply( + ev.data.cal.data('colorpicker') + .fields + .eq(4) + .val(parseInt(360*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.y))))/150, 10)) + .get(0), + [ev.data.preview] + ); + return false; + }, + upHue = function (ev) { + fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + $(document).unbind('mouseup', upHue); + $(document).unbind('mousemove', moveHue); + return false; + }, + downSelector = function (ev) { + var current = { + cal: $(this).parent(), + pos: $(this).offset() + }; + current.preview = current.cal.data('colorpicker').livePreview; + $(document).bind('mouseup', current, upSelector); + $(document).bind('mousemove', current, moveSelector); + }, + moveSelector = function (ev) { + change.apply( + ev.data.cal.data('colorpicker') + .fields + .eq(6) + .val(parseInt(100*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.pos.top))))/150, 10)) + .end() + .eq(5) + .val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX - ev.data.pos.left))))/150, 10)) + .get(0), + [ev.data.preview] + ); + return false; + }, + upSelector = function (ev) { + fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + $(document).unbind('mouseup', upSelector); + $(document).unbind('mousemove', moveSelector); + return false; + }, + enterSubmit = function (ev) { + $(this).addClass('colorpicker_focus'); + }, + leaveSubmit = function (ev) { + $(this).removeClass('colorpicker_focus'); + }, + clickSubmit = function (ev) { + var cal = $(this).parent(); + var col = cal.data('colorpicker').color; + cal.data('colorpicker').origColor = col; + setCurrentColor(col, cal.get(0)); + cal.data('colorpicker').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colorpicker').el); + }, + show = function (ev) { + var cal = $('#' + $(this).data('colorpickerId')); + cal.data('colorpicker').onBeforeShow.apply(this, [cal.get(0)]); + var pos = $(this).offset(); + var viewPort = getViewport(); + var top = pos.top + this.offsetHeight; + var left = pos.left; + if (top + 176 > viewPort.t + viewPort.h) { + top -= this.offsetHeight + 176; + } + if (left + 356 > viewPort.l + viewPort.w) { + left -= 356; + } + cal.css({left: left + 'px', top: top + 'px'}); + if (cal.data('colorpicker').onShow.apply(this, [cal.get(0)]) != false) { + cal.show(); + } + $(document).bind('mousedown', {cal: cal}, hide); + return false; + }, + hide = function (ev) { + if (!isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) { + if (ev.data.cal.data('colorpicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) { + ev.data.cal.hide(); + } + $(document).unbind('mousedown', hide); + } + }, + isChildOf = function(parentEl, el, container) { + if (parentEl == el) { + return true; + } + if (parentEl.contains) { + return parentEl.contains(el); + } + if ( parentEl.compareDocumentPosition ) { + return !!(parentEl.compareDocumentPosition(el) & 16); + } + var prEl = el.parentNode; + while(prEl && prEl != container) { + if (prEl == parentEl) + return true; + prEl = prEl.parentNode; + } + return false; + }, + getViewport = function () { + var m = document.compatMode == 'CSS1Compat'; + return { + l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft), + t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop), + w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth), + h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight) + }; + }, + fixHSB = function (hsb) { + return { + h: Math.min(360, Math.max(0, hsb.h)), + s: Math.min(100, Math.max(0, hsb.s)), + b: Math.min(100, Math.max(0, hsb.b)) + }; + }, + fixRGB = function (rgb) { + return { + r: Math.min(255, Math.max(0, rgb.r)), + g: Math.min(255, Math.max(0, rgb.g)), + b: Math.min(255, Math.max(0, rgb.b)) + }; + }, + fixHex = function (hex) { + var len = 6 - hex.length; + if (len > 0) { + var o = []; + for (var i=0; i<len; i++) { + o.push('0'); + } + o.push(hex); + hex = o.join(''); + } + return hex; + }, + HexToRGB = function (hex) { + var hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); + return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)}; + }, + HexToHSB = function (hex) { + return RGBToHSB(HexToRGB(hex)); + }, + RGBToHSB = function (rgb) { + var hsb = { + h: 0, + s: 0, + b: 0 + }; + var min = Math.min(rgb.r, rgb.g, rgb.b); + var max = Math.max(rgb.r, rgb.g, rgb.b); + var delta = max - min; + hsb.b = max; + if (max != 0) { + + } + hsb.s = max != 0 ? 255 * delta / max : 0; + if (hsb.s != 0) { + if (rgb.r == max) { + hsb.h = (rgb.g - rgb.b) / delta; + } else if (rgb.g == max) { + hsb.h = 2 + (rgb.b - rgb.r) / delta; + } else { + hsb.h = 4 + (rgb.r - rgb.g) / delta; + } + } else { + hsb.h = -1; + } + hsb.h *= 60; + if (hsb.h < 0) { + hsb.h += 360; + } + hsb.s *= 100/255; + hsb.b *= 100/255; + return hsb; + }, + HSBToRGB = function (hsb) { + var rgb = {}; + var h = Math.round(hsb.h); + var s = Math.round(hsb.s*255/100); + var v = Math.round(hsb.b*255/100); + if(s == 0) { + rgb.r = rgb.g = rgb.b = v; + } else { + var t1 = v; + var t2 = (255-s)*v/255; + var t3 = (t1-t2)*(h%60)/60; + if(h==360) h = 0; + if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3} + else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} + else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} + else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} + else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} + else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} + else {rgb.r=0; rgb.g=0; rgb.b=0} + } + return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)}; + }, + RGBToHex = function (rgb) { + var hex = [ + rgb.r.toString(16), + rgb.g.toString(16), + rgb.b.toString(16) + ]; + $.each(hex, function (nr, val) { + if (val.length == 1) { + hex[nr] = '0' + val; + } + }); + return hex.join(''); + }, + HSBToHex = function (hsb) { + return RGBToHex(HSBToRGB(hsb)); + }, + restoreOriginal = function () { + var cal = $(this).parent(); + var col = cal.data('colorpicker').origColor; + cal.data('colorpicker').color = col; + fillRGBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + setSelector(col, cal.get(0)); + setHue(col, cal.get(0)); + setNewColor(col, cal.get(0)); + }; + return { + init: function (opt) { + opt = $.extend({}, defaults, opt||{}); + if (typeof opt.color == 'string') { + opt.color = HexToHSB(opt.color); + } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) { + opt.color = RGBToHSB(opt.color); + } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) { + opt.color = fixHSB(opt.color); + } else { + return this; + } + return this.each(function () { + if (!$(this).data('colorpickerId')) { + var options = $.extend({}, opt); + options.origColor = opt.color; + var id = 'collorpicker_' + parseInt(Math.random() * 1000); + $(this).data('colorpickerId', id); + var cal = $(tpl).attr('id', id); + if (options.flat) { + cal.appendTo(this).show(); + } else { + cal.appendTo(document.body); + } + options.fields = cal + .find('input') + .bind('keyup', keyDown) + .bind('change', change) + .bind('blur', blur) + .bind('focus', focus); + cal + .find('span').bind('mousedown', downIncrement).end() + .find('>div.colorpicker_current_color').bind('click', restoreOriginal); + options.selector = cal.find('div.colorpicker_color').bind('mousedown', downSelector); + options.selectorIndic = options.selector.find('div div'); + options.el = this; + options.hue = cal.find('div.colorpicker_hue div'); + cal.find('div.colorpicker_hue').bind('mousedown', downHue); + options.newColor = cal.find('div.colorpicker_new_color'); + options.currentColor = cal.find('div.colorpicker_current_color'); + cal.data('colorpicker', options); + cal.find('div.colorpicker_submit') + .bind('mouseenter', enterSubmit) + .bind('mouseleave', leaveSubmit) + .bind('click', clickSubmit); + fillRGBFields(options.color, cal.get(0)); + fillHSBFields(options.color, cal.get(0)); + fillHexFields(options.color, cal.get(0)); + setHue(options.color, cal.get(0)); + setSelector(options.color, cal.get(0)); + setCurrentColor(options.color, cal.get(0)); + setNewColor(options.color, cal.get(0)); + if (options.flat) { + cal.css({ + position: 'relative', + display: 'block' + }); + } else { + $(this).bind(options.eventName, show); + } + } + }); + }, + showPicker: function() { + return this.each( function () { + if ($(this).data('colorpickerId')) { + show.apply(this); + } + }); + }, + hidePicker: function() { + return this.each( function () { + if ($(this).data('colorpickerId')) { + $('#' + $(this).data('colorpickerId')).hide(); + } + }); + }, + setColor: function(col) { + if (typeof col == 'string') { + col = HexToHSB(col); + } else if (col.r != undefined && col.g != undefined && col.b != undefined) { + col = RGBToHSB(col); + } else if (col.h != undefined && col.s != undefined && col.b != undefined) { + col = fixHSB(col); + } else { + return this; + } + return this.each(function(){ + if ($(this).data('colorpickerId')) { + var cal = $('#' + $(this).data('colorpickerId')); + cal.data('colorpicker').color = col; + cal.data('colorpicker').origColor = col; + fillRGBFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + setHue(col, cal.get(0)); + setSelector(col, cal.get(0)); + setCurrentColor(col, cal.get(0)); + setNewColor(col, cal.get(0)); + } + }); + } + }; + }(); + $.fn.extend({ + ColorPicker: ColorPicker.init, + ColorPickerHide: ColorPicker.hidePicker, + ColorPickerShow: ColorPicker.showPicker, + ColorPickerSetColor: ColorPicker.setColor + }); })(jQuery) \ No newline at end of file diff --git a/js/functions.js b/js/functions.js index 7fcf00b7e..a3080180b 100644 --- a/js/functions.js +++ b/js/functions.js @@ -1434,14 +1434,8 @@ function refreshDragOption(e) { function refreshLayout() { var elm = $('#pdflayout') var orientation = $('#orientation_opt').val(); - if($('#paper_opt').length==1) - { - var paper = $('#paper_opt').val(); - } - else - { - var paper = 'A4'; - } + var paper = $('#paper_opt').val(); + if (orientation == 'P') { posa = 'x'; posb = 'y'; @@ -1731,24 +1725,5 @@ $(document).ready(function(){ insertQuery(evt.target.id); return false; }); - - $("#export_type").change(function(){ - - if($("#export_type").val()!='pdf') - { - $("#show_grid_opt").attr("disabled","disabled"); - $("#orientation_opt").attr("disabled","disabled"); - $("#with_doc").attr("disabled","disabled"); - $(this).css("background-color","yellow"); - - } - if($("#export_type").val()=='pdf') - { - $("#show_grid_opt").removeAttr("disabled"); - $("#orientation_opt").removeAttr("disabled"); - $("#with_doc").removeAttr("disabled","disabled"); - } - - }); }); diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/APC.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/APC.php index bf12551c7..62e3a1459 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/APC.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/APC.php @@ -1,193 +1,193 @@ -<?php -/** - * PHPExcel - * - * Copyright (c) 2006 - 2010 PHPExcel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL - * @version 1.7.3c, 2010-06-01 - */ - - -/** - * PHPExcel_CachedObjectStorage_APC - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - */ -class PHPExcel_CachedObjectStorage_APC extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { - - private $_cachePrefix = null; - - private $_cacheTime = 600; - - - private function _storeData() { - $this->_currentObject->detach(); - - if (!apc_store($this->_cachePrefix.$this->_currentObjectID.'.cache',serialize($this->_currentObject),$this->_cacheTime)) { - $this->__destruct(); - throw new Exception('Failed to store cell in APC'); - } - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - $this->_cellCache[$pCoord] = true; - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * @return void - * @return boolean - */ - public function isDataSet($pCoord) { - // Check if the requested entry is the current object, or exists in the cache - if (parent::isDataSet($pCoord)) { - if ($this->_currentObjectID == $pCoord) { - return true; - } - // Check if the requested entry still exists in apc - $success = apc_fetch($this->_cachePrefix.$pCoord.'.cache'); - if ($success === false) { - // Entry no longer exists in APC, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in APC'); - } - return true; - } - return false; - } // function isDataSet() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (parent::isDataSet($pCoord)) { - $obj = apc_fetch($this->_cachePrefix.$pCoord.'.cache'); - if ($obj === false) { - // Entry no longer exists in APC, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in APC'); - } - } else { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize($obj); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - /** - * Delete a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to delete - * @throws Exception - */ - public function deleteCacheData($pCoord) { - // Delete the entry from APC - apc_delete($this->_cachePrefix.$pCoord.'.cache'); - - // Delete the entry from our cell address array - parent::deleteCacheData($pCoord); - } // function deleteCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - - // Flush the APC cache - $this->__destruct(); - - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - - - public function __construct(PHPExcel_Worksheet $parent, $arguments) { - $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; - - if (is_null($this->_cachePrefix)) { - if (function_exists('posix_getpid')) { - $baseUnique = posix_getpid(); - } else { - $baseUnique = mt_rand(); - } - $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; - $this->_cacheTime = $cacheTime; - - parent::__construct($parent); - } - } // function __construct() - - - public function __destruct() { - $cacheList = $this->getCellList(); - foreach($cacheList as $cellID) { - apc_delete($this->_cachePrefix.$cellID.'.cache'); - } - } // function __destruct() - -} +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + + +/** + * PHPExcel_CachedObjectStorage_APC + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_CachedObjectStorage_APC extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { + + private $_cachePrefix = null; + + private $_cacheTime = 600; + + + private function _storeData() { + $this->_currentObject->detach(); + + if (!apc_store($this->_cachePrefix.$this->_currentObjectID.'.cache',serialize($this->_currentObject),$this->_cacheTime)) { + $this->__destruct(); + throw new Exception('Failed to store cell in APC'); + } + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + $this->_cellCache[$pCoord] = true; + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? + * + * @param string $pCoord Coordinate address of the cell to check + * @return void + * @return boolean + */ + public function isDataSet($pCoord) { + // Check if the requested entry is the current object, or exists in the cache + if (parent::isDataSet($pCoord)) { + if ($this->_currentObjectID == $pCoord) { + return true; + } + // Check if the requested entry still exists in apc + $success = apc_fetch($this->_cachePrefix.$pCoord.'.cache'); + if ($success === false) { + // Entry no longer exists in APC, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in APC'); + } + return true; + } + return false; + } // function isDataSet() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (parent::isDataSet($pCoord)) { + $obj = apc_fetch($this->_cachePrefix.$pCoord.'.cache'); + if ($obj === false) { + // Entry no longer exists in APC, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in APC'); + } + } else { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + $this->_currentObject = unserialize($obj); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + /** + * Delete a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to delete + * @throws Exception + */ + public function deleteCacheData($pCoord) { + // Delete the entry from APC + apc_delete($this->_cachePrefix.$pCoord.'.cache'); + + // Delete the entry from our cell address array + parent::deleteCacheData($pCoord); + } // function deleteCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + + // Flush the APC cache + $this->__destruct(); + + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent, $arguments) { + $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; + + if (is_null($this->_cachePrefix)) { + if (function_exists('posix_getpid')) { + $baseUnique = posix_getpid(); + } else { + $baseUnique = mt_rand(); + } + $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; + $this->_cacheTime = $cacheTime; + + parent::__construct($parent); + } + } // function __construct() + + + public function __destruct() { + $cacheList = $this->getCellList(); + foreach($cacheList as $cellID) { + apc_delete($this->_cachePrefix.$cellID.'.cache'); + } + } // function __destruct() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/CacheBase.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/CacheBase.php index 8a2c25049..9871d90dd 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/CacheBase.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/CacheBase.php @@ -1,153 +1,153 @@ -<?php -/** - * PHPExcel - * - * Copyright (c) 2006 - 2010 PHPExcel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL - * @version 1.7.3c, 2010-06-01 - */ - - -/** - * PHPExcel_CachedObjectStorage_CacheBase - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - */ -class PHPExcel_CachedObjectStorage_CacheBase { - - /** - * Parent worksheet - * - * @var PHPExcel_Worksheet - */ - protected $_parent; - - /** - * The currently active Cell - * - * @var PHPExcel_Cell - */ - protected $_currentObject = null; - - /** - * Coordinate address of the currently active Cell - * - * @var string - */ - protected $_currentObjectID = null; - - - /** - * An array of cells or cell pointers for the worksheet cells held in this cache, - * and indexed by their coordinate address within the worksheet - * - * @var array of mixed - */ - protected $_cellCache = array(); - - - public function __construct(PHPExcel_Worksheet $parent) { - // Set our parent worksheet. - // This is maintained within the cache controller to facilitate re-attaching it to PHPExcel_Cell objects when - // they are woken from a serialized state - $this->_parent = $parent; - } // function __construct() - - - /** - * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * @return void - * @return boolean - */ - public function isDataSet($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return true; - } - // Check if the requested entry exists in the cache - return isset($this->_cellCache[$pCoord]); - } // function isDataSet() - - - /** - * Add or Update a cell in cache - * - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function updateCacheData(PHPExcel_Cell $cell) { - $pCoord = $cell->getCoordinate(); - - return $this->addCacheData($pCoord,$cell); - } // function updateCacheData() - - - /** - * Delete a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to delete - * @throws Exception - */ - public function deleteCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - $this->_currentObject->detach(); - $this->_currentObjectID = $this->_currentObject = null; - } - - if (isset($this->_cellCache[$pCoord])) { - $this->_cellCache[$pCoord]->detach(); - unset($this->_cellCache[$pCoord]); - } - } // function deleteCacheData() - - - /** - * Get a list of all cell addresses currently held in cache - * - * @return array of string - */ - public function getCellList() { - return array_keys($this->_cellCache); - } // function getCellList() - - - /** - * Sort the list of all cell addresses currently held in cache by row and column - * - * @return void - */ - public function getSortedCellList() { - $sortKeys = array(); - foreach ($this->_cellCache as $coord => $value) { - preg_match('/^(\w+)(\d+)$/U',$coord,$matches); - list(,$colNum,$rowNum) = $matches; - $sortKeys[$coord] = str_pad($rowNum . str_pad($colNum,3,'@',STR_PAD_LEFT),12,'0',STR_PAD_LEFT); - } - asort($sortKeys); - - return array_keys($sortKeys); - } // function sortCellList() - -} +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + + +/** + * PHPExcel_CachedObjectStorage_CacheBase + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_CachedObjectStorage_CacheBase { + + /** + * Parent worksheet + * + * @var PHPExcel_Worksheet + */ + protected $_parent; + + /** + * The currently active Cell + * + * @var PHPExcel_Cell + */ + protected $_currentObject = null; + + /** + * Coordinate address of the currently active Cell + * + * @var string + */ + protected $_currentObjectID = null; + + + /** + * An array of cells or cell pointers for the worksheet cells held in this cache, + * and indexed by their coordinate address within the worksheet + * + * @var array of mixed + */ + protected $_cellCache = array(); + + + public function __construct(PHPExcel_Worksheet $parent) { + // Set our parent worksheet. + // This is maintained within the cache controller to facilitate re-attaching it to PHPExcel_Cell objects when + // they are woken from a serialized state + $this->_parent = $parent; + } // function __construct() + + + /** + * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? + * + * @param string $pCoord Coordinate address of the cell to check + * @return void + * @return boolean + */ + public function isDataSet($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return true; + } + // Check if the requested entry exists in the cache + return isset($this->_cellCache[$pCoord]); + } // function isDataSet() + + + /** + * Add or Update a cell in cache + * + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function updateCacheData(PHPExcel_Cell $cell) { + $pCoord = $cell->getCoordinate(); + + return $this->addCacheData($pCoord,$cell); + } // function updateCacheData() + + + /** + * Delete a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to delete + * @throws Exception + */ + public function deleteCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + $this->_currentObject->detach(); + $this->_currentObjectID = $this->_currentObject = null; + } + + if (isset($this->_cellCache[$pCoord])) { + $this->_cellCache[$pCoord]->detach(); + unset($this->_cellCache[$pCoord]); + } + } // function deleteCacheData() + + + /** + * Get a list of all cell addresses currently held in cache + * + * @return array of string + */ + public function getCellList() { + return array_keys($this->_cellCache); + } // function getCellList() + + + /** + * Sort the list of all cell addresses currently held in cache by row and column + * + * @return void + */ + public function getSortedCellList() { + $sortKeys = array(); + foreach ($this->_cellCache as $coord => $value) { + preg_match('/^(\w+)(\d+)$/U',$coord,$matches); + list(,$colNum,$rowNum) = $matches; + $sortKeys[$coord] = str_pad($rowNum . str_pad($colNum,3,'@',STR_PAD_LEFT),12,'0',STR_PAD_LEFT); + } + asort($sortKeys); + + return array_keys($sortKeys); + } // function sortCellList() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/DiscISAM.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/DiscISAM.php index f00c377c2..e685607f7 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/DiscISAM.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/DiscISAM.php @@ -1,143 +1,143 @@ -<?php -/** - * PHPExcel - * - * Copyright (c) 2006 - 2010 PHPExcel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL - * @version 1.7.3c, 2010-06-01 - */ - - -/** - * PHPExcel_CachedObjectStorage_DiscISAM - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - */ -class PHPExcel_CachedObjectStorage_DiscISAM extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { - - private $_fileName = null; - private $_fileHandle = null; - - - private function _storeData() { - $this->_currentObject->detach(); - - fseek($this->_fileHandle,0,SEEK_END); - $offset = ftell($this->_fileHandle); - fwrite($this->_fileHandle, serialize($this->_currentObject)); - $this->_cellCache[$this->_currentObjectID] = array('ptr' => $offset, - 'sz' => ftell($this->_fileHandle) - $offset - ); - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->_cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - fseek($this->_fileHandle,$this->_cellCache[$pCoord]['ptr']); - $this->_currentObject = unserialize(fread($this->_fileHandle,$this->_cellCache[$pCoord]['sz'])); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - - // Close down the temporary cache file - $this->__destruct(); - } // function unsetWorksheetCells() - - - public function __construct(PHPExcel_Worksheet $parent) { - parent::__construct($parent); - if (is_null($this->_fileHandle)) { - if (function_exists('posix_getpid')) { - $baseUnique = posix_getpid(); - } else { - $baseUnique = mt_rand(); - } - $this->_fileName = sys_get_temp_dir().'/PHPExcel.'.uniqid($baseUnique,true).'.cache'; - $this->_fileHandle = fopen($this->_fileName,'a+'); - } - } // function __construct() - - - public function __destruct() { - if (!is_null($this->_fileHandle)) { - fclose($this->_fileHandle); - unlink($this->_fileName); - } - $this->_fileHandle = null; - } // function __destruct() - -} +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + + +/** + * PHPExcel_CachedObjectStorage_DiscISAM + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_CachedObjectStorage_DiscISAM extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { + + private $_fileName = null; + private $_fileHandle = null; + + + private function _storeData() { + $this->_currentObject->detach(); + + fseek($this->_fileHandle,0,SEEK_END); + $offset = ftell($this->_fileHandle); + fwrite($this->_fileHandle, serialize($this->_currentObject)); + $this->_cellCache[$this->_currentObjectID] = array('ptr' => $offset, + 'sz' => ftell($this->_fileHandle) - $offset + ); + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (!isset($this->_cellCache[$pCoord])) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + fseek($this->_fileHandle,$this->_cellCache[$pCoord]['ptr']); + $this->_currentObject = unserialize(fread($this->_fileHandle,$this->_cellCache[$pCoord]['sz'])); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + + // Close down the temporary cache file + $this->__destruct(); + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent) { + parent::__construct($parent); + if (is_null($this->_fileHandle)) { + if (function_exists('posix_getpid')) { + $baseUnique = posix_getpid(); + } else { + $baseUnique = mt_rand(); + } + $this->_fileName = sys_get_temp_dir().'/PHPExcel.'.uniqid($baseUnique,true).'.cache'; + $this->_fileHandle = fopen($this->_fileName,'a+'); + } + } // function __construct() + + + public function __destruct() { + if (!is_null($this->_fileHandle)) { + fclose($this->_fileHandle); + unlink($this->_fileName); + } + $this->_fileHandle = null; + } // function __destruct() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memcache.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memcache.php index d5e9ab1a1..f1fc43c40 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memcache.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memcache.php @@ -1,214 +1,214 @@ -<?php -/** - * PHPExcel - * - * Copyright (c) 2006 - 2010 PHPExcel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL - * @version 1.7.3c, 2010-06-01 - */ - - -/** - * PHPExcel_CachedObjectStorage_Memcache - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - */ -class PHPExcel_CachedObjectStorage_Memcache extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { - - private $_cachePrefix = null; - - private $_cacheTime = 600; - - private $_memcache = null; - - - private function _storeData() { - $this->_currentObject->detach(); - - $obj = serialize($this->_currentObject); - if (!$this->_memcache->replace($this->_cachePrefix.$this->_currentObjectID.'.cache',$obj,NULL,$this->_cacheTime)) { - if (!$this->_memcache->add($this->_cachePrefix.$this->_currentObjectID.'.cache',$obj,NULL,$this->_cacheTime)) { - $this->__destruct(); - throw new Exception('Failed to store cell in Memcache'); - } - } - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - $this->_cellCache[$pCoord] = true; - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * @return void - * @return boolean - */ - public function isDataSet($pCoord) { - // Check if the requested entry is the current object, or exists in the cache - if (parent::isDataSet($pCoord)) { - if ($this->_currentObjectID == $pCoord) { - return true; - } - // Check if the requested entry still exists in apc - $success = $this->_memcache->get($this->_cachePrefix.$pCoord.'.cache'); - if ($success === false) { - // Entry no longer exists in Memcache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in Memcache'); - } - return true; - } - return false; - } // function isDataSet() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (parent::isDataSet($pCoord)) { - $obj = $this->_memcache->get($this->_cachePrefix.$pCoord.'.cache'); - if ($obj === false) { - // Entry no longer exists in Memcache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in Memcache'); - } - } else { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize($obj); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - /** - * Delete a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to delete - * @throws Exception - */ - public function deleteCacheData($pCoord) { - // Delete the entry from Memcache - $this->_memcache->delete($this->_cachePrefix.$pCoord.'.cache'); - - // Delete the entry from our cell address array - parent::deleteCacheData($pCoord); - } // function deleteCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - - // Flush the Memcache cache - $this->__destruct(); - - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - - - public function __construct(PHPExcel_Worksheet $parent, $arguments) { - $memcacheServer = (isset($arguments['memcacheServer'])) ? $arguments['memcacheServer'] : 'localhost'; - $memcachePort = (isset($arguments['memcachePort'])) ? $arguments['memcachePort'] : 11211; - $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; - - if (is_null($this->_cachePrefix)) { - if (function_exists('posix_getpid')) { - $baseUnique = posix_getpid(); - } else { - $baseUnique = mt_rand(); - } - $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; - - // Set a new Memcache object and connect to the Memcache server - $this->_memcache = new Memcache(); - if (!$this->_memcache->addServer($memcacheServer, $memcachePort, false, 50, 5, 5, true, array($this, 'failureCallback')) { - throw new Exception('Could not connect to Memcache server at '.$memcacheServer.':'.$memcachePort); - } - $this->_cacheTime = $cacheTime; - - parent::__construct($parent); - } - } // function __construct() - - - public function failureCallback($host, $port) { - throw new Exception('memcache '.$host.':'.$port' failed'); - } - - - public function __destruct() { - $cacheList = $this->getCellList(); - foreach($cacheList as $cellID) { - $this->_memcache->delete($this->_cachePrefix.$cellID.'.cache'); - } - } // function __destruct() - -} - - -?> +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + + +/** + * PHPExcel_CachedObjectStorage_Memcache + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_CachedObjectStorage_Memcache extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { + + private $_cachePrefix = null; + + private $_cacheTime = 600; + + private $_memcache = null; + + + private function _storeData() { + $this->_currentObject->detach(); + + $obj = serialize($this->_currentObject); + if (!$this->_memcache->replace($this->_cachePrefix.$this->_currentObjectID.'.cache',$obj,NULL,$this->_cacheTime)) { + if (!$this->_memcache->add($this->_cachePrefix.$this->_currentObjectID.'.cache',$obj,NULL,$this->_cacheTime)) { + $this->__destruct(); + throw new Exception('Failed to store cell in Memcache'); + } + } + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + $this->_cellCache[$pCoord] = true; + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? + * + * @param string $pCoord Coordinate address of the cell to check + * @return void + * @return boolean + */ + public function isDataSet($pCoord) { + // Check if the requested entry is the current object, or exists in the cache + if (parent::isDataSet($pCoord)) { + if ($this->_currentObjectID == $pCoord) { + return true; + } + // Check if the requested entry still exists in apc + $success = $this->_memcache->get($this->_cachePrefix.$pCoord.'.cache'); + if ($success === false) { + // Entry no longer exists in Memcache, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in Memcache'); + } + return true; + } + return false; + } // function isDataSet() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (parent::isDataSet($pCoord)) { + $obj = $this->_memcache->get($this->_cachePrefix.$pCoord.'.cache'); + if ($obj === false) { + // Entry no longer exists in Memcache, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in Memcache'); + } + } else { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + $this->_currentObject = unserialize($obj); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + /** + * Delete a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to delete + * @throws Exception + */ + public function deleteCacheData($pCoord) { + // Delete the entry from Memcache + $this->_memcache->delete($this->_cachePrefix.$pCoord.'.cache'); + + // Delete the entry from our cell address array + parent::deleteCacheData($pCoord); + } // function deleteCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + + // Flush the Memcache cache + $this->__destruct(); + + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent, $arguments) { + $memcacheServer = (isset($arguments['memcacheServer'])) ? $arguments['memcacheServer'] : 'localhost'; + $memcachePort = (isset($arguments['memcachePort'])) ? $arguments['memcachePort'] : 11211; + $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; + + if (is_null($this->_cachePrefix)) { + if (function_exists('posix_getpid')) { + $baseUnique = posix_getpid(); + } else { + $baseUnique = mt_rand(); + } + $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; + + // Set a new Memcache object and connect to the Memcache server + $this->_memcache = new Memcache(); + if (!$this->_memcache->addServer($memcacheServer, $memcachePort, false, 50, 5, 5, true, array($this, 'failureCallback')) { + throw new Exception('Could not connect to Memcache server at '.$memcacheServer.':'.$memcachePort); + } + $this->_cacheTime = $cacheTime; + + parent::__construct($parent); + } + } // function __construct() + + + public function failureCallback($host, $port) { + throw new Exception('memcache '.$host.':'.$port' failed'); + } + + + public function __destruct() { + $cacheList = $this->getCellList(); + foreach($cacheList as $cellID) { + $this->_memcache->delete($this->_cachePrefix.$cellID.'.cache'); + } + } // function __destruct() + +} + + +?> diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memory.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memory.php index bbb7cb069..be0fadea6 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memory.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Memory.php @@ -1,85 +1,85 @@ -<?php -/** - * PHPExcel - * - * Copyright (c) 2006 - 2010 PHPExcel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL - * @version 1.7.3c, 2010-06-01 - */ - - -/** - * PHPExcel_CachedObjectStorage_Memory - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - */ -class PHPExcel_CachedObjectStorage_Memory extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - $this->_cellCache[$pCoord] = $cell; - return $cell; - } // function addCacheData() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - // Check if the entry that has been requested actually exists - if (!isset($this->_cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Return requested entry - return $this->_cellCache[$pCoord]; - } // function getCacheData() - - - public function unsetWorksheetCells() { - // Because cells are all stored as intact objects in memory, we need to detach each one from the parent - foreach($this->_cellCache as $k => &$cell) { - $cell->detach(); - $this->_cellCache[$k] = null; - } - unset($cell); - - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - -} +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + + +/** + * PHPExcel_CachedObjectStorage_Memory + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_CachedObjectStorage_Memory extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + $this->_cellCache[$pCoord] = $cell; + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + // Check if the entry that has been requested actually exists + if (!isset($this->_cellCache[$pCoord])) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Return requested entry + return $this->_cellCache[$pCoord]; + } // function getCacheData() + + + public function unsetWorksheetCells() { + // Because cells are all stored as intact objects in memory, we need to detach each one from the parent + foreach($this->_cellCache as $k => &$cell) { + $cell->detach(); + $this->_cellCache[$k] = null; + } + unset($cell); + + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemoryGZip.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemoryGZip.php index 529b6bf27..c28138907 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemoryGZip.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemoryGZip.php @@ -1,107 +1,107 @@ -<?php -/** - * PHPExcel - * - * Copyright (c) 2006 - 2010 PHPExcel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL - * @version 1.7.3c, 2010-06-01 - */ - - -/** - * PHPExcel_CachedObjectStorage_MemoryGZip - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - */ -class PHPExcel_CachedObjectStorage_MemoryGZip extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { - - private function _storeData() { - $this->_currentObject->detach(); - - $this->_cellCache[$this->_currentObjectID] = gzdeflate(serialize($this->_currentObject)); - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->_cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize(gzinflate($this->_cellCache[$pCoord])); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - -} +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + + +/** + * PHPExcel_CachedObjectStorage_MemoryGZip + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_CachedObjectStorage_MemoryGZip extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { + + private function _storeData() { + $this->_currentObject->detach(); + + $this->_cellCache[$this->_currentObjectID] = gzdeflate(serialize($this->_currentObject)); + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (!isset($this->_cellCache[$pCoord])) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + $this->_currentObject = unserialize(gzinflate($this->_cellCache[$pCoord])); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemorySerialized.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemorySerialized.php index cbc1dc152..9c29ea245 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemorySerialized.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/MemorySerialized.php @@ -1,107 +1,107 @@ -<?php -/** - * PHPExcel - * - * Copyright (c) 2006 - 2010 PHPExcel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL - * @version 1.7.3c, 2010-06-01 - */ - - -/** - * PHPExcel_CachedObjectStorage_MemorySerialized - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - */ -class PHPExcel_CachedObjectStorage_MemorySerialized extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { - - private function _storeData() { - $this->_currentObject->detach(); - - $this->_cellCache[$this->_currentObjectID] = serialize($this->_currentObject); - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->_cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize($this->_cellCache[$pCoord]); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - -} +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + + +/** + * PHPExcel_CachedObjectStorage_MemorySerialized + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_CachedObjectStorage_MemorySerialized extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { + + private function _storeData() { + $this->_currentObject->detach(); + + $this->_cellCache[$this->_currentObjectID] = serialize($this->_currentObject); + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (!isset($this->_cellCache[$pCoord])) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + $this->_currentObject = unserialize($this->_cellCache[$pCoord]); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/PHPTemp.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/PHPTemp.php index 3c426a76f..98cd9524c 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/PHPTemp.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/PHPTemp.php @@ -1,137 +1,137 @@ -<?php -/** - * PHPExcel - * - * Copyright (c) 2006 - 2010 PHPExcel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL - * @version 1.7.3c, 2010-06-01 - */ - - -/** - * PHPExcel_CachedObjectStorage_PHPTemp - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - */ -class PHPExcel_CachedObjectStorage_PHPTemp extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { - - private $_fileHandle = null; - - - private function _storeData() { - $this->_currentObject->detach(); - - fseek($this->_fileHandle,0,SEEK_END); - $offset = ftell($this->_fileHandle); - fwrite($this->_fileHandle, serialize($this->_currentObject)); - $this->_cellCache[$this->_currentObjectID] = array('ptr' => $offset, - 'sz' => ftell($this->_fileHandle) - $offset - ); - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - if (!isset($this->_cellCache[$pCoord])) { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - fseek($this->_fileHandle,$this->_cellCache[$pCoord]['ptr']); - $this->_currentObject = unserialize(fread($this->_fileHandle,$this->_cellCache[$pCoord]['sz'])); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - - // Close down the php://temp file - $this->__destruct(); - } // function unsetWorksheetCells() - - - public function __construct(PHPExcel_Worksheet $parent, $memoryCacheSize = '1MB') { - $memoryCacheSize = (isset($arguments['memoryCacheSize'])) ? $arguments['memoryCacheSize'] : '1MB'; - - parent::__construct($parent); - if (is_null($this->_fileHandle)) { - $this->_fileHandle = fopen('php://temp/maxmemory:'.$memoryCacheSize,'a+'); - } - } // function __construct() - - - public function __destruct() { - if (!is_null($this->_fileHandle)) { - fclose($this->_fileHandle); - } - $this->_fileHandle = null; - } // function __destruct() - -} +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + + +/** + * PHPExcel_CachedObjectStorage_PHPTemp + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_CachedObjectStorage_PHPTemp extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { + + private $_fileHandle = null; + + + private function _storeData() { + $this->_currentObject->detach(); + + fseek($this->_fileHandle,0,SEEK_END); + $offset = ftell($this->_fileHandle); + fwrite($this->_fileHandle, serialize($this->_currentObject)); + $this->_cellCache[$this->_currentObjectID] = array('ptr' => $offset, + 'sz' => ftell($this->_fileHandle) - $offset + ); + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + if (!isset($this->_cellCache[$pCoord])) { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + fseek($this->_fileHandle,$this->_cellCache[$pCoord]['ptr']); + $this->_currentObject = unserialize(fread($this->_fileHandle,$this->_cellCache[$pCoord]['sz'])); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + + // Close down the php://temp file + $this->__destruct(); + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent, $memoryCacheSize = '1MB') { + $memoryCacheSize = (isset($arguments['memoryCacheSize'])) ? $arguments['memoryCacheSize'] : '1MB'; + + parent::__construct($parent); + if (is_null($this->_fileHandle)) { + $this->_fileHandle = fopen('php://temp/maxmemory:'.$memoryCacheSize,'a+'); + } + } // function __construct() + + + public function __destruct() { + if (!is_null($this->_fileHandle)) { + fclose($this->_fileHandle); + } + $this->_fileHandle = null; + } // function __destruct() + +} diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Wincache.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Wincache.php index c14bb1a37..d759d7c40 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Wincache.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorage/Wincache.php @@ -1,201 +1,201 @@ -<?php -/** - * PHPExcel - * - * Copyright (c) 2006 - 2010 PHPExcel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL - * @version 1.7.3c, 2010-06-01 - */ - - -/** - * PHPExcel_CachedObjectStorage_Wincache - * - * @category PHPExcel - * @package PHPExcel_CachedObjectStorage - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - */ -class PHPExcel_CachedObjectStorage_Wincache extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { - - private $_cachePrefix = null; - - private $_cacheTime = 600; - - - private function _storeData() { - $this->_currentObject->detach(); - - $obj = serialize($this->_currentObject); - if (wincache_ucache_exists($this->_cachePrefix.$this->_currentObjectID.'.cache')) { - wincache_ucache_set($this->_cachePrefix.$this->_currentObjectID.'.cache', $obj, $this->_cacheTime); - } else { - wincache_ucache_add($this->_cachePrefix.$this->_currentObjectID.'.cache', $obj, $this->_cacheTime); - } - - $this->_currentObjectID = $this->_currentObject = null; - } // function _storeData() - - - /** - * Add or Update a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to update - * @param PHPExcel_Cell $cell Cell to update - * @return void - * @throws Exception - */ - public function addCacheData($pCoord, PHPExcel_Cell $cell) { - if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { - $this->_storeData(); - } - $this->_cellCache[$pCoord] = true; - - $this->_currentObjectID = $pCoord; - $this->_currentObject = $cell; - - return $cell; - } // function addCacheData() - - - /** - * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? - * - * @param string $pCoord Coordinate address of the cell to check - * @return void - * @return boolean - */ - public function isDataSet($pCoord) { - // Check if the requested entry is the current object, or exists in the cache - if (parent::isDataSet($pCoord)) { - if ($this->_currentObjectID == $pCoord) { - return true; - } - // Check if the requested entry still exists in cache - $success = wincache_ucache_exists($this->_cachePrefix.$pCoord.'.cache'); - if ($success === false) { - // Entry no longer exists in Wincache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in Wincache'); - } - return true; - } - return false; - } // function isDataSet() - - - /** - * Get cell at a specific coordinate - * - * @param string $pCoord Coordinate of the cell - * @throws Exception - * @return PHPExcel_Cell Cell that was found, or null if not found - */ - public function getCacheData($pCoord) { - if ($pCoord === $this->_currentObjectID) { - return $this->_currentObject; - } - $this->_storeData(); - - // Check if the entry that has been requested actually exists - $obj = null; - if (parent::isDataSet($pCoord)) { - $success = false; - $obj = wincache_ucache_get($this->_cachePrefix.$pCoord.'.cache', $success); - if ($success === false) { - // Entry no longer exists in Wincache, so clear it from the cache array - parent::deleteCacheData($pCoord); - throw new Exception('Cell entry no longer exists in Wincache'); - } - } else { - // Return null if requested entry doesn't exist in cache - return null; - } - - // Set current entry to the requested entry - $this->_currentObjectID = $pCoord; - $this->_currentObject = unserialize($obj); - // Re-attach the parent worksheet - $this->_currentObject->attach($this->_parent); - - // Return requested entry - return $this->_currentObject; - } // function getCacheData() - - - /** - * Delete a cell in cache identified by coordinate address - * - * @param string $pCoord Coordinate address of the cell to delete - * @throws Exception - */ - public function deleteCacheData($pCoord) { - // Delete the entry from Wincache - wincache_ucache_delete($this->_cachePrefix.$pCoord.'.cache'); - - // Delete the entry from our cell address array - parent::deleteCacheData($pCoord); - } // function deleteCacheData() - - - public function unsetWorksheetCells() { - if(!is_null($this->_currentObject)) { - $this->_currentObject->detach(); - $this->_currentObject = $this->_currentObjectID = null; - } - - // Flush the Wincache cache - $this->__destruct(); - - $this->_cellCache = array(); - - // detach ourself from the worksheet, so that it can then delete this object successfully - $this->_parent = null; - } // function unsetWorksheetCells() - - - public function __construct(PHPExcel_Worksheet $parent, $arguments) { - $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; - - if (is_null($this->_cachePrefix)) { - if (function_exists('posix_getpid')) { - $baseUnique = posix_getpid(); - } else { - $baseUnique = mt_rand(); - } - $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; - $this->_cacheTime = $cacheTime; - - parent::__construct($parent); - } - } // function __construct() - - - public function __destruct() { - $cacheList = $this->getCellList(); - foreach($cacheList as $cellID) { - wincache_ucache_delete($this->_cachePrefix.$cellID.'.cache'); - } - } // function __destruct() - -} - - +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + + +/** + * PHPExcel_CachedObjectStorage_Wincache + * + * @category PHPExcel + * @package PHPExcel_CachedObjectStorage + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_CachedObjectStorage_Wincache extends PHPExcel_CachedObjectStorage_CacheBase implements PHPExcel_CachedObjectStorage_ICache { + + private $_cachePrefix = null; + + private $_cacheTime = 600; + + + private function _storeData() { + $this->_currentObject->detach(); + + $obj = serialize($this->_currentObject); + if (wincache_ucache_exists($this->_cachePrefix.$this->_currentObjectID.'.cache')) { + wincache_ucache_set($this->_cachePrefix.$this->_currentObjectID.'.cache', $obj, $this->_cacheTime); + } else { + wincache_ucache_add($this->_cachePrefix.$this->_currentObjectID.'.cache', $obj, $this->_cacheTime); + } + + $this->_currentObjectID = $this->_currentObject = null; + } // function _storeData() + + + /** + * Add or Update a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to update + * @param PHPExcel_Cell $cell Cell to update + * @return void + * @throws Exception + */ + public function addCacheData($pCoord, PHPExcel_Cell $cell) { + if (($pCoord !== $this->_currentObjectID) && ($this->_currentObjectID !== null)) { + $this->_storeData(); + } + $this->_cellCache[$pCoord] = true; + + $this->_currentObjectID = $pCoord; + $this->_currentObject = $cell; + + return $cell; + } // function addCacheData() + + + /** + * Is a value set in the current PHPExcel_CachedObjectStorage_ICache for an indexed cell? + * + * @param string $pCoord Coordinate address of the cell to check + * @return void + * @return boolean + */ + public function isDataSet($pCoord) { + // Check if the requested entry is the current object, or exists in the cache + if (parent::isDataSet($pCoord)) { + if ($this->_currentObjectID == $pCoord) { + return true; + } + // Check if the requested entry still exists in cache + $success = wincache_ucache_exists($this->_cachePrefix.$pCoord.'.cache'); + if ($success === false) { + // Entry no longer exists in Wincache, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in Wincache'); + } + return true; + } + return false; + } // function isDataSet() + + + /** + * Get cell at a specific coordinate + * + * @param string $pCoord Coordinate of the cell + * @throws Exception + * @return PHPExcel_Cell Cell that was found, or null if not found + */ + public function getCacheData($pCoord) { + if ($pCoord === $this->_currentObjectID) { + return $this->_currentObject; + } + $this->_storeData(); + + // Check if the entry that has been requested actually exists + $obj = null; + if (parent::isDataSet($pCoord)) { + $success = false; + $obj = wincache_ucache_get($this->_cachePrefix.$pCoord.'.cache', $success); + if ($success === false) { + // Entry no longer exists in Wincache, so clear it from the cache array + parent::deleteCacheData($pCoord); + throw new Exception('Cell entry no longer exists in Wincache'); + } + } else { + // Return null if requested entry doesn't exist in cache + return null; + } + + // Set current entry to the requested entry + $this->_currentObjectID = $pCoord; + $this->_currentObject = unserialize($obj); + // Re-attach the parent worksheet + $this->_currentObject->attach($this->_parent); + + // Return requested entry + return $this->_currentObject; + } // function getCacheData() + + + /** + * Delete a cell in cache identified by coordinate address + * + * @param string $pCoord Coordinate address of the cell to delete + * @throws Exception + */ + public function deleteCacheData($pCoord) { + // Delete the entry from Wincache + wincache_ucache_delete($this->_cachePrefix.$pCoord.'.cache'); + + // Delete the entry from our cell address array + parent::deleteCacheData($pCoord); + } // function deleteCacheData() + + + public function unsetWorksheetCells() { + if(!is_null($this->_currentObject)) { + $this->_currentObject->detach(); + $this->_currentObject = $this->_currentObjectID = null; + } + + // Flush the Wincache cache + $this->__destruct(); + + $this->_cellCache = array(); + + // detach ourself from the worksheet, so that it can then delete this object successfully + $this->_parent = null; + } // function unsetWorksheetCells() + + + public function __construct(PHPExcel_Worksheet $parent, $arguments) { + $cacheTime = (isset($arguments['cacheTime'])) ? $arguments['cacheTime'] : 600; + + if (is_null($this->_cachePrefix)) { + if (function_exists('posix_getpid')) { + $baseUnique = posix_getpid(); + } else { + $baseUnique = mt_rand(); + } + $this->_cachePrefix = substr(md5(uniqid($baseUnique,true)),0,8).'.'; + $this->_cacheTime = $cacheTime; + + parent::__construct($parent); + } + } // function __construct() + + + public function __destruct() { + $cacheList = $this->getCellList(); + foreach($cacheList as $cellID) { + wincache_ucache_delete($this->_cachePrefix.$cellID.'.cache'); + } + } // function __destruct() + +} + + ?> \ No newline at end of file diff --git a/libraries/PHPExcel/PHPExcel/CachedObjectStorageFactory.php b/libraries/PHPExcel/PHPExcel/CachedObjectStorageFactory.php index 1486ad11a..d4c947976 100644 --- a/libraries/PHPExcel/PHPExcel/CachedObjectStorageFactory.php +++ b/libraries/PHPExcel/PHPExcel/CachedObjectStorageFactory.php @@ -1,131 +1,131 @@ -<?php - -class PHPExcel_CachedObjectStorageFactory { - const cache_in_memory = 'Memory'; - const cache_in_memory_gzip = 'MemoryGZip'; - const cache_in_memory_serialized = 'MemorySerialized'; - const cache_to_discISAM = 'DiscISAM'; - const cache_to_apc = 'APC'; - const cache_to_memcache = 'Memcache'; - const cache_to_phpTemp = 'PHPTemp'; - const cache_to_wincache = 'Wincache'; - - - private static $_cacheStorageMethod = null; - - private static $_cacheStorageClass = null; - - - private static $_storageMethods = array( - self::cache_in_memory, - self::cache_in_memory_gzip, - self::cache_in_memory_serialized, - self::cache_to_phpTemp, - self::cache_to_discISAM, - self::cache_to_apc, - self::cache_to_memcache, - self::cache_to_wincache, - ); - - - private static $_storageMethodDefaultParameters = array( - self::cache_in_memory => array( - ), - self::cache_in_memory_gzip => array( - ), - self::cache_in_memory_serialized => array( - ), - self::cache_to_phpTemp => array( 'memoryCacheSize' => '1MB' - ), - self::cache_to_discISAM => array( - ), - self::cache_to_apc => array( 'cacheTime' => 600 - ), - self::cache_to_memcache => array( 'memcacheServer' => 'localhost', - 'memcachePort' => 11211, - 'cacheTime' => 600 - ), - self::cache_to_wincache => array( 'cacheTime' => 600 - ) - ); - - - private static $_storageMethodParameters = array(); - - - public static function getCacheStorageMethod() { - if (!is_null(self::$_cacheStorageMethod)) { - return self::$_cacheStorageMethod; - } - return null; - } // function getCacheStorageMethod() - - - public static function getCacheStorageClass() { - if (!is_null(self::$_cacheStorageClass)) { - return self::$_cacheStorageClass; - } - return null; - } // function getCacheStorageClass() - - - public static function getCacheStorageMethods() { - return self::$_storageMethods; - } // function getCacheStorageMethods() - - - public static function initialize($method = self::cache_in_memory, $arguments = array()) { - if (!in_array($method,self::$_storageMethods)) { - return false; - } - - switch($method) { - case self::cache_to_apc : - if (!function_exists('apc_store')) { - return false; - } - if (apc_sma_info() === false) { - return false; - } - break; - case self::cache_to_memcache : - if (!function_exists('memcache_add')) { - return false; - } - break; - case self::cache_to_wincache : - if (!function_exists('wincache_ucache_add')) { - return false; - } - break; - } - - self::$_storageMethodParameters[$method] = self::$_storageMethodDefaultParameters[$method]; - foreach($arguments as $k => $v) { - if (isset(self::$_storageMethodParameters[$method][$k])) { - self::$_storageMethodParameters[$method][$k] = $v; - } - } - - if (is_null(self::$_cacheStorageMethod)) { - self::$_cacheStorageClass = 'PHPExcel_CachedObjectStorage_'.$method; - self::$_cacheStorageMethod = $method; - } - return true; - } // function initialize() - - - public static function getInstance(PHPExcel_Worksheet $parent) { - if (is_null(self::$_cacheStorageMethod)) { - self::initialize(); - } - - $instance = new self::$_cacheStorageClass($parent,self::$_storageMethodParameters[self::$_cacheStorageMethod]); - if (!is_null($instance)) { - return $instance; - } - - return false; - } // function getInstance() - +<?php + +class PHPExcel_CachedObjectStorageFactory { + const cache_in_memory = 'Memory'; + const cache_in_memory_gzip = 'MemoryGZip'; + const cache_in_memory_serialized = 'MemorySerialized'; + const cache_to_discISAM = 'DiscISAM'; + const cache_to_apc = 'APC'; + const cache_to_memcache = 'Memcache'; + const cache_to_phpTemp = 'PHPTemp'; + const cache_to_wincache = 'Wincache'; + + + private static $_cacheStorageMethod = null; + + private static $_cacheStorageClass = null; + + + private static $_storageMethods = array( + self::cache_in_memory, + self::cache_in_memory_gzip, + self::cache_in_memory_serialized, + self::cache_to_phpTemp, + self::cache_to_discISAM, + self::cache_to_apc, + self::cache_to_memcache, + self::cache_to_wincache, + ); + + + private static $_storageMethodDefaultParameters = array( + self::cache_in_memory => array( + ), + self::cache_in_memory_gzip => array( + ), + self::cache_in_memory_serialized => array( + ), + self::cache_to_phpTemp => array( 'memoryCacheSize' => '1MB' + ), + self::cache_to_discISAM => array( + ), + self::cache_to_apc => array( 'cacheTime' => 600 + ), + self::cache_to_memcache => array( 'memcacheServer' => 'localhost', + 'memcachePort' => 11211, + 'cacheTime' => 600 + ), + self::cache_to_wincache => array( 'cacheTime' => 600 + ) + ); + + + private static $_storageMethodParameters = array(); + + + public static function getCacheStorageMethod() { + if (!is_null(self::$_cacheStorageMethod)) { + return self::$_cacheStorageMethod; + } + return null; + } // function getCacheStorageMethod() + + + public static function getCacheStorageClass() { + if (!is_null(self::$_cacheStorageClass)) { + return self::$_cacheStorageClass; + } + return null; + } // function getCacheStorageClass() + + + public static function getCacheStorageMethods() { + return self::$_storageMethods; + } // function getCacheStorageMethods() + + + public static function initialize($method = self::cache_in_memory, $arguments = array()) { + if (!in_array($method,self::$_storageMethods)) { + return false; + } + + switch($method) { + case self::cache_to_apc : + if (!function_exists('apc_store')) { + return false; + } + if (apc_sma_info() === false) { + return false; + } + break; + case self::cache_to_memcache : + if (!function_exists('memcache_add')) { + return false; + } + break; + case self::cache_to_wincache : + if (!function_exists('wincache_ucache_add')) { + return false; + } + break; + } + + self::$_storageMethodParameters[$method] = self::$_storageMethodDefaultParameters[$method]; + foreach($arguments as $k => $v) { + if (isset(self::$_storageMethodParameters[$method][$k])) { + self::$_storageMethodParameters[$method][$k] = $v; + } + } + + if (is_null(self::$_cacheStorageMethod)) { + self::$_cacheStorageClass = 'PHPExcel_CachedObjectStorage_'.$method; + self::$_cacheStorageMethod = $method; + } + return true; + } // function initialize() + + + public static function getInstance(PHPExcel_Worksheet $parent) { + if (is_null(self::$_cacheStorageMethod)) { + self::initialize(); + } + + $instance = new self::$_cacheStorageClass($parent,self::$_storageMethodParameters[self::$_cacheStorageMethod]); + if (!is_null($instance)) { + return $instance; + } + + return false; + } // function getInstance() + } \ No newline at end of file diff --git a/libraries/PHPExcel/PHPExcel/Reader/Excel2007.php b/libraries/PHPExcel/PHPExcel/Reader/Excel2007.php index 43826396a..031dee559 100644 --- a/libraries/PHPExcel/PHPExcel/Reader/Excel2007.php +++ b/libraries/PHPExcel/PHPExcel/Reader/Excel2007.php @@ -160,9 +160,9 @@ class PHPExcel_Reader_Excel2007 implements PHPExcel_Reader_IReader */ public function canRead($pFilename) { - // Check if zip class exists - if (!class_exists('ZipArchive')) { - return false; + // Check if zip class exists + if (!class_exists('ZipArchive')) { + return false; } // Check if file exists diff --git a/libraries/PHPExcel/PHPExcel/Reader/Excel5.php b/libraries/PHPExcel/PHPExcel/Reader/Excel5.php index 0b52918c4..a4f20e7a4 100644 --- a/libraries/PHPExcel/PHPExcel/Reader/Excel5.php +++ b/libraries/PHPExcel/PHPExcel/Reader/Excel5.php @@ -1,6177 +1,6177 @@ -<?php -/** - * PHPExcel - * - * Copyright (c) 2006 - 2010 PHPExcel - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * @category PHPExcel - * @package PHPExcel_Reader_Excel5 - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL - * @version 1.7.3c, 2010-06-01 - */ - -// Original file header of ParseXL (used as the base for this class): -// -------------------------------------------------------------------------------- -// Adapted from Excel_Spreadsheet_Reader developed by users bizon153, -// trex005, and mmp11 (SourceForge.net) -// http://sourceforge.net/projects/phpexcelreader/ -// Primary changes made by canyoncasa (dvc) for ParseXL 1.00 ... -// Modelled moreso after Perl Excel Parse/Write modules -// Added Parse_Excel_Spreadsheet object -// Reads a whole worksheet or tab as row,column array or as -// associated hash of indexed rows and named column fields -// Added variables for worksheet (tab) indexes and names -// Added an object call for loading individual woorksheets -// Changed default indexing defaults to 0 based arrays -// Fixed date/time and percent formats -// Includes patches found at SourceForge... -// unicode patch by nobody -// unpack("d") machine depedency patch by matchy -// boundsheet utf16 patch by bjaenichen -// Renamed functions for shorter names -// General code cleanup and rigor, including <80 column width -// Included a testcase Excel file and PHP example calls -// Code works for PHP 5.x - -// Primary changes made by canyoncasa (dvc) for ParseXL 1.10 ... -// http://sourceforge.net/tracker/index.php?func=detail&aid=1466964&group_id=99160&atid=623334 -// Decoding of formula conditions, results, and tokens. -// Support for user-defined named cells added as an array "namedcells" -// Patch code for user-defined named cells supports single cells only. -// NOTE: this patch only works for BIFF8 as BIFF5-7 use a different -// external sheet reference structure - - -/** PHPExcel root directory */ -if (!defined('PHPEXCEL_ROOT')) { - /** - * @ignore - */ - define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../'); - require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php'); - PHPExcel_Autoloader::Register(); - PHPExcel_Shared_ZipStreamWrapper::register(); - // check mbstring.func_overload - if (ini_get('mbstring.func_overload') & 2) { - throw new Exception('Multibyte function overloading in PHP must be disabled for string functions (2).'); - } -} - -/** - * PHPExcel_Reader_Excel5 - * - * This class uses {@link http://sourceforge.net/projects/phpexcelreader/parseXL} - * - * @category PHPExcel - * @package PHPExcel_Reader_Excel5 - * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) - */ -class PHPExcel_Reader_Excel5 implements PHPExcel_Reader_IReader -{ - // ParseXL definitions - const XLS_BIFF8 = 0x0600; - const XLS_BIFF7 = 0x0500; - const XLS_WorkbookGlobals = 0x0005; - const XLS_Worksheet = 0x0010; - - // record identifiers - const XLS_Type_FORMULA = 0x0006; - const XLS_Type_EOF = 0x000a; - const XLS_Type_PROTECT = 0x0012; - const XLS_Type_OBJECTPROTECT = 0x0063; - const XLS_Type_SCENPROTECT = 0x00dd; - const XLS_Type_PASSWORD = 0x0013; - const XLS_Type_HEADER = 0x0014; - const XLS_Type_FOOTER = 0x0015; - const XLS_Type_EXTERNSHEET = 0x0017; - const XLS_Type_DEFINEDNAME = 0x0018; - const XLS_Type_VERTICALPAGEBREAKS = 0x001a; - const XLS_Type_HORIZONTALPAGEBREAKS = 0x001b; - const XLS_Type_NOTE = 0x001c; - const XLS_Type_SELECTION = 0x001d; - const XLS_Type_DATEMODE = 0x0022; - const XLS_Type_EXTERNNAME = 0x0023; - const XLS_Type_LEFTMARGIN = 0x0026; - const XLS_Type_RIGHTMARGIN = 0x0027; - const XLS_Type_TOPMARGIN = 0x0028; - const XLS_Type_BOTTOMMARGIN = 0x0029; - const XLS_Type_PRINTGRIDLINES = 0x002b; - const XLS_Type_FILEPASS = 0x002f; - const XLS_Type_FONT = 0x0031; - const XLS_Type_CONTINUE = 0x003c; - const XLS_Type_PANE = 0x0041; - const XLS_Type_CODEPAGE = 0x0042; - const XLS_Type_DEFCOLWIDTH = 0x0055; - const XLS_Type_OBJ = 0x005d; - const XLS_Type_COLINFO = 0x007d; - const XLS_Type_IMDATA = 0x007f; - const XLS_Type_SHEETPR = 0x0081; - const XLS_Type_HCENTER = 0x0083; - const XLS_Type_VCENTER = 0x0084; - const XLS_Type_SHEET = 0x0085; - const XLS_Type_PALETTE = 0x0092; - const XLS_Type_SCL = 0x00a0; - const XLS_Type_PAGESETUP = 0x00a1; - const XLS_Type_MULRK = 0x00bd; - const XLS_Type_MULBLANK = 0x00be; - const XLS_Type_DBCELL = 0x00d7; - const XLS_Type_XF = 0x00e0; - const XLS_Type_MERGEDCELLS = 0x00e5; - const XLS_Type_MSODRAWINGGROUP = 0x00eb; - const XLS_Type_MSODRAWING = 0x00ec; - const XLS_Type_SST = 0x00fc; - const XLS_Type_LABELSST = 0x00fd; - const XLS_Type_EXTSST = 0x00ff; - const XLS_Type_EXTERNALBOOK = 0x01ae; - const XLS_Type_DATAVALIDATIONS = 0x01b2; - const XLS_Type_TXO = 0x01b6; - const XLS_Type_HYPERLINK = 0x01b8; - const XLS_Type_DATAVALIDATION = 0x01be; - const XLS_Type_DIMENSION = 0x0200; - const XLS_Type_BLANK = 0x0201; - const XLS_Type_NUMBER = 0x0203; - const XLS_Type_LABEL = 0x0204; - const XLS_Type_BOOLERR = 0x0205; - const XLS_Type_STRING = 0x0207; - const XLS_Type_ROW = 0x0208; - const XLS_Type_INDEX = 0x020b; - const XLS_Type_ARRAY = 0x0221; - const XLS_Type_DEFAULTROWHEIGHT = 0x0225; - const XLS_Type_WINDOW2 = 0x023e; - const XLS_Type_RK = 0x027e; - const XLS_Type_STYLE = 0x0293; - const XLS_Type_FORMAT = 0x041e; - const XLS_Type_SHAREDFMLA = 0x04bc; - const XLS_Type_BOF = 0x0809; - const XLS_Type_SHEETPROTECTION = 0x0867; - const XLS_Type_RANGEPROTECTION = 0x0868; - const XLS_Type_SHEETLAYOUT = 0x0862; - const XLS_Type_XFEXT = 0x087d; - const XLS_Type_UNKNOWN = 0xffff; - - /** - * Read data only? - * - * @var boolean - */ - private $_readDataOnly = false; - - /** - * Restict which sheets should be loaded? - * - * @var array - */ - private $_loadSheetsOnly = null; - - /** - * PHPExcel_Reader_IReadFilter instance - * - * @var PHPExcel_Reader_IReadFilter - */ - private $_readFilter = null; - - /** - * Summary Information stream data. - * - * @var string - */ - private $_summaryInformation; - - /** - * Workbook stream data. (Includes workbook globals substream as well as sheet substreams) - * - * @var string - */ - private $_data; - - /** - * Size in bytes of $this->_data - * - * @var int - */ - private $_dataSize; - - /** - * Current position in stream - * - * @var integer - */ - private $_pos; - - /** - * Workbook to be returned by the reader. - * - * @var PHPExcel - */ - private $_phpExcel; - - /** - * Worksheet that is currently being built by the reader. - * - * @var PHPExcel_Worksheet - */ - private $_phpSheet; - - /** - * BIFF version - * - * @var int - */ - private $_version; - - /** - * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95) - * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE' - * - * @var string - */ - private $_codepage; - - /** - * Shared formats - * - * @var array - */ - private $_formats; - - /** - * Shared fonts - * - * @var array - */ - private $_objFonts; - - /** - * Color palette - * - * @var array - */ - private $_palette; - - /** - * Worksheets - * - * @var array - */ - private $_sheets; - - /** - * External books - * - * @var array - */ - private $_externalBooks; - - /** - * REF structures. Only applies to BIFF8. - * - * @var array - */ - private $_ref; - - /** - * External names - * - * @var array - */ - private $_externalNames; - - /** - * Defined names - * - * @var array - */ - private $_definedname; - - /** - * Shared strings. Only applies to BIFF8. - * - * @var array - */ - private $_sst; - - /** - * Panes are frozen? (in sheet currently being read). See WINDOW2 record. - * - * @var boolean - */ - private $_frozen; - - /** - * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record. - * - * @var boolean - */ - private $_isFitToPages; - - /** - * Objects. One OBJ record contributes with one entry. - * - * @var array - */ - private $_objs; - - /** - * The combined MSODRAWINGGROUP data - * - * @var string - */ - private $_drawingGroupData; - - /** - * The combined MSODRAWING data (per sheet) - * - * @var string - */ - private $_drawingData; - - /** - * Keep track of XF index - * - * @var int - */ - private $_xfIndex; - - /** - * Mapping of XF index (that is a cell XF) to final index in cellXf collection - * - * @var array - */ - private $_mapCellXfIndex; - - /** - * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection - * - * @var array - */ - private $_mapCellStyleXfIndex; - - /** - * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value. - * - * @var array - */ - private $_sharedFormulas; - - /** - * The shared formula parts in a sheet. One FORMULA record contributes with one value if it - * refers to a shared formula. - * - * @var array - */ - private $_sharedFormulaParts; - - /** - * Read data only? - * - * @return boolean - */ - public function getReadDataOnly() - { - return $this->_readDataOnly; - } - - /** - * Set read data only - * - * @param boolean $pValue - * @return PHPExcel_Reader_Excel5 - */ - public function setReadDataOnly($pValue = false) - { - $this->_readDataOnly = $pValue; - return $this; - } - - /** - * Get which sheets to load - * - * @return mixed - */ - public function getLoadSheetsOnly() - { - return $this->_loadSheetsOnly; - } - - /** - * Set which sheets to load - * - * @param mixed $value - * @return PHPExcel_Reader_Excel5 - */ - public function setLoadSheetsOnly($value = null) - { - $this->_loadSheetsOnly = is_array($value) ? - $value : array($value); - return $this; - } - - /** - * Set all sheets to load - * - * @return PHPExcel_Reader_Excel5 - */ - public function setLoadAllSheets() - { - $this->_loadSheetsOnly = null; - return $this; - } - - /** - * Read filter - * - * @return PHPExcel_Reader_IReadFilter - */ - public function getReadFilter() { - return $this->_readFilter; - } - - /** - * Set read filter - * - * @param PHPExcel_Reader_IReadFilter $pValue - * @return PHPExcel_Reader_Excel5 - */ - public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) { - $this->_readFilter = $pValue; - return $this; - } - - /** - * Create a new PHPExcel_Reader_Excel5 instance - */ - public function __construct() { - $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter(); - } - - /** - * Can the current PHPExcel_Reader_IReader read the file? - * - * @param string $pFileName - * @return boolean - */ - public function canRead($pFilename) - { - // Check if file exists - if (!file_exists($pFilename)) { - throw new Exception("Could not open " . $pFilename . " for reading! File does not exist."); - } - - try { - // Use ParseXL for the hard work. - $ole = new PHPExcel_Shared_OLERead(); - - // get excel data - $res = $ole->read($pFilename); - return true; - - } catch (Exception $e) { - return false; - } - } - - /** - * Loads PHPExcel from file - * - * @param string $pFilename - * @return PHPExcel - * @throws Exception - */ - public function load($pFilename) - { - // Read the OLE file - $this->_loadOLE($pFilename); - - // Initialisations - $this->_phpExcel = new PHPExcel; - $this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet - if (!$this->_readDataOnly) { - $this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style - $this->_phpExcel->removeCellXfByIndex(0); // remove the default style - } - - // Read the summary information stream (containing meta data) - $this->_readSummaryInformation(); - - // total byte size of Excel data (workbook global substream + sheet substreams) - $this->_dataSize = strlen($this->_data); - - // initialize - $this->_pos = 0; - $this->_codepage = 'CP1252'; - $this->_formats = array(); - $this->_objFonts = array(); - $this->_palette = array(); - $this->_sheets = array(); - $this->_externalBooks = array(); - $this->_ref = array(); - $this->_definedname = array(); - $this->_sst = array(); - $this->_drawingGroupData = ''; - $this->_xfIndex = ''; - $this->_mapCellXfIndex = array(); - $this->_mapCellStyleXfIndex = array(); - - // Parse Workbook Global Substream - while ($this->_pos < $this->_dataSize) { - $code = $this->_GetInt2d($this->_data, $this->_pos); - - switch ($code) { - case self::XLS_Type_BOF: $this->_readBof(); break; - case self::XLS_Type_FILEPASS: $this->_readFilepass(); break; - case self::XLS_Type_CODEPAGE: $this->_readCodepage(); break; - case self::XLS_Type_DATEMODE: $this->_readDateMode(); break; - case self::XLS_Type_FONT: $this->_readFont(); break; - case self::XLS_Type_FORMAT: $this->_readFormat(); break; - case self::XLS_Type_XF: $this->_readXf(); break; - case self::XLS_Type_XFEXT: $this->_readXfExt(); break; - case self::XLS_Type_STYLE: $this->_readStyle(); break; - case self::XLS_Type_PALETTE: $this->_readPalette(); break; - case self::XLS_Type_SHEET: $this->_readSheet(); break; - case self::XLS_Type_EXTERNALBOOK: $this->_readExternalBook(); break; - case self::XLS_Type_EXTERNNAME: $this->_readExternName(); break; - case self::XLS_Type_EXTERNSHEET: $this->_readExternSheet(); break; - case self::XLS_Type_DEFINEDNAME: $this->_readDefinedName(); break; - case self::XLS_Type_MSODRAWINGGROUP: $this->_readMsoDrawingGroup(); break; - case self::XLS_Type_SST: $this->_readSst(); break; - case self::XLS_Type_EOF: $this->_readDefault(); break 2; - default: $this->_readDefault(); break; - } - } - - // Resolve indexed colors for font, fill, and border colors - // Cannot be resolved already in XF record, because PALETTE record comes afterwards - if (!$this->_readDataOnly) { - foreach ($this->_objFonts as $objFont) { - if (isset($objFont->colorIndex)) { - $color = $this->_readColor($objFont->colorIndex); - $objFont->getColor()->setRGB($color['rgb']); - } - } - - foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) { - // fill start and end color - $fill = $objStyle->getFill(); - - if (isset($fill->startcolorIndex)) { - $startColor = $this->_readColor($fill->startcolorIndex); - $fill->getStartColor()->setRGB($startColor['rgb']); - } - - if (isset($fill->endcolorIndex)) { - $endColor = $this->_readColor($fill->endcolorIndex); - $fill->getEndColor()->setRGB($endColor['rgb']); - } - - // border colors - $top = $objStyle->getBorders()->getTop(); - $right = $objStyle->getBorders()->getRight(); - $bottom = $objStyle->getBorders()->getBottom(); - $left = $objStyle->getBorders()->getLeft(); - $diagonal = $objStyle->getBorders()->getDiagonal(); - - if (isset($top->colorIndex)) { - $borderTopColor = $this->_readColor($top->colorIndex); - $top->getColor()->setRGB($borderTopColor['rgb']); - } - - if (isset($right->colorIndex)) { - $borderRightColor = $this->_readColor($right->colorIndex); - $right->getColor()->setRGB($borderRightColor['rgb']); - } - - if (isset($bottom->colorIndex)) { - $borderBottomColor = $this->_readColor($bottom->colorIndex); - $bottom->getColor()->setRGB($borderBottomColor['rgb']); - } - - if (isset($left->colorIndex)) { - $borderLeftColor = $this->_readColor($left->colorIndex); - $left->getColor()->setRGB($borderLeftColor['rgb']); - } - - if (isset($diagonal->colorIndex)) { - $borderDiagonalColor = $this->_readColor($diagonal->colorIndex); - $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']); - } - } - } - - // treat MSODRAWINGGROUP records, workbook-level Escher - if (!$this->_readDataOnly && $this->_drawingGroupData) { - $escherWorkbook = new PHPExcel_Shared_Escher(); - $reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook); - $escherWorkbook = $reader->load($this->_drawingGroupData); - - // debug Escher stream - //$debug = new Debug_Escher(new PHPExcel_Shared_Escher()); - //$debug->load($this->_drawingGroupData); - } - - // Parse the individual sheets - foreach ($this->_sheets as $sheet) { - - if ($sheet['sheetType'] != 0x00) { - // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module - continue; - } - - // check if sheet should be skipped - if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) { - continue; - } - - // add sheet to PHPExcel object - $this->_phpSheet = $this->_phpExcel->createSheet(); - $this->_phpSheet->setTitle($sheet['name']); - $this->_phpSheet->setSheetState($sheet['sheetState']); - - $this->_pos = $sheet['offset']; - - // Initialize isFitToPages. May change after reading SHEETPR record. - $this->_isFitToPages = false; - - // Initialize drawingData - $this->_drawingData = ''; - - // Initialize objs - $this->_objs = array(); - - // Initialize shared formula parts - $this->_sharedFormulaParts = array(); - - // Initialize shared formulas - $this->_sharedFormulas = array(); - - while ($this->_pos <= $this->_dataSize - 4) { - $code = $this->_GetInt2d($this->_data, $this->_pos); - - switch ($code) { - case self::XLS_Type_BOF: $this->_readBof(); break; - case self::XLS_Type_PRINTGRIDLINES: $this->_readPrintGridlines(); break; - case self::XLS_Type_DEFAULTROWHEIGHT: $this->_readDefaultRowHeight(); break; - case self::XLS_Type_SHEETPR: $this->_readSheetPr(); break; - case self::XLS_Type_HORIZONTALPAGEBREAKS: $this->_readHorizontalPageBreaks(); break; - case self::XLS_Type_VERTICALPAGEBREAKS: $this->_readVerticalPageBreaks(); break; - case self::XLS_Type_HEADER: $this->_readHeader(); break; - case self::XLS_Type_FOOTER: $this->_readFooter(); break; - case self::XLS_Type_HCENTER: $this->_readHcenter(); break; - case self::XLS_Type_VCENTER: $this->_readVcenter(); break; - case self::XLS_Type_LEFTMARGIN: $this->_readLeftMargin(); break; - case self::XLS_Type_RIGHTMARGIN: $this->_readRightMargin(); break; - case self::XLS_Type_TOPMARGIN: $this->_readTopMargin(); break; - case self::XLS_Type_BOTTOMMARGIN: $this->_readBottomMargin(); break; - case self::XLS_Type_PAGESETUP: $this->_readPageSetup(); break; - case self::XLS_Type_PROTECT: $this->_readProtect(); break; - case self::XLS_Type_SCENPROTECT: $this->_readScenProtect(); break; - case self::XLS_Type_OBJECTPROTECT: $this->_readObjectProtect(); break; - case self::XLS_Type_PASSWORD: $this->_readPassword(); break; - case self::XLS_Type_DEFCOLWIDTH: $this->_readDefColWidth(); break; - case self::XLS_Type_COLINFO: $this->_readColInfo(); break; - case self::XLS_Type_DIMENSION: $this->_readDefault(); break; - case self::XLS_Type_ROW: $this->_readRow(); break; - case self::XLS_Type_DBCELL: $this->_readDefault(); break; - case self::XLS_Type_RK: $this->_readRk(); break; - case self::XLS_Type_LABELSST: $this->_readLabelSst(); break; - case self::XLS_Type_MULRK: $this->_readMulRk(); break; - case self::XLS_Type_NUMBER: $this->_readNumber(); break; - case self::XLS_Type_FORMULA: $this->_readFormula(); break; - case self::XLS_Type_SHAREDFMLA: $this->_readSharedFmla(); break; - case self::XLS_Type_BOOLERR: $this->_readBoolErr(); break; - case self::XLS_Type_MULBLANK: $this->_readMulBlank(); break; - case self::XLS_Type_LABEL: $this->_readLabel(); break; - case self::XLS_Type_BLANK: $this->_readBlank(); break; - case self::XLS_Type_MSODRAWING: $this->_readMsoDrawing(); break; - case self::XLS_Type_OBJ: $this->_readObj(); break; - case self::XLS_Type_WINDOW2: $this->_readWindow2(); break; - case self::XLS_Type_SCL: $this->_readScl(); break; - case self::XLS_Type_PANE: $this->_readPane(); break; - case self::XLS_Type_SELECTION: $this->_readSelection(); break; - case self::XLS_Type_MERGEDCELLS: $this->_readMergedCells(); break; - case self::XLS_Type_HYPERLINK: $this->_readHyperLink(); break; - case self::XLS_Type_DATAVALIDATIONS: $this->_readDataValidations(); break; - case self::XLS_Type_DATAVALIDATION: $this->_readDataValidation(); break; - case self::XLS_Type_SHEETLAYOUT: $this->_readSheetLayout(); break; - case self::XLS_Type_SHEETPROTECTION: $this->_readSheetProtection(); break; - case self::XLS_Type_RANGEPROTECTION: $this->_readRangeProtection(); break; - //case self::XLS_Type_IMDATA: $this->_readImData(); break; - case self::XLS_Type_CONTINUE: $this->_readContinue(); break; - case self::XLS_Type_EOF: $this->_readDefault(); break 2; - default: $this->_readDefault(); break; - } - - } - - // treat MSODRAWING records, sheet-level Escher - if (!$this->_readDataOnly && $this->_drawingData) { - $escherWorksheet = new PHPExcel_Shared_Escher(); - $reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet); - $escherWorksheet = $reader->load($this->_drawingData); - - // debug Escher stream - //$debug = new Debug_Escher(new PHPExcel_Shared_Escher()); - //$debug->load($this->_drawingData); - - // get all spContainers in one long array, so they can be mapped to OBJ records - $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers(); - } - - // treat OBJ records - foreach ($this->_objs as $n => $obj) { - - // the first shape container never has a corresponding OBJ record, hence $n + 1 - $spContainer = $allSpContainers[$n + 1]; - - // we skip all spContainers that are a part of a group shape since we cannot yet handle those - if ($spContainer->getNestingLevel() > 1) { - continue; - } - - // calculate the width and height of the shape - list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates()); - list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates()); - - $startOffsetX = $spContainer->getStartOffsetX(); - $startOffsetY = $spContainer->getStartOffsetY(); - $endOffsetX = $spContainer->getEndOffsetX(); - $endOffsetY = $spContainer->getEndOffsetY(); - - $width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX); - $height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY); - - // calculate offsetX and offsetY of the shape - $offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024; - $offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256; - - switch ($obj['type']) { - - case 0x08: - // picture - - // get index to BSE entry (1-based) - $BSEindex = $spContainer->getOPT(0x0104); - $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection(); - $BSE = $BSECollection[$BSEindex - 1]; - $blipType = $BSE->getBlipType(); - - // need check because some blip types are not supported by Escher reader such as EMF - if ($blip = $BSE->getBlip()) { - $ih = imagecreatefromstring($blip->getData()); - $drawing = new PHPExcel_Worksheet_MemoryDrawing(); - $drawing->setImageResource($ih); - - // width, height, offsetX, offsetY - $drawing->setResizeProportional(false); - $drawing->setWidth($width); - $drawing->setHeight($height); - $drawing->setOffsetX($offsetX); - $drawing->setOffsetY($offsetY); - - switch ($blipType) { - - case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG: - $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG); - $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG); - break; - - case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG: - $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG); - $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG); - break; - } - - $drawing->setWorksheet($this->_phpSheet); - $drawing->setCoordinates($spContainer->getStartCoordinates()); - } - - break; - - default: - // other object type - break; - - } - } - - // treat SHAREDFMLA records - if ($this->_version == self::XLS_BIFF8) { - foreach ($this->_sharedFormulaParts as $cell => $baseCell) { - $formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell); - $this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA); - } - } - } - - // add the named ranges (defined names) - foreach ($this->_definedname as $definedName) { - if ($definedName['isBuiltInName']) { - switch ($definedName['name']) { - - case pack('C', 0x06): - // print area - // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2 - - $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma? - - $extractedRanges = array(); - foreach ($ranges as $range) { - // $range should look like one of these - // Foo!$C$7:$J$66 - // Bar!$A$1:$IV$2 - - $explodes = explode('!', $range); - $sheetName = $explodes[0]; - - if (count($explodes) == 2) { - $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66 - } - } - if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) { - $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2 - } - break; - - case pack('C', 0x07): - // print titles (repeating rows) - // Assuming BIFF8, there are 3 cases - // 1. repeating rows - // formula looks like this: Sheet!$A$1:$IV$2 - // rows 1-2 repeat - // 2. repeating columns - // formula looks like this: Sheet!$A$1:$B$65536 - // columns A-B repeat - // 3. both repeating rows and repeating columns - // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2 - - $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma? - - foreach ($ranges as $range) { - // $range should look like this one of these - // Sheet!$A$1:$B$65536 - // Sheet!$A$1:$IV$2 - - $explodes = explode('!', $range); - - if (count($explodes) == 2) { - if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) { - - $extractedRange = $explodes[1]; - $extractedRange = str_replace('$', '', $extractedRange); - - $coordinateStrings = explode(':', $extractedRange); - if (count($coordinateStrings) == 2) { - list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]); - list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]); - - if ($firstColumn == 'A' and $lastColumn == 'IV') { - // then we have repeating rows - $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow)); - } elseif ($firstRow == 1 and $lastRow == 65536) { - // then we have repeating columns - $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn)); - } - } - } - } - } - break; - - } - } else { - // Extract range - $explodes = explode('!', $definedName['formula']); - - if (count($explodes) == 2) { - if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) { - $extractedRange = $explodes[1]; - $extractedRange = str_replace('$', '', $extractedRange); - - $localOnly = ($definedName['scope'] == 0) ? false : true; - $scope = ($definedName['scope'] == 0) ? - null : $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'] - 1]['name']); - - $this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, $localOnly, $scope) ); - } - } - } - } - - return $this->_phpExcel; - } - - /** - * Use OLE reader to extract the relevant data streams from the OLE file - * - * @param string $pFilename - */ - private function _loadOLE($pFilename) - { - // OLE reader - $ole = new PHPExcel_Shared_OLERead(); - - // get excel data - $res = $ole->read($pFilename); - $this->_data = $ole->getWorkBook(); - - // Get summary information data - $this->_summaryInformation = $ole->getSummaryInformation(); - } - - /** - * Read summary information - */ - private function _readSummaryInformation() - { - if (!isset($this->_summaryInformation)) { - return; - } - - // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark) - // offset: 2; size: 2; - // offset: 4; size: 2; OS version - // offset: 6; size: 2; OS indicator - // offset: 8; size: 16 - // offset: 24; size: 4; section count - - // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9 - // offset: 44; size: 4 - - // section header - // offset: 48; size: 4; section length - $secLength = $this->_GetInt4d($this->_summaryInformation, 48); - - // offset: 52; size: 4; property count - $countProperties = $this->_GetInt4d($this->_summaryInformation, 52); - - // initialize code page (used to resolve string values) - $codePage = 'CP1252'; - - // offset: 56; size: var - // loop through property decarations and properties - for ($i = 0; $i < $countProperties; ++$i) { - - // offset: 56 + 8 * $i; size: 4; property ID - $id = $this->_GetInt4d($this->_summaryInformation, 56 + 8 * $i); - - // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48) - $offset = $this->_GetInt4d($this->_summaryInformation, 60 + 8 * $i); - - $type = $this->_GetInt4d($this->_summaryInformation, 48 + $offset); - - // initialize property value - $value = null; - - // extract property value based on property type - switch ($type) { - case 0x02: // 2 byte signed integer - $value = $this->_GetInt2d($this->_summaryInformation, 52 + $offset); - break; - - case 0x03: // 4 byte signed integer - $value = $this->_GetInt4d($this->_summaryInformation, 52 + $offset); - break; - - case 0x13: // 4 byte unsigned integer - // not needed yet, fix later if necessary - break; - - case 0x1E: // null-terminated string prepended by dword string length - $byteLength = $this->_GetInt4d($this->_summaryInformation, 52 + $offset); - $value = substr($this->_summaryInformation, 56 + $offset, $byteLength); - $value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage); - $value = rtrim($value); - break; - - case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601) - // PHP-time - $value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation, 52 + $offset, 8)); - break; - - case 0x47: // Clipboard format - // not needed yet, fix later if necessary - break; - } - - // Use value of property id as appropriate - switch ($id) { - case 0x01: // Code Page - $codePage = PHPExcel_Shared_CodePage::NumberToName($value); - break; - - case 0x02: // Title - $this->_phpExcel->getProperties()->setTitle($value); - break; - - case 0x03: // Subject - $this->_phpExcel->getProperties()->setSubject($value); - break; - - case 0x04: // Author (Creator) - $this->_phpExcel->getProperties()->setCreator($value); - break; - - case 0x05: // Keywords - $this->_phpExcel->getProperties()->setKeywords($value); - break; - - case 0x06: // Comments (Description) - $this->_phpExcel->getProperties()->setDescription($value); - break; - - case 0x08: // Last Saved By (LastModifiedBy) - $this->_phpExcel->getProperties()->setLastModifiedBy($value); - break; - - case 0x09: // Revision - // not supported by PHPExcel - break; - - case 0x0C: // Created - $this->_phpExcel->getProperties()->setCreated($value); - break; - - case 0x0D: // Modified - $this->_phpExcel->getProperties()->setModified($value); - break; - - case 0x12: // Name of creating application - // not supported by PHPExcel - break; - } - } - } - - /** - * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record. - */ - private function _readDefault() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - } - - /** - * Read BOF - */ - private function _readBof() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 2; size: 2; type of the following data - $substreamType = $this->_GetInt2d($recordData, 2); - - switch ($substreamType) { - case self::XLS_WorkbookGlobals: - $version = $this->_GetInt2d($recordData, 0); - if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) { - throw new Exception('Cannot read this Excel file. Version is too old.'); - } - $this->_version = $version; - break; - - case self::XLS_Worksheet: - // do not use this version information for anything - // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream - break; - - default: - // substream, e.g. chart - // just skip the entire substream - do { - $code = $this->_GetInt2d($this->_data, $this->_pos); - $this->_readDefault(); - } while ($code != self::XLS_Type_EOF && $this->_pos < $this->_dataSize); - break; - } - } - - /** - * FILEPASS - * - * This record is part of the File Protection Block. It - * contains information about the read/write password of the - * file. All record contents following this record will be - * encrypted. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readFilepass() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - throw new Exception('Cannot read encrypted file'); - } - - /** - * CODEPAGE - * - * This record stores the text encoding used to write byte - * strings, stored as MS Windows code page identifier. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readCodepage() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; code page identifier - $codepage = $this->_GetInt2d($recordData, 0); - - $this->_codepage = PHPExcel_Shared_CodePage::NumberToName($codepage); - } - - /** - * DATEMODE - * - * This record specifies the base date for displaying date - * values. All dates are stored as count of days past this - * base date. In BIFF2-BIFF4 this record is part of the - * Calculation Settings Block. In BIFF5-BIFF8 it is - * stored in the Workbook Globals Substream. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readDateMode() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; 0 = base 1900, 1 = base 1904 - PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900); - if (ord($recordData{0}) == 1) { - PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904); - } - } - - /** - * Read a FONT record - */ - private function _readFont() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - $objFont = new PHPExcel_Style_Font(); - - // offset: 0; size: 2; height of the font (in twips = 1/20 of a point) - $size = $this->_GetInt2d($recordData, 0); - $objFont->setSize($size / 20); - - // offset: 2; size: 2; option flags - // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8) - // bit: 1; mask 0x0002; italic - $isItalic = (0x0002 & $this->_GetInt2d($recordData, 2)) >> 1; - if ($isItalic) $objFont->setItalic(true); - - // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8) - // bit: 3; mask 0x0008; strike - $isStrike = (0x0008 & $this->_GetInt2d($recordData, 2)) >> 3; - if ($isStrike) $objFont->setStrikethrough(true); - - // offset: 4; size: 2; colour index - $colorIndex = $this->_GetInt2d($recordData, 4); - $objFont->colorIndex = $colorIndex; - - // offset: 6; size: 2; font weight - $weight = $this->_GetInt2d($recordData, 6); - switch ($weight) { - case 0x02BC: - $objFont->setBold(true); - break; - } - - // offset: 8; size: 2; escapement type - $escapement = $this->_GetInt2d($recordData, 8); - switch ($escapement) { - case 0x0001: - $objFont->setSuperScript(true); - break; - case 0x0002: - $objFont->setSubScript(true); - break; - } - - // offset: 10; size: 1; underline type - $underlineType = ord($recordData{10}); - switch ($underlineType) { - case 0x00: - break; // no underline - case 0x01: - $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE); - break; - case 0x02: - $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLE); - break; - case 0x21: - $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING); - break; - case 0x22: - $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING); - break; - } - - // offset: 11; size: 1; font family - // offset: 12; size: 1; character set - // offset: 13; size: 1; not used - // offset: 14; size: var; font name - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringShort(substr($recordData, 14)); - } else { - $string = $this->_readByteStringShort(substr($recordData, 14)); - } - $objFont->setName($string['value']); - - $this->_objFonts[] = $objFont; - } - } - - /** - * FORMAT - * - * This record contains information about a number format. - * All FORMAT records occur together in a sequential list. - * - * In BIFF2-BIFF4 other records referencing a FORMAT record - * contain a zero-based index into this list. From BIFF5 on - * the FORMAT record contains the index itself that will be - * used by other records. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readFormat() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - $indexCode = $this->_GetInt2d($recordData, 0); - - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringLong(substr($recordData, 2)); - } else { - // BIFF7 - $string = $this->_readByteStringShort(substr($recordData, 2)); - } - - $formatString = $string['value']; - $this->_formats[$indexCode] = $formatString; - } - } - - /** - * XF - Extended Format - * - * This record contains formatting information for cells, rows, columns or styles. - * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF - * and 1 cell XF. - * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF - * and XF record 15 is a cell XF - * We only read the first cell style XF and skip the remaining cell style XF records - * We read all cell XF records. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readXf() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - $objStyle = new PHPExcel_Style(); - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; Index to FONT record - if ($this->_GetInt2d($recordData, 0) < 4) { - $fontIndex = $this->_GetInt2d($recordData, 0); - } else { - // this has to do with that index 4 is omitted in all BIFF versions for some strange reason - // check the OpenOffice documentation of the FONT record - $fontIndex = $this->_GetInt2d($recordData, 0) - 1; - } - $objStyle->setFont($this->_objFonts[$fontIndex]); - - // offset: 2; size: 2; Index to FORMAT record - $numberFormatIndex = $this->_GetInt2d($recordData, 2); - if (isset($this->_formats[$numberFormatIndex])) { - // then we have user-defined format code - $numberformat = array('code' => $this->_formats[$numberFormatIndex]); - } elseif (($code = PHPExcel_Style_NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') { - // then we have built-in format code - $numberformat = array('code' => $code); - } else { - // we set the general format code - $numberformat = array('code' => 'General'); - } - $objStyle->getNumberFormat()->setFormatCode($numberformat['code']); - - // offset: 4; size: 2; XF type, cell protection, and parent style XF - // bit 2-0; mask 0x0007; XF_TYPE_PROT - $xfTypeProt = $this->_GetInt2d($recordData, 4); - // bit 0; mask 0x01; 1 = cell is locked - $isLocked = (0x01 & $xfTypeProt) >> 0; - $objStyle->getProtection()->setLocked($isLocked ? - PHPExcel_Style_Protection::PROTECTION_INHERIT : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED); - - // bit 1; mask 0x02; 1 = Formula is hidden - $isHidden = (0x02 & $xfTypeProt) >> 1; - $objStyle->getProtection()->setHidden($isHidden ? - PHPExcel_Style_Protection::PROTECTION_PROTECTED : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED); - - // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF - $isCellStyleXf = (0x04 & $xfTypeProt) >> 2; - - // offset: 6; size: 1; Alignment and text break - // bit 2-0, mask 0x07; horizontal alignment - $horAlign = (0x07 & ord($recordData{6})) >> 0; - switch ($horAlign) { - case 0: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_GENERAL); - break; - case 1: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT); - break; - case 2: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER); - break; - case 3: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT); - break; - case 5: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY); - break; - case 6: - $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS); - break; - } - // bit 3, mask 0x08; wrap text - $wrapText = (0x08 & ord($recordData{6})) >> 3; - switch ($wrapText) { - case 0: - $objStyle->getAlignment()->setWrapText(false); - break; - case 1: - $objStyle->getAlignment()->setWrapText(true); - break; - } - // bit 6-4, mask 0x70; vertical alignment - $vertAlign = (0x70 & ord($recordData{6})) >> 4; - switch ($vertAlign) { - case 0: - $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP); - break; - case 1: - $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER); - break; - case 2: - $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_BOTTOM); - break; - case 3: - $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_JUSTIFY); - break; - } - - if ($this->_version == self::XLS_BIFF8) { - // offset: 7; size: 1; XF_ROTATION: Text rotation angle - $angle = ord($recordData{7}); - $rotation = 0; - if ($angle <= 90) { - $rotation = $angle; - } else if ($angle <= 180) { - $rotation = 90 - $angle; - } else if ($angle == 255) { - $rotation = -165; - } - $objStyle->getAlignment()->setTextRotation($rotation); - - // offset: 8; size: 1; Indentation, shrink to cell size, and text direction - // bit: 3-0; mask: 0x0F; indent level - $indent = (0x0F & ord($recordData{8})) >> 0; - $objStyle->getAlignment()->setIndent($indent); - - // bit: 4; mask: 0x10; 1 = shrink content to fit into cell - $shrinkToFit = (0x10 & ord($recordData{8})) >> 4; - switch ($shrinkToFit) { - case 0: - $objStyle->getAlignment()->setShrinkToFit(false); - break; - case 1: - $objStyle->getAlignment()->setShrinkToFit(true); - break; - } - - // offset: 9; size: 1; Flags used for attribute groups - - // offset: 10; size: 4; Cell border lines and background area - // bit: 3-0; mask: 0x0000000F; left style - if ($bordersLeftStyle = $this->_mapBorderStyle((0x0000000F & $this->_GetInt4d($recordData, 10)) >> 0)) { - $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle); - } - // bit: 7-4; mask: 0x000000F0; right style - if ($bordersRightStyle = $this->_mapBorderStyle((0x000000F0 & $this->_GetInt4d($recordData, 10)) >> 4)) { - $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle); - } - // bit: 11-8; mask: 0x00000F00; top style - if ($bordersTopStyle = $this->_mapBorderStyle((0x00000F00 & $this->_GetInt4d($recordData, 10)) >> 8)) { - $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle); - } - // bit: 15-12; mask: 0x0000F000; bottom style - if ($bordersBottomStyle = $this->_mapBorderStyle((0x0000F000 & $this->_GetInt4d($recordData, 10)) >> 12)) { - $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle); - } - // bit: 22-16; mask: 0x007F0000; left color - $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $this->_GetInt4d($recordData, 10)) >> 16; - - // bit: 29-23; mask: 0x3F800000; right color - $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $this->_GetInt4d($recordData, 10)) >> 23; - - // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom - $diagonalDown = (0x40000000 & $this->_GetInt4d($recordData, 10)) >> 30 ? - true : false; - - // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right - $diagonalUp = (0x80000000 & $this->_GetInt4d($recordData, 10)) >> 31 ? - true : false; - - if ($diagonalUp == false && $diagonalDown == false) { - $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_NONE); - } elseif ($diagonalUp == true && $diagonalDown == false) { - $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_UP); - } elseif ($diagonalUp == false && $diagonalDown == true) { - $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_DOWN); - } elseif ($diagonalUp == true && $diagonalDown == true) { - $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_BOTH); - } - - // offset: 14; size: 4; - // bit: 6-0; mask: 0x0000007F; top color - $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & $this->_GetInt4d($recordData, 14)) >> 0; - - // bit: 13-7; mask: 0x00003F80; bottom color - $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & $this->_GetInt4d($recordData, 14)) >> 7; - - // bit: 20-14; mask: 0x001FC000; diagonal color - $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & $this->_GetInt4d($recordData, 14)) >> 14; - - // bit: 24-21; mask: 0x01E00000; diagonal style - if ($bordersDiagonalStyle = $this->_mapBorderStyle((0x01E00000 & $this->_GetInt4d($recordData, 14)) >> 21)) { - $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle); - } - - // bit: 31-26; mask: 0xFC000000 fill pattern - if ($fillType = $this->_mapFillPattern((0xFC000000 & $this->_GetInt4d($recordData, 14)) >> 26)) { - $objStyle->getFill()->setFillType($fillType); - } - // offset: 18; size: 2; pattern and background colour - // bit: 6-0; mask: 0x007F; color index for pattern color - $objStyle->getFill()->startcolorIndex = (0x007F & $this->_GetInt2d($recordData, 18)) >> 0; - - // bit: 13-7; mask: 0x3F80; color index for pattern background - $objStyle->getFill()->endcolorIndex = (0x3F80 & $this->_GetInt2d($recordData, 18)) >> 7; - } else { - // BIFF5 - - // offset: 7; size: 1; Text orientation and flags - $orientationAndFlags = ord($recordData{7}); - - // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation - $xfOrientation = (0x03 & $orientationAndFlags) >> 0; - switch ($xfOrientation) { - case 0: - $objStyle->getAlignment()->setTextRotation(0); - break; - case 1: - $objStyle->getAlignment()->setTextRotation(-165); - break; - case 2: - $objStyle->getAlignment()->setTextRotation(90); - break; - case 3: - $objStyle->getAlignment()->setTextRotation(-90); - break; - } - - // offset: 8; size: 4; cell border lines and background area - $borderAndBackground = $this->_GetInt4d($recordData, 8); - - // bit: 6-0; mask: 0x0000007F; color index for pattern color - $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0; - - // bit: 13-7; mask: 0x00003F80; color index for pattern background - $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7; - - // bit: 21-16; mask: 0x003F0000; fill pattern - $objStyle->getFill()->setFillType($this->_mapFillPattern((0x003F0000 & $borderAndBackground) >> 16)); - - // bit: 24-22; mask: 0x01C00000; bottom line style - $objStyle->getBorders()->getBottom()->setBorderStyle($this->_mapBorderStyle((0x01C00000 & $borderAndBackground) >> 22)); - - // bit: 31-25; mask: 0xFE000000; bottom line color - $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25; - - // offset: 12; size: 4; cell border lines - $borderLines = $this->_GetInt4d($recordData, 12); - - // bit: 2-0; mask: 0x00000007; top line style - $objStyle->getBorders()->getTop()->setBorderStyle($this->_mapBorderStyle((0x00000007 & $borderLines) >> 0)); - - // bit: 5-3; mask: 0x00000038; left line style - $objStyle->getBorders()->getLeft()->setBorderStyle($this->_mapBorderStyle((0x00000038 & $borderLines) >> 3)); - - // bit: 8-6; mask: 0x000001C0; right line style - $objStyle->getBorders()->getRight()->setBorderStyle($this->_mapBorderStyle((0x000001C0 & $borderLines) >> 6)); - - // bit: 15-9; mask: 0x0000FE00; top line color index - $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9; - - // bit: 22-16; mask: 0x007F0000; left line color index - $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16; - - // bit: 29-23; mask: 0x3F800000; right line color index - $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23; - } - - // add cellStyleXf or cellXf and update mapping - if ($isCellStyleXf) { - // we only read one style XF record which is always the first - if ($this->_xfIndex == 0) { - $this->_phpExcel->addCellStyleXf($objStyle); - $this->_mapCellStyleXfIndex[$this->_xfIndex] = 0; - } - } else { - // we read all cell XF records - $this->_phpExcel->addCellXf($objStyle); - $this->_mapCellXfIndex[$this->_xfIndex] = count($this->_phpExcel->getCellXfCollection()) - 1; - } - - // update XF index for when we read next record - ++$this->_xfIndex; - } - } - - /** - * - */ - private function _readXfExt() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; 0x087D = repeated header - - // offset: 2; size: 2 - - // offset: 4; size: 8; not used - - // offset: 12; size: 2; record version - - // offset: 14; size: 2; index to XF record which this record modifies - $ixfe = $this->_GetInt2d($recordData, 14); - - // offset: 16; size: 2; not used - - // offset: 18; size: 2; number of extension properties that follow - $cexts = $this->_GetInt2d($recordData, 18); - - // start reading the actual extension data - $offset = 20; - while ($offset < $length) { - // extension type - $extType = $this->_GetInt2d($recordData, $offset); - - // extension length - $cb = $this->_GetInt2d($recordData, $offset + 2); - - // extension data - $extData = substr($recordData, $offset + 4, $cb); - - switch ($extType) { - case 4: // fill start color - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill(); - $fill->getStartColor()->setRGB($rgb); - unset($fill->startcolorIndex); // normal color index does not apply, discard - } - } - break; - - case 5: // fill end color - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill(); - $fill->getEndColor()->setRGB($rgb); - unset($fill->endcolorIndex); // normal color index does not apply, discard - } - } - break; - - case 7: // border color top - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $top = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getTop(); - $top->getColor()->setRGB($rgb); - unset($top->colorIndex); // normal color index does not apply, discard - } - } - break; - - case 8: // border color bottom - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $bottom = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getBottom(); - $bottom->getColor()->setRGB($rgb); - unset($bottom->colorIndex); // normal color index does not apply, discard - } - } - break; - - case 9: // border color left - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $left = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getLeft(); - $left->getColor()->setRGB($rgb); - unset($left->colorIndex); // normal color index does not apply, discard - } - } - break; - - case 10: // border color right - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $right = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getRight(); - $right->getColor()->setRGB($rgb); - unset($right->colorIndex); // normal color index does not apply, discard - } - } - break; - - case 11: // border color diagonal - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $diagonal = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getDiagonal(); - $diagonal->getColor()->setRGB($rgb); - unset($diagonal->colorIndex); // normal color index does not apply, discard - } - } - break; - - case 13: // font color - $xclfType = $this->_GetInt2d($extData, 0); // color type - $xclrValue = substr($extData, 4, 4); // color value (value based on color type) - - if ($xclfType == 2) { - $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); - - // modify the relevant style property - if ( isset($this->_mapCellXfIndex[$ixfe]) ) { - $font = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFont(); - $font->getColor()->setRGB($rgb); - unset($font->colorIndex); // normal color index does not apply, discard - } - } - break; - } - - $offset += $cb; - } - } - - } - - /** - * Read STYLE record - */ - private function _readStyle() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; index to XF record and flag for built-in style - $ixfe = $this->_GetInt2d($recordData, 0); - - // bit: 11-0; mask 0x0FFF; index to XF record - $xfIndex = (0x0FFF & $ixfe) >> 0; - - // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style - $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15); - - if ($isBuiltIn) { - // offset: 2; size: 1; identifier for built-in style - $builtInId = ord($recordData{2}); - - switch ($builtInId) { - case 0x00: - // currently, we are not using this for anything - break; - - default: - break; - } - - } else { - // user-defined; not supported by PHPExcel - } - } - } - - /** - * Read PALETTE record - */ - private function _readPalette() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; number of following colors - $nm = $this->_GetInt2d($recordData, 0); - - // list of RGB colors - for ($i = 0; $i < $nm; ++$i) { - $rgb = substr($recordData, 2 + 4 * $i, 4); - $this->_palette[] = $this->_readRGB($rgb); - } - } - } - - /** - * SHEET - * - * This record is located in the Workbook Globals - * Substream and represents a sheet inside the workbook. - * One SHEET record is written for each sheet. It stores the - * sheet name and a stream offset to the BOF record of the - * respective Sheet Substream within the Workbook Stream. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readSheet() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 4; absolute stream position of the BOF record of the sheet - $rec_offset = $this->_GetInt4d($recordData, 0); - - // offset: 4; size: 1; sheet state - switch (ord($recordData{4})) { - case 0x00: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break; - case 0x01: $sheetState = PHPExcel_Worksheet::SHEETSTATE_HIDDEN; break; - case 0x02: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VERYHIDDEN; break; - default: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break; - } - - // offset: 5; size: 1; sheet type - $sheetType = ord($recordData{5}); - - // offset: 6; size: var; sheet name - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringShort(substr($recordData, 6)); - $rec_name = $string['value']; - } elseif ($this->_version == self::XLS_BIFF7) { - $string = $this->_readByteStringShort(substr($recordData, 6)); - $rec_name = $string['value']; - } - - $this->_sheets[] = array( - 'name' => $rec_name, - 'offset' => $rec_offset, - 'sheetState' => $sheetState, - 'sheetType' => $sheetType, - ); - } - - /** - * Read EXTERNALBOOK record - */ - private function _readExternalBook() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset within record data - $offset = 0; - - // there are 4 types of records - if (strlen($recordData) > 4) { - // external reference - // offset: 0; size: 2; number of sheet names ($nm) - $nm = $this->_GetInt2d($recordData, 0); - $offset += 2; - - // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length) - $encodedUrlString = $this->_readUnicodeStringLong(substr($recordData, 2)); - $offset += $encodedUrlString['size']; - - // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length) - $externalSheetNames = array(); - for ($i = 0; $i < $nm; ++$i) { - $externalSheetNameString = $this->_readUnicodeStringLong(substr($recordData, $offset)); - $externalSheetNames[] = $externalSheetNameString['value']; - $offset += $externalSheetNameString['size']; - } - - // store the record data - $this->_externalBooks[] = array( - 'type' => 'external', - 'encodedUrl' => $encodedUrlString['value'], - 'externalSheetNames' => $externalSheetNames, - ); - - } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) { - // internal reference - // offset: 0; size: 2; number of sheet in this document - // offset: 2; size: 2; 0x01 0x04 - $this->_externalBooks[] = array( - 'type' => 'internal', - ); - } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) { - // add-in function - // offset: 0; size: 2; 0x0001 - $this->_externalBooks[] = array( - 'type' => 'addInFunction', - ); - } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) { - // DDE links, OLE links - // offset: 0; size: 2; 0x0000 - // offset: 2; size: var; encoded source document name - $this->_externalBooks[] = array( - 'type' => 'DDEorOLE', - ); - } - } - - /** - * Read EXTERNNAME record. - */ - private function _readExternName() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // external sheet references provided for named cells - if ($this->_version == self::XLS_BIFF8) { - // offset: 0; size: 2; options - $options = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; - - // offset: 4; size: 2; not used - - // offset: 6; size: var - $nameString = $this->_readUnicodeStringShort(substr($recordData, 6)); - - // offset: var; size: var; formula data - $offset = 6 + $nameString['size']; - $formula = $this->_getFormulaFromStructure(substr($recordData, $offset)); - - $this->_externalNames[] = array( - 'name' => $nameString['value'], - 'formula' => $formula, - ); - } - } - - /** - * Read EXTERNSHEET record - */ - private function _readExternSheet() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // external sheet references provided for named cells - if ($this->_version == self::XLS_BIFF8) { - // offset: 0; size: 2; number of following ref structures - $nm = $this->_GetInt2d($recordData, 0); - for ($i = 0; $i < $nm; ++$i) { - $this->_ref[] = array( - // offset: 2 + 6 * $i; index to EXTERNALBOOK record - 'externalBookIndex' => $this->_GetInt2d($recordData, 2 + 6 * $i), - // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record - 'firstSheetIndex' => $this->_GetInt2d($recordData, 4 + 6 * $i), - // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record - 'lastSheetIndex' => $this->_GetInt2d($recordData, 6 + 6 * $i), - ); - } - } - } - - /** - * DEFINEDNAME - * - * This record is part of a Link Table. It contains the name - * and the token array of an internal defined name. Token - * arrays of defined names contain tokens with aberrant - * token classes. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readDefinedName() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8) { - // retrieves named cells - - // offset: 0; size: 2; option flags - $opts = $this->_GetInt2d($recordData, 0); - - // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name - $isBuiltInName = (0x0020 & $opts) >> 5; - - // offset: 2; size: 1; keyboard shortcut - - // offset: 3; size: 1; length of the name (character count) - $nlen = ord($recordData{3}); - - // offset: 4; size: 2; size of the formula data (it can happen that this is zero) - // note: there can also be additional data, this is not included in $flen - $flen = $this->_GetInt2d($recordData, 4); - - // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based) - $scope = $this->_GetInt2d($recordData, 8); - - // offset: 14; size: var; Name (Unicode string without length field) - $string = $this->_readUnicodeString(substr($recordData, 14), $nlen); - - // offset: var; size: $flen; formula data - $offset = 14 + $string['size']; - $formulaStructure = pack('v', $flen) . substr($recordData, $offset); - - try { - $formula = $this->_getFormulaFromStructure($formulaStructure); - } catch (Exception $e) { - $formula = ''; - } - - $this->_definedname[] = array( - 'isBuiltInName' => $isBuiltInName, - 'name' => $string['value'], - 'formula' => $formula, - 'scope' => $scope, - ); - } - } - - /** - * Read MSODRAWINGGROUP record - */ - private function _readMsoDrawingGroup() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - - // get spliced record data - $splicedRecordData = $this->_getSplicedRecordData(); - $recordData = $splicedRecordData['recordData']; - - $this->_drawingGroupData .= $recordData; - } - - /** - * SST - Shared String Table - * - * This record contains a list of all strings used anywhere - * in the workbook. Each string occurs only once. The - * workbook uses indexes into the list to reference the - * strings. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - **/ - private function _readSst() - { - // offset within (spliced) record data - $pos = 0; - - // get spliced record data - $splicedRecordData = $this->_getSplicedRecordData(); - - $recordData = $splicedRecordData['recordData']; - $spliceOffsets = $splicedRecordData['spliceOffsets']; - - // offset: 0; size: 4; total number of strings in the workbook - $pos += 4; - - // offset: 4; size: 4; number of following strings ($nm) - $nm = $this->_GetInt4d($recordData, 4); - $pos += 4; - - // loop through the Unicode strings (16-bit length) - for ($i = 0; $i < $nm; ++$i) { - - // number of characters in the Unicode string - $numChars = $this->_GetInt2d($recordData, $pos); - $pos += 2; - - // option flags - $optionFlags = ord($recordData{$pos}); - ++$pos; - - // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed - $isCompressed = (($optionFlags & 0x01) == 0) ; - - // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic - $hasAsian = (($optionFlags & 0x04) != 0); - - // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text - $hasRichText = (($optionFlags & 0x08) != 0); - - if ($hasRichText) { - // number of Rich-Text formatting runs - $formattingRuns = $this->_GetInt2d($recordData, $pos); - $pos += 2; - } - - if ($hasAsian) { - // size of Asian phonetic setting - $extendedRunLength = $this->_GetInt4d($recordData, $pos); - $pos += 4; - } - - // expected byte length of character array if not split - $len = ($isCompressed) ? $numChars : $numChars * 2; - - // look up limit position - foreach ($spliceOffsets as $spliceOffset) { - // it can happen that the string is empty, therefore we need - // <= and not just < - if ($pos <= $spliceOffset) { - $limitpos = $spliceOffset; - break; - } - } - - if ($pos + $len <= $limitpos) { - // character array is not split between records - - $retstr = substr($recordData, $pos, $len); - $pos += $len; - - } else { - // character array is split between records - - // first part of character array - $retstr = substr($recordData, $pos, $limitpos - $pos); - - $bytesRead = $limitpos - $pos; - - // remaining characters in Unicode string - $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2)); - - $pos = $limitpos; - - // keep reading the characters - while ($charsLeft > 0) { - - // look up next limit position, in case the string span more than one continue record - foreach ($spliceOffsets as $spliceOffset) { - if ($pos < $spliceOffset) { - $limitpos = $spliceOffset; - break; - } - } - - // repeated option flags - // OpenOffice.org documentation 5.21 - $option = ord($recordData{$pos}); - ++$pos; - - if ($isCompressed && ($option == 0)) { - // 1st fragment compressed - // this fragment compressed - $len = min($charsLeft, $limitpos - $pos); - $retstr .= substr($recordData, $pos, $len); - $charsLeft -= $len; - $isCompressed = true; - - } elseif (!$isCompressed && ($option != 0)) { - // 1st fragment uncompressed - // this fragment uncompressed - $len = min($charsLeft * 2, $limitpos - $pos); - $retstr .= substr($recordData, $pos, $len); - $charsLeft -= $len / 2; - $isCompressed = false; - - } elseif (!$isCompressed && ($option == 0)) { - // 1st fragment uncompressed - // this fragment compressed - $len = min($charsLeft, $limitpos - $pos); - for ($j = 0; $j < $len; ++$j) { - $retstr .= $recordData{$pos + $j} . chr(0); - } - $charsLeft -= $len; - $isCompressed = false; - - } else { - // 1st fragment compressed - // this fragment uncompressed - $newstr = ''; - for ($j = 0; $j < strlen($retstr); ++$j) { - $newstr .= $retstr[$j] . chr(0); - } - $retstr = $newstr; - $len = min($charsLeft * 2, $limitpos - $pos); - $retstr .= substr($recordData, $pos, $len); - $charsLeft -= $len / 2; - $isCompressed = false; - } - - $pos += $len; - } - } - - // convert to UTF-8 - $retstr = $this->_encodeUTF16($retstr, $isCompressed); - - // read additional Rich-Text information, if any - $fmtRuns = array(); - if ($hasRichText) { - // list of formatting runs - for ($j = 0; $j < $formattingRuns; ++$j) { - // first formatted character; zero-based - $charPos = $this->_GetInt2d($recordData, $pos + $j * 4); - - // index to font record - $fontIndex = $this->_GetInt2d($recordData, $pos + 2 + $j * 4); - - $fmtRuns[] = array( - 'charPos' => $charPos, - 'fontIndex' => $fontIndex, - ); - } - $pos += 4 * $formattingRuns; - } - - // read additional Asian phonetics information, if any - if ($hasAsian) { - // For Asian phonetic settings, we skip the extended string data - $pos += $extendedRunLength; - } - - // store the shared sting - $this->_sst[] = array( - 'value' => $retstr, - 'fmtRuns' => $fmtRuns, - ); - } - - // _getSplicedRecordData() takes care of moving current position in data stream - } - - /** - * Read PRINTGRIDLINES record - */ - private function _readPrintGridlines() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { - // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines - $printGridlines = (bool) $this->_GetInt2d($recordData, 0); - $this->_phpSheet->setPrintGridlines($printGridlines); - } - } - - /** - * Read DEFAULTROWHEIGHT record - */ - private function _readDefaultRowHeight() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; option flags - // offset: 2; size: 2; default height for unused rows, (twips 1/20 point) - $height = $this->_GetInt2d($recordData, 2); - $this->_phpSheet->getDefaultRowDimension()->setRowHeight($height / 20); - } - - /** - * Read SHEETPR record - */ - private function _readSheetPr() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2 - - // bit: 6; mask: 0x0040; 0 = outline buttons above outline group - $isSummaryBelow = (0x0040 & $this->_GetInt2d($recordData, 0)) >> 6; - $this->_phpSheet->setShowSummaryBelow($isSummaryBelow); - - // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group - $isSummaryRight = (0x0080 & $this->_GetInt2d($recordData, 0)) >> 7; - $this->_phpSheet->setShowSummaryRight($isSummaryRight); - - // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages - // this corresponds to radio button setting in page setup dialog in Excel - $this->_isFitToPages = (bool) ((0x0100 & $this->_GetInt2d($recordData, 0)) >> 8); - } - - /** - * Read HORIZONTALPAGEBREAKS record - */ - private function _readHorizontalPageBreaks() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { - - // offset: 0; size: 2; number of the following row index structures - $nm = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 6 * $nm; list of $nm row index structures - for ($i = 0; $i < $nm; ++$i) { - $r = $this->_GetInt2d($recordData, 2 + 6 * $i); - $cf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2); - $cl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4); - - // not sure why two column indexes are necessary? - $this->_phpSheet->setBreakByColumnAndRow($cf, $r, PHPExcel_Worksheet::BREAK_ROW); - } - } - } - - /** - * Read VERTICALPAGEBREAKS record - */ - private function _readVerticalPageBreaks() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { - // offset: 0; size: 2; number of the following column index structures - $nm = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 6 * $nm; list of $nm row index structures - for ($i = 0; $i < $nm; ++$i) { - $c = $this->_GetInt2d($recordData, 2 + 6 * $i); - $rf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2); - $rl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4); - - // not sure why two row indexes are necessary? - $this->_phpSheet->setBreakByColumnAndRow($c, $rf, PHPExcel_Worksheet::BREAK_COLUMN); - } - } - } - - /** - * Read HEADER record - */ - private function _readHeader() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: var - // realized that $recordData can be empty even when record exists - if ($recordData) { - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringLong($recordData); - } else { - $string = $this->_readByteStringShort($recordData); - } - - $this->_phpSheet->getHeaderFooter()->setOddHeader($string['value']); - $this->_phpSheet->getHeaderFooter()->setEvenHeader($string['value']); - } - } - } - - /** - * Read FOOTER record - */ - private function _readFooter() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: var - // realized that $recordData can be empty even when record exists - if ($recordData) { - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringLong($recordData); - } else { - $string = $this->_readByteStringShort($recordData); - } - $this->_phpSheet->getHeaderFooter()->setOddFooter($string['value']); - $this->_phpSheet->getHeaderFooter()->setEvenFooter($string['value']); - } - } - } - - /** - * Read HCENTER record - */ - private function _readHcenter() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally - $isHorizontalCentered = (bool) $this->_GetInt2d($recordData, 0); - - $this->_phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered); - } - } - - /** - * Read VCENTER record - */ - private function _readVcenter() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered - $isVerticalCentered = (bool) $this->_GetInt2d($recordData, 0); - - $this->_phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered); - } - } - - /** - * Read LEFTMARGIN record - */ - private function _readLeftMargin() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 8 - $this->_phpSheet->getPageMargins()->setLeft($this->_extractNumber($recordData)); - } - } - - /** - * Read RIGHTMARGIN record - */ - private function _readRightMargin() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 8 - $this->_phpSheet->getPageMargins()->setRight($this->_extractNumber($recordData)); - } - } - - /** - * Read TOPMARGIN record - */ - private function _readTopMargin() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 8 - $this->_phpSheet->getPageMargins()->setTop($this->_extractNumber($recordData)); - } - } - - /** - * Read BOTTOMMARGIN record - */ - private function _readBottomMargin() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 8 - $this->_phpSheet->getPageMargins()->setBottom($this->_extractNumber($recordData)); - } - } - - /** - * Read PAGESETUP record - */ - private function _readPageSetup() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; paper size - $paperSize = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; scaling factor - $scale = $this->_GetInt2d($recordData, 2); - - // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed - $fitToWidth = $this->_GetInt2d($recordData, 6); - - // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed - $fitToHeight = $this->_GetInt2d($recordData, 8); - - // offset: 10; size: 2; option flags - - // bit: 1; mask: 0x0002; 0=landscape, 1=portrait - $isPortrait = (0x0002 & $this->_GetInt2d($recordData, 10)) >> 1; - - // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init - // when this bit is set, do not use flags for those properties - $isNotInit = (0x0004 & $this->_GetInt2d($recordData, 10)) >> 2; - - if (!$isNotInit) { - $this->_phpSheet->getPageSetup()->setPaperSize($paperSize); - switch ($isPortrait) { - case 0: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE); break; - case 1: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_PORTRAIT); break; - } - - $this->_phpSheet->getPageSetup()->setScale($scale, false); - $this->_phpSheet->getPageSetup()->setFitToPage((bool) $this->_isFitToPages); - $this->_phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false); - $this->_phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false); - } - - // offset: 16; size: 8; header margin (IEEE 754 floating-point value) - $marginHeader = $this->_extractNumber(substr($recordData, 16, 8)); - $this->_phpSheet->getPageMargins()->setHeader($marginHeader); - - // offset: 24; size: 8; footer margin (IEEE 754 floating-point value) - $marginFooter = $this->_extractNumber(substr($recordData, 24, 8)); - $this->_phpSheet->getPageMargins()->setFooter($marginFooter); - } - } - - /** - * PROTECT - Sheet protection (BIFF2 through BIFF8) - * if this record is omitted, then it also means no sheet protection - */ - private function _readProtect() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly) { - return; - } - - // offset: 0; size: 2; - - // bit 0, mask 0x01; 1 = sheet is protected - $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; - $this->_phpSheet->getProtection()->setSheet((bool)$bool); - } - - /** - * SCENPROTECT - */ - private function _readScenProtect() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly) { - return; - } - - // offset: 0; size: 2; - - // bit: 0, mask 0x01; 1 = scenarios are protected - $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; - - $this->_phpSheet->getProtection()->setScenarios((bool)$bool); - } - - /** - * OBJECTPROTECT - */ - private function _readObjectProtect() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly) { - return; - } - - // offset: 0; size: 2; - - // bit: 0, mask 0x01; 1 = objects are protected - $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; - - $this->_phpSheet->getProtection()->setObjects((bool)$bool); - } - - /** - * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8) - */ - private function _readPassword() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; 16-bit hash value of password - $password = strtoupper(dechex($this->_GetInt2d($recordData, 0))); // the hashed password - $this->_phpSheet->getProtection()->setPassword($password, true); - } - } - - /** - * Read DEFCOLWIDTH record - */ - private function _readDefColWidth() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; default column width - $width = $this->_GetInt2d($recordData, 0); - if ($width != 8) { - $this->_phpSheet->getDefaultColumnDimension()->setWidth($width); - } - } - - /** - * Read COLINFO record - */ - private function _readColInfo() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; index to first column in range - $fc = $this->_GetInt2d($recordData, 0); // first column index - - // offset: 2; size: 2; index to last column in range - $lc = $this->_GetInt2d($recordData, 2); // first column index - - // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character - $width = $this->_GetInt2d($recordData, 4); - - // offset: 6; size: 2; index to XF record for default column formatting - $xfIndex = $this->_GetInt2d($recordData, 6); - - // offset: 8; size: 2; option flags - - // bit: 0; mask: 0x0001; 1= columns are hidden - $isHidden = (0x0001 & $this->_GetInt2d($recordData, 8)) >> 0; - - // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline) - $level = (0x0700 & $this->_GetInt2d($recordData, 8)) >> 8; - - // bit: 12; mask: 0x1000; 1 = collapsed - $isCollapsed = (0x1000 & $this->_GetInt2d($recordData, 8)) >> 12; - - // offset: 10; size: 2; not used - - for ($i = $fc; $i <= $lc; ++$i) { - if ($lc == 255 || $lc == 256) { - $this->_phpSheet->getDefaultColumnDimension()->setWidth($width / 256); - break; - } - $this->_phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256); - $this->_phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden); - $this->_phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level); - $this->_phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed); - $this->_phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - /** - * ROW - * - * This record contains the properties of a single row in a - * sheet. Rows and cells in a sheet are divided into blocks - * of 32 rows. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readRow() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; index of this row - $r = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to column of the first cell which is described by a cell record - - // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1 - - // offset: 6; size: 2; - - // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point - $height = (0x7FFF & $this->_GetInt2d($recordData, 6)) >> 0; - - // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height - $useDefaultHeight = (0x8000 & $this->_GetInt2d($recordData, 6)) >> 15; - - if (!$useDefaultHeight) { - $this->_phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20); - } - - // offset: 8; size: 2; not used - - // offset: 10; size: 2; not used in BIFF5-BIFF8 - - // offset: 12; size: 4; option flags and default row formatting - - // bit: 2-0: mask: 0x00000007; outline level of the row - $level = (0x00000007 & $this->_GetInt4d($recordData, 12)) >> 0; - $this->_phpSheet->getRowDimension($r + 1)->setOutlineLevel($level); - - // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed - $isCollapsed = (0x00000010 & $this->_GetInt4d($recordData, 12)) >> 4; - $this->_phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed); - - // bit: 5; mask: 0x00000020; 1 = row is hidden - $isHidden = (0x00000020 & $this->_GetInt4d($recordData, 12)) >> 5; - $this->_phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden); - - // bit: 7; mask: 0x00000080; 1 = row has explicit format - $hasExplicitFormat = (0x00000080 & $this->_GetInt4d($recordData, 12)) >> 7; - - // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record - $xfIndex = (0x0FFF0000 & $this->_GetInt4d($recordData, 12)) >> 16; - - if ($hasExplicitFormat) { - $this->_phpSheet->getRowDimension($r + 1)->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - /** - * Read RK record - * This record represents a cell that contains an RK value - * (encoded integer or floating-point value). If a - * floating-point value cannot be encoded to an RK value, - * a NUMBER record will be written. This record replaces the - * record INTEGER written in BIFF2. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readRk() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to column - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset: 4; size: 2; index to XF record - $xfIndex = $this->_GetInt2d($recordData, 4); - - // offset: 6; size: 4; RK value - $rknum = $this->_GetInt4d($recordData, 6); - $numValue = $this->_GetIEEE754($rknum); - - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - if (!$this->_readDataOnly) { - // add style information - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - - // add cell - $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); - } - } - - /** - * Read LABELSST record - * This record represents a cell that contains a string. It - * replaces the LABEL record and RSTRING record used in - * BIFF2-BIFF5. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readLabelSst() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to column - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset: 4; size: 2; index to XF record - $xfIndex = $this->_GetInt2d($recordData, 4); - - // offset: 6; size: 4; index to SST record - $index = $this->_GetInt4d($recordData, 6); - - // add cell - if (($fmtRuns = $this->_sst[$index]['fmtRuns']) && !$this->_readDataOnly) { - // then we should treat as rich text - $richText = new PHPExcel_RichText(); - $charPos = 0; - for ($i = 0; $i <= count($this->_sst[$index]['fmtRuns']); ++$i) { - if (isset($fmtRuns[$i])) { - $text = PHPExcel_Shared_String::Substring($this->_sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos); - $charPos = $fmtRuns[$i]['charPos']; - } else { - $text = PHPExcel_Shared_String::Substring($this->_sst[$index]['value'], $charPos, PHPExcel_Shared_String::CountCharacters($this->_sst[$index]['value'])); - } - - if (PHPExcel_Shared_String::CountCharacters($text) > 0) { - if ($i == 0) { // first text run, no style - $richText->createText($text); - } else { - $textRun = $richText->createTextRun($text); - if (isset($fmtRuns[$i - 1])) { - if ($fmtRuns[$i - 1]['fontIndex'] < 4) { - $fontIndex = $fmtRuns[$i - 1]['fontIndex']; - } else { - // this has to do with that index 4 is omitted in all BIFF versions for some strange reason - // check the OpenOffice documentation of the FONT record - $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1; - } - $textRun->setFont(clone $this->_objFonts[$fontIndex]); - } - } - } - } - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - $cell->setValueExplicit($richText, PHPExcel_Cell_DataType::TYPE_STRING); - } else { - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - $cell->setValueExplicit($this->_sst[$index]['value'], PHPExcel_Cell_DataType::TYPE_STRING); - } - - if (!$this->_readDataOnly) { - // add style information - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - /** - * Read MULRK record - * This record represents a cell range containing RK value - * cells. All cells are located in the same row. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readMulRk() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to first column - $colFirst = $this->_GetInt2d($recordData, 2); - - // offset: var; size: 2; index to last column - $colLast = $this->_GetInt2d($recordData, $length - 2); - $columns = $colLast - $colFirst + 1; - - // offset within record data - $offset = 4; - - for ($i = 0; $i < $columns; ++$i) { - $columnString = PHPExcel_Cell::stringFromColumnIndex($colFirst + $i); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - - // offset: var; size: 2; index to XF record - $xfIndex = $this->_GetInt2d($recordData, $offset); - - // offset: var; size: 4; RK value - $numValue = $this->_GetIEEE754($this->_GetInt4d($recordData, $offset + 2)); - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - if (!$this->_readDataOnly) { - // add style - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - - // add cell value - $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); - } - - $offset += 6; - } - } - - /** - * Read NUMBER record - * This record represents a cell that contains a - * floating-point value. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readNumber() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size 2; index to column - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset 4; size: 2; index to XF record - $xfIndex = $this->_GetInt2d($recordData, 4); - - $numValue = $this->_extractNumber(substr($recordData, 6, 8)); - - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - if (!$this->_readDataOnly) { - // add cell style - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - - // add cell value - $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); - } - } - - /** - * Read FORMULA record + perhaps a following STRING record if formula result is a string - * This record contains the token array and the result of a - * formula cell. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readFormula() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; row index - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; col index - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // offset: 20: size: variable; formula structure - $formulaStructure = substr($recordData, 20); - - // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc. - $options = $this->_GetInt2d($recordData, 14); - - // bit: 0; mask: 0x0001; 1 = recalculate always - // bit: 1; mask: 0x0002; 1 = calculate on open - // bit: 2; mask: 0x0008; 1 = part of a shared formula - $isPartOfSharedFormula = (bool) (0x0008 & $options); - - // WARNING: - // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true - // the formula data may be ordinary formula data, therefore we need to check - // explicitly for the tExp token (0x01) - $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure{2}) == 0x01; - - if ($isPartOfSharedFormula) { - // part of shared formula which means there will be a formula with a tExp token and nothing else - // get the base cell, grab tExp token - $baseRow = $this->_GetInt2d($formulaStructure, 3); - $baseCol = $this->_GetInt2d($formulaStructure, 5); - $this->_baseCell = PHPExcel_Cell::stringFromColumnIndex($baseCol). ($baseRow + 1); - } - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - - if ($isPartOfSharedFormula) { - // formula is added to this cell after the sheet has been read - $this->_sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell; - } - - // offset: 16: size: 4; not used - - // offset: 4; size: 2; XF index - $xfIndex = $this->_GetInt2d($recordData, 4); - - // offset: 6; size: 8; result of the formula - if ( (ord($recordData{6}) == 0) - && (ord($recordData{12}) == 255) - && (ord($recordData{13}) == 255) ) { - - // String formula. Result follows in appended STRING record - $dataType = PHPExcel_Cell_DataType::TYPE_STRING; - - // read possible SHAREDFMLA record - $code = $this->_GetInt2d($this->_data, $this->_pos); - if ($code == self::XLS_Type_SHAREDFMLA) { - $this->_readSharedFmla(); - } - - // read STRING record - $value = $this->_readString(); - - } elseif ((ord($recordData{6}) == 1) - && (ord($recordData{12}) == 255) - && (ord($recordData{13}) == 255)) { - - // Boolean formula. Result is in +2; 0=false, 1=true - $dataType = PHPExcel_Cell_DataType::TYPE_BOOL; - $value = (bool) ord($recordData{8}); - - } elseif ((ord($recordData{6}) == 2) - && (ord($recordData{12}) == 255) - && (ord($recordData{13}) == 255)) { - - // Error formula. Error code is in +2 - $dataType = PHPExcel_Cell_DataType::TYPE_ERROR; - $value = $this->_mapErrorCode(ord($recordData{8})); - - } elseif ((ord($recordData{6}) == 3) - && (ord($recordData{12}) == 255) - && (ord($recordData{13}) == 255)) { - - // Formula result is a null string - $dataType = PHPExcel_Cell_DataType::TYPE_NULL; - $value = ''; - - } else { - - // forumla result is a number, first 14 bytes like _NUMBER record - $dataType = PHPExcel_Cell_DataType::TYPE_NUMERIC; - $value = $this->_extractNumber(substr($recordData, 6, 8)); - - } - - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - if (!$this->_readDataOnly) { - // add cell style - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - - // store the formula - if (!$isPartOfSharedFormula) { - // not part of shared formula - // add cell value. If we can read formula, populate with formula, otherwise just used cached value - try { - if ($this->_version != self::XLS_BIFF8) { - throw new Exception('Not BIFF8. Can only read BIFF8 formulas'); - } - $formula = $this->_getFormulaFromStructure($formulaStructure); // get formula in human language - $cell->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA); - - } catch (Exception $e) { - $cell->setValueExplicit($value, $dataType); - } - } else { - if ($this->_version == self::XLS_BIFF8) { - // do nothing at this point, formula id added later in the code - } else { - $cell->setValueExplicit($value, $dataType); - } - } - - // store the cached calculated value - $cell->setCalculatedValue($value); - } - } - - /** - * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader, - * which usually contains relative references. - * These will be used to construct the formula in each shared formula part after the sheet is read. - */ - private function _readSharedFmla() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything - $cellRange = substr($recordData, 0, 6); - $cellRange = $this->_readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax - - // offset: 6, size: 1; not used - - // offset: 7, size: 1; number of existing FORMULA records for this shared formula - $no = ord($recordData{7}); - - // offset: 8, size: var; Binary token array of the shared formula - $formula = substr($recordData, 8); - - // at this point we only store the shared formula for later use - $this->_sharedFormulas[$this->_baseCell] = $formula; - - } - - /** - * Read a STRING record from current stream position and advance the stream pointer to next record - * This record is used for storing result from FORMULA record when it is a string, and - * it occurs directly after the FORMULA record - * - * @return string The string contents as UTF-8 - */ - private function _readString() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringLong($recordData); - $value = $string['value']; - } else { - $string = $this->_readByteStringLong($recordData); - $value = $string['value']; - } - - return $value; - } - - /** - * Read BOOLERR record - * This record represents a Boolean value or error value - * cell. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readBoolErr() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; row index - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; column index - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset: 4; size: 2; index to XF record - $xfIndex = $this->_GetInt2d($recordData, 4); - - // offset: 6; size: 1; the boolean value or error value - $boolErr = ord($recordData{6}); - - // offset: 7; size: 1; 0=boolean; 1=error - $isError = ord($recordData{7}); - - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - switch ($isError) { - case 0: // boolean - $value = (bool) $boolErr; - - // add cell value - $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_BOOL); - break; - - case 1: // error type - $value = $this->_mapErrorCode($boolErr); - - // add cell value - $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_ERROR); - break; - } - - if (!$this->_readDataOnly) { - // add cell style - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - /** - * Read MULBLANK record - * This record represents a cell range of empty cells. All - * cells are located in the same row - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readMulBlank() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to first column - $fc = $this->_GetInt2d($recordData, 2); - - // offset: 4; size: 2 x nc; list of indexes to XF records - // add style information - if (!$this->_readDataOnly) { - for ($i = 0; $i < $length / 2 - 3; ++$i) { - $columnString = PHPExcel_Cell::stringFromColumnIndex($fc + $i); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - $xfIndex = $this->_GetInt2d($recordData, 4 + 2 * $i); - $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - // offset: 6; size 2; index to last column (not needed) - } - - /** - * Read LABEL record - * This record represents a cell that contains a string. In - * BIFF8 it is usually replaced by the LABELSST record. - * Excel still uses this record, if it copies unformatted - * text cells to the clipboard. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readLabel() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; index to row - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; index to column - $column = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($column); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset: 4; size: 2; XF index - $xfIndex = $this->_GetInt2d($recordData, 4); - - // add cell value - // todo: what if string is very long? continue record - if ($this->_version == self::XLS_BIFF8) { - $string = $this->_readUnicodeStringLong(substr($recordData, 6)); - $value = $string['value']; - } else { - $string = $this->_readByteStringLong(substr($recordData, 6)); - $value = $string['value']; - } - $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); - $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING); - - if (!$this->_readDataOnly) { - // add cell style - $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - } - - /** - * Read BLANK record - */ - private function _readBlank() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; row index - $row = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; col index - $col = $this->_GetInt2d($recordData, 2); - $columnString = PHPExcel_Cell::stringFromColumnIndex($col); - - // Read cell? - if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { - // offset: 4; size: 2; XF index - $xfIndex = $this->_GetInt2d($recordData, 4); - - // add style information - if (!$this->_readDataOnly) { - $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]); - } - } - - } - - /** - * Read MSODRAWING record - */ - private function _readMsoDrawing() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - - // get spliced record data - $splicedRecordData = $this->_getSplicedRecordData(); - $recordData = $splicedRecordData['recordData']; - - $this->_drawingData .= $recordData; - } - - /** - * Read OBJ record - */ - private function _readObj() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly || $this->_version != self::XLS_BIFF8) { - return; - } - - // recordData consists of an array of subrecords looking like this: - // ft: 2 bytes; id number - // cb: 2 bytes; size in bytes of following data - // data: var; subrecord data - - // for now, we are just interested in the second subrecord containing the object type - $ot = $this->_GetInt2d($recordData, 4); - - $this->_objs[] = array( - 'type' => $ot, - ); - } - - /** - * Read WINDOW2 record - */ - private function _readWindow2() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; option flags - $options = $this->_GetInt2d($recordData, 0); - - // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines - $showGridlines = (bool) ((0x0002 & $options) >> 1); - $this->_phpSheet->setShowGridlines($showGridlines); - - // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers - $showRowColHeaders = (bool) ((0x0004 & $options) >> 2); - $this->_phpSheet->setShowRowColHeaders($showRowColHeaders); - - // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen - $this->_frozen = (bool) ((0x0008 & $options) >> 3); - - // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left - $this->_phpSheet->setRightToLeft((bool)((0x0040 & $options) >> 6)); - - // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active - $isActive = (bool) ((0x0400 & $options) >> 10); - if ($isActive) { - $this->_phpExcel->setActiveSheetIndex($this->_phpExcel->getIndex($this->_phpSheet)); - } - } - - /** - * Read SCL record - */ - private function _readScl() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // offset: 0; size: 2; numerator of the view magnification - $numerator = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; numerator of the view magnification - $denumerator = $this->_GetInt2d($recordData, 2); - - // set the zoom scale (in percent) - $this->_phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator); - } - - /** - * Read PANE record - */ - private function _readPane() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; position of vertical split - $px = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; position of horizontal split - $py = $this->_GetInt2d($recordData, 2); - - if ($this->_frozen) { - // frozen panes - $this->_phpSheet->freezePane(PHPExcel_Cell::stringFromColumnIndex($px) . ($py + 1)); - } else { - // unfrozen panes; split windows; not supported by PHPExcel core - } - } - } - - /** - * Read SELECTION record. There is one such record for each pane in the sheet. - */ - private function _readSelection() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 1; pane identifier - $paneId = ord($recordData{0}); - - // offset: 1; size: 2; index to row of the active cell - $r = $this->_GetInt2d($recordData, 1); - - // offset: 3; size: 2; index to column of the active cell - $c = $this->_GetInt2d($recordData, 3); - - // offset: 5; size: 2; index into the following cell range list to the - // entry that contains the active cell - $index = $this->_GetInt2d($recordData, 5); - - // offset: 7; size: var; cell range address list containing all selected cell ranges - $data = substr($recordData, 7); - $cellRangeAddressList = $this->_readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax - - $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0]; - - // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!) - if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) { - $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells); - } - - // first row '1' + last row '65536' indicates that full column is selected - if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) { - $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells); - } - - // first column 'A' + last column 'IV' indicates that full row is selected - if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) { - $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells); - } - - $this->_phpSheet->setSelectedCells($selectedCells); - } - } - - /** - * MERGEDCELLS - * - * This record contains the addresses of merged cell ranges - * in the current sheet. - * - * -- "OpenOffice.org's Documentation of the Microsoft - * Excel File Format" - */ - private function _readMergedCells() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { - $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($recordData); - foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) { - $this->_phpSheet->mergeCells($cellRangeAddress); - } - } - } - - /** - * Read HYPERLINK record - */ - private function _readHyperLink() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer forward to next record - $this->_pos += 4 + $length; - - if (!$this->_readDataOnly) { - // offset: 0; size: 8; cell range address of all cells containing this hyperlink - try { - $cellRange = $this->_readBIFF8CellRangeAddressFixed($recordData, 0, 8); - } catch (Exception $e) { - return; - } - - // offset: 8, size: 16; GUID of StdLink - - // offset: 24, size: 4; unknown value - - // offset: 28, size: 4; option flags - - // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL - $isFileLinkOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 0; - - // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL - $isAbsPathOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 1; - - // bit: 2 (and 4); mask: 0x00000014; 0 = no description - $hasDesc = (0x00000014 & $this->_GetInt2d($recordData, 28)) >> 2; - - // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text - $hasText = (0x00000008 & $this->_GetInt2d($recordData, 28)) >> 3; - - // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame - $hasFrame = (0x00000080 & $this->_GetInt2d($recordData, 28)) >> 7; - - // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name) - $isUNC = (0x00000100 & $this->_GetInt2d($recordData, 28)) >> 8; - - // offset within record data - $offset = 32; - - if ($hasDesc) { - // offset: 32; size: var; character count of description text - $dl = $this->_GetInt4d($recordData, 32); - // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated - $desc = $this->_encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false); - $offset += 4 + 2 * $dl; - } - if ($hasFrame) { - $fl = $this->_GetInt4d($recordData, $offset); - $offset += 4 + 2 * $fl; - } - - // detect type of hyperlink (there are 4 types) - $hyperlinkType = null; - - if ($isUNC) { - $hyperlinkType = 'UNC'; - } else if (!$isFileLinkOrUrl) { - $hyperlinkType = 'workbook'; - } else if (ord($recordData{$offset}) == 0x03) { - $hyperlinkType = 'local'; - } else if (ord($recordData{$offset}) == 0xE0) { - $hyperlinkType = 'URL'; - } - - switch ($hyperlinkType) { - case 'URL': - // section 5.58.2: Hyperlink containing a URL - // e.g. http://example.org/index.php - - // offset: var; size: 16; GUID of URL Moniker - $offset += 16; - // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word - $us = $this->_GetInt4d($recordData, $offset); - $offset += 4; - // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated - $url = $this->_encodeUTF16(substr($recordData, $offset, $us - 2), false); - $url .= $hasText ? '#' : ''; - $offset += $us; - break; - - case 'local': - // section 5.58.3: Hyperlink to local file - // examples: - // mydoc.txt - // ../../somedoc.xls#Sheet!A1 - - // offset: var; size: 16; GUI of File Moniker - $offset += 16; - - // offset: var; size: 2; directory up-level count. - $upLevelCount = $this->_GetInt2d($recordData, $offset); - $offset += 2; - - // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word - $sl = $this->_GetInt4d($recordData, $offset); - $offset += 4; - - // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string) - $shortenedFilePath = substr($recordData, $offset, $sl); - $shortenedFilePath = $this->_encodeUTF16($shortenedFilePath, true); - $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero - - $offset += $sl; - - // offset: var; size: 24; unknown sequence - $offset += 24; - - // extended file path - // offset: var; size: 4; size of the following file link field including string lenth mark - $sz = $this->_GetInt4d($recordData, $offset); - $offset += 4; - - // only present if $sz > 0 - if ($sz > 0) { - // offset: var; size: 4; size of the character array of the extended file path and name - $xl = $this->_GetInt4d($recordData, $offset); - $offset += 4; - - // offset: var; size 2; unknown - $offset += 2; - - // offset: var; size $xl; character array of the extended file path and name. - $extendedFilePath = substr($recordData, $offset, $xl); - $extendedFilePath = $this->_encodeUTF16($extendedFilePath, false); - $offset += $xl; - } - - // construct the path - $url = str_repeat('..\\', $upLevelCount); - $url .= ($sz > 0) ? - $extendedFilePath : $shortenedFilePath; // use extended path if available - $url .= $hasText ? '#' : ''; - - break; - - - case 'UNC': - // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path - // todo: implement - return; - - case 'workbook': - // section 5.58.5: Hyperlink to the Current Workbook - // e.g. Sheet2!B1:C2, stored in text mark field - $url = 'sheet://'; - break; - - default: - return; - - } - - if ($hasText) { - // offset: var; size: 4; character count of text mark including trailing zero word - $tl = $this->_GetInt4d($recordData, $offset); - $offset += 4; - // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated - $text = $this->_encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false); - $url .= $text; - } - - // apply the hyperlink to all the relevant cells - foreach (PHPExcel_Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) { - $this->_phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url); - } - } - } - - /** - * Read DATAVALIDATIONS record - */ - private function _readDataValidations() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer forward to next record - $this->_pos += 4 + $length; - } - - /** - * Read DATAVALIDATION record - */ - private function _readDataValidation() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer forward to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly) { - return; - } - - // offset: 0; size: 4; Options - $options = $this->_GetInt4d($recordData, 0); - - // bit: 0-3; mask: 0x0000000F; type - $type = (0x0000000F & $options) >> 0; - switch ($type) { - case 0x00: $type = PHPExcel_Cell_DataValidation::TYPE_NONE; break; - case 0x01: $type = PHPExcel_Cell_DataValidation::TYPE_WHOLE; break; - case 0x02: $type = PHPExcel_Cell_DataValidation::TYPE_DECIMAL; break; - case 0x03: $type = PHPExcel_Cell_DataValidation::TYPE_LIST; break; - case 0x04: $type = PHPExcel_Cell_DataValidation::TYPE_DATE; break; - case 0x05: $type = PHPExcel_Cell_DataValidation::TYPE_TIME; break; - case 0x06: $type = PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH; break; - case 0x07: $type = PHPExcel_Cell_DataValidation::TYPE_CUSTOM; break; - } - - // bit: 4-6; mask: 0x00000070; error type - $errorStyle = (0x00000070 & $options) >> 4; - switch ($errorStyle) { - case 0x00: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_STOP; break; - case 0x01: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_WARNING; break; - case 0x02: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_INFORMATION; break; - } - - // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list) - // I have only seen cases where this is 1 - $explicitFormula = (0x00000080 & $options) >> 7; - - // bit: 8; mask: 0x00000100; 1= empty cells allowed - $allowBlank = (0x00000100 & $options) >> 8; - - // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity - $suppressDropDown = (0x00000200 & $options) >> 9; - - // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected - $showInputMessage = (0x00040000 & $options) >> 18; - - // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered - $showErrorMessage = (0x00080000 & $options) >> 19; - - // bit: 20-23; mask: 0x00F00000; condition operator - $operator = (0x00F00000 & $options) >> 20; - switch ($operator) { - case 0x00: $operator = PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN ; break; - case 0x01: $operator = PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN ; break; - case 0x02: $operator = PHPExcel_Cell_DataValidation::OPERATOR_EQUAL ; break; - case 0x03: $operator = PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL ; break; - case 0x04: $operator = PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN ; break; - case 0x05: $operator = PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN ; break; - case 0x06: $operator = PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL; break; - case 0x07: $operator = PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL ; break; - } - - // offset: 4; size: var; title of the prompt box - $offset = 4; - $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); - $promptTitle = $string['value'] !== chr(0) ? - $string['value'] : ''; - $offset += $string['size']; - - // offset: var; size: var; title of the error box - $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); - $errorTitle = $string['value'] !== chr(0) ? - $string['value'] : ''; - $offset += $string['size']; - - // offset: var; size: var; text of the prompt box - $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); - $prompt = $string['value'] !== chr(0) ? - $string['value'] : ''; - $offset += $string['size']; - - // offset: var; size: var; text of the error box - $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); - $error = $string['value'] !== chr(0) ? - $string['value'] : ''; - $offset += $string['size']; - - // offset: var; size: 2; size of the formula data for the first condition - $sz1 = $this->_GetInt2d($recordData, $offset); - $offset += 2; - - // offset: var; size: 2; not used - $offset += 2; - - // offset: var; size: $sz1; formula data for first condition (without size field) - $formula1 = substr($recordData, $offset, $sz1); - $formula1 = pack('v', $sz1) . $formula1; // prepend the length - try { - $formula1 = $this->_getFormulaFromStructure($formula1); - - // in list type validity, null characters are used as item separators - if ($type == PHPExcel_Cell_DataValidation::TYPE_LIST) { - $formula1 = str_replace(chr(0), ',', $formula1); - } - } catch (Exception $e) { - return; - } - $offset += $sz1; - - // offset: var; size: 2; size of the formula data for the first condition - $sz2 = $this->_GetInt2d($recordData, $offset); - $offset += 2; - - // offset: var; size: 2; not used - $offset += 2; - - // offset: var; size: $sz2; formula data for second condition (without size field) - $formula2 = substr($recordData, $offset, $sz2); - $formula2 = pack('v', $sz2) . $formula2; // prepend the length - try { - $formula2 = $this->_getFormulaFromStructure($formula2); - } catch (Exception $e) { - return; - } - $offset += $sz2; - - // offset: var; size: var; cell range address list with - $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList(substr($recordData, $offset)); - $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses']; - - foreach ($cellRangeAddresses as $cellRange) { - $stRange = $this->_phpSheet->shrinkRangeToFit($cellRange); - $stRange = PHPExcel_Cell::extractAllCellReferencesInRange($stRange); - foreach ($stRange as $coordinate) { - $objValidation = $this->_phpSheet->getCell($coordinate)->getDataValidation(); - $objValidation->setType($type); - $objValidation->setErrorStyle($errorStyle); - $objValidation->setAllowBlank((bool)$allowBlank); - $objValidation->setShowInputMessage((bool)$showInputMessage); - $objValidation->setShowErrorMessage((bool)$showErrorMessage); - $objValidation->setShowDropDown(!$suppressDropDown); - $objValidation->setOperator($operator); - $objValidation->setErrorTitle($errorTitle); - $objValidation->setError($error); - $objValidation->setPromptTitle($promptTitle); - $objValidation->setPrompt($prompt); - $objValidation->setFormula1($formula1); - $objValidation->setFormula2($formula2); - } - } - - } - - /** - * Read SHEETLAYOUT record. Stores sheet tab color information. - */ - private function _readSheetLayout() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // local pointer in record data - $offset = 0; - - if (!$this->_readDataOnly) { - // offset: 0; size: 2; repeated record identifier 0x0862 - - // offset: 2; size: 10; not used - - // offset: 12; size: 4; size of record data - // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?) - $sz = $this->_GetInt4d($recordData, 12); - - switch ($sz) { - case 0x14: - // offset: 16; size: 2; color index for sheet tab - $colorIndex = $this->_GetInt2d($recordData, 16); - $color = $this->_readColor($colorIndex); - $this->_phpSheet->getTabColor()->setRGB($color['rgb']); - break; - - case 0x28: - // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007 - return; - break; - } - } - } - - /** - * Read SHEETPROTECTION record (FEATHEADR) - */ - private function _readSheetProtection() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - if ($this->_readDataOnly) { - return; - } - - // offset: 0; size: 2; repeated record header - - // offset: 2; size: 2; FRT cell reference flag (=0 currently) - - // offset: 4; size: 8; Currently not used and set to 0 - - // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag) - $isf = $this->_GetInt2d($recordData, 12); - if ($isf != 2) { - return; - } - - // offset: 14; size: 1; =1 since this is a feat header - - // offset: 15; size: 4; size of rgbHdrSData - - // rgbHdrSData, assume "Enhanced Protection" - // offset: 19; size: 2; option flags - $options = $this->_GetInt2d($recordData, 19); - - // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects - $bool = (0x0001 & $options) >> 0; - $this->_phpSheet->getProtection()->setObjects(!$bool); - - // bit: 1; mask 0x0002; edit scenarios - $bool = (0x0002 & $options) >> 1; - $this->_phpSheet->getProtection()->setScenarios(!$bool); - - // bit: 2; mask 0x0004; format cells - $bool = (0x0004 & $options) >> 2; - $this->_phpSheet->getProtection()->setFormatCells(!$bool); - - // bit: 3; mask 0x0008; format columns - $bool = (0x0008 & $options) >> 3; - $this->_phpSheet->getProtection()->setFormatColumns(!$bool); - - // bit: 4; mask 0x0010; format rows - $bool = (0x0010 & $options) >> 4; - $this->_phpSheet->getProtection()->setFormatRows(!$bool); - - // bit: 5; mask 0x0020; insert columns - $bool = (0x0020 & $options) >> 5; - $this->_phpSheet->getProtection()->setInsertColumns(!$bool); - - // bit: 6; mask 0x0040; insert rows - $bool = (0x0040 & $options) >> 6; - $this->_phpSheet->getProtection()->setInsertRows(!$bool); - - // bit: 7; mask 0x0080; insert hyperlinks - $bool = (0x0080 & $options) >> 7; - $this->_phpSheet->getProtection()->setInsertHyperlinks(!$bool); - - // bit: 8; mask 0x0100; delete columns - $bool = (0x0100 & $options) >> 8; - $this->_phpSheet->getProtection()->setDeleteColumns(!$bool); - - // bit: 9; mask 0x0200; delete rows - $bool = (0x0200 & $options) >> 9; - $this->_phpSheet->getProtection()->setDeleteRows(!$bool); - - // bit: 10; mask 0x0400; select locked cells - $bool = (0x0400 & $options) >> 10; - $this->_phpSheet->getProtection()->setSelectLockedCells(!$bool); - - // bit: 11; mask 0x0800; sort cell range - $bool = (0x0800 & $options) >> 11; - $this->_phpSheet->getProtection()->setSort(!$bool); - - // bit: 12; mask 0x1000; auto filter - $bool = (0x1000 & $options) >> 12; - $this->_phpSheet->getProtection()->setAutoFilter(!$bool); - - // bit: 13; mask 0x2000; pivot tables - $bool = (0x2000 & $options) >> 13; - $this->_phpSheet->getProtection()->setPivotTables(!$bool); - - // bit: 14; mask 0x4000; select unlocked cells - $bool = (0x4000 & $options) >> 14; - $this->_phpSheet->getProtection()->setSelectUnlockedCells(!$bool); - - // offset: 21; size: 2; not used - } - - /** - * Read RANGEPROTECTION record - * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification, - * where it is referred to as FEAT record - */ - private function _readRangeProtection() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // move stream pointer to next record - $this->_pos += 4 + $length; - - // local pointer in record data - $offset = 0; - - if (!$this->_readDataOnly) { - $offset += 12; - - // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag - $isf = $this->_GetInt2d($recordData, 12); - if ($isf != 2) { - // we only read FEAT records of type 2 - return; - } - $offset += 2; - - $offset += 5; - - // offset: 19; size: 2; count of ref ranges this feature is on - $cref = $this->_GetInt2d($recordData, 19); - $offset += 2; - - $offset += 6; - - // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record) - $cellRanges = array(); - for ($i = 0; $i < $cref; ++$i) { - try { - $cellRange = $this->_readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8)); - } catch (Exception $e) { - return; - } - $cellRanges[] = $cellRange; - $offset += 8; - } - - // offset: var; size: var; variable length of feature specific data - $rgbFeat = substr($recordData, $offset); - $offset += 4; - - // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit) - $wPassword = $this->_GetInt4d($recordData, $offset); - $offset += 4; - - // Apply range protection to sheet - if ($cellRanges) { - $this->_phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true); - } - } - } - - /** - * Read IMDATA record - */ - private function _readImData() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - - // get spliced record data - $splicedRecordData = $this->_getSplicedRecordData(); - $recordData = $splicedRecordData['recordData']; - - // UNDER CONSTRUCTION - - // offset: 0; size: 2; image format - $cf = $this->_GetInt2d($recordData, 0); - - // offset: 2; size: 2; environment from which the file was written - $env = $this->_GetInt2d($recordData, 2); - - // offset: 4; size: 4; length of the image data - $lcb = $this->_GetInt4d($recordData, 4); - - // offset: 8; size: var; image data - $iData = substr($recordData, 8); - - switch ($cf) { - case 0x09: // Windows bitmap format - // BITMAPCOREINFO - // 1. BITMAPCOREHEADER - // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure - $bcSize = $this->_GetInt4d($iData, 0); - var_dump($bcSize); - - // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels - $bcWidth = $this->_GetInt2d($iData, 4); - var_dump($bcWidth); - - // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels. - $bcHeight = $this->_GetInt2d($iData, 6); - var_dump($bcHeight); - $ih = imagecreatetruecolor($bcWidth, $bcHeight); - - // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1 - - // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24 - $bcBitCount = $this->_GetInt2d($iData, 10); - var_dump($bcBitCount); - - $rgbString = substr($iData, 12); - $rgbTriples = array(); - while (strlen($rgbString) > 0) { - $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString); - $rgbString = substr($rgbString, 3); - } - $x = 0; - $y = 0; - foreach ($rgbTriples as $i => $rgbTriple) { - $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']); - imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color); - $x = ($x + 1) % $bcWidth; - $y = $y + floor(($x + 1) / $bcWidth); - } - //imagepng($ih, 'image.png'); - - $drawing = new PHPExcel_Worksheet_Drawing(); - $drawing->setPath($filename); - $drawing->setWorksheet($this->_phpSheet); - - break; - - case 0x02: // Windows metafile or Macintosh PICT format - case 0x0e: // native format - default; - break; - - } - - // _getSplicedRecordData() takes care of moving current position in data stream - } - - /** - * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record - * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented. - * In this case, we must treat the CONTINUE record as a MSODRAWING record - */ - private function _readContinue() - { - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $recordData = substr($this->_data, $this->_pos + 4, $length); - - // check if we are reading drawing data - // this is in case a free CONTINUE record occurs in other circumstances we are unaware of - if ($this->_drawingData == '') { - // move stream pointer to next record - $this->_pos += 4 + $length; - - return; - } - - // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data - if ($length < 4) { - // move stream pointer to next record - $this->_pos += 4 + $length; - - return; - } - - // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record - // look inside CONTINUE record to see if it looks like a part of an Escher stream - // we know that Escher stream may be split at least at - // 0xF003 MsofbtSpgrContainer - // 0xF004 MsofbtSpContainer - // 0xF00D MsofbtClientTextbox - $validSplitPoints = array(0xF003, 0xF004, 0xF00D); // add identifiers if we find more - - $splitPoint = $this->_GetInt2d($recordData, 2); - if (in_array($splitPoint, $validSplitPoints)) { - // get spliced record data (and move pointer to next record) - $splicedRecordData = $this->_getSplicedRecordData(); - $this->_drawingData .= $splicedRecordData['recordData']; - - return; - } - - // move stream pointer to next record - $this->_pos += 4 + $length; - - } - - - /** - * Reads a record from current position in data stream and continues reading data as long as CONTINUE - * records are found. Splices the record data pieces and returns the combined string as if record data - * is in one piece. - * Moves to next current position in data stream to start of next record different from a CONtINUE record - * - * @return array - */ - private function _getSplicedRecordData() - { - $data = ''; - $spliceOffsets = array(); - - $i = 0; - $spliceOffsets[0] = 0; - - do { - ++$i; - - // offset: 0; size: 2; identifier - $identifier = $this->_GetInt2d($this->_data, $this->_pos); - // offset: 2; size: 2; length - $length = $this->_GetInt2d($this->_data, $this->_pos + 2); - $data .= substr($this->_data, $this->_pos + 4, $length); - - $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length; - - $this->_pos += 4 + $length; - $nextIdentifier = $this->_GetInt2d($this->_data, $this->_pos); - } - while ($nextIdentifier == self::XLS_Type_CONTINUE); - - $splicedData = array( - 'recordData' => $data, - 'spliceOffsets' => $spliceOffsets, - ); - - return $splicedData; - - } - - /** - * Convert formula structure into human readable Excel formula like 'A3+A5*5' - * - * @param string $formulaStructure The complete binary data for the formula - * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas - * @return string Human readable formula - */ - private function _getFormulaFromStructure($formulaStructure, $baseCell = 'A1') - { - // offset: 0; size: 2; size of the following formula data - $sz = $this->_GetInt2d($formulaStructure, 0); - - // offset: 2; size: sz - $formulaData = substr($formulaStructure, 2, $sz); - - // for debug: dump the formula data - //echo '<xmp>'; - //echo 'size: ' . $sz . "\n"; - //echo 'the entire formula data: '; - //Debug::dump($formulaData); - //echo "\n----\n"; - - // offset: 2 + sz; size: variable (optional) - if (strlen($formulaStructure) > 2 + $sz) { - $additionalData = substr($formulaStructure, 2 + $sz); - - // for debug: dump the additional data - //echo 'the entire additional data: '; - //Debug::dump($additionalData); - //echo "\n----\n"; - - } else { - $additionalData = ''; - } - - return $this->_getFormulaFromData($formulaData, $additionalData, $baseCell); - } - - /** - * Take formula data and additional data for formula and return human readable formula - * - * @param string $formulaData The binary data for the formula itself - * @param string $additionalData Additional binary data going with the formula - * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas - * @return string Human readable formula - */ - private function _getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1') - { - // start parsing the formula data - $tokens = array(); - - while (strlen($formulaData) > 0 and $token = $this->_getNextToken($formulaData, $baseCell)) { - $tokens[] = $token; - $formulaData = substr($formulaData, $token['size']); - - // for debug: dump the token - //var_dump($token); - } - - $formulaString = $this->_createFormulaFromTokens($tokens, $additionalData); - - return $formulaString; - } - - /** - * Take array of tokens together with additional data for formula and return human readable formula - * - * @param array $tokens - * @param array $additionalData Additional binary data going with the formula - * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas - * @return string Human readable formula - */ - private function _createFormulaFromTokens($tokens, $additionalData) - { - // empty formula? - if (count($tokens) == 0) { - return ''; - } - - $formulaStrings = array(); - foreach ($tokens as $token) { - // initialize spaces - $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen - $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen - $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis - $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis - $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis - $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis - - switch ($token['name']) { - case 'tAdd': // addition - case 'tConcat': // addition - case 'tDiv': // division - case 'tEQ': // equaltiy - case 'tGE': // greater than or equal - case 'tGT': // greater than - case 'tIsect': // intersection - case 'tLE': // less than or equal - case 'tList': // less than or equal - case 'tLT': // less than - case 'tMul': // multiplication - case 'tNE': // multiplication - case 'tPower': // power - case 'tRange': // range - case 'tSub': // subtraction - $op2 = array_pop($formulaStrings); - $op1 = array_pop($formulaStrings); - $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2"; - unset($space0, $space1); - break; - case 'tUplus': // unary plus - case 'tUminus': // unary minus - $op = array_pop($formulaStrings); - $formulaStrings[] = "$space1$space0{$token['data']}$op"; - unset($space0, $space1); - break; - case 'tPercent': // percent sign - $op = array_pop($formulaStrings); - $formulaStrings[] = "$op$space1$space0{$token['data']}"; - unset($space0, $space1); - break; - case 'tAttrVolatile': // indicates volatile function - case 'tAttrIf': - case 'tAttrSkip': - case 'tAttrChoose': - // token is only important for Excel formula evaluator - // do nothing - break; - case 'tAttrSpace': // space / carriage return - // space will be used when next token arrives, do not alter formulaString stack - switch ($token['data']['spacetype']) { - case 'type0': - $space0 = str_repeat(' ', $token['data']['spacecount']); - break; - case 'type1': - $space1 = str_repeat("\n", $token['data']['spacecount']); - break; - case 'type2': - $space2 = str_repeat(' ', $token['data']['spacecount']); - break; - case 'type3': - $space3 = str_repeat("\n", $token['data']['spacecount']); - break; - case 'type4': - $space4 = str_repeat(' ', $token['data']['spacecount']); - break; - case 'type5': - $space5 = str_repeat("\n", $token['data']['spacecount']); - break; - } - break; - case 'tAttrSum': // SUM function with one parameter - $op = array_pop($formulaStrings); - $formulaStrings[] = "{$space1}{$space0}SUM($op)"; - unset($space0, $space1); - break; - case 'tFunc': // function with fixed number of arguments - case 'tFuncV': // function with variable number of arguments - if ($token['data']['function'] != '') { - // normal function - $ops = array(); // array of operators - for ($i = 0; $i < $token['data']['args']; ++$i) { - $ops[] = array_pop($formulaStrings); - } - $ops = array_reverse($ops); - $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ")"; - unset($space0, $space1); - } else { - // add-in function - $ops = array(); // array of operators - for ($i = 0; $i < $token['data']['args'] - 1; ++$i) { - $ops[] = array_pop($formulaStrings); - } - $ops = array_reverse($ops); - $function = array_pop($formulaStrings); - $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ")"; - unset($space0, $space1); - } - break; - case 'tParen': // parenthesis - $expression = array_pop($formulaStrings); - $formulaStrings[] = "$space3$space2($expression$space5$space4)"; - unset($space2, $space3, $space4, $space5); - break; - case 'tArray': // array constant - $constantArray = $this->_readBIFF8ConstantArray($additionalData); - $formulaStrings[] = $space1 . $space0 . $constantArray['value']; - $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data - unset($space0, $space1); - break; - case 'tMemArea': - // bite off chunk of additional data - $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($additionalData); - $additionalData = substr($additionalData, $cellRangeAddressList['size']); - $formulaStrings[] = "$space1$space0{$token['data']}"; - unset($space0, $space1); - break; - case 'tArea': // cell range address - case 'tBool': // boolean - case 'tErr': // error code - case 'tInt': // integer - case 'tMemErr': - case 'tMemFunc': - case 'tMissArg': - case 'tName': - case 'tNameX': - case 'tNum': // number - case 'tRef': // single cell reference - case 'tRef3d': // 3d cell reference - case 'tArea3d': // 3d cell range reference - case 'tRefN': - case 'tAreaN': - case 'tStr': // string - $formulaStrings[] = "$space1$space0{$token['data']}"; - unset($space0, $space1); - break; - } - } - $formulaString = $formulaStrings[0]; - - // for debug: dump the human readable formula - //echo '----' . "\n"; - //echo 'Formula: ' . $formulaString; - - return $formulaString; - } - - /** - * Fetch next token from binary formula data - * - * @param string Formula data - * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas - * @return array - * @throws Exception - */ - private function _getNextToken($formulaData, $baseCell = 'A1') - { - // offset: 0; size: 1; token id - $id = ord($formulaData[0]); // token id - $name = false; // initialize token name - - switch ($id) { - case 0x03: $name = 'tAdd'; $size = 1; $data = '+'; break; - case 0x04: $name = 'tSub'; $size = 1; $data = '-'; break; - case 0x05: $name = 'tMul'; $size = 1; $data = '*'; break; - case 0x06: $name = 'tDiv'; $size = 1; $data = '/'; break; - case 0x07: $name = 'tPower'; $size = 1; $data = '^'; break; - case 0x08: $name = 'tConcat'; $size = 1; $data = '&'; break; - case 0x09: $name = 'tLT'; $size = 1; $data = '<'; break; - case 0x0A: $name = 'tLE'; $size = 1; $data = '<='; break; - case 0x0B: $name = 'tEQ'; $size = 1; $data = '='; break; - case 0x0C: $name = 'tGE'; $size = 1; $data = '>='; break; - case 0x0D: $name = 'tGT'; $size = 1; $data = '>'; break; - case 0x0E: $name = 'tNE'; $size = 1; $data = '<>'; break; - case 0x0F: $name = 'tIsect'; $size = 1; $data = ' '; break; - case 0x10: $name = 'tList'; $size = 1; $data = ','; break; - case 0x11: $name = 'tRange'; $size = 1; $data = ':'; break; - case 0x12: $name = 'tUplus'; $size = 1; $data = '+'; break; - case 0x13: $name = 'tUminus'; $size = 1; $data = '-'; break; - case 0x14: $name = 'tPercent'; $size = 1; $data = '%'; break; - case 0x15: // parenthesis - $name = 'tParen'; - $size = 1; - $data = null; - break; - case 0x16: // missing argument - $name = 'tMissArg'; - $size = 1; - $data = ''; - break; - case 0x17: // string - $name = 'tStr'; - // offset: 1; size: var; Unicode string, 8-bit string length - $string = $this->_readUnicodeStringShort(substr($formulaData, 1)); - $size = 1 + $string['size']; - $data = $this->_UTF8toExcelDoubleQuoted($string['value']); - break; - case 0x19: // Special attribute - // offset: 1; size: 1; attribute type flags: - switch (ord($formulaData[1])) { - case 0x01: - $name = 'tAttrVolatile'; - $size = 4; - $data = null; - break; - case 0x02: - $name = 'tAttrIf'; - $size = 4; - $data = null; - break; - case 0x04: - $name = 'tAttrChoose'; - // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1) - $nc = $this->_GetInt2d($formulaData, 2); - // offset: 4; size: 2 * $nc - // offset: 4 + 2 * $nc; size: 2 - $size = 2 * $nc + 6; - $data = null; - break; - case 0x08: - $name = 'tAttrSkip'; - $size = 4; - $data = null; - break; - case 0x10: - $name = 'tAttrSum'; - $size = 4; - $data = null; - break; - case 0x40: - case 0x41: - $name = 'tAttrSpace'; - $size = 4; - // offset: 2; size: 2; space type and position - switch (ord($formulaData[2])) { - case 0x00: - $spacetype = 'type0'; - break; - case 0x01: - $spacetype = 'type1'; - break; - case 0x02: - $spacetype = 'type2'; - break; - case 0x03: - $spacetype = 'type3'; - break; - case 0x04: - $spacetype = 'type4'; - break; - case 0x05: - $spacetype = 'type5'; - break; - default: - throw new Exception('Unrecognized space type in tAttrSpace token'); - break; - } - // offset: 3; size: 1; number of inserted spaces/carriage returns - $spacecount = ord($formulaData[3]); - - $data = array('spacetype' => $spacetype, 'spacecount' => $spacecount); - break; - default: - throw new Exception('Unrecognized attribute flag in tAttr token'); - break; - } - break; - case 0x1C: // error code - // offset: 1; size: 1; error code - $name = 'tErr'; - $size = 2; - $data = $this->_mapErrorCode(ord($formulaData[1])); - break; - case 0x1D: // boolean - // offset: 1; size: 1; 0 = false, 1 = true; - $name = 'tBool'; - $size = 2; - $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE'; - break; - case 0x1E: // integer - // offset: 1; size: 2; unsigned 16-bit integer - $name = 'tInt'; - $size = 3; - $data = $this->_GetInt2d($formulaData, 1); - break; - case 0x1F: // number - // offset: 1; size: 8; - $name = 'tNum'; - $size = 9; - $data = $this->_extractNumber(substr($formulaData, 1)); - $data = str_replace(',', '.', (string)$data); // in case non-English locale - break; - case 0x40: // array constant - case 0x60: // array constant - // offset: 1; size: 7; not used - $name = 'tArray'; - $size = 8; - $data = null; - break; - case 0x41: // function with fixed number of arguments - $name = 'tFunc'; - $size = 3; - // offset: 1; size: 2; index to built-in sheet function - switch ($this->_GetInt2d($formulaData, 1)) { - case 2: $function = 'ISNA'; $args = 1; break; - case 3: $function = 'ISERROR'; $args = 1; break; - case 10: $function = 'NA'; $args = 0; break; - case 15: $function = 'SIN'; $args = 1; break; - case 16: $function = 'COS'; $args = 1; break; - case 17: $function = 'TAN'; $args = 1; break; - case 18: $function = 'ATAN'; $args = 1; break; - case 19: $function = 'PI'; $args = 0; break; - case 20: $function = 'SQRT'; $args = 1; break; - case 21: $function = 'EXP'; $args = 1; break; - case 22: $function = 'LN'; $args = 1; break; - case 23: $function = 'LOG10'; $args = 1; break; - case 24: $function = 'ABS'; $args = 1; break; - case 25: $function = 'INT'; $args = 1; break; - case 26: $function = 'SIGN'; $args = 1; break; - case 27: $function = 'ROUND'; $args = 2; break; - case 30: $function = 'REPT'; $args = 2; break; - case 31: $function = 'MID'; $args = 3; break; - case 32: $function = 'LEN'; $args = 1; break; - case 33: $function = 'VALUE'; $args = 1; break; - case 34: $function = 'TRUE'; $args = 0; break; - case 35: $function = 'FALSE'; $args = 0; break; - case 38: $function = 'NOT'; $args = 1; break; - case 39: $function = 'MOD'; $args = 2; break; - case 40: $function = 'DCOUNT'; $args = 3; break; - case 41: $function = 'DSUM'; $args = 3; break; - case 42: $function = 'DAVERAGE'; $args = 3; break; - case 43: $function = 'DMIN'; $args = 3; break; - case 44: $function = 'DMAX'; $args = 3; break; - case 45: $function = 'DSTDEV'; $args = 3; break; - case 48: $function = 'TEXT'; $args = 2; break; - case 61: $function = 'MIRR'; $args = 3; break; - case 63: $function = 'RAND'; $args = 0; break; - case 65: $function = 'DATE'; $args = 3; break; - case 66: $function = 'TIME'; $args = 3; break; - case 67: $function = 'DAY'; $args = 1; break; - case 68: $function = 'MONTH'; $args = 1; break; - case 69: $function = 'YEAR'; $args = 1; break; - case 71: $function = 'HOUR'; $args = 1; break; - case 72: $function = 'MINUTE'; $args = 1; break; - case 73: $function = 'SECOND'; $args = 1; break; - case 74: $function = 'NOW'; $args = 0; break; - case 75: $function = 'AREAS'; $args = 1; break; - case 76: $function = 'ROWS'; $args = 1; break; - case 77: $function = 'COLUMNS'; $args = 1; break; - case 83: $function = 'TRANSPOSE'; $args = 1; break; - case 86: $function = 'TYPE'; $args = 1; break; - case 97: $function = 'ATAN2'; $args = 2; break; - case 98: $function = 'ASIN'; $args = 1; break; - case 99: $function = 'ACOS'; $args = 1; break; - case 105: $function = 'ISREF'; $args = 1; break; - case 111: $function = 'CHAR'; $args = 1; break; - case 112: $function = 'LOWER'; $args = 1; break; - case 113: $function = 'UPPER'; $args = 1; break; - case 114: $function = 'PROPER'; $args = 1; break; - case 117: $function = 'EXACT'; $args = 2; break; - case 118: $function = 'TRIM'; $args = 1; break; - case 119: $function = 'REPLACE'; $args = 4; break; - case 121: $function = 'CODE'; $args = 1; break; - case 126: $function = 'ISERR'; $args = 1; break; - case 127: $function = 'ISTEXT'; $args = 1; break; - case 128: $function = 'ISNUMBER'; $args = 1; break; - case 129: $function = 'ISBLANK'; $args = 1; break; - case 130: $function = 'T'; $args = 1; break; - case 131: $function = 'N'; $args = 1; break; - case 140: $function = 'DATEVALUE'; $args = 1; break; - case 141: $function = 'TIMEVALUE'; $args = 1; break; - case 142: $function = 'SLN'; $args = 3; break; - case 143: $function = 'SYD'; $args = 4; break; - case 162: $function = 'CLEAN'; $args = 1; break; - case 163: $function = 'MDETERM'; $args = 1; break; - case 164: $function = 'MINVERSE'; $args = 1; break; - case 165: $function = 'MMULT'; $args = 2; break; - case 184: $function = 'FACT'; $args = 1; break; - case 189: $function = 'DPRODUCT'; $args = 3; break; - case 190: $function = 'ISNONTEXT'; $args = 1; break; - case 195: $function = 'DSTDEVP'; $args = 3; break; - case 196: $function = 'DVARP'; $args = 3; break; - case 198: $function = 'ISLOGICAL'; $args = 1; break; - case 199: $function = 'DCOUNTA'; $args = 3; break; - case 207: $function = 'REPLACEB'; $args = 4; break; - case 210: $function = 'MIDB'; $args = 3; break; - case 211: $function = 'LENB'; $args = 1; break; - case 212: $function = 'ROUNDUP'; $args = 2; break; - case 213: $function = 'ROUNDDOWN'; $args = 2; break; - case 214: $function = 'ASC'; $args = 1; break; - case 215: $function = 'DBCS'; $args = 1; break; - case 221: $function = 'TODAY'; $args = 0; break; - case 229: $function = 'SINH'; $args = 1; break; - case 230: $function = 'COSH'; $args = 1; break; - case 231: $function = 'TANH'; $args = 1; break; - case 232: $function = 'ASINH'; $args = 1; break; - case 233: $function = 'ACOSH'; $args = 1; break; - case 234: $function = 'ATANH'; $args = 1; break; - case 235: $function = 'DGET'; $args = 3; break; - case 244: $function = 'INFO'; $args = 1; break; - case 252: $function = 'FREQUENCY'; $args = 2; break; - case 261: $function = 'ERROR.TYPE'; $args = 1; break; - case 271: $function = 'GAMMALN'; $args = 1; break; - case 273: $function = 'BINOMDIST'; $args = 4; break; - case 274: $function = 'CHIDIST'; $args = 2; break; - case 275: $function = 'CHIINV'; $args = 2; break; - case 276: $function = 'COMBIN'; $args = 2; break; - case 277: $function = 'CONFIDENCE'; $args = 3; break; - case 278: $function = 'CRITBINOM'; $args = 3; break; - case 279: $function = 'EVEN'; $args = 1; break; - case 280: $function = 'EXPONDIST'; $args = 3; break; - case 281: $function = 'FDIST'; $args = 3; break; - case 282: $function = 'FINV'; $args = 3; break; - case 283: $function = 'FISHER'; $args = 1; break; - case 284: $function = 'FISHERINV'; $args = 1; break; - case 285: $function = 'FLOOR'; $args = 2; break; - case 286: $function = 'GAMMADIST'; $args = 4; break; - case 287: $function = 'GAMMAINV'; $args = 3; break; - case 288: $function = 'CEILING'; $args = 2; break; - case 289: $function = 'HYPGEOMDIST'; $args = 4; break; - case 290: $function = 'LOGNORMDIST'; $args = 3; break; - case 291: $function = 'LOGINV'; $args = 3; break; - case 292: $function = 'NEGBINOMDIST'; $args = 3; break; - case 293: $function = 'NORMDIST'; $args = 4; break; - case 294: $function = 'NORMSDIST'; $args = 1; break; - case 295: $function = 'NORMINV'; $args = 3; break; - case 296: $function = 'NORMSINV'; $args = 1; break; - case 297: $function = 'STANDARDIZE'; $args = 3; break; - case 298: $function = 'ODD'; $args = 1; break; - case 299: $function = 'PERMUT'; $args = 2; break; - case 300: $function = 'POISSON'; $args = 3; break; - case 301: $function = 'TDIST'; $args = 3; break; - case 302: $function = 'WEIBULL'; $args = 4; break; - case 303: $function = 'SUMXMY2'; $args = 2; break; - case 304: $function = 'SUMX2MY2'; $args = 2; break; - case 305: $function = 'SUMX2PY2'; $args = 2; break; - case 306: $function = 'CHITEST'; $args = 2; break; - case 307: $function = 'CORREL'; $args = 2; break; - case 308: $function = 'COVAR'; $args = 2; break; - case 309: $function = 'FORECAST'; $args = 3; break; - case 310: $function = 'FTEST'; $args = 2; break; - case 311: $function = 'INTERCEPT'; $args = 2; break; - case 312: $function = 'PEARSON'; $args = 2; break; - case 313: $function = 'RSQ'; $args = 2; break; - case 314: $function = 'STEYX'; $args = 2; break; - case 315: $function = 'SLOPE'; $args = 2; break; - case 316: $function = 'TTEST'; $args = 4; break; - case 325: $function = 'LARGE'; $args = 2; break; - case 326: $function = 'SMALL'; $args = 2; break; - case 327: $function = 'QUARTILE'; $args = 2; break; - case 328: $function = 'PERCENTILE'; $args = 2; break; - case 331: $function = 'TRIMMEAN'; $args = 2; break; - case 332: $function = 'TINV'; $args = 2; break; - case 337: $function = 'POWER'; $args = 2; break; - case 342: $function = 'RADIANS'; $args = 1; break; - case 343: $function = 'DEGREES'; $args = 1; break; - case 346: $function = 'COUNTIF'; $args = 2; break; - case 347: $function = 'COUNTBLANK'; $args = 1; break; - case 350: $function = 'ISPMT'; $args = 4; break; - case 351: $function = 'DATEDIF'; $args = 3; break; - case 352: $function = 'DATESTRING'; $args = 1; break; - case 353: $function = 'NUMBERSTRING'; $args = 2; break; - case 360: $function = 'PHONETIC'; $args = 1; break; - default: - throw new Exception('Unrecognized function in formula'); - break; - } - $data = array('function' => $function, 'args' => $args); - break; - case 0x22: // function with variable number of arguments - case 0x42: // function with variable number of arguments - case 0x62: // function with variable number of arguments - $name = 'tFuncV'; - $size = 4; - // offset: 1; size: 1; number of arguments - $args = ord($formulaData[1]); - // offset: 2: size: 2; index to built-in sheet function - $index = $this->_GetInt2d($formulaData, 2); - switch ($index) { - case 0: $function = 'COUNT'; break; - case 1: $function = 'IF'; break; - case 4: $function = 'SUM'; break; - case 5: $function = 'AVERAGE'; break; - case 6: $function = 'MIN'; break; - case 7: $function = 'MAX'; break; - case 8: $function = 'ROW'; break; - case 9: $function = 'COLUMN'; break; - case 11: $function = 'NPV'; break; - case 12: $function = 'STDEV'; break; - case 13: $function = 'DOLLAR'; break; - case 14: $function = 'FIXED'; break; - case 28: $function = 'LOOKUP'; break; - case 29: $function = 'INDEX'; break; - case 36: $function = 'AND'; break; - case 37: $function = 'OR'; break; - case 46: $function = 'VAR'; break; - case 49: $function = 'LINEST'; break; - case 50: $function = 'TREND'; break; - case 51: $function = 'LOGEST'; break; - case 52: $function = 'GROWTH'; break; - case 56: $function = 'PV'; break; - case 57: $function = 'FV'; break; - case 58: $function = 'NPER'; break; - case 59: $function = 'PMT'; break; - case 60: $function = 'RATE'; break; - case 62: $function = 'IRR'; break; - case 64: $function = 'MATCH'; break; - case 70: $function = 'WEEKDAY'; break; - case 78: $function = 'OFFSET'; break; - case 82: $function = 'SEARCH'; break; - case 100: $function = 'CHOOSE'; break; - case 101: $function = 'HLOOKUP'; break; - case 102: $function = 'VLOOKUP'; break; - case 109: $function = 'LOG'; break; - case 115: $function = 'LEFT'; break; - case 116: $function = 'RIGHT'; break; - case 120: $function = 'SUBSTITUTE'; break; - case 124: $function = 'FIND'; break; - case 125: $function = 'CELL'; break; - case 144: $function = 'DDB'; break; - case 148: $function = 'INDIRECT'; break; - case 167: $function = 'IPMT'; break; - case 168: $function = 'PPMT'; break; - case 169: $function = 'COUNTA'; break; - case 183: $function = 'PRODUCT'; break; - case 193: $function = 'STDEVP'; break; - case 194: $function = 'VARP'; break; - case 197: $function = 'TRUNC'; break; - case 204: $function = 'USDOLLAR'; break; - case 205: $function = 'FINDB'; break; - case 206: $function = 'SEARCHB'; break; - case 208: $function = 'LEFTB'; break; - case 209: $function = 'RIGHTB'; break; - case 216: $function = 'RANK'; break; - case 219: $function = 'ADDRESS'; break; - case 220: $function = 'DAYS360'; break; - case 222: $function = 'VDB'; break; - case 227: $function = 'MEDIAN'; break; - case 228: $function = 'SUMPRODUCT'; break; - case 247: $function = 'DB'; break; - case 255: $function = ''; break; - case 269: $function = 'AVEDEV'; break; - case 270: $function = 'BETADIST'; break; - case 272: $function = 'BETAINV'; break; - case 317: $function = 'PROB'; break; - case 318: $function = 'DEVSQ'; break; - case 319: $function = 'GEOMEAN'; break; - case 320: $function = 'HARMEAN'; break; - case 321: $function = 'SUMSQ'; break; - case 322: $function = 'KURT'; break; - case 323: $function = 'SKEW'; break; - case 324: $function = 'ZTEST'; break; - case 329: $function = 'PERCENTRANK'; break; - case 330: $function = 'MODE'; break; - case 336: $function = 'CONCATENATE'; break; - case 344: $function = 'SUBTOTAL'; break; - case 345: $function = 'SUMIF'; break; - case 354: $function = 'ROMAN'; break; - case 358: $function = 'GETPIVOTDATA'; break; - case 359: $function = 'HYPERLINK'; break; - case 361: $function = 'AVERAGEA'; break; - case 362: $function = 'MAXA'; break; - case 363: $function = 'MINA'; break; - case 364: $function = 'STDEVPA'; break; - case 365: $function = 'VARPA'; break; - case 366: $function = 'STDEVA'; break; - case 367: $function = 'VARA'; break; - default: - throw new Exception('Unrecognized function in formula'); - break; - } - $data = array('function' => $function, 'args' => $args); - break; - case 0x23: // index to defined name - case 0x43: - $name = 'tName'; - $size = 5; - // offset: 1; size: 2; one-based index to definedname record - $definedNameIndex = $this->_GetInt2d($formulaData, 1) - 1; - // offset: 2; size: 2; not used - $data = $this->_definedname[$definedNameIndex]['name']; - break; - case 0x24: // single cell reference e.g. A5 - case 0x44: - case 0x64: - $name = 'tRef'; - $size = 5; - $data = $this->_readBIFF8CellAddress(substr($formulaData, 1, 4)); - break; - case 0x25: // cell range reference to cells in the same sheet - case 0x45: - case 0x65: - $name = 'tArea'; - $size = 9; - $data = $this->_readBIFF8CellRangeAddress(substr($formulaData, 1, 8)); - break; - case 0x26: - case 0x46: - $name = 'tMemArea'; - // offset: 1; size: 4; not used - // offset: 5; size: 2; size of the following subexpression - $subSize = $this->_GetInt2d($formulaData, 5); - $size = 7 + $subSize; - $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize)); - break; - case 0x47: - $name = 'tMemErr'; - // offset: 1; size: 4; not used - // offset: 5; size: 2; size of the following subexpression - $subSize = $this->_GetInt2d($formulaData, 5); - $size = 7 + $subSize; - $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize)); - break; - case 0x29: - case 0x49: - $name = 'tMemFunc'; - // offset: 1; size: 2; size of the following subexpression - $subSize = $this->_GetInt2d($formulaData, 1); - $size = 3 + $subSize; - $data = $this->_getFormulaFromData(substr($formulaData, 3, $subSize)); - break; - - case 0x2C: // Relative reference, used in shared formulas and some other places - case 0x4C: - case 0x6C: - $name = 'tRefN'; - $size = 5; - $data = $this->_readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell); - break; - - case 0x2D: - case 0x4D: - case 0x6D: - $name = 'tAreaN'; - $size = 9; - $data = $this->_readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell); - break; - - case 0x39: - case 0x59: - case 0x79: - $name = 'tNameX'; - $size = 7; - // offset: 1; size: 2; index to REF entry in EXTERNSHEET record - // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record - $index = $this->_GetInt2d($formulaData, 3); - // assume index is to EXTERNNAME record - $data = $this->_externalNames[$index - 1]['name']; - // offset: 5; size: 2; not used - break; - - case 0x3A: // 3d reference to cell - case 0x5A: - $name = 'tRef3d'; - $size = 7; - - try { - // offset: 1; size: 2; index to REF entry - $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1)); - // offset: 3; size: 4; cell address - $cellAddress = $this->_readBIFF8CellAddress(substr($formulaData, 3, 4)); - - $data = "$sheetRange!$cellAddress"; - - } catch (Exception $e) { - // deleted sheet reference - $data = '#REF!'; - } - - break; - case 0x3B: // 3d reference to cell range - case 0x5B: - $name = 'tArea3d'; - $size = 11; - - try { - // offset: 1; size: 2; index to REF entry - $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1)); - // offset: 3; size: 8; cell address - $cellRangeAddress = $this->_readBIFF8CellRangeAddress(substr($formulaData, 3, 8)); - - $data = "$sheetRange!$cellRangeAddress"; - - } catch (Exception $e) { - // deleted sheet reference - $data = '#REF!'; - - } - - break; - // case 0x39: // don't know how to deal with - default: - throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula'); - break; - } - - return array( - 'id' => $id, - 'name' => $name, - 'size' => $size, - 'data' => $data, - ); - } - - /** - * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2' - * section 3.3.4 - * - * @param string $cellAddressStructure - * @return string - */ - private function _readBIFF8CellAddress($cellAddressStructure) - { - // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767)) - $row = $this->_GetInt2d($cellAddressStructure, 0) + 1; - - // offset: 2; size: 2; index to column or column offset + relative flags - - // bit: 7-0; mask 0x00FF; column index - $column = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($cellAddressStructure, 2)); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) { - $column = '$' . $column; - } - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) { - $row = '$' . $row; - } - - return $column . $row; - } - - /** - * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column - * to indicate offsets from a base cell - * section 3.3.4 - * - * @param string $cellAddressStructure - * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas - * @return string - */ - private function _readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1') - { - list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell); - $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1; - - // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767)) - $rowIndex = $this->_GetInt2d($cellAddressStructure, 0); - $row = $this->_GetInt2d($cellAddressStructure, 0) + 1; - - // offset: 2; size: 2; index to column or column offset + relative flags - - // bit: 7-0; mask 0x00FF; column index - $colIndex = 0x00FF & $this->_GetInt2d($cellAddressStructure, 2); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) { - $column = PHPExcel_Cell::stringFromColumnIndex($colIndex); - $column = '$' . $column; - } else { - $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256; - $column = PHPExcel_Cell::stringFromColumnIndex($baseCol + $colIndex); - } - - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) { - $row = '$' . $row; - } else { - $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536; - $row = $baseRow + $rowIndex; - } - - return $column . $row; - } - - /** - * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1' - * always fixed range - * section 2.5.14 - * - * @param string $subData - * @return string - * @throws Exception - */ - private function _readBIFF5CellRangeAddressFixed($subData) - { - // offset: 0; size: 2; index to first row - $fr = $this->_GetInt2d($subData, 0) + 1; - - // offset: 2; size: 2; index to last row - $lr = $this->_GetInt2d($subData, 2) + 1; - - // offset: 4; size: 1; index to first column - $fc = ord($subData{4}); - - // offset: 5; size: 1; index to last column - $lc = ord($subData{5}); - - // check values - if ($fr > $lr || $fc > $lc) { - throw new Exception('Not a cell range address'); - } - - // column index to letter - $fc = PHPExcel_Cell::stringFromColumnIndex($fc); - $lc = PHPExcel_Cell::stringFromColumnIndex($lc); - - if ($fr == $lr and $fc == $lc) { - return "$fc$fr"; - } - return "$fc$fr:$lc$lr"; - } - - /** - * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1' - * always fixed range - * section 2.5.14 - * - * @param string $subData - * @return string - * @throws Exception - */ - private function _readBIFF8CellRangeAddressFixed($subData) - { - // offset: 0; size: 2; index to first row - $fr = $this->_GetInt2d($subData, 0) + 1; - - // offset: 2; size: 2; index to last row - $lr = $this->_GetInt2d($subData, 2) + 1; - - // offset: 4; size: 2; index to first column - $fc = $this->_GetInt2d($subData, 4); - - // offset: 6; size: 2; index to last column - $lc = $this->_GetInt2d($subData, 6); - - // check values - if ($fr > $lr || $fc > $lc) { - throw new Exception('Not a cell range address'); - } - - // column index to letter - $fc = PHPExcel_Cell::stringFromColumnIndex($fc); - $lc = PHPExcel_Cell::stringFromColumnIndex($lc); - - if ($fr == $lr and $fc == $lc) { - return "$fc$fr"; - } - return "$fc$fr:$lc$lr"; - } - - /** - * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6' - * there are flags indicating whether column/row index is relative - * section 3.3.4 - * - * @param string $subData - * @return string - */ - private function _readBIFF8CellRangeAddress($subData) - { - // todo: if cell range is just a single cell, should this funciton - // not just return e.g. 'A1' and not 'A1:A1' ? - - // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767)) - $fr = $this->_GetInt2d($subData, 0) + 1; - - // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767)) - $lr = $this->_GetInt2d($subData, 2) + 1; - - // offset: 4; size: 2; index to first column or column offset + relative flags - - // bit: 7-0; mask 0x00FF; column index - $fc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 4)); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($subData, 4))) { - $fc = '$' . $fc; - } - - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($subData, 4))) { - $fr = '$' . $fr; - } - - // offset: 6; size: 2; index to last column or column offset + relative flags - - // bit: 7-0; mask 0x00FF; column index - $lc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 6)); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($subData, 6))) { - $lc = '$' . $lc; - } - - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($subData, 6))) { - $lr = '$' . $lr; - } - - return "$fc$fr:$lc$lr"; - } - - /** - * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column - * to indicate offsets from a base cell - * section 3.3.4 - * - * @param string $subData - * @param string $baseCell Base cell - * @return string Cell range address - */ - private function _readBIFF8CellRangeAddressB($subData, $baseCell = 'A1') - { - list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell); - $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1; - - // TODO: if cell range is just a single cell, should this funciton - // not just return e.g. 'A1' and not 'A1:A1' ? - - // offset: 0; size: 2; first row - $frIndex = $this->_GetInt2d($subData, 0); // adjust below - - // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767) - $lrIndex = $this->_GetInt2d($subData, 2); // adjust below - - // offset: 4; size: 2; first column with relative/absolute flags - - // bit: 7-0; mask 0x00FF; column index - $fcIndex = 0x00FF & $this->_GetInt2d($subData, 4); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($subData, 4))) { - // absolute column index - $fc = PHPExcel_Cell::stringFromColumnIndex($fcIndex); - $fc = '$' . $fc; - } else { - // column offset - $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256; - $fc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $fcIndex); - } - - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($subData, 4))) { - // absolute row index - $fr = $frIndex + 1; - $fr = '$' . $fr; - } else { - // row offset - $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536; - $fr = $baseRow + $frIndex; - } - - // offset: 6; size: 2; last column with relative/absolute flags - - // bit: 7-0; mask 0x00FF; column index - $lcIndex = 0x00FF & $this->_GetInt2d($subData, 6); - $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256; - $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex); - - // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) - if (!(0x4000 & $this->_GetInt2d($subData, 6))) { - // absolute column index - $lc = PHPExcel_Cell::stringFromColumnIndex($lcIndex); - $lc = '$' . $lc; - } else { - // column offset - $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256; - $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex); - } - - // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) - if (!(0x8000 & $this->_GetInt2d($subData, 6))) { - // absolute row index - $lr = $lrIndex + 1; - $lr = '$' . $lr; - } else { - // row offset - $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536; - $lr = $baseRow + $lrIndex; - } - - return "$fc$fr:$lc$lr"; - } - - /** - * Read BIFF8 cell range address list - * section 2.5.15 - * - * @param string $subData - * @return array - */ - private function _readBIFF8CellRangeAddressList($subData) - { - $cellRangeAddresses = array(); - - // offset: 0; size: 2; number of the following cell range addresses - $nm = $this->_GetInt2d($subData, 0); - - $offset = 2; - // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses - for ($i = 0; $i < $nm; ++$i) { - $cellRangeAddresses[] = $this->_readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8)); - $offset += 8; - } - - return array( - 'size' => 2 + 8 * $nm, - 'cellRangeAddresses' => $cellRangeAddresses, - ); - } - - /** - * Read BIFF5 cell range address list - * section 2.5.15 - * - * @param string $subData - * @return array - */ - private function _readBIFF5CellRangeAddressList($subData) - { - $cellRangeAddresses = array(); - - // offset: 0; size: 2; number of the following cell range addresses - $nm = $this->_GetInt2d($subData, 0); - - $offset = 2; - // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses - for ($i = 0; $i < $nm; ++$i) { - $cellRangeAddresses[] = $this->_readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6)); - $offset += 6; - } - - return array( - 'size' => 2 + 6 * $nm, - 'cellRangeAddresses' => $cellRangeAddresses, - ); - } - - /** - * Get a sheet range like Sheet1:Sheet3 from REF index - * Note: If there is only one sheet in the range, one gets e.g Sheet1 - * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets, - * in which case an exception is thrown - * - * @param int $index - * @return string|false - * @throws Exception - */ - private function _readSheetRangeByRefIndex($index) - { - if (isset($this->_ref[$index])) { - - $type = $this->_externalBooks[$this->_ref[$index]['externalBookIndex']]['type']; - - switch ($type) { - case 'internal': - // check if we have a deleted 3d reference - if ($this->_ref[$index]['firstSheetIndex'] == 0xFFFF or $this->_ref[$index]['lastSheetIndex'] == 0xFFFF) { - throw new Exception('Deleted sheet reference'); - } - - // we have normal sheet range (collapsed or uncollapsed) - $firstSheetName = $this->_sheets[$this->_ref[$index]['firstSheetIndex']]['name']; - $lastSheetName = $this->_sheets[$this->_ref[$index]['lastSheetIndex']]['name']; - - if ($firstSheetName == $lastSheetName) { - // collapsed sheet range - $sheetRange = $firstSheetName; - } else { - $sheetRange = "$firstSheetName:$lastSheetName"; - } - - // escape the single-quotes - $sheetRange = str_replace("'", "''", $sheetRange); - - // if there are special characters, we need to enclose the range in single-quotes - // todo: check if we have identified the whole set of special characters - // it seems that the following characters are not accepted for sheet names - // and we may assume that they are not present: []*/:\? - if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) { - $sheetRange = "'$sheetRange'"; - } - - return $sheetRange; - break; - - default: - // TODO: external sheet support - throw new Exception('Excel5 reader only supports internal sheets in fomulas'); - break; - } - } - return false; - } - - /** - * read BIFF8 constant value array from array data - * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40} - * section 2.5.8 - * - * @param string $arrayData - * @return array - */ - private function _readBIFF8ConstantArray($arrayData) - { - // offset: 0; size: 1; number of columns decreased by 1 - $nc = ord($arrayData[0]); - - // offset: 1; size: 2; number of rows decreased by 1 - $nr = $this->_GetInt2d($arrayData, 1); - $size = 3; // initialize - $arrayData = substr($arrayData, 3); - - // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values - $matrixChunks = array(); - for ($r = 1; $r <= $nr + 1; ++$r) { - $items = array(); - for ($c = 1; $c <= $nc + 1; ++$c) { - $constant = $this->_readBIFF8Constant($arrayData); - $items[] = $constant['value']; - $arrayData = substr($arrayData, $constant['size']); - $size += $constant['size']; - } - $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"' - } - $matrix = '{' . implode(';', $matrixChunks) . '}'; - - return array( - 'value' => $matrix, - 'size' => $size, - ); - } - - /** - * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value' - * section 2.5.7 - * returns e.g. array('value' => '5', 'size' => 9) - * - * @param string $valueData - * @return array - */ - private function _readBIFF8Constant($valueData) - { - // offset: 0; size: 1; identifier for type of constant - $identifier = ord($valueData[0]); - - switch ($identifier) { - case 0x00: // empty constant (what is this?) - $value = ''; - $size = 9; - break; - case 0x01: // number - // offset: 1; size: 8; IEEE 754 floating-point value - $value = $this->_extractNumber(substr($valueData, 1, 8)); - $size = 9; - break; - case 0x02: // string value - // offset: 1; size: var; Unicode string, 16-bit string length - $string = $this->_readUnicodeStringLong(substr($valueData, 1)); - $value = '"' . $string['value'] . '"'; - $size = 1 + $string['size']; - break; - case 0x04: // boolean - // offset: 1; size: 1; 0 = FALSE, 1 = TRUE - if (ord($valueData[1])) { - $value = 'TRUE'; - } else { - $value = 'FALSE'; - } - $size = 9; - break; - case 0x10: // error code - // offset: 1; size: 1; error code - $value = $this->_mapErrorCode(ord($valueData[1])); - $size = 9; - break; - } - return array( - 'value' => $value, - 'size' => $size, - ); - } - - /** - * Extract RGB color - * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4 - * - * @param string $rgb Encoded RGB value (4 bytes) - * @return array - */ - private function _readRGB($rgb) - { - // offset: 0; size 1; Red component - $r = ord($rgb{0}); - - // offset: 1; size: 1; Green component - $g = ord($rgb{1}); - - // offset: 2; size: 1; Blue component - $b = ord($rgb{2}); - - // HEX notation, e.g. 'FF00FC' - $rgb = sprintf('%02X', $r) . sprintf('%02X', $g) . sprintf('%02X', $b); - - return array('rgb' => $rgb); - } - - /** - * Read byte string (8-bit string length) - * OpenOffice documentation: 2.5.2 - * - * @param string $subData - * @return array - */ - private function _readByteStringShort($subData) - { - // offset: 0; size: 1; length of the string (character count) - $ln = ord($subData[0]); - - // offset: 1: size: var; character array (8-bit characters) - $value = $this->_decodeCodepage(substr($subData, 1, $ln)); - - return array( - 'value' => $value, - 'size' => 1 + $ln, // size in bytes of data structure - ); - } - - /** - * Read byte string (16-bit string length) - * OpenOffice documentation: 2.5.2 - * - * @param string $subData - * @return array - */ - private function _readByteStringLong($subData) - { - // offset: 0; size: 2; length of the string (character count) - $ln = $this->_GetInt2d($subData, 0); - - // offset: 2: size: var; character array (8-bit characters) - $value = $this->_decodeCodepage(substr($subData, 2)); - - //return $string; - return array( - 'value' => $value, - 'size' => 2 + $ln, // size in bytes of data structure - ); - } - - /** - * Extracts an Excel Unicode short string (8-bit string length) - * OpenOffice documentation: 2.5.3 - * function will automatically find out where the Unicode string ends. - * - * @param string $subData - * @return array - */ - private function _readUnicodeStringShort($subData) - { - $value = ''; - - // offset: 0: size: 1; length of the string (character count) - $characterCount = ord($subData[0]); - - $string = $this->_readUnicodeString(substr($subData, 1), $characterCount); - - // add 1 for the string length - $string['size'] += 1; - - return $string; - } - - /** - * Extracts an Excel Unicode long string (16-bit string length) - * OpenOffice documentation: 2.5.3 - * this function is under construction, needs to support rich text, and Asian phonetic settings - * - * @param string $subData - * @return array - */ - private function _readUnicodeStringLong($subData) - { - $value = ''; - - // offset: 0: size: 2; length of the string (character count) - $characterCount = $this->_GetInt2d($subData, 0); - - $string = $this->_readUnicodeString(substr($subData, 2), $characterCount); - - // add 2 for the string length - $string['size'] += 2; - - return $string; - } - - /** - * Read Unicode string with no string length field, but with known character count - * this function is under construction, needs to support rich text, and Asian phonetic settings - * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3 - * - * @param string $subData - * @param int $characterCount - * @return array - */ - private function _readUnicodeString($subData, $characterCount) - { - $value = ''; - - // offset: 0: size: 1; option flags - - // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit) - $isCompressed = !((0x01 & ord($subData[0])) >> 0); - - // bit: 2; mask: 0x04; Asian phonetic settings - $hasAsian = (0x04) & ord($subData[0]) >> 2; - - // bit: 3; mask: 0x08; Rich-Text settings - $hasRichText = (0x08) & ord($subData[0]) >> 3; - - // offset: 1: size: var; character array - // this offset assumes richtext and Asian phonetic settings are off which is generally wrong - // needs to be fixed - $value = $this->_encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed); - - return array( - 'value' => $value, - 'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags - ); - } - - /** - * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas. - * Example: hello"world --> "hello""world" - * - * @param string $value UTF-8 encoded string - * @return string - */ - private function _UTF8toExcelDoubleQuoted($value) - { - return '"' . str_replace('"', '""', $value) . '"'; - } - - /** - * Reads first 8 bytes of a string and return IEEE 754 float - * - * @param string $data Binary string that is at least 8 bytes long - * @return float - */ - private function _extractNumber($data) - { - $rknumhigh = $this->_GetInt4d($data, 4); - $rknumlow = $this->_GetInt4d($data, 0); - $sign = ($rknumhigh & 0x80000000) >> 31; - $exp = ($rknumhigh & 0x7ff00000) >> 20; - $mantissa = (0x100000 | ($rknumhigh & 0x000fffff)); - $mantissalow1 = ($rknumlow & 0x80000000) >> 31; - $mantissalow2 = ($rknumlow & 0x7fffffff); - $value = $mantissa / pow( 2 , (20 - ($exp - 1023))); - - if ($mantissalow1 != 0) { - $value += 1 / pow (2 , (21 - ($exp - 1023))); - } - - $value += $mantissalow2 / pow (2 , (52 - ($exp - 1023))); - if ($sign) { - $value = -1 * $value; - } - - return $value; - } - - private function _GetIEEE754($rknum) - { - if (($rknum & 0x02) != 0) { - $value = $rknum >> 2; - } - else { - // changes by mmp, info on IEEE754 encoding from - // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html - // The RK format calls for using only the most significant 30 bits - // of the 64 bit floating point value. The other 34 bits are assumed - // to be 0 so we use the upper 30 bits of $rknum as follows... - $sign = ($rknum & 0x80000000) >> 31; - $exp = ($rknum & 0x7ff00000) >> 20; - $mantissa = (0x100000 | ($rknum & 0x000ffffc)); - $value = $mantissa / pow( 2 , (20- ($exp - 1023))); - if ($sign) { - $value = -1 * $value; - } - //end of changes by mmp - } - if (($rknum & 0x01) != 0) { - $value /= 100; - } - return $value; - } - - /** - * Get UTF-8 string from (compressed or uncompressed) UTF-16 string - * - * @param string $string - * @param bool $compressed - * @return string - */ - private function _encodeUTF16($string, $compressed = '') - { - if ($compressed) { - $string = $this->_uncompressByteString($string); - } - - $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', 'UTF-16LE'); - - return $result; - } - - /** - * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8. - * - * @param string $string - * @return string - */ - private function _uncompressByteString($string) - { - $uncompressedString = ''; - for ($i = 0; $i < strlen($string); ++$i) { - $uncompressedString .= $string[$i] . "\0"; - } - - return $uncompressedString; - } - - /** - * Convert string to UTF-8. Only used for BIFF5. - * - * @param string $string - * @return string - */ - private function _decodeCodepage($string) - { - $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', $this->_codepage); - return $result; - } - - /** - * Read 16-bit unsigned integer - * - * @param string $data - * @param int $pos - * @return int - */ - private function _GetInt2d($data, $pos) - { - return ord($data[$pos]) | (ord($data[$pos + 1]) << 8); - } - - /** - * Read 32-bit signed integer - * - * @param string $data - * @param int $pos - * @return int - */ - private function _GetInt4d($data, $pos) - { - //return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | - // (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24); - - // FIX: represent numbers correctly on 64-bit system - // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334 - $_or_24 = ord($data[$pos + 3]); - if ($_or_24 >= 128) { - // negative number - $_ord_24 = -abs((256 - $_or_24) << 24); - } else { - $_ord_24 = ($_or_24 & 127) << 24; - } - return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24; - } - - /** - * Read color - * - * @param int $color Indexed color - * @return array RGB color value, example: array('rgb' => 'FF0000') - */ - private function _readColor($color) - { - if ($color <= 0x07 || $color >= 0x40) { - // special built-in color - $color = $this->_mapBuiltInColor($color); - } else if (isset($this->_palette) && isset($this->_palette[$color - 8])) { - // palette color, color index 0x08 maps to pallete index 0 - $color = $this->_palette[$color - 8]; - } else { - // default color table - if ($this->_version == self::XLS_BIFF8) { - $color = $this->_mapColor($color); - } else { - // BIFF5 - $color = $this->_mapColorBIFF5($color); - } - } - - return $color; - } - - - /** - * Map border style - * OpenOffice documentation: 2.5.11 - * - * @param int $index - * @return string - */ - private function _mapBorderStyle($index) - { - switch ($index) { - case 0x00: return PHPExcel_Style_Border::BORDER_NONE; - case 0x01: return PHPExcel_Style_Border::BORDER_THIN; - case 0x02: return PHPExcel_Style_Border::BORDER_MEDIUM; - case 0x03: return PHPExcel_Style_Border::BORDER_DASHED; - case 0x04: return PHPExcel_Style_Border::BORDER_DOTTED; - case 0x05: return PHPExcel_Style_Border::BORDER_THICK; - case 0x06: return PHPExcel_Style_Border::BORDER_DOUBLE; - case 0x07: return PHPExcel_Style_Border::BORDER_HAIR; - case 0x08: return PHPExcel_Style_Border::BORDER_MEDIUMDASHED; - case 0x09: return PHPExcel_Style_Border::BORDER_DASHDOT; - case 0x0A: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT; - case 0x0B: return PHPExcel_Style_Border::BORDER_DASHDOTDOT; - case 0x0C: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT; - case 0x0D: return PHPExcel_Style_Border::BORDER_SLANTDASHDOT; - default: return PHPExcel_Style_Border::BORDER_NONE; - } - } - - /** - * Get fill pattern from index - * OpenOffice documentation: 2.5.12 - * - * @param int $index - * @return string - */ - private function _mapFillPattern($index) - { - switch ($index) { - case 0x00: return PHPExcel_Style_Fill::FILL_NONE; - case 0x01: return PHPExcel_Style_Fill::FILL_SOLID; - case 0x02: return PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY; - case 0x03: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY; - case 0x04: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY; - case 0x05: return PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL; - case 0x06: return PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL; - case 0x07: return PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN; - case 0x08: return PHPExcel_Style_Fill::FILL_PATTERN_DARKUP; - case 0x09: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID; - case 0x0A: return PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS; - case 0x0B: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL; - case 0x0C: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL; - case 0x0D: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN; - case 0x0E: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP; - case 0x0F: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID; - case 0x10: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS; - case 0x11: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY125; - case 0x12: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625; - default: return PHPExcel_Style_Fill::FILL_NONE; - } - } - - /** - * Map error code, e.g. '#N/A' - * - * @param int $subData - * @return string - */ - private function _mapErrorCode($subData) - { - switch ($subData) { - case 0x00: return '#NULL!'; break; - case 0x07: return '#DIV/0!'; break; - case 0x0F: return '#VALUE!'; break; - case 0x17: return '#REF!'; break; - case 0x1D: return '#NAME?'; break; - case 0x24: return '#NUM!'; break; - case 0x2A: return '#N/A'; break; - default: return false; - } - } - - /** - * Map built-in color to RGB value - * - * @param int $color Indexed color - * @return array - */ - private function _mapBuiltInColor($color) - { - switch ($color) { - case 0x00: return array('rgb' => '000000'); - case 0x01: return array('rgb' => 'FFFFFF'); - case 0x02: return array('rgb' => 'FF0000'); - case 0x03: return array('rgb' => '00FF00'); - case 0x04: return array('rgb' => '0000FF'); - case 0x05: return array('rgb' => 'FFFF00'); - case 0x06: return array('rgb' => 'FF00FF'); - case 0x07: return array('rgb' => '00FFFF'); - case 0x40: return array('rgb' => '000000'); // system window text color - case 0x41: return array('rgb' => 'FFFFFF'); // system window background color - default: return array('rgb' => '000000'); - } - } - - /** - * Map color array from BIFF5 built-in color index - * - * @param int $subData - * @return array - */ - private function _mapColorBIFF5($subData) - { - switch ($subData) { - case 0x08: return array('rgb' => '000000'); - case 0x09: return array('rgb' => 'FFFFFF'); - case 0x0A: return array('rgb' => 'FF0000'); - case 0x0B: return array('rgb' => '00FF00'); - case 0x0C: return array('rgb' => '0000FF'); - case 0x0D: return array('rgb' => 'FFFF00'); - case 0x0E: return array('rgb' => 'FF00FF'); - case 0x0F: return array('rgb' => '00FFFF'); - case 0x10: return array('rgb' => '800000'); - case 0x11: return array('rgb' => '008000'); - case 0x12: return array('rgb' => '000080'); - case 0x13: return array('rgb' => '808000'); - case 0x14: return array('rgb' => '800080'); - case 0x15: return array('rgb' => '008080'); - case 0x16: return array('rgb' => 'C0C0C0'); - case 0x17: return array('rgb' => '808080'); - case 0x18: return array('rgb' => '8080FF'); - case 0x19: return array('rgb' => '802060'); - case 0x1A: return array('rgb' => 'FFFFC0'); - case 0x1B: return array('rgb' => 'A0E0F0'); - case 0x1C: return array('rgb' => '600080'); - case 0x1D: return array('rgb' => 'FF8080'); - case 0x1E: return array('rgb' => '0080C0'); - case 0x1F: return array('rgb' => 'C0C0FF'); - case 0x20: return array('rgb' => '000080'); - case 0x21: return array('rgb' => 'FF00FF'); - case 0x22: return array('rgb' => 'FFFF00'); - case 0x23: return array('rgb' => '00FFFF'); - case 0x24: return array('rgb' => '800080'); - case 0x25: return array('rgb' => '800000'); - case 0x26: return array('rgb' => '008080'); - case 0x27: return array('rgb' => '0000FF'); - case 0x28: return array('rgb' => '00CFFF'); - case 0x29: return array('rgb' => '69FFFF'); - case 0x2A: return array('rgb' => 'E0FFE0'); - case 0x2B: return array('rgb' => 'FFFF80'); - case 0x2C: return array('rgb' => 'A6CAF0'); - case 0x2D: return array('rgb' => 'DD9CB3'); - case 0x2E: return array('rgb' => 'B38FEE'); - case 0x2F: return array('rgb' => 'E3E3E3'); - case 0x30: return array('rgb' => '2A6FF9'); - case 0x31: return array('rgb' => '3FB8CD'); - case 0x32: return array('rgb' => '488436'); - case 0x33: return array('rgb' => '958C41'); - case 0x34: return array('rgb' => '8E5E42'); - case 0x35: return array('rgb' => 'A0627A'); - case 0x36: return array('rgb' => '624FAC'); - case 0x37: return array('rgb' => '969696'); - case 0x38: return array('rgb' => '1D2FBE'); - case 0x39: return array('rgb' => '286676'); - case 0x3A: return array('rgb' => '004500'); - case 0x3B: return array('rgb' => '453E01'); - case 0x3C: return array('rgb' => '6A2813'); - case 0x3D: return array('rgb' => '85396A'); - case 0x3E: return array('rgb' => '4A3285'); - case 0x3F: return array('rgb' => '424242'); - default: return array('rgb' => '000000'); - } - } - - /** - * Map color array from BIFF8 built-in color index - * - * @param int $subData - * @return array - */ - private function _mapColor($subData) - { - switch ($subData) { - case 0x08: return array('rgb' => '000000'); - case 0x09: return array('rgb' => 'FFFFFF'); - case 0x0A: return array('rgb' => 'FF0000'); - case 0x0B: return array('rgb' => '00FF00'); - case 0x0C: return array('rgb' => '0000FF'); - case 0x0D: return array('rgb' => 'FFFF00'); - case 0x0E: return array('rgb' => 'FF00FF'); - case 0x0F: return array('rgb' => '00FFFF'); - case 0x10: return array('rgb' => '800000'); - case 0x11: return array('rgb' => '008000'); - case 0x12: return array('rgb' => '000080'); - case 0x13: return array('rgb' => '808000'); - case 0x14: return array('rgb' => '800080'); - case 0x15: return array('rgb' => '008080'); - case 0x16: return array('rgb' => 'C0C0C0'); - case 0x17: return array('rgb' => '808080'); - case 0x18: return array('rgb' => '9999FF'); - case 0x19: return array('rgb' => '993366'); - case 0x1A: return array('rgb' => 'FFFFCC'); - case 0x1B: return array('rgb' => 'CCFFFF'); - case 0x1C: return array('rgb' => '660066'); - case 0x1D: return array('rgb' => 'FF8080'); - case 0x1E: return array('rgb' => '0066CC'); - case 0x1F: return array('rgb' => 'CCCCFF'); - case 0x20: return array('rgb' => '000080'); - case 0x21: return array('rgb' => 'FF00FF'); - case 0x22: return array('rgb' => 'FFFF00'); - case 0x23: return array('rgb' => '00FFFF'); - case 0x24: return array('rgb' => '800080'); - case 0x25: return array('rgb' => '800000'); - case 0x26: return array('rgb' => '008080'); - case 0x27: return array('rgb' => '0000FF'); - case 0x28: return array('rgb' => '00CCFF'); - case 0x29: return array('rgb' => 'CCFFFF'); - case 0x2A: return array('rgb' => 'CCFFCC'); - case 0x2B: return array('rgb' => 'FFFF99'); - case 0x2C: return array('rgb' => '99CCFF'); - case 0x2D: return array('rgb' => 'FF99CC'); - case 0x2E: return array('rgb' => 'CC99FF'); - case 0x2F: return array('rgb' => 'FFCC99'); - case 0x30: return array('rgb' => '3366FF'); - case 0x31: return array('rgb' => '33CCCC'); - case 0x32: return array('rgb' => '99CC00'); - case 0x33: return array('rgb' => 'FFCC00'); - case 0x34: return array('rgb' => 'FF9900'); - case 0x35: return array('rgb' => 'FF6600'); - case 0x36: return array('rgb' => '666699'); - case 0x37: return array('rgb' => '969696'); - case 0x38: return array('rgb' => '003366'); - case 0x39: return array('rgb' => '339966'); - case 0x3A: return array('rgb' => '003300'); - case 0x3B: return array('rgb' => '333300'); - case 0x3C: return array('rgb' => '993300'); - case 0x3D: return array('rgb' => '993366'); - case 0x3E: return array('rgb' => '333399'); - case 0x3F: return array('rgb' => '333333'); - default: return array('rgb' => '000000'); - } - } - -} +<?php +/** + * PHPExcel + * + * Copyright (c) 2006 - 2010 PHPExcel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category PHPExcel + * @package PHPExcel_Reader_Excel5 + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL + * @version 1.7.3c, 2010-06-01 + */ + +// Original file header of ParseXL (used as the base for this class): +// -------------------------------------------------------------------------------- +// Adapted from Excel_Spreadsheet_Reader developed by users bizon153, +// trex005, and mmp11 (SourceForge.net) +// http://sourceforge.net/projects/phpexcelreader/ +// Primary changes made by canyoncasa (dvc) for ParseXL 1.00 ... +// Modelled moreso after Perl Excel Parse/Write modules +// Added Parse_Excel_Spreadsheet object +// Reads a whole worksheet or tab as row,column array or as +// associated hash of indexed rows and named column fields +// Added variables for worksheet (tab) indexes and names +// Added an object call for loading individual woorksheets +// Changed default indexing defaults to 0 based arrays +// Fixed date/time and percent formats +// Includes patches found at SourceForge... +// unicode patch by nobody +// unpack("d") machine depedency patch by matchy +// boundsheet utf16 patch by bjaenichen +// Renamed functions for shorter names +// General code cleanup and rigor, including <80 column width +// Included a testcase Excel file and PHP example calls +// Code works for PHP 5.x + +// Primary changes made by canyoncasa (dvc) for ParseXL 1.10 ... +// http://sourceforge.net/tracker/index.php?func=detail&aid=1466964&group_id=99160&atid=623334 +// Decoding of formula conditions, results, and tokens. +// Support for user-defined named cells added as an array "namedcells" +// Patch code for user-defined named cells supports single cells only. +// NOTE: this patch only works for BIFF8 as BIFF5-7 use a different +// external sheet reference structure + + +/** PHPExcel root directory */ +if (!defined('PHPEXCEL_ROOT')) { + /** + * @ignore + */ + define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../'); + require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php'); + PHPExcel_Autoloader::Register(); + PHPExcel_Shared_ZipStreamWrapper::register(); + // check mbstring.func_overload + if (ini_get('mbstring.func_overload') & 2) { + throw new Exception('Multibyte function overloading in PHP must be disabled for string functions (2).'); + } +} + +/** + * PHPExcel_Reader_Excel5 + * + * This class uses {@link http://sourceforge.net/projects/phpexcelreader/parseXL} + * + * @category PHPExcel + * @package PHPExcel_Reader_Excel5 + * @copyright Copyright (c) 2006 - 2010 PHPExcel (http://www.codeplex.com/PHPExcel) + */ +class PHPExcel_Reader_Excel5 implements PHPExcel_Reader_IReader +{ + // ParseXL definitions + const XLS_BIFF8 = 0x0600; + const XLS_BIFF7 = 0x0500; + const XLS_WorkbookGlobals = 0x0005; + const XLS_Worksheet = 0x0010; + + // record identifiers + const XLS_Type_FORMULA = 0x0006; + const XLS_Type_EOF = 0x000a; + const XLS_Type_PROTECT = 0x0012; + const XLS_Type_OBJECTPROTECT = 0x0063; + const XLS_Type_SCENPROTECT = 0x00dd; + const XLS_Type_PASSWORD = 0x0013; + const XLS_Type_HEADER = 0x0014; + const XLS_Type_FOOTER = 0x0015; + const XLS_Type_EXTERNSHEET = 0x0017; + const XLS_Type_DEFINEDNAME = 0x0018; + const XLS_Type_VERTICALPAGEBREAKS = 0x001a; + const XLS_Type_HORIZONTALPAGEBREAKS = 0x001b; + const XLS_Type_NOTE = 0x001c; + const XLS_Type_SELECTION = 0x001d; + const XLS_Type_DATEMODE = 0x0022; + const XLS_Type_EXTERNNAME = 0x0023; + const XLS_Type_LEFTMARGIN = 0x0026; + const XLS_Type_RIGHTMARGIN = 0x0027; + const XLS_Type_TOPMARGIN = 0x0028; + const XLS_Type_BOTTOMMARGIN = 0x0029; + const XLS_Type_PRINTGRIDLINES = 0x002b; + const XLS_Type_FILEPASS = 0x002f; + const XLS_Type_FONT = 0x0031; + const XLS_Type_CONTINUE = 0x003c; + const XLS_Type_PANE = 0x0041; + const XLS_Type_CODEPAGE = 0x0042; + const XLS_Type_DEFCOLWIDTH = 0x0055; + const XLS_Type_OBJ = 0x005d; + const XLS_Type_COLINFO = 0x007d; + const XLS_Type_IMDATA = 0x007f; + const XLS_Type_SHEETPR = 0x0081; + const XLS_Type_HCENTER = 0x0083; + const XLS_Type_VCENTER = 0x0084; + const XLS_Type_SHEET = 0x0085; + const XLS_Type_PALETTE = 0x0092; + const XLS_Type_SCL = 0x00a0; + const XLS_Type_PAGESETUP = 0x00a1; + const XLS_Type_MULRK = 0x00bd; + const XLS_Type_MULBLANK = 0x00be; + const XLS_Type_DBCELL = 0x00d7; + const XLS_Type_XF = 0x00e0; + const XLS_Type_MERGEDCELLS = 0x00e5; + const XLS_Type_MSODRAWINGGROUP = 0x00eb; + const XLS_Type_MSODRAWING = 0x00ec; + const XLS_Type_SST = 0x00fc; + const XLS_Type_LABELSST = 0x00fd; + const XLS_Type_EXTSST = 0x00ff; + const XLS_Type_EXTERNALBOOK = 0x01ae; + const XLS_Type_DATAVALIDATIONS = 0x01b2; + const XLS_Type_TXO = 0x01b6; + const XLS_Type_HYPERLINK = 0x01b8; + const XLS_Type_DATAVALIDATION = 0x01be; + const XLS_Type_DIMENSION = 0x0200; + const XLS_Type_BLANK = 0x0201; + const XLS_Type_NUMBER = 0x0203; + const XLS_Type_LABEL = 0x0204; + const XLS_Type_BOOLERR = 0x0205; + const XLS_Type_STRING = 0x0207; + const XLS_Type_ROW = 0x0208; + const XLS_Type_INDEX = 0x020b; + const XLS_Type_ARRAY = 0x0221; + const XLS_Type_DEFAULTROWHEIGHT = 0x0225; + const XLS_Type_WINDOW2 = 0x023e; + const XLS_Type_RK = 0x027e; + const XLS_Type_STYLE = 0x0293; + const XLS_Type_FORMAT = 0x041e; + const XLS_Type_SHAREDFMLA = 0x04bc; + const XLS_Type_BOF = 0x0809; + const XLS_Type_SHEETPROTECTION = 0x0867; + const XLS_Type_RANGEPROTECTION = 0x0868; + const XLS_Type_SHEETLAYOUT = 0x0862; + const XLS_Type_XFEXT = 0x087d; + const XLS_Type_UNKNOWN = 0xffff; + + /** + * Read data only? + * + * @var boolean + */ + private $_readDataOnly = false; + + /** + * Restict which sheets should be loaded? + * + * @var array + */ + private $_loadSheetsOnly = null; + + /** + * PHPExcel_Reader_IReadFilter instance + * + * @var PHPExcel_Reader_IReadFilter + */ + private $_readFilter = null; + + /** + * Summary Information stream data. + * + * @var string + */ + private $_summaryInformation; + + /** + * Workbook stream data. (Includes workbook globals substream as well as sheet substreams) + * + * @var string + */ + private $_data; + + /** + * Size in bytes of $this->_data + * + * @var int + */ + private $_dataSize; + + /** + * Current position in stream + * + * @var integer + */ + private $_pos; + + /** + * Workbook to be returned by the reader. + * + * @var PHPExcel + */ + private $_phpExcel; + + /** + * Worksheet that is currently being built by the reader. + * + * @var PHPExcel_Worksheet + */ + private $_phpSheet; + + /** + * BIFF version + * + * @var int + */ + private $_version; + + /** + * Codepage set in the Excel file being read. Only important for BIFF5 (Excel 5.0 - Excel 95) + * For BIFF8 (Excel 97 - Excel 2003) this will always have the value 'UTF-16LE' + * + * @var string + */ + private $_codepage; + + /** + * Shared formats + * + * @var array + */ + private $_formats; + + /** + * Shared fonts + * + * @var array + */ + private $_objFonts; + + /** + * Color palette + * + * @var array + */ + private $_palette; + + /** + * Worksheets + * + * @var array + */ + private $_sheets; + + /** + * External books + * + * @var array + */ + private $_externalBooks; + + /** + * REF structures. Only applies to BIFF8. + * + * @var array + */ + private $_ref; + + /** + * External names + * + * @var array + */ + private $_externalNames; + + /** + * Defined names + * + * @var array + */ + private $_definedname; + + /** + * Shared strings. Only applies to BIFF8. + * + * @var array + */ + private $_sst; + + /** + * Panes are frozen? (in sheet currently being read). See WINDOW2 record. + * + * @var boolean + */ + private $_frozen; + + /** + * Fit printout to number of pages? (in sheet currently being read). See SHEETPR record. + * + * @var boolean + */ + private $_isFitToPages; + + /** + * Objects. One OBJ record contributes with one entry. + * + * @var array + */ + private $_objs; + + /** + * The combined MSODRAWINGGROUP data + * + * @var string + */ + private $_drawingGroupData; + + /** + * The combined MSODRAWING data (per sheet) + * + * @var string + */ + private $_drawingData; + + /** + * Keep track of XF index + * + * @var int + */ + private $_xfIndex; + + /** + * Mapping of XF index (that is a cell XF) to final index in cellXf collection + * + * @var array + */ + private $_mapCellXfIndex; + + /** + * Mapping of XF index (that is a style XF) to final index in cellStyleXf collection + * + * @var array + */ + private $_mapCellStyleXfIndex; + + /** + * The shared formulas in a sheet. One SHAREDFMLA record contributes with one value. + * + * @var array + */ + private $_sharedFormulas; + + /** + * The shared formula parts in a sheet. One FORMULA record contributes with one value if it + * refers to a shared formula. + * + * @var array + */ + private $_sharedFormulaParts; + + /** + * Read data only? + * + * @return boolean + */ + public function getReadDataOnly() + { + return $this->_readDataOnly; + } + + /** + * Set read data only + * + * @param boolean $pValue + * @return PHPExcel_Reader_Excel5 + */ + public function setReadDataOnly($pValue = false) + { + $this->_readDataOnly = $pValue; + return $this; + } + + /** + * Get which sheets to load + * + * @return mixed + */ + public function getLoadSheetsOnly() + { + return $this->_loadSheetsOnly; + } + + /** + * Set which sheets to load + * + * @param mixed $value + * @return PHPExcel_Reader_Excel5 + */ + public function setLoadSheetsOnly($value = null) + { + $this->_loadSheetsOnly = is_array($value) ? + $value : array($value); + return $this; + } + + /** + * Set all sheets to load + * + * @return PHPExcel_Reader_Excel5 + */ + public function setLoadAllSheets() + { + $this->_loadSheetsOnly = null; + return $this; + } + + /** + * Read filter + * + * @return PHPExcel_Reader_IReadFilter + */ + public function getReadFilter() { + return $this->_readFilter; + } + + /** + * Set read filter + * + * @param PHPExcel_Reader_IReadFilter $pValue + * @return PHPExcel_Reader_Excel5 + */ + public function setReadFilter(PHPExcel_Reader_IReadFilter $pValue) { + $this->_readFilter = $pValue; + return $this; + } + + /** + * Create a new PHPExcel_Reader_Excel5 instance + */ + public function __construct() { + $this->_readFilter = new PHPExcel_Reader_DefaultReadFilter(); + } + + /** + * Can the current PHPExcel_Reader_IReader read the file? + * + * @param string $pFileName + * @return boolean + */ + public function canRead($pFilename) + { + // Check if file exists + if (!file_exists($pFilename)) { + throw new Exception("Could not open " . $pFilename . " for reading! File does not exist."); + } + + try { + // Use ParseXL for the hard work. + $ole = new PHPExcel_Shared_OLERead(); + + // get excel data + $res = $ole->read($pFilename); + return true; + + } catch (Exception $e) { + return false; + } + } + + /** + * Loads PHPExcel from file + * + * @param string $pFilename + * @return PHPExcel + * @throws Exception + */ + public function load($pFilename) + { + // Read the OLE file + $this->_loadOLE($pFilename); + + // Initialisations + $this->_phpExcel = new PHPExcel; + $this->_phpExcel->removeSheetByIndex(0); // remove 1st sheet + if (!$this->_readDataOnly) { + $this->_phpExcel->removeCellStyleXfByIndex(0); // remove the default style + $this->_phpExcel->removeCellXfByIndex(0); // remove the default style + } + + // Read the summary information stream (containing meta data) + $this->_readSummaryInformation(); + + // total byte size of Excel data (workbook global substream + sheet substreams) + $this->_dataSize = strlen($this->_data); + + // initialize + $this->_pos = 0; + $this->_codepage = 'CP1252'; + $this->_formats = array(); + $this->_objFonts = array(); + $this->_palette = array(); + $this->_sheets = array(); + $this->_externalBooks = array(); + $this->_ref = array(); + $this->_definedname = array(); + $this->_sst = array(); + $this->_drawingGroupData = ''; + $this->_xfIndex = ''; + $this->_mapCellXfIndex = array(); + $this->_mapCellStyleXfIndex = array(); + + // Parse Workbook Global Substream + while ($this->_pos < $this->_dataSize) { + $code = $this->_GetInt2d($this->_data, $this->_pos); + + switch ($code) { + case self::XLS_Type_BOF: $this->_readBof(); break; + case self::XLS_Type_FILEPASS: $this->_readFilepass(); break; + case self::XLS_Type_CODEPAGE: $this->_readCodepage(); break; + case self::XLS_Type_DATEMODE: $this->_readDateMode(); break; + case self::XLS_Type_FONT: $this->_readFont(); break; + case self::XLS_Type_FORMAT: $this->_readFormat(); break; + case self::XLS_Type_XF: $this->_readXf(); break; + case self::XLS_Type_XFEXT: $this->_readXfExt(); break; + case self::XLS_Type_STYLE: $this->_readStyle(); break; + case self::XLS_Type_PALETTE: $this->_readPalette(); break; + case self::XLS_Type_SHEET: $this->_readSheet(); break; + case self::XLS_Type_EXTERNALBOOK: $this->_readExternalBook(); break; + case self::XLS_Type_EXTERNNAME: $this->_readExternName(); break; + case self::XLS_Type_EXTERNSHEET: $this->_readExternSheet(); break; + case self::XLS_Type_DEFINEDNAME: $this->_readDefinedName(); break; + case self::XLS_Type_MSODRAWINGGROUP: $this->_readMsoDrawingGroup(); break; + case self::XLS_Type_SST: $this->_readSst(); break; + case self::XLS_Type_EOF: $this->_readDefault(); break 2; + default: $this->_readDefault(); break; + } + } + + // Resolve indexed colors for font, fill, and border colors + // Cannot be resolved already in XF record, because PALETTE record comes afterwards + if (!$this->_readDataOnly) { + foreach ($this->_objFonts as $objFont) { + if (isset($objFont->colorIndex)) { + $color = $this->_readColor($objFont->colorIndex); + $objFont->getColor()->setRGB($color['rgb']); + } + } + + foreach ($this->_phpExcel->getCellXfCollection() as $objStyle) { + // fill start and end color + $fill = $objStyle->getFill(); + + if (isset($fill->startcolorIndex)) { + $startColor = $this->_readColor($fill->startcolorIndex); + $fill->getStartColor()->setRGB($startColor['rgb']); + } + + if (isset($fill->endcolorIndex)) { + $endColor = $this->_readColor($fill->endcolorIndex); + $fill->getEndColor()->setRGB($endColor['rgb']); + } + + // border colors + $top = $objStyle->getBorders()->getTop(); + $right = $objStyle->getBorders()->getRight(); + $bottom = $objStyle->getBorders()->getBottom(); + $left = $objStyle->getBorders()->getLeft(); + $diagonal = $objStyle->getBorders()->getDiagonal(); + + if (isset($top->colorIndex)) { + $borderTopColor = $this->_readColor($top->colorIndex); + $top->getColor()->setRGB($borderTopColor['rgb']); + } + + if (isset($right->colorIndex)) { + $borderRightColor = $this->_readColor($right->colorIndex); + $right->getColor()->setRGB($borderRightColor['rgb']); + } + + if (isset($bottom->colorIndex)) { + $borderBottomColor = $this->_readColor($bottom->colorIndex); + $bottom->getColor()->setRGB($borderBottomColor['rgb']); + } + + if (isset($left->colorIndex)) { + $borderLeftColor = $this->_readColor($left->colorIndex); + $left->getColor()->setRGB($borderLeftColor['rgb']); + } + + if (isset($diagonal->colorIndex)) { + $borderDiagonalColor = $this->_readColor($diagonal->colorIndex); + $diagonal->getColor()->setRGB($borderDiagonalColor['rgb']); + } + } + } + + // treat MSODRAWINGGROUP records, workbook-level Escher + if (!$this->_readDataOnly && $this->_drawingGroupData) { + $escherWorkbook = new PHPExcel_Shared_Escher(); + $reader = new PHPExcel_Reader_Excel5_Escher($escherWorkbook); + $escherWorkbook = $reader->load($this->_drawingGroupData); + + // debug Escher stream + //$debug = new Debug_Escher(new PHPExcel_Shared_Escher()); + //$debug->load($this->_drawingGroupData); + } + + // Parse the individual sheets + foreach ($this->_sheets as $sheet) { + + if ($sheet['sheetType'] != 0x00) { + // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module + continue; + } + + // check if sheet should be skipped + if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) { + continue; + } + + // add sheet to PHPExcel object + $this->_phpSheet = $this->_phpExcel->createSheet(); + $this->_phpSheet->setTitle($sheet['name']); + $this->_phpSheet->setSheetState($sheet['sheetState']); + + $this->_pos = $sheet['offset']; + + // Initialize isFitToPages. May change after reading SHEETPR record. + $this->_isFitToPages = false; + + // Initialize drawingData + $this->_drawingData = ''; + + // Initialize objs + $this->_objs = array(); + + // Initialize shared formula parts + $this->_sharedFormulaParts = array(); + + // Initialize shared formulas + $this->_sharedFormulas = array(); + + while ($this->_pos <= $this->_dataSize - 4) { + $code = $this->_GetInt2d($this->_data, $this->_pos); + + switch ($code) { + case self::XLS_Type_BOF: $this->_readBof(); break; + case self::XLS_Type_PRINTGRIDLINES: $this->_readPrintGridlines(); break; + case self::XLS_Type_DEFAULTROWHEIGHT: $this->_readDefaultRowHeight(); break; + case self::XLS_Type_SHEETPR: $this->_readSheetPr(); break; + case self::XLS_Type_HORIZONTALPAGEBREAKS: $this->_readHorizontalPageBreaks(); break; + case self::XLS_Type_VERTICALPAGEBREAKS: $this->_readVerticalPageBreaks(); break; + case self::XLS_Type_HEADER: $this->_readHeader(); break; + case self::XLS_Type_FOOTER: $this->_readFooter(); break; + case self::XLS_Type_HCENTER: $this->_readHcenter(); break; + case self::XLS_Type_VCENTER: $this->_readVcenter(); break; + case self::XLS_Type_LEFTMARGIN: $this->_readLeftMargin(); break; + case self::XLS_Type_RIGHTMARGIN: $this->_readRightMargin(); break; + case self::XLS_Type_TOPMARGIN: $this->_readTopMargin(); break; + case self::XLS_Type_BOTTOMMARGIN: $this->_readBottomMargin(); break; + case self::XLS_Type_PAGESETUP: $this->_readPageSetup(); break; + case self::XLS_Type_PROTECT: $this->_readProtect(); break; + case self::XLS_Type_SCENPROTECT: $this->_readScenProtect(); break; + case self::XLS_Type_OBJECTPROTECT: $this->_readObjectProtect(); break; + case self::XLS_Type_PASSWORD: $this->_readPassword(); break; + case self::XLS_Type_DEFCOLWIDTH: $this->_readDefColWidth(); break; + case self::XLS_Type_COLINFO: $this->_readColInfo(); break; + case self::XLS_Type_DIMENSION: $this->_readDefault(); break; + case self::XLS_Type_ROW: $this->_readRow(); break; + case self::XLS_Type_DBCELL: $this->_readDefault(); break; + case self::XLS_Type_RK: $this->_readRk(); break; + case self::XLS_Type_LABELSST: $this->_readLabelSst(); break; + case self::XLS_Type_MULRK: $this->_readMulRk(); break; + case self::XLS_Type_NUMBER: $this->_readNumber(); break; + case self::XLS_Type_FORMULA: $this->_readFormula(); break; + case self::XLS_Type_SHAREDFMLA: $this->_readSharedFmla(); break; + case self::XLS_Type_BOOLERR: $this->_readBoolErr(); break; + case self::XLS_Type_MULBLANK: $this->_readMulBlank(); break; + case self::XLS_Type_LABEL: $this->_readLabel(); break; + case self::XLS_Type_BLANK: $this->_readBlank(); break; + case self::XLS_Type_MSODRAWING: $this->_readMsoDrawing(); break; + case self::XLS_Type_OBJ: $this->_readObj(); break; + case self::XLS_Type_WINDOW2: $this->_readWindow2(); break; + case self::XLS_Type_SCL: $this->_readScl(); break; + case self::XLS_Type_PANE: $this->_readPane(); break; + case self::XLS_Type_SELECTION: $this->_readSelection(); break; + case self::XLS_Type_MERGEDCELLS: $this->_readMergedCells(); break; + case self::XLS_Type_HYPERLINK: $this->_readHyperLink(); break; + case self::XLS_Type_DATAVALIDATIONS: $this->_readDataValidations(); break; + case self::XLS_Type_DATAVALIDATION: $this->_readDataValidation(); break; + case self::XLS_Type_SHEETLAYOUT: $this->_readSheetLayout(); break; + case self::XLS_Type_SHEETPROTECTION: $this->_readSheetProtection(); break; + case self::XLS_Type_RANGEPROTECTION: $this->_readRangeProtection(); break; + //case self::XLS_Type_IMDATA: $this->_readImData(); break; + case self::XLS_Type_CONTINUE: $this->_readContinue(); break; + case self::XLS_Type_EOF: $this->_readDefault(); break 2; + default: $this->_readDefault(); break; + } + + } + + // treat MSODRAWING records, sheet-level Escher + if (!$this->_readDataOnly && $this->_drawingData) { + $escherWorksheet = new PHPExcel_Shared_Escher(); + $reader = new PHPExcel_Reader_Excel5_Escher($escherWorksheet); + $escherWorksheet = $reader->load($this->_drawingData); + + // debug Escher stream + //$debug = new Debug_Escher(new PHPExcel_Shared_Escher()); + //$debug->load($this->_drawingData); + + // get all spContainers in one long array, so they can be mapped to OBJ records + $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers(); + } + + // treat OBJ records + foreach ($this->_objs as $n => $obj) { + + // the first shape container never has a corresponding OBJ record, hence $n + 1 + $spContainer = $allSpContainers[$n + 1]; + + // we skip all spContainers that are a part of a group shape since we cannot yet handle those + if ($spContainer->getNestingLevel() > 1) { + continue; + } + + // calculate the width and height of the shape + list($startColumn, $startRow) = PHPExcel_Cell::coordinateFromString($spContainer->getStartCoordinates()); + list($endColumn, $endRow) = PHPExcel_Cell::coordinateFromString($spContainer->getEndCoordinates()); + + $startOffsetX = $spContainer->getStartOffsetX(); + $startOffsetY = $spContainer->getStartOffsetY(); + $endOffsetX = $spContainer->getEndOffsetX(); + $endOffsetY = $spContainer->getEndOffsetY(); + + $width = PHPExcel_Shared_Excel5::getDistanceX($this->_phpSheet, $startColumn, $startOffsetX, $endColumn, $endOffsetX); + $height = PHPExcel_Shared_Excel5::getDistanceY($this->_phpSheet, $startRow, $startOffsetY, $endRow, $endOffsetY); + + // calculate offsetX and offsetY of the shape + $offsetX = $startOffsetX * PHPExcel_Shared_Excel5::sizeCol($this->_phpSheet, $startColumn) / 1024; + $offsetY = $startOffsetY * PHPExcel_Shared_Excel5::sizeRow($this->_phpSheet, $startRow) / 256; + + switch ($obj['type']) { + + case 0x08: + // picture + + // get index to BSE entry (1-based) + $BSEindex = $spContainer->getOPT(0x0104); + $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection(); + $BSE = $BSECollection[$BSEindex - 1]; + $blipType = $BSE->getBlipType(); + + // need check because some blip types are not supported by Escher reader such as EMF + if ($blip = $BSE->getBlip()) { + $ih = imagecreatefromstring($blip->getData()); + $drawing = new PHPExcel_Worksheet_MemoryDrawing(); + $drawing->setImageResource($ih); + + // width, height, offsetX, offsetY + $drawing->setResizeProportional(false); + $drawing->setWidth($width); + $drawing->setHeight($height); + $drawing->setOffsetX($offsetX); + $drawing->setOffsetY($offsetY); + + switch ($blipType) { + + case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_JPEG: + $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_JPEG); + $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_JPEG); + break; + + case PHPExcel_Shared_Escher_DggContainer_BstoreContainer_BSE::BLIPTYPE_PNG: + $drawing->setRenderingFunction(PHPExcel_Worksheet_MemoryDrawing::RENDERING_PNG); + $drawing->setMimeType(PHPExcel_Worksheet_MemoryDrawing::MIMETYPE_PNG); + break; + } + + $drawing->setWorksheet($this->_phpSheet); + $drawing->setCoordinates($spContainer->getStartCoordinates()); + } + + break; + + default: + // other object type + break; + + } + } + + // treat SHAREDFMLA records + if ($this->_version == self::XLS_BIFF8) { + foreach ($this->_sharedFormulaParts as $cell => $baseCell) { + $formula = $this->_getFormulaFromStructure($this->_sharedFormulas[$baseCell], $cell); + $this->_phpSheet->getCell($cell)->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA); + } + } + } + + // add the named ranges (defined names) + foreach ($this->_definedname as $definedName) { + if ($definedName['isBuiltInName']) { + switch ($definedName['name']) { + + case pack('C', 0x06): + // print area + // in general, formula looks like this: Foo!$C$7:$J$66,Bar!$A$1:$IV$2 + + $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma? + + $extractedRanges = array(); + foreach ($ranges as $range) { + // $range should look like one of these + // Foo!$C$7:$J$66 + // Bar!$A$1:$IV$2 + + $explodes = explode('!', $range); + $sheetName = $explodes[0]; + + if (count($explodes) == 2) { + $extractedRanges[] = str_replace('$', '', $explodes[1]); // C7:J66 + } + } + if ($docSheet = $this->_phpExcel->getSheetByName($sheetName)) { + $docSheet->getPageSetup()->setPrintArea(implode(',', $extractedRanges)); // C7:J66,A1:IV2 + } + break; + + case pack('C', 0x07): + // print titles (repeating rows) + // Assuming BIFF8, there are 3 cases + // 1. repeating rows + // formula looks like this: Sheet!$A$1:$IV$2 + // rows 1-2 repeat + // 2. repeating columns + // formula looks like this: Sheet!$A$1:$B$65536 + // columns A-B repeat + // 3. both repeating rows and repeating columns + // formula looks like this: Sheet!$A$1:$B$65536,Sheet!$A$1:$IV$2 + + $ranges = explode(',', $definedName['formula']); // FIXME: what if sheetname contains comma? + + foreach ($ranges as $range) { + // $range should look like this one of these + // Sheet!$A$1:$B$65536 + // Sheet!$A$1:$IV$2 + + $explodes = explode('!', $range); + + if (count($explodes) == 2) { + if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) { + + $extractedRange = $explodes[1]; + $extractedRange = str_replace('$', '', $extractedRange); + + $coordinateStrings = explode(':', $extractedRange); + if (count($coordinateStrings) == 2) { + list($firstColumn, $firstRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[0]); + list($lastColumn, $lastRow) = PHPExcel_Cell::coordinateFromString($coordinateStrings[1]); + + if ($firstColumn == 'A' and $lastColumn == 'IV') { + // then we have repeating rows + $docSheet->getPageSetup()->setRowsToRepeatAtTop(array($firstRow, $lastRow)); + } elseif ($firstRow == 1 and $lastRow == 65536) { + // then we have repeating columns + $docSheet->getPageSetup()->setColumnsToRepeatAtLeft(array($firstColumn, $lastColumn)); + } + } + } + } + } + break; + + } + } else { + // Extract range + $explodes = explode('!', $definedName['formula']); + + if (count($explodes) == 2) { + if ($docSheet = $this->_phpExcel->getSheetByName($explodes[0])) { + $extractedRange = $explodes[1]; + $extractedRange = str_replace('$', '', $extractedRange); + + $localOnly = ($definedName['scope'] == 0) ? false : true; + $scope = ($definedName['scope'] == 0) ? + null : $this->_phpExcel->getSheetByName($this->_sheets[$definedName['scope'] - 1]['name']); + + $this->_phpExcel->addNamedRange( new PHPExcel_NamedRange((string)$definedName['name'], $docSheet, $extractedRange, $localOnly, $scope) ); + } + } + } + } + + return $this->_phpExcel; + } + + /** + * Use OLE reader to extract the relevant data streams from the OLE file + * + * @param string $pFilename + */ + private function _loadOLE($pFilename) + { + // OLE reader + $ole = new PHPExcel_Shared_OLERead(); + + // get excel data + $res = $ole->read($pFilename); + $this->_data = $ole->getWorkBook(); + + // Get summary information data + $this->_summaryInformation = $ole->getSummaryInformation(); + } + + /** + * Read summary information + */ + private function _readSummaryInformation() + { + if (!isset($this->_summaryInformation)) { + return; + } + + // offset: 0; size: 2; must be 0xFE 0xFF (UTF-16 LE byte order mark) + // offset: 2; size: 2; + // offset: 4; size: 2; OS version + // offset: 6; size: 2; OS indicator + // offset: 8; size: 16 + // offset: 24; size: 4; section count + + // offset: 28; size: 16; first section's class id: e0 85 9f f2 f9 4f 68 10 ab 91 08 00 2b 27 b3 d9 + // offset: 44; size: 4 + + // section header + // offset: 48; size: 4; section length + $secLength = $this->_GetInt4d($this->_summaryInformation, 48); + + // offset: 52; size: 4; property count + $countProperties = $this->_GetInt4d($this->_summaryInformation, 52); + + // initialize code page (used to resolve string values) + $codePage = 'CP1252'; + + // offset: 56; size: var + // loop through property decarations and properties + for ($i = 0; $i < $countProperties; ++$i) { + + // offset: 56 + 8 * $i; size: 4; property ID + $id = $this->_GetInt4d($this->_summaryInformation, 56 + 8 * $i); + + // offset: 60 + 8 * $i; size: 4; offset from beginning of section (48) + $offset = $this->_GetInt4d($this->_summaryInformation, 60 + 8 * $i); + + $type = $this->_GetInt4d($this->_summaryInformation, 48 + $offset); + + // initialize property value + $value = null; + + // extract property value based on property type + switch ($type) { + case 0x02: // 2 byte signed integer + $value = $this->_GetInt2d($this->_summaryInformation, 52 + $offset); + break; + + case 0x03: // 4 byte signed integer + $value = $this->_GetInt4d($this->_summaryInformation, 52 + $offset); + break; + + case 0x13: // 4 byte unsigned integer + // not needed yet, fix later if necessary + break; + + case 0x1E: // null-terminated string prepended by dword string length + $byteLength = $this->_GetInt4d($this->_summaryInformation, 52 + $offset); + $value = substr($this->_summaryInformation, 56 + $offset, $byteLength); + $value = PHPExcel_Shared_String::ConvertEncoding($value, 'UTF-8', $codePage); + $value = rtrim($value); + break; + + case 0x40: // Filetime (64-bit value representing the number of 100-nanosecond intervals since January 1, 1601) + // PHP-time + $value = PHPExcel_Shared_OLE::OLE2LocalDate(substr($this->_summaryInformation, 52 + $offset, 8)); + break; + + case 0x47: // Clipboard format + // not needed yet, fix later if necessary + break; + } + + // Use value of property id as appropriate + switch ($id) { + case 0x01: // Code Page + $codePage = PHPExcel_Shared_CodePage::NumberToName($value); + break; + + case 0x02: // Title + $this->_phpExcel->getProperties()->setTitle($value); + break; + + case 0x03: // Subject + $this->_phpExcel->getProperties()->setSubject($value); + break; + + case 0x04: // Author (Creator) + $this->_phpExcel->getProperties()->setCreator($value); + break; + + case 0x05: // Keywords + $this->_phpExcel->getProperties()->setKeywords($value); + break; + + case 0x06: // Comments (Description) + $this->_phpExcel->getProperties()->setDescription($value); + break; + + case 0x08: // Last Saved By (LastModifiedBy) + $this->_phpExcel->getProperties()->setLastModifiedBy($value); + break; + + case 0x09: // Revision + // not supported by PHPExcel + break; + + case 0x0C: // Created + $this->_phpExcel->getProperties()->setCreated($value); + break; + + case 0x0D: // Modified + $this->_phpExcel->getProperties()->setModified($value); + break; + + case 0x12: // Name of creating application + // not supported by PHPExcel + break; + } + } + } + + /** + * Reads a general type of BIFF record. Does nothing except for moving stream pointer forward to next record. + */ + private function _readDefault() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + } + + /** + * Read BOF + */ + private function _readBof() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 2; size: 2; type of the following data + $substreamType = $this->_GetInt2d($recordData, 2); + + switch ($substreamType) { + case self::XLS_WorkbookGlobals: + $version = $this->_GetInt2d($recordData, 0); + if (($version != self::XLS_BIFF8) && ($version != self::XLS_BIFF7)) { + throw new Exception('Cannot read this Excel file. Version is too old.'); + } + $this->_version = $version; + break; + + case self::XLS_Worksheet: + // do not use this version information for anything + // it is unreliable (OpenOffice doc, 5.8), use only version information from the global stream + break; + + default: + // substream, e.g. chart + // just skip the entire substream + do { + $code = $this->_GetInt2d($this->_data, $this->_pos); + $this->_readDefault(); + } while ($code != self::XLS_Type_EOF && $this->_pos < $this->_dataSize); + break; + } + } + + /** + * FILEPASS + * + * This record is part of the File Protection Block. It + * contains information about the read/write password of the + * file. All record contents following this record will be + * encrypted. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readFilepass() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + throw new Exception('Cannot read encrypted file'); + } + + /** + * CODEPAGE + * + * This record stores the text encoding used to write byte + * strings, stored as MS Windows code page identifier. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readCodepage() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; code page identifier + $codepage = $this->_GetInt2d($recordData, 0); + + $this->_codepage = PHPExcel_Shared_CodePage::NumberToName($codepage); + } + + /** + * DATEMODE + * + * This record specifies the base date for displaying date + * values. All dates are stored as count of days past this + * base date. In BIFF2-BIFF4 this record is part of the + * Calculation Settings Block. In BIFF5-BIFF8 it is + * stored in the Workbook Globals Substream. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readDateMode() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; 0 = base 1900, 1 = base 1904 + PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900); + if (ord($recordData{0}) == 1) { + PHPExcel_Shared_Date::setExcelCalendar(PHPExcel_Shared_Date::CALENDAR_MAC_1904); + } + } + + /** + * Read a FONT record + */ + private function _readFont() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + $objFont = new PHPExcel_Style_Font(); + + // offset: 0; size: 2; height of the font (in twips = 1/20 of a point) + $size = $this->_GetInt2d($recordData, 0); + $objFont->setSize($size / 20); + + // offset: 2; size: 2; option flags + // bit: 0; mask 0x0001; bold (redundant in BIFF5-BIFF8) + // bit: 1; mask 0x0002; italic + $isItalic = (0x0002 & $this->_GetInt2d($recordData, 2)) >> 1; + if ($isItalic) $objFont->setItalic(true); + + // bit: 2; mask 0x0004; underlined (redundant in BIFF5-BIFF8) + // bit: 3; mask 0x0008; strike + $isStrike = (0x0008 & $this->_GetInt2d($recordData, 2)) >> 3; + if ($isStrike) $objFont->setStrikethrough(true); + + // offset: 4; size: 2; colour index + $colorIndex = $this->_GetInt2d($recordData, 4); + $objFont->colorIndex = $colorIndex; + + // offset: 6; size: 2; font weight + $weight = $this->_GetInt2d($recordData, 6); + switch ($weight) { + case 0x02BC: + $objFont->setBold(true); + break; + } + + // offset: 8; size: 2; escapement type + $escapement = $this->_GetInt2d($recordData, 8); + switch ($escapement) { + case 0x0001: + $objFont->setSuperScript(true); + break; + case 0x0002: + $objFont->setSubScript(true); + break; + } + + // offset: 10; size: 1; underline type + $underlineType = ord($recordData{10}); + switch ($underlineType) { + case 0x00: + break; // no underline + case 0x01: + $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLE); + break; + case 0x02: + $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLE); + break; + case 0x21: + $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_SINGLEACCOUNTING); + break; + case 0x22: + $objFont->setUnderline(PHPExcel_Style_Font::UNDERLINE_DOUBLEACCOUNTING); + break; + } + + // offset: 11; size: 1; font family + // offset: 12; size: 1; character set + // offset: 13; size: 1; not used + // offset: 14; size: var; font name + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringShort(substr($recordData, 14)); + } else { + $string = $this->_readByteStringShort(substr($recordData, 14)); + } + $objFont->setName($string['value']); + + $this->_objFonts[] = $objFont; + } + } + + /** + * FORMAT + * + * This record contains information about a number format. + * All FORMAT records occur together in a sequential list. + * + * In BIFF2-BIFF4 other records referencing a FORMAT record + * contain a zero-based index into this list. From BIFF5 on + * the FORMAT record contains the index itself that will be + * used by other records. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readFormat() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + $indexCode = $this->_GetInt2d($recordData, 0); + + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringLong(substr($recordData, 2)); + } else { + // BIFF7 + $string = $this->_readByteStringShort(substr($recordData, 2)); + } + + $formatString = $string['value']; + $this->_formats[$indexCode] = $formatString; + } + } + + /** + * XF - Extended Format + * + * This record contains formatting information for cells, rows, columns or styles. + * According to http://support.microsoft.com/kb/147732 there are always at least 15 cell style XF + * and 1 cell XF. + * Inspection of Excel files generated by MS Office Excel shows that XF records 0-14 are cell style XF + * and XF record 15 is a cell XF + * We only read the first cell style XF and skip the remaining cell style XF records + * We read all cell XF records. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readXf() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + $objStyle = new PHPExcel_Style(); + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; Index to FONT record + if ($this->_GetInt2d($recordData, 0) < 4) { + $fontIndex = $this->_GetInt2d($recordData, 0); + } else { + // this has to do with that index 4 is omitted in all BIFF versions for some strange reason + // check the OpenOffice documentation of the FONT record + $fontIndex = $this->_GetInt2d($recordData, 0) - 1; + } + $objStyle->setFont($this->_objFonts[$fontIndex]); + + // offset: 2; size: 2; Index to FORMAT record + $numberFormatIndex = $this->_GetInt2d($recordData, 2); + if (isset($this->_formats[$numberFormatIndex])) { + // then we have user-defined format code + $numberformat = array('code' => $this->_formats[$numberFormatIndex]); + } elseif (($code = PHPExcel_Style_NumberFormat::builtInFormatCode($numberFormatIndex)) !== '') { + // then we have built-in format code + $numberformat = array('code' => $code); + } else { + // we set the general format code + $numberformat = array('code' => 'General'); + } + $objStyle->getNumberFormat()->setFormatCode($numberformat['code']); + + // offset: 4; size: 2; XF type, cell protection, and parent style XF + // bit 2-0; mask 0x0007; XF_TYPE_PROT + $xfTypeProt = $this->_GetInt2d($recordData, 4); + // bit 0; mask 0x01; 1 = cell is locked + $isLocked = (0x01 & $xfTypeProt) >> 0; + $objStyle->getProtection()->setLocked($isLocked ? + PHPExcel_Style_Protection::PROTECTION_INHERIT : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED); + + // bit 1; mask 0x02; 1 = Formula is hidden + $isHidden = (0x02 & $xfTypeProt) >> 1; + $objStyle->getProtection()->setHidden($isHidden ? + PHPExcel_Style_Protection::PROTECTION_PROTECTED : PHPExcel_Style_Protection::PROTECTION_UNPROTECTED); + + // bit 2; mask 0x04; 0 = Cell XF, 1 = Cell Style XF + $isCellStyleXf = (0x04 & $xfTypeProt) >> 2; + + // offset: 6; size: 1; Alignment and text break + // bit 2-0, mask 0x07; horizontal alignment + $horAlign = (0x07 & ord($recordData{6})) >> 0; + switch ($horAlign) { + case 0: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_GENERAL); + break; + case 1: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_LEFT); + break; + case 2: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER); + break; + case 3: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_RIGHT); + break; + case 5: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_JUSTIFY); + break; + case 6: + $objStyle->getAlignment()->setHorizontal(PHPExcel_Style_Alignment::HORIZONTAL_CENTER_CONTINUOUS); + break; + } + // bit 3, mask 0x08; wrap text + $wrapText = (0x08 & ord($recordData{6})) >> 3; + switch ($wrapText) { + case 0: + $objStyle->getAlignment()->setWrapText(false); + break; + case 1: + $objStyle->getAlignment()->setWrapText(true); + break; + } + // bit 6-4, mask 0x70; vertical alignment + $vertAlign = (0x70 & ord($recordData{6})) >> 4; + switch ($vertAlign) { + case 0: + $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP); + break; + case 1: + $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_CENTER); + break; + case 2: + $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_BOTTOM); + break; + case 3: + $objStyle->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_JUSTIFY); + break; + } + + if ($this->_version == self::XLS_BIFF8) { + // offset: 7; size: 1; XF_ROTATION: Text rotation angle + $angle = ord($recordData{7}); + $rotation = 0; + if ($angle <= 90) { + $rotation = $angle; + } else if ($angle <= 180) { + $rotation = 90 - $angle; + } else if ($angle == 255) { + $rotation = -165; + } + $objStyle->getAlignment()->setTextRotation($rotation); + + // offset: 8; size: 1; Indentation, shrink to cell size, and text direction + // bit: 3-0; mask: 0x0F; indent level + $indent = (0x0F & ord($recordData{8})) >> 0; + $objStyle->getAlignment()->setIndent($indent); + + // bit: 4; mask: 0x10; 1 = shrink content to fit into cell + $shrinkToFit = (0x10 & ord($recordData{8})) >> 4; + switch ($shrinkToFit) { + case 0: + $objStyle->getAlignment()->setShrinkToFit(false); + break; + case 1: + $objStyle->getAlignment()->setShrinkToFit(true); + break; + } + + // offset: 9; size: 1; Flags used for attribute groups + + // offset: 10; size: 4; Cell border lines and background area + // bit: 3-0; mask: 0x0000000F; left style + if ($bordersLeftStyle = $this->_mapBorderStyle((0x0000000F & $this->_GetInt4d($recordData, 10)) >> 0)) { + $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle); + } + // bit: 7-4; mask: 0x000000F0; right style + if ($bordersRightStyle = $this->_mapBorderStyle((0x000000F0 & $this->_GetInt4d($recordData, 10)) >> 4)) { + $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle); + } + // bit: 11-8; mask: 0x00000F00; top style + if ($bordersTopStyle = $this->_mapBorderStyle((0x00000F00 & $this->_GetInt4d($recordData, 10)) >> 8)) { + $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle); + } + // bit: 15-12; mask: 0x0000F000; bottom style + if ($bordersBottomStyle = $this->_mapBorderStyle((0x0000F000 & $this->_GetInt4d($recordData, 10)) >> 12)) { + $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle); + } + // bit: 22-16; mask: 0x007F0000; left color + $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $this->_GetInt4d($recordData, 10)) >> 16; + + // bit: 29-23; mask: 0x3F800000; right color + $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $this->_GetInt4d($recordData, 10)) >> 23; + + // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom + $diagonalDown = (0x40000000 & $this->_GetInt4d($recordData, 10)) >> 30 ? + true : false; + + // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right + $diagonalUp = (0x80000000 & $this->_GetInt4d($recordData, 10)) >> 31 ? + true : false; + + if ($diagonalUp == false && $diagonalDown == false) { + $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_NONE); + } elseif ($diagonalUp == true && $diagonalDown == false) { + $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_UP); + } elseif ($diagonalUp == false && $diagonalDown == true) { + $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_DOWN); + } elseif ($diagonalUp == true && $diagonalDown == true) { + $objStyle->getBorders()->setDiagonalDirection(PHPExcel_Style_Borders::DIAGONAL_BOTH); + } + + // offset: 14; size: 4; + // bit: 6-0; mask: 0x0000007F; top color + $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & $this->_GetInt4d($recordData, 14)) >> 0; + + // bit: 13-7; mask: 0x00003F80; bottom color + $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & $this->_GetInt4d($recordData, 14)) >> 7; + + // bit: 20-14; mask: 0x001FC000; diagonal color + $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & $this->_GetInt4d($recordData, 14)) >> 14; + + // bit: 24-21; mask: 0x01E00000; diagonal style + if ($bordersDiagonalStyle = $this->_mapBorderStyle((0x01E00000 & $this->_GetInt4d($recordData, 14)) >> 21)) { + $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle); + } + + // bit: 31-26; mask: 0xFC000000 fill pattern + if ($fillType = $this->_mapFillPattern((0xFC000000 & $this->_GetInt4d($recordData, 14)) >> 26)) { + $objStyle->getFill()->setFillType($fillType); + } + // offset: 18; size: 2; pattern and background colour + // bit: 6-0; mask: 0x007F; color index for pattern color + $objStyle->getFill()->startcolorIndex = (0x007F & $this->_GetInt2d($recordData, 18)) >> 0; + + // bit: 13-7; mask: 0x3F80; color index for pattern background + $objStyle->getFill()->endcolorIndex = (0x3F80 & $this->_GetInt2d($recordData, 18)) >> 7; + } else { + // BIFF5 + + // offset: 7; size: 1; Text orientation and flags + $orientationAndFlags = ord($recordData{7}); + + // bit: 1-0; mask: 0x03; XF_ORIENTATION: Text orientation + $xfOrientation = (0x03 & $orientationAndFlags) >> 0; + switch ($xfOrientation) { + case 0: + $objStyle->getAlignment()->setTextRotation(0); + break; + case 1: + $objStyle->getAlignment()->setTextRotation(-165); + break; + case 2: + $objStyle->getAlignment()->setTextRotation(90); + break; + case 3: + $objStyle->getAlignment()->setTextRotation(-90); + break; + } + + // offset: 8; size: 4; cell border lines and background area + $borderAndBackground = $this->_GetInt4d($recordData, 8); + + // bit: 6-0; mask: 0x0000007F; color index for pattern color + $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0; + + // bit: 13-7; mask: 0x00003F80; color index for pattern background + $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7; + + // bit: 21-16; mask: 0x003F0000; fill pattern + $objStyle->getFill()->setFillType($this->_mapFillPattern((0x003F0000 & $borderAndBackground) >> 16)); + + // bit: 24-22; mask: 0x01C00000; bottom line style + $objStyle->getBorders()->getBottom()->setBorderStyle($this->_mapBorderStyle((0x01C00000 & $borderAndBackground) >> 22)); + + // bit: 31-25; mask: 0xFE000000; bottom line color + $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25; + + // offset: 12; size: 4; cell border lines + $borderLines = $this->_GetInt4d($recordData, 12); + + // bit: 2-0; mask: 0x00000007; top line style + $objStyle->getBorders()->getTop()->setBorderStyle($this->_mapBorderStyle((0x00000007 & $borderLines) >> 0)); + + // bit: 5-3; mask: 0x00000038; left line style + $objStyle->getBorders()->getLeft()->setBorderStyle($this->_mapBorderStyle((0x00000038 & $borderLines) >> 3)); + + // bit: 8-6; mask: 0x000001C0; right line style + $objStyle->getBorders()->getRight()->setBorderStyle($this->_mapBorderStyle((0x000001C0 & $borderLines) >> 6)); + + // bit: 15-9; mask: 0x0000FE00; top line color index + $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9; + + // bit: 22-16; mask: 0x007F0000; left line color index + $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16; + + // bit: 29-23; mask: 0x3F800000; right line color index + $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23; + } + + // add cellStyleXf or cellXf and update mapping + if ($isCellStyleXf) { + // we only read one style XF record which is always the first + if ($this->_xfIndex == 0) { + $this->_phpExcel->addCellStyleXf($objStyle); + $this->_mapCellStyleXfIndex[$this->_xfIndex] = 0; + } + } else { + // we read all cell XF records + $this->_phpExcel->addCellXf($objStyle); + $this->_mapCellXfIndex[$this->_xfIndex] = count($this->_phpExcel->getCellXfCollection()) - 1; + } + + // update XF index for when we read next record + ++$this->_xfIndex; + } + } + + /** + * + */ + private function _readXfExt() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; 0x087D = repeated header + + // offset: 2; size: 2 + + // offset: 4; size: 8; not used + + // offset: 12; size: 2; record version + + // offset: 14; size: 2; index to XF record which this record modifies + $ixfe = $this->_GetInt2d($recordData, 14); + + // offset: 16; size: 2; not used + + // offset: 18; size: 2; number of extension properties that follow + $cexts = $this->_GetInt2d($recordData, 18); + + // start reading the actual extension data + $offset = 20; + while ($offset < $length) { + // extension type + $extType = $this->_GetInt2d($recordData, $offset); + + // extension length + $cb = $this->_GetInt2d($recordData, $offset + 2); + + // extension data + $extData = substr($recordData, $offset + 4, $cb); + + switch ($extType) { + case 4: // fill start color + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill(); + $fill->getStartColor()->setRGB($rgb); + unset($fill->startcolorIndex); // normal color index does not apply, discard + } + } + break; + + case 5: // fill end color + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $fill = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFill(); + $fill->getEndColor()->setRGB($rgb); + unset($fill->endcolorIndex); // normal color index does not apply, discard + } + } + break; + + case 7: // border color top + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $top = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getTop(); + $top->getColor()->setRGB($rgb); + unset($top->colorIndex); // normal color index does not apply, discard + } + } + break; + + case 8: // border color bottom + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $bottom = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getBottom(); + $bottom->getColor()->setRGB($rgb); + unset($bottom->colorIndex); // normal color index does not apply, discard + } + } + break; + + case 9: // border color left + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $left = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getLeft(); + $left->getColor()->setRGB($rgb); + unset($left->colorIndex); // normal color index does not apply, discard + } + } + break; + + case 10: // border color right + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $right = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getRight(); + $right->getColor()->setRGB($rgb); + unset($right->colorIndex); // normal color index does not apply, discard + } + } + break; + + case 11: // border color diagonal + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $diagonal = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getBorders()->getDiagonal(); + $diagonal->getColor()->setRGB($rgb); + unset($diagonal->colorIndex); // normal color index does not apply, discard + } + } + break; + + case 13: // font color + $xclfType = $this->_GetInt2d($extData, 0); // color type + $xclrValue = substr($extData, 4, 4); // color value (value based on color type) + + if ($xclfType == 2) { + $rgb = sprintf('%02X%02X%02X', ord($xclrValue{0}), ord($xclrValue{1}), ord($xclrValue{2})); + + // modify the relevant style property + if ( isset($this->_mapCellXfIndex[$ixfe]) ) { + $font = $this->_phpExcel->getCellXfByIndex($this->_mapCellXfIndex[$ixfe])->getFont(); + $font->getColor()->setRGB($rgb); + unset($font->colorIndex); // normal color index does not apply, discard + } + } + break; + } + + $offset += $cb; + } + } + + } + + /** + * Read STYLE record + */ + private function _readStyle() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; index to XF record and flag for built-in style + $ixfe = $this->_GetInt2d($recordData, 0); + + // bit: 11-0; mask 0x0FFF; index to XF record + $xfIndex = (0x0FFF & $ixfe) >> 0; + + // bit: 15; mask 0x8000; 0 = user-defined style, 1 = built-in style + $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15); + + if ($isBuiltIn) { + // offset: 2; size: 1; identifier for built-in style + $builtInId = ord($recordData{2}); + + switch ($builtInId) { + case 0x00: + // currently, we are not using this for anything + break; + + default: + break; + } + + } else { + // user-defined; not supported by PHPExcel + } + } + } + + /** + * Read PALETTE record + */ + private function _readPalette() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; number of following colors + $nm = $this->_GetInt2d($recordData, 0); + + // list of RGB colors + for ($i = 0; $i < $nm; ++$i) { + $rgb = substr($recordData, 2 + 4 * $i, 4); + $this->_palette[] = $this->_readRGB($rgb); + } + } + } + + /** + * SHEET + * + * This record is located in the Workbook Globals + * Substream and represents a sheet inside the workbook. + * One SHEET record is written for each sheet. It stores the + * sheet name and a stream offset to the BOF record of the + * respective Sheet Substream within the Workbook Stream. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readSheet() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 4; absolute stream position of the BOF record of the sheet + $rec_offset = $this->_GetInt4d($recordData, 0); + + // offset: 4; size: 1; sheet state + switch (ord($recordData{4})) { + case 0x00: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break; + case 0x01: $sheetState = PHPExcel_Worksheet::SHEETSTATE_HIDDEN; break; + case 0x02: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VERYHIDDEN; break; + default: $sheetState = PHPExcel_Worksheet::SHEETSTATE_VISIBLE; break; + } + + // offset: 5; size: 1; sheet type + $sheetType = ord($recordData{5}); + + // offset: 6; size: var; sheet name + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringShort(substr($recordData, 6)); + $rec_name = $string['value']; + } elseif ($this->_version == self::XLS_BIFF7) { + $string = $this->_readByteStringShort(substr($recordData, 6)); + $rec_name = $string['value']; + } + + $this->_sheets[] = array( + 'name' => $rec_name, + 'offset' => $rec_offset, + 'sheetState' => $sheetState, + 'sheetType' => $sheetType, + ); + } + + /** + * Read EXTERNALBOOK record + */ + private function _readExternalBook() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset within record data + $offset = 0; + + // there are 4 types of records + if (strlen($recordData) > 4) { + // external reference + // offset: 0; size: 2; number of sheet names ($nm) + $nm = $this->_GetInt2d($recordData, 0); + $offset += 2; + + // offset: 2; size: var; encoded URL without sheet name (Unicode string, 16-bit length) + $encodedUrlString = $this->_readUnicodeStringLong(substr($recordData, 2)); + $offset += $encodedUrlString['size']; + + // offset: var; size: var; list of $nm sheet names (Unicode strings, 16-bit length) + $externalSheetNames = array(); + for ($i = 0; $i < $nm; ++$i) { + $externalSheetNameString = $this->_readUnicodeStringLong(substr($recordData, $offset)); + $externalSheetNames[] = $externalSheetNameString['value']; + $offset += $externalSheetNameString['size']; + } + + // store the record data + $this->_externalBooks[] = array( + 'type' => 'external', + 'encodedUrl' => $encodedUrlString['value'], + 'externalSheetNames' => $externalSheetNames, + ); + + } elseif (substr($recordData, 2, 2) == pack('CC', 0x01, 0x04)) { + // internal reference + // offset: 0; size: 2; number of sheet in this document + // offset: 2; size: 2; 0x01 0x04 + $this->_externalBooks[] = array( + 'type' => 'internal', + ); + } elseif (substr($recordData, 0, 4) == pack('vCC', 0x0001, 0x01, 0x3A)) { + // add-in function + // offset: 0; size: 2; 0x0001 + $this->_externalBooks[] = array( + 'type' => 'addInFunction', + ); + } elseif (substr($recordData, 0, 2) == pack('v', 0x0000)) { + // DDE links, OLE links + // offset: 0; size: 2; 0x0000 + // offset: 2; size: var; encoded source document name + $this->_externalBooks[] = array( + 'type' => 'DDEorOLE', + ); + } + } + + /** + * Read EXTERNNAME record. + */ + private function _readExternName() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // external sheet references provided for named cells + if ($this->_version == self::XLS_BIFF8) { + // offset: 0; size: 2; options + $options = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; + + // offset: 4; size: 2; not used + + // offset: 6; size: var + $nameString = $this->_readUnicodeStringShort(substr($recordData, 6)); + + // offset: var; size: var; formula data + $offset = 6 + $nameString['size']; + $formula = $this->_getFormulaFromStructure(substr($recordData, $offset)); + + $this->_externalNames[] = array( + 'name' => $nameString['value'], + 'formula' => $formula, + ); + } + } + + /** + * Read EXTERNSHEET record + */ + private function _readExternSheet() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // external sheet references provided for named cells + if ($this->_version == self::XLS_BIFF8) { + // offset: 0; size: 2; number of following ref structures + $nm = $this->_GetInt2d($recordData, 0); + for ($i = 0; $i < $nm; ++$i) { + $this->_ref[] = array( + // offset: 2 + 6 * $i; index to EXTERNALBOOK record + 'externalBookIndex' => $this->_GetInt2d($recordData, 2 + 6 * $i), + // offset: 4 + 6 * $i; index to first sheet in EXTERNALBOOK record + 'firstSheetIndex' => $this->_GetInt2d($recordData, 4 + 6 * $i), + // offset: 6 + 6 * $i; index to last sheet in EXTERNALBOOK record + 'lastSheetIndex' => $this->_GetInt2d($recordData, 6 + 6 * $i), + ); + } + } + } + + /** + * DEFINEDNAME + * + * This record is part of a Link Table. It contains the name + * and the token array of an internal defined name. Token + * arrays of defined names contain tokens with aberrant + * token classes. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readDefinedName() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8) { + // retrieves named cells + + // offset: 0; size: 2; option flags + $opts = $this->_GetInt2d($recordData, 0); + + // bit: 5; mask: 0x0020; 0 = user-defined name, 1 = built-in-name + $isBuiltInName = (0x0020 & $opts) >> 5; + + // offset: 2; size: 1; keyboard shortcut + + // offset: 3; size: 1; length of the name (character count) + $nlen = ord($recordData{3}); + + // offset: 4; size: 2; size of the formula data (it can happen that this is zero) + // note: there can also be additional data, this is not included in $flen + $flen = $this->_GetInt2d($recordData, 4); + + // offset: 8; size: 2; 0=Global name, otherwise index to sheet (1-based) + $scope = $this->_GetInt2d($recordData, 8); + + // offset: 14; size: var; Name (Unicode string without length field) + $string = $this->_readUnicodeString(substr($recordData, 14), $nlen); + + // offset: var; size: $flen; formula data + $offset = 14 + $string['size']; + $formulaStructure = pack('v', $flen) . substr($recordData, $offset); + + try { + $formula = $this->_getFormulaFromStructure($formulaStructure); + } catch (Exception $e) { + $formula = ''; + } + + $this->_definedname[] = array( + 'isBuiltInName' => $isBuiltInName, + 'name' => $string['value'], + 'formula' => $formula, + 'scope' => $scope, + ); + } + } + + /** + * Read MSODRAWINGGROUP record + */ + private function _readMsoDrawingGroup() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + + // get spliced record data + $splicedRecordData = $this->_getSplicedRecordData(); + $recordData = $splicedRecordData['recordData']; + + $this->_drawingGroupData .= $recordData; + } + + /** + * SST - Shared String Table + * + * This record contains a list of all strings used anywhere + * in the workbook. Each string occurs only once. The + * workbook uses indexes into the list to reference the + * strings. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + **/ + private function _readSst() + { + // offset within (spliced) record data + $pos = 0; + + // get spliced record data + $splicedRecordData = $this->_getSplicedRecordData(); + + $recordData = $splicedRecordData['recordData']; + $spliceOffsets = $splicedRecordData['spliceOffsets']; + + // offset: 0; size: 4; total number of strings in the workbook + $pos += 4; + + // offset: 4; size: 4; number of following strings ($nm) + $nm = $this->_GetInt4d($recordData, 4); + $pos += 4; + + // loop through the Unicode strings (16-bit length) + for ($i = 0; $i < $nm; ++$i) { + + // number of characters in the Unicode string + $numChars = $this->_GetInt2d($recordData, $pos); + $pos += 2; + + // option flags + $optionFlags = ord($recordData{$pos}); + ++$pos; + + // bit: 0; mask: 0x01; 0 = compressed; 1 = uncompressed + $isCompressed = (($optionFlags & 0x01) == 0) ; + + // bit: 2; mask: 0x02; 0 = ordinary; 1 = Asian phonetic + $hasAsian = (($optionFlags & 0x04) != 0); + + // bit: 3; mask: 0x03; 0 = ordinary; 1 = Rich-Text + $hasRichText = (($optionFlags & 0x08) != 0); + + if ($hasRichText) { + // number of Rich-Text formatting runs + $formattingRuns = $this->_GetInt2d($recordData, $pos); + $pos += 2; + } + + if ($hasAsian) { + // size of Asian phonetic setting + $extendedRunLength = $this->_GetInt4d($recordData, $pos); + $pos += 4; + } + + // expected byte length of character array if not split + $len = ($isCompressed) ? $numChars : $numChars * 2; + + // look up limit position + foreach ($spliceOffsets as $spliceOffset) { + // it can happen that the string is empty, therefore we need + // <= and not just < + if ($pos <= $spliceOffset) { + $limitpos = $spliceOffset; + break; + } + } + + if ($pos + $len <= $limitpos) { + // character array is not split between records + + $retstr = substr($recordData, $pos, $len); + $pos += $len; + + } else { + // character array is split between records + + // first part of character array + $retstr = substr($recordData, $pos, $limitpos - $pos); + + $bytesRead = $limitpos - $pos; + + // remaining characters in Unicode string + $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2)); + + $pos = $limitpos; + + // keep reading the characters + while ($charsLeft > 0) { + + // look up next limit position, in case the string span more than one continue record + foreach ($spliceOffsets as $spliceOffset) { + if ($pos < $spliceOffset) { + $limitpos = $spliceOffset; + break; + } + } + + // repeated option flags + // OpenOffice.org documentation 5.21 + $option = ord($recordData{$pos}); + ++$pos; + + if ($isCompressed && ($option == 0)) { + // 1st fragment compressed + // this fragment compressed + $len = min($charsLeft, $limitpos - $pos); + $retstr .= substr($recordData, $pos, $len); + $charsLeft -= $len; + $isCompressed = true; + + } elseif (!$isCompressed && ($option != 0)) { + // 1st fragment uncompressed + // this fragment uncompressed + $len = min($charsLeft * 2, $limitpos - $pos); + $retstr .= substr($recordData, $pos, $len); + $charsLeft -= $len / 2; + $isCompressed = false; + + } elseif (!$isCompressed && ($option == 0)) { + // 1st fragment uncompressed + // this fragment compressed + $len = min($charsLeft, $limitpos - $pos); + for ($j = 0; $j < $len; ++$j) { + $retstr .= $recordData{$pos + $j} . chr(0); + } + $charsLeft -= $len; + $isCompressed = false; + + } else { + // 1st fragment compressed + // this fragment uncompressed + $newstr = ''; + for ($j = 0; $j < strlen($retstr); ++$j) { + $newstr .= $retstr[$j] . chr(0); + } + $retstr = $newstr; + $len = min($charsLeft * 2, $limitpos - $pos); + $retstr .= substr($recordData, $pos, $len); + $charsLeft -= $len / 2; + $isCompressed = false; + } + + $pos += $len; + } + } + + // convert to UTF-8 + $retstr = $this->_encodeUTF16($retstr, $isCompressed); + + // read additional Rich-Text information, if any + $fmtRuns = array(); + if ($hasRichText) { + // list of formatting runs + for ($j = 0; $j < $formattingRuns; ++$j) { + // first formatted character; zero-based + $charPos = $this->_GetInt2d($recordData, $pos + $j * 4); + + // index to font record + $fontIndex = $this->_GetInt2d($recordData, $pos + 2 + $j * 4); + + $fmtRuns[] = array( + 'charPos' => $charPos, + 'fontIndex' => $fontIndex, + ); + } + $pos += 4 * $formattingRuns; + } + + // read additional Asian phonetics information, if any + if ($hasAsian) { + // For Asian phonetic settings, we skip the extended string data + $pos += $extendedRunLength; + } + + // store the shared sting + $this->_sst[] = array( + 'value' => $retstr, + 'fmtRuns' => $fmtRuns, + ); + } + + // _getSplicedRecordData() takes care of moving current position in data stream + } + + /** + * Read PRINTGRIDLINES record + */ + private function _readPrintGridlines() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { + // offset: 0; size: 2; 0 = do not print sheet grid lines; 1 = print sheet gridlines + $printGridlines = (bool) $this->_GetInt2d($recordData, 0); + $this->_phpSheet->setPrintGridlines($printGridlines); + } + } + + /** + * Read DEFAULTROWHEIGHT record + */ + private function _readDefaultRowHeight() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; option flags + // offset: 2; size: 2; default height for unused rows, (twips 1/20 point) + $height = $this->_GetInt2d($recordData, 2); + $this->_phpSheet->getDefaultRowDimension()->setRowHeight($height / 20); + } + + /** + * Read SHEETPR record + */ + private function _readSheetPr() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2 + + // bit: 6; mask: 0x0040; 0 = outline buttons above outline group + $isSummaryBelow = (0x0040 & $this->_GetInt2d($recordData, 0)) >> 6; + $this->_phpSheet->setShowSummaryBelow($isSummaryBelow); + + // bit: 7; mask: 0x0080; 0 = outline buttons left of outline group + $isSummaryRight = (0x0080 & $this->_GetInt2d($recordData, 0)) >> 7; + $this->_phpSheet->setShowSummaryRight($isSummaryRight); + + // bit: 8; mask: 0x100; 0 = scale printout in percent, 1 = fit printout to number of pages + // this corresponds to radio button setting in page setup dialog in Excel + $this->_isFitToPages = (bool) ((0x0100 & $this->_GetInt2d($recordData, 0)) >> 8); + } + + /** + * Read HORIZONTALPAGEBREAKS record + */ + private function _readHorizontalPageBreaks() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { + + // offset: 0; size: 2; number of the following row index structures + $nm = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 6 * $nm; list of $nm row index structures + for ($i = 0; $i < $nm; ++$i) { + $r = $this->_GetInt2d($recordData, 2 + 6 * $i); + $cf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2); + $cl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4); + + // not sure why two column indexes are necessary? + $this->_phpSheet->setBreakByColumnAndRow($cf, $r, PHPExcel_Worksheet::BREAK_ROW); + } + } + } + + /** + * Read VERTICALPAGEBREAKS record + */ + private function _readVerticalPageBreaks() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { + // offset: 0; size: 2; number of the following column index structures + $nm = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 6 * $nm; list of $nm row index structures + for ($i = 0; $i < $nm; ++$i) { + $c = $this->_GetInt2d($recordData, 2 + 6 * $i); + $rf = $this->_GetInt2d($recordData, 2 + 6 * $i + 2); + $rl = $this->_GetInt2d($recordData, 2 + 6 * $i + 4); + + // not sure why two row indexes are necessary? + $this->_phpSheet->setBreakByColumnAndRow($c, $rf, PHPExcel_Worksheet::BREAK_COLUMN); + } + } + } + + /** + * Read HEADER record + */ + private function _readHeader() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: var + // realized that $recordData can be empty even when record exists + if ($recordData) { + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringLong($recordData); + } else { + $string = $this->_readByteStringShort($recordData); + } + + $this->_phpSheet->getHeaderFooter()->setOddHeader($string['value']); + $this->_phpSheet->getHeaderFooter()->setEvenHeader($string['value']); + } + } + } + + /** + * Read FOOTER record + */ + private function _readFooter() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: var + // realized that $recordData can be empty even when record exists + if ($recordData) { + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringLong($recordData); + } else { + $string = $this->_readByteStringShort($recordData); + } + $this->_phpSheet->getHeaderFooter()->setOddFooter($string['value']); + $this->_phpSheet->getHeaderFooter()->setEvenFooter($string['value']); + } + } + } + + /** + * Read HCENTER record + */ + private function _readHcenter() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; 0 = print sheet left aligned, 1 = print sheet centered horizontally + $isHorizontalCentered = (bool) $this->_GetInt2d($recordData, 0); + + $this->_phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered); + } + } + + /** + * Read VCENTER record + */ + private function _readVcenter() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; 0 = print sheet aligned at top page border, 1 = print sheet vertically centered + $isVerticalCentered = (bool) $this->_GetInt2d($recordData, 0); + + $this->_phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered); + } + } + + /** + * Read LEFTMARGIN record + */ + private function _readLeftMargin() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 8 + $this->_phpSheet->getPageMargins()->setLeft($this->_extractNumber($recordData)); + } + } + + /** + * Read RIGHTMARGIN record + */ + private function _readRightMargin() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 8 + $this->_phpSheet->getPageMargins()->setRight($this->_extractNumber($recordData)); + } + } + + /** + * Read TOPMARGIN record + */ + private function _readTopMargin() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 8 + $this->_phpSheet->getPageMargins()->setTop($this->_extractNumber($recordData)); + } + } + + /** + * Read BOTTOMMARGIN record + */ + private function _readBottomMargin() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 8 + $this->_phpSheet->getPageMargins()->setBottom($this->_extractNumber($recordData)); + } + } + + /** + * Read PAGESETUP record + */ + private function _readPageSetup() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; paper size + $paperSize = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; scaling factor + $scale = $this->_GetInt2d($recordData, 2); + + // offset: 6; size: 2; fit worksheet width to this number of pages, 0 = use as many as needed + $fitToWidth = $this->_GetInt2d($recordData, 6); + + // offset: 8; size: 2; fit worksheet height to this number of pages, 0 = use as many as needed + $fitToHeight = $this->_GetInt2d($recordData, 8); + + // offset: 10; size: 2; option flags + + // bit: 1; mask: 0x0002; 0=landscape, 1=portrait + $isPortrait = (0x0002 & $this->_GetInt2d($recordData, 10)) >> 1; + + // bit: 2; mask: 0x0004; 1= paper size, scaling factor, paper orient. not init + // when this bit is set, do not use flags for those properties + $isNotInit = (0x0004 & $this->_GetInt2d($recordData, 10)) >> 2; + + if (!$isNotInit) { + $this->_phpSheet->getPageSetup()->setPaperSize($paperSize); + switch ($isPortrait) { + case 0: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE); break; + case 1: $this->_phpSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_PORTRAIT); break; + } + + $this->_phpSheet->getPageSetup()->setScale($scale, false); + $this->_phpSheet->getPageSetup()->setFitToPage((bool) $this->_isFitToPages); + $this->_phpSheet->getPageSetup()->setFitToWidth($fitToWidth, false); + $this->_phpSheet->getPageSetup()->setFitToHeight($fitToHeight, false); + } + + // offset: 16; size: 8; header margin (IEEE 754 floating-point value) + $marginHeader = $this->_extractNumber(substr($recordData, 16, 8)); + $this->_phpSheet->getPageMargins()->setHeader($marginHeader); + + // offset: 24; size: 8; footer margin (IEEE 754 floating-point value) + $marginFooter = $this->_extractNumber(substr($recordData, 24, 8)); + $this->_phpSheet->getPageMargins()->setFooter($marginFooter); + } + } + + /** + * PROTECT - Sheet protection (BIFF2 through BIFF8) + * if this record is omitted, then it also means no sheet protection + */ + private function _readProtect() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly) { + return; + } + + // offset: 0; size: 2; + + // bit 0, mask 0x01; 1 = sheet is protected + $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; + $this->_phpSheet->getProtection()->setSheet((bool)$bool); + } + + /** + * SCENPROTECT + */ + private function _readScenProtect() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly) { + return; + } + + // offset: 0; size: 2; + + // bit: 0, mask 0x01; 1 = scenarios are protected + $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; + + $this->_phpSheet->getProtection()->setScenarios((bool)$bool); + } + + /** + * OBJECTPROTECT + */ + private function _readObjectProtect() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly) { + return; + } + + // offset: 0; size: 2; + + // bit: 0, mask 0x01; 1 = objects are protected + $bool = (0x01 & $this->_GetInt2d($recordData, 0)) >> 0; + + $this->_phpSheet->getProtection()->setObjects((bool)$bool); + } + + /** + * PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8) + */ + private function _readPassword() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; 16-bit hash value of password + $password = strtoupper(dechex($this->_GetInt2d($recordData, 0))); // the hashed password + $this->_phpSheet->getProtection()->setPassword($password, true); + } + } + + /** + * Read DEFCOLWIDTH record + */ + private function _readDefColWidth() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; default column width + $width = $this->_GetInt2d($recordData, 0); + if ($width != 8) { + $this->_phpSheet->getDefaultColumnDimension()->setWidth($width); + } + } + + /** + * Read COLINFO record + */ + private function _readColInfo() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; index to first column in range + $fc = $this->_GetInt2d($recordData, 0); // first column index + + // offset: 2; size: 2; index to last column in range + $lc = $this->_GetInt2d($recordData, 2); // first column index + + // offset: 4; size: 2; width of the column in 1/256 of the width of the zero character + $width = $this->_GetInt2d($recordData, 4); + + // offset: 6; size: 2; index to XF record for default column formatting + $xfIndex = $this->_GetInt2d($recordData, 6); + + // offset: 8; size: 2; option flags + + // bit: 0; mask: 0x0001; 1= columns are hidden + $isHidden = (0x0001 & $this->_GetInt2d($recordData, 8)) >> 0; + + // bit: 10-8; mask: 0x0700; outline level of the columns (0 = no outline) + $level = (0x0700 & $this->_GetInt2d($recordData, 8)) >> 8; + + // bit: 12; mask: 0x1000; 1 = collapsed + $isCollapsed = (0x1000 & $this->_GetInt2d($recordData, 8)) >> 12; + + // offset: 10; size: 2; not used + + for ($i = $fc; $i <= $lc; ++$i) { + if ($lc == 255 || $lc == 256) { + $this->_phpSheet->getDefaultColumnDimension()->setWidth($width / 256); + break; + } + $this->_phpSheet->getColumnDimensionByColumn($i)->setWidth($width / 256); + $this->_phpSheet->getColumnDimensionByColumn($i)->setVisible(!$isHidden); + $this->_phpSheet->getColumnDimensionByColumn($i)->setOutlineLevel($level); + $this->_phpSheet->getColumnDimensionByColumn($i)->setCollapsed($isCollapsed); + $this->_phpSheet->getColumnDimensionByColumn($i)->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * ROW + * + * This record contains the properties of a single row in a + * sheet. Rows and cells in a sheet are divided into blocks + * of 32 rows. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readRow() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; index of this row + $r = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to column of the first cell which is described by a cell record + + // offset: 4; size: 2; index to column of the last cell which is described by a cell record, increased by 1 + + // offset: 6; size: 2; + + // bit: 14-0; mask: 0x7FFF; height of the row, in twips = 1/20 of a point + $height = (0x7FFF & $this->_GetInt2d($recordData, 6)) >> 0; + + // bit: 15: mask: 0x8000; 0 = row has custom height; 1= row has default height + $useDefaultHeight = (0x8000 & $this->_GetInt2d($recordData, 6)) >> 15; + + if (!$useDefaultHeight) { + $this->_phpSheet->getRowDimension($r + 1)->setRowHeight($height / 20); + } + + // offset: 8; size: 2; not used + + // offset: 10; size: 2; not used in BIFF5-BIFF8 + + // offset: 12; size: 4; option flags and default row formatting + + // bit: 2-0: mask: 0x00000007; outline level of the row + $level = (0x00000007 & $this->_GetInt4d($recordData, 12)) >> 0; + $this->_phpSheet->getRowDimension($r + 1)->setOutlineLevel($level); + + // bit: 4; mask: 0x00000010; 1 = outline group start or ends here... and is collapsed + $isCollapsed = (0x00000010 & $this->_GetInt4d($recordData, 12)) >> 4; + $this->_phpSheet->getRowDimension($r + 1)->setCollapsed($isCollapsed); + + // bit: 5; mask: 0x00000020; 1 = row is hidden + $isHidden = (0x00000020 & $this->_GetInt4d($recordData, 12)) >> 5; + $this->_phpSheet->getRowDimension($r + 1)->setVisible(!$isHidden); + + // bit: 7; mask: 0x00000080; 1 = row has explicit format + $hasExplicitFormat = (0x00000080 & $this->_GetInt4d($recordData, 12)) >> 7; + + // bit: 27-16; mask: 0x0FFF0000; only applies when hasExplicitFormat = 1; index to XF record + $xfIndex = (0x0FFF0000 & $this->_GetInt4d($recordData, 12)) >> 16; + + if ($hasExplicitFormat) { + $this->_phpSheet->getRowDimension($r + 1)->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read RK record + * This record represents a cell that contains an RK value + * (encoded integer or floating-point value). If a + * floating-point value cannot be encoded to an RK value, + * a NUMBER record will be written. This record replaces the + * record INTEGER written in BIFF2. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readRk() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to column + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset: 4; size: 2; index to XF record + $xfIndex = $this->_GetInt2d($recordData, 4); + + // offset: 6; size: 4; RK value + $rknum = $this->_GetInt4d($recordData, 6); + $numValue = $this->_GetIEEE754($rknum); + + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + if (!$this->_readDataOnly) { + // add style information + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + + // add cell + $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); + } + } + + /** + * Read LABELSST record + * This record represents a cell that contains a string. It + * replaces the LABEL record and RSTRING record used in + * BIFF2-BIFF5. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readLabelSst() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to column + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset: 4; size: 2; index to XF record + $xfIndex = $this->_GetInt2d($recordData, 4); + + // offset: 6; size: 4; index to SST record + $index = $this->_GetInt4d($recordData, 6); + + // add cell + if (($fmtRuns = $this->_sst[$index]['fmtRuns']) && !$this->_readDataOnly) { + // then we should treat as rich text + $richText = new PHPExcel_RichText(); + $charPos = 0; + for ($i = 0; $i <= count($this->_sst[$index]['fmtRuns']); ++$i) { + if (isset($fmtRuns[$i])) { + $text = PHPExcel_Shared_String::Substring($this->_sst[$index]['value'], $charPos, $fmtRuns[$i]['charPos'] - $charPos); + $charPos = $fmtRuns[$i]['charPos']; + } else { + $text = PHPExcel_Shared_String::Substring($this->_sst[$index]['value'], $charPos, PHPExcel_Shared_String::CountCharacters($this->_sst[$index]['value'])); + } + + if (PHPExcel_Shared_String::CountCharacters($text) > 0) { + if ($i == 0) { // first text run, no style + $richText->createText($text); + } else { + $textRun = $richText->createTextRun($text); + if (isset($fmtRuns[$i - 1])) { + if ($fmtRuns[$i - 1]['fontIndex'] < 4) { + $fontIndex = $fmtRuns[$i - 1]['fontIndex']; + } else { + // this has to do with that index 4 is omitted in all BIFF versions for some strange reason + // check the OpenOffice documentation of the FONT record + $fontIndex = $fmtRuns[$i - 1]['fontIndex'] - 1; + } + $textRun->setFont(clone $this->_objFonts[$fontIndex]); + } + } + } + } + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + $cell->setValueExplicit($richText, PHPExcel_Cell_DataType::TYPE_STRING); + } else { + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + $cell->setValueExplicit($this->_sst[$index]['value'], PHPExcel_Cell_DataType::TYPE_STRING); + } + + if (!$this->_readDataOnly) { + // add style information + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read MULRK record + * This record represents a cell range containing RK value + * cells. All cells are located in the same row. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readMulRk() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to first column + $colFirst = $this->_GetInt2d($recordData, 2); + + // offset: var; size: 2; index to last column + $colLast = $this->_GetInt2d($recordData, $length - 2); + $columns = $colLast - $colFirst + 1; + + // offset within record data + $offset = 4; + + for ($i = 0; $i < $columns; ++$i) { + $columnString = PHPExcel_Cell::stringFromColumnIndex($colFirst + $i); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + + // offset: var; size: 2; index to XF record + $xfIndex = $this->_GetInt2d($recordData, $offset); + + // offset: var; size: 4; RK value + $numValue = $this->_GetIEEE754($this->_GetInt4d($recordData, $offset + 2)); + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + if (!$this->_readDataOnly) { + // add style + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + + // add cell value + $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); + } + + $offset += 6; + } + } + + /** + * Read NUMBER record + * This record represents a cell that contains a + * floating-point value. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readNumber() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size 2; index to column + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset 4; size: 2; index to XF record + $xfIndex = $this->_GetInt2d($recordData, 4); + + $numValue = $this->_extractNumber(substr($recordData, 6, 8)); + + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + if (!$this->_readDataOnly) { + // add cell style + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + + // add cell value + $cell->setValueExplicit($numValue, PHPExcel_Cell_DataType::TYPE_NUMERIC); + } + } + + /** + * Read FORMULA record + perhaps a following STRING record if formula result is a string + * This record contains the token array and the result of a + * formula cell. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readFormula() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; row index + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; col index + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // offset: 20: size: variable; formula structure + $formulaStructure = substr($recordData, 20); + + // offset: 14: size: 2; option flags, recalculate always, recalculate on open etc. + $options = $this->_GetInt2d($recordData, 14); + + // bit: 0; mask: 0x0001; 1 = recalculate always + // bit: 1; mask: 0x0002; 1 = calculate on open + // bit: 2; mask: 0x0008; 1 = part of a shared formula + $isPartOfSharedFormula = (bool) (0x0008 & $options); + + // WARNING: + // We can apparently not rely on $isPartOfSharedFormula. Even when $isPartOfSharedFormula = true + // the formula data may be ordinary formula data, therefore we need to check + // explicitly for the tExp token (0x01) + $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure{2}) == 0x01; + + if ($isPartOfSharedFormula) { + // part of shared formula which means there will be a formula with a tExp token and nothing else + // get the base cell, grab tExp token + $baseRow = $this->_GetInt2d($formulaStructure, 3); + $baseCol = $this->_GetInt2d($formulaStructure, 5); + $this->_baseCell = PHPExcel_Cell::stringFromColumnIndex($baseCol). ($baseRow + 1); + } + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + + if ($isPartOfSharedFormula) { + // formula is added to this cell after the sheet has been read + $this->_sharedFormulaParts[$columnString . ($row + 1)] = $this->_baseCell; + } + + // offset: 16: size: 4; not used + + // offset: 4; size: 2; XF index + $xfIndex = $this->_GetInt2d($recordData, 4); + + // offset: 6; size: 8; result of the formula + if ( (ord($recordData{6}) == 0) + && (ord($recordData{12}) == 255) + && (ord($recordData{13}) == 255) ) { + + // String formula. Result follows in appended STRING record + $dataType = PHPExcel_Cell_DataType::TYPE_STRING; + + // read possible SHAREDFMLA record + $code = $this->_GetInt2d($this->_data, $this->_pos); + if ($code == self::XLS_Type_SHAREDFMLA) { + $this->_readSharedFmla(); + } + + // read STRING record + $value = $this->_readString(); + + } elseif ((ord($recordData{6}) == 1) + && (ord($recordData{12}) == 255) + && (ord($recordData{13}) == 255)) { + + // Boolean formula. Result is in +2; 0=false, 1=true + $dataType = PHPExcel_Cell_DataType::TYPE_BOOL; + $value = (bool) ord($recordData{8}); + + } elseif ((ord($recordData{6}) == 2) + && (ord($recordData{12}) == 255) + && (ord($recordData{13}) == 255)) { + + // Error formula. Error code is in +2 + $dataType = PHPExcel_Cell_DataType::TYPE_ERROR; + $value = $this->_mapErrorCode(ord($recordData{8})); + + } elseif ((ord($recordData{6}) == 3) + && (ord($recordData{12}) == 255) + && (ord($recordData{13}) == 255)) { + + // Formula result is a null string + $dataType = PHPExcel_Cell_DataType::TYPE_NULL; + $value = ''; + + } else { + + // forumla result is a number, first 14 bytes like _NUMBER record + $dataType = PHPExcel_Cell_DataType::TYPE_NUMERIC; + $value = $this->_extractNumber(substr($recordData, 6, 8)); + + } + + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + if (!$this->_readDataOnly) { + // add cell style + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + + // store the formula + if (!$isPartOfSharedFormula) { + // not part of shared formula + // add cell value. If we can read formula, populate with formula, otherwise just used cached value + try { + if ($this->_version != self::XLS_BIFF8) { + throw new Exception('Not BIFF8. Can only read BIFF8 formulas'); + } + $formula = $this->_getFormulaFromStructure($formulaStructure); // get formula in human language + $cell->setValueExplicit('=' . $formula, PHPExcel_Cell_DataType::TYPE_FORMULA); + + } catch (Exception $e) { + $cell->setValueExplicit($value, $dataType); + } + } else { + if ($this->_version == self::XLS_BIFF8) { + // do nothing at this point, formula id added later in the code + } else { + $cell->setValueExplicit($value, $dataType); + } + } + + // store the cached calculated value + $cell->setCalculatedValue($value); + } + } + + /** + * Read a SHAREDFMLA record. This function just stores the binary shared formula in the reader, + * which usually contains relative references. + * These will be used to construct the formula in each shared formula part after the sheet is read. + */ + private function _readSharedFmla() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0, size: 6; cell range address of the area used by the shared formula, not used for anything + $cellRange = substr($recordData, 0, 6); + $cellRange = $this->_readBIFF5CellRangeAddressFixed($cellRange); // note: even BIFF8 uses BIFF5 syntax + + // offset: 6, size: 1; not used + + // offset: 7, size: 1; number of existing FORMULA records for this shared formula + $no = ord($recordData{7}); + + // offset: 8, size: var; Binary token array of the shared formula + $formula = substr($recordData, 8); + + // at this point we only store the shared formula for later use + $this->_sharedFormulas[$this->_baseCell] = $formula; + + } + + /** + * Read a STRING record from current stream position and advance the stream pointer to next record + * This record is used for storing result from FORMULA record when it is a string, and + * it occurs directly after the FORMULA record + * + * @return string The string contents as UTF-8 + */ + private function _readString() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringLong($recordData); + $value = $string['value']; + } else { + $string = $this->_readByteStringLong($recordData); + $value = $string['value']; + } + + return $value; + } + + /** + * Read BOOLERR record + * This record represents a Boolean value or error value + * cell. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readBoolErr() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; row index + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; column index + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset: 4; size: 2; index to XF record + $xfIndex = $this->_GetInt2d($recordData, 4); + + // offset: 6; size: 1; the boolean value or error value + $boolErr = ord($recordData{6}); + + // offset: 7; size: 1; 0=boolean; 1=error + $isError = ord($recordData{7}); + + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + switch ($isError) { + case 0: // boolean + $value = (bool) $boolErr; + + // add cell value + $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_BOOL); + break; + + case 1: // error type + $value = $this->_mapErrorCode($boolErr); + + // add cell value + $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_ERROR); + break; + } + + if (!$this->_readDataOnly) { + // add cell style + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read MULBLANK record + * This record represents a cell range of empty cells. All + * cells are located in the same row + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readMulBlank() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to first column + $fc = $this->_GetInt2d($recordData, 2); + + // offset: 4; size: 2 x nc; list of indexes to XF records + // add style information + if (!$this->_readDataOnly) { + for ($i = 0; $i < $length / 2 - 3; ++$i) { + $columnString = PHPExcel_Cell::stringFromColumnIndex($fc + $i); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + $xfIndex = $this->_GetInt2d($recordData, 4 + 2 * $i); + $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + // offset: 6; size 2; index to last column (not needed) + } + + /** + * Read LABEL record + * This record represents a cell that contains a string. In + * BIFF8 it is usually replaced by the LABELSST record. + * Excel still uses this record, if it copies unformatted + * text cells to the clipboard. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readLabel() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; index to row + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; index to column + $column = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($column); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset: 4; size: 2; XF index + $xfIndex = $this->_GetInt2d($recordData, 4); + + // add cell value + // todo: what if string is very long? continue record + if ($this->_version == self::XLS_BIFF8) { + $string = $this->_readUnicodeStringLong(substr($recordData, 6)); + $value = $string['value']; + } else { + $string = $this->_readByteStringLong(substr($recordData, 6)); + $value = $string['value']; + } + $cell = $this->_phpSheet->getCell($columnString . ($row + 1)); + $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING); + + if (!$this->_readDataOnly) { + // add cell style + $cell->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + } + + /** + * Read BLANK record + */ + private function _readBlank() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; row index + $row = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; col index + $col = $this->_GetInt2d($recordData, 2); + $columnString = PHPExcel_Cell::stringFromColumnIndex($col); + + // Read cell? + if ( !is_null($this->getReadFilter()) && $this->getReadFilter()->readCell($columnString, $row + 1, $this->_phpSheet->getTitle()) ) { + // offset: 4; size: 2; XF index + $xfIndex = $this->_GetInt2d($recordData, 4); + + // add style information + if (!$this->_readDataOnly) { + $this->_phpSheet->getCell($columnString . ($row + 1))->setXfIndex($this->_mapCellXfIndex[$xfIndex]); + } + } + + } + + /** + * Read MSODRAWING record + */ + private function _readMsoDrawing() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + + // get spliced record data + $splicedRecordData = $this->_getSplicedRecordData(); + $recordData = $splicedRecordData['recordData']; + + $this->_drawingData .= $recordData; + } + + /** + * Read OBJ record + */ + private function _readObj() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly || $this->_version != self::XLS_BIFF8) { + return; + } + + // recordData consists of an array of subrecords looking like this: + // ft: 2 bytes; id number + // cb: 2 bytes; size in bytes of following data + // data: var; subrecord data + + // for now, we are just interested in the second subrecord containing the object type + $ot = $this->_GetInt2d($recordData, 4); + + $this->_objs[] = array( + 'type' => $ot, + ); + } + + /** + * Read WINDOW2 record + */ + private function _readWindow2() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; option flags + $options = $this->_GetInt2d($recordData, 0); + + // bit: 1; mask: 0x0002; 0 = do not show gridlines, 1 = show gridlines + $showGridlines = (bool) ((0x0002 & $options) >> 1); + $this->_phpSheet->setShowGridlines($showGridlines); + + // bit: 2; mask: 0x0004; 0 = do not show headers, 1 = show headers + $showRowColHeaders = (bool) ((0x0004 & $options) >> 2); + $this->_phpSheet->setShowRowColHeaders($showRowColHeaders); + + // bit: 3; mask: 0x0008; 0 = panes are not frozen, 1 = panes are frozen + $this->_frozen = (bool) ((0x0008 & $options) >> 3); + + // bit: 6; mask: 0x0040; 0 = columns from left to right, 1 = columns from right to left + $this->_phpSheet->setRightToLeft((bool)((0x0040 & $options) >> 6)); + + // bit: 10; mask: 0x0400; 0 = sheet not active, 1 = sheet active + $isActive = (bool) ((0x0400 & $options) >> 10); + if ($isActive) { + $this->_phpExcel->setActiveSheetIndex($this->_phpExcel->getIndex($this->_phpSheet)); + } + } + + /** + * Read SCL record + */ + private function _readScl() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // offset: 0; size: 2; numerator of the view magnification + $numerator = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; numerator of the view magnification + $denumerator = $this->_GetInt2d($recordData, 2); + + // set the zoom scale (in percent) + $this->_phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator); + } + + /** + * Read PANE record + */ + private function _readPane() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; position of vertical split + $px = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; position of horizontal split + $py = $this->_GetInt2d($recordData, 2); + + if ($this->_frozen) { + // frozen panes + $this->_phpSheet->freezePane(PHPExcel_Cell::stringFromColumnIndex($px) . ($py + 1)); + } else { + // unfrozen panes; split windows; not supported by PHPExcel core + } + } + } + + /** + * Read SELECTION record. There is one such record for each pane in the sheet. + */ + private function _readSelection() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 1; pane identifier + $paneId = ord($recordData{0}); + + // offset: 1; size: 2; index to row of the active cell + $r = $this->_GetInt2d($recordData, 1); + + // offset: 3; size: 2; index to column of the active cell + $c = $this->_GetInt2d($recordData, 3); + + // offset: 5; size: 2; index into the following cell range list to the + // entry that contains the active cell + $index = $this->_GetInt2d($recordData, 5); + + // offset: 7; size: var; cell range address list containing all selected cell ranges + $data = substr($recordData, 7); + $cellRangeAddressList = $this->_readBIFF5CellRangeAddressList($data); // note: also BIFF8 uses BIFF5 syntax + + $selectedCells = $cellRangeAddressList['cellRangeAddresses'][0]; + + // first row '1' + last row '16384' indicates that full column is selected (apparently also in BIFF8!) + if (preg_match('/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) { + $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)16384$/', '${1}1048576', $selectedCells); + } + + // first row '1' + last row '65536' indicates that full column is selected + if (preg_match('/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) { + $selectedCells = preg_replace('/^([A-Z]+1\:[A-Z]+)65536$/', '${1}1048576', $selectedCells); + } + + // first column 'A' + last column 'IV' indicates that full row is selected + if (preg_match('/^(A[0-9]+\:)IV([0-9]+)$/', $selectedCells)) { + $selectedCells = preg_replace('/^(A[0-9]+\:)IV([0-9]+)$/', '${1}XFD${2}', $selectedCells); + } + + $this->_phpSheet->setSelectedCells($selectedCells); + } + } + + /** + * MERGEDCELLS + * + * This record contains the addresses of merged cell ranges + * in the current sheet. + * + * -- "OpenOffice.org's Documentation of the Microsoft + * Excel File Format" + */ + private function _readMergedCells() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_version == self::XLS_BIFF8 && !$this->_readDataOnly) { + $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($recordData); + foreach ($cellRangeAddressList['cellRangeAddresses'] as $cellRangeAddress) { + $this->_phpSheet->mergeCells($cellRangeAddress); + } + } + } + + /** + * Read HYPERLINK record + */ + private function _readHyperLink() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer forward to next record + $this->_pos += 4 + $length; + + if (!$this->_readDataOnly) { + // offset: 0; size: 8; cell range address of all cells containing this hyperlink + try { + $cellRange = $this->_readBIFF8CellRangeAddressFixed($recordData, 0, 8); + } catch (Exception $e) { + return; + } + + // offset: 8, size: 16; GUID of StdLink + + // offset: 24, size: 4; unknown value + + // offset: 28, size: 4; option flags + + // bit: 0; mask: 0x00000001; 0 = no link or extant, 1 = file link or URL + $isFileLinkOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 0; + + // bit: 1; mask: 0x00000002; 0 = relative path, 1 = absolute path or URL + $isAbsPathOrUrl = (0x00000001 & $this->_GetInt2d($recordData, 28)) >> 1; + + // bit: 2 (and 4); mask: 0x00000014; 0 = no description + $hasDesc = (0x00000014 & $this->_GetInt2d($recordData, 28)) >> 2; + + // bit: 3; mask: 0x00000008; 0 = no text, 1 = has text + $hasText = (0x00000008 & $this->_GetInt2d($recordData, 28)) >> 3; + + // bit: 7; mask: 0x00000080; 0 = no target frame, 1 = has target frame + $hasFrame = (0x00000080 & $this->_GetInt2d($recordData, 28)) >> 7; + + // bit: 8; mask: 0x00000100; 0 = file link or URL, 1 = UNC path (inc. server name) + $isUNC = (0x00000100 & $this->_GetInt2d($recordData, 28)) >> 8; + + // offset within record data + $offset = 32; + + if ($hasDesc) { + // offset: 32; size: var; character count of description text + $dl = $this->_GetInt4d($recordData, 32); + // offset: 36; size: var; character array of description text, no Unicode string header, always 16-bit characters, zero terminated + $desc = $this->_encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)), false); + $offset += 4 + 2 * $dl; + } + if ($hasFrame) { + $fl = $this->_GetInt4d($recordData, $offset); + $offset += 4 + 2 * $fl; + } + + // detect type of hyperlink (there are 4 types) + $hyperlinkType = null; + + if ($isUNC) { + $hyperlinkType = 'UNC'; + } else if (!$isFileLinkOrUrl) { + $hyperlinkType = 'workbook'; + } else if (ord($recordData{$offset}) == 0x03) { + $hyperlinkType = 'local'; + } else if (ord($recordData{$offset}) == 0xE0) { + $hyperlinkType = 'URL'; + } + + switch ($hyperlinkType) { + case 'URL': + // section 5.58.2: Hyperlink containing a URL + // e.g. http://example.org/index.php + + // offset: var; size: 16; GUID of URL Moniker + $offset += 16; + // offset: var; size: 4; size (in bytes) of character array of the URL including trailing zero word + $us = $this->_GetInt4d($recordData, $offset); + $offset += 4; + // offset: var; size: $us; character array of the URL, no Unicode string header, always 16-bit characters, zero-terminated + $url = $this->_encodeUTF16(substr($recordData, $offset, $us - 2), false); + $url .= $hasText ? '#' : ''; + $offset += $us; + break; + + case 'local': + // section 5.58.3: Hyperlink to local file + // examples: + // mydoc.txt + // ../../somedoc.xls#Sheet!A1 + + // offset: var; size: 16; GUI of File Moniker + $offset += 16; + + // offset: var; size: 2; directory up-level count. + $upLevelCount = $this->_GetInt2d($recordData, $offset); + $offset += 2; + + // offset: var; size: 4; character count of the shortened file path and name, including trailing zero word + $sl = $this->_GetInt4d($recordData, $offset); + $offset += 4; + + // offset: var; size: sl; character array of the shortened file path and name in 8.3-DOS-format (compressed Unicode string) + $shortenedFilePath = substr($recordData, $offset, $sl); + $shortenedFilePath = $this->_encodeUTF16($shortenedFilePath, true); + $shortenedFilePath = substr($shortenedFilePath, 0, -1); // remove trailing zero + + $offset += $sl; + + // offset: var; size: 24; unknown sequence + $offset += 24; + + // extended file path + // offset: var; size: 4; size of the following file link field including string lenth mark + $sz = $this->_GetInt4d($recordData, $offset); + $offset += 4; + + // only present if $sz > 0 + if ($sz > 0) { + // offset: var; size: 4; size of the character array of the extended file path and name + $xl = $this->_GetInt4d($recordData, $offset); + $offset += 4; + + // offset: var; size 2; unknown + $offset += 2; + + // offset: var; size $xl; character array of the extended file path and name. + $extendedFilePath = substr($recordData, $offset, $xl); + $extendedFilePath = $this->_encodeUTF16($extendedFilePath, false); + $offset += $xl; + } + + // construct the path + $url = str_repeat('..\\', $upLevelCount); + $url .= ($sz > 0) ? + $extendedFilePath : $shortenedFilePath; // use extended path if available + $url .= $hasText ? '#' : ''; + + break; + + + case 'UNC': + // section 5.58.4: Hyperlink to a File with UNC (Universal Naming Convention) Path + // todo: implement + return; + + case 'workbook': + // section 5.58.5: Hyperlink to the Current Workbook + // e.g. Sheet2!B1:C2, stored in text mark field + $url = 'sheet://'; + break; + + default: + return; + + } + + if ($hasText) { + // offset: var; size: 4; character count of text mark including trailing zero word + $tl = $this->_GetInt4d($recordData, $offset); + $offset += 4; + // offset: var; size: var; character array of the text mark without the # sign, no Unicode header, always 16-bit characters, zero-terminated + $text = $this->_encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)), false); + $url .= $text; + } + + // apply the hyperlink to all the relevant cells + foreach (PHPExcel_Cell::extractAllCellReferencesInRange($cellRange) as $coordinate) { + $this->_phpSheet->getCell($coordinate)->getHyperLink()->setUrl($url); + } + } + } + + /** + * Read DATAVALIDATIONS record + */ + private function _readDataValidations() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer forward to next record + $this->_pos += 4 + $length; + } + + /** + * Read DATAVALIDATION record + */ + private function _readDataValidation() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer forward to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly) { + return; + } + + // offset: 0; size: 4; Options + $options = $this->_GetInt4d($recordData, 0); + + // bit: 0-3; mask: 0x0000000F; type + $type = (0x0000000F & $options) >> 0; + switch ($type) { + case 0x00: $type = PHPExcel_Cell_DataValidation::TYPE_NONE; break; + case 0x01: $type = PHPExcel_Cell_DataValidation::TYPE_WHOLE; break; + case 0x02: $type = PHPExcel_Cell_DataValidation::TYPE_DECIMAL; break; + case 0x03: $type = PHPExcel_Cell_DataValidation::TYPE_LIST; break; + case 0x04: $type = PHPExcel_Cell_DataValidation::TYPE_DATE; break; + case 0x05: $type = PHPExcel_Cell_DataValidation::TYPE_TIME; break; + case 0x06: $type = PHPExcel_Cell_DataValidation::TYPE_TEXTLENGTH; break; + case 0x07: $type = PHPExcel_Cell_DataValidation::TYPE_CUSTOM; break; + } + + // bit: 4-6; mask: 0x00000070; error type + $errorStyle = (0x00000070 & $options) >> 4; + switch ($errorStyle) { + case 0x00: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_STOP; break; + case 0x01: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_WARNING; break; + case 0x02: $errorStyle = PHPExcel_Cell_DataValidation::STYLE_INFORMATION; break; + } + + // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list) + // I have only seen cases where this is 1 + $explicitFormula = (0x00000080 & $options) >> 7; + + // bit: 8; mask: 0x00000100; 1= empty cells allowed + $allowBlank = (0x00000100 & $options) >> 8; + + // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity + $suppressDropDown = (0x00000200 & $options) >> 9; + + // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected + $showInputMessage = (0x00040000 & $options) >> 18; + + // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered + $showErrorMessage = (0x00080000 & $options) >> 19; + + // bit: 20-23; mask: 0x00F00000; condition operator + $operator = (0x00F00000 & $options) >> 20; + switch ($operator) { + case 0x00: $operator = PHPExcel_Cell_DataValidation::OPERATOR_BETWEEN ; break; + case 0x01: $operator = PHPExcel_Cell_DataValidation::OPERATOR_NOTBETWEEN ; break; + case 0x02: $operator = PHPExcel_Cell_DataValidation::OPERATOR_EQUAL ; break; + case 0x03: $operator = PHPExcel_Cell_DataValidation::OPERATOR_NOTEQUAL ; break; + case 0x04: $operator = PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHAN ; break; + case 0x05: $operator = PHPExcel_Cell_DataValidation::OPERATOR_LESSTHAN ; break; + case 0x06: $operator = PHPExcel_Cell_DataValidation::OPERATOR_GREATERTHANOREQUAL; break; + case 0x07: $operator = PHPExcel_Cell_DataValidation::OPERATOR_LESSTHANOREQUAL ; break; + } + + // offset: 4; size: var; title of the prompt box + $offset = 4; + $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); + $promptTitle = $string['value'] !== chr(0) ? + $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: var; title of the error box + $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); + $errorTitle = $string['value'] !== chr(0) ? + $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: var; text of the prompt box + $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); + $prompt = $string['value'] !== chr(0) ? + $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: var; text of the error box + $string = $this->_readUnicodeStringLong(substr($recordData, $offset)); + $error = $string['value'] !== chr(0) ? + $string['value'] : ''; + $offset += $string['size']; + + // offset: var; size: 2; size of the formula data for the first condition + $sz1 = $this->_GetInt2d($recordData, $offset); + $offset += 2; + + // offset: var; size: 2; not used + $offset += 2; + + // offset: var; size: $sz1; formula data for first condition (without size field) + $formula1 = substr($recordData, $offset, $sz1); + $formula1 = pack('v', $sz1) . $formula1; // prepend the length + try { + $formula1 = $this->_getFormulaFromStructure($formula1); + + // in list type validity, null characters are used as item separators + if ($type == PHPExcel_Cell_DataValidation::TYPE_LIST) { + $formula1 = str_replace(chr(0), ',', $formula1); + } + } catch (Exception $e) { + return; + } + $offset += $sz1; + + // offset: var; size: 2; size of the formula data for the first condition + $sz2 = $this->_GetInt2d($recordData, $offset); + $offset += 2; + + // offset: var; size: 2; not used + $offset += 2; + + // offset: var; size: $sz2; formula data for second condition (without size field) + $formula2 = substr($recordData, $offset, $sz2); + $formula2 = pack('v', $sz2) . $formula2; // prepend the length + try { + $formula2 = $this->_getFormulaFromStructure($formula2); + } catch (Exception $e) { + return; + } + $offset += $sz2; + + // offset: var; size: var; cell range address list with + $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList(substr($recordData, $offset)); + $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses']; + + foreach ($cellRangeAddresses as $cellRange) { + $stRange = $this->_phpSheet->shrinkRangeToFit($cellRange); + $stRange = PHPExcel_Cell::extractAllCellReferencesInRange($stRange); + foreach ($stRange as $coordinate) { + $objValidation = $this->_phpSheet->getCell($coordinate)->getDataValidation(); + $objValidation->setType($type); + $objValidation->setErrorStyle($errorStyle); + $objValidation->setAllowBlank((bool)$allowBlank); + $objValidation->setShowInputMessage((bool)$showInputMessage); + $objValidation->setShowErrorMessage((bool)$showErrorMessage); + $objValidation->setShowDropDown(!$suppressDropDown); + $objValidation->setOperator($operator); + $objValidation->setErrorTitle($errorTitle); + $objValidation->setError($error); + $objValidation->setPromptTitle($promptTitle); + $objValidation->setPrompt($prompt); + $objValidation->setFormula1($formula1); + $objValidation->setFormula2($formula2); + } + } + + } + + /** + * Read SHEETLAYOUT record. Stores sheet tab color information. + */ + private function _readSheetLayout() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // local pointer in record data + $offset = 0; + + if (!$this->_readDataOnly) { + // offset: 0; size: 2; repeated record identifier 0x0862 + + // offset: 2; size: 10; not used + + // offset: 12; size: 4; size of record data + // Excel 2003 uses size of 0x14 (documented), Excel 2007 uses size of 0x28 (not documented?) + $sz = $this->_GetInt4d($recordData, 12); + + switch ($sz) { + case 0x14: + // offset: 16; size: 2; color index for sheet tab + $colorIndex = $this->_GetInt2d($recordData, 16); + $color = $this->_readColor($colorIndex); + $this->_phpSheet->getTabColor()->setRGB($color['rgb']); + break; + + case 0x28: + // TODO: Investigate structure for .xls SHEETLAYOUT record as saved by MS Office Excel 2007 + return; + break; + } + } + } + + /** + * Read SHEETPROTECTION record (FEATHEADR) + */ + private function _readSheetProtection() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + if ($this->_readDataOnly) { + return; + } + + // offset: 0; size: 2; repeated record header + + // offset: 2; size: 2; FRT cell reference flag (=0 currently) + + // offset: 4; size: 8; Currently not used and set to 0 + + // offset: 12; size: 2; Shared feature type index (2=Enhanced Protetion, 4=SmartTag) + $isf = $this->_GetInt2d($recordData, 12); + if ($isf != 2) { + return; + } + + // offset: 14; size: 1; =1 since this is a feat header + + // offset: 15; size: 4; size of rgbHdrSData + + // rgbHdrSData, assume "Enhanced Protection" + // offset: 19; size: 2; option flags + $options = $this->_GetInt2d($recordData, 19); + + // bit: 0; mask 0x0001; 1 = user may edit objects, 0 = users must not edit objects + $bool = (0x0001 & $options) >> 0; + $this->_phpSheet->getProtection()->setObjects(!$bool); + + // bit: 1; mask 0x0002; edit scenarios + $bool = (0x0002 & $options) >> 1; + $this->_phpSheet->getProtection()->setScenarios(!$bool); + + // bit: 2; mask 0x0004; format cells + $bool = (0x0004 & $options) >> 2; + $this->_phpSheet->getProtection()->setFormatCells(!$bool); + + // bit: 3; mask 0x0008; format columns + $bool = (0x0008 & $options) >> 3; + $this->_phpSheet->getProtection()->setFormatColumns(!$bool); + + // bit: 4; mask 0x0010; format rows + $bool = (0x0010 & $options) >> 4; + $this->_phpSheet->getProtection()->setFormatRows(!$bool); + + // bit: 5; mask 0x0020; insert columns + $bool = (0x0020 & $options) >> 5; + $this->_phpSheet->getProtection()->setInsertColumns(!$bool); + + // bit: 6; mask 0x0040; insert rows + $bool = (0x0040 & $options) >> 6; + $this->_phpSheet->getProtection()->setInsertRows(!$bool); + + // bit: 7; mask 0x0080; insert hyperlinks + $bool = (0x0080 & $options) >> 7; + $this->_phpSheet->getProtection()->setInsertHyperlinks(!$bool); + + // bit: 8; mask 0x0100; delete columns + $bool = (0x0100 & $options) >> 8; + $this->_phpSheet->getProtection()->setDeleteColumns(!$bool); + + // bit: 9; mask 0x0200; delete rows + $bool = (0x0200 & $options) >> 9; + $this->_phpSheet->getProtection()->setDeleteRows(!$bool); + + // bit: 10; mask 0x0400; select locked cells + $bool = (0x0400 & $options) >> 10; + $this->_phpSheet->getProtection()->setSelectLockedCells(!$bool); + + // bit: 11; mask 0x0800; sort cell range + $bool = (0x0800 & $options) >> 11; + $this->_phpSheet->getProtection()->setSort(!$bool); + + // bit: 12; mask 0x1000; auto filter + $bool = (0x1000 & $options) >> 12; + $this->_phpSheet->getProtection()->setAutoFilter(!$bool); + + // bit: 13; mask 0x2000; pivot tables + $bool = (0x2000 & $options) >> 13; + $this->_phpSheet->getProtection()->setPivotTables(!$bool); + + // bit: 14; mask 0x4000; select unlocked cells + $bool = (0x4000 & $options) >> 14; + $this->_phpSheet->getProtection()->setSelectUnlockedCells(!$bool); + + // offset: 21; size: 2; not used + } + + /** + * Read RANGEPROTECTION record + * Reading of this record is based on Microsoft Office Excel 97-2000 Binary File Format Specification, + * where it is referred to as FEAT record + */ + private function _readRangeProtection() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // move stream pointer to next record + $this->_pos += 4 + $length; + + // local pointer in record data + $offset = 0; + + if (!$this->_readDataOnly) { + $offset += 12; + + // offset: 12; size: 2; shared feature type, 2 = enhanced protection, 4 = smart tag + $isf = $this->_GetInt2d($recordData, 12); + if ($isf != 2) { + // we only read FEAT records of type 2 + return; + } + $offset += 2; + + $offset += 5; + + // offset: 19; size: 2; count of ref ranges this feature is on + $cref = $this->_GetInt2d($recordData, 19); + $offset += 2; + + $offset += 6; + + // offset: 27; size: 8 * $cref; list of cell ranges (like in hyperlink record) + $cellRanges = array(); + for ($i = 0; $i < $cref; ++$i) { + try { + $cellRange = $this->_readBIFF8CellRangeAddressFixed(substr($recordData, 27 + 8 * $i, 8)); + } catch (Exception $e) { + return; + } + $cellRanges[] = $cellRange; + $offset += 8; + } + + // offset: var; size: var; variable length of feature specific data + $rgbFeat = substr($recordData, $offset); + $offset += 4; + + // offset: var; size: 4; the encrypted password (only 16-bit although field is 32-bit) + $wPassword = $this->_GetInt4d($recordData, $offset); + $offset += 4; + + // Apply range protection to sheet + if ($cellRanges) { + $this->_phpSheet->protectCells(implode(' ', $cellRanges), strtoupper(dechex($wPassword)), true); + } + } + } + + /** + * Read IMDATA record + */ + private function _readImData() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + + // get spliced record data + $splicedRecordData = $this->_getSplicedRecordData(); + $recordData = $splicedRecordData['recordData']; + + // UNDER CONSTRUCTION + + // offset: 0; size: 2; image format + $cf = $this->_GetInt2d($recordData, 0); + + // offset: 2; size: 2; environment from which the file was written + $env = $this->_GetInt2d($recordData, 2); + + // offset: 4; size: 4; length of the image data + $lcb = $this->_GetInt4d($recordData, 4); + + // offset: 8; size: var; image data + $iData = substr($recordData, 8); + + switch ($cf) { + case 0x09: // Windows bitmap format + // BITMAPCOREINFO + // 1. BITMAPCOREHEADER + // offset: 0; size: 4; bcSize, Specifies the number of bytes required by the structure + $bcSize = $this->_GetInt4d($iData, 0); + var_dump($bcSize); + + // offset: 4; size: 2; bcWidth, specifies the width of the bitmap, in pixels + $bcWidth = $this->_GetInt2d($iData, 4); + var_dump($bcWidth); + + // offset: 6; size: 2; bcHeight, specifies the height of the bitmap, in pixels. + $bcHeight = $this->_GetInt2d($iData, 6); + var_dump($bcHeight); + $ih = imagecreatetruecolor($bcWidth, $bcHeight); + + // offset: 8; size: 2; bcPlanes, specifies the number of planes for the target device. This value must be 1 + + // offset: 10; size: 2; bcBitCount specifies the number of bits-per-pixel. This value must be 1, 4, 8, or 24 + $bcBitCount = $this->_GetInt2d($iData, 10); + var_dump($bcBitCount); + + $rgbString = substr($iData, 12); + $rgbTriples = array(); + while (strlen($rgbString) > 0) { + $rgbTriples[] = unpack('Cb/Cg/Cr', $rgbString); + $rgbString = substr($rgbString, 3); + } + $x = 0; + $y = 0; + foreach ($rgbTriples as $i => $rgbTriple) { + $color = imagecolorallocate($ih, $rgbTriple['r'], $rgbTriple['g'], $rgbTriple['b']); + imagesetpixel($ih, $x, $bcHeight - 1 - $y, $color); + $x = ($x + 1) % $bcWidth; + $y = $y + floor(($x + 1) / $bcWidth); + } + //imagepng($ih, 'image.png'); + + $drawing = new PHPExcel_Worksheet_Drawing(); + $drawing->setPath($filename); + $drawing->setWorksheet($this->_phpSheet); + + break; + + case 0x02: // Windows metafile or Macintosh PICT format + case 0x0e: // native format + default; + break; + + } + + // _getSplicedRecordData() takes care of moving current position in data stream + } + + /** + * Read a free CONTINUE record. Free CONTINUE record may be a camouflaged MSODRAWING record + * When MSODRAWING data on a sheet exceeds 8224 bytes, CONTINUE records are used instead. Undocumented. + * In this case, we must treat the CONTINUE record as a MSODRAWING record + */ + private function _readContinue() + { + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $recordData = substr($this->_data, $this->_pos + 4, $length); + + // check if we are reading drawing data + // this is in case a free CONTINUE record occurs in other circumstances we are unaware of + if ($this->_drawingData == '') { + // move stream pointer to next record + $this->_pos += 4 + $length; + + return; + } + + // check if record data is at least 4 bytes long, otherwise there is no chance this is MSODRAWING data + if ($length < 4) { + // move stream pointer to next record + $this->_pos += 4 + $length; + + return; + } + + // dirty check to see if CONTINUE record could be a camouflaged MSODRAWING record + // look inside CONTINUE record to see if it looks like a part of an Escher stream + // we know that Escher stream may be split at least at + // 0xF003 MsofbtSpgrContainer + // 0xF004 MsofbtSpContainer + // 0xF00D MsofbtClientTextbox + $validSplitPoints = array(0xF003, 0xF004, 0xF00D); // add identifiers if we find more + + $splitPoint = $this->_GetInt2d($recordData, 2); + if (in_array($splitPoint, $validSplitPoints)) { + // get spliced record data (and move pointer to next record) + $splicedRecordData = $this->_getSplicedRecordData(); + $this->_drawingData .= $splicedRecordData['recordData']; + + return; + } + + // move stream pointer to next record + $this->_pos += 4 + $length; + + } + + + /** + * Reads a record from current position in data stream and continues reading data as long as CONTINUE + * records are found. Splices the record data pieces and returns the combined string as if record data + * is in one piece. + * Moves to next current position in data stream to start of next record different from a CONtINUE record + * + * @return array + */ + private function _getSplicedRecordData() + { + $data = ''; + $spliceOffsets = array(); + + $i = 0; + $spliceOffsets[0] = 0; + + do { + ++$i; + + // offset: 0; size: 2; identifier + $identifier = $this->_GetInt2d($this->_data, $this->_pos); + // offset: 2; size: 2; length + $length = $this->_GetInt2d($this->_data, $this->_pos + 2); + $data .= substr($this->_data, $this->_pos + 4, $length); + + $spliceOffsets[$i] = $spliceOffsets[$i - 1] + $length; + + $this->_pos += 4 + $length; + $nextIdentifier = $this->_GetInt2d($this->_data, $this->_pos); + } + while ($nextIdentifier == self::XLS_Type_CONTINUE); + + $splicedData = array( + 'recordData' => $data, + 'spliceOffsets' => $spliceOffsets, + ); + + return $splicedData; + + } + + /** + * Convert formula structure into human readable Excel formula like 'A3+A5*5' + * + * @param string $formulaStructure The complete binary data for the formula + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * @return string Human readable formula + */ + private function _getFormulaFromStructure($formulaStructure, $baseCell = 'A1') + { + // offset: 0; size: 2; size of the following formula data + $sz = $this->_GetInt2d($formulaStructure, 0); + + // offset: 2; size: sz + $formulaData = substr($formulaStructure, 2, $sz); + + // for debug: dump the formula data + //echo '<xmp>'; + //echo 'size: ' . $sz . "\n"; + //echo 'the entire formula data: '; + //Debug::dump($formulaData); + //echo "\n----\n"; + + // offset: 2 + sz; size: variable (optional) + if (strlen($formulaStructure) > 2 + $sz) { + $additionalData = substr($formulaStructure, 2 + $sz); + + // for debug: dump the additional data + //echo 'the entire additional data: '; + //Debug::dump($additionalData); + //echo "\n----\n"; + + } else { + $additionalData = ''; + } + + return $this->_getFormulaFromData($formulaData, $additionalData, $baseCell); + } + + /** + * Take formula data and additional data for formula and return human readable formula + * + * @param string $formulaData The binary data for the formula itself + * @param string $additionalData Additional binary data going with the formula + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * @return string Human readable formula + */ + private function _getFormulaFromData($formulaData, $additionalData = '', $baseCell = 'A1') + { + // start parsing the formula data + $tokens = array(); + + while (strlen($formulaData) > 0 and $token = $this->_getNextToken($formulaData, $baseCell)) { + $tokens[] = $token; + $formulaData = substr($formulaData, $token['size']); + + // for debug: dump the token + //var_dump($token); + } + + $formulaString = $this->_createFormulaFromTokens($tokens, $additionalData); + + return $formulaString; + } + + /** + * Take array of tokens together with additional data for formula and return human readable formula + * + * @param array $tokens + * @param array $additionalData Additional binary data going with the formula + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * @return string Human readable formula + */ + private function _createFormulaFromTokens($tokens, $additionalData) + { + // empty formula? + if (count($tokens) == 0) { + return ''; + } + + $formulaStrings = array(); + foreach ($tokens as $token) { + // initialize spaces + $space0 = isset($space0) ? $space0 : ''; // spaces before next token, not tParen + $space1 = isset($space1) ? $space1 : ''; // carriage returns before next token, not tParen + $space2 = isset($space2) ? $space2 : ''; // spaces before opening parenthesis + $space3 = isset($space3) ? $space3 : ''; // carriage returns before opening parenthesis + $space4 = isset($space4) ? $space4 : ''; // spaces before closing parenthesis + $space5 = isset($space5) ? $space5 : ''; // carriage returns before closing parenthesis + + switch ($token['name']) { + case 'tAdd': // addition + case 'tConcat': // addition + case 'tDiv': // division + case 'tEQ': // equaltiy + case 'tGE': // greater than or equal + case 'tGT': // greater than + case 'tIsect': // intersection + case 'tLE': // less than or equal + case 'tList': // less than or equal + case 'tLT': // less than + case 'tMul': // multiplication + case 'tNE': // multiplication + case 'tPower': // power + case 'tRange': // range + case 'tSub': // subtraction + $op2 = array_pop($formulaStrings); + $op1 = array_pop($formulaStrings); + $formulaStrings[] = "$op1$space1$space0{$token['data']}$op2"; + unset($space0, $space1); + break; + case 'tUplus': // unary plus + case 'tUminus': // unary minus + $op = array_pop($formulaStrings); + $formulaStrings[] = "$space1$space0{$token['data']}$op"; + unset($space0, $space1); + break; + case 'tPercent': // percent sign + $op = array_pop($formulaStrings); + $formulaStrings[] = "$op$space1$space0{$token['data']}"; + unset($space0, $space1); + break; + case 'tAttrVolatile': // indicates volatile function + case 'tAttrIf': + case 'tAttrSkip': + case 'tAttrChoose': + // token is only important for Excel formula evaluator + // do nothing + break; + case 'tAttrSpace': // space / carriage return + // space will be used when next token arrives, do not alter formulaString stack + switch ($token['data']['spacetype']) { + case 'type0': + $space0 = str_repeat(' ', $token['data']['spacecount']); + break; + case 'type1': + $space1 = str_repeat("\n", $token['data']['spacecount']); + break; + case 'type2': + $space2 = str_repeat(' ', $token['data']['spacecount']); + break; + case 'type3': + $space3 = str_repeat("\n", $token['data']['spacecount']); + break; + case 'type4': + $space4 = str_repeat(' ', $token['data']['spacecount']); + break; + case 'type5': + $space5 = str_repeat("\n", $token['data']['spacecount']); + break; + } + break; + case 'tAttrSum': // SUM function with one parameter + $op = array_pop($formulaStrings); + $formulaStrings[] = "{$space1}{$space0}SUM($op)"; + unset($space0, $space1); + break; + case 'tFunc': // function with fixed number of arguments + case 'tFuncV': // function with variable number of arguments + if ($token['data']['function'] != '') { + // normal function + $ops = array(); // array of operators + for ($i = 0; $i < $token['data']['args']; ++$i) { + $ops[] = array_pop($formulaStrings); + } + $ops = array_reverse($ops); + $formulaStrings[] = "$space1$space0{$token['data']['function']}(" . implode(',', $ops) . ")"; + unset($space0, $space1); + } else { + // add-in function + $ops = array(); // array of operators + for ($i = 0; $i < $token['data']['args'] - 1; ++$i) { + $ops[] = array_pop($formulaStrings); + } + $ops = array_reverse($ops); + $function = array_pop($formulaStrings); + $formulaStrings[] = "$space1$space0$function(" . implode(',', $ops) . ")"; + unset($space0, $space1); + } + break; + case 'tParen': // parenthesis + $expression = array_pop($formulaStrings); + $formulaStrings[] = "$space3$space2($expression$space5$space4)"; + unset($space2, $space3, $space4, $space5); + break; + case 'tArray': // array constant + $constantArray = $this->_readBIFF8ConstantArray($additionalData); + $formulaStrings[] = $space1 . $space0 . $constantArray['value']; + $additionalData = substr($additionalData, $constantArray['size']); // bite of chunk of additional data + unset($space0, $space1); + break; + case 'tMemArea': + // bite off chunk of additional data + $cellRangeAddressList = $this->_readBIFF8CellRangeAddressList($additionalData); + $additionalData = substr($additionalData, $cellRangeAddressList['size']); + $formulaStrings[] = "$space1$space0{$token['data']}"; + unset($space0, $space1); + break; + case 'tArea': // cell range address + case 'tBool': // boolean + case 'tErr': // error code + case 'tInt': // integer + case 'tMemErr': + case 'tMemFunc': + case 'tMissArg': + case 'tName': + case 'tNameX': + case 'tNum': // number + case 'tRef': // single cell reference + case 'tRef3d': // 3d cell reference + case 'tArea3d': // 3d cell range reference + case 'tRefN': + case 'tAreaN': + case 'tStr': // string + $formulaStrings[] = "$space1$space0{$token['data']}"; + unset($space0, $space1); + break; + } + } + $formulaString = $formulaStrings[0]; + + // for debug: dump the human readable formula + //echo '----' . "\n"; + //echo 'Formula: ' . $formulaString; + + return $formulaString; + } + + /** + * Fetch next token from binary formula data + * + * @param string Formula data + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * @return array + * @throws Exception + */ + private function _getNextToken($formulaData, $baseCell = 'A1') + { + // offset: 0; size: 1; token id + $id = ord($formulaData[0]); // token id + $name = false; // initialize token name + + switch ($id) { + case 0x03: $name = 'tAdd'; $size = 1; $data = '+'; break; + case 0x04: $name = 'tSub'; $size = 1; $data = '-'; break; + case 0x05: $name = 'tMul'; $size = 1; $data = '*'; break; + case 0x06: $name = 'tDiv'; $size = 1; $data = '/'; break; + case 0x07: $name = 'tPower'; $size = 1; $data = '^'; break; + case 0x08: $name = 'tConcat'; $size = 1; $data = '&'; break; + case 0x09: $name = 'tLT'; $size = 1; $data = '<'; break; + case 0x0A: $name = 'tLE'; $size = 1; $data = '<='; break; + case 0x0B: $name = 'tEQ'; $size = 1; $data = '='; break; + case 0x0C: $name = 'tGE'; $size = 1; $data = '>='; break; + case 0x0D: $name = 'tGT'; $size = 1; $data = '>'; break; + case 0x0E: $name = 'tNE'; $size = 1; $data = '<>'; break; + case 0x0F: $name = 'tIsect'; $size = 1; $data = ' '; break; + case 0x10: $name = 'tList'; $size = 1; $data = ','; break; + case 0x11: $name = 'tRange'; $size = 1; $data = ':'; break; + case 0x12: $name = 'tUplus'; $size = 1; $data = '+'; break; + case 0x13: $name = 'tUminus'; $size = 1; $data = '-'; break; + case 0x14: $name = 'tPercent'; $size = 1; $data = '%'; break; + case 0x15: // parenthesis + $name = 'tParen'; + $size = 1; + $data = null; + break; + case 0x16: // missing argument + $name = 'tMissArg'; + $size = 1; + $data = ''; + break; + case 0x17: // string + $name = 'tStr'; + // offset: 1; size: var; Unicode string, 8-bit string length + $string = $this->_readUnicodeStringShort(substr($formulaData, 1)); + $size = 1 + $string['size']; + $data = $this->_UTF8toExcelDoubleQuoted($string['value']); + break; + case 0x19: // Special attribute + // offset: 1; size: 1; attribute type flags: + switch (ord($formulaData[1])) { + case 0x01: + $name = 'tAttrVolatile'; + $size = 4; + $data = null; + break; + case 0x02: + $name = 'tAttrIf'; + $size = 4; + $data = null; + break; + case 0x04: + $name = 'tAttrChoose'; + // offset: 2; size: 2; number of choices in the CHOOSE function ($nc, number of parameters decreased by 1) + $nc = $this->_GetInt2d($formulaData, 2); + // offset: 4; size: 2 * $nc + // offset: 4 + 2 * $nc; size: 2 + $size = 2 * $nc + 6; + $data = null; + break; + case 0x08: + $name = 'tAttrSkip'; + $size = 4; + $data = null; + break; + case 0x10: + $name = 'tAttrSum'; + $size = 4; + $data = null; + break; + case 0x40: + case 0x41: + $name = 'tAttrSpace'; + $size = 4; + // offset: 2; size: 2; space type and position + switch (ord($formulaData[2])) { + case 0x00: + $spacetype = 'type0'; + break; + case 0x01: + $spacetype = 'type1'; + break; + case 0x02: + $spacetype = 'type2'; + break; + case 0x03: + $spacetype = 'type3'; + break; + case 0x04: + $spacetype = 'type4'; + break; + case 0x05: + $spacetype = 'type5'; + break; + default: + throw new Exception('Unrecognized space type in tAttrSpace token'); + break; + } + // offset: 3; size: 1; number of inserted spaces/carriage returns + $spacecount = ord($formulaData[3]); + + $data = array('spacetype' => $spacetype, 'spacecount' => $spacecount); + break; + default: + throw new Exception('Unrecognized attribute flag in tAttr token'); + break; + } + break; + case 0x1C: // error code + // offset: 1; size: 1; error code + $name = 'tErr'; + $size = 2; + $data = $this->_mapErrorCode(ord($formulaData[1])); + break; + case 0x1D: // boolean + // offset: 1; size: 1; 0 = false, 1 = true; + $name = 'tBool'; + $size = 2; + $data = ord($formulaData[1]) ? 'TRUE' : 'FALSE'; + break; + case 0x1E: // integer + // offset: 1; size: 2; unsigned 16-bit integer + $name = 'tInt'; + $size = 3; + $data = $this->_GetInt2d($formulaData, 1); + break; + case 0x1F: // number + // offset: 1; size: 8; + $name = 'tNum'; + $size = 9; + $data = $this->_extractNumber(substr($formulaData, 1)); + $data = str_replace(',', '.', (string)$data); // in case non-English locale + break; + case 0x40: // array constant + case 0x60: // array constant + // offset: 1; size: 7; not used + $name = 'tArray'; + $size = 8; + $data = null; + break; + case 0x41: // function with fixed number of arguments + $name = 'tFunc'; + $size = 3; + // offset: 1; size: 2; index to built-in sheet function + switch ($this->_GetInt2d($formulaData, 1)) { + case 2: $function = 'ISNA'; $args = 1; break; + case 3: $function = 'ISERROR'; $args = 1; break; + case 10: $function = 'NA'; $args = 0; break; + case 15: $function = 'SIN'; $args = 1; break; + case 16: $function = 'COS'; $args = 1; break; + case 17: $function = 'TAN'; $args = 1; break; + case 18: $function = 'ATAN'; $args = 1; break; + case 19: $function = 'PI'; $args = 0; break; + case 20: $function = 'SQRT'; $args = 1; break; + case 21: $function = 'EXP'; $args = 1; break; + case 22: $function = 'LN'; $args = 1; break; + case 23: $function = 'LOG10'; $args = 1; break; + case 24: $function = 'ABS'; $args = 1; break; + case 25: $function = 'INT'; $args = 1; break; + case 26: $function = 'SIGN'; $args = 1; break; + case 27: $function = 'ROUND'; $args = 2; break; + case 30: $function = 'REPT'; $args = 2; break; + case 31: $function = 'MID'; $args = 3; break; + case 32: $function = 'LEN'; $args = 1; break; + case 33: $function = 'VALUE'; $args = 1; break; + case 34: $function = 'TRUE'; $args = 0; break; + case 35: $function = 'FALSE'; $args = 0; break; + case 38: $function = 'NOT'; $args = 1; break; + case 39: $function = 'MOD'; $args = 2; break; + case 40: $function = 'DCOUNT'; $args = 3; break; + case 41: $function = 'DSUM'; $args = 3; break; + case 42: $function = 'DAVERAGE'; $args = 3; break; + case 43: $function = 'DMIN'; $args = 3; break; + case 44: $function = 'DMAX'; $args = 3; break; + case 45: $function = 'DSTDEV'; $args = 3; break; + case 48: $function = 'TEXT'; $args = 2; break; + case 61: $function = 'MIRR'; $args = 3; break; + case 63: $function = 'RAND'; $args = 0; break; + case 65: $function = 'DATE'; $args = 3; break; + case 66: $function = 'TIME'; $args = 3; break; + case 67: $function = 'DAY'; $args = 1; break; + case 68: $function = 'MONTH'; $args = 1; break; + case 69: $function = 'YEAR'; $args = 1; break; + case 71: $function = 'HOUR'; $args = 1; break; + case 72: $function = 'MINUTE'; $args = 1; break; + case 73: $function = 'SECOND'; $args = 1; break; + case 74: $function = 'NOW'; $args = 0; break; + case 75: $function = 'AREAS'; $args = 1; break; + case 76: $function = 'ROWS'; $args = 1; break; + case 77: $function = 'COLUMNS'; $args = 1; break; + case 83: $function = 'TRANSPOSE'; $args = 1; break; + case 86: $function = 'TYPE'; $args = 1; break; + case 97: $function = 'ATAN2'; $args = 2; break; + case 98: $function = 'ASIN'; $args = 1; break; + case 99: $function = 'ACOS'; $args = 1; break; + case 105: $function = 'ISREF'; $args = 1; break; + case 111: $function = 'CHAR'; $args = 1; break; + case 112: $function = 'LOWER'; $args = 1; break; + case 113: $function = 'UPPER'; $args = 1; break; + case 114: $function = 'PROPER'; $args = 1; break; + case 117: $function = 'EXACT'; $args = 2; break; + case 118: $function = 'TRIM'; $args = 1; break; + case 119: $function = 'REPLACE'; $args = 4; break; + case 121: $function = 'CODE'; $args = 1; break; + case 126: $function = 'ISERR'; $args = 1; break; + case 127: $function = 'ISTEXT'; $args = 1; break; + case 128: $function = 'ISNUMBER'; $args = 1; break; + case 129: $function = 'ISBLANK'; $args = 1; break; + case 130: $function = 'T'; $args = 1; break; + case 131: $function = 'N'; $args = 1; break; + case 140: $function = 'DATEVALUE'; $args = 1; break; + case 141: $function = 'TIMEVALUE'; $args = 1; break; + case 142: $function = 'SLN'; $args = 3; break; + case 143: $function = 'SYD'; $args = 4; break; + case 162: $function = 'CLEAN'; $args = 1; break; + case 163: $function = 'MDETERM'; $args = 1; break; + case 164: $function = 'MINVERSE'; $args = 1; break; + case 165: $function = 'MMULT'; $args = 2; break; + case 184: $function = 'FACT'; $args = 1; break; + case 189: $function = 'DPRODUCT'; $args = 3; break; + case 190: $function = 'ISNONTEXT'; $args = 1; break; + case 195: $function = 'DSTDEVP'; $args = 3; break; + case 196: $function = 'DVARP'; $args = 3; break; + case 198: $function = 'ISLOGICAL'; $args = 1; break; + case 199: $function = 'DCOUNTA'; $args = 3; break; + case 207: $function = 'REPLACEB'; $args = 4; break; + case 210: $function = 'MIDB'; $args = 3; break; + case 211: $function = 'LENB'; $args = 1; break; + case 212: $function = 'ROUNDUP'; $args = 2; break; + case 213: $function = 'ROUNDDOWN'; $args = 2; break; + case 214: $function = 'ASC'; $args = 1; break; + case 215: $function = 'DBCS'; $args = 1; break; + case 221: $function = 'TODAY'; $args = 0; break; + case 229: $function = 'SINH'; $args = 1; break; + case 230: $function = 'COSH'; $args = 1; break; + case 231: $function = 'TANH'; $args = 1; break; + case 232: $function = 'ASINH'; $args = 1; break; + case 233: $function = 'ACOSH'; $args = 1; break; + case 234: $function = 'ATANH'; $args = 1; break; + case 235: $function = 'DGET'; $args = 3; break; + case 244: $function = 'INFO'; $args = 1; break; + case 252: $function = 'FREQUENCY'; $args = 2; break; + case 261: $function = 'ERROR.TYPE'; $args = 1; break; + case 271: $function = 'GAMMALN'; $args = 1; break; + case 273: $function = 'BINOMDIST'; $args = 4; break; + case 274: $function = 'CHIDIST'; $args = 2; break; + case 275: $function = 'CHIINV'; $args = 2; break; + case 276: $function = 'COMBIN'; $args = 2; break; + case 277: $function = 'CONFIDENCE'; $args = 3; break; + case 278: $function = 'CRITBINOM'; $args = 3; break; + case 279: $function = 'EVEN'; $args = 1; break; + case 280: $function = 'EXPONDIST'; $args = 3; break; + case 281: $function = 'FDIST'; $args = 3; break; + case 282: $function = 'FINV'; $args = 3; break; + case 283: $function = 'FISHER'; $args = 1; break; + case 284: $function = 'FISHERINV'; $args = 1; break; + case 285: $function = 'FLOOR'; $args = 2; break; + case 286: $function = 'GAMMADIST'; $args = 4; break; + case 287: $function = 'GAMMAINV'; $args = 3; break; + case 288: $function = 'CEILING'; $args = 2; break; + case 289: $function = 'HYPGEOMDIST'; $args = 4; break; + case 290: $function = 'LOGNORMDIST'; $args = 3; break; + case 291: $function = 'LOGINV'; $args = 3; break; + case 292: $function = 'NEGBINOMDIST'; $args = 3; break; + case 293: $function = 'NORMDIST'; $args = 4; break; + case 294: $function = 'NORMSDIST'; $args = 1; break; + case 295: $function = 'NORMINV'; $args = 3; break; + case 296: $function = 'NORMSINV'; $args = 1; break; + case 297: $function = 'STANDARDIZE'; $args = 3; break; + case 298: $function = 'ODD'; $args = 1; break; + case 299: $function = 'PERMUT'; $args = 2; break; + case 300: $function = 'POISSON'; $args = 3; break; + case 301: $function = 'TDIST'; $args = 3; break; + case 302: $function = 'WEIBULL'; $args = 4; break; + case 303: $function = 'SUMXMY2'; $args = 2; break; + case 304: $function = 'SUMX2MY2'; $args = 2; break; + case 305: $function = 'SUMX2PY2'; $args = 2; break; + case 306: $function = 'CHITEST'; $args = 2; break; + case 307: $function = 'CORREL'; $args = 2; break; + case 308: $function = 'COVAR'; $args = 2; break; + case 309: $function = 'FORECAST'; $args = 3; break; + case 310: $function = 'FTEST'; $args = 2; break; + case 311: $function = 'INTERCEPT'; $args = 2; break; + case 312: $function = 'PEARSON'; $args = 2; break; + case 313: $function = 'RSQ'; $args = 2; break; + case 314: $function = 'STEYX'; $args = 2; break; + case 315: $function = 'SLOPE'; $args = 2; break; + case 316: $function = 'TTEST'; $args = 4; break; + case 325: $function = 'LARGE'; $args = 2; break; + case 326: $function = 'SMALL'; $args = 2; break; + case 327: $function = 'QUARTILE'; $args = 2; break; + case 328: $function = 'PERCENTILE'; $args = 2; break; + case 331: $function = 'TRIMMEAN'; $args = 2; break; + case 332: $function = 'TINV'; $args = 2; break; + case 337: $function = 'POWER'; $args = 2; break; + case 342: $function = 'RADIANS'; $args = 1; break; + case 343: $function = 'DEGREES'; $args = 1; break; + case 346: $function = 'COUNTIF'; $args = 2; break; + case 347: $function = 'COUNTBLANK'; $args = 1; break; + case 350: $function = 'ISPMT'; $args = 4; break; + case 351: $function = 'DATEDIF'; $args = 3; break; + case 352: $function = 'DATESTRING'; $args = 1; break; + case 353: $function = 'NUMBERSTRING'; $args = 2; break; + case 360: $function = 'PHONETIC'; $args = 1; break; + default: + throw new Exception('Unrecognized function in formula'); + break; + } + $data = array('function' => $function, 'args' => $args); + break; + case 0x22: // function with variable number of arguments + case 0x42: // function with variable number of arguments + case 0x62: // function with variable number of arguments + $name = 'tFuncV'; + $size = 4; + // offset: 1; size: 1; number of arguments + $args = ord($formulaData[1]); + // offset: 2: size: 2; index to built-in sheet function + $index = $this->_GetInt2d($formulaData, 2); + switch ($index) { + case 0: $function = 'COUNT'; break; + case 1: $function = 'IF'; break; + case 4: $function = 'SUM'; break; + case 5: $function = 'AVERAGE'; break; + case 6: $function = 'MIN'; break; + case 7: $function = 'MAX'; break; + case 8: $function = 'ROW'; break; + case 9: $function = 'COLUMN'; break; + case 11: $function = 'NPV'; break; + case 12: $function = 'STDEV'; break; + case 13: $function = 'DOLLAR'; break; + case 14: $function = 'FIXED'; break; + case 28: $function = 'LOOKUP'; break; + case 29: $function = 'INDEX'; break; + case 36: $function = 'AND'; break; + case 37: $function = 'OR'; break; + case 46: $function = 'VAR'; break; + case 49: $function = 'LINEST'; break; + case 50: $function = 'TREND'; break; + case 51: $function = 'LOGEST'; break; + case 52: $function = 'GROWTH'; break; + case 56: $function = 'PV'; break; + case 57: $function = 'FV'; break; + case 58: $function = 'NPER'; break; + case 59: $function = 'PMT'; break; + case 60: $function = 'RATE'; break; + case 62: $function = 'IRR'; break; + case 64: $function = 'MATCH'; break; + case 70: $function = 'WEEKDAY'; break; + case 78: $function = 'OFFSET'; break; + case 82: $function = 'SEARCH'; break; + case 100: $function = 'CHOOSE'; break; + case 101: $function = 'HLOOKUP'; break; + case 102: $function = 'VLOOKUP'; break; + case 109: $function = 'LOG'; break; + case 115: $function = 'LEFT'; break; + case 116: $function = 'RIGHT'; break; + case 120: $function = 'SUBSTITUTE'; break; + case 124: $function = 'FIND'; break; + case 125: $function = 'CELL'; break; + case 144: $function = 'DDB'; break; + case 148: $function = 'INDIRECT'; break; + case 167: $function = 'IPMT'; break; + case 168: $function = 'PPMT'; break; + case 169: $function = 'COUNTA'; break; + case 183: $function = 'PRODUCT'; break; + case 193: $function = 'STDEVP'; break; + case 194: $function = 'VARP'; break; + case 197: $function = 'TRUNC'; break; + case 204: $function = 'USDOLLAR'; break; + case 205: $function = 'FINDB'; break; + case 206: $function = 'SEARCHB'; break; + case 208: $function = 'LEFTB'; break; + case 209: $function = 'RIGHTB'; break; + case 216: $function = 'RANK'; break; + case 219: $function = 'ADDRESS'; break; + case 220: $function = 'DAYS360'; break; + case 222: $function = 'VDB'; break; + case 227: $function = 'MEDIAN'; break; + case 228: $function = 'SUMPRODUCT'; break; + case 247: $function = 'DB'; break; + case 255: $function = ''; break; + case 269: $function = 'AVEDEV'; break; + case 270: $function = 'BETADIST'; break; + case 272: $function = 'BETAINV'; break; + case 317: $function = 'PROB'; break; + case 318: $function = 'DEVSQ'; break; + case 319: $function = 'GEOMEAN'; break; + case 320: $function = 'HARMEAN'; break; + case 321: $function = 'SUMSQ'; break; + case 322: $function = 'KURT'; break; + case 323: $function = 'SKEW'; break; + case 324: $function = 'ZTEST'; break; + case 329: $function = 'PERCENTRANK'; break; + case 330: $function = 'MODE'; break; + case 336: $function = 'CONCATENATE'; break; + case 344: $function = 'SUBTOTAL'; break; + case 345: $function = 'SUMIF'; break; + case 354: $function = 'ROMAN'; break; + case 358: $function = 'GETPIVOTDATA'; break; + case 359: $function = 'HYPERLINK'; break; + case 361: $function = 'AVERAGEA'; break; + case 362: $function = 'MAXA'; break; + case 363: $function = 'MINA'; break; + case 364: $function = 'STDEVPA'; break; + case 365: $function = 'VARPA'; break; + case 366: $function = 'STDEVA'; break; + case 367: $function = 'VARA'; break; + default: + throw new Exception('Unrecognized function in formula'); + break; + } + $data = array('function' => $function, 'args' => $args); + break; + case 0x23: // index to defined name + case 0x43: + $name = 'tName'; + $size = 5; + // offset: 1; size: 2; one-based index to definedname record + $definedNameIndex = $this->_GetInt2d($formulaData, 1) - 1; + // offset: 2; size: 2; not used + $data = $this->_definedname[$definedNameIndex]['name']; + break; + case 0x24: // single cell reference e.g. A5 + case 0x44: + case 0x64: + $name = 'tRef'; + $size = 5; + $data = $this->_readBIFF8CellAddress(substr($formulaData, 1, 4)); + break; + case 0x25: // cell range reference to cells in the same sheet + case 0x45: + case 0x65: + $name = 'tArea'; + $size = 9; + $data = $this->_readBIFF8CellRangeAddress(substr($formulaData, 1, 8)); + break; + case 0x26: + case 0x46: + $name = 'tMemArea'; + // offset: 1; size: 4; not used + // offset: 5; size: 2; size of the following subexpression + $subSize = $this->_GetInt2d($formulaData, 5); + $size = 7 + $subSize; + $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize)); + break; + case 0x47: + $name = 'tMemErr'; + // offset: 1; size: 4; not used + // offset: 5; size: 2; size of the following subexpression + $subSize = $this->_GetInt2d($formulaData, 5); + $size = 7 + $subSize; + $data = $this->_getFormulaFromData(substr($formulaData, 7, $subSize)); + break; + case 0x29: + case 0x49: + $name = 'tMemFunc'; + // offset: 1; size: 2; size of the following subexpression + $subSize = $this->_GetInt2d($formulaData, 1); + $size = 3 + $subSize; + $data = $this->_getFormulaFromData(substr($formulaData, 3, $subSize)); + break; + + case 0x2C: // Relative reference, used in shared formulas and some other places + case 0x4C: + case 0x6C: + $name = 'tRefN'; + $size = 5; + $data = $this->_readBIFF8CellAddressB(substr($formulaData, 1, 4), $baseCell); + break; + + case 0x2D: + case 0x4D: + case 0x6D: + $name = 'tAreaN'; + $size = 9; + $data = $this->_readBIFF8CellRangeAddressB(substr($formulaData, 1, 8), $baseCell); + break; + + case 0x39: + case 0x59: + case 0x79: + $name = 'tNameX'; + $size = 7; + // offset: 1; size: 2; index to REF entry in EXTERNSHEET record + // offset: 3; size: 2; one-based index to DEFINEDNAME or EXTERNNAME record + $index = $this->_GetInt2d($formulaData, 3); + // assume index is to EXTERNNAME record + $data = $this->_externalNames[$index - 1]['name']; + // offset: 5; size: 2; not used + break; + + case 0x3A: // 3d reference to cell + case 0x5A: + $name = 'tRef3d'; + $size = 7; + + try { + // offset: 1; size: 2; index to REF entry + $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1)); + // offset: 3; size: 4; cell address + $cellAddress = $this->_readBIFF8CellAddress(substr($formulaData, 3, 4)); + + $data = "$sheetRange!$cellAddress"; + + } catch (Exception $e) { + // deleted sheet reference + $data = '#REF!'; + } + + break; + case 0x3B: // 3d reference to cell range + case 0x5B: + $name = 'tArea3d'; + $size = 11; + + try { + // offset: 1; size: 2; index to REF entry + $sheetRange = $this->_readSheetRangeByRefIndex($this->_GetInt2d($formulaData, 1)); + // offset: 3; size: 8; cell address + $cellRangeAddress = $this->_readBIFF8CellRangeAddress(substr($formulaData, 3, 8)); + + $data = "$sheetRange!$cellRangeAddress"; + + } catch (Exception $e) { + // deleted sheet reference + $data = '#REF!'; + + } + + break; + // case 0x39: // don't know how to deal with + default: + throw new Exception('Unrecognized token ' . sprintf('%02X', $id) . ' in formula'); + break; + } + + return array( + 'id' => $id, + 'name' => $name, + 'size' => $size, + 'data' => $data, + ); + } + + /** + * Reads a cell address in BIFF8 e.g. 'A2' or '$A$2' + * section 3.3.4 + * + * @param string $cellAddressStructure + * @return string + */ + private function _readBIFF8CellAddress($cellAddressStructure) + { + // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767)) + $row = $this->_GetInt2d($cellAddressStructure, 0) + 1; + + // offset: 2; size: 2; index to column or column offset + relative flags + + // bit: 7-0; mask 0x00FF; column index + $column = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($cellAddressStructure, 2)); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) { + $column = '$' . $column; + } + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) { + $row = '$' . $row; + } + + return $column . $row; + } + + /** + * Reads a cell address in BIFF8 for shared formulas. Uses positive and negative values for row and column + * to indicate offsets from a base cell + * section 3.3.4 + * + * @param string $cellAddressStructure + * @param string $baseCell Base cell, only needed when formula contains tRefN tokens, e.g. with shared formulas + * @return string + */ + private function _readBIFF8CellAddressB($cellAddressStructure, $baseCell = 'A1') + { + list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell); + $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1; + + // offset: 0; size: 2; index to row (0... 65535) (or offset (-32768... 32767)) + $rowIndex = $this->_GetInt2d($cellAddressStructure, 0); + $row = $this->_GetInt2d($cellAddressStructure, 0) + 1; + + // offset: 2; size: 2; index to column or column offset + relative flags + + // bit: 7-0; mask 0x00FF; column index + $colIndex = 0x00FF & $this->_GetInt2d($cellAddressStructure, 2); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($cellAddressStructure, 2))) { + $column = PHPExcel_Cell::stringFromColumnIndex($colIndex); + $column = '$' . $column; + } else { + $colIndex = ($colIndex <= 127) ? $colIndex : $colIndex - 256; + $column = PHPExcel_Cell::stringFromColumnIndex($baseCol + $colIndex); + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($cellAddressStructure, 2))) { + $row = '$' . $row; + } else { + $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536; + $row = $baseRow + $rowIndex; + } + + return $column . $row; + } + + /** + * Reads a cell range address in BIFF5 e.g. 'A2:B6' or 'A1' + * always fixed range + * section 2.5.14 + * + * @param string $subData + * @return string + * @throws Exception + */ + private function _readBIFF5CellRangeAddressFixed($subData) + { + // offset: 0; size: 2; index to first row + $fr = $this->_GetInt2d($subData, 0) + 1; + + // offset: 2; size: 2; index to last row + $lr = $this->_GetInt2d($subData, 2) + 1; + + // offset: 4; size: 1; index to first column + $fc = ord($subData{4}); + + // offset: 5; size: 1; index to last column + $lc = ord($subData{5}); + + // check values + if ($fr > $lr || $fc > $lc) { + throw new Exception('Not a cell range address'); + } + + // column index to letter + $fc = PHPExcel_Cell::stringFromColumnIndex($fc); + $lc = PHPExcel_Cell::stringFromColumnIndex($lc); + + if ($fr == $lr and $fc == $lc) { + return "$fc$fr"; + } + return "$fc$fr:$lc$lr"; + } + + /** + * Reads a cell range address in BIFF8 e.g. 'A2:B6' or 'A1' + * always fixed range + * section 2.5.14 + * + * @param string $subData + * @return string + * @throws Exception + */ + private function _readBIFF8CellRangeAddressFixed($subData) + { + // offset: 0; size: 2; index to first row + $fr = $this->_GetInt2d($subData, 0) + 1; + + // offset: 2; size: 2; index to last row + $lr = $this->_GetInt2d($subData, 2) + 1; + + // offset: 4; size: 2; index to first column + $fc = $this->_GetInt2d($subData, 4); + + // offset: 6; size: 2; index to last column + $lc = $this->_GetInt2d($subData, 6); + + // check values + if ($fr > $lr || $fc > $lc) { + throw new Exception('Not a cell range address'); + } + + // column index to letter + $fc = PHPExcel_Cell::stringFromColumnIndex($fc); + $lc = PHPExcel_Cell::stringFromColumnIndex($lc); + + if ($fr == $lr and $fc == $lc) { + return "$fc$fr"; + } + return "$fc$fr:$lc$lr"; + } + + /** + * Reads a cell range address in BIFF8 e.g. 'A2:B6' or '$A$2:$B$6' + * there are flags indicating whether column/row index is relative + * section 3.3.4 + * + * @param string $subData + * @return string + */ + private function _readBIFF8CellRangeAddress($subData) + { + // todo: if cell range is just a single cell, should this funciton + // not just return e.g. 'A1' and not 'A1:A1' ? + + // offset: 0; size: 2; index to first row (0... 65535) (or offset (-32768... 32767)) + $fr = $this->_GetInt2d($subData, 0) + 1; + + // offset: 2; size: 2; index to last row (0... 65535) (or offset (-32768... 32767)) + $lr = $this->_GetInt2d($subData, 2) + 1; + + // offset: 4; size: 2; index to first column or column offset + relative flags + + // bit: 7-0; mask 0x00FF; column index + $fc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 4)); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($subData, 4))) { + $fc = '$' . $fc; + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($subData, 4))) { + $fr = '$' . $fr; + } + + // offset: 6; size: 2; index to last column or column offset + relative flags + + // bit: 7-0; mask 0x00FF; column index + $lc = PHPExcel_Cell::stringFromColumnIndex(0x00FF & $this->_GetInt2d($subData, 6)); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($subData, 6))) { + $lc = '$' . $lc; + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($subData, 6))) { + $lr = '$' . $lr; + } + + return "$fc$fr:$lc$lr"; + } + + /** + * Reads a cell range address in BIFF8 for shared formulas. Uses positive and negative values for row and column + * to indicate offsets from a base cell + * section 3.3.4 + * + * @param string $subData + * @param string $baseCell Base cell + * @return string Cell range address + */ + private function _readBIFF8CellRangeAddressB($subData, $baseCell = 'A1') + { + list($baseCol, $baseRow) = PHPExcel_Cell::coordinateFromString($baseCell); + $baseCol = PHPExcel_Cell::columnIndexFromString($baseCol) - 1; + + // TODO: if cell range is just a single cell, should this funciton + // not just return e.g. 'A1' and not 'A1:A1' ? + + // offset: 0; size: 2; first row + $frIndex = $this->_GetInt2d($subData, 0); // adjust below + + // offset: 2; size: 2; relative index to first row (0... 65535) should be treated as offset (-32768... 32767) + $lrIndex = $this->_GetInt2d($subData, 2); // adjust below + + // offset: 4; size: 2; first column with relative/absolute flags + + // bit: 7-0; mask 0x00FF; column index + $fcIndex = 0x00FF & $this->_GetInt2d($subData, 4); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($subData, 4))) { + // absolute column index + $fc = PHPExcel_Cell::stringFromColumnIndex($fcIndex); + $fc = '$' . $fc; + } else { + // column offset + $fcIndex = ($fcIndex <= 127) ? $fcIndex : $fcIndex - 256; + $fc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $fcIndex); + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($subData, 4))) { + // absolute row index + $fr = $frIndex + 1; + $fr = '$' . $fr; + } else { + // row offset + $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536; + $fr = $baseRow + $frIndex; + } + + // offset: 6; size: 2; last column with relative/absolute flags + + // bit: 7-0; mask 0x00FF; column index + $lcIndex = 0x00FF & $this->_GetInt2d($subData, 6); + $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256; + $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex); + + // bit: 14; mask 0x4000; (1 = relative column index, 0 = absolute column index) + if (!(0x4000 & $this->_GetInt2d($subData, 6))) { + // absolute column index + $lc = PHPExcel_Cell::stringFromColumnIndex($lcIndex); + $lc = '$' . $lc; + } else { + // column offset + $lcIndex = ($lcIndex <= 127) ? $lcIndex : $lcIndex - 256; + $lc = PHPExcel_Cell::stringFromColumnIndex($baseCol + $lcIndex); + } + + // bit: 15; mask 0x8000; (1 = relative row index, 0 = absolute row index) + if (!(0x8000 & $this->_GetInt2d($subData, 6))) { + // absolute row index + $lr = $lrIndex + 1; + $lr = '$' . $lr; + } else { + // row offset + $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536; + $lr = $baseRow + $lrIndex; + } + + return "$fc$fr:$lc$lr"; + } + + /** + * Read BIFF8 cell range address list + * section 2.5.15 + * + * @param string $subData + * @return array + */ + private function _readBIFF8CellRangeAddressList($subData) + { + $cellRangeAddresses = array(); + + // offset: 0; size: 2; number of the following cell range addresses + $nm = $this->_GetInt2d($subData, 0); + + $offset = 2; + // offset: 2; size: 8 * $nm; list of $nm (fixed) cell range addresses + for ($i = 0; $i < $nm; ++$i) { + $cellRangeAddresses[] = $this->_readBIFF8CellRangeAddressFixed(substr($subData, $offset, 8)); + $offset += 8; + } + + return array( + 'size' => 2 + 8 * $nm, + 'cellRangeAddresses' => $cellRangeAddresses, + ); + } + + /** + * Read BIFF5 cell range address list + * section 2.5.15 + * + * @param string $subData + * @return array + */ + private function _readBIFF5CellRangeAddressList($subData) + { + $cellRangeAddresses = array(); + + // offset: 0; size: 2; number of the following cell range addresses + $nm = $this->_GetInt2d($subData, 0); + + $offset = 2; + // offset: 2; size: 6 * $nm; list of $nm (fixed) cell range addresses + for ($i = 0; $i < $nm; ++$i) { + $cellRangeAddresses[] = $this->_readBIFF5CellRangeAddressFixed(substr($subData, $offset, 6)); + $offset += 6; + } + + return array( + 'size' => 2 + 6 * $nm, + 'cellRangeAddresses' => $cellRangeAddresses, + ); + } + + /** + * Get a sheet range like Sheet1:Sheet3 from REF index + * Note: If there is only one sheet in the range, one gets e.g Sheet1 + * It can also happen that the REF structure uses the -1 (FFFF) code to indicate deleted sheets, + * in which case an exception is thrown + * + * @param int $index + * @return string|false + * @throws Exception + */ + private function _readSheetRangeByRefIndex($index) + { + if (isset($this->_ref[$index])) { + + $type = $this->_externalBooks[$this->_ref[$index]['externalBookIndex']]['type']; + + switch ($type) { + case 'internal': + // check if we have a deleted 3d reference + if ($this->_ref[$index]['firstSheetIndex'] == 0xFFFF or $this->_ref[$index]['lastSheetIndex'] == 0xFFFF) { + throw new Exception('Deleted sheet reference'); + } + + // we have normal sheet range (collapsed or uncollapsed) + $firstSheetName = $this->_sheets[$this->_ref[$index]['firstSheetIndex']]['name']; + $lastSheetName = $this->_sheets[$this->_ref[$index]['lastSheetIndex']]['name']; + + if ($firstSheetName == $lastSheetName) { + // collapsed sheet range + $sheetRange = $firstSheetName; + } else { + $sheetRange = "$firstSheetName:$lastSheetName"; + } + + // escape the single-quotes + $sheetRange = str_replace("'", "''", $sheetRange); + + // if there are special characters, we need to enclose the range in single-quotes + // todo: check if we have identified the whole set of special characters + // it seems that the following characters are not accepted for sheet names + // and we may assume that they are not present: []*/:\? + if (preg_match("/[ !\"@#£$%&{()}<>=+'|^,;-]/", $sheetRange)) { + $sheetRange = "'$sheetRange'"; + } + + return $sheetRange; + break; + + default: + // TODO: external sheet support + throw new Exception('Excel5 reader only supports internal sheets in fomulas'); + break; + } + } + return false; + } + + /** + * read BIFF8 constant value array from array data + * returns e.g. array('value' => '{1,2;3,4}', 'size' => 40} + * section 2.5.8 + * + * @param string $arrayData + * @return array + */ + private function _readBIFF8ConstantArray($arrayData) + { + // offset: 0; size: 1; number of columns decreased by 1 + $nc = ord($arrayData[0]); + + // offset: 1; size: 2; number of rows decreased by 1 + $nr = $this->_GetInt2d($arrayData, 1); + $size = 3; // initialize + $arrayData = substr($arrayData, 3); + + // offset: 3; size: var; list of ($nc + 1) * ($nr + 1) constant values + $matrixChunks = array(); + for ($r = 1; $r <= $nr + 1; ++$r) { + $items = array(); + for ($c = 1; $c <= $nc + 1; ++$c) { + $constant = $this->_readBIFF8Constant($arrayData); + $items[] = $constant['value']; + $arrayData = substr($arrayData, $constant['size']); + $size += $constant['size']; + } + $matrixChunks[] = implode(',', $items); // looks like e.g. '1,"hello"' + } + $matrix = '{' . implode(';', $matrixChunks) . '}'; + + return array( + 'value' => $matrix, + 'size' => $size, + ); + } + + /** + * read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value', 'Error Value' + * section 2.5.7 + * returns e.g. array('value' => '5', 'size' => 9) + * + * @param string $valueData + * @return array + */ + private function _readBIFF8Constant($valueData) + { + // offset: 0; size: 1; identifier for type of constant + $identifier = ord($valueData[0]); + + switch ($identifier) { + case 0x00: // empty constant (what is this?) + $value = ''; + $size = 9; + break; + case 0x01: // number + // offset: 1; size: 8; IEEE 754 floating-point value + $value = $this->_extractNumber(substr($valueData, 1, 8)); + $size = 9; + break; + case 0x02: // string value + // offset: 1; size: var; Unicode string, 16-bit string length + $string = $this->_readUnicodeStringLong(substr($valueData, 1)); + $value = '"' . $string['value'] . '"'; + $size = 1 + $string['size']; + break; + case 0x04: // boolean + // offset: 1; size: 1; 0 = FALSE, 1 = TRUE + if (ord($valueData[1])) { + $value = 'TRUE'; + } else { + $value = 'FALSE'; + } + $size = 9; + break; + case 0x10: // error code + // offset: 1; size: 1; error code + $value = $this->_mapErrorCode(ord($valueData[1])); + $size = 9; + break; + } + return array( + 'value' => $value, + 'size' => $size, + ); + } + + /** + * Extract RGB color + * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4 + * + * @param string $rgb Encoded RGB value (4 bytes) + * @return array + */ + private function _readRGB($rgb) + { + // offset: 0; size 1; Red component + $r = ord($rgb{0}); + + // offset: 1; size: 1; Green component + $g = ord($rgb{1}); + + // offset: 2; size: 1; Blue component + $b = ord($rgb{2}); + + // HEX notation, e.g. 'FF00FC' + $rgb = sprintf('%02X', $r) . sprintf('%02X', $g) . sprintf('%02X', $b); + + return array('rgb' => $rgb); + } + + /** + * Read byte string (8-bit string length) + * OpenOffice documentation: 2.5.2 + * + * @param string $subData + * @return array + */ + private function _readByteStringShort($subData) + { + // offset: 0; size: 1; length of the string (character count) + $ln = ord($subData[0]); + + // offset: 1: size: var; character array (8-bit characters) + $value = $this->_decodeCodepage(substr($subData, 1, $ln)); + + return array( + 'value' => $value, + 'size' => 1 + $ln, // size in bytes of data structure + ); + } + + /** + * Read byte string (16-bit string length) + * OpenOffice documentation: 2.5.2 + * + * @param string $subData + * @return array + */ + private function _readByteStringLong($subData) + { + // offset: 0; size: 2; length of the string (character count) + $ln = $this->_GetInt2d($subData, 0); + + // offset: 2: size: var; character array (8-bit characters) + $value = $this->_decodeCodepage(substr($subData, 2)); + + //return $string; + return array( + 'value' => $value, + 'size' => 2 + $ln, // size in bytes of data structure + ); + } + + /** + * Extracts an Excel Unicode short string (8-bit string length) + * OpenOffice documentation: 2.5.3 + * function will automatically find out where the Unicode string ends. + * + * @param string $subData + * @return array + */ + private function _readUnicodeStringShort($subData) + { + $value = ''; + + // offset: 0: size: 1; length of the string (character count) + $characterCount = ord($subData[0]); + + $string = $this->_readUnicodeString(substr($subData, 1), $characterCount); + + // add 1 for the string length + $string['size'] += 1; + + return $string; + } + + /** + * Extracts an Excel Unicode long string (16-bit string length) + * OpenOffice documentation: 2.5.3 + * this function is under construction, needs to support rich text, and Asian phonetic settings + * + * @param string $subData + * @return array + */ + private function _readUnicodeStringLong($subData) + { + $value = ''; + + // offset: 0: size: 2; length of the string (character count) + $characterCount = $this->_GetInt2d($subData, 0); + + $string = $this->_readUnicodeString(substr($subData, 2), $characterCount); + + // add 2 for the string length + $string['size'] += 2; + + return $string; + } + + /** + * Read Unicode string with no string length field, but with known character count + * this function is under construction, needs to support rich text, and Asian phonetic settings + * OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.3 + * + * @param string $subData + * @param int $characterCount + * @return array + */ + private function _readUnicodeString($subData, $characterCount) + { + $value = ''; + + // offset: 0: size: 1; option flags + + // bit: 0; mask: 0x01; character compression (0 = compressed 8-bit, 1 = uncompressed 16-bit) + $isCompressed = !((0x01 & ord($subData[0])) >> 0); + + // bit: 2; mask: 0x04; Asian phonetic settings + $hasAsian = (0x04) & ord($subData[0]) >> 2; + + // bit: 3; mask: 0x08; Rich-Text settings + $hasRichText = (0x08) & ord($subData[0]) >> 3; + + // offset: 1: size: var; character array + // this offset assumes richtext and Asian phonetic settings are off which is generally wrong + // needs to be fixed + $value = $this->_encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed); + + return array( + 'value' => $value, + 'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount, // the size in bytes including the option flags + ); + } + + /** + * Convert UTF-8 string to string surounded by double quotes. Used for explicit string tokens in formulas. + * Example: hello"world --> "hello""world" + * + * @param string $value UTF-8 encoded string + * @return string + */ + private function _UTF8toExcelDoubleQuoted($value) + { + return '"' . str_replace('"', '""', $value) . '"'; + } + + /** + * Reads first 8 bytes of a string and return IEEE 754 float + * + * @param string $data Binary string that is at least 8 bytes long + * @return float + */ + private function _extractNumber($data) + { + $rknumhigh = $this->_GetInt4d($data, 4); + $rknumlow = $this->_GetInt4d($data, 0); + $sign = ($rknumhigh & 0x80000000) >> 31; + $exp = ($rknumhigh & 0x7ff00000) >> 20; + $mantissa = (0x100000 | ($rknumhigh & 0x000fffff)); + $mantissalow1 = ($rknumlow & 0x80000000) >> 31; + $mantissalow2 = ($rknumlow & 0x7fffffff); + $value = $mantissa / pow( 2 , (20 - ($exp - 1023))); + + if ($mantissalow1 != 0) { + $value += 1 / pow (2 , (21 - ($exp - 1023))); + } + + $value += $mantissalow2 / pow (2 , (52 - ($exp - 1023))); + if ($sign) { + $value = -1 * $value; + } + + return $value; + } + + private function _GetIEEE754($rknum) + { + if (($rknum & 0x02) != 0) { + $value = $rknum >> 2; + } + else { + // changes by mmp, info on IEEE754 encoding from + // research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html + // The RK format calls for using only the most significant 30 bits + // of the 64 bit floating point value. The other 34 bits are assumed + // to be 0 so we use the upper 30 bits of $rknum as follows... + $sign = ($rknum & 0x80000000) >> 31; + $exp = ($rknum & 0x7ff00000) >> 20; + $mantissa = (0x100000 | ($rknum & 0x000ffffc)); + $value = $mantissa / pow( 2 , (20- ($exp - 1023))); + if ($sign) { + $value = -1 * $value; + } + //end of changes by mmp + } + if (($rknum & 0x01) != 0) { + $value /= 100; + } + return $value; + } + + /** + * Get UTF-8 string from (compressed or uncompressed) UTF-16 string + * + * @param string $string + * @param bool $compressed + * @return string + */ + private function _encodeUTF16($string, $compressed = '') + { + if ($compressed) { + $string = $this->_uncompressByteString($string); + } + + $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', 'UTF-16LE'); + + return $result; + } + + /** + * Convert UTF-16 string in compressed notation to uncompressed form. Only used for BIFF8. + * + * @param string $string + * @return string + */ + private function _uncompressByteString($string) + { + $uncompressedString = ''; + for ($i = 0; $i < strlen($string); ++$i) { + $uncompressedString .= $string[$i] . "\0"; + } + + return $uncompressedString; + } + + /** + * Convert string to UTF-8. Only used for BIFF5. + * + * @param string $string + * @return string + */ + private function _decodeCodepage($string) + { + $result = PHPExcel_Shared_String::ConvertEncoding($string, 'UTF-8', $this->_codepage); + return $result; + } + + /** + * Read 16-bit unsigned integer + * + * @param string $data + * @param int $pos + * @return int + */ + private function _GetInt2d($data, $pos) + { + return ord($data[$pos]) | (ord($data[$pos + 1]) << 8); + } + + /** + * Read 32-bit signed integer + * + * @param string $data + * @param int $pos + * @return int + */ + private function _GetInt4d($data, $pos) + { + //return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | + // (ord($data[$pos + 2]) << 16) | (ord($data[$pos + 3]) << 24); + + // FIX: represent numbers correctly on 64-bit system + // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334 + $_or_24 = ord($data[$pos + 3]); + if ($_or_24 >= 128) { + // negative number + $_ord_24 = -abs((256 - $_or_24) << 24); + } else { + $_ord_24 = ($_or_24 & 127) << 24; + } + return ord($data[$pos]) | (ord($data[$pos + 1]) << 8) | (ord($data[$pos + 2]) << 16) | $_ord_24; + } + + /** + * Read color + * + * @param int $color Indexed color + * @return array RGB color value, example: array('rgb' => 'FF0000') + */ + private function _readColor($color) + { + if ($color <= 0x07 || $color >= 0x40) { + // special built-in color + $color = $this->_mapBuiltInColor($color); + } else if (isset($this->_palette) && isset($this->_palette[$color - 8])) { + // palette color, color index 0x08 maps to pallete index 0 + $color = $this->_palette[$color - 8]; + } else { + // default color table + if ($this->_version == self::XLS_BIFF8) { + $color = $this->_mapColor($color); + } else { + // BIFF5 + $color = $this->_mapColorBIFF5($color); + } + } + + return $color; + } + + + /** + * Map border style + * OpenOffice documentation: 2.5.11 + * + * @param int $index + * @return string + */ + private function _mapBorderStyle($index) + { + switch ($index) { + case 0x00: return PHPExcel_Style_Border::BORDER_NONE; + case 0x01: return PHPExcel_Style_Border::BORDER_THIN; + case 0x02: return PHPExcel_Style_Border::BORDER_MEDIUM; + case 0x03: return PHPExcel_Style_Border::BORDER_DASHED; + case 0x04: return PHPExcel_Style_Border::BORDER_DOTTED; + case 0x05: return PHPExcel_Style_Border::BORDER_THICK; + case 0x06: return PHPExcel_Style_Border::BORDER_DOUBLE; + case 0x07: return PHPExcel_Style_Border::BORDER_HAIR; + case 0x08: return PHPExcel_Style_Border::BORDER_MEDIUMDASHED; + case 0x09: return PHPExcel_Style_Border::BORDER_DASHDOT; + case 0x0A: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOT; + case 0x0B: return PHPExcel_Style_Border::BORDER_DASHDOTDOT; + case 0x0C: return PHPExcel_Style_Border::BORDER_MEDIUMDASHDOTDOT; + case 0x0D: return PHPExcel_Style_Border::BORDER_SLANTDASHDOT; + default: return PHPExcel_Style_Border::BORDER_NONE; + } + } + + /** + * Get fill pattern from index + * OpenOffice documentation: 2.5.12 + * + * @param int $index + * @return string + */ + private function _mapFillPattern($index) + { + switch ($index) { + case 0x00: return PHPExcel_Style_Fill::FILL_NONE; + case 0x01: return PHPExcel_Style_Fill::FILL_SOLID; + case 0x02: return PHPExcel_Style_Fill::FILL_PATTERN_MEDIUMGRAY; + case 0x03: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRAY; + case 0x04: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRAY; + case 0x05: return PHPExcel_Style_Fill::FILL_PATTERN_DARKHORIZONTAL; + case 0x06: return PHPExcel_Style_Fill::FILL_PATTERN_DARKVERTICAL; + case 0x07: return PHPExcel_Style_Fill::FILL_PATTERN_DARKDOWN; + case 0x08: return PHPExcel_Style_Fill::FILL_PATTERN_DARKUP; + case 0x09: return PHPExcel_Style_Fill::FILL_PATTERN_DARKGRID; + case 0x0A: return PHPExcel_Style_Fill::FILL_PATTERN_DARKTRELLIS; + case 0x0B: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTHORIZONTAL; + case 0x0C: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTVERTICAL; + case 0x0D: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTDOWN; + case 0x0E: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTUP; + case 0x0F: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTGRID; + case 0x10: return PHPExcel_Style_Fill::FILL_PATTERN_LIGHTTRELLIS; + case 0x11: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY125; + case 0x12: return PHPExcel_Style_Fill::FILL_PATTERN_GRAY0625; + default: return PHPExcel_Style_Fill::FILL_NONE; + } + } + + /** + * Map error code, e.g. '#N/A' + * + * @param int $subData + * @return string + */ + private function _mapErrorCode($subData) + { + switch ($subData) { + case 0x00: return '#NULL!'; break; + case 0x07: return '#DIV/0!'; break; + case 0x0F: return '#VALUE!'; break; + case 0x17: return '#REF!'; break; + case 0x1D: return '#NAME?'; break; + case 0x24: return '#NUM!'; break; + case 0x2A: return '#N/A'; break; + default: return false; + } + } + + /** + * Map built-in color to RGB value + * + * @param int $color Indexed color + * @return array + */ + private function _mapBuiltInColor($color) + { + switch ($color) { + case 0x00: return array('rgb' => '000000'); + case 0x01: return array('rgb' => 'FFFFFF'); + case 0x02: return array('rgb' => 'FF0000'); + case 0x03: return array('rgb' => '00FF00'); + case 0x04: return array('rgb' => '0000FF'); + case 0x05: return array('rgb' => 'FFFF00'); + case 0x06: return array('rgb' => 'FF00FF'); + case 0x07: return array('rgb' => '00FFFF'); + case 0x40: return array('rgb' => '000000'); // system window text color + case 0x41: return array('rgb' => 'FFFFFF'); // system window background color + default: return array('rgb' => '000000'); + } + } + + /** + * Map color array from BIFF5 built-in color index + * + * @param int $subData + * @return array + */ + private function _mapColorBIFF5($subData) + { + switch ($subData) { + case 0x08: return array('rgb' => '000000'); + case 0x09: return array('rgb' => 'FFFFFF'); + case 0x0A: return array('rgb' => 'FF0000'); + case 0x0B: return array('rgb' => '00FF00'); + case 0x0C: return array('rgb' => '0000FF'); + case 0x0D: return array('rgb' => 'FFFF00'); + case 0x0E: return array('rgb' => 'FF00FF'); + case 0x0F: return array('rgb' => '00FFFF'); + case 0x10: return array('rgb' => '800000'); + case 0x11: return array('rgb' => '008000'); + case 0x12: return array('rgb' => '000080'); + case 0x13: return array('rgb' => '808000'); + case 0x14: return array('rgb' => '800080'); + case 0x15: return array('rgb' => '008080'); + case 0x16: return array('rgb' => 'C0C0C0'); + case 0x17: return array('rgb' => '808080'); + case 0x18: return array('rgb' => '8080FF'); + case 0x19: return array('rgb' => '802060'); + case 0x1A: return array('rgb' => 'FFFFC0'); + case 0x1B: return array('rgb' => 'A0E0F0'); + case 0x1C: return array('rgb' => '600080'); + case 0x1D: return array('rgb' => 'FF8080'); + case 0x1E: return array('rgb' => '0080C0'); + case 0x1F: return array('rgb' => 'C0C0FF'); + case 0x20: return array('rgb' => '000080'); + case 0x21: return array('rgb' => 'FF00FF'); + case 0x22: return array('rgb' => 'FFFF00'); + case 0x23: return array('rgb' => '00FFFF'); + case 0x24: return array('rgb' => '800080'); + case 0x25: return array('rgb' => '800000'); + case 0x26: return array('rgb' => '008080'); + case 0x27: return array('rgb' => '0000FF'); + case 0x28: return array('rgb' => '00CFFF'); + case 0x29: return array('rgb' => '69FFFF'); + case 0x2A: return array('rgb' => 'E0FFE0'); + case 0x2B: return array('rgb' => 'FFFF80'); + case 0x2C: return array('rgb' => 'A6CAF0'); + case 0x2D: return array('rgb' => 'DD9CB3'); + case 0x2E: return array('rgb' => 'B38FEE'); + case 0x2F: return array('rgb' => 'E3E3E3'); + case 0x30: return array('rgb' => '2A6FF9'); + case 0x31: return array('rgb' => '3FB8CD'); + case 0x32: return array('rgb' => '488436'); + case 0x33: return array('rgb' => '958C41'); + case 0x34: return array('rgb' => '8E5E42'); + case 0x35: return array('rgb' => 'A0627A'); + case 0x36: return array('rgb' => '624FAC'); + case 0x37: return array('rgb' => '969696'); + case 0x38: return array('rgb' => '1D2FBE'); + case 0x39: return array('rgb' => '286676'); + case 0x3A: return array('rgb' => '004500'); + case 0x3B: return array('rgb' => '453E01'); + case 0x3C: return array('rgb' => '6A2813'); + case 0x3D: return array('rgb' => '85396A'); + case 0x3E: return array('rgb' => '4A3285'); + case 0x3F: return array('rgb' => '424242'); + default: return array('rgb' => '000000'); + } + } + + /** + * Map color array from BIFF8 built-in color index + * + * @param int $subData + * @return array + */ + private function _mapColor($subData) + { + switch ($subData) { + case 0x08: return array('rgb' => '000000'); + case 0x09: return array('rgb' => 'FFFFFF'); + case 0x0A: return array('rgb' => 'FF0000'); + case 0x0B: return array('rgb' => '00FF00'); + case 0x0C: return array('rgb' => '0000FF'); + case 0x0D: return array('rgb' => 'FFFF00'); + case 0x0E: return array('rgb' => 'FF00FF'); + case 0x0F: return array('rgb' => '00FFFF'); + case 0x10: return array('rgb' => '800000'); + case 0x11: return array('rgb' => '008000'); + case 0x12: return array('rgb' => '000080'); + case 0x13: return array('rgb' => '808000'); + case 0x14: return array('rgb' => '800080'); + case 0x15: return array('rgb' => '008080'); + case 0x16: return array('rgb' => 'C0C0C0'); + case 0x17: return array('rgb' => '808080'); + case 0x18: return array('rgb' => '9999FF'); + case 0x19: return array('rgb' => '993366'); + case 0x1A: return array('rgb' => 'FFFFCC'); + case 0x1B: return array('rgb' => 'CCFFFF'); + case 0x1C: return array('rgb' => '660066'); + case 0x1D: return array('rgb' => 'FF8080'); + case 0x1E: return array('rgb' => '0066CC'); + case 0x1F: return array('rgb' => 'CCCCFF'); + case 0x20: return array('rgb' => '000080'); + case 0x21: return array('rgb' => 'FF00FF'); + case 0x22: return array('rgb' => 'FFFF00'); + case 0x23: return array('rgb' => '00FFFF'); + case 0x24: return array('rgb' => '800080'); + case 0x25: return array('rgb' => '800000'); + case 0x26: return array('rgb' => '008080'); + case 0x27: return array('rgb' => '0000FF'); + case 0x28: return array('rgb' => '00CCFF'); + case 0x29: return array('rgb' => 'CCFFFF'); + case 0x2A: return array('rgb' => 'CCFFCC'); + case 0x2B: return array('rgb' => 'FFFF99'); + case 0x2C: return array('rgb' => '99CCFF'); + case 0x2D: return array('rgb' => 'FF99CC'); + case 0x2E: return array('rgb' => 'CC99FF'); + case 0x2F: return array('rgb' => 'FFCC99'); + case 0x30: return array('rgb' => '3366FF'); + case 0x31: return array('rgb' => '33CCCC'); + case 0x32: return array('rgb' => '99CC00'); + case 0x33: return array('rgb' => 'FFCC00'); + case 0x34: return array('rgb' => 'FF9900'); + case 0x35: return array('rgb' => 'FF6600'); + case 0x36: return array('rgb' => '666699'); + case 0x37: return array('rgb' => '969696'); + case 0x38: return array('rgb' => '003366'); + case 0x39: return array('rgb' => '339966'); + case 0x3A: return array('rgb' => '003300'); + case 0x3B: return array('rgb' => '333300'); + case 0x3C: return array('rgb' => '993300'); + case 0x3D: return array('rgb' => '993366'); + case 0x3E: return array('rgb' => '333399'); + case 0x3F: return array('rgb' => '333333'); + default: return array('rgb' => '000000'); + } + } + +} diff --git a/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/Root.php b/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/Root.php index fe447cb7f..412f99282 100644 --- a/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/Root.php +++ b/libraries/PHPExcel/PHPExcel/Shared/OLE/PPS/Root.php @@ -69,11 +69,11 @@ class PHPExcel_Shared_OLE_PPS_Root extends PHPExcel_Shared_OLE_PPS if (is_resource($filename)) { $this->_FILEH_ = $filename; - } else if ($filename == '-' || $filename == '') { - $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root"); - $this->_FILEH_ = fopen($this->_tmp_filename,"w+b"); - if ($this->_FILEH_ == false) { - throw new Exception("Can't create temporary file."); + } else if ($filename == '-' || $filename == '') { + $this->_tmp_filename = tempnam($this->_tmp_dir, "OLE_PPS_Root"); + $this->_FILEH_ = fopen($this->_tmp_filename,"w+b"); + if ($this->_FILEH_ == false) { + throw new Exception("Can't create temporary file."); } } else { $this->_FILEH_ = fopen($filename, "wb"); diff --git a/libraries/config.default.php b/libraries/config.default.php index 0819692df..b2a393275 100644 --- a/libraries/config.default.php +++ b/libraries/config.default.php @@ -589,7 +589,7 @@ $cfg['LoginCookieRecall'] = true; * * @global integer $cfg['LoginCookieValidity'] */ -$cfg['LoginCookieValidity'] = 99440; +$cfg['LoginCookieValidity'] = 1440; /** * how long login cookie should be stored (in seconds) diff --git a/libraries/schema/diaSchema.class.php b/libraries/schema/diaSchema.class.php new file mode 100644 index 000000000..e0fb1ccd2 --- /dev/null +++ b/libraries/schema/diaSchema.class.php @@ -0,0 +1,7 @@ +<?php +include_once("relationSchema.abstract.class.php"); +class diaSchema extends exportRelationSchema +{ + +} +?> \ No newline at end of file diff --git a/libraries/schema/epsSchema.class.php b/libraries/schema/epsSchema.class.php new file mode 100644 index 000000000..3e0b942a0 --- /dev/null +++ b/libraries/schema/epsSchema.class.php @@ -0,0 +1,7 @@ +<?php +include_once("relationSchema.abstract.class.php"); +class epsSchema extends exportRelationSchema +{ + +} +?> \ No newline at end of file diff --git a/libraries/schema/pdfSchema.class.php b/libraries/schema/pdfSchema.class.php new file mode 100644 index 000000000..cd9cfe14b --- /dev/null +++ b/libraries/schema/pdfSchema.class.php @@ -0,0 +1,9 @@ +<?php +include_once("relationSchema.abstract.class.php"); +class pdfSchema extends exportRelationSchema +{ + +} +$pdf= new pdfSchema(); +$pdf->createPageHTML(); +?> \ No newline at end of file diff --git a/libraries/schema/relationSchema.abstract.class.php b/libraries/schema/relationSchema.abstract.class.php new file mode 100644 index 000000000..a30e9d2a1 --- /dev/null +++ b/libraries/schema/relationSchema.abstract.class.php @@ -0,0 +1,53 @@ +<?php +// Using Abstract Factory Pattern for exporting relational Schema in different Formats ! +abstract class exportRelationSchema +{ + private $pageTitle; // Title of the page + private $autoLayoutType; // Internal or Foreign Key Relations; + + public function setPageTitle($title) + { + $this->pageTitle=$title; + } + + public function createPage() + { + + } + public function createPageHTML() + { + ?> + <form method="post" action="pdf_pages.php" name="crpage"> + <fieldset> + <legend> + <?php echo __('Create a page') . "\n"; ?> + </legend> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="do" value="createpage" /> + <table> + <tr> + <td><label for="id_newpage"><?php echo __('Page name'); ?></label></td> + <td><input type="text" name="newpage" id="id_newpage" size="20" maxlength="50" /></td> + </tr> + <tr> + <td><?php echo __('Automatic layout based on'); ?></td> + <td> + <input type="checkbox" name="auto_layout_internal" id="id_auto_layout_internal" /><label for="id_auto_layout_internal"><?php echo __('Internal relations'); ?></label><br /> + <?php + if (PMA_StorageEngine::isValid('InnoDB') || PMA_StorageEngine::isValid('PBXT')) { + ?> + <input type="checkbox" name="auto_layout_foreign" id="id_auto_layout_foreign" /><label for="id_auto_layout_foreign">FOREIGN KEY</label><br /> + <?php + } + ?> + </td></tr> + </table> + </fieldset> + <fieldset class="tblFooters"> + <input type="submit" value="<?php echo __('Go'); ?>" /> + </fieldset> + </form> +<?php + } +} +?> \ No newline at end of file diff --git a/libraries/schema/svgSchema.class.php b/libraries/schema/svgSchema.class.php new file mode 100644 index 000000000..61ccc3bfc --- /dev/null +++ b/libraries/schema/svgSchema.class.php @@ -0,0 +1,7 @@ +<?php +include_once("relationSchema.abstract.class.php"); +class svgSchema extends exportRelationSchema +{ + +} +?> \ No newline at end of file diff --git a/libraries/schema/visioSchema.class.php b/libraries/schema/visioSchema.class.php new file mode 100644 index 000000000..167d5220b --- /dev/null +++ b/libraries/schema/visioSchema.class.php @@ -0,0 +1,7 @@ +<?php +include_once("relationSchema.abstract.class.php"); +class visioSchema extends exportRelationSchema +{ + +} +?> \ No newline at end of file From 9befa3965e3b9e82532a1bc43e199e269f73a925 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Thu, 24 Jun 2010 20:37:39 +0500 Subject: [PATCH 08/48] Schema Classes structure --- db_operations.php | 7 +++++++ js/functions.js | 29 +++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/db_operations.php b/db_operations.php index 40ec65272..6ca9461cb 100644 --- a/db_operations.php +++ b/db_operations.php @@ -646,6 +646,13 @@ if ($cfgRelation['pdfwork'] && $num_tables > 0) { ?> .' alt="" width="16" height="16" />'; } echo __('Edit PDF Pages') . '</a></fieldset>'; + // Export Relational Schema View + echo '<fieldset><a href="export_relation_schema.php?' . $url_query . '">'; + if ($cfg['PropertiesIconic']) { + echo '<img class="icon" src="' . $pmaThemeImage . 'b_edit.png"' + .' alt="" width="16" height="16" />'; + } + echo __('Export Relational Schema View') . '</a></fieldset>'; } // end if /** diff --git a/js/functions.js b/js/functions.js index a3080180b..7fcf00b7e 100644 --- a/js/functions.js +++ b/js/functions.js @@ -1434,8 +1434,14 @@ function refreshDragOption(e) { function refreshLayout() { var elm = $('#pdflayout') var orientation = $('#orientation_opt').val(); - var paper = $('#paper_opt').val(); - + if($('#paper_opt').length==1) + { + var paper = $('#paper_opt').val(); + } + else + { + var paper = 'A4'; + } if (orientation == 'P') { posa = 'x'; posb = 'y'; @@ -1725,5 +1731,24 @@ $(document).ready(function(){ insertQuery(evt.target.id); return false; }); + + $("#export_type").change(function(){ + + if($("#export_type").val()!='pdf') + { + $("#show_grid_opt").attr("disabled","disabled"); + $("#orientation_opt").attr("disabled","disabled"); + $("#with_doc").attr("disabled","disabled"); + $(this).css("background-color","yellow"); + + } + if($("#export_type").val()=='pdf') + { + $("#show_grid_opt").removeAttr("disabled"); + $("#orientation_opt").removeAttr("disabled"); + $("#with_doc").removeAttr("disabled","disabled"); + } + + }); }); From 7660295dca0850f3918abcda3559db5a86c73ab6 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Thu, 24 Jun 2010 20:39:46 +0500 Subject: [PATCH 09/48] Schema Classes structure, PDF and SVG --- export_relation_schema.php | 124 ++ libraries/schema/dia_relation_schema.php | 7 + libraries/schema/eps_relation_chema.php | 7 + libraries/schema/pdf_relation_schema.php | 1338 ++++++++++++++++++++ libraries/schema/relation_schema.php | 71 ++ libraries/schema/svg_relation_schema.php | 127 ++ libraries/schema/user_schema.php | 636 ++++++++++ libraries/schema/visio_relation_schema.php | 7 + process_relation_schema.php | 82 ++ 9 files changed, 2399 insertions(+) create mode 100644 export_relation_schema.php create mode 100644 libraries/schema/dia_relation_schema.php create mode 100644 libraries/schema/eps_relation_chema.php create mode 100644 libraries/schema/pdf_relation_schema.php create mode 100644 libraries/schema/relation_schema.php create mode 100644 libraries/schema/svg_relation_schema.php create mode 100644 libraries/schema/user_schema.php create mode 100644 libraries/schema/visio_relation_schema.php create mode 100644 process_relation_schema.php diff --git a/export_relation_schema.php b/export_relation_schema.php new file mode 100644 index 000000000..9c35609e2 --- /dev/null +++ b/export_relation_schema.php @@ -0,0 +1,124 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * + * @version $Id$ + * @package phpMyAdmin + */ + +/** + * Gets some core libraries + */ + +require_once './libraries/common.inc.php'; +require_once './libraries/db_common.inc.php'; +require './libraries/StorageEngine.class.php'; + +$active_page = 'db_operations.php'; +require_once './libraries/db_common.inc.php'; +$url_query .= '&amp;goto=export_relation_schema.php'; +require_once './libraries/db_info.inc.php'; + +/** + * Settings for relation stuff + */ +require_once './libraries/relation.lib.php'; +$cfgRelation = PMA_getRelationsParam(); + +// This is to avoid "Command out of sync" errors. Before switching this to +// a value of 0 (for MYSQLI_USE_RESULT), please check the logic +// to free results wherever needed. +$query_default_option = PMA_DBI_QUERY_STORE; + +/** + * Now in ./libraries/relation.lib.php we check for all tables + * that we need, but if we don't find them we are quiet about it + * so people can work without. + * This page is absolutely useless if you didn't set up your tables + * correctly, so it is a good place to see which tables we can and + * complain ;-) + */ +if (!$cfgRelation['relwork']) { + echo sprintf(__('<b>%s</b> table not found or not set in %s'), 'relation', 'config.inc.php') . '<br />' . "\n" + . PMA_showDocu('relation') . "\n"; + require_once './libraries/footer.inc.php'; +} + +if (!$cfgRelation['displaywork']) { + echo sprintf(__('<b>%s</b> table not found or not set in %s'), 'table_info', 'config.inc.php') . '<br />' . "\n" + . PMA_showDocu('table_info') . "\n"; + require_once './libraries/footer.inc.php'; +} + +if (!isset($cfgRelation['table_coords'])){ + echo sprintf(__('<b>%s</b> table not found or not set in %s'), 'table_coords', 'config.inc.php') . '<br />' . "\n" + . PMA_showDocu('table_coords') . "\n"; + require_once './libraries/footer.inc.php'; +} +if (!isset($cfgRelation['pdf_pages'])) { + echo sprintf(__('<b>%s</b> table not found or not set in %s'), 'pdf_page', 'config.inc.php') . '<br />' . "\n" + . PMA_showDocu('pdf_pages') . "\n"; + require_once './libraries/footer.inc.php'; +} + +if ($cfgRelation['pdfwork']) { + + /** + * User Object Created for displaying the HTML options + * so, user can play with it and perform export of relations schema + */ + + require_once './libraries/schema/user_schema.php'; + $user_schema = new PMA_USER_SCHEMA(); + + /** + * This function will process the user input + * + */ + $user_schema->userInputProcess($do); + + /** + * Now first show some possibility to select a page for the export of relation schema + */ + $user_schema->selectPage(); + + /** + * Possibility to create a new page: + */ + $user_schema->createPage(); + + /** + * After selection of page or creating a page + * It will show you the list of tables + * A dashboard will also be shown where you can position the tables + */ + $user_schema->showTableDashBoard(); + + if (isset($do) + && ($do == 'edcoord' + || ($do == 'selectpage' && isset($chpage)) + || ($do == 'createpage' && isset($chpage)))) { + /** + * show Export schema generation options + */ + $user_schema->displaySchemaGenerationOptions(); + + if ((isset($showwysiwyg) && $showwysiwyg == '1')) { + ?> + <script type="text/javascript"> + //<![CDATA[ + ToggleDragDrop('pdflayout'); + //]]> + </script> + <?php + } + } // end if +} // end if ($cfgRelation['pdfwork']) + + +/** + * Displays the footer + */ +echo "\n"; +require_once './libraries/footer.inc.php'; +?> \ No newline at end of file diff --git a/libraries/schema/dia_relation_schema.php b/libraries/schema/dia_relation_schema.php new file mode 100644 index 000000000..f7567ebde --- /dev/null +++ b/libraries/schema/dia_relation_schema.php @@ -0,0 +1,7 @@ +<?php +include_once("relationSchema.abstract.class.php"); +class PMA_DIA_RELAION_SCHEMA extends PMA_Relation_Schema +{ + +} +?> \ No newline at end of file diff --git a/libraries/schema/eps_relation_chema.php b/libraries/schema/eps_relation_chema.php new file mode 100644 index 000000000..d188304b9 --- /dev/null +++ b/libraries/schema/eps_relation_chema.php @@ -0,0 +1,7 @@ +<?php +include_once("relationSchema.abstract.class.php"); +class PMA_PDF_RELAION_SCHEMA extends PMA_Relation_Schema +{ + +} +?> \ No newline at end of file diff --git a/libraries/schema/pdf_relation_schema.php b/libraries/schema/pdf_relation_schema.php new file mode 100644 index 000000000..5fc107826 --- /dev/null +++ b/libraries/schema/pdf_relation_schema.php @@ -0,0 +1,1338 @@ +<?php +include_once("relation_schema.php"); +/** + * Font used in PDF. + * + * @todo Make this configuratble (at least Sans/Serif). + */ +define('PMA_PDF_FONT', 'DejaVuSans'); +require_once './libraries/tcpdf/tcpdf.php'; + +/** + * Extends the "FPDF" class and prepares the work + * + * @access public + * @see FPDF + * @package phpMyAdmin + */ +class PMA_PDF extends TCPDF { + /** + * Defines private properties + */ + var $x_min; + var $y_min; + var $l_marg = 10; + var $t_marg = 10; + var $scale; + var $PMA_links; + var $Outlines = array(); + var $def_outlines; + var $Alias = array(); + var $widths; + + public function getFh() + { + return $this->fh; + } + + public function getFw() + { + return $this->fw; + } + + public function setCMargin($c_margin) + { + $this->cMargin = $c_margin; + } + + function SetAlias($name, $value) + { + $this->Alias[$name] = $value ; + } + + function _putpages() + { + if (count($this->Alias) > 0) { + $nb = $this->page; + foreach ($this->Alias as $alias => $value) { + for ($n = 1;$n <= $nb;$n++) + $this->pages[$n]=str_replace($alias, $value, $this->pages[$n]); + } + } + parent::_putpages(); + } + + /** + * Sets the scaling factor, defines minimum coordinates and margins + * + * @param float scale The scaling factor + * @param float x_min The minimum X coordinate + * @param float y_min The minimum Y coordinate + * @param float l_marg The left margin + * @param float t_marg The top margin + * @access public + */ + function PMA_PDF_setScale($scale = 1, $x_min = 0, $y_min = 0, $l_marg = -1, $t_marg = -1) + { + $this->scale = $scale; + $this->x_min = $x_min; + $this->y_min = $y_min; + if ($this->l_marg != -1) { + $this->l_marg = $l_marg; + } + if ($this->t_marg != -1) { + $this->t_marg = $t_marg; + } + } // end of the "PMA_PDF_setScale" function + /** + * Outputs a scaled cell + * + * @param float w The cell width + * @param float h The cell height + * @param string txt The text to output + * @param mixed border Whether to add borders or not + * @param integer ln Where to put the cursor once the output is done + * @param string align Align mode + * @param integer fill Whether to fill the cell with a color or not + * @access public + * @see FPDF::Cell() + */ + function PMA_PDF_cellScale($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '') + { + $h = $h / $this->scale; + $w = $w / $this->scale; + $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link); + } // end of the "PMA_PDF_cellScale" function + /** + * Draws a scaled line + * + * @param float x1 The horizontal position of the starting point + * @param float y1 The vertical position of the starting point + * @param float x2 The horizontal position of the ending point + * @param float y2 The vertical position of the ending point + * @access public + * @see FPDF::Line() + */ + function PMA_PDF_lineScale($x1, $y1, $x2, $y2) + { + $x1 = ($x1 - $this->x_min) / $this->scale + $this->l_marg; + $y1 = ($y1 - $this->y_min) / $this->scale + $this->t_marg; + $x2 = ($x2 - $this->x_min) / $this->scale + $this->l_marg; + $y2 = ($y2 - $this->y_min) / $this->scale + $this->t_marg; + $this->Line($x1, $y1, $x2, $y2); + } // end of the "PMA_PDF_lineScale" function + /** + * Sets x and y scaled positions + * + * @param float x The x position + * @param float y The y position + * @access public + * @see FPDF::SetXY() + */ + function PMA_PDF_setXyScale($x, $y) + { + $x = ($x - $this->x_min) / $this->scale + $this->l_marg; + $y = ($y - $this->y_min) / $this->scale + $this->t_marg; + $this->SetXY($x, $y); + } // end of the "PMA_PDF_setXyScale" function + /** + * Sets the X scaled positions + * + * @param float x The x position + * @access public + * @see FPDF::SetX() + */ + function PMA_PDF_setXScale($x) + { + $x = ($x - $this->x_min) / $this->scale + $this->l_marg; + $this->SetX($x); + } // end of the "PMA_PDF_setXScale" function + /** + * Sets the scaled font size + * + * @param float size The font size (in points) + * @access public + * @see FPDF::SetFontSize() + */ + function PMA_PDF_setFontSizeScale($size) + { + // Set font size in points + $size = $size / $this->scale; + $this->SetFontSize($size); + } // end of the "PMA_PDF_setFontSizeScale" function + /** + * Sets the scaled line width + * + * @param float width The line width + * @access public + * @see FPDF::SetLineWidth() + */ + function PMA_PDF_setLineWidthScale($width) + { + $width = $width / $this->scale; + $this->SetLineWidth($width); + } // end of the "PMA_PDF_setLineWidthScale" function + /** + * Displays an error message + * + * @param string error_message the error mesage + * @global array the PMA configuration array + * @global integer the current server id + * @global string the current language + * @global string the charset to convert to + * @global string the current database name + * @global string the current charset + * @global string the current text direction + * @global string a localized string + * @global string an other localized string + * @access public + */ +/* function PMA_PDF_die($error_message = '') + { + global $cfg; + global $server, $lang, $convcharset, $db; + global $charset, $text_dir; + + require_once './libraries/header.inc.php'; + + echo '<p><strong>PDF - ' . __('Error') . '</strong></p>' . "\n"; + if (!empty($error_message)) { + $error_message = htmlspecialchars($error_message); + } + echo '<p>' . "\n"; + echo ' ' . $error_message . "\n"; + echo '</p>' . "\n"; + + echo '<a href="db_structure.php?' . PMA_generate_common_url($db) + . '">' . __('Back') . '</a>'; + echo "\n"; + + require_once './libraries/footer.inc.php'; + }*/ // end of the "PMA_PDF_die()" function + /** + * Aliases the "Error()" function from the FPDF class to the + * "PMA_PDF_die()" one + * + * @param string error_message the error mesage + * @access public + * @see PMA_PDF_die + */ + function Error($error_message = '') + { + PMA_Relation_Schema::PMA_Schema_die($error_message); + } // end of the "Error()" method + function Header() + { + // We only show this if we find something in the new pdf_pages table + + // This function must be named "Header" to work with the FPDF library + global $cfgRelation, $db, $pdf_page_number, $with_doc; + if ($with_doc) { + $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND page_nr = \'' . $pdf_page_number . '\''; + $test_rs = PMA_query_as_controluser($test_query); + $pages = @PMA_DBI_fetch_assoc($test_rs); + $this->SetFont('', 'B', 14); + $this->Cell(0, 6, ucfirst($pages['page_descr']), 'B', 1, 'C'); + $this->SetFont('', ''); + $this->Ln(); + } + } + function Footer() + { + // This function must be named "Footer" to work with the FPDF library + global $with_doc; + if ($with_doc) { + $this->SetY(-15); + $this->SetFont('', '', 14); + $this->Cell(0, 6, __('Page number:') . ' ' . $this->PageNo() . '/{nb}', 'T', 0, 'C'); + $this->Cell(0, 6, PMA_localisedDate(), 0, 1, 'R'); + $this->SetY(20); + } + } + function Bookmark($txt, $level = 0, $y = 0) + { + // Add a bookmark + $this->Outlines[0][] = $level; + $this->Outlines[1][] = $txt; + $this->Outlines[2][] = $this->page; + if ($y == -1) { + $y = $this->GetY(); + } + $this->Outlines[3][] = round($this->hPt - $y * $this->k, 2); + } + + function _putbookmarks() + { + if (count($this->Outlines) > 0) { + // Save object number + $memo_n = $this->n; + // Take the number of sub elements for an outline + $nb_outlines = sizeof($this->Outlines[0]); + $first_level = array(); + $parent = array(); + $parent[0] = 1; + for ($i = 0; $i < $nb_outlines; $i++) { + $level = $this->Outlines[0][$i]; + $kids = 0; + $last = -1; + $prev = -1; + $next = -1; + if ($i > 0) { + $cursor = $i-1; + // Take the previous outline in the same level + while ($this->Outlines[0][$cursor] > $level && $cursor > 0) + $cursor--; + if ($this->Outlines[0][$cursor] == $level) { + $prev = $cursor; + } + } + if ($i < $nb_outlines-1) { + $cursor = $i + 1; + while (isset($this->Outlines[0][$cursor]) && $this->Outlines[0][$cursor] > $level) { + // Take the immediate kid in level + 1 + if ($this->Outlines[0][$cursor] == $level + 1) { + $kids++; + $last = $cursor; + } + $cursor++; + } + $cursor = $i + 1; + // Take the next outline in the same level + while ($this->Outlines[0][$cursor] > $level && ($cursor + 1 < sizeof($this->Outlines[0]))) + $cursor++; + if ($this->Outlines[0][$cursor] == $level) { + $next = $cursor; + } + } + $this->_newobj(); + $parent[$level + 1] = $this->n; + if ($level == 0) { + $first_level[] = $this->n; + } + $this->_out('<<'); + $this->_out('/Title (' . $this->Outlines[1][$i] . ')'); + $this->_out('/Parent ' . $parent[$level] . ' 0 R'); + if ($prev != -1) { + $this->_out('/Prev ' . ($memo_n + $prev + 1) . ' 0 R'); + } + if ($next != -1) { + $this->_out('/Next ' . ($this->n + $next - $i) . ' 0 R'); + } + $this->_out('/Dest [' . (1 + (2 * $this->Outlines[2][$i])) . ' 0 R /XYZ null ' . $this->Outlines[3][$i] . ' null]'); + if ($kids > 0) { + $this->_out('/First ' . ($this->n + 1) . ' 0 R'); + $this->_out('/Last ' . ($this->n + $last - $i) . ' 0 R'); + $this->_out('/Count -' . $kids); + } + $this->_out('>>'); + $this->_out('endobj'); + } + // First page of outlines + $this->_newobj(); + $this->def_outlines = $this->n; + $this->_out('<<'); + $this->_out('/Type'); + $this->_out('/Outlines'); + $this->_out('/First ' . $first_level[0] . ' 0 R'); + $this->_out('/Last ' . $first_level[sizeof($first_level)-1] . ' 0 R'); + $this->_out('/Count ' . sizeof($first_level)); + $this->_out('>>'); + $this->_out('endobj'); + } + } + + function _putresources() + { + parent::_putresources(); + $this->_putbookmarks(); + } + + function _putcatalog() + { + parent::_putcatalog(); + if (count($this->Outlines) > 0) { + $this->_out('/Outlines ' . $this->def_outlines . ' 0 R'); + $this->_out('/PageMode /UseOutlines'); + } + } + function SetWidths($w) + { + // column widths + $this->widths = $w; + } + + function Row($data, $links) + { + // line height + $nb = 0; + $data_cnt = count($data); + for ($i = 0;$i < $data_cnt;$i++) + $nb = max($nb, $this->NbLines($this->widths[$i], $data[$i])); + $il = $this->FontSize; + $h = ($il + 1) * $nb; + // page break if necessary + $this->CheckPageBreak($h); + // draw the cells + $data_cnt = count($data); + for ($i = 0;$i < $data_cnt;$i++) { + $w = $this->widths[$i]; + // save current position + $x = $this->GetX(); + $y = $this->GetY(); + // draw the border + $this->Rect($x, $y, $w, $h); + if (isset($links[$i])) { + $this->Link($x, $y, $w, $h, $links[$i]); + } + // print text + $this->MultiCell($w, $il + 1, $data[$i], 0, 'L'); + // go to right side + $this->SetXY($x + $w, $y); + } + // go to line + $this->Ln($h); + } + + function CheckPageBreak($h) + { + // if height h overflows, manual page break + if ($this->GetY() + $h > $this->PageBreakTrigger) { + $this->AddPage($this->CurOrientation); + } + } + + function NbLines($w, $txt) + { + // compute number of lines used by a multicell of width w + $cw = &$this->CurrentFont['cw']; + if ($w == 0) { + $w = $this->w - $this->rMargin - $this->x; + } + $wmax = ($w-2 * $this->cMargin) * 1000 / $this->FontSize; + $s = str_replace("\r", '', $txt); + $nb = strlen($s); + if ($nb > 0 and $s[$nb-1] == "\n") { + $nb--; + } + $sep = -1; + $i = 0; + $j = 0; + $l = 0; + $nl = 1; + while ($i < $nb) { + $c = $s[$i]; + if ($c == "\n") { + $i++; + $sep = -1; + $j = $i; + $l = 0; + $nl++; + continue; + } + if ($c == ' ') { + $sep = $i; + } + $l += isset($cw[ord($c)])?$cw[ord($c)]:0 ; + if ($l > $wmax) { + if ($sep == -1) { + if ($i == $j) { + $i++; + } + } else { + $i = $sep + 1; + } + $sep = -1; + $j = $i; + $l = 0; + $nl++; + } else { + $i++; + } + } + return $nl; + } +} // end of the "PMA_PDF" class + +/** + * Draws tables schema + * + * @access private + * @see PMA_RT + * @package phpMyAdmin + */ +class PMA_RT_Table { + /** + * Defines private properties + */ + var $nb_fiels; + var $table_name; + var $width = 0; + var $height; + var $fields = array(); + var $height_cell = 6; + var $x, $y; + var $primary = array(); + var $show_info = false; + + /** + * Returns title of the current table, + * title can have the dimensions of the table + * + * @access private + */ + function getTitle() + { + return ($this->show_info ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->height) : '') . ' ' . $this->table_name; + } // end of the "getTitle" function + /** + * Sets the width of the table + * + * @param integer ff The font size + * @global object The current PDF document + * @access private + * @see PMA_PDF + */ + function PMA_RT_Table_setWidth($ff) + { + global $pdf; + + foreach ($this->fields as $field) { + $this->width = max($this->width, $pdf->GetStringWidth($field)); + } + //echo $this->width.'<br />'; + $this->width += $pdf->GetStringWidth(' '); + //echo $this->width.'<br />'; + $pdf->SetFont($ff, 'B'); + // it is unknown what value must be added, because + // table title is affected by the tabe width value + //echo $this->getTitle(); + while ($this->width < $pdf->GetStringWidth($this->getTitle())) { + $this->width += 5; + } + //echo $this->width.'<br />'; + $pdf->SetFont($ff, ''); + } // end of the "PMA_RT_Table_setWidth()" method + /** + * Sets the height of the table + * + * @access private + */ + function PMA_RT_Table_setHeight() + { + $this->height = (count($this->fields) + 1) * $this->height_cell; + } // end of the "PMA_RT_Table_setHeight()" method + /** + * Do draw the table + * + * @param integer ff The font size + * @param boolean setcolortWhether to display color + * @global object The current PDF document + * @access private + * @see PMA_PDF + */ + function PMA_RT_Table_draw($ff, $setcolor = 0) + { + global $pdf, $with_doc; + + $pdf->PMA_PDF_setXyScale($this->x, $this->y); + $pdf->SetFont($ff, 'B'); + if ($setcolor) { + $pdf->SetTextColor(200); + $pdf->SetFillColor(0, 0, 128); + } + if ($with_doc) { + $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name]['-'], -1); + } else { + $pdf->PMA_links['doc'][$this->table_name]['-'] = ''; + } + + $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, $this->getTitle(), 1, 1, 'C', $setcolor, $pdf->PMA_links['doc'][$this->table_name]['-']); + $pdf->PMA_PDF_setXScale($this->x); + $pdf->SetFont($ff, ''); + $pdf->SetTextColor(0); + $pdf->SetFillColor(255); + + foreach ($this->fields as $field) { + if ($setcolor) { + if (in_array($field, $this->primary)) { + $pdf->SetFillColor(215, 121, 123); + } + if ($field == $this->displayfield) { + $pdf->SetFillColor(142, 159, 224); + } + } + if ($with_doc) { + $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name][$field], -1); + } else { + $pdf->PMA_links['doc'][$this->table_name][$field] = ''; + } + + $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, ' ' . $field, 1, 1, 'L', $setcolor, $pdf->PMA_links['doc'][$this->table_name][$field]); + $pdf->PMA_PDF_setXScale($this->x); + $pdf->SetFillColor(255); + } // end while + /*if ($pdf->PageNo() > 1) { + $pdf->PMA_PDF_die(__('The scale factor is too small to fit the schema on one page')); + } */ + } // end of the "PMA_RT_Table_draw()" method + /** + * The "PMA_RT_Table" constructor + * + * @param string table_name The table name + * @param integer ff The font size + * @param integer same_width The max. with among tables + * @param boolean show_keys Whether to display keys or not + * @param boolean show_info Whether to display table position or not + * @global object The current PDF document + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @global array The relations settings + * @global string The current db name + * @access private + * @see PMA_PDF, PMA_RT_Table::PMA_RT_Table_setWidth, + PMA_RT_Table::PMA_RT_Table_setHeight + */ + function __construct($table_name, $ff, &$same_wide_width, $show_keys = false, $show_info = false) + { + global $pdf, $pdf_page_number, $cfgRelation, $db; + + $this->table_name = $table_name; + $sql = 'DESCRIBE ' . PMA_backquote($table_name); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $pdf->PMA_PDF_die(sprintf(__('The %s table doesn\'t exist!'), $table_name)); + } + // load fields + //check to see if it will load all fields or only the foreign keys + if ($show_keys) { + $indexes = PMA_Index::getFromTable($this->table_name, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $this->show_info = $show_info; + + // height and width + $this->PMA_RT_Table_setHeight(); + // setWidth must me after setHeight, because title + // can include table height which changes table width + $this->PMA_RT_Table_setWidth($ff); + if ($same_wide_width < $this->width) { + $same_wide_width = $this->width; + } + // x and y + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($table_name) . '\'' + . ' AND pdf_page_number = ' . $pdf_page_number; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + + if (!$result || !PMA_DBI_num_rows($result)) { + $pdf->PMA_PDF_die(sprintf(__('Please configure the coordinates for table %s'), $table_name)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + // displayfield + $this->displayfield = PMA_getDisplayField($db, $table_name); + // index + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($table_name) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } // end if + } // end of the "PMA_RT_Table()" method +} // end class "PMA_RT_Table" + +/** + * Draws relation links + * + * @access private + * @see PMA_RT + * @package phpMyAdmin + */ +class PMA_RT_Relation { + /** + * Defines private properties + */ + var $x_src, $y_src; + var $src_dir ; + var $dest_dir; + var $x_dest, $y_dest; + var $w_tick = 5; + + /** + * Gets arrows coordinates + * + * @param string table The current table name + * @param string column The relation column name + * @return array Arrows coordinates + * @access private + */ + function PMA_RT_Relation_getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // x_left, x_right, y + return array($table->x, $table->x + + $table->width, $table->y + ($pos + 1.5) * $table->height_cell); + } // end of the "PMA_RT_Relation_getXy()" method + /** + * Do draws relation links + * + * @param boolean change_color Whether to use one color per relation or not + * @param integer i The id of the link to draw + * @global object The current PDF document + * @access private + * @see PMA_PDF + */ + function PMA_RT_Relation_draw($change_color, $i) + { + global $pdf; + + if ($change_color) { + $d = $i % 6; + $j = ($i - $d) / 6; + $j = $j % 4; + $j++; + $case = array( + array(1, 0, 0), + array(0, 1, 0), + array(0, 0, 1), + array(1, 1, 0), + array(1, 0, 1), + array(0, 1, 1) + ); + list ($a, $b, $c) = $case[$d]; + $e = (1 - ($j - 1) / 6); + $pdf->SetDrawColor($a * 255 * $e, $b * 255 * $e, $c * 255 * $e); + } else { + $pdf->SetDrawColor(0); + } // end if... else... + $pdf->PMA_PDF_setLineWidthScale(0.2); + $pdf->PMA_PDF_lineScale($this->x_src, $this->y_src, $this->x_src + $this->src_dir * $this->w_tick, $this->y_src); + $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest, $this->x_dest, $this->y_dest); + $pdf->PMA_PDF_setLineWidthScale(0.1); + $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick, $this->y_src, $this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest); + // arrow + $root2 = 2 * sqrt(2); + $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src + $this->w_tick / $root2); + $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src - $this->w_tick / $root2); + + $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest + $this->w_tick / $root2); + $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest - $this->w_tick / $root2); + $pdf->SetDrawColor(0); + } // end of the "PMA_RT_Relation_draw()" method + /** + * The "PMA_RT_Relation" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @access private + * @see PMA_RT_Relation::PMA_RT_Relation_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->PMA_RT_Relation_getXy($master_table, $master_field); + $dest_pos = $this->PMA_RT_Relation_getXy($foreign_table, $foreign_field); + $src_left = $src_pos[0] - $this->w_tick; + $src_right = $src_pos[1] + $this->w_tick; + $dest_left = $dest_pos[0] - $this->w_tick; + $dest_right = $dest_pos[1] + $this->w_tick; + + $d1 = abs($src_left - $dest_left); + $d2 = abs($src_right - $dest_left); + $d3 = abs($src_left - $dest_right); + $d4 = abs($src_right - $dest_right); + $d = min($d1, $d2, $d3, $d4); + + if ($d == $d1) { + $this->x_src = $src_pos[0]; + $this->src_dir = -1; + $this->x_dest = $dest_pos[0]; + $this->dest_dir = -1; + } elseif ($d == $d2) { + $this->x_src = $src_pos[1]; + $this->src_dir = 1; + $this->x_dest = $dest_pos[0]; + $this->dest_dir = -1; + } elseif ($d == $d3) { + $this->x_src = $src_pos[0]; + $this->src_dir = -1; + $this->x_dest = $dest_pos[1]; + $this->dest_dir = 1; + } else { + $this->x_src = $src_pos[1]; + $this->src_dir = 1; + $this->x_dest = $dest_pos[1]; + $this->dest_dir = 1; + } + $this->y_src = $src_pos[2]; + $this->y_dest = $dest_pos[2]; + } // end of the "PMA_RT_Relation()" method +} // end of the "PMA_RT_Relation" class + +class PMA_PDF_RELAION_SCHEMA extends PMA_Relation_Schema +{ + /** + * Defines private properties + */ + private $tables = array(); + private $relations = array(); + private $ff = PMA_PDF_FONT; + private $x_max = 0; + private $y_max = 0; + private $scale; + private $x_min = 100000; + private $y_min = 100000; + private $t_marg = 10; + private $b_marg = 10; + private $l_marg = 10; + private $r_marg = 10; + private $_tablewidth; + + /** + * The "PMA_RT" constructor + * + * @param integer page_number The page number to draw (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @param boolean show_info Whether to display table position or not + * @param boolean change_color Was originally whether to use one color per + * relation or not, now enables/disables color + * everywhere, due to some problems printing with color + * @param boolean show_grid Whether to draw grids or not + * @param boolean all_tab_same_wide Whether all tables should have the same width or not + * @param boolean show_keys Wheter to show all field or only the keys + * @global object The current PDF document + * @global string The current db name + * @global array The relations settings + * @access private + * @see PMA_PDF + */ + function __construct($page_number, $show_info = 0, $change_color = 0, $show_grid = 0, $all_table_same_wide = 0, $orientation = 'L', $paper = 'A4', $show_keys = 0) + { + global $pdf, $db, $cfgRelation, $with_doc; + + $this->setSameWidthTables($all_table_same_wide); + + // Initializes a new document + $pdf = new PMA_PDF('L', 'mm', $paper); + $pdf->SetTitle(sprintf(__('Schema of the %s database - Page %s'), $GLOBALS['db'], $page_number)); + $pdf->setCMargin(0); + $pdf->Open(); + $pdf->SetAuthor('phpMyAdmin ' . PMA_VERSION); + $pdf->AliasNbPages(); + $pdf->AddFont('DejaVuSans', '', 'dejavusans.php'); + $pdf->AddFont('DejaVuSans', 'B', 'dejavusansb.php'); + $pdf->AddFont('DejaVuSerif', '', 'dejavuserif.php'); + $pdf->AddFont('DejaVuSerif', 'B', 'dejavuserifb.php'); + // $this->ff = PMA_PDF_FONT; + + $pdf->SetFont($this->ff, '', 14); + $pdf->SetAutoPageBreak('auto'); + + + // Gets tables on this page +/* $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = ' . $page_number; + echo $tab_sql; + $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); + if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { + $pdf->PMA_PDF_die(__('No tables')); + // die('No tables'); + } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { + $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); + // $intable = '\'' . implode('\', \'', $alltables) . '\''; + }*/ + $alltables=$this->getAllTables($db,$page_number); + + + // make doc // + if ($with_doc) { + $pdf->SetAutoPageBreak('auto', 15); + $pdf->setCMargin(1); + $this->PMA_RT_DOC($alltables); + $pdf->SetAutoPageBreak('auto'); + $pdf->setCMargin(0); + } + + $pdf->Addpage(); + + if ($with_doc) { + $pdf->SetLink($pdf->PMA_links['RT']['-'], -1); + $pdf->Bookmark(__('Relational schema')); + $pdf->SetAlias('{00}', $pdf->PageNo()) ; + $this->t_marg = 28; + $this->b_marg = 28; + } + + /* snip */ + + foreach ($alltables as $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->_tablewidth, $show_keys, $show_info); + // $this->tables[$table]=$table; + } + + if ($this->same_wide) { + $this->tables[$table]->width = $this->_tablewidth; + } + $this->PMA_RT_setMinMax($this->tables[$table]); + } + + // Defines the scale factor + $this->scale = ceil( + max( + ($this->x_max - $this->x_min) / ($pdf->getFh() - $this->r_marg - $this->l_marg), + ($this->y_max - $this->y_min) / ($pdf->getFw() - $this->t_marg - $this->b_marg)) + * 100) / 100; + + $pdf->PMA_PDF_setScale($this->scale, $this->x_min, $this->y_min, $this->l_marg, $this->t_marg); + // Builds and save the PDF document + $pdf->PMA_PDF_setLineWidthScale(0.1); + + if ($show_grid) { + $pdf->SetFontSize(10); + $this->PMA_RT_strokeGrid(); + } + $pdf->PMA_PDF_setFontSizeScale(14); + // $sql = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) + // . ' WHERE master_db = \'' . PMA_sqlAddslashes($db) . '\' ' + // . ' AND foreign_db = \'' . PMA_sqlAddslashes($db) . '\' ' + // . ' AND master_table IN (' . $intable . ')' + // . ' AND foreign_table IN (' . $intable . ')'; + // $result = PMA_query_as_controluser($sql); + + // previous logic was checking master tables and foreign tables + // but I think that looping on every table of the pdf page as a master + // and finding its foreigns is OK (then we can support innodb) + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + // put the foreign table on the schema only if selected + // by the user + // (do not use array_search() because we would have to + // to do a === FALSE and this is not PHP3 compatible) + if (in_array($rel['foreign_table'], $alltables)) { + $this->PMA_RT_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'], $show_info); + } + } // end while + } // end if + } // end while + +/* print '<pre>'; + print_r(get_object_vars($this)); + print '</pre>'; + exit();*/ + + if ($seen_a_relation) { + $this->PMA_RT_drawRelations($change_color); + } + + $this->PMA_RT_drawTables($change_color); + + $this->PMA_RT_showRt(); + } // end of the "PMA_RT()" Constructor method + /** + * Sets X and Y minimum and maximum for a table cell + * + * @param string table The table name + * @access private + */ + function PMA_RT_setMinMax($table) + { + $this->x_max = max($this->x_max, $table->x + $table->width); + $this->y_max = max($this->y_max, $table->y + $table->height); + $this->x_min = min($this->x_min, $table->x); + $this->y_min = min($this->y_min, $table->y); + } // end of the "PMA_RT_setMinMax()" method + /** + * Defines relation objects + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreign_field The relation field in the foreign table + * @param boolean show_info Whether to display table position or not + * @access private + * @see PMA_RT_setMinMax + */ + function PMA_RT_addRelation($master_table, $master_field, $foreign_table, $foreign_field, $show_info) + { + if (!isset($this->tables[$master_table])) { + $this->tables[$master_table] = new PMA_RT_Table($master_table, $this->ff, $this->_tablewidth, false, $show_info); + $this->PMA_RT_setMinMax($this->tables[$master_table]); + } + if (!isset($this->tables[$foreign_table])) { + $this->tables[$foreign_table] = new PMA_RT_Table($foreign_table, $this->ff, $this->_tablewidth, false, $show_info); + $this->PMA_RT_setMinMax($this->tables[$foreign_table]); + } + $this->relations[] = new PMA_RT_Relation($this->tables[$master_table], $master_field, $this->tables[$foreign_table], $foreign_field); + } // end of the "PMA_RT_addRelation()" method + /** + * Draws the grid + * + * @global object the current PMA_PDF instance + * @access private + * @see PMA_PDF + */ + function PMA_RT_strokeGrid() + { + global $pdf; + + $pdf->SetMargins(0, 0); + $pdf->SetDrawColor(200, 200, 200); + // Draws horizontal lines + for ($l = 0; $l < 21; $l++) { + $pdf->line(0, $l * 10, $pdf->getFh(), $l * 10); + // Avoid duplicates + if ($l > 0) { + $pdf->SetXY(0, $l * 10); + $label = (string) sprintf('%.0f', ($l * 10 - $this->t_marg) * $this->scale + $this->y_min); + $pdf->Cell(5, 5, ' ' . $label); + } // end if + } // end for + // Draws vertical lines + for ($j = 0; $j < 30 ;$j++) { + $pdf->line($j * 10, 0, $j * 10, $pdf->getFw()); + $pdf->SetXY($j * 10, 0); + $label = (string) sprintf('%.0f', ($j * 10 - $this->l_marg) * $this->scale + $this->x_min); + $pdf->Cell(5, 7, $label); + } // end for + } // end of the "PMA_RT_strokeGrid()" method + /** + * Draws relation arrows + * + * @param boolean change_color Whether to use one color per relation or not + * @access private + * @see PMA_RT_Relation::PMA_RT_Relation_draw() + */ + function PMA_RT_drawRelations($change_color) + { + $i = 0; + foreach ($this->relations as $relation) { + $relation->PMA_RT_Relation_draw($change_color, $i); + $i++; + } // end while + } // end of the "PMA_RT_drawRelations()" method + /** + * Draws tables + * + * @param boolean draw_color Whether to display table position or not + * @access private + * @see PMA_RT_Table::PMA_RT_Table_draw() + */ + function PMA_RT_drawTables($draw_color = 0) + { + foreach ($this->tables as $table) { + $table->PMA_RT_Table_draw($this->ff, $draw_color); + } + } // end of the "PMA_RT_drawTables()" method + /** + * Ouputs the PDF document to a file + * + * @global object The current PDF document + * @global string The current database name + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @access private + * @see PMA_PDF + */ + function PMA_RT_showRt() + { + global $pdf, $db, $pdf_page_number, $cfgRelation; + + $pdf->SetFontSize(14); + $pdf->SetLineWidth(0.2); + $pdf->SetDisplayMode('fullpage'); + // Get the name of this pdfpage to use as filename (Mike Beck) + $_name_sql = 'SELECT page_descr FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE page_nr = ' . $pdf_page_number; + $_name_rs = PMA_query_as_controluser($_name_sql); + if ($_name_rs) { + $_name_row = PMA_DBI_fetch_row($_name_rs); + $filename = $_name_row[0] . '.pdf'; + } + // i don't know if there is a chance for this to happen, but rather be on the safe side: + if (empty($filename)) { + $filename = $pdf_page_number . '.pdf'; + } + // $pdf->Output($db . '_' . $filename, TRUE); + $pdf->Output($db . '_' . $filename, 'I'); // destination: Inline + } // end of the "PMA_RT_showRt()" method + + function PMA_RT_DOC($alltables) +{ + global $db, $pdf, $orientation, $paper; + // TOC + $pdf->addpage($GLOBALS['orientation']); + $pdf->Cell(0, 9, __('Table of contents'), 1, 0, 'C'); + $pdf->Ln(15); + $i = 1; + foreach ($alltables as $table) { + $pdf->PMA_links['doc'][$table]['-'] = $pdf->AddLink(); + $pdf->SetX(10); + // $pdf->Ln(1); + $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$table]['-']); + $pdf->SetX(10); + $pdf->Cell(0, 6, $i . ' ' . $table, 0, 1, 'L', 0, $pdf->PMA_links['doc'][$table]['-']); + // $pdf->Ln(1); + $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';'); + while ($row = PMA_DBI_fetch_assoc($result)) { + $pdf->SetX(20); + $field_name = $row['Field']; + $pdf->PMA_links['doc'][$table][$field_name] = $pdf->AddLink(); + // $pdf->Cell(0, 6, $field_name,0,1,'L',0, $pdf->PMA_links['doc'][$table][$field_name]); + } + $lasttable = $table; + $i++; + } + $pdf->PMA_links['RT']['-'] = $pdf->AddLink(); + $pdf->SetX(10); + $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$lasttable]['-']); + $pdf->SetX(10); + $pdf->Cell(0, 6, $i + 1 . ' ' . __('Relational schema'), 0, 1, 'L', 0, $pdf->PMA_links['RT']['-']); + $z = 0; + foreach ($alltables as $table) { + $z++; + $pdf->addpage($GLOBALS['orientation']); + $pdf->Bookmark($table); + $pdf->SetAlias('{' . sprintf("%02d", $z) . '}', $pdf->PageNo()) ; + $pdf->PMA_links['RT'][$table]['-'] = $pdf->AddLink(); + $pdf->SetLink($pdf->PMA_links['doc'][$table]['-'], -1); + $pdf->SetFont('', 'B', 18); + $pdf->Cell(0, 8, $z . ' ' . $table, 1, 1, 'C', 0, $pdf->PMA_links['RT'][$table]['-']); + $pdf->SetFont('', '', 8); + $pdf->ln(); + + $cfgRelation = PMA_getRelationsParam(); + $comments = PMA_getComments($db, $table); + if ($cfgRelation['mimework']) { + $mime_map = PMA_getMIME($db, $table, true); + } + + /** + * Gets table informations + */ + $showtable = PMA_Table::sGetStatusInfo($db, $table); + $num_rows = (isset($showtable['Rows']) ? $showtable['Rows'] : 0); + $show_comment = (isset($showtable['Comment']) ? $showtable['Comment'] : ''); + $create_time = (isset($showtable['Create_time']) ? PMA_localisedDate(strtotime($showtable['Create_time'])) : ''); + $update_time = (isset($showtable['Update_time']) ? PMA_localisedDate(strtotime($showtable['Update_time'])) : ''); + $check_time = (isset($showtable['Check_time']) ? PMA_localisedDate(strtotime($showtable['Check_time'])) : ''); + + /** + * Gets table keys and retains them + */ + $result = PMA_DBI_query('SHOW KEYS FROM ' . PMA_backquote($table) . ';'); + $primary = ''; + $indexes = array(); + $lastIndex = ''; + $indexes_info = array(); + $indexes_data = array(); + $pk_array = array(); // will be use to emphasis prim. keys in the table + // view + while ($row = PMA_DBI_fetch_assoc($result)) { + // Backups the list of primary keys + if ($row['Key_name'] == 'PRIMARY') { + $primary .= $row['Column_name'] . ', '; + $pk_array[$row['Column_name']] = 1; + } + // Retains keys informations + if ($row['Key_name'] != $lastIndex) { + $indexes[] = $row['Key_name']; + $lastIndex = $row['Key_name']; + } + $indexes_info[$row['Key_name']]['Sequences'][] = $row['Seq_in_index']; + $indexes_info[$row['Key_name']]['Non_unique'] = $row['Non_unique']; + if (isset($row['Cardinality'])) { + $indexes_info[$row['Key_name']]['Cardinality'] = $row['Cardinality']; + } + // I don't know what does following column mean.... + // $indexes_info[$row['Key_name']]['Packed'] = $row['Packed']; + $indexes_info[$row['Key_name']]['Comment'] = $row['Comment']; + + $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Column_name'] = $row['Column_name']; + if (isset($row['Sub_part'])) { + $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Sub_part'] = $row['Sub_part']; + } + } // end while + if ($result) { + PMA_DBI_free_result($result); + } + + /** + * Gets fields properties + */ + $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE); + $fields_cnt = PMA_DBI_num_rows($result); + // Check if we can use Relations (Mike Beck) + if (!empty($cfgRelation['relation'])) { + // Find which tables are related with the current one and write it in + // an array + $res_rel = PMA_getForeigners($db, $table); + + if (count($res_rel) > 0) { + $have_rel = true; + } else { + $have_rel = false; + } + } else { + $have_rel = false; + } // end if + /** + * Displays the comments of the table if MySQL >= 3.23 + */ + + $break = false; + if (!empty($show_comment)) { + $pdf->Cell(0, 3, __('Table comments') . ' : ' . $show_comment, 0, 1); + $break = true; + } + + if (!empty($create_time)) { + $pdf->Cell(0, 3, __('Creation') . ': ' . $create_time, 0, 1); + $break = true; + } + + if (!empty($update_time)) { + $pdf->Cell(0, 3, __('Last update') . ': ' . $update_time, 0, 1); + $break = true; + } + + if (!empty($check_time)) { + $pdf->Cell(0, 3, __('Last check') . ': ' . $check_time, 0, 1); + $break = true; + } + + if ($break == true) { + $pdf->Cell(0, 3, '', 0, 1); + $pdf->Ln(); + } + + $pdf->SetFont('', 'B'); + if (isset($orientation) && $orientation == 'L') { + $pdf->Cell(25, 8, ucfirst(__('Column')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); + $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Default')), 1, 0, 'C'); + $pdf->Cell(25, 8, ucfirst(__('Extra')), 1, 0, 'C'); + $pdf->Cell(45, 8, ucfirst(__('Links to')), 1, 0, 'C'); + + if ($paper == 'A4') { + $comments_width = 67; + } else { + // this is really intended for 'letter' + /** + * @todo find optimal width for all formats + */ + $comments_width = 50; + } + $pdf->Cell($comments_width, 8, ucfirst(__('Comments')), 1, 0, 'C'); + $pdf->Cell(45, 8, 'MIME', 1, 1, 'C'); + $pdf->SetWidths(array(25, 20, 20, 10, 20, 25, 45, $comments_width, 45)); + } else { + $pdf->Cell(20, 8, ucfirst(__('Column')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); + $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); + $pdf->Cell(15, 8, ucfirst(__('Default')), 1, 0, 'C'); + $pdf->Cell(15, 8, ucfirst(__('Extra')), 1, 0, 'C'); + $pdf->Cell(30, 8, ucfirst(__('Links to')), 1, 0, 'C'); + $pdf->Cell(30, 8, ucfirst(__('Comments')), 1, 0, 'C'); + $pdf->Cell(30, 8, 'MIME', 1, 1, 'C'); + $pdf->SetWidths(array(20, 20, 20, 10, 15, 15, 30, 30, 30)); + } + $pdf->SetFont('', ''); + + while ($row = PMA_DBI_fetch_assoc($result)) { + $type = $row['Type']; + // reformat mysql query output + // set or enum types: slashes single quotes inside options + if (preg_match('@^(set|enum)\((.+)\)$@i', $type, $tmp)) { + $tmp[2] = substr(preg_replace("@([^,])''@", "\\1\\'", ',' . $tmp[2]), 1); + $type = $tmp[1] . '(' . str_replace(',', ', ', $tmp[2]) . ')'; + $type_nowrap = ''; + + $binary = 0; + $unsigned = 0; + $zerofill = 0; + } else { + $type_nowrap = ' nowrap="nowrap"'; + $type = preg_replace('@BINARY@i', '', $type); + $type = preg_replace('@ZEROFILL@i', '', $type); + $type = preg_replace('@UNSIGNED@i', '', $type); + if (empty($type)) { + $type = '&nbsp;'; + } + + $binary = stristr($row['Type'], 'BINARY'); + $unsigned = stristr($row['Type'], 'UNSIGNED'); + $zerofill = stristr($row['Type'], 'ZEROFILL'); + } + $attribute = ' '; + if ($binary) { + $attribute = 'BINARY'; + } + if ($unsigned) { + $attribute = 'UNSIGNED'; + } + if ($zerofill) { + $attribute = 'UNSIGNED ZEROFILL'; + } + if (!isset($row['Default'])) { + if ($row['Null'] != '' && $row['Null'] != 'NO') { + $row['Default'] = 'NULL'; + } + } + $field_name = $row['Field']; + // $pdf->Ln(); + $pdf->PMA_links['RT'][$table][$field_name] = $pdf->AddLink(); + $pdf->Bookmark($field_name, 1, -1); + $pdf->SetLink($pdf->PMA_links['doc'][$table][$field_name], -1); + $pdf_row = array($field_name, + $type, + $attribute, + ($row['Null'] == '' || $row['Null'] == 'NO') ? __('No') : __('Yes'), + ((isset($row['Default'])) ? $row['Default'] : ''), + $row['Extra'], + ((isset($res_rel[$field_name])) ? $res_rel[$field_name]['foreign_table'] . ' -> ' . $res_rel[$field_name]['foreign_field'] : ''), + ((isset($comments[$field_name])) ? $comments[$field_name] : ''), + ((isset($mime_map) && isset($mime_map[$field_name])) ? str_replace('_', '/', $mime_map[$field_name]['mimetype']) : '') + ); + $links[0] = $pdf->PMA_links['RT'][$table][$field_name]; + if (isset($res_rel[$field_name]['foreign_table']) AND + isset($res_rel[$field_name]['foreign_field']) AND + isset($pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]) + ) + { + $links[6] = $pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]; + } else { + unset($links[6]); + } + $pdf->Row($pdf_row, $links); + } // end while + $pdf->SetFont('', '', 14); + PMA_DBI_free_result($result); + } //end each + } +} +?> \ No newline at end of file diff --git a/libraries/schema/relation_schema.php b/libraries/schema/relation_schema.php new file mode 100644 index 000000000..e0df5935c --- /dev/null +++ b/libraries/schema/relation_schema.php @@ -0,0 +1,71 @@ +<?php +// Using Abstract Factory Pattern for exporting relational Schema in different Formats ! +abstract class PMA_Relation_Schema +{ + private $_pageTitle; // Title of the page + private $_autoLayoutType; // Internal or Foreign Key Relations; + protected $same_wide; + + public function setPageTitle($title) + { + $this->_pageTitle=$title; + } + + public function setExportType($type) + { + $this->_pageTitle=$title; + } + + public function setSameWidthTables($width) + { + $this->same_wide=$width; + } + + public function getAllTables($db,$page_number) + { + global $cfgRelation; + // Get All tables + $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = ' . $page_number; + // echo $tab_sql; + $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); + if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { + $this->PMA_SCHEMA_die('',__('No tables')); + } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { + $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); + } + return $alltables; + } + + public function initTable() + { + + } + + + /** + * Displays an error message + */ + function PMA_Schema_die($type = '',$error_message = '') + { + global $db; + + require_once './libraries/header.inc.php'; + + echo "<p><strong> {$type} " . __("SCHEMA ERROR") . "</strong></p>" . "\n"; + if (!empty($error_message)) { + $error_message = htmlspecialchars($error_message); + } + echo '<p>' . "\n"; + echo ' ' . $error_message . "\n"; + echo '</p>' . "\n"; + + echo '<a href="export_relation_schema.php?' . PMA_generate_common_url($db) + . '">' . __('Back') . '</a>'; + echo "\n"; + + require_once './libraries/footer.inc.php'; + } // end of the "PMA_PDF_die()" function +} +?> \ No newline at end of file diff --git a/libraries/schema/svg_relation_schema.php b/libraries/schema/svg_relation_schema.php new file mode 100644 index 000000000..e9a57efec --- /dev/null +++ b/libraries/schema/svg_relation_schema.php @@ -0,0 +1,127 @@ +<?php +include_once("relation_schema.php"); +class PMA_SVG extends XMLWriter +{ + public $title; + public $author; + public $font; + public $fontSize; + + function __construct() + { + //$writer = new XMLWriter(); + $this->openMemory(); + /* Set indenting using three spaces, so output is formatted */ + $this->setIndent(TRUE); + $this->setIndentString(' '); + /* Create the XML document */ + $this->startDocument('1.0','UTF-8'); + $this->startDtd('svg','-//W3C//DTD SVG 1.1//EN','http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'); + $this->endDtd(); + } + + function setTitle($title) + { + $this->title=$title; + } + + function setAuthor($author) + { + $this->author=$author; + } + + function setFont($font) + { + $this->font=$font; + } + function setFontSize($fontSize) + { + $this->fontSize=$fontSize; + } + + function startSvgDoc($width,$height) + { + + $this->startElement('svg'); + $this->writeAttribute('width', $width); + $this->writeAttribute('height', $height); + $this->writeAttribute('xmlns', 'http://www.w3.org/2000/svg'); + $this->writeAttribute('version', '1.1'); + } + + function endSvgDoc() + { + + $this->endElement(); + $this->endDocument(); + } + + function showOutput() + { + + header('Content-type: image/svg+xml'); + $output = $this->flush(); + print $output; + } + + function printElement($name,$x,$y,$width='',$height='',$text='',$styles='') + { + + $this->startElement($name); + $this->writeAttribute('width',$width); + $this->writeAttribute('height',$height); + $this->writeAttribute('x', $x); + $this->writeAttribute('y', $y); + $this->writeAttribute('style', $styles); + if(isset($text)){ + $this->writeAttribute('font-family', $this->font); + $this->writeAttribute('font-size', $this->fontSize); + $this->text($text); + } + $this->endElement(); + } +} + +class PMA_SVG_RELAION_SCHEMA extends PMA_Relation_Schema +{ + + function __construct($page_number, $show_info = 0, $change_color = 0, $all_table_same_wide = 0, $show_keys = 0) + { + //global $pdf, $db, $cfgRelation, $with_doc; + global $db,$writer; + + $svg = new PMA_SVG(); + $this->setSameWidthTables($all_table_same_wide); + + $svg->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $page_number)); + $svg->SetAuthor('phpMyAdmin ' . PMA_VERSION); + $svg->setFont('Arial'); + $svg->setFontSize('16px'); + $svg->startSvgDoc('500px','500px'); + $svg->printElement('rect',0,0,'100','100',NULL,'fill:none;stroke:black;'); + $svg->printElement('text',100,100,'100','100','this is just a test'); + $svg->endSvgDoc(); + $svg->showOutput(); + //echo $svg->getTitle(); + /* $alltables=$this->getAllTables($db,$page_number); + foreach ($alltables AS $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->_tablewidth, $show_keys, $show_info); + // $this->tables[$table]=$table; + } + + if ($this->same_wide) { + $this->tables[$table]->width = $this->_tablewidth; + } + $this->PMA_RT_setMinMax($this->tables[$table]); + }*/ + + +/* print '<pre>'; + print_r(get_object_vars($svg)); + print_r($this); + print '</pre>'; + exit();*/ + } +} +?> \ No newline at end of file diff --git a/libraries/schema/user_schema.php b/libraries/schema/user_schema.php new file mode 100644 index 000000000..af86e9117 --- /dev/null +++ b/libraries/schema/user_schema.php @@ -0,0 +1,636 @@ +<?php +/** + * This Class will interact with the user to gather the information + * about their tables for which they want to export the relational schema + */ + +class PMA_USER_SCHEMA +{ +/* private $_exportType; + public function __construct() + { + $this->_exportType='pdf'; // default export type + } + + public function setExportType($type) + { + $this->_exportType=$type; + } + + public function getExportType() + { + return $this->_exportType; + }*/ + + /** + * This function will process the user input + * + */ + public function userInputProcess($do) + { + global $action_choose,$chpage,$db,$cfgRelation,$cfg,$auto_layout_foreign,$auto_layout_internal,$newpage,$c_table_rows,$query_default_option; + // Now is the time to work on all changes + //echo $do; + if (isset($do)) { + switch ($do) { + case 'selectpage': + if ($action_choose=="1") { + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND page_nr = \'' . PMA_sqlAddslashes($chpage) . '\''; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + + unset($chpage); + } + break; + case 'createpage': + + $pdf_page_number = PMA_REL_create_page($newpage, $cfgRelation, $db, $query_default_option); + + // A u t o m a t i c l a y o u t + // ================================ + if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { + $all_tables = array(); + } + + if (isset($auto_layout_foreign)) { + // get the tables list + $tables = PMA_DBI_get_tables_full($db); + // find the ones who support FOREIGN KEY; it's not + // important that we group together InnoDB tables + // and PBXT tables, as this logic is just to put + // the tables on the layout, not to determine relations + $foreignkey_tables = array(); + foreach($tables as $table_name => $table_properties) { + if (PMA_foreignkey_supported($table_properties['ENGINE'])) { + $foreignkey_tables[] = $table_name; + } + } + $all_tables = $foreignkey_tables; + // could be improved by finding the tables which have the + // most references keys and placing them at the beginning + // of the array (so that they are all center of schema) + unset($tables, $foreignkey_tables); + } // endif auto_layout_foreign + + if (isset($auto_layout_internal)) { + // get the tables that have relations, by descending + // number of links + $master_tables = 'SELECT COUNT(master_table), master_table' + . ' FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) + . ' WHERE master_db = \'' . $db . '\'' + . ' GROUP BY master_table' + . ' ORDER BY ' . PMA_backquote('COUNT(master_table)') . ' DESC '; + $master_tables_rs = PMA_query_as_controluser($master_tables, FALSE, $query_default_option); + if ($master_tables_rs && PMA_DBI_num_rows($master_tables_rs) > 0) { + // first put all the master tables at beginning + // of the list, so they are near the center of + // the schema + while (list(, $master_table) = PMA_DBI_fetch_row($master_tables_rs)) { + $all_tables[] = $master_table; + } + + // then for each master, add its foreigns into an array + // of foreign tables, if not already there + // (a foreign might be foreign for more than + // one table, and might be a master itself) + + $foreign_tables = array(); + foreach ($all_tables as $master_table) { + $foreigners = PMA_getForeigners($db, $master_table); + foreach ($foreigners as $foreigner) { + if (!in_array($foreigner['foreign_table'], $foreign_tables)) { + $foreign_tables[] = $foreigner['foreign_table']; + } + } + } + + // then merge the arrays + foreach ($foreign_tables as $foreign_table) { + if (!in_array($foreign_table, $all_tables)) { + $all_tables[] = $foreign_table; + } + } + } // endif there are master tables + } // endif auto_layout_internal + + if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { + // now generate the coordinates for the schema, + // in a clockwise spiral + + $pos_x = 300; + $pos_y = 300; + $delta = 110; + $delta_mult = 1.10; + $direction = "right"; + foreach ($all_tables as $current_table) { + + // save current table's coordinates + $insert_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . '(db_name, table_name, pdf_page_number, x, y) ' + . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($current_table) . '\',' . $pdf_page_number . ',' . $pos_x . ',' . $pos_y . ')'; + PMA_query_as_controluser($insert_query, FALSE, $query_default_option); + + // compute for the next table + switch ($direction) { + case 'right': + $pos_x += $delta; + $direction = "down"; + $delta *= $delta_mult; + break; + case 'down': + $pos_y += $delta; + $direction = "left"; + $delta *= $delta_mult; + break; + case 'left': + $pos_x -= $delta; + $direction = "up"; + $delta *= $delta_mult; + break; + case 'up': + $pos_y -= $delta; + $direction = "right"; + $delta *= $delta_mult; + break; + } // end switch + } // end foreach + } // end if some auto-layout to do + + $chpage = $pdf_page_number; + + break; + + case 'edcoord': + for ($i = 0; $i < $c_table_rows; $i++) { + $arrvalue = 'c_table_' . $i; + global $$arrvalue; + $arrvalue = $$arrvalue; + if (!isset($arrvalue['x']) || $arrvalue['x'] == '') { + $arrvalue['x'] = 0; + } + if (!isset($arrvalue['y']) || $arrvalue['y'] == '') { + $arrvalue['y'] = 0; + } + if (isset($arrvalue['name']) && $arrvalue['name'] != '--') { + $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + // echo $test_query; + $test_rs = PMA_query_as_controluser($test_query, FALSE, $query_default_option); + if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { + if (isset($arrvalue['delete']) && $arrvalue['delete'] == 'y') { + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + } else { + $ch_query = 'UPDATE ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . 'SET x = ' . $arrvalue['x'] . ', y= ' . $arrvalue['y'] + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + } + } else { + $ch_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . '(db_name, table_name, pdf_page_number, x, y) ' + . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($arrvalue['name']) . '\', \'' . PMA_sqlAddslashes($chpage) . '\',' . $arrvalue['x'] . ',' . $arrvalue['y'] . ')'; + } + //echo $ch_query; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + } // end if + } // end for + break; + case 'deleteCrap': + foreach ($delrow as $current_row) { + $d_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' . "\n" + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . "\n" + . ' AND table_name = \'' . PMA_sqlAddslashes($current_row) . '\'' . "\n" + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + PMA_query_as_controluser($d_query, FALSE, $query_default_option); + } + break; + } // end switch + } // end if (isset($do)) + + } + + /** + * This function shows/displays the HTML FORM to create the page + * + */ + public function createPage() + { + global $db,$table; + ?> + <form method="post" action="export_relation_schema.php" name="frm_create_page"> + <fieldset> + <legend> + <?php echo __('Create a page !') . "\n"; ?> + </legend> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="do" value="createpage" /> + <table> + <tr> + <td><label for="id_newpage"><?php echo __('Page name'); ?></label></td> + <td><input type="text" name="newpage" id="id_newpage" size="20" maxlength="50" /></td> + </tr> + <tr> + <td><?php echo __('Automatic layout based on'); ?></td> + <td> + <input type="checkbox" name="auto_layout_internal" id="id_auto_layout_internal" /><label for="id_auto_layout_internal"> + <?php echo __('Internal relations'); ?></label><br /> + <?php + /** + * Check to see whether INNODB and PBXT storage engines are Available in MYSQL PACKAGE + * If available, then provide AutoLayout for Foreign Keys in Schema View + */ + if (PMA_StorageEngine::isValid('InnoDB') || PMA_StorageEngine::isValid('PBXT')) { + ?> + <input type="checkbox" name="auto_layout_foreign" id="id_auto_layout_foreign" /><label for="id_auto_layout_foreign"> + <?php echo __('FOREIGN KEY'); ?></label><br /> + <?php + } + ?> + </td></tr> + </table> + </fieldset> + <fieldset class="tblFooters"> + <input type="submit" value="<?php echo __('Go'); ?>" /> + </fieldset> + </form> + <?php + } + + /** + * This function shows/displays the created page names + * All page names in a list + * User can select any page number and edit it using dashboard etc + */ + public function selectPage() + { + global $db,$table,$query_default_option,$cfgRelation; + $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\''; + //echo $page_query; + $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); + if ($page_rs && PMA_DBI_num_rows($page_rs) > 0) { + ?> + <form method="get" action="export_relation_schema.php" name="frm_select_page"> + <fieldset> + <legend> + <?php echo __('Please select a page to edit') . "\n"; ?> + </legend> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="do" value="selectpage" /> + <select name="chpage" onchange="this.form.submit()"> + <?php + while ($curr_page = PMA_DBI_fetch_assoc($page_rs)) { + echo "\n" . ' ' + . '<option value="' . $curr_page['page_nr'] . '"'; + if (isset($chpage) && $chpage == $curr_page['page_nr']) { + echo ' selected="selected"'; + } + echo '>' . $curr_page['page_nr'] . ': ' . htmlspecialchars($curr_page['page_descr']) . '</option>'; + } // end while + echo "\n"; + ?> + </select> + <?php + $choices = array( + '0' => __('Edit'), + '1' => __('Delete')); + PMA_display_html_radio('action_choose', $choices, '0', false); + unset($choices); + ?> + </fieldset> + <fieldset class="tblFooters"> + <input type="submit" value="<?php echo __('Go'); ?>" /><br /> + </fieldset> + </form> + <?php + } // end IF + echo "\n"; + } // end function + + /** + * A dashboard will also be shown where you can position the tables + */ + public function showTableDashBoard() + { + global $db,$cfgRelation,$table,$cfg,$with_field_names,$with_field_names,$chpage; + // We will need an array of all tables in this db + $selectboxall = array('--'); + $alltab_rs = PMA_DBI_query('SHOW TABLES FROM ' . PMA_backquote($db) . ';', null, PMA_DBI_QUERY_STORE); + while ($val = @PMA_DBI_fetch_row($alltab_rs)) { + $selectboxall[] = $val[0]; + } + + // Now if we already have chosen a page number then we should show the + // tables involved + if (isset($chpage) && $chpage > 0) { + echo "\n"; + ?> + <h2><?php echo __('Select Tables') ;?></h2> + + <?php + $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); + $array_sh_page = array(); + $draginit = ''; + $reset_draginit = ''; + $i = 0; + while ($temp_sh_page = @PMA_DBI_fetch_assoc($page_rs)) { + $array_sh_page[] = $temp_sh_page; + } + + // Display WYSIWYG-PDF parts? + if ($cfg['WYSIWYG-PDF']) { + if (!isset($_POST['with_field_names']) && !isset($_POST['showwysiwyg'])) { + $with_field_names = TRUE; + } + ?> + <script type="text/javascript" src="./js/dom-drag.js"></script> + <form method="post" action="export_relation_schema.php" name="dragdrop"> + <input type="button" name="dragdrop" value="<?php echo __('Toggle scratchboard'); ?>" onclick="ToggleDragDrop('pdflayout');" /> + <input type="button" name="dragdropreset" value="<?php echo __('Reset'); ?>" onclick="resetDrag();" /> + </form> + <div id="pdflayout" class="pdflayout" style="visibility: hidden;"> + <?php + foreach ($array_sh_page as $key => $temp_sh_page) { + $drag_x = $temp_sh_page['x']; + $drag_y = $temp_sh_page['y']; + + $draginit .= ' Drag.init(getElement("table_' . $i . '"), null, 0, parseInt(myid.style.width)-2, 0, parseInt(myid.style.height)-5);' . "\n"; + $draginit .= ' getElement("table_' . $i . '").onDrag = function (x, y) { document.edcoord.elements["c_table_' . $i . '[x]"].value = parseInt(x); document.edcoord.elements["c_table_' . $i . '[y]"].value = parseInt(y) }' . "\n"; + $draginit .= ' getElement("table_' . $i . '").style.left = "' . $drag_x . 'px";' . "\n"; + $draginit .= ' getElement("table_' . $i . '").style.top = "' . $drag_y . 'px";' . "\n"; + $reset_draginit .= ' getElement("table_' . $i . '").style.left = "2px";' . "\n"; + $reset_draginit .= ' getElement("table_' . $i . '").style.top = "' . (15 * $i) . 'px";' . "\n"; + $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[x]"].value = "2"' . "\n"; + $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[y]"].value = "' . (15 * $i) . '"' . "\n"; + + $local_query = 'SHOW FIELDS FROM ' + . PMA_backquote($temp_sh_page['table_name']) + . ' FROM ' . PMA_backquote($db); + $fields_rs = PMA_DBI_query($local_query); + unset($local_query); + $fields_cnt = PMA_DBI_num_rows($fields_rs); + + echo '<div id="table_' . $i . '" class="pdflayout_table"><u>' . $temp_sh_page['table_name'] . '</u>'; + if (isset($with_field_names)) { + while ($row = PMA_DBI_fetch_assoc($fields_rs)) { + echo '<br />' . htmlspecialchars($row['Field']) . "\n"; + } + } + echo '</div>' . "\n"; + PMA_DBI_free_result($fields_rs); + unset($fields_rs); + + $i++; + } + ?> + </div> + <script type="text/javascript"> + //<![CDATA[ + function PDFinit() { + refreshLayout(); + myid = getElement('pdflayout'); + <?php echo $draginit; ?> + } + + function resetDrag() { + <?php echo $reset_draginit; ?> + } + //]]> + </script> + <?php + } // end if WYSIWYG-PDF + ?> + + <form method="post" action="export_relation_schema.php" name="edcoord"> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="chpage" value="<?php echo htmlspecialchars($chpage); ?>" /> + <input type="hidden" name="do" value="edcoord" /> + <table border="0"> + <tr> + <th><?php echo __('Table'); ?></th> + <th><?php echo __('Delete'); ?></th> + <th>X</th> + <th>Y</th> + </tr> + <?php + if (isset($ctable)) { + unset($ctable); + } + + + $i = 0; + $odd_row = true; + foreach ($array_sh_page as $dummy_sh_page => $sh_page) { + $_mtab = $sh_page['table_name']; + $tabExist[$_mtab] = FALSE; + echo "\n" . ' <tr class="'; + if ($odd_row) { + echo 'odd'; + } else { + echo 'even'; + } + echo '">'; + $odd_row != $odd_row; + echo "\n" . ' <td>' + . "\n" . ' <select name="c_table_' . $i . '[name]">'; + foreach ($selectboxall as $key => $value) { + echo "\n" . ' <option value="' . htmlspecialchars($value) . '"'; + if ($value == $sh_page['table_name']) { + echo ' selected="selected"'; + $tabExist[$_mtab] = TRUE; + } + echo '>' . htmlspecialchars($value) . '</option>'; + } // end while + echo "\n" . ' </select>' + . "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'x\', this.value)"' : '') . ' name="c_table_' . $i . '[x]" value="' . $sh_page['x'] . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'y\', this.value)"' : '') . ' name="c_table_' . $i . '[y]" value="' . $sh_page['y'] . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' </tr>'; + $i++; + } // end while + // Do one more empty row + echo "\n" . ' <tr class="'; + if ($odd_row) { + echo 'odd'; + } else { + echo 'even'; + } + $odd_row != $odd_row; + echo '">'; + echo "\n" . ' <td>' + . "\n" . ' <select name="c_table_' . $i . '[name]">'; + foreach ($selectboxall as $key => $value) { + echo "\n" . ' <option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($value) . '</option>'; + } + echo "\n" . ' </select>' + . "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" name="c_table_' . $i . '[x]" value="' . (isset($sh_page['x'])?$sh_page['x']:'') . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" name="c_table_' . $i . '[y]" value="' . (isset($sh_page['y'])?$sh_page['y']:'') . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' </tr>'; + echo "\n" . ' </table>' . "\n"; + + echo "\n" . ' <input type="hidden" name="c_table_rows" value="' . ($i + 1) . '" />'; + echo ($cfg['WYSIWYG-PDF'] ? "\n" . ' <input type="hidden" id="showwysiwyg" name="showwysiwyg" value="' . ((isset($showwysiwyg) && $showwysiwyg == '1') ? '1' : '0') . '" />' : ''); + echo "\n" . ' <input type="checkbox" name="with_field_names" ' . (isset($with_field_names) ? 'checked="checked"' : ''). ' />' . __('Column names') . '<br />'; + echo "\n" . ' <input type="submit" value="' . __('Save') . '" />'; + echo "\n" . '</form>' . "\n\n"; + } // end if + + $this->deleteTables($chpage); + + }// end function + + /** + * show Export relational schema generation options + */ + public function displaySchemaGenerationOptions() + { + global $cfg,$pmaThemeImage,$db,$test_rs,$chpage; + ?> + <!-- Export relational schema --> + <form method="post" action="process_relation_schema.php"> + <fieldset> + <legend> + <?php + echo PMA_generate_common_hidden_inputs($db); + if ($cfg['PropertiesIconic']) { + echo '<img class="icon" src="' . $pmaThemeImage . 'b_view.png"' + .' alt="" width="16" height="16" />'; + } + echo __('Display Relational Schema'); + ?>: + </legend> + + <select name="export_type" id="export_type"> + <option value="pdf" selected="selected"><?php echo __('PDF');?></option> + <option value="svg"><?php echo __('SVG');?></option> + <option value="dia"><?php echo __('DIA');?></option> + <option value="visio"><?php echo __('VISIO');?></option> + <option value="eps"><?php echo __('EPS');?></option> + </select> + <label><?php echo __('Select Export Relational Type');?></label><br /> + <?php + if (isset($test_rs)) { + ?> + <label for="pdf_page_number_opt"><?php echo __('Page number:'); ?></label> + <select name="pdf_page_number" id="pdf_page_number_opt"> + <?php + while ($pages = @PMA_DBI_fetch_assoc($test_rs)) { + echo ' <option value="' . $pages['page_nr'] . '">' + . $pages['page_nr'] . ': ' . htmlspecialchars($pages['page_descr']) . '</option>' . "\n"; + } // end while + PMA_DBI_free_result($test_rs); + unset($test_rs); + ?> + </select><br /> + <?php } else { ?> + <input type="hidden" name="pdf_page_number" value="<?php echo htmlspecialchars($chpage); ?>" /> + <?php } ?> + + <input type="checkbox" name="show_grid" id="show_grid_opt" /> + <label for="show_grid_opt"><?php echo __('Show grid'); ?></label><br /> + <input type="checkbox" name="show_color" id="show_color_opt" + checked="checked" /> + <label for="show_color_opt"><?php echo __('Show color'); ?></label><br /> + <input type="checkbox" name="show_table_dimension" id="show_table_dim_opt" /> + <label for="show_table_dim_opt"><?php echo __('Show dimension of tables'); ?> + </label><br /> + <input type="checkbox" name="all_tab_same_wide" id="all_tab_same_wide" /> + <label for="all_tab_same_wide"><?php echo __('Display all tables with the same width'); ?> + </label><br /> + <input type="checkbox" name="with_doc" id="with_doc" checked="checked" /> + <label for="with_doc"><?php echo __('Data Dictionary'); ?></label><br /> + <input type="checkbox" name="show_keys" id="show_keys" /> + <label for="show_keys"><?php echo __('Only show keys'); ?></label><br /> + + <label for="orientation_opt"><?php echo __('Data Dictionary Format'); ?></label> + <select name="orientation" id="orientation_opt"> + <option value="L"><?php echo __('Landscape');?></option> + <option value="P"><?php echo __('Portrait');?></option> + </select><br /> + + <label for="paper_opt"><?php echo __('Paper size'); ?></label> + <select name="paper" id="paper_opt"> + <?php + foreach ($cfg['PDFPageSizes'] as $key => $val) { + echo '<option value="' . $val . '"'; + if ($val == $cfg['PDFDefaultPageSize']) { + echo ' selected="selected"'; + } + echo ' >' . $val . '</option>' . "\n"; + } + ?> + </select> + </fieldset> + <fieldset class="tblFooters"> + <input type="submit" value="<?php echo __('Go'); ?>" /> + </fieldset> + </form> + <?php + }// end function + + /** + * Check if there are tables that need to be deleted in dashboard, + * if there are, ask the user for allowance + */ + public function deleteTables($chpage) + { + global $db, $table,$tabExist; + + $_strtrans = ''; + $_strname = ''; + $shoot = FALSE; + if (!empty($tabExist) && is_array($tabExist)) { + foreach ($tabExist as $key => $value) { + if (!$value) { + $_strtrans .= '<input type="hidden" name="delrow[]" value="' . htmlspecialchars($key) . '" />' . "\n"; + $_strname .= '<li>' . htmlspecialchars($key) . '</li>' . "\n"; + $shoot = TRUE; + } + } + if ($shoot) { + echo '<form action="export_relation_schema.php" method="post">' . "\n" + . PMA_generate_common_hidden_inputs($db, $table) + . '<input type="hidden" name="do" value="deleteCrap" />' . "\n" + . '<input type="hidden" name="chpage" value="' . htmlspecialchars($chpage) . '" />' . "\n" + . __('The current page has references to tables that no longer exist. Would you like to delete those references?') + . '<ul>' . "\n" + . $_strname + . '</ul>' . "\n" + . $_strtrans + . '<input type="submit" value="' . __('Go') . '" />' . "\n" + . '</form>'; + } + } + } // end function +} +?> \ No newline at end of file diff --git a/libraries/schema/visio_relation_schema.php b/libraries/schema/visio_relation_schema.php new file mode 100644 index 000000000..d188304b9 --- /dev/null +++ b/libraries/schema/visio_relation_schema.php @@ -0,0 +1,7 @@ +<?php +include_once("relationSchema.abstract.class.php"); +class PMA_PDF_RELAION_SCHEMA extends PMA_Relation_Schema +{ + +} +?> \ No newline at end of file diff --git a/process_relation_schema.php b/process_relation_schema.php new file mode 100644 index 000000000..22714de65 --- /dev/null +++ b/process_relation_schema.php @@ -0,0 +1,82 @@ +<?php +error_reporting(E_ALL | E_WARNING); +/** + * Gets some core scripts + */ +require_once './libraries/common.inc.php'; + +/** + * Settings for relation stuff + */ +require_once './libraries/relation.lib.php'; +require_once './libraries/transformations.lib.php'; +require_once './libraries/Index.class.php'; + +$cfgRelation = PMA_getRelationsParam(); + +/** + * Now in ./libraries/relation.lib.php we check for all tables + * that we need, but if we don't find them we are quiet about it + * so people can work without. + * This page is absolutely useless if you didn't set up your tables + * correctly, so it is a good place to see which tables we can and + * complain ;-) + */ +if (!$cfgRelation['pdfwork']) { + echo '<font color="red">' . __('Error') . '</font><br />' . "\n"; + $url_to_goto = '<a href="' . $cfg['PmaAbsoluteUri'] . 'chk_rel.php?' . $url_query . '">'; + echo sprintf(__('The additional features for working with linked tables have been deactivated. To find out why click %shere%s.'), $url_to_goto, '</a>') . "\n"; +} + +/** + * Main logic + */ +try + { + $pdf_page_number = isset($pdf_page_number) ? $pdf_page_number : 1; + $show_grid = (isset($show_grid) && $show_grid == 'on') ? 1 : 0; + $show_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; + $show_table_dimension = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; + $all_table_same_wide = (isset($all_table_same_wide) && $all_table_same_wide == 'on') ? 1 : 0; + $with_doc = (isset($with_doc) && $with_doc == 'on') ? 1 : 0; + $orientation = (isset($orientation) && $orientation == 'P') ? 'P' : 'L'; + $paper = isset($paper) ? $paper : 'A4'; + $show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; + $export_type = isset($export_type) ? $export_type : 'pdf'; // default is PDF + PMA_DBI_select_db($db); + + switch($export_type) + { + case 'pdf'; + include_once("./libraries/schema/pdf_relation_schema.php"); + $obj_schema=new PMA_PDF_RELAION_SCHEMA($pdf_page_number, $show_table_dimension, $show_color, + $show_grid, $all_table_same_wide, $orientation, $paper, + $show_keys + ); + break; + case 'svg'; + include_once("./libraries/schema/svg_relation_schema.php"); + $obj_schema=new PMA_SVG_RELAION_SCHEMA($pdf_page_number, $show_table_dimension, $show_color, + $all_table_same_wide,$show_keys); + break; + case 'dia'; + include_once("./libraries/schema/diaSchema.class.php"); + $obj_schema=new diaSchema(); + break; + case 'visio'; + include_once("./libraries/schema/visioSchema.class.php"); + $obj_schema=new visioSchema(); + break; + case 'eps'; + include_once("./libraries/schema/epsSchema.class.php"); + $obj_schema=new epsSchema(); + break; + } +} +catch (Exception $e) +{ + print('<pre>'); + print_r($e); + print('</pre>'); +} +?> \ No newline at end of file From dd217e8dffe87efeb6f01a9ee4210c3c3f6ab394 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Fri, 25 Jun 2010 19:14:47 +0500 Subject: [PATCH 10/48] Export Relation Schema: renaming files + renaming classes according to pear standards --- .../schema/Dia_Relation_Schema.class.php | 7 + .../schema/Eps_Relation_Schema.class.php | 7 + .../schema/Export_Relation_Schema.class.php | 71 + .../schema/Pdf_Relation_Schema.class.php | 1338 +++++++++++++++++ .../schema/Svg_Relation_Schema.class.php | 127 ++ libraries/schema/User_Schema.class.php | 636 ++++++++ .../schema/Visio_Relation_Schema.class.php | 7 + 7 files changed, 2193 insertions(+) create mode 100644 libraries/schema/Dia_Relation_Schema.class.php create mode 100644 libraries/schema/Eps_Relation_Schema.class.php create mode 100644 libraries/schema/Export_Relation_Schema.class.php create mode 100644 libraries/schema/Pdf_Relation_Schema.class.php create mode 100644 libraries/schema/Svg_Relation_Schema.class.php create mode 100644 libraries/schema/User_Schema.class.php create mode 100644 libraries/schema/Visio_Relation_Schema.class.php diff --git a/libraries/schema/Dia_Relation_Schema.class.php b/libraries/schema/Dia_Relation_Schema.class.php new file mode 100644 index 000000000..2b027e1aa --- /dev/null +++ b/libraries/schema/Dia_Relation_Schema.class.php @@ -0,0 +1,7 @@ +<?php +include_once("Export_Relation_Schema.class.php"); +class PMA_Dia_Relation_Schema extends PMA_Export_Relation_Schema +{ + +} +?> \ No newline at end of file diff --git a/libraries/schema/Eps_Relation_Schema.class.php b/libraries/schema/Eps_Relation_Schema.class.php new file mode 100644 index 000000000..fa2bef147 --- /dev/null +++ b/libraries/schema/Eps_Relation_Schema.class.php @@ -0,0 +1,7 @@ +<?php +include_once("Export_Relation_Schema.class.php"); +class PMA_Eps_Relation_Schema extends PMA_Export_Relation_Schema +{ + +} +?> \ No newline at end of file diff --git a/libraries/schema/Export_Relation_Schema.class.php b/libraries/schema/Export_Relation_Schema.class.php new file mode 100644 index 000000000..0d0659433 --- /dev/null +++ b/libraries/schema/Export_Relation_Schema.class.php @@ -0,0 +1,71 @@ +<?php +// Using Abstract Factory Pattern for exporting relational Schema in different Formats ! +abstract class PMA_Export_Relation_Schema +{ + private $_pageTitle; // Title of the page + private $_autoLayoutType; // Internal or Foreign Key Relations; + protected $same_wide; + + public function setPageTitle($title) + { + $this->_pageTitle=$title; + } + + public function setExportType($type) + { + $this->_pageTitle=$title; + } + + public function setSameWidthTables($width) + { + $this->same_wide=$width; + } + + public function getAllTables($db,$page_number) + { + global $cfgRelation; + // Get All tables + $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = ' . $page_number; + // echo $tab_sql; + $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); + if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { + $this->PMA_SCHEMA_die('',__('No tables')); + } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { + $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); + } + return $alltables; + } + + public function initTable() + { + + } + + + /** + * Displays an error message + */ + function PMA_Schema_die($type = '',$error_message = '') + { + global $db; + + require_once './libraries/header.inc.php'; + + echo "<p><strong> {$type} " . __("SCHEMA ERROR") . "</strong></p>" . "\n"; + if (!empty($error_message)) { + $error_message = htmlspecialchars($error_message); + } + echo '<p>' . "\n"; + echo ' ' . $error_message . "\n"; + echo '</p>' . "\n"; + + echo '<a href="export_relation_schema.php?' . PMA_generate_common_url($db) + . '">' . __('Back') . '</a>'; + echo "\n"; + + require_once './libraries/footer.inc.php'; + } // end of the "PMA_PDF_die()" function +} +?> \ No newline at end of file diff --git a/libraries/schema/Pdf_Relation_Schema.class.php b/libraries/schema/Pdf_Relation_Schema.class.php new file mode 100644 index 000000000..f94aa1a1d --- /dev/null +++ b/libraries/schema/Pdf_Relation_Schema.class.php @@ -0,0 +1,1338 @@ +<?php +include_once("Export_Relation_Schema.class.php"); +/** + * Font used in PDF. + * + * @todo Make this configuratble (at least Sans/Serif). + */ +define('PMA_PDF_FONT', 'DejaVuSans'); +require_once './libraries/tcpdf/tcpdf.php'; + +/** + * Extends the "FPDF" class and prepares the work + * + * @access public + * @see FPDF + * @package phpMyAdmin + */ +class PMA_PDF extends TCPDF { + /** + * Defines private properties + */ + var $x_min; + var $y_min; + var $l_marg = 10; + var $t_marg = 10; + var $scale; + var $PMA_links; + var $Outlines = array(); + var $def_outlines; + var $Alias = array(); + var $widths; + + public function getFh() + { + return $this->fh; + } + + public function getFw() + { + return $this->fw; + } + + public function setCMargin($c_margin) + { + $this->cMargin = $c_margin; + } + + function SetAlias($name, $value) + { + $this->Alias[$name] = $value ; + } + + function _putpages() + { + if (count($this->Alias) > 0) { + $nb = $this->page; + foreach ($this->Alias as $alias => $value) { + for ($n = 1;$n <= $nb;$n++) + $this->pages[$n]=str_replace($alias, $value, $this->pages[$n]); + } + } + parent::_putpages(); + } + + /** + * Sets the scaling factor, defines minimum coordinates and margins + * + * @param float scale The scaling factor + * @param float x_min The minimum X coordinate + * @param float y_min The minimum Y coordinate + * @param float l_marg The left margin + * @param float t_marg The top margin + * @access public + */ + function PMA_PDF_setScale($scale = 1, $x_min = 0, $y_min = 0, $l_marg = -1, $t_marg = -1) + { + $this->scale = $scale; + $this->x_min = $x_min; + $this->y_min = $y_min; + if ($this->l_marg != -1) { + $this->l_marg = $l_marg; + } + if ($this->t_marg != -1) { + $this->t_marg = $t_marg; + } + } // end of the "PMA_PDF_setScale" function + /** + * Outputs a scaled cell + * + * @param float w The cell width + * @param float h The cell height + * @param string txt The text to output + * @param mixed border Whether to add borders or not + * @param integer ln Where to put the cursor once the output is done + * @param string align Align mode + * @param integer fill Whether to fill the cell with a color or not + * @access public + * @see FPDF::Cell() + */ + function PMA_PDF_cellScale($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '') + { + $h = $h / $this->scale; + $w = $w / $this->scale; + $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link); + } // end of the "PMA_PDF_cellScale" function + /** + * Draws a scaled line + * + * @param float x1 The horizontal position of the starting point + * @param float y1 The vertical position of the starting point + * @param float x2 The horizontal position of the ending point + * @param float y2 The vertical position of the ending point + * @access public + * @see FPDF::Line() + */ + function PMA_PDF_lineScale($x1, $y1, $x2, $y2) + { + $x1 = ($x1 - $this->x_min) / $this->scale + $this->l_marg; + $y1 = ($y1 - $this->y_min) / $this->scale + $this->t_marg; + $x2 = ($x2 - $this->x_min) / $this->scale + $this->l_marg; + $y2 = ($y2 - $this->y_min) / $this->scale + $this->t_marg; + $this->Line($x1, $y1, $x2, $y2); + } // end of the "PMA_PDF_lineScale" function + /** + * Sets x and y scaled positions + * + * @param float x The x position + * @param float y The y position + * @access public + * @see FPDF::SetXY() + */ + function PMA_PDF_setXyScale($x, $y) + { + $x = ($x - $this->x_min) / $this->scale + $this->l_marg; + $y = ($y - $this->y_min) / $this->scale + $this->t_marg; + $this->SetXY($x, $y); + } // end of the "PMA_PDF_setXyScale" function + /** + * Sets the X scaled positions + * + * @param float x The x position + * @access public + * @see FPDF::SetX() + */ + function PMA_PDF_setXScale($x) + { + $x = ($x - $this->x_min) / $this->scale + $this->l_marg; + $this->SetX($x); + } // end of the "PMA_PDF_setXScale" function + /** + * Sets the scaled font size + * + * @param float size The font size (in points) + * @access public + * @see FPDF::SetFontSize() + */ + function PMA_PDF_setFontSizeScale($size) + { + // Set font size in points + $size = $size / $this->scale; + $this->SetFontSize($size); + } // end of the "PMA_PDF_setFontSizeScale" function + /** + * Sets the scaled line width + * + * @param float width The line width + * @access public + * @see FPDF::SetLineWidth() + */ + function PMA_PDF_setLineWidthScale($width) + { + $width = $width / $this->scale; + $this->SetLineWidth($width); + } // end of the "PMA_PDF_setLineWidthScale" function + /** + * Displays an error message + * + * @param string error_message the error mesage + * @global array the PMA configuration array + * @global integer the current server id + * @global string the current language + * @global string the charset to convert to + * @global string the current database name + * @global string the current charset + * @global string the current text direction + * @global string a localized string + * @global string an other localized string + * @access public + */ +/* function PMA_PDF_die($error_message = '') + { + global $cfg; + global $server, $lang, $convcharset, $db; + global $charset, $text_dir; + + require_once './libraries/header.inc.php'; + + echo '<p><strong>PDF - ' . __('Error') . '</strong></p>' . "\n"; + if (!empty($error_message)) { + $error_message = htmlspecialchars($error_message); + } + echo '<p>' . "\n"; + echo ' ' . $error_message . "\n"; + echo '</p>' . "\n"; + + echo '<a href="db_structure.php?' . PMA_generate_common_url($db) + . '">' . __('Back') . '</a>'; + echo "\n"; + + require_once './libraries/footer.inc.php'; + }*/ // end of the "PMA_PDF_die()" function + /** + * Aliases the "Error()" function from the FPDF class to the + * "PMA_PDF_die()" one + * + * @param string error_message the error mesage + * @access public + * @see PMA_PDF_die + */ + function Error($error_message = '') + { + PMA_Relation_Schema::PMA_Schema_die($error_message); + } // end of the "Error()" method + function Header() + { + // We only show this if we find something in the new pdf_pages table + + // This function must be named "Header" to work with the FPDF library + global $cfgRelation, $db, $pdf_page_number, $with_doc; + if ($with_doc) { + $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND page_nr = \'' . $pdf_page_number . '\''; + $test_rs = PMA_query_as_controluser($test_query); + $pages = @PMA_DBI_fetch_assoc($test_rs); + $this->SetFont('', 'B', 14); + $this->Cell(0, 6, ucfirst($pages['page_descr']), 'B', 1, 'C'); + $this->SetFont('', ''); + $this->Ln(); + } + } + function Footer() + { + // This function must be named "Footer" to work with the FPDF library + global $with_doc; + if ($with_doc) { + $this->SetY(-15); + $this->SetFont('', '', 14); + $this->Cell(0, 6, __('Page number:') . ' ' . $this->PageNo() . '/{nb}', 'T', 0, 'C'); + $this->Cell(0, 6, PMA_localisedDate(), 0, 1, 'R'); + $this->SetY(20); + } + } + function Bookmark($txt, $level = 0, $y = 0) + { + // Add a bookmark + $this->Outlines[0][] = $level; + $this->Outlines[1][] = $txt; + $this->Outlines[2][] = $this->page; + if ($y == -1) { + $y = $this->GetY(); + } + $this->Outlines[3][] = round($this->hPt - $y * $this->k, 2); + } + + function _putbookmarks() + { + if (count($this->Outlines) > 0) { + // Save object number + $memo_n = $this->n; + // Take the number of sub elements for an outline + $nb_outlines = sizeof($this->Outlines[0]); + $first_level = array(); + $parent = array(); + $parent[0] = 1; + for ($i = 0; $i < $nb_outlines; $i++) { + $level = $this->Outlines[0][$i]; + $kids = 0; + $last = -1; + $prev = -1; + $next = -1; + if ($i > 0) { + $cursor = $i-1; + // Take the previous outline in the same level + while ($this->Outlines[0][$cursor] > $level && $cursor > 0) + $cursor--; + if ($this->Outlines[0][$cursor] == $level) { + $prev = $cursor; + } + } + if ($i < $nb_outlines-1) { + $cursor = $i + 1; + while (isset($this->Outlines[0][$cursor]) && $this->Outlines[0][$cursor] > $level) { + // Take the immediate kid in level + 1 + if ($this->Outlines[0][$cursor] == $level + 1) { + $kids++; + $last = $cursor; + } + $cursor++; + } + $cursor = $i + 1; + // Take the next outline in the same level + while ($this->Outlines[0][$cursor] > $level && ($cursor + 1 < sizeof($this->Outlines[0]))) + $cursor++; + if ($this->Outlines[0][$cursor] == $level) { + $next = $cursor; + } + } + $this->_newobj(); + $parent[$level + 1] = $this->n; + if ($level == 0) { + $first_level[] = $this->n; + } + $this->_out('<<'); + $this->_out('/Title (' . $this->Outlines[1][$i] . ')'); + $this->_out('/Parent ' . $parent[$level] . ' 0 R'); + if ($prev != -1) { + $this->_out('/Prev ' . ($memo_n + $prev + 1) . ' 0 R'); + } + if ($next != -1) { + $this->_out('/Next ' . ($this->n + $next - $i) . ' 0 R'); + } + $this->_out('/Dest [' . (1 + (2 * $this->Outlines[2][$i])) . ' 0 R /XYZ null ' . $this->Outlines[3][$i] . ' null]'); + if ($kids > 0) { + $this->_out('/First ' . ($this->n + 1) . ' 0 R'); + $this->_out('/Last ' . ($this->n + $last - $i) . ' 0 R'); + $this->_out('/Count -' . $kids); + }tur + $this->_out('>>'); + $this->_out('endobj'); + } + // First page of outlines + $this->_newobj(); + $this->def_outlines = $this->n; + $this->_out('<<'); + $this->_out('/Type'); + $this->_out('/Outlines'); + $this->_out('/First ' . $first_level[0] . ' 0 R'); + $this->_out('/Last ' . $first_level[sizeof($first_level)-1] . ' 0 R'); + $this->_out('/Count ' . sizeof($first_level)); + $this->_out('>>'); + $this->_out('endobj'); + } + } + + function _putresources() + { + parent::_putresources(); + $this->_putbookmarks(); + } + + function _putcatalog() + { + parent::_putcatalog(); + if (count($this->Outlines) > 0) { + $this->_out('/Outlines ' . $this->def_outlines . ' 0 R'); + $this->_out('/PageMode /UseOutlines'); + } + } + function SetWidths($w) + { + // column widths + $this->widths = $w; + } + + function Row($data, $links) + { + // line height + $nb = 0; + $data_cnt = count($data); + for ($i = 0;$i < $data_cnt;$i++) + $nb = max($nb, $this->NbLines($this->widths[$i], $data[$i])); + $il = $this->FontSize; + $h = ($il + 1) * $nb; + // page break if necessary + $this->CheckPageBreak($h); + // draw the cells + $data_cnt = count($data); + for ($i = 0;$i < $data_cnt;$i++) { + $w = $this->widths[$i]; + // save current position + $x = $this->GetX(); + $y = $this->GetY(); + // draw the border + $this->Rect($x, $y, $w, $h); + if (isset($links[$i])) { + $this->Link($x, $y, $w, $h, $links[$i]); + } + // print text + $this->MultiCell($w, $il + 1, $data[$i], 0, 'L'); + // go to right side + $this->SetXY($x + $w, $y); + } + // go to line + $this->Ln($h); + } + + function CheckPageBreak($h) + { + // if height h overflows, manual page break + if ($this->GetY() + $h > $this->PageBreakTrigger) { + $this->AddPage($this->CurOrientation); + } + } + + function NbLines($w, $txt) + { + // compute number of lines used by a multicell of width w + $cw = &$this->CurrentFont['cw']; + if ($w == 0) { + $w = $this->w - $this->rMargin - $this->x; + } + $wmax = ($w-2 * $this->cMargin) * 1000 / $this->FontSize; + $s = str_replace("\r", '', $txt); + $nb = strlen($s); + if ($nb > 0 and $s[$nb-1] == "\n") { + $nb--; + } + $sep = -1; + $i = 0; + $j = 0; + $l = 0; + $nl = 1; + while ($i < $nb) { + $c = $s[$i]; + if ($c == "\n") { + $i++; + $sep = -1; + $j = $i; + $l = 0; + $nl++; + continue; + } + if ($c == ' ') { + $sep = $i; + } + $l += isset($cw[ord($c)])?$cw[ord($c)]:0 ; + if ($l > $wmax) { + if ($sep == -1) { + if ($i == $j) { + $i++; + } + } else { + $i = $sep + 1; + } + $sep = -1; + $j = $i; + $l = 0; + $nl++; + } else { + $i++; + } + } + return $nl; + } +} // end of the "PMA_PDF" class + +/** + * Draws tables schema + * + * @access private + * @see PMA_RT + * @package phpMyAdmin + */ +class PMA_RT_Table { + /** + * Defines private properties + */ + var $nb_fiels; + var $table_name; + var $width = 0; + var $height; + var $fields = array(); + var $height_cell = 6; + var $x, $y; + var $primary = array(); + var $show_info = false; + + /** + * Returns title of the current table, + * title can have the dimensions of the table + * + * @access private + */ + function getTitle() + { + return ($this->show_info ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->height) : '') . ' ' . $this->table_name; + } // end of the "getTitle" function + /** + * Sets the width of the table + * + * @param integer ff The font size + * @global object The current PDF document + * @access private + * @see PMA_PDF + */ + function PMA_RT_Table_setWidth($ff) + { + global $pdf; + + foreach ($this->fields as $field) { + $this->width = max($this->width, $pdf->GetStringWidth($field)); + } + //echo $this->width.'<br />'; + $this->width += $pdf->GetStringWidth(' '); + //echo $this->width.'<br />'; + $pdf->SetFont($ff, 'B'); + // it is unknown what value must be added, because + // table title is affected by the tabe width value + //echo $this->getTitle(); + while ($this->width < $pdf->GetStringWidth($this->getTitle())) { + $this->width += 5; + } + //echo $this->width.'<br />'; + $pdf->SetFont($ff, ''); + } // end of the "PMA_RT_Table_setWidth()" method + /** + * Sets the height of the table + * + * @access private + */ + function PMA_RT_Table_setHeight() + { + $this->height = (count($this->fields) + 1) * $this->height_cell; + } // end of the "PMA_RT_Table_setHeight()" method + /** + * Do draw the table + * + * @param integer ff The font size + * @param boolean setcolortWhether to display color + * @global object The current PDF document + * @access private + * @see PMA_PDF + */ + function PMA_RT_Table_draw($ff, $setcolor = 0) + { + global $pdf, $with_doc; + + $pdf->PMA_PDF_setXyScale($this->x, $this->y); + $pdf->SetFont($ff, 'B'); + if ($setcolor) { + $pdf->SetTextColor(200); + $pdf->SetFillColor(0, 0, 128); + } + if ($with_doc) { + $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name]['-'], -1); + } else { + $pdf->PMA_links['doc'][$this->table_name]['-'] = ''; + } + + $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, $this->getTitle(), 1, 1, 'C', $setcolor, $pdf->PMA_links['doc'][$this->table_name]['-']); + $pdf->PMA_PDF_setXScale($this->x); + $pdf->SetFont($ff, ''); + $pdf->SetTextColor(0); + $pdf->SetFillColor(255); + + foreach ($this->fields as $field) { + if ($setcolor) { + if (in_array($field, $this->primary)) { + $pdf->SetFillColor(215, 121, 123); + } + if ($field == $this->displayfield) { + $pdf->SetFillColor(142, 159, 224); + } + } + if ($with_doc) { + $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name][$field], -1); + } else { + $pdf->PMA_links['doc'][$this->table_name][$field] = ''; + } + + $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, ' ' . $field, 1, 1, 'L', $setcolor, $pdf->PMA_links['doc'][$this->table_name][$field]); + $pdf->PMA_PDF_setXScale($this->x); + $pdf->SetFillColor(255); + } // end while + /*if ($pdf->PageNo() > 1) { + $pdf->PMA_PDF_die(__('The scale factor is too small to fit the schema on one page')); + } */ + } // end of the "PMA_RT_Table_draw()" method + /** + * The "PMA_RT_Table" constructor + * + * @param string table_name The table name + * @param integer ff The font size + * @param integer same_width The max. with among tables + * @param boolean show_keys Whether to display keys or not + * @param boolean show_info Whether to display table position or not + * @global object The current PDF document + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @global array The relations settings + * @global string The current db name + * @access private + * @see PMA_PDF, PMA_RT_Table::PMA_RT_Table_setWidth, + PMA_RT_Table::PMA_RT_Table_setHeight + */ + function __construct($table_name, $ff, &$same_wide_width, $show_keys = false, $show_info = false) + { + global $pdf, $pdf_page_number, $cfgRelation, $db; + + $this->table_name = $table_name; + $sql = 'DESCRIBE ' . PMA_backquote($table_name); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $pdf->PMA_PDF_die(sprintf(__('The %s table doesn\'t exist!'), $table_name)); + } + // load fields + //check to see if it will load all fields or only the foreign keys + if ($show_keys) { + $indexes = PMA_Index::getFromTable($this->table_name, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $this->show_info = $show_info; + + // height and width + $this->PMA_RT_Table_setHeight(); + // setWidth must me after setHeight, because title + // can include table height which changes table width + $this->PMA_RT_Table_setWidth($ff); + if ($same_wide_width < $this->width) { + $same_wide_width = $this->width; + } + // x and y + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($table_name) . '\'' + . ' AND pdf_page_number = ' . $pdf_page_number; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + + if (!$result || !PMA_DBI_num_rows($result)) { + $pdf->PMA_PDF_die(sprintf(__('Please configure the coordinates for table %s'), $table_name)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + // displayfield + $this->displayfield = PMA_getDisplayField($db, $table_name); + // index + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($table_name) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } // end if + } // end of the "PMA_RT_Table()" method +} // end class "PMA_RT_Table" + +/** + * Draws relation links + * + * @access private + * @see PMA_RT + * @package phpMyAdmin + */ +class PMA_RT_Relation { + /** + * Defines private properties + */ + var $x_src, $y_src; + var $src_dir ; + var $dest_dir; + var $x_dest, $y_dest; + var $w_tick = 5; + + /** + * Gets arrows coordinates + * + * @param string table The current table name + * @param string column The relation column name + * @return array Arrows coordinates + * @access private + */ + function PMA_RT_Relation_getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // x_left, x_right, y + return array($table->x, $table->x + + $table->width, $table->y + ($pos + 1.5) * $table->height_cell); + } // end of the "PMA_RT_Relation_getXy()" method + /** + * Do draws relation links + * + * @param boolean change_color Whether to use one color per relation or not + * @param integer i The id of the link to draw + * @global object The current PDF document + * @access private + * @see PMA_PDF + */ + function PMA_RT_Relation_draw($change_color, $i) + { + global $pdf; + + if ($change_color) { + $d = $i % 6; + $j = ($i - $d) / 6; + $j = $j % 4; + $j++; + $case = array( + array(1, 0, 0), + array(0, 1, 0), + array(0, 0, 1), + array(1, 1, 0), + array(1, 0, 1), + array(0, 1, 1) + ); + list ($a, $b, $c) = $case[$d]; + $e = (1 - ($j - 1) / 6); + $pdf->SetDrawColor($a * 255 * $e, $b * 255 * $e, $c * 255 * $e); + } else { + $pdf->SetDrawColor(0); + } // end if... else... + $pdf->PMA_PDF_setLineWidthScale(0.2); + $pdf->PMA_PDF_lineScale($this->x_src, $this->y_src, $this->x_src + $this->src_dir * $this->w_tick, $this->y_src); + $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest, $this->x_dest, $this->y_dest); + $pdf->PMA_PDF_setLineWidthScale(0.1); + $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick, $this->y_src, $this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest); + // arrow + $root2 = 2 * sqrt(2); + $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src + $this->w_tick / $root2); + $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src - $this->w_tick / $root2); + + $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest + $this->w_tick / $root2); + $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest - $this->w_tick / $root2); + $pdf->SetDrawColor(0); + } // end of the "PMA_RT_Relation_draw()" method + /** + * The "PMA_RT_Relation" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @access private + * @see PMA_RT_Relation::PMA_RT_Relation_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->PMA_RT_Relation_getXy($master_table, $master_field); + $dest_pos = $this->PMA_RT_Relation_getXy($foreign_table, $foreign_field); + $src_left = $src_pos[0] - $this->w_tick; + $src_right = $src_pos[1] + $this->w_tick; + $dest_left = $dest_pos[0] - $this->w_tick; + $dest_right = $dest_pos[1] + $this->w_tick; + + $d1 = abs($src_left - $dest_left); + $d2 = abs($src_right - $dest_left); + $d3 = abs($src_left - $dest_right); + $d4 = abs($src_right - $dest_right); + $d = min($d1, $d2, $d3, $d4); + + if ($d == $d1) { + $this->x_src = $src_pos[0]; + $this->src_dir = -1; + $this->x_dest = $dest_pos[0]; + $this->dest_dir = -1; + } elseif ($d == $d2) { + $this->x_src = $src_pos[1]; + $this->src_dir = 1; + $this->x_dest = $dest_pos[0]; + $this->dest_dir = -1; + } elseif ($d == $d3) { + $this->x_src = $src_pos[0]; + $this->src_dir = -1; + $this->x_dest = $dest_pos[1]; + $this->dest_dir = 1; + } else { + $this->x_src = $src_pos[1]; + $this->src_dir = 1; + $this->x_dest = $dest_pos[1]; + $this->dest_dir = 1; + } + $this->y_src = $src_pos[2]; + $this->y_dest = $dest_pos[2]; + } // end of the "PMA_RT_Relation()" method +} // end of the "PMA_RT_Relation" class + +class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema +{ + /** + * Defines private properties + */ + private $tables = array(); + private $relations = array(); + private $ff = PMA_PDF_FONT; + private $x_max = 0; + private $y_max = 0; + private $scale; + private $x_min = 100000; + private $y_min = 100000; + private $t_marg = 10; + private $b_marg = 10; + private $l_marg = 10; + private $r_marg = 10; + private $_tablewidth; + + /** + * The "PMA_RT" constructor + * + * @param integer page_number The page number to draw (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @param boolean show_info Whether to display table position or not + * @param boolean change_color Was originally whether to use one color per + * relation or not, now enables/disables color + * everywhere, due to some problems printing with color + * @param boolean show_grid Whether to draw grids or not + * @param boolean all_tab_same_wide Whether all tables should have the same width or not + * @param boolean show_keys Wheter to show all field or only the keys + * @global object The current PDF document + * @global string The current db name + * @global array The relations settings + * @access private + * @see PMA_PDF + */ + function __construct($page_number, $show_info = 0, $change_color = 0, $show_grid = 0, $all_table_same_wide = 0, $orientation = 'L', $paper = 'A4', $show_keys = 0) + { + global $pdf, $db, $cfgRelation, $with_doc; + + $this->setSameWidthTables($all_table_same_wide); + + // Initializes a new document + $pdf = new PMA_PDF('L', 'mm', $paper); + $pdf->SetTitle(sprintf(__('Schema of the %s database - Page %s'), $GLOBALS['db'], $page_number)); + $pdf->setCMargin(0); + $pdf->Open(); + $pdf->SetAuthor('phpMyAdmin ' . PMA_VERSION); + $pdf->AliasNbPages(); + $pdf->AddFont('DejaVuSans', '', 'dejavusans.php'); + $pdf->AddFont('DejaVuSans', 'B', 'dejavusansb.php'); + $pdf->AddFont('DejaVuSerif', '', 'dejavuserif.php'); + $pdf->AddFont('DejaVuSerif', 'B', 'dejavuserifb.php'); + // $this->ff = PMA_PDF_FONT; + + $pdf->SetFont($this->ff, '', 14); + $pdf->SetAutoPageBreak('auto'); + + + // Gets tables on this page +/* $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = ' . $page_number; + echo $tab_sql; + $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); + if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { + $pdf->PMA_PDF_die(__('No tables')); + // die('No tables'); + } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { + $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); + // $intable = '\'' . implode('\', \'', $alltables) . '\''; + }*/ + $alltables=$this->getAllTables($db,$page_number); + + + // make doc // + if ($with_doc) { + $pdf->SetAutoPageBreak('auto', 15); + $pdf->setCMargin(1); + $this->PMA_RT_DOC($alltables); + $pdf->SetAutoPageBreak('auto'); + $pdf->setCMargin(0); + } + + $pdf->Addpage(); + + if ($with_doc) { + $pdf->SetLink($pdf->PMA_links['RT']['-'], -1); + $pdf->Bookmark(__('Relational schema')); + $pdf->SetAlias('{00}', $pdf->PageNo()) ; + $this->t_marg = 28; + $this->b_marg = 28; + } + + /* snip */ + + foreach ($alltables as $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->_tablewidth, $show_keys, $show_info); + // $this->tables[$table]=$table; + } + + if ($this->same_wide) { + $this->tables[$table]->width = $this->_tablewidth; + } + $this->PMA_RT_setMinMax($this->tables[$table]); + } + + // Defines the scale factor + $this->scale = ceil( + max( + ($this->x_max - $this->x_min) / ($pdf->getFh() - $this->r_marg - $this->l_marg), + ($this->y_max - $this->y_min) / ($pdf->getFw() - $this->t_marg - $this->b_marg)) + * 100) / 100; + + $pdf->PMA_PDF_setScale($this->scale, $this->x_min, $this->y_min, $this->l_marg, $this->t_marg); + // Builds and save the PDF document + $pdf->PMA_PDF_setLineWidthScale(0.1); + + if ($show_grid) { + $pdf->SetFontSize(10); + $this->PMA_RT_strokeGrid(); + } + $pdf->PMA_PDF_setFontSizeScale(14); + // $sql = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) + // . ' WHERE master_db = \'' . PMA_sqlAddslashes($db) . '\' ' + // . ' AND foreign_db = \'' . PMA_sqlAddslashes($db) . '\' ' + // . ' AND master_table IN (' . $intable . ')' + // . ' AND foreign_table IN (' . $intable . ')'; + // $result = PMA_query_as_controluser($sql); + + // previous logic was checking master tables and foreign tables + // but I think that looping on every table of the pdf page as a master + // and finding its foreigns is OK (then we can support innodb) + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + // put the foreign table on the schema only if selected + // by the user + // (do not use array_search() because we would have to + // to do a === FALSE and this is not PHP3 compatible) + if (in_array($rel['foreign_table'], $alltables)) { + $this->PMA_RT_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'], $show_info); + } + } // end while + } // end if + } // end while + +/* print '<pre>'; + print_r(get_object_vars($this)); + print '</pre>'; + exit();*/ + + if ($seen_a_relation) { + $this->PMA_RT_drawRelations($change_color); + } + + $this->PMA_RT_drawTables($change_color); + + $this->PMA_RT_showRt(); + } // end of the "PMA_RT()" Constructor method + /** + * Sets X and Y minimum and maximum for a table cell + * + * @param string table The table name + * @access private + */ + function PMA_RT_setMinMax($table) + { + $this->x_max = max($this->x_max, $table->x + $table->width); + $this->y_max = max($this->y_max, $table->y + $table->height); + $this->x_min = min($this->x_min, $table->x); + $this->y_min = min($this->y_min, $table->y); + } // end of the "PMA_RT_setMinMax()" method + /** + * Defines relation objects + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreign_field The relation field in the foreign table + * @param boolean show_info Whether to display table position or not + * @access private + * @see PMA_RT_setMinMax + */ + function PMA_RT_addRelation($master_table, $master_field, $foreign_table, $foreign_field, $show_info) + { + if (!isset($this->tables[$master_table])) { + $this->tables[$master_table] = new PMA_RT_Table($master_table, $this->ff, $this->_tablewidth, false, $show_info); + $this->PMA_RT_setMinMax($this->tables[$master_table]); + } + if (!isset($this->tables[$foreign_table])) { + $this->tables[$foreign_table] = new PMA_RT_Table($foreign_table, $this->ff, $this->_tablewidth, false, $show_info); + $this->PMA_RT_setMinMax($this->tables[$foreign_table]); + } + $this->relations[] = new PMA_RT_Relation($this->tables[$master_table], $master_field, $this->tables[$foreign_table], $foreign_field); + } // end of the "PMA_RT_addRelation()" method + /** + * Draws the grid + * + * @global object the current PMA_PDF instance + * @access private + * @see PMA_PDF + */ + function PMA_RT_strokeGrid() + { + global $pdf; + + $pdf->SetMargins(0, 0); + $pdf->SetDrawColor(200, 200, 200); + // Draws horizontal lines + for ($l = 0; $l < 21; $l++) { + $pdf->line(0, $l * 10, $pdf->getFh(), $l * 10); + // Avoid duplicates + if ($l > 0) { + $pdf->SetXY(0, $l * 10); + $label = (string) sprintf('%.0f', ($l * 10 - $this->t_marg) * $this->scale + $this->y_min); + $pdf->Cell(5, 5, ' ' . $label); + } // end if + } // end for + // Draws vertical lines + for ($j = 0; $j < 30 ;$j++) { + $pdf->line($j * 10, 0, $j * 10, $pdf->getFw()); + $pdf->SetXY($j * 10, 0); + $label = (string) sprintf('%.0f', ($j * 10 - $this->l_marg) * $this->scale + $this->x_min); + $pdf->Cell(5, 7, $label); + } // end for + } // end of the "PMA_RT_strokeGrid()" method + /** + * Draws relation arrows + * + * @param boolean change_color Whether to use one color per relation or not + * @access private + * @see PMA_RT_Relation::PMA_RT_Relation_draw() + */ + function PMA_RT_drawRelations($change_color) + { + $i = 0; + foreach ($this->relations as $relation) { + $relation->PMA_RT_Relation_draw($change_color, $i); + $i++; + } // end while + } // end of the "PMA_RT_drawRelations()" method + /** + * Draws tables + * + * @param boolean draw_color Whether to display table position or not + * @access private + * @see PMA_RT_Table::PMA_RT_Table_draw() + */ + function PMA_RT_drawTables($draw_color = 0) + { + foreach ($this->tables as $table) { + $table->PMA_RT_Table_draw($this->ff, $draw_color); + } + } // end of the "PMA_RT_drawTables()" method + /** + * Ouputs the PDF document to a file + * + * @global object The current PDF document + * @global string The current database name + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @access private + * @see PMA_PDF + */ + function PMA_RT_showRt() + { + global $pdf, $db, $pdf_page_number, $cfgRelation; + + $pdf->SetFontSize(14); + $pdf->SetLineWidth(0.2); + $pdf->SetDisplayMode('fullpage'); + // Get the name of this pdfpage to use as filename (Mike Beck) + $_name_sql = 'SELECT page_descr FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE page_nr = ' . $pdf_page_number; + $_name_rs = PMA_query_as_controluser($_name_sql); + if ($_name_rs) { + $_name_row = PMA_DBI_fetch_row($_name_rs); + $filename = $_name_row[0] . '.pdf'; + } + // i don't know if there is a chance for this to happen, but rather be on the safe side: + if (empty($filename)) { + $filename = $pdf_page_number . '.pdf'; + } + // $pdf->Output($db . '_' . $filename, TRUE); + $pdf->Output($db . '_' . $filename, 'I'); // destination: Inline + } // end of the "PMA_RT_showRt()" method + + function PMA_RT_DOC($alltables) +{ + global $db, $pdf, $orientation, $paper; + // TOC + $pdf->addpage($GLOBALS['orientation']); + $pdf->Cell(0, 9, __('Table of contents'), 1, 0, 'C'); + $pdf->Ln(15); + $i = 1; + foreach ($alltables as $table) { + $pdf->PMA_links['doc'][$table]['-'] = $pdf->AddLink(); + $pdf->SetX(10); + // $pdf->Ln(1); + $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$table]['-']); + $pdf->SetX(10); + $pdf->Cell(0, 6, $i . ' ' . $table, 0, 1, 'L', 0, $pdf->PMA_links['doc'][$table]['-']); + // $pdf->Ln(1); + $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';'); + while ($row = PMA_DBI_fetch_assoc($result)) { + $pdf->SetX(20); + $field_name = $row['Field']; + $pdf->PMA_links['doc'][$table][$field_name] = $pdf->AddLink(); + // $pdf->Cell(0, 6, $field_name,0,1,'L',0, $pdf->PMA_links['doc'][$table][$field_name]); + } + $lasttable = $table; + $i++; + } + $pdf->PMA_links['RT']['-'] = $pdf->AddLink(); + $pdf->SetX(10); + $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$lasttable]['-']); + $pdf->SetX(10); + $pdf->Cell(0, 6, $i + 1 . ' ' . __('Relational schema'), 0, 1, 'L', 0, $pdf->PMA_links['RT']['-']); + $z = 0; + foreach ($alltables as $table) { + $z++; + $pdf->addpage($GLOBALS['orientation']); + $pdf->Bookmark($table); + $pdf->SetAlias('{' . sprintf("%02d", $z) . '}', $pdf->PageNo()) ; + $pdf->PMA_links['RT'][$table]['-'] = $pdf->AddLink(); + $pdf->SetLink($pdf->PMA_links['doc'][$table]['-'], -1); + $pdf->SetFont('', 'B', 18); + $pdf->Cell(0, 8, $z . ' ' . $table, 1, 1, 'C', 0, $pdf->PMA_links['RT'][$table]['-']); + $pdf->SetFont('', '', 8); + $pdf->ln(); + + $cfgRelation = PMA_getRelationsParam(); + $comments = PMA_getComments($db, $table); + if ($cfgRelation['mimework']) { + $mime_map = PMA_getMIME($db, $table, true); + } + + /** + * Gets table informations + */ + $showtable = PMA_Table::sGetStatusInfo($db, $table); + $num_rows = (isset($showtable['Rows']) ? $showtable['Rows'] : 0); + $show_comment = (isset($showtable['Comment']) ? $showtable['Comment'] : ''); + $create_time = (isset($showtable['Create_time']) ? PMA_localisedDate(strtotime($showtable['Create_time'])) : ''); + $update_time = (isset($showtable['Update_time']) ? PMA_localisedDate(strtotime($showtable['Update_time'])) : ''); + $check_time = (isset($showtable['Check_time']) ? PMA_localisedDate(strtotime($showtable['Check_time'])) : ''); + + /** + * Gets table keys and retains them + */ + $result = PMA_DBI_query('SHOW KEYS FROM ' . PMA_backquote($table) . ';'); + $primary = ''; + $indexes = array(); + $lastIndex = ''; + $indexes_info = array(); + $indexes_data = array(); + $pk_array = array(); // will be use to emphasis prim. keys in the table + // view + while ($row = PMA_DBI_fetch_assoc($result)) { + // Backups the list of primary keys + if ($row['Key_name'] == 'PRIMARY') { + $primary .= $row['Column_name'] . ', '; + $pk_array[$row['Column_name']] = 1; + } + // Retains keys informations + if ($row['Key_name'] != $lastIndex) { + $indexes[] = $row['Key_name']; + $lastIndex = $row['Key_name']; + } + $indexes_info[$row['Key_name']]['Sequences'][] = $row['Seq_in_index']; + $indexes_info[$row['Key_name']]['Non_unique'] = $row['Non_unique']; + if (isset($row['Cardinality'])) { + $indexes_info[$row['Key_name']]['Cardinality'] = $row['Cardinality']; + } + // I don't know what does following column mean.... + // $indexes_info[$row['Key_name']]['Packed'] = $row['Packed']; + $indexes_info[$row['Key_name']]['Comment'] = $row['Comment']; + + $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Column_name'] = $row['Column_name']; + if (isset($row['Sub_part'])) { + $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Sub_part'] = $row['Sub_part']; + } + } // end while + if ($result) { + PMA_DBI_free_result($result); + } + + /** + * Gets fields properties + */ + $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE); + $fields_cnt = PMA_DBI_num_rows($result); + // Check if we can use Relations (Mike Beck) + if (!empty($cfgRelation['relation'])) { + // Find which tables are related with the current one and write it in + // an array + $res_rel = PMA_getForeigners($db, $table); + + if (count($res_rel) > 0) { + $have_rel = true; + } else { + $have_rel = false; + } + } else { + $have_rel = false; + } // end if + /** + * Displays the comments of the table if MySQL >= 3.23 + */ + + $break = false; + if (!empty($show_comment)) { + $pdf->Cell(0, 3, __('Table comments') . ' : ' . $show_comment, 0, 1); + $break = true; + } + + if (!empty($create_time)) { + $pdf->Cell(0, 3, __('Creation') . ': ' . $create_time, 0, 1); + $break = true; + } + + if (!empty($update_time)) { + $pdf->Cell(0, 3, __('Last update') . ': ' . $update_time, 0, 1); + $break = true; + } + + if (!empty($check_time)) { + $pdf->Cell(0, 3, __('Last check') . ': ' . $check_time, 0, 1); + $break = true; + } + + if ($break == true) { + $pdf->Cell(0, 3, '', 0, 1); + $pdf->Ln(); + } + + $pdf->SetFont('', 'B'); + if (isset($orientation) && $orientation == 'L') { + $pdf->Cell(25, 8, ucfirst(__('Column')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); + $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Default')), 1, 0, 'C'); + $pdf->Cell(25, 8, ucfirst(__('Extra')), 1, 0, 'C'); + $pdf->Cell(45, 8, ucfirst(__('Links to')), 1, 0, 'C'); + + if ($paper == 'A4') { + $comments_width = 67; + } else { + // this is really intended for 'letter' + /** + * @todo find optimal width for all formats + */ + $comments_width = 50; + } + $pdf->Cell($comments_width, 8, ucfirst(__('Comments')), 1, 0, 'C'); + $pdf->Cell(45, 8, 'MIME', 1, 1, 'C'); + $pdf->SetWidths(array(25, 20, 20, 10, 20, 25, 45, $comments_width, 45)); + } else { + $pdf->Cell(20, 8, ucfirst(__('Column')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); + $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); + $pdf->Cell(15, 8, ucfirst(__('Default')), 1, 0, 'C'); + $pdf->Cell(15, 8, ucfirst(__('Extra')), 1, 0, 'C'); + $pdf->Cell(30, 8, ucfirst(__('Links to')), 1, 0, 'C'); + $pdf->Cell(30, 8, ucfirst(__('Comments')), 1, 0, 'C'); + $pdf->Cell(30, 8, 'MIME', 1, 1, 'C'); + $pdf->SetWidths(array(20, 20, 20, 10, 15, 15, 30, 30, 30)); + } + $pdf->SetFont('', ''); + + while ($row = PMA_DBI_fetch_assoc($result)) { + $type = $row['Type']; + // reformat mysql query output + // set or enum types: slashes single quotes inside options + if (preg_match('@^(set|enum)\((.+)\)$@i', $type, $tmp)) { + $tmp[2] = substr(preg_replace("@([^,])''@", "\\1\\'", ',' . $tmp[2]), 1); + $type = $tmp[1] . '(' . str_replace(',', ', ', $tmp[2]) . ')'; + $type_nowrap = ''; + + $binary = 0; + $unsigned = 0; + $zerofill = 0; + } else { + $type_nowrap = ' nowrap="nowrap"'; + $type = preg_replace('@BINARY@i', '', $type); + $type = preg_replace('@ZEROFILL@i', '', $type); + $type = preg_replace('@UNSIGNED@i', '', $type); + if (empty($type)) { + $type = '&nbsp;'; + } + + $binary = stristr($row['Type'], 'BINARY'); + $unsigned = stristr($row['Type'], 'UNSIGNED'); + $zerofill = stristr($row['Type'], 'ZEROFILL'); + } + $attribute = ' '; + if ($binary) { + $attribute = 'BINARY'; + } + if ($unsigned) { + $attribute = 'UNSIGNED'; + } + if ($zerofill) { + $attribute = 'UNSIGNED ZEROFILL'; + } + if (!isset($row['Default'])) { + if ($row['Null'] != '' && $row['Null'] != 'NO') { + $row['Default'] = 'NULL'; + } + } + $field_name = $row['Field']; + // $pdf->Ln(); + $pdf->PMA_links['RT'][$table][$field_name] = $pdf->AddLink(); + $pdf->Bookmark($field_name, 1, -1); + $pdf->SetLink($pdf->PMA_links['doc'][$table][$field_name], -1); + $pdf_row = array($field_name, + $type, + $attribute, + ($row['Null'] == '' || $row['Null'] == 'NO') ? __('No') : __('Yes'), + ((isset($row['Default'])) ? $row['Default'] : ''), + $row['Extra'], + ((isset($res_rel[$field_name])) ? $res_rel[$field_name]['foreign_table'] . ' -> ' . $res_rel[$field_name]['foreign_field'] : ''), + ((isset($comments[$field_name])) ? $comments[$field_name] : ''), + ((isset($mime_map) && isset($mime_map[$field_name])) ? str_replace('_', '/', $mime_map[$field_name]['mimetype']) : '') + ); + $links[0] = $pdf->PMA_links['RT'][$table][$field_name]; + if (isset($res_rel[$field_name]['foreign_table']) AND + isset($res_rel[$field_name]['foreign_field']) AND + isset($pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]) + ) + { + $links[6] = $pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]; + } else { + unset($links[6]); + } + $pdf->Row($pdf_row, $links); + } // end while + $pdf->SetFont('', '', 14); + PMA_DBI_free_result($result); + } //end each + } +} +?> \ No newline at end of file diff --git a/libraries/schema/Svg_Relation_Schema.class.php b/libraries/schema/Svg_Relation_Schema.class.php new file mode 100644 index 000000000..c37a86c6b --- /dev/null +++ b/libraries/schema/Svg_Relation_Schema.class.php @@ -0,0 +1,127 @@ +<?php +include_once("Export_Relation_Schema.class.php"); +class PMA_SVG extends XMLWriter +{ + public $title; + public $author; + public $font; + public $fontSize; + + function __construct() + { + //$writer = new XMLWriter(); + $this->openMemory(); + /* Set indenting using three spaces, so output is formatted */ + $this->setIndent(TRUE); + $this->setIndentString(' '); + /* Create the XML document */ + $this->startDocument('1.0','UTF-8'); + $this->startDtd('svg','-//W3C//DTD SVG 1.1//EN','http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'); + $this->endDtd(); + } + + function setTitle($title) + { + $this->title=$title; + } + + function setAuthor($author) + { + $this->author=$author; + } + + function setFont($font) + { + $this->font=$font; + } + function setFontSize($fontSize) + { + $this->fontSize=$fontSize; + } + + function startSvgDoc($width,$height) + { + + $this->startElement('svg'); + $this->writeAttribute('width', $width); + $this->writeAttribute('height', $height); + $this->writeAttribute('xmlns', 'http://www.w3.org/2000/svg'); + $this->writeAttribute('version', '1.1'); + } + + function endSvgDoc() + { + + $this->endElement(); + $this->endDocument(); + } + + function showOutput() + { + + header('Content-type: image/svg+xml'); + $output = $this->flush(); + print $output; + } + + function printElement($name,$x,$y,$width='',$height='',$text='',$styles='') + { + + $this->startElement($name); + $this->writeAttribute('width',$width); + $this->writeAttribute('height',$height); + $this->writeAttribute('x', $x); + $this->writeAttribute('y', $y); + $this->writeAttribute('style', $styles); + if(isset($text)){ + $this->writeAttribute('font-family', $this->font); + $this->writeAttribute('font-size', $this->fontSize); + $this->text($text); + } + $this->endElement(); + } +} + +class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema +{ + + function __construct($page_number, $show_info = 0, $change_color = 0, $all_table_same_wide = 0, $show_keys = 0) + { + //global $pdf, $db, $cfgRelation, $with_doc; + global $db,$writer; + + $svg = new PMA_SVG(); + $this->setSameWidthTables($all_table_same_wide); + + $svg->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $page_number)); + $svg->SetAuthor('phpMyAdmin ' . PMA_VERSION); + $svg->setFont('Arial'); + $svg->setFontSize('16px'); + $svg->startSvgDoc('500px','500px'); + $svg->printElement('rect',0,0,'100','100',NULL,'fill:none;stroke:black;'); + $svg->printElement('text',100,100,'100','100','this is just a test'); + $svg->endSvgDoc(); + $svg->showOutput(); + //echo $svg->getTitle(); + /* $alltables=$this->getAllTables($db,$page_number); + foreach ($alltables AS $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->_tablewidth, $show_keys, $show_info); + // $this->tables[$table]=$table; + } + + if ($this->same_wide) { + $this->tables[$table]->width = $this->_tablewidth; + } + $this->PMA_RT_setMinMax($this->tables[$table]); + }*/ + + +/* print '<pre>'; + print_r(get_object_vars($svg)); + print_r($this); + print '</pre>'; + exit();*/ + } +} +?> \ No newline at end of file diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php new file mode 100644 index 000000000..723ce3d69 --- /dev/null +++ b/libraries/schema/User_Schema.class.php @@ -0,0 +1,636 @@ +<?php +/** + * This Class will interact with the user to gather the information + * about their tables for which they want to export the relational schema + */ + +class PMA_User_Schema +{ +/* private $_exportType; + public function __construct() + { + $this->_exportType='pdf'; // default export type + } + + public function setExportType($type) + { + $this->_exportType=$type; + } + + public function getExportType() + { + return $this->_exportType; + }*/ + + /** + * This function will process the user input + * + */ + public function userInputProcess($do) + { + global $action_choose,$chpage,$db,$cfgRelation,$cfg,$auto_layout_foreign,$auto_layout_internal,$newpage,$c_table_rows,$query_default_option; + // Now is the time to work on all changes + //echo $do; + if (isset($do)) { + switch ($do) { + case 'selectpage': + if ($action_choose=="1") { + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND page_nr = \'' . PMA_sqlAddslashes($chpage) . '\''; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + + unset($chpage); + } + break; + case 'createpage': + + $pdf_page_number = PMA_REL_create_page($newpage, $cfgRelation, $db, $query_default_option); + + // A u t o m a t i c l a y o u t + // ================================ + if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { + $all_tables = array(); + } + + if (isset($auto_layout_foreign)) { + // get the tables list + $tables = PMA_DBI_get_tables_full($db); + // find the ones who support FOREIGN KEY; it's not + // important that we group together InnoDB tables + // and PBXT tables, as this logic is just to put + // the tables on the layout, not to determine relations + $foreignkey_tables = array(); + foreach($tables as $table_name => $table_properties) { + if (PMA_foreignkey_supported($table_properties['ENGINE'])) { + $foreignkey_tables[] = $table_name; + } + } + $all_tables = $foreignkey_tables; + // could be improved by finding the tables which have the + // most references keys and placing them at the beginning + // of the array (so that they are all center of schema) + unset($tables, $foreignkey_tables); + } // endif auto_layout_foreign + + if (isset($auto_layout_internal)) { + // get the tables that have relations, by descending + // number of links + $master_tables = 'SELECT COUNT(master_table), master_table' + . ' FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) + . ' WHERE master_db = \'' . $db . '\'' + . ' GROUP BY master_table' + . ' ORDER BY ' . PMA_backquote('COUNT(master_table)') . ' DESC '; + $master_tables_rs = PMA_query_as_controluser($master_tables, FALSE, $query_default_option); + if ($master_tables_rs && PMA_DBI_num_rows($master_tables_rs) > 0) { + // first put all the master tables at beginning + // of the list, so they are near the center of + // the schema + while (list(, $master_table) = PMA_DBI_fetch_row($master_tables_rs)) { + $all_tables[] = $master_table; + } + + // then for each master, add its foreigns into an array + // of foreign tables, if not already there + // (a foreign might be foreign for more than + // one table, and might be a master itself) + + $foreign_tables = array(); + foreach ($all_tables as $master_table) { + $foreigners = PMA_getForeigners($db, $master_table); + foreach ($foreigners as $foreigner) { + if (!in_array($foreigner['foreign_table'], $foreign_tables)) { + $foreign_tables[] = $foreigner['foreign_table']; + } + } + } + + // then merge the arrays + foreach ($foreign_tables as $foreign_table) { + if (!in_array($foreign_table, $all_tables)) { + $all_tables[] = $foreign_table; + } + } + } // endif there are master tables + } // endif auto_layout_internal + + if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { + // now generate the coordinates for the schema, + // in a clockwise spiral + + $pos_x = 300; + $pos_y = 300; + $delta = 110; + $delta_mult = 1.10; + $direction = "right"; + foreach ($all_tables as $current_table) { + + // save current table's coordinates + $insert_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . '(db_name, table_name, pdf_page_number, x, y) ' + . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($current_table) . '\',' . $pdf_page_number . ',' . $pos_x . ',' . $pos_y . ')'; + PMA_query_as_controluser($insert_query, FALSE, $query_default_option); + + // compute for the next table + switch ($direction) { + case 'right': + $pos_x += $delta; + $direction = "down"; + $delta *= $delta_mult; + break; + case 'down': + $pos_y += $delta; + $direction = "left"; + $delta *= $delta_mult; + break; + case 'left': + $pos_x -= $delta; + $direction = "up"; + $delta *= $delta_mult; + break; + case 'up': + $pos_y -= $delta; + $direction = "right"; + $delta *= $delta_mult; + break; + } // end switch + } // end foreach + } // end if some auto-layout to do + + $chpage = $pdf_page_number; + + break; + + case 'edcoord': + for ($i = 0; $i < $c_table_rows; $i++) { + $arrvalue = 'c_table_' . $i; + global $$arrvalue; + $arrvalue = $$arrvalue; + if (!isset($arrvalue['x']) || $arrvalue['x'] == '') { + $arrvalue['x'] = 0; + } + if (!isset($arrvalue['y']) || $arrvalue['y'] == '') { + $arrvalue['y'] = 0; + } + if (isset($arrvalue['name']) && $arrvalue['name'] != '--') { + $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + // echo $test_query; + $test_rs = PMA_query_as_controluser($test_query, FALSE, $query_default_option); + if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { + if (isset($arrvalue['delete']) && $arrvalue['delete'] == 'y') { + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + } else { + $ch_query = 'UPDATE ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . 'SET x = ' . $arrvalue['x'] . ', y= ' . $arrvalue['y'] + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + } + } else { + $ch_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . '(db_name, table_name, pdf_page_number, x, y) ' + . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($arrvalue['name']) . '\', \'' . PMA_sqlAddslashes($chpage) . '\',' . $arrvalue['x'] . ',' . $arrvalue['y'] . ')'; + } + //echo $ch_query; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + } // end if + } // end for + break; + case 'deleteCrap': + foreach ($delrow as $current_row) { + $d_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' . "\n" + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . "\n" + . ' AND table_name = \'' . PMA_sqlAddslashes($current_row) . '\'' . "\n" + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + PMA_query_as_controluser($d_query, FALSE, $query_default_option); + } + break; + } // end switch + } // end if (isset($do)) + + } + + /** + * This function shows/displays the HTML FORM to create the page + * + */ + public function createPage() + { + global $db,$table; + ?> + <form method="post" action="export_relation_schema.php" name="frm_create_page"> + <fieldset> + <legend> + <?php echo __('Create a page !') . "\n"; ?> + </legend> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="do" value="createpage" /> + <table> + <tr> + <td><label for="id_newpage"><?php echo __('Page name'); ?></label></td> + <td><input type="text" name="newpage" id="id_newpage" size="20" maxlength="50" /></td> + </tr> + <tr> + <td><?php echo __('Automatic layout based on'); ?></td> + <td> + <input type="checkbox" name="auto_layout_internal" id="id_auto_layout_internal" /><label for="id_auto_layout_internal"> + <?php echo __('Internal relations'); ?></label><br /> + <?php + /** + * Check to see whether INNODB and PBXT storage engines are Available in MYSQL PACKAGE + * If available, then provide AutoLayout for Foreign Keys in Schema View + */ + if (PMA_StorageEngine::isValid('InnoDB') || PMA_StorageEngine::isValid('PBXT')) { + ?> + <input type="checkbox" name="auto_layout_foreign" id="id_auto_layout_foreign" /><label for="id_auto_layout_foreign"> + <?php echo __('FOREIGN KEY'); ?></label><br /> + <?php + } + ?> + </td></tr> + </table> + </fieldset> + <fieldset class="tblFooters"> + <input type="submit" value="<?php echo __('Go'); ?>" /> + </fieldset> + </form> + <?php + } + + /** + * This function shows/displays the created page names + * All page names in a list + * User can select any page number and edit it using dashboard etc + */ + public function selectPage() + { + global $db,$table,$query_default_option,$cfgRelation; + $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\''; + //echo $page_query; + $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); + if ($page_rs && PMA_DBI_num_rows($page_rs) > 0) { + ?> + <form method="get" action="export_relation_schema.php" name="frm_select_page"> + <fieldset> + <legend> + <?php echo __('Please select a page to edit') . "\n"; ?> + </legend> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="do" value="selectpage" /> + <select name="chpage" onchange="this.form.submit()"> + <?php + while ($curr_page = PMA_DBI_fetch_assoc($page_rs)) { + echo "\n" . ' ' + . '<option value="' . $curr_page['page_nr'] . '"'; + if (isset($chpage) && $chpage == $curr_page['page_nr']) { + echo ' selected="selected"'; + } + echo '>' . $curr_page['page_nr'] . ': ' . htmlspecialchars($curr_page['page_descr']) . '</option>'; + } // end while + echo "\n"; + ?> + </select> + <?php + $choices = array( + '0' => __('Edit'), + '1' => __('Delete')); + PMA_display_html_radio('action_choose', $choices, '0', false); + unset($choices); + ?> + </fieldset> + <fieldset class="tblFooters"> + <input type="submit" value="<?php echo __('Go'); ?>" /><br /> + </fieldset> + </form> + <?php + } // end IF + echo "\n"; + } // end function + + /** + * A dashboard will also be shown where you can position the tables + */ + public function showTableDashBoard() + { + global $db,$cfgRelation,$table,$cfg,$with_field_names,$with_field_names,$chpage; + // We will need an array of all tables in this db + $selectboxall = array('--'); + $alltab_rs = PMA_DBI_query('SHOW TABLES FROM ' . PMA_backquote($db) . ';', null, PMA_DBI_QUERY_STORE); + while ($val = @PMA_DBI_fetch_row($alltab_rs)) { + $selectboxall[] = $val[0]; + } + + // Now if we already have chosen a page number then we should show the + // tables involved + if (isset($chpage) && $chpage > 0) { + echo "\n"; + ?> + <h2><?php echo __('Select Tables') ;?></h2> + + <?php + $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); + $array_sh_page = array(); + $draginit = ''; + $reset_draginit = ''; + $i = 0; + while ($temp_sh_page = @PMA_DBI_fetch_assoc($page_rs)) { + $array_sh_page[] = $temp_sh_page; + } + + // Display WYSIWYG-PDF parts? + if ($cfg['WYSIWYG-PDF']) { + if (!isset($_POST['with_field_names']) && !isset($_POST['showwysiwyg'])) { + $with_field_names = TRUE; + } + ?> + <script type="text/javascript" src="./js/dom-drag.js"></script> + <form method="post" action="export_relation_schema.php" name="dragdrop"> + <input type="button" name="dragdrop" value="<?php echo __('Toggle scratchboard'); ?>" onclick="ToggleDragDrop('pdflayout');" /> + <input type="button" name="dragdropreset" value="<?php echo __('Reset'); ?>" onclick="resetDrag();" /> + </form> + <div id="pdflayout" class="pdflayout" style="visibility: hidden;"> + <?php + foreach ($array_sh_page as $key => $temp_sh_page) { + $drag_x = $temp_sh_page['x']; + $drag_y = $temp_sh_page['y']; + + $draginit .= ' Drag.init(getElement("table_' . $i . '"), null, 0, parseInt(myid.style.width)-2, 0, parseInt(myid.style.height)-5);' . "\n"; + $draginit .= ' getElement("table_' . $i . '").onDrag = function (x, y) { document.edcoord.elements["c_table_' . $i . '[x]"].value = parseInt(x); document.edcoord.elements["c_table_' . $i . '[y]"].value = parseInt(y) }' . "\n"; + $draginit .= ' getElement("table_' . $i . '").style.left = "' . $drag_x . 'px";' . "\n"; + $draginit .= ' getElement("table_' . $i . '").style.top = "' . $drag_y . 'px";' . "\n"; + $reset_draginit .= ' getElement("table_' . $i . '").style.left = "2px";' . "\n"; + $reset_draginit .= ' getElement("table_' . $i . '").style.top = "' . (15 * $i) . 'px";' . "\n"; + $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[x]"].value = "2"' . "\n"; + $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[y]"].value = "' . (15 * $i) . '"' . "\n"; + + $local_query = 'SHOW FIELDS FROM ' + . PMA_backquote($temp_sh_page['table_name']) + . ' FROM ' . PMA_backquote($db); + $fields_rs = PMA_DBI_query($local_query); + unset($local_query); + $fields_cnt = PMA_DBI_num_rows($fields_rs); + + echo '<div id="table_' . $i . '" class="pdflayout_table"><u>' . $temp_sh_page['table_name'] . '</u>'; + if (isset($with_field_names)) { + while ($row = PMA_DBI_fetch_assoc($fields_rs)) { + echo '<br />' . htmlspecialchars($row['Field']) . "\n"; + } + } + echo '</div>' . "\n"; + PMA_DBI_free_result($fields_rs); + unset($fields_rs); + + $i++; + } + ?> + </div> + <script type="text/javascript"> + //<![CDATA[ + function PDFinit() { + refreshLayout(); + myid = getElement('pdflayout'); + <?php echo $draginit; ?> + } + + function resetDrag() { + <?php echo $reset_draginit; ?> + } + //]]> + </script> + <?php + } // end if WYSIWYG-PDF + ?> + + <form method="post" action="export_relation_schema.php" name="edcoord"> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="chpage" value="<?php echo htmlspecialchars($chpage); ?>" /> + <input type="hidden" name="do" value="edcoord" /> + <table border="0"> + <tr> + <th><?php echo __('Table'); ?></th> + <th><?php echo __('Delete'); ?></th> + <th>X</th> + <th>Y</th> + </tr> + <?php + if (isset($ctable)) { + unset($ctable); + } + + + $i = 0; + $odd_row = true; + foreach ($array_sh_page as $dummy_sh_page => $sh_page) { + $_mtab = $sh_page['table_name']; + $tabExist[$_mtab] = FALSE; + echo "\n" . ' <tr class="'; + if ($odd_row) { + echo 'odd'; + } else { + echo 'even'; + } + echo '">'; + $odd_row != $odd_row; + echo "\n" . ' <td>' + . "\n" . ' <select name="c_table_' . $i . '[name]">'; + foreach ($selectboxall as $key => $value) { + echo "\n" . ' <option value="' . htmlspecialchars($value) . '"'; + if ($value == $sh_page['table_name']) { + echo ' selected="selected"'; + $tabExist[$_mtab] = TRUE; + } + echo '>' . htmlspecialchars($value) . '</option>'; + } // end while + echo "\n" . ' </select>' + . "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'x\', this.value)"' : '') . ' name="c_table_' . $i . '[x]" value="' . $sh_page['x'] . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'y\', this.value)"' : '') . ' name="c_table_' . $i . '[y]" value="' . $sh_page['y'] . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' </tr>'; + $i++; + } // end while + // Do one more empty row + echo "\n" . ' <tr class="'; + if ($odd_row) { + echo 'odd'; + } else { + echo 'even'; + } + $odd_row != $odd_row; + echo '">'; + echo "\n" . ' <td>' + . "\n" . ' <select name="c_table_' . $i . '[name]">'; + foreach ($selectboxall as $key => $value) { + echo "\n" . ' <option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($value) . '</option>'; + } + echo "\n" . ' </select>' + . "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" name="c_table_' . $i . '[x]" value="' . (isset($sh_page['x'])?$sh_page['x']:'') . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" name="c_table_' . $i . '[y]" value="' . (isset($sh_page['y'])?$sh_page['y']:'') . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' </tr>'; + echo "\n" . ' </table>' . "\n"; + + echo "\n" . ' <input type="hidden" name="c_table_rows" value="' . ($i + 1) . '" />'; + echo ($cfg['WYSIWYG-PDF'] ? "\n" . ' <input type="hidden" id="showwysiwyg" name="showwysiwyg" value="' . ((isset($showwysiwyg) && $showwysiwyg == '1') ? '1' : '0') . '" />' : ''); + echo "\n" . ' <input type="checkbox" name="with_field_names" ' . (isset($with_field_names) ? 'checked="checked"' : ''). ' />' . __('Column names') . '<br />'; + echo "\n" . ' <input type="submit" value="' . __('Save') . '" />'; + echo "\n" . '</form>' . "\n\n"; + } // end if + + $this->deleteTables($chpage); + + }// end function + + /** + * show Export relational schema generation options + */ + public function displaySchemaGenerationOptions() + { + global $cfg,$pmaThemeImage,$db,$test_rs,$chpage; + ?> + <!-- Export relational schema --> + <form method="post" action="process_relation_schema.php"> + <fieldset> + <legend> + <?php + echo PMA_generate_common_hidden_inputs($db); + if ($cfg['PropertiesIconic']) { + echo '<img class="icon" src="' . $pmaThemeImage . 'b_view.png"' + .' alt="" width="16" height="16" />'; + } + echo __('Display Relational Schema'); + ?>: + </legend> + + <select name="export_type" id="export_type"> + <option value="pdf" selected="selected"><?php echo __('PDF');?></option> + <option value="svg"><?php echo __('SVG');?></option> + <option value="dia"><?php echo __('DIA');?></option> + <option value="visio"><?php echo __('VISIO');?></option> + <option value="eps"><?php echo __('EPS');?></option> + </select> + <label><?php echo __('Select Export Relational Type');?></label><br /> + <?php + if (isset($test_rs)) { + ?> + <label for="pdf_page_number_opt"><?php echo __('Page number:'); ?></label> + <select name="pdf_page_number" id="pdf_page_number_opt"> + <?php + while ($pages = @PMA_DBI_fetch_assoc($test_rs)) { + echo ' <option value="' . $pages['page_nr'] . '">' + . $pages['page_nr'] . ': ' . htmlspecialchars($pages['page_descr']) . '</option>' . "\n"; + } // end while + PMA_DBI_free_result($test_rs); + unset($test_rs); + ?> + </select><br /> + <?php } else { ?> + <input type="hidden" name="pdf_page_number" value="<?php echo htmlspecialchars($chpage); ?>" /> + <?php } ?> + + <input type="checkbox" name="show_grid" id="show_grid_opt" /> + <label for="show_grid_opt"><?php echo __('Show grid'); ?></label><br /> + <input type="checkbox" name="show_color" id="show_color_opt" + checked="checked" /> + <label for="show_color_opt"><?php echo __('Show color'); ?></label><br /> + <input type="checkbox" name="show_table_dimension" id="show_table_dim_opt" /> + <label for="show_table_dim_opt"><?php echo __('Show dimension of tables'); ?> + </label><br /> + <input type="checkbox" name="all_tab_same_wide" id="all_tab_same_wide" /> + <label for="all_tab_same_wide"><?php echo __('Display all tables with the same width'); ?> + </label><br /> + <input type="checkbox" name="with_doc" id="with_doc" checked="checked" /> + <label for="with_doc"><?php echo __('Data Dictionary'); ?></label><br /> + <input type="checkbox" name="show_keys" id="show_keys" /> + <label for="show_keys"><?php echo __('Only show keys'); ?></label><br /> + + <label for="orientation_opt"><?php echo __('Data Dictionary Format'); ?></label> + <select name="orientation" id="orientation_opt"> + <option value="L"><?php echo __('Landscape');?></option> + <option value="P"><?php echo __('Portrait');?></option> + </select><br /> + + <label for="paper_opt"><?php echo __('Paper size'); ?></label> + <select name="paper" id="paper_opt"> + <?php + foreach ($cfg['PDFPageSizes'] as $key => $val) { + echo '<option value="' . $val . '"'; + if ($val == $cfg['PDFDefaultPageSize']) { + echo ' selected="selected"'; + } + echo ' >' . $val . '</option>' . "\n"; + } + ?> + </select> + </fieldset> + <fieldset class="tblFooters"> + <input type="submit" value="<?php echo __('Go'); ?>" /> + </fieldset> + </form> + <?php + }// end function + + /** + * Check if there are tables that need to be deleted in dashboard, + * if there are, ask the user for allowance + */ + public function deleteTables($chpage) + { + global $db, $table,$tabExist; + + $_strtrans = ''; + $_strname = ''; + $shoot = FALSE; + if (!empty($tabExist) && is_array($tabExist)) { + foreach ($tabExist as $key => $value) { + if (!$value) { + $_strtrans .= '<input type="hidden" name="delrow[]" value="' . htmlspecialchars($key) . '" />' . "\n"; + $_strname .= '<li>' . htmlspecialchars($key) . '</li>' . "\n"; + $shoot = TRUE; + } + } + if ($shoot) { + echo '<form action="export_relation_schema.php" method="post">' . "\n" + . PMA_generate_common_hidden_inputs($db, $table) + . '<input type="hidden" name="do" value="deleteCrap" />' . "\n" + . '<input type="hidden" name="chpage" value="' . htmlspecialchars($chpage) . '" />' . "\n" + . __('The current page has references to tables that no longer exist. Would you like to delete those references?') + . '<ul>' . "\n" + . $_strname + . '</ul>' . "\n" + . $_strtrans + . '<input type="submit" value="' . __('Go') . '" />' . "\n" + . '</form>'; + } + } + } // end function +} +?> \ No newline at end of file diff --git a/libraries/schema/Visio_Relation_Schema.class.php b/libraries/schema/Visio_Relation_Schema.class.php new file mode 100644 index 000000000..e20c383b9 --- /dev/null +++ b/libraries/schema/Visio_Relation_Schema.class.php @@ -0,0 +1,7 @@ +<?php +include_once("Export_Relation_Schema.class.php"); +class PMA_Visio_Relation_Schema extends PMA_Export_Relation_Schema +{ + +} +?> \ No newline at end of file From cba214aee41a6da0e24b559d32fbb15a1d6bbb64 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Sat, 26 Jun 2010 11:16:11 +0500 Subject: [PATCH 11/48] Export Relation Schema: deleting wrong name files --- export_relation_schema.php | 2 +- libraries/schema/diaSchema.class.php | 7 - libraries/schema/dia_relation_schema.php | 7 - libraries/schema/epsSchema.class.php | 7 - libraries/schema/eps_relation_chema.php | 7 - libraries/schema/pdfSchema.class.php | 9 - libraries/schema/pdf_relation_schema.php | 1338 ----------------- .../schema/relationSchema.abstract.class.php | 53 - libraries/schema/relation_schema.php | 71 - libraries/schema/svgSchema.class.php | 7 - libraries/schema/svg_relation_schema.php | 127 -- libraries/schema/user_schema.php | 636 -------- libraries/schema/visioSchema.class.php | 7 - libraries/schema/visio_relation_schema.php | 7 - process_relation_schema.php | 14 +- 15 files changed, 8 insertions(+), 2291 deletions(-) delete mode 100644 libraries/schema/diaSchema.class.php delete mode 100644 libraries/schema/dia_relation_schema.php delete mode 100644 libraries/schema/epsSchema.class.php delete mode 100644 libraries/schema/eps_relation_chema.php delete mode 100644 libraries/schema/pdfSchema.class.php delete mode 100644 libraries/schema/pdf_relation_schema.php delete mode 100644 libraries/schema/relationSchema.abstract.class.php delete mode 100644 libraries/schema/relation_schema.php delete mode 100644 libraries/schema/svgSchema.class.php delete mode 100644 libraries/schema/svg_relation_schema.php delete mode 100644 libraries/schema/user_schema.php delete mode 100644 libraries/schema/visioSchema.class.php delete mode 100644 libraries/schema/visio_relation_schema.php diff --git a/export_relation_schema.php b/export_relation_schema.php index 9c35609e2..c3c985283 100644 --- a/export_relation_schema.php +++ b/export_relation_schema.php @@ -69,7 +69,7 @@ if ($cfgRelation['pdfwork']) { */ require_once './libraries/schema/user_schema.php'; - $user_schema = new PMA_USER_SCHEMA(); + $user_schema = new PMA_User_Schema(); /** * This function will process the user input diff --git a/libraries/schema/diaSchema.class.php b/libraries/schema/diaSchema.class.php deleted file mode 100644 index e0fb1ccd2..000000000 --- a/libraries/schema/diaSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class diaSchema extends exportRelationSchema -{ - -} -?> \ No newline at end of file diff --git a/libraries/schema/dia_relation_schema.php b/libraries/schema/dia_relation_schema.php deleted file mode 100644 index f7567ebde..000000000 --- a/libraries/schema/dia_relation_schema.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class PMA_DIA_RELAION_SCHEMA extends PMA_Relation_Schema -{ - -} -?> \ No newline at end of file diff --git a/libraries/schema/epsSchema.class.php b/libraries/schema/epsSchema.class.php deleted file mode 100644 index 3e0b942a0..000000000 --- a/libraries/schema/epsSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class epsSchema extends exportRelationSchema -{ - -} -?> \ No newline at end of file diff --git a/libraries/schema/eps_relation_chema.php b/libraries/schema/eps_relation_chema.php deleted file mode 100644 index d188304b9..000000000 --- a/libraries/schema/eps_relation_chema.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class PMA_PDF_RELAION_SCHEMA extends PMA_Relation_Schema -{ - -} -?> \ No newline at end of file diff --git a/libraries/schema/pdfSchema.class.php b/libraries/schema/pdfSchema.class.php deleted file mode 100644 index cd9cfe14b..000000000 --- a/libraries/schema/pdfSchema.class.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class pdfSchema extends exportRelationSchema -{ - -} -$pdf= new pdfSchema(); -$pdf->createPageHTML(); -?> \ No newline at end of file diff --git a/libraries/schema/pdf_relation_schema.php b/libraries/schema/pdf_relation_schema.php deleted file mode 100644 index 5fc107826..000000000 --- a/libraries/schema/pdf_relation_schema.php +++ /dev/null @@ -1,1338 +0,0 @@ -<?php -include_once("relation_schema.php"); -/** - * Font used in PDF. - * - * @todo Make this configuratble (at least Sans/Serif). - */ -define('PMA_PDF_FONT', 'DejaVuSans'); -require_once './libraries/tcpdf/tcpdf.php'; - -/** - * Extends the "FPDF" class and prepares the work - * - * @access public - * @see FPDF - * @package phpMyAdmin - */ -class PMA_PDF extends TCPDF { - /** - * Defines private properties - */ - var $x_min; - var $y_min; - var $l_marg = 10; - var $t_marg = 10; - var $scale; - var $PMA_links; - var $Outlines = array(); - var $def_outlines; - var $Alias = array(); - var $widths; - - public function getFh() - { - return $this->fh; - } - - public function getFw() - { - return $this->fw; - } - - public function setCMargin($c_margin) - { - $this->cMargin = $c_margin; - } - - function SetAlias($name, $value) - { - $this->Alias[$name] = $value ; - } - - function _putpages() - { - if (count($this->Alias) > 0) { - $nb = $this->page; - foreach ($this->Alias as $alias => $value) { - for ($n = 1;$n <= $nb;$n++) - $this->pages[$n]=str_replace($alias, $value, $this->pages[$n]); - } - } - parent::_putpages(); - } - - /** - * Sets the scaling factor, defines minimum coordinates and margins - * - * @param float scale The scaling factor - * @param float x_min The minimum X coordinate - * @param float y_min The minimum Y coordinate - * @param float l_marg The left margin - * @param float t_marg The top margin - * @access public - */ - function PMA_PDF_setScale($scale = 1, $x_min = 0, $y_min = 0, $l_marg = -1, $t_marg = -1) - { - $this->scale = $scale; - $this->x_min = $x_min; - $this->y_min = $y_min; - if ($this->l_marg != -1) { - $this->l_marg = $l_marg; - } - if ($this->t_marg != -1) { - $this->t_marg = $t_marg; - } - } // end of the "PMA_PDF_setScale" function - /** - * Outputs a scaled cell - * - * @param float w The cell width - * @param float h The cell height - * @param string txt The text to output - * @param mixed border Whether to add borders or not - * @param integer ln Where to put the cursor once the output is done - * @param string align Align mode - * @param integer fill Whether to fill the cell with a color or not - * @access public - * @see FPDF::Cell() - */ - function PMA_PDF_cellScale($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '') - { - $h = $h / $this->scale; - $w = $w / $this->scale; - $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link); - } // end of the "PMA_PDF_cellScale" function - /** - * Draws a scaled line - * - * @param float x1 The horizontal position of the starting point - * @param float y1 The vertical position of the starting point - * @param float x2 The horizontal position of the ending point - * @param float y2 The vertical position of the ending point - * @access public - * @see FPDF::Line() - */ - function PMA_PDF_lineScale($x1, $y1, $x2, $y2) - { - $x1 = ($x1 - $this->x_min) / $this->scale + $this->l_marg; - $y1 = ($y1 - $this->y_min) / $this->scale + $this->t_marg; - $x2 = ($x2 - $this->x_min) / $this->scale + $this->l_marg; - $y2 = ($y2 - $this->y_min) / $this->scale + $this->t_marg; - $this->Line($x1, $y1, $x2, $y2); - } // end of the "PMA_PDF_lineScale" function - /** - * Sets x and y scaled positions - * - * @param float x The x position - * @param float y The y position - * @access public - * @see FPDF::SetXY() - */ - function PMA_PDF_setXyScale($x, $y) - { - $x = ($x - $this->x_min) / $this->scale + $this->l_marg; - $y = ($y - $this->y_min) / $this->scale + $this->t_marg; - $this->SetXY($x, $y); - } // end of the "PMA_PDF_setXyScale" function - /** - * Sets the X scaled positions - * - * @param float x The x position - * @access public - * @see FPDF::SetX() - */ - function PMA_PDF_setXScale($x) - { - $x = ($x - $this->x_min) / $this->scale + $this->l_marg; - $this->SetX($x); - } // end of the "PMA_PDF_setXScale" function - /** - * Sets the scaled font size - * - * @param float size The font size (in points) - * @access public - * @see FPDF::SetFontSize() - */ - function PMA_PDF_setFontSizeScale($size) - { - // Set font size in points - $size = $size / $this->scale; - $this->SetFontSize($size); - } // end of the "PMA_PDF_setFontSizeScale" function - /** - * Sets the scaled line width - * - * @param float width The line width - * @access public - * @see FPDF::SetLineWidth() - */ - function PMA_PDF_setLineWidthScale($width) - { - $width = $width / $this->scale; - $this->SetLineWidth($width); - } // end of the "PMA_PDF_setLineWidthScale" function - /** - * Displays an error message - * - * @param string error_message the error mesage - * @global array the PMA configuration array - * @global integer the current server id - * @global string the current language - * @global string the charset to convert to - * @global string the current database name - * @global string the current charset - * @global string the current text direction - * @global string a localized string - * @global string an other localized string - * @access public - */ -/* function PMA_PDF_die($error_message = '') - { - global $cfg; - global $server, $lang, $convcharset, $db; - global $charset, $text_dir; - - require_once './libraries/header.inc.php'; - - echo '<p><strong>PDF - ' . __('Error') . '</strong></p>' . "\n"; - if (!empty($error_message)) { - $error_message = htmlspecialchars($error_message); - } - echo '<p>' . "\n"; - echo ' ' . $error_message . "\n"; - echo '</p>' . "\n"; - - echo '<a href="db_structure.php?' . PMA_generate_common_url($db) - . '">' . __('Back') . '</a>'; - echo "\n"; - - require_once './libraries/footer.inc.php'; - }*/ // end of the "PMA_PDF_die()" function - /** - * Aliases the "Error()" function from the FPDF class to the - * "PMA_PDF_die()" one - * - * @param string error_message the error mesage - * @access public - * @see PMA_PDF_die - */ - function Error($error_message = '') - { - PMA_Relation_Schema::PMA_Schema_die($error_message); - } // end of the "Error()" method - function Header() - { - // We only show this if we find something in the new pdf_pages table - - // This function must be named "Header" to work with the FPDF library - global $cfgRelation, $db, $pdf_page_number, $with_doc; - if ($with_doc) { - $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND page_nr = \'' . $pdf_page_number . '\''; - $test_rs = PMA_query_as_controluser($test_query); - $pages = @PMA_DBI_fetch_assoc($test_rs); - $this->SetFont('', 'B', 14); - $this->Cell(0, 6, ucfirst($pages['page_descr']), 'B', 1, 'C'); - $this->SetFont('', ''); - $this->Ln(); - } - } - function Footer() - { - // This function must be named "Footer" to work with the FPDF library - global $with_doc; - if ($with_doc) { - $this->SetY(-15); - $this->SetFont('', '', 14); - $this->Cell(0, 6, __('Page number:') . ' ' . $this->PageNo() . '/{nb}', 'T', 0, 'C'); - $this->Cell(0, 6, PMA_localisedDate(), 0, 1, 'R'); - $this->SetY(20); - } - } - function Bookmark($txt, $level = 0, $y = 0) - { - // Add a bookmark - $this->Outlines[0][] = $level; - $this->Outlines[1][] = $txt; - $this->Outlines[2][] = $this->page; - if ($y == -1) { - $y = $this->GetY(); - } - $this->Outlines[3][] = round($this->hPt - $y * $this->k, 2); - } - - function _putbookmarks() - { - if (count($this->Outlines) > 0) { - // Save object number - $memo_n = $this->n; - // Take the number of sub elements for an outline - $nb_outlines = sizeof($this->Outlines[0]); - $first_level = array(); - $parent = array(); - $parent[0] = 1; - for ($i = 0; $i < $nb_outlines; $i++) { - $level = $this->Outlines[0][$i]; - $kids = 0; - $last = -1; - $prev = -1; - $next = -1; - if ($i > 0) { - $cursor = $i-1; - // Take the previous outline in the same level - while ($this->Outlines[0][$cursor] > $level && $cursor > 0) - $cursor--; - if ($this->Outlines[0][$cursor] == $level) { - $prev = $cursor; - } - } - if ($i < $nb_outlines-1) { - $cursor = $i + 1; - while (isset($this->Outlines[0][$cursor]) && $this->Outlines[0][$cursor] > $level) { - // Take the immediate kid in level + 1 - if ($this->Outlines[0][$cursor] == $level + 1) { - $kids++; - $last = $cursor; - } - $cursor++; - } - $cursor = $i + 1; - // Take the next outline in the same level - while ($this->Outlines[0][$cursor] > $level && ($cursor + 1 < sizeof($this->Outlines[0]))) - $cursor++; - if ($this->Outlines[0][$cursor] == $level) { - $next = $cursor; - } - } - $this->_newobj(); - $parent[$level + 1] = $this->n; - if ($level == 0) { - $first_level[] = $this->n; - } - $this->_out('<<'); - $this->_out('/Title (' . $this->Outlines[1][$i] . ')'); - $this->_out('/Parent ' . $parent[$level] . ' 0 R'); - if ($prev != -1) { - $this->_out('/Prev ' . ($memo_n + $prev + 1) . ' 0 R'); - } - if ($next != -1) { - $this->_out('/Next ' . ($this->n + $next - $i) . ' 0 R'); - } - $this->_out('/Dest [' . (1 + (2 * $this->Outlines[2][$i])) . ' 0 R /XYZ null ' . $this->Outlines[3][$i] . ' null]'); - if ($kids > 0) { - $this->_out('/First ' . ($this->n + 1) . ' 0 R'); - $this->_out('/Last ' . ($this->n + $last - $i) . ' 0 R'); - $this->_out('/Count -' . $kids); - } - $this->_out('>>'); - $this->_out('endobj'); - } - // First page of outlines - $this->_newobj(); - $this->def_outlines = $this->n; - $this->_out('<<'); - $this->_out('/Type'); - $this->_out('/Outlines'); - $this->_out('/First ' . $first_level[0] . ' 0 R'); - $this->_out('/Last ' . $first_level[sizeof($first_level)-1] . ' 0 R'); - $this->_out('/Count ' . sizeof($first_level)); - $this->_out('>>'); - $this->_out('endobj'); - } - } - - function _putresources() - { - parent::_putresources(); - $this->_putbookmarks(); - } - - function _putcatalog() - { - parent::_putcatalog(); - if (count($this->Outlines) > 0) { - $this->_out('/Outlines ' . $this->def_outlines . ' 0 R'); - $this->_out('/PageMode /UseOutlines'); - } - } - function SetWidths($w) - { - // column widths - $this->widths = $w; - } - - function Row($data, $links) - { - // line height - $nb = 0; - $data_cnt = count($data); - for ($i = 0;$i < $data_cnt;$i++) - $nb = max($nb, $this->NbLines($this->widths[$i], $data[$i])); - $il = $this->FontSize; - $h = ($il + 1) * $nb; - // page break if necessary - $this->CheckPageBreak($h); - // draw the cells - $data_cnt = count($data); - for ($i = 0;$i < $data_cnt;$i++) { - $w = $this->widths[$i]; - // save current position - $x = $this->GetX(); - $y = $this->GetY(); - // draw the border - $this->Rect($x, $y, $w, $h); - if (isset($links[$i])) { - $this->Link($x, $y, $w, $h, $links[$i]); - } - // print text - $this->MultiCell($w, $il + 1, $data[$i], 0, 'L'); - // go to right side - $this->SetXY($x + $w, $y); - } - // go to line - $this->Ln($h); - } - - function CheckPageBreak($h) - { - // if height h overflows, manual page break - if ($this->GetY() + $h > $this->PageBreakTrigger) { - $this->AddPage($this->CurOrientation); - } - } - - function NbLines($w, $txt) - { - // compute number of lines used by a multicell of width w - $cw = &$this->CurrentFont['cw']; - if ($w == 0) { - $w = $this->w - $this->rMargin - $this->x; - } - $wmax = ($w-2 * $this->cMargin) * 1000 / $this->FontSize; - $s = str_replace("\r", '', $txt); - $nb = strlen($s); - if ($nb > 0 and $s[$nb-1] == "\n") { - $nb--; - } - $sep = -1; - $i = 0; - $j = 0; - $l = 0; - $nl = 1; - while ($i < $nb) { - $c = $s[$i]; - if ($c == "\n") { - $i++; - $sep = -1; - $j = $i; - $l = 0; - $nl++; - continue; - } - if ($c == ' ') { - $sep = $i; - } - $l += isset($cw[ord($c)])?$cw[ord($c)]:0 ; - if ($l > $wmax) { - if ($sep == -1) { - if ($i == $j) { - $i++; - } - } else { - $i = $sep + 1; - } - $sep = -1; - $j = $i; - $l = 0; - $nl++; - } else { - $i++; - } - } - return $nl; - } -} // end of the "PMA_PDF" class - -/** - * Draws tables schema - * - * @access private - * @see PMA_RT - * @package phpMyAdmin - */ -class PMA_RT_Table { - /** - * Defines private properties - */ - var $nb_fiels; - var $table_name; - var $width = 0; - var $height; - var $fields = array(); - var $height_cell = 6; - var $x, $y; - var $primary = array(); - var $show_info = false; - - /** - * Returns title of the current table, - * title can have the dimensions of the table - * - * @access private - */ - function getTitle() - { - return ($this->show_info ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->height) : '') . ' ' . $this->table_name; - } // end of the "getTitle" function - /** - * Sets the width of the table - * - * @param integer ff The font size - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Table_setWidth($ff) - { - global $pdf; - - foreach ($this->fields as $field) { - $this->width = max($this->width, $pdf->GetStringWidth($field)); - } - //echo $this->width.'<br />'; - $this->width += $pdf->GetStringWidth(' '); - //echo $this->width.'<br />'; - $pdf->SetFont($ff, 'B'); - // it is unknown what value must be added, because - // table title is affected by the tabe width value - //echo $this->getTitle(); - while ($this->width < $pdf->GetStringWidth($this->getTitle())) { - $this->width += 5; - } - //echo $this->width.'<br />'; - $pdf->SetFont($ff, ''); - } // end of the "PMA_RT_Table_setWidth()" method - /** - * Sets the height of the table - * - * @access private - */ - function PMA_RT_Table_setHeight() - { - $this->height = (count($this->fields) + 1) * $this->height_cell; - } // end of the "PMA_RT_Table_setHeight()" method - /** - * Do draw the table - * - * @param integer ff The font size - * @param boolean setcolortWhether to display color - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Table_draw($ff, $setcolor = 0) - { - global $pdf, $with_doc; - - $pdf->PMA_PDF_setXyScale($this->x, $this->y); - $pdf->SetFont($ff, 'B'); - if ($setcolor) { - $pdf->SetTextColor(200); - $pdf->SetFillColor(0, 0, 128); - } - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name]['-'], -1); - } else { - $pdf->PMA_links['doc'][$this->table_name]['-'] = ''; - } - - $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, $this->getTitle(), 1, 1, 'C', $setcolor, $pdf->PMA_links['doc'][$this->table_name]['-']); - $pdf->PMA_PDF_setXScale($this->x); - $pdf->SetFont($ff, ''); - $pdf->SetTextColor(0); - $pdf->SetFillColor(255); - - foreach ($this->fields as $field) { - if ($setcolor) { - if (in_array($field, $this->primary)) { - $pdf->SetFillColor(215, 121, 123); - } - if ($field == $this->displayfield) { - $pdf->SetFillColor(142, 159, 224); - } - } - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name][$field], -1); - } else { - $pdf->PMA_links['doc'][$this->table_name][$field] = ''; - } - - $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, ' ' . $field, 1, 1, 'L', $setcolor, $pdf->PMA_links['doc'][$this->table_name][$field]); - $pdf->PMA_PDF_setXScale($this->x); - $pdf->SetFillColor(255); - } // end while - /*if ($pdf->PageNo() > 1) { - $pdf->PMA_PDF_die(__('The scale factor is too small to fit the schema on one page')); - } */ - } // end of the "PMA_RT_Table_draw()" method - /** - * The "PMA_RT_Table" constructor - * - * @param string table_name The table name - * @param integer ff The font size - * @param integer same_width The max. with among tables - * @param boolean show_keys Whether to display keys or not - * @param boolean show_info Whether to display table position or not - * @global object The current PDF document - * @global integer The current page number (from the - * $cfg['Servers'][$i]['table_coords'] table) - * @global array The relations settings - * @global string The current db name - * @access private - * @see PMA_PDF, PMA_RT_Table::PMA_RT_Table_setWidth, - PMA_RT_Table::PMA_RT_Table_setHeight - */ - function __construct($table_name, $ff, &$same_wide_width, $show_keys = false, $show_info = false) - { - global $pdf, $pdf_page_number, $cfgRelation, $db; - - $this->table_name = $table_name; - $sql = 'DESCRIBE ' . PMA_backquote($table_name); - $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); - if (!$result || !PMA_DBI_num_rows($result)) { - $pdf->PMA_PDF_die(sprintf(__('The %s table doesn\'t exist!'), $table_name)); - } - // load fields - //check to see if it will load all fields or only the foreign keys - if ($show_keys) { - $indexes = PMA_Index::getFromTable($this->table_name, $db); - $all_columns = array(); - foreach ($indexes as $index) { - $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); - } - $this->fields = array_keys($all_columns); - } else { - while ($row = PMA_DBI_fetch_row($result)) { - $this->fields[] = $row[0]; - } - } - - $this->show_info = $show_info; - - // height and width - $this->PMA_RT_Table_setHeight(); - // setWidth must me after setHeight, because title - // can include table height which changes table width - $this->PMA_RT_Table_setWidth($ff); - if ($same_wide_width < $this->width) { - $same_wide_width = $this->width; - } - // x and y - $sql = 'SELECT x, y FROM ' - . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($table_name) . '\'' - . ' AND pdf_page_number = ' . $pdf_page_number; - $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); - - if (!$result || !PMA_DBI_num_rows($result)) { - $pdf->PMA_PDF_die(sprintf(__('Please configure the coordinates for table %s'), $table_name)); - } - list($this->x, $this->y) = PMA_DBI_fetch_row($result); - $this->x = (double) $this->x; - $this->y = (double) $this->y; - // displayfield - $this->displayfield = PMA_getDisplayField($db, $table_name); - // index - $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($table_name) . ';', null, PMA_DBI_QUERY_STORE); - if (PMA_DBI_num_rows($result) > 0) { - while ($row = PMA_DBI_fetch_assoc($result)) { - if ($row['Key_name'] == 'PRIMARY') { - $this->primary[] = $row['Column_name']; - } - } - } // end if - } // end of the "PMA_RT_Table()" method -} // end class "PMA_RT_Table" - -/** - * Draws relation links - * - * @access private - * @see PMA_RT - * @package phpMyAdmin - */ -class PMA_RT_Relation { - /** - * Defines private properties - */ - var $x_src, $y_src; - var $src_dir ; - var $dest_dir; - var $x_dest, $y_dest; - var $w_tick = 5; - - /** - * Gets arrows coordinates - * - * @param string table The current table name - * @param string column The relation column name - * @return array Arrows coordinates - * @access private - */ - function PMA_RT_Relation_getXy($table, $column) - { - $pos = array_search($column, $table->fields); - // x_left, x_right, y - return array($table->x, $table->x + + $table->width, $table->y + ($pos + 1.5) * $table->height_cell); - } // end of the "PMA_RT_Relation_getXy()" method - /** - * Do draws relation links - * - * @param boolean change_color Whether to use one color per relation or not - * @param integer i The id of the link to draw - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Relation_draw($change_color, $i) - { - global $pdf; - - if ($change_color) { - $d = $i % 6; - $j = ($i - $d) / 6; - $j = $j % 4; - $j++; - $case = array( - array(1, 0, 0), - array(0, 1, 0), - array(0, 0, 1), - array(1, 1, 0), - array(1, 0, 1), - array(0, 1, 1) - ); - list ($a, $b, $c) = $case[$d]; - $e = (1 - ($j - 1) / 6); - $pdf->SetDrawColor($a * 255 * $e, $b * 255 * $e, $c * 255 * $e); - } else { - $pdf->SetDrawColor(0); - } // end if... else... - $pdf->PMA_PDF_setLineWidthScale(0.2); - $pdf->PMA_PDF_lineScale($this->x_src, $this->y_src, $this->x_src + $this->src_dir * $this->w_tick, $this->y_src); - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest, $this->x_dest, $this->y_dest); - $pdf->PMA_PDF_setLineWidthScale(0.1); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick, $this->y_src, $this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest); - // arrow - $root2 = 2 * sqrt(2); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src + $this->w_tick / $root2); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src - $this->w_tick / $root2); - - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest + $this->w_tick / $root2); - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest - $this->w_tick / $root2); - $pdf->SetDrawColor(0); - } // end of the "PMA_RT_Relation_draw()" method - /** - * The "PMA_RT_Relation" constructor - * - * @param string master_table The master table name - * @param string master_field The relation field in the master table - * @param string foreign_table The foreign table name - * @param string foreigh_field The relation field in the foreign table - * @access private - * @see PMA_RT_Relation::PMA_RT_Relation_getXy - */ - function __construct($master_table, $master_field, $foreign_table, $foreign_field) - { - $src_pos = $this->PMA_RT_Relation_getXy($master_table, $master_field); - $dest_pos = $this->PMA_RT_Relation_getXy($foreign_table, $foreign_field); - $src_left = $src_pos[0] - $this->w_tick; - $src_right = $src_pos[1] + $this->w_tick; - $dest_left = $dest_pos[0] - $this->w_tick; - $dest_right = $dest_pos[1] + $this->w_tick; - - $d1 = abs($src_left - $dest_left); - $d2 = abs($src_right - $dest_left); - $d3 = abs($src_left - $dest_right); - $d4 = abs($src_right - $dest_right); - $d = min($d1, $d2, $d3, $d4); - - if ($d == $d1) { - $this->x_src = $src_pos[0]; - $this->src_dir = -1; - $this->x_dest = $dest_pos[0]; - $this->dest_dir = -1; - } elseif ($d == $d2) { - $this->x_src = $src_pos[1]; - $this->src_dir = 1; - $this->x_dest = $dest_pos[0]; - $this->dest_dir = -1; - } elseif ($d == $d3) { - $this->x_src = $src_pos[0]; - $this->src_dir = -1; - $this->x_dest = $dest_pos[1]; - $this->dest_dir = 1; - } else { - $this->x_src = $src_pos[1]; - $this->src_dir = 1; - $this->x_dest = $dest_pos[1]; - $this->dest_dir = 1; - } - $this->y_src = $src_pos[2]; - $this->y_dest = $dest_pos[2]; - } // end of the "PMA_RT_Relation()" method -} // end of the "PMA_RT_Relation" class - -class PMA_PDF_RELAION_SCHEMA extends PMA_Relation_Schema -{ - /** - * Defines private properties - */ - private $tables = array(); - private $relations = array(); - private $ff = PMA_PDF_FONT; - private $x_max = 0; - private $y_max = 0; - private $scale; - private $x_min = 100000; - private $y_min = 100000; - private $t_marg = 10; - private $b_marg = 10; - private $l_marg = 10; - private $r_marg = 10; - private $_tablewidth; - - /** - * The "PMA_RT" constructor - * - * @param integer page_number The page number to draw (from the - * $cfg['Servers'][$i]['table_coords'] table) - * @param boolean show_info Whether to display table position or not - * @param boolean change_color Was originally whether to use one color per - * relation or not, now enables/disables color - * everywhere, due to some problems printing with color - * @param boolean show_grid Whether to draw grids or not - * @param boolean all_tab_same_wide Whether all tables should have the same width or not - * @param boolean show_keys Wheter to show all field or only the keys - * @global object The current PDF document - * @global string The current db name - * @global array The relations settings - * @access private - * @see PMA_PDF - */ - function __construct($page_number, $show_info = 0, $change_color = 0, $show_grid = 0, $all_table_same_wide = 0, $orientation = 'L', $paper = 'A4', $show_keys = 0) - { - global $pdf, $db, $cfgRelation, $with_doc; - - $this->setSameWidthTables($all_table_same_wide); - - // Initializes a new document - $pdf = new PMA_PDF('L', 'mm', $paper); - $pdf->SetTitle(sprintf(__('Schema of the %s database - Page %s'), $GLOBALS['db'], $page_number)); - $pdf->setCMargin(0); - $pdf->Open(); - $pdf->SetAuthor('phpMyAdmin ' . PMA_VERSION); - $pdf->AliasNbPages(); - $pdf->AddFont('DejaVuSans', '', 'dejavusans.php'); - $pdf->AddFont('DejaVuSans', 'B', 'dejavusansb.php'); - $pdf->AddFont('DejaVuSerif', '', 'dejavuserif.php'); - $pdf->AddFont('DejaVuSerif', 'B', 'dejavuserifb.php'); - // $this->ff = PMA_PDF_FONT; - - $pdf->SetFont($this->ff, '', 14); - $pdf->SetAutoPageBreak('auto'); - - - // Gets tables on this page -/* $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = ' . $page_number; - echo $tab_sql; - $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); - if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { - $pdf->PMA_PDF_die(__('No tables')); - // die('No tables'); - } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { - $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); - // $intable = '\'' . implode('\', \'', $alltables) . '\''; - }*/ - $alltables=$this->getAllTables($db,$page_number); - - - // make doc // - if ($with_doc) { - $pdf->SetAutoPageBreak('auto', 15); - $pdf->setCMargin(1); - $this->PMA_RT_DOC($alltables); - $pdf->SetAutoPageBreak('auto'); - $pdf->setCMargin(0); - } - - $pdf->Addpage(); - - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT']['-'], -1); - $pdf->Bookmark(__('Relational schema')); - $pdf->SetAlias('{00}', $pdf->PageNo()) ; - $this->t_marg = 28; - $this->b_marg = 28; - } - - /* snip */ - - foreach ($alltables as $table) { - if (!isset($this->tables[$table])) { - $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->_tablewidth, $show_keys, $show_info); - // $this->tables[$table]=$table; - } - - if ($this->same_wide) { - $this->tables[$table]->width = $this->_tablewidth; - } - $this->PMA_RT_setMinMax($this->tables[$table]); - } - - // Defines the scale factor - $this->scale = ceil( - max( - ($this->x_max - $this->x_min) / ($pdf->getFh() - $this->r_marg - $this->l_marg), - ($this->y_max - $this->y_min) / ($pdf->getFw() - $this->t_marg - $this->b_marg)) - * 100) / 100; - - $pdf->PMA_PDF_setScale($this->scale, $this->x_min, $this->y_min, $this->l_marg, $this->t_marg); - // Builds and save the PDF document - $pdf->PMA_PDF_setLineWidthScale(0.1); - - if ($show_grid) { - $pdf->SetFontSize(10); - $this->PMA_RT_strokeGrid(); - } - $pdf->PMA_PDF_setFontSizeScale(14); - // $sql = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) - // . ' WHERE master_db = \'' . PMA_sqlAddslashes($db) . '\' ' - // . ' AND foreign_db = \'' . PMA_sqlAddslashes($db) . '\' ' - // . ' AND master_table IN (' . $intable . ')' - // . ' AND foreign_table IN (' . $intable . ')'; - // $result = PMA_query_as_controluser($sql); - - // previous logic was checking master tables and foreign tables - // but I think that looping on every table of the pdf page as a master - // and finding its foreigns is OK (then we can support innodb) - $seen_a_relation = false; - foreach ($alltables as $one_table) { - $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); - if ($exist_rel) { - $seen_a_relation = true; - foreach ($exist_rel as $master_field => $rel) { - // put the foreign table on the schema only if selected - // by the user - // (do not use array_search() because we would have to - // to do a === FALSE and this is not PHP3 compatible) - if (in_array($rel['foreign_table'], $alltables)) { - $this->PMA_RT_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'], $show_info); - } - } // end while - } // end if - } // end while - -/* print '<pre>'; - print_r(get_object_vars($this)); - print '</pre>'; - exit();*/ - - if ($seen_a_relation) { - $this->PMA_RT_drawRelations($change_color); - } - - $this->PMA_RT_drawTables($change_color); - - $this->PMA_RT_showRt(); - } // end of the "PMA_RT()" Constructor method - /** - * Sets X and Y minimum and maximum for a table cell - * - * @param string table The table name - * @access private - */ - function PMA_RT_setMinMax($table) - { - $this->x_max = max($this->x_max, $table->x + $table->width); - $this->y_max = max($this->y_max, $table->y + $table->height); - $this->x_min = min($this->x_min, $table->x); - $this->y_min = min($this->y_min, $table->y); - } // end of the "PMA_RT_setMinMax()" method - /** - * Defines relation objects - * - * @param string master_table The master table name - * @param string master_field The relation field in the master table - * @param string foreign_table The foreign table name - * @param string foreign_field The relation field in the foreign table - * @param boolean show_info Whether to display table position or not - * @access private - * @see PMA_RT_setMinMax - */ - function PMA_RT_addRelation($master_table, $master_field, $foreign_table, $foreign_field, $show_info) - { - if (!isset($this->tables[$master_table])) { - $this->tables[$master_table] = new PMA_RT_Table($master_table, $this->ff, $this->_tablewidth, false, $show_info); - $this->PMA_RT_setMinMax($this->tables[$master_table]); - } - if (!isset($this->tables[$foreign_table])) { - $this->tables[$foreign_table] = new PMA_RT_Table($foreign_table, $this->ff, $this->_tablewidth, false, $show_info); - $this->PMA_RT_setMinMax($this->tables[$foreign_table]); - } - $this->relations[] = new PMA_RT_Relation($this->tables[$master_table], $master_field, $this->tables[$foreign_table], $foreign_field); - } // end of the "PMA_RT_addRelation()" method - /** - * Draws the grid - * - * @global object the current PMA_PDF instance - * @access private - * @see PMA_PDF - */ - function PMA_RT_strokeGrid() - { - global $pdf; - - $pdf->SetMargins(0, 0); - $pdf->SetDrawColor(200, 200, 200); - // Draws horizontal lines - for ($l = 0; $l < 21; $l++) { - $pdf->line(0, $l * 10, $pdf->getFh(), $l * 10); - // Avoid duplicates - if ($l > 0) { - $pdf->SetXY(0, $l * 10); - $label = (string) sprintf('%.0f', ($l * 10 - $this->t_marg) * $this->scale + $this->y_min); - $pdf->Cell(5, 5, ' ' . $label); - } // end if - } // end for - // Draws vertical lines - for ($j = 0; $j < 30 ;$j++) { - $pdf->line($j * 10, 0, $j * 10, $pdf->getFw()); - $pdf->SetXY($j * 10, 0); - $label = (string) sprintf('%.0f', ($j * 10 - $this->l_marg) * $this->scale + $this->x_min); - $pdf->Cell(5, 7, $label); - } // end for - } // end of the "PMA_RT_strokeGrid()" method - /** - * Draws relation arrows - * - * @param boolean change_color Whether to use one color per relation or not - * @access private - * @see PMA_RT_Relation::PMA_RT_Relation_draw() - */ - function PMA_RT_drawRelations($change_color) - { - $i = 0; - foreach ($this->relations as $relation) { - $relation->PMA_RT_Relation_draw($change_color, $i); - $i++; - } // end while - } // end of the "PMA_RT_drawRelations()" method - /** - * Draws tables - * - * @param boolean draw_color Whether to display table position or not - * @access private - * @see PMA_RT_Table::PMA_RT_Table_draw() - */ - function PMA_RT_drawTables($draw_color = 0) - { - foreach ($this->tables as $table) { - $table->PMA_RT_Table_draw($this->ff, $draw_color); - } - } // end of the "PMA_RT_drawTables()" method - /** - * Ouputs the PDF document to a file - * - * @global object The current PDF document - * @global string The current database name - * @global integer The current page number (from the - * $cfg['Servers'][$i]['table_coords'] table) - * @access private - * @see PMA_PDF - */ - function PMA_RT_showRt() - { - global $pdf, $db, $pdf_page_number, $cfgRelation; - - $pdf->SetFontSize(14); - $pdf->SetLineWidth(0.2); - $pdf->SetDisplayMode('fullpage'); - // Get the name of this pdfpage to use as filename (Mike Beck) - $_name_sql = 'SELECT page_descr FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE page_nr = ' . $pdf_page_number; - $_name_rs = PMA_query_as_controluser($_name_sql); - if ($_name_rs) { - $_name_row = PMA_DBI_fetch_row($_name_rs); - $filename = $_name_row[0] . '.pdf'; - } - // i don't know if there is a chance for this to happen, but rather be on the safe side: - if (empty($filename)) { - $filename = $pdf_page_number . '.pdf'; - } - // $pdf->Output($db . '_' . $filename, TRUE); - $pdf->Output($db . '_' . $filename, 'I'); // destination: Inline - } // end of the "PMA_RT_showRt()" method - - function PMA_RT_DOC($alltables) -{ - global $db, $pdf, $orientation, $paper; - // TOC - $pdf->addpage($GLOBALS['orientation']); - $pdf->Cell(0, 9, __('Table of contents'), 1, 0, 'C'); - $pdf->Ln(15); - $i = 1; - foreach ($alltables as $table) { - $pdf->PMA_links['doc'][$table]['-'] = $pdf->AddLink(); - $pdf->SetX(10); - // $pdf->Ln(1); - $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$table]['-']); - $pdf->SetX(10); - $pdf->Cell(0, 6, $i . ' ' . $table, 0, 1, 'L', 0, $pdf->PMA_links['doc'][$table]['-']); - // $pdf->Ln(1); - $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';'); - while ($row = PMA_DBI_fetch_assoc($result)) { - $pdf->SetX(20); - $field_name = $row['Field']; - $pdf->PMA_links['doc'][$table][$field_name] = $pdf->AddLink(); - // $pdf->Cell(0, 6, $field_name,0,1,'L',0, $pdf->PMA_links['doc'][$table][$field_name]); - } - $lasttable = $table; - $i++; - } - $pdf->PMA_links['RT']['-'] = $pdf->AddLink(); - $pdf->SetX(10); - $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$lasttable]['-']); - $pdf->SetX(10); - $pdf->Cell(0, 6, $i + 1 . ' ' . __('Relational schema'), 0, 1, 'L', 0, $pdf->PMA_links['RT']['-']); - $z = 0; - foreach ($alltables as $table) { - $z++; - $pdf->addpage($GLOBALS['orientation']); - $pdf->Bookmark($table); - $pdf->SetAlias('{' . sprintf("%02d", $z) . '}', $pdf->PageNo()) ; - $pdf->PMA_links['RT'][$table]['-'] = $pdf->AddLink(); - $pdf->SetLink($pdf->PMA_links['doc'][$table]['-'], -1); - $pdf->SetFont('', 'B', 18); - $pdf->Cell(0, 8, $z . ' ' . $table, 1, 1, 'C', 0, $pdf->PMA_links['RT'][$table]['-']); - $pdf->SetFont('', '', 8); - $pdf->ln(); - - $cfgRelation = PMA_getRelationsParam(); - $comments = PMA_getComments($db, $table); - if ($cfgRelation['mimework']) { - $mime_map = PMA_getMIME($db, $table, true); - } - - /** - * Gets table informations - */ - $showtable = PMA_Table::sGetStatusInfo($db, $table); - $num_rows = (isset($showtable['Rows']) ? $showtable['Rows'] : 0); - $show_comment = (isset($showtable['Comment']) ? $showtable['Comment'] : ''); - $create_time = (isset($showtable['Create_time']) ? PMA_localisedDate(strtotime($showtable['Create_time'])) : ''); - $update_time = (isset($showtable['Update_time']) ? PMA_localisedDate(strtotime($showtable['Update_time'])) : ''); - $check_time = (isset($showtable['Check_time']) ? PMA_localisedDate(strtotime($showtable['Check_time'])) : ''); - - /** - * Gets table keys and retains them - */ - $result = PMA_DBI_query('SHOW KEYS FROM ' . PMA_backquote($table) . ';'); - $primary = ''; - $indexes = array(); - $lastIndex = ''; - $indexes_info = array(); - $indexes_data = array(); - $pk_array = array(); // will be use to emphasis prim. keys in the table - // view - while ($row = PMA_DBI_fetch_assoc($result)) { - // Backups the list of primary keys - if ($row['Key_name'] == 'PRIMARY') { - $primary .= $row['Column_name'] . ', '; - $pk_array[$row['Column_name']] = 1; - } - // Retains keys informations - if ($row['Key_name'] != $lastIndex) { - $indexes[] = $row['Key_name']; - $lastIndex = $row['Key_name']; - } - $indexes_info[$row['Key_name']]['Sequences'][] = $row['Seq_in_index']; - $indexes_info[$row['Key_name']]['Non_unique'] = $row['Non_unique']; - if (isset($row['Cardinality'])) { - $indexes_info[$row['Key_name']]['Cardinality'] = $row['Cardinality']; - } - // I don't know what does following column mean.... - // $indexes_info[$row['Key_name']]['Packed'] = $row['Packed']; - $indexes_info[$row['Key_name']]['Comment'] = $row['Comment']; - - $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Column_name'] = $row['Column_name']; - if (isset($row['Sub_part'])) { - $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Sub_part'] = $row['Sub_part']; - } - } // end while - if ($result) { - PMA_DBI_free_result($result); - } - - /** - * Gets fields properties - */ - $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE); - $fields_cnt = PMA_DBI_num_rows($result); - // Check if we can use Relations (Mike Beck) - if (!empty($cfgRelation['relation'])) { - // Find which tables are related with the current one and write it in - // an array - $res_rel = PMA_getForeigners($db, $table); - - if (count($res_rel) > 0) { - $have_rel = true; - } else { - $have_rel = false; - } - } else { - $have_rel = false; - } // end if - /** - * Displays the comments of the table if MySQL >= 3.23 - */ - - $break = false; - if (!empty($show_comment)) { - $pdf->Cell(0, 3, __('Table comments') . ' : ' . $show_comment, 0, 1); - $break = true; - } - - if (!empty($create_time)) { - $pdf->Cell(0, 3, __('Creation') . ': ' . $create_time, 0, 1); - $break = true; - } - - if (!empty($update_time)) { - $pdf->Cell(0, 3, __('Last update') . ': ' . $update_time, 0, 1); - $break = true; - } - - if (!empty($check_time)) { - $pdf->Cell(0, 3, __('Last check') . ': ' . $check_time, 0, 1); - $break = true; - } - - if ($break == true) { - $pdf->Cell(0, 3, '', 0, 1); - $pdf->Ln(); - } - - $pdf->SetFont('', 'B'); - if (isset($orientation) && $orientation == 'L') { - $pdf->Cell(25, 8, ucfirst(__('Column')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); - $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Default')), 1, 0, 'C'); - $pdf->Cell(25, 8, ucfirst(__('Extra')), 1, 0, 'C'); - $pdf->Cell(45, 8, ucfirst(__('Links to')), 1, 0, 'C'); - - if ($paper == 'A4') { - $comments_width = 67; - } else { - // this is really intended for 'letter' - /** - * @todo find optimal width for all formats - */ - $comments_width = 50; - } - $pdf->Cell($comments_width, 8, ucfirst(__('Comments')), 1, 0, 'C'); - $pdf->Cell(45, 8, 'MIME', 1, 1, 'C'); - $pdf->SetWidths(array(25, 20, 20, 10, 20, 25, 45, $comments_width, 45)); - } else { - $pdf->Cell(20, 8, ucfirst(__('Column')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); - $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); - $pdf->Cell(15, 8, ucfirst(__('Default')), 1, 0, 'C'); - $pdf->Cell(15, 8, ucfirst(__('Extra')), 1, 0, 'C'); - $pdf->Cell(30, 8, ucfirst(__('Links to')), 1, 0, 'C'); - $pdf->Cell(30, 8, ucfirst(__('Comments')), 1, 0, 'C'); - $pdf->Cell(30, 8, 'MIME', 1, 1, 'C'); - $pdf->SetWidths(array(20, 20, 20, 10, 15, 15, 30, 30, 30)); - } - $pdf->SetFont('', ''); - - while ($row = PMA_DBI_fetch_assoc($result)) { - $type = $row['Type']; - // reformat mysql query output - // set or enum types: slashes single quotes inside options - if (preg_match('@^(set|enum)\((.+)\)$@i', $type, $tmp)) { - $tmp[2] = substr(preg_replace("@([^,])''@", "\\1\\'", ',' . $tmp[2]), 1); - $type = $tmp[1] . '(' . str_replace(',', ', ', $tmp[2]) . ')'; - $type_nowrap = ''; - - $binary = 0; - $unsigned = 0; - $zerofill = 0; - } else { - $type_nowrap = ' nowrap="nowrap"'; - $type = preg_replace('@BINARY@i', '', $type); - $type = preg_replace('@ZEROFILL@i', '', $type); - $type = preg_replace('@UNSIGNED@i', '', $type); - if (empty($type)) { - $type = '&nbsp;'; - } - - $binary = stristr($row['Type'], 'BINARY'); - $unsigned = stristr($row['Type'], 'UNSIGNED'); - $zerofill = stristr($row['Type'], 'ZEROFILL'); - } - $attribute = ' '; - if ($binary) { - $attribute = 'BINARY'; - } - if ($unsigned) { - $attribute = 'UNSIGNED'; - } - if ($zerofill) { - $attribute = 'UNSIGNED ZEROFILL'; - } - if (!isset($row['Default'])) { - if ($row['Null'] != '' && $row['Null'] != 'NO') { - $row['Default'] = 'NULL'; - } - } - $field_name = $row['Field']; - // $pdf->Ln(); - $pdf->PMA_links['RT'][$table][$field_name] = $pdf->AddLink(); - $pdf->Bookmark($field_name, 1, -1); - $pdf->SetLink($pdf->PMA_links['doc'][$table][$field_name], -1); - $pdf_row = array($field_name, - $type, - $attribute, - ($row['Null'] == '' || $row['Null'] == 'NO') ? __('No') : __('Yes'), - ((isset($row['Default'])) ? $row['Default'] : ''), - $row['Extra'], - ((isset($res_rel[$field_name])) ? $res_rel[$field_name]['foreign_table'] . ' -> ' . $res_rel[$field_name]['foreign_field'] : ''), - ((isset($comments[$field_name])) ? $comments[$field_name] : ''), - ((isset($mime_map) && isset($mime_map[$field_name])) ? str_replace('_', '/', $mime_map[$field_name]['mimetype']) : '') - ); - $links[0] = $pdf->PMA_links['RT'][$table][$field_name]; - if (isset($res_rel[$field_name]['foreign_table']) AND - isset($res_rel[$field_name]['foreign_field']) AND - isset($pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]) - ) - { - $links[6] = $pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]; - } else { - unset($links[6]); - } - $pdf->Row($pdf_row, $links); - } // end while - $pdf->SetFont('', '', 14); - PMA_DBI_free_result($result); - } //end each - } -} -?> \ No newline at end of file diff --git a/libraries/schema/relationSchema.abstract.class.php b/libraries/schema/relationSchema.abstract.class.php deleted file mode 100644 index a30e9d2a1..000000000 --- a/libraries/schema/relationSchema.abstract.class.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php -// Using Abstract Factory Pattern for exporting relational Schema in different Formats ! -abstract class exportRelationSchema -{ - private $pageTitle; // Title of the page - private $autoLayoutType; // Internal or Foreign Key Relations; - - public function setPageTitle($title) - { - $this->pageTitle=$title; - } - - public function createPage() - { - - } - public function createPageHTML() - { - ?> - <form method="post" action="pdf_pages.php" name="crpage"> - <fieldset> - <legend> - <?php echo __('Create a page') . "\n"; ?> - </legend> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="do" value="createpage" /> - <table> - <tr> - <td><label for="id_newpage"><?php echo __('Page name'); ?></label></td> - <td><input type="text" name="newpage" id="id_newpage" size="20" maxlength="50" /></td> - </tr> - <tr> - <td><?php echo __('Automatic layout based on'); ?></td> - <td> - <input type="checkbox" name="auto_layout_internal" id="id_auto_layout_internal" /><label for="id_auto_layout_internal"><?php echo __('Internal relations'); ?></label><br /> - <?php - if (PMA_StorageEngine::isValid('InnoDB') || PMA_StorageEngine::isValid('PBXT')) { - ?> - <input type="checkbox" name="auto_layout_foreign" id="id_auto_layout_foreign" /><label for="id_auto_layout_foreign">FOREIGN KEY</label><br /> - <?php - } - ?> - </td></tr> - </table> - </fieldset> - <fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /> - </fieldset> - </form> -<?php - } -} -?> \ No newline at end of file diff --git a/libraries/schema/relation_schema.php b/libraries/schema/relation_schema.php deleted file mode 100644 index e0df5935c..000000000 --- a/libraries/schema/relation_schema.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php -// Using Abstract Factory Pattern for exporting relational Schema in different Formats ! -abstract class PMA_Relation_Schema -{ - private $_pageTitle; // Title of the page - private $_autoLayoutType; // Internal or Foreign Key Relations; - protected $same_wide; - - public function setPageTitle($title) - { - $this->_pageTitle=$title; - } - - public function setExportType($type) - { - $this->_pageTitle=$title; - } - - public function setSameWidthTables($width) - { - $this->same_wide=$width; - } - - public function getAllTables($db,$page_number) - { - global $cfgRelation; - // Get All tables - $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = ' . $page_number; - // echo $tab_sql; - $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); - if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { - $this->PMA_SCHEMA_die('',__('No tables')); - } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { - $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); - } - return $alltables; - } - - public function initTable() - { - - } - - - /** - * Displays an error message - */ - function PMA_Schema_die($type = '',$error_message = '') - { - global $db; - - require_once './libraries/header.inc.php'; - - echo "<p><strong> {$type} " . __("SCHEMA ERROR") . "</strong></p>" . "\n"; - if (!empty($error_message)) { - $error_message = htmlspecialchars($error_message); - } - echo '<p>' . "\n"; - echo ' ' . $error_message . "\n"; - echo '</p>' . "\n"; - - echo '<a href="export_relation_schema.php?' . PMA_generate_common_url($db) - . '">' . __('Back') . '</a>'; - echo "\n"; - - require_once './libraries/footer.inc.php'; - } // end of the "PMA_PDF_die()" function -} -?> \ No newline at end of file diff --git a/libraries/schema/svgSchema.class.php b/libraries/schema/svgSchema.class.php deleted file mode 100644 index 61ccc3bfc..000000000 --- a/libraries/schema/svgSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class svgSchema extends exportRelationSchema -{ - -} -?> \ No newline at end of file diff --git a/libraries/schema/svg_relation_schema.php b/libraries/schema/svg_relation_schema.php deleted file mode 100644 index e9a57efec..000000000 --- a/libraries/schema/svg_relation_schema.php +++ /dev/null @@ -1,127 +0,0 @@ -<?php -include_once("relation_schema.php"); -class PMA_SVG extends XMLWriter -{ - public $title; - public $author; - public $font; - public $fontSize; - - function __construct() - { - //$writer = new XMLWriter(); - $this->openMemory(); - /* Set indenting using three spaces, so output is formatted */ - $this->setIndent(TRUE); - $this->setIndentString(' '); - /* Create the XML document */ - $this->startDocument('1.0','UTF-8'); - $this->startDtd('svg','-//W3C//DTD SVG 1.1//EN','http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'); - $this->endDtd(); - } - - function setTitle($title) - { - $this->title=$title; - } - - function setAuthor($author) - { - $this->author=$author; - } - - function setFont($font) - { - $this->font=$font; - } - function setFontSize($fontSize) - { - $this->fontSize=$fontSize; - } - - function startSvgDoc($width,$height) - { - - $this->startElement('svg'); - $this->writeAttribute('width', $width); - $this->writeAttribute('height', $height); - $this->writeAttribute('xmlns', 'http://www.w3.org/2000/svg'); - $this->writeAttribute('version', '1.1'); - } - - function endSvgDoc() - { - - $this->endElement(); - $this->endDocument(); - } - - function showOutput() - { - - header('Content-type: image/svg+xml'); - $output = $this->flush(); - print $output; - } - - function printElement($name,$x,$y,$width='',$height='',$text='',$styles='') - { - - $this->startElement($name); - $this->writeAttribute('width',$width); - $this->writeAttribute('height',$height); - $this->writeAttribute('x', $x); - $this->writeAttribute('y', $y); - $this->writeAttribute('style', $styles); - if(isset($text)){ - $this->writeAttribute('font-family', $this->font); - $this->writeAttribute('font-size', $this->fontSize); - $this->text($text); - } - $this->endElement(); - } -} - -class PMA_SVG_RELAION_SCHEMA extends PMA_Relation_Schema -{ - - function __construct($page_number, $show_info = 0, $change_color = 0, $all_table_same_wide = 0, $show_keys = 0) - { - //global $pdf, $db, $cfgRelation, $with_doc; - global $db,$writer; - - $svg = new PMA_SVG(); - $this->setSameWidthTables($all_table_same_wide); - - $svg->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $page_number)); - $svg->SetAuthor('phpMyAdmin ' . PMA_VERSION); - $svg->setFont('Arial'); - $svg->setFontSize('16px'); - $svg->startSvgDoc('500px','500px'); - $svg->printElement('rect',0,0,'100','100',NULL,'fill:none;stroke:black;'); - $svg->printElement('text',100,100,'100','100','this is just a test'); - $svg->endSvgDoc(); - $svg->showOutput(); - //echo $svg->getTitle(); - /* $alltables=$this->getAllTables($db,$page_number); - foreach ($alltables AS $table) { - if (!isset($this->tables[$table])) { - $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->_tablewidth, $show_keys, $show_info); - // $this->tables[$table]=$table; - } - - if ($this->same_wide) { - $this->tables[$table]->width = $this->_tablewidth; - } - $this->PMA_RT_setMinMax($this->tables[$table]); - }*/ - - -/* print '<pre>'; - print_r(get_object_vars($svg)); - print_r($this); - print '</pre>'; - exit();*/ - } -} -?> \ No newline at end of file diff --git a/libraries/schema/user_schema.php b/libraries/schema/user_schema.php deleted file mode 100644 index af86e9117..000000000 --- a/libraries/schema/user_schema.php +++ /dev/null @@ -1,636 +0,0 @@ -<?php -/** - * This Class will interact with the user to gather the information - * about their tables for which they want to export the relational schema - */ - -class PMA_USER_SCHEMA -{ -/* private $_exportType; - public function __construct() - { - $this->_exportType='pdf'; // default export type - } - - public function setExportType($type) - { - $this->_exportType=$type; - } - - public function getExportType() - { - return $this->_exportType; - }*/ - - /** - * This function will process the user input - * - */ - public function userInputProcess($do) - { - global $action_choose,$chpage,$db,$cfgRelation,$cfg,$auto_layout_foreign,$auto_layout_internal,$newpage,$c_table_rows,$query_default_option; - // Now is the time to work on all changes - //echo $do; - if (isset($do)) { - switch ($do) { - case 'selectpage': - if ($action_choose=="1") { - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND page_nr = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - - unset($chpage); - } - break; - case 'createpage': - - $pdf_page_number = PMA_REL_create_page($newpage, $cfgRelation, $db, $query_default_option); - - // A u t o m a t i c l a y o u t - // ================================ - if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { - $all_tables = array(); - } - - if (isset($auto_layout_foreign)) { - // get the tables list - $tables = PMA_DBI_get_tables_full($db); - // find the ones who support FOREIGN KEY; it's not - // important that we group together InnoDB tables - // and PBXT tables, as this logic is just to put - // the tables on the layout, not to determine relations - $foreignkey_tables = array(); - foreach($tables as $table_name => $table_properties) { - if (PMA_foreignkey_supported($table_properties['ENGINE'])) { - $foreignkey_tables[] = $table_name; - } - } - $all_tables = $foreignkey_tables; - // could be improved by finding the tables which have the - // most references keys and placing them at the beginning - // of the array (so that they are all center of schema) - unset($tables, $foreignkey_tables); - } // endif auto_layout_foreign - - if (isset($auto_layout_internal)) { - // get the tables that have relations, by descending - // number of links - $master_tables = 'SELECT COUNT(master_table), master_table' - . ' FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) - . ' WHERE master_db = \'' . $db . '\'' - . ' GROUP BY master_table' - . ' ORDER BY ' . PMA_backquote('COUNT(master_table)') . ' DESC '; - $master_tables_rs = PMA_query_as_controluser($master_tables, FALSE, $query_default_option); - if ($master_tables_rs && PMA_DBI_num_rows($master_tables_rs) > 0) { - // first put all the master tables at beginning - // of the list, so they are near the center of - // the schema - while (list(, $master_table) = PMA_DBI_fetch_row($master_tables_rs)) { - $all_tables[] = $master_table; - } - - // then for each master, add its foreigns into an array - // of foreign tables, if not already there - // (a foreign might be foreign for more than - // one table, and might be a master itself) - - $foreign_tables = array(); - foreach ($all_tables as $master_table) { - $foreigners = PMA_getForeigners($db, $master_table); - foreach ($foreigners as $foreigner) { - if (!in_array($foreigner['foreign_table'], $foreign_tables)) { - $foreign_tables[] = $foreigner['foreign_table']; - } - } - } - - // then merge the arrays - foreach ($foreign_tables as $foreign_table) { - if (!in_array($foreign_table, $all_tables)) { - $all_tables[] = $foreign_table; - } - } - } // endif there are master tables - } // endif auto_layout_internal - - if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { - // now generate the coordinates for the schema, - // in a clockwise spiral - - $pos_x = 300; - $pos_y = 300; - $delta = 110; - $delta_mult = 1.10; - $direction = "right"; - foreach ($all_tables as $current_table) { - - // save current table's coordinates - $insert_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . '(db_name, table_name, pdf_page_number, x, y) ' - . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($current_table) . '\',' . $pdf_page_number . ',' . $pos_x . ',' . $pos_y . ')'; - PMA_query_as_controluser($insert_query, FALSE, $query_default_option); - - // compute for the next table - switch ($direction) { - case 'right': - $pos_x += $delta; - $direction = "down"; - $delta *= $delta_mult; - break; - case 'down': - $pos_y += $delta; - $direction = "left"; - $delta *= $delta_mult; - break; - case 'left': - $pos_x -= $delta; - $direction = "up"; - $delta *= $delta_mult; - break; - case 'up': - $pos_y -= $delta; - $direction = "right"; - $delta *= $delta_mult; - break; - } // end switch - } // end foreach - } // end if some auto-layout to do - - $chpage = $pdf_page_number; - - break; - - case 'edcoord': - for ($i = 0; $i < $c_table_rows; $i++) { - $arrvalue = 'c_table_' . $i; - global $$arrvalue; - $arrvalue = $$arrvalue; - if (!isset($arrvalue['x']) || $arrvalue['x'] == '') { - $arrvalue['x'] = 0; - } - if (!isset($arrvalue['y']) || $arrvalue['y'] == '') { - $arrvalue['y'] = 0; - } - if (isset($arrvalue['name']) && $arrvalue['name'] != '--') { - $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - // echo $test_query; - $test_rs = PMA_query_as_controluser($test_query, FALSE, $query_default_option); - if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { - if (isset($arrvalue['delete']) && $arrvalue['delete'] == 'y') { - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - } else { - $ch_query = 'UPDATE ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . 'SET x = ' . $arrvalue['x'] . ', y= ' . $arrvalue['y'] - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - } - } else { - $ch_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . '(db_name, table_name, pdf_page_number, x, y) ' - . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($arrvalue['name']) . '\', \'' . PMA_sqlAddslashes($chpage) . '\',' . $arrvalue['x'] . ',' . $arrvalue['y'] . ')'; - } - //echo $ch_query; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - } // end if - } // end for - break; - case 'deleteCrap': - foreach ($delrow as $current_row) { - $d_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' . "\n" - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . "\n" - . ' AND table_name = \'' . PMA_sqlAddslashes($current_row) . '\'' . "\n" - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($d_query, FALSE, $query_default_option); - } - break; - } // end switch - } // end if (isset($do)) - - } - - /** - * This function shows/displays the HTML FORM to create the page - * - */ - public function createPage() - { - global $db,$table; - ?> - <form method="post" action="export_relation_schema.php" name="frm_create_page"> - <fieldset> - <legend> - <?php echo __('Create a page !') . "\n"; ?> - </legend> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="do" value="createpage" /> - <table> - <tr> - <td><label for="id_newpage"><?php echo __('Page name'); ?></label></td> - <td><input type="text" name="newpage" id="id_newpage" size="20" maxlength="50" /></td> - </tr> - <tr> - <td><?php echo __('Automatic layout based on'); ?></td> - <td> - <input type="checkbox" name="auto_layout_internal" id="id_auto_layout_internal" /><label for="id_auto_layout_internal"> - <?php echo __('Internal relations'); ?></label><br /> - <?php - /** - * Check to see whether INNODB and PBXT storage engines are Available in MYSQL PACKAGE - * If available, then provide AutoLayout for Foreign Keys in Schema View - */ - if (PMA_StorageEngine::isValid('InnoDB') || PMA_StorageEngine::isValid('PBXT')) { - ?> - <input type="checkbox" name="auto_layout_foreign" id="id_auto_layout_foreign" /><label for="id_auto_layout_foreign"> - <?php echo __('FOREIGN KEY'); ?></label><br /> - <?php - } - ?> - </td></tr> - </table> - </fieldset> - <fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /> - </fieldset> - </form> - <?php - } - - /** - * This function shows/displays the created page names - * All page names in a list - * User can select any page number and edit it using dashboard etc - */ - public function selectPage() - { - global $db,$table,$query_default_option,$cfgRelation; - $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\''; - //echo $page_query; - $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); - if ($page_rs && PMA_DBI_num_rows($page_rs) > 0) { - ?> - <form method="get" action="export_relation_schema.php" name="frm_select_page"> - <fieldset> - <legend> - <?php echo __('Please select a page to edit') . "\n"; ?> - </legend> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="do" value="selectpage" /> - <select name="chpage" onchange="this.form.submit()"> - <?php - while ($curr_page = PMA_DBI_fetch_assoc($page_rs)) { - echo "\n" . ' ' - . '<option value="' . $curr_page['page_nr'] . '"'; - if (isset($chpage) && $chpage == $curr_page['page_nr']) { - echo ' selected="selected"'; - } - echo '>' . $curr_page['page_nr'] . ': ' . htmlspecialchars($curr_page['page_descr']) . '</option>'; - } // end while - echo "\n"; - ?> - </select> - <?php - $choices = array( - '0' => __('Edit'), - '1' => __('Delete')); - PMA_display_html_radio('action_choose', $choices, '0', false); - unset($choices); - ?> - </fieldset> - <fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /><br /> - </fieldset> - </form> - <?php - } // end IF - echo "\n"; - } // end function - - /** - * A dashboard will also be shown where you can position the tables - */ - public function showTableDashBoard() - { - global $db,$cfgRelation,$table,$cfg,$with_field_names,$with_field_names,$chpage; - // We will need an array of all tables in this db - $selectboxall = array('--'); - $alltab_rs = PMA_DBI_query('SHOW TABLES FROM ' . PMA_backquote($db) . ';', null, PMA_DBI_QUERY_STORE); - while ($val = @PMA_DBI_fetch_row($alltab_rs)) { - $selectboxall[] = $val[0]; - } - - // Now if we already have chosen a page number then we should show the - // tables involved - if (isset($chpage) && $chpage > 0) { - echo "\n"; - ?> - <h2><?php echo __('Select Tables') ;?></h2> - - <?php - $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); - $array_sh_page = array(); - $draginit = ''; - $reset_draginit = ''; - $i = 0; - while ($temp_sh_page = @PMA_DBI_fetch_assoc($page_rs)) { - $array_sh_page[] = $temp_sh_page; - } - - // Display WYSIWYG-PDF parts? - if ($cfg['WYSIWYG-PDF']) { - if (!isset($_POST['with_field_names']) && !isset($_POST['showwysiwyg'])) { - $with_field_names = TRUE; - } - ?> - <script type="text/javascript" src="./js/dom-drag.js"></script> - <form method="post" action="export_relation_schema.php" name="dragdrop"> - <input type="button" name="dragdrop" value="<?php echo __('Toggle scratchboard'); ?>" onclick="ToggleDragDrop('pdflayout');" /> - <input type="button" name="dragdropreset" value="<?php echo __('Reset'); ?>" onclick="resetDrag();" /> - </form> - <div id="pdflayout" class="pdflayout" style="visibility: hidden;"> - <?php - foreach ($array_sh_page as $key => $temp_sh_page) { - $drag_x = $temp_sh_page['x']; - $drag_y = $temp_sh_page['y']; - - $draginit .= ' Drag.init(getElement("table_' . $i . '"), null, 0, parseInt(myid.style.width)-2, 0, parseInt(myid.style.height)-5);' . "\n"; - $draginit .= ' getElement("table_' . $i . '").onDrag = function (x, y) { document.edcoord.elements["c_table_' . $i . '[x]"].value = parseInt(x); document.edcoord.elements["c_table_' . $i . '[y]"].value = parseInt(y) }' . "\n"; - $draginit .= ' getElement("table_' . $i . '").style.left = "' . $drag_x . 'px";' . "\n"; - $draginit .= ' getElement("table_' . $i . '").style.top = "' . $drag_y . 'px";' . "\n"; - $reset_draginit .= ' getElement("table_' . $i . '").style.left = "2px";' . "\n"; - $reset_draginit .= ' getElement("table_' . $i . '").style.top = "' . (15 * $i) . 'px";' . "\n"; - $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[x]"].value = "2"' . "\n"; - $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[y]"].value = "' . (15 * $i) . '"' . "\n"; - - $local_query = 'SHOW FIELDS FROM ' - . PMA_backquote($temp_sh_page['table_name']) - . ' FROM ' . PMA_backquote($db); - $fields_rs = PMA_DBI_query($local_query); - unset($local_query); - $fields_cnt = PMA_DBI_num_rows($fields_rs); - - echo '<div id="table_' . $i . '" class="pdflayout_table"><u>' . $temp_sh_page['table_name'] . '</u>'; - if (isset($with_field_names)) { - while ($row = PMA_DBI_fetch_assoc($fields_rs)) { - echo '<br />' . htmlspecialchars($row['Field']) . "\n"; - } - } - echo '</div>' . "\n"; - PMA_DBI_free_result($fields_rs); - unset($fields_rs); - - $i++; - } - ?> - </div> - <script type="text/javascript"> - //<![CDATA[ - function PDFinit() { - refreshLayout(); - myid = getElement('pdflayout'); - <?php echo $draginit; ?> - } - - function resetDrag() { - <?php echo $reset_draginit; ?> - } - //]]> - </script> - <?php - } // end if WYSIWYG-PDF - ?> - - <form method="post" action="export_relation_schema.php" name="edcoord"> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="chpage" value="<?php echo htmlspecialchars($chpage); ?>" /> - <input type="hidden" name="do" value="edcoord" /> - <table border="0"> - <tr> - <th><?php echo __('Table'); ?></th> - <th><?php echo __('Delete'); ?></th> - <th>X</th> - <th>Y</th> - </tr> - <?php - if (isset($ctable)) { - unset($ctable); - } - - - $i = 0; - $odd_row = true; - foreach ($array_sh_page as $dummy_sh_page => $sh_page) { - $_mtab = $sh_page['table_name']; - $tabExist[$_mtab] = FALSE; - echo "\n" . ' <tr class="'; - if ($odd_row) { - echo 'odd'; - } else { - echo 'even'; - } - echo '">'; - $odd_row != $odd_row; - echo "\n" . ' <td>' - . "\n" . ' <select name="c_table_' . $i . '[name]">'; - foreach ($selectboxall as $key => $value) { - echo "\n" . ' <option value="' . htmlspecialchars($value) . '"'; - if ($value == $sh_page['table_name']) { - echo ' selected="selected"'; - $tabExist[$_mtab] = TRUE; - } - echo '>' . htmlspecialchars($value) . '</option>'; - } // end while - echo "\n" . ' </select>' - . "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'x\', this.value)"' : '') . ' name="c_table_' . $i . '[x]" value="' . $sh_page['x'] . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'y\', this.value)"' : '') . ' name="c_table_' . $i . '[y]" value="' . $sh_page['y'] . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' </tr>'; - $i++; - } // end while - // Do one more empty row - echo "\n" . ' <tr class="'; - if ($odd_row) { - echo 'odd'; - } else { - echo 'even'; - } - $odd_row != $odd_row; - echo '">'; - echo "\n" . ' <td>' - . "\n" . ' <select name="c_table_' . $i . '[name]">'; - foreach ($selectboxall as $key => $value) { - echo "\n" . ' <option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($value) . '</option>'; - } - echo "\n" . ' </select>' - . "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" name="c_table_' . $i . '[x]" value="' . (isset($sh_page['x'])?$sh_page['x']:'') . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" name="c_table_' . $i . '[y]" value="' . (isset($sh_page['y'])?$sh_page['y']:'') . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' </tr>'; - echo "\n" . ' </table>' . "\n"; - - echo "\n" . ' <input type="hidden" name="c_table_rows" value="' . ($i + 1) . '" />'; - echo ($cfg['WYSIWYG-PDF'] ? "\n" . ' <input type="hidden" id="showwysiwyg" name="showwysiwyg" value="' . ((isset($showwysiwyg) && $showwysiwyg == '1') ? '1' : '0') . '" />' : ''); - echo "\n" . ' <input type="checkbox" name="with_field_names" ' . (isset($with_field_names) ? 'checked="checked"' : ''). ' />' . __('Column names') . '<br />'; - echo "\n" . ' <input type="submit" value="' . __('Save') . '" />'; - echo "\n" . '</form>' . "\n\n"; - } // end if - - $this->deleteTables($chpage); - - }// end function - - /** - * show Export relational schema generation options - */ - public function displaySchemaGenerationOptions() - { - global $cfg,$pmaThemeImage,$db,$test_rs,$chpage; - ?> - <!-- Export relational schema --> - <form method="post" action="process_relation_schema.php"> - <fieldset> - <legend> - <?php - echo PMA_generate_common_hidden_inputs($db); - if ($cfg['PropertiesIconic']) { - echo '<img class="icon" src="' . $pmaThemeImage . 'b_view.png"' - .' alt="" width="16" height="16" />'; - } - echo __('Display Relational Schema'); - ?>: - </legend> - - <select name="export_type" id="export_type"> - <option value="pdf" selected="selected"><?php echo __('PDF');?></option> - <option value="svg"><?php echo __('SVG');?></option> - <option value="dia"><?php echo __('DIA');?></option> - <option value="visio"><?php echo __('VISIO');?></option> - <option value="eps"><?php echo __('EPS');?></option> - </select> - <label><?php echo __('Select Export Relational Type');?></label><br /> - <?php - if (isset($test_rs)) { - ?> - <label for="pdf_page_number_opt"><?php echo __('Page number:'); ?></label> - <select name="pdf_page_number" id="pdf_page_number_opt"> - <?php - while ($pages = @PMA_DBI_fetch_assoc($test_rs)) { - echo ' <option value="' . $pages['page_nr'] . '">' - . $pages['page_nr'] . ': ' . htmlspecialchars($pages['page_descr']) . '</option>' . "\n"; - } // end while - PMA_DBI_free_result($test_rs); - unset($test_rs); - ?> - </select><br /> - <?php } else { ?> - <input type="hidden" name="pdf_page_number" value="<?php echo htmlspecialchars($chpage); ?>" /> - <?php } ?> - - <input type="checkbox" name="show_grid" id="show_grid_opt" /> - <label for="show_grid_opt"><?php echo __('Show grid'); ?></label><br /> - <input type="checkbox" name="show_color" id="show_color_opt" - checked="checked" /> - <label for="show_color_opt"><?php echo __('Show color'); ?></label><br /> - <input type="checkbox" name="show_table_dimension" id="show_table_dim_opt" /> - <label for="show_table_dim_opt"><?php echo __('Show dimension of tables'); ?> - </label><br /> - <input type="checkbox" name="all_tab_same_wide" id="all_tab_same_wide" /> - <label for="all_tab_same_wide"><?php echo __('Display all tables with the same width'); ?> - </label><br /> - <input type="checkbox" name="with_doc" id="with_doc" checked="checked" /> - <label for="with_doc"><?php echo __('Data Dictionary'); ?></label><br /> - <input type="checkbox" name="show_keys" id="show_keys" /> - <label for="show_keys"><?php echo __('Only show keys'); ?></label><br /> - - <label for="orientation_opt"><?php echo __('Data Dictionary Format'); ?></label> - <select name="orientation" id="orientation_opt"> - <option value="L"><?php echo __('Landscape');?></option> - <option value="P"><?php echo __('Portrait');?></option> - </select><br /> - - <label for="paper_opt"><?php echo __('Paper size'); ?></label> - <select name="paper" id="paper_opt"> - <?php - foreach ($cfg['PDFPageSizes'] as $key => $val) { - echo '<option value="' . $val . '"'; - if ($val == $cfg['PDFDefaultPageSize']) { - echo ' selected="selected"'; - } - echo ' >' . $val . '</option>' . "\n"; - } - ?> - </select> - </fieldset> - <fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /> - </fieldset> - </form> - <?php - }// end function - - /** - * Check if there are tables that need to be deleted in dashboard, - * if there are, ask the user for allowance - */ - public function deleteTables($chpage) - { - global $db, $table,$tabExist; - - $_strtrans = ''; - $_strname = ''; - $shoot = FALSE; - if (!empty($tabExist) && is_array($tabExist)) { - foreach ($tabExist as $key => $value) { - if (!$value) { - $_strtrans .= '<input type="hidden" name="delrow[]" value="' . htmlspecialchars($key) . '" />' . "\n"; - $_strname .= '<li>' . htmlspecialchars($key) . '</li>' . "\n"; - $shoot = TRUE; - } - } - if ($shoot) { - echo '<form action="export_relation_schema.php" method="post">' . "\n" - . PMA_generate_common_hidden_inputs($db, $table) - . '<input type="hidden" name="do" value="deleteCrap" />' . "\n" - . '<input type="hidden" name="chpage" value="' . htmlspecialchars($chpage) . '" />' . "\n" - . __('The current page has references to tables that no longer exist. Would you like to delete those references?') - . '<ul>' . "\n" - . $_strname - . '</ul>' . "\n" - . $_strtrans - . '<input type="submit" value="' . __('Go') . '" />' . "\n" - . '</form>'; - } - } - } // end function -} -?> \ No newline at end of file diff --git a/libraries/schema/visioSchema.class.php b/libraries/schema/visioSchema.class.php deleted file mode 100644 index 167d5220b..000000000 --- a/libraries/schema/visioSchema.class.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class visioSchema extends exportRelationSchema -{ - -} -?> \ No newline at end of file diff --git a/libraries/schema/visio_relation_schema.php b/libraries/schema/visio_relation_schema.php deleted file mode 100644 index d188304b9..000000000 --- a/libraries/schema/visio_relation_schema.php +++ /dev/null @@ -1,7 +0,0 @@ -<?php -include_once("relationSchema.abstract.class.php"); -class PMA_PDF_RELAION_SCHEMA extends PMA_Relation_Schema -{ - -} -?> \ No newline at end of file diff --git a/process_relation_schema.php b/process_relation_schema.php index 22714de65..b8188f28c 100644 --- a/process_relation_schema.php +++ b/process_relation_schema.php @@ -48,27 +48,27 @@ try switch($export_type) { case 'pdf'; - include_once("./libraries/schema/pdf_relation_schema.php"); - $obj_schema=new PMA_PDF_RELAION_SCHEMA($pdf_page_number, $show_table_dimension, $show_color, + include_once("./libraries/schema/Pdf_Relation_Schema.class.php"); + $obj_schema=new PMA_Pdf_Relation_Schema($pdf_page_number, $show_table_dimension, $show_color, $show_grid, $all_table_same_wide, $orientation, $paper, $show_keys ); break; case 'svg'; - include_once("./libraries/schema/svg_relation_schema.php"); - $obj_schema=new PMA_SVG_RELAION_SCHEMA($pdf_page_number, $show_table_dimension, $show_color, + include_once("./libraries/schema/Svg_Relation_Schema.class.php"); + $obj_schema=new PMA_Svg_Relation_Schema($pdf_page_number, $show_table_dimension, $show_color, $all_table_same_wide,$show_keys); break; case 'dia'; - include_once("./libraries/schema/diaSchema.class.php"); + include_once("./libraries/schema/Dia_Relation_Schema.class.php"); $obj_schema=new diaSchema(); break; case 'visio'; - include_once("./libraries/schema/visioSchema.class.php"); + include_once("./libraries/schema/Visio_Relation_Schema.class.php"); $obj_schema=new visioSchema(); break; case 'eps'; - include_once("./libraries/schema/epsSchema.class.php"); + include_once("./libraries/schema/Eps_Relation_Schema.class.php"); $obj_schema=new epsSchema(); break; } From 188c7b8d38fdffa19ed0cefe8ca0af047495adba Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Fri, 2 Jul 2010 16:31:32 +0500 Subject: [PATCH 12/48] User_Schema class : removed tabs + commented code + organized structure, Delete listing of schema classes in process relation schema file --- export_relation_schema.php | 90 +- .../schema/Dia_Relation_Schema.class.php | 11 + .../schema/Pdf_Relation_Schema.class.php | 2 +- libraries/schema/User_Schema.class.php | 1155 +++++++++-------- process_relation_schema.php | 82 -- 5 files changed, 682 insertions(+), 658 deletions(-) delete mode 100644 process_relation_schema.php diff --git a/export_relation_schema.php b/export_relation_schema.php index c3c985283..b8c2a4d17 100644 --- a/export_relation_schema.php +++ b/export_relation_schema.php @@ -20,20 +20,24 @@ $url_query .= '&amp;goto=export_relation_schema.php'; require_once './libraries/db_info.inc.php'; /** - * Settings for relation stuff + * Includ settings for relation stuff + * get all variables needed for exporting relational schema + * in $cfgRelation */ require_once './libraries/relation.lib.php'; $cfgRelation = PMA_getRelationsParam(); -// This is to avoid "Command out of sync" errors. Before switching this to -// a value of 0 (for MYSQLI_USE_RESULT), please check the logic -// to free results wherever needed. +/** + * This is to avoid "Command out of sync" errors. Before switching this to + * a value of 0 (for MYSQLI_USE_RESULT), please check the logic + * to free results wherever needed. + */ $query_default_option = PMA_DBI_QUERY_STORE; /** * Now in ./libraries/relation.lib.php we check for all tables * that we need, but if we don't find them we are quiet about it - * so people can work without. + * so people can't work without relational variables. * This page is absolutely useless if you didn't set up your tables * correctly, so it is a good place to see which tables we can and * complain ;-) @@ -63,46 +67,50 @@ if (!isset($cfgRelation['pdf_pages'])) { if ($cfgRelation['pdfwork']) { - /** - * User Object Created for displaying the HTML options - * so, user can play with it and perform export of relations schema - */ + /** + * User object created for presenting the HTML options + * so, user can interact with it and perform export of relations schema + */ - require_once './libraries/schema/user_schema.php'; - $user_schema = new PMA_User_Schema(); + require_once './libraries/schema/User_Schema.class.php'; + $user_schema = new PMA_User_Schema(); - /** - * This function will process the user input - * - */ - $user_schema->userInputProcess($do); - - /** - * Now first show some possibility to select a page for the export of relation schema - */ - $user_schema->selectPage(); - - /** - * Possibility to create a new page: - */ - $user_schema->createPage(); - - /** - * After selection of page or creating a page - * It will show you the list of tables - * A dashboard will also be shown where you can position the tables - */ - $user_schema->showTableDashBoard(); + /** + * This function will process the user input + */ + + $user_schema->processUserPreferences($do); + + /** + * Now first show some possibility to select a page for the export of relation schema + */ + + $user_schema->selectPage(); + + /** + * Possibility to create a new page: + */ + + $user_schema->createPage(); + + /** + * After selection of page or creating a page + * It will show you the list of tables + * A dashboard will also be shown where you can position the tables + */ + + $user_schema->showTableDashBoard(); if (isset($do) && ($do == 'edcoord' - || ($do == 'selectpage' && isset($chpage)) - || ($do == 'createpage' && isset($chpage)))) { - /** - * show Export schema generation options - */ + || ($do == 'selectpage' && isset($chpage) && $chpage != 0) + || ($do == 'createpage' && isset($chpage) && $chpage != 0))) { + + /** + * show Export schema generation options + */ $user_schema->displaySchemaGenerationOptions(); - + if ((isset($showwysiwyg) && $showwysiwyg == '1')) { ?> <script type="text/javascript"> @@ -110,8 +118,8 @@ if ($cfgRelation['pdfwork']) { ToggleDragDrop('pdflayout'); //]]> </script> - <?php - } + <?php + } } // end if } // end if ($cfgRelation['pdfwork']) diff --git a/libraries/schema/Dia_Relation_Schema.class.php b/libraries/schema/Dia_Relation_Schema.class.php index 2b027e1aa..30982a308 100644 --- a/libraries/schema/Dia_Relation_Schema.class.php +++ b/libraries/schema/Dia_Relation_Schema.class.php @@ -1,5 +1,16 @@ <?php include_once("Export_Relation_Schema.class.php"); + +class PMA_DIA extends XMLWriter +{ + public $title; + public $author; + public $font; + public $fontSize; + + +} + class PMA_Dia_Relation_Schema extends PMA_Export_Relation_Schema { diff --git a/libraries/schema/Pdf_Relation_Schema.class.php b/libraries/schema/Pdf_Relation_Schema.class.php index f94aa1a1d..3a4bc3724 100644 --- a/libraries/schema/Pdf_Relation_Schema.class.php +++ b/libraries/schema/Pdf_Relation_Schema.class.php @@ -325,7 +325,7 @@ class PMA_PDF extends TCPDF { $this->_out('/First ' . ($this->n + 1) . ' 0 R'); $this->_out('/Last ' . ($this->n + $last - $i) . ' 0 R'); $this->_out('/Count -' . $kids); - }tur + } $this->_out('>>'); $this->_out('endobj'); } diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php index 723ce3d69..60c703a92 100644 --- a/libraries/schema/User_Schema.class.php +++ b/libraries/schema/User_Schema.class.php @@ -1,608 +1,588 @@ <?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ /** - * This Class will interact with the user to gather the information - * about their tables for which they want to export the relational schema + * + * @version $Id$ + * @package phpMyAdmin */ - + +/** + * This Class interacts with the user to gather the information + * about their tables for which they want to export the relational schema + * export options are shown to user from they can choose + * + * @name + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + */ + class PMA_User_Schema -{ -/* private $_exportType; - public function __construct() - { - $this->_exportType='pdf'; // default export type - } - - public function setExportType($type) - { - $this->_exportType=$type; - } - - public function getExportType() - { - return $this->_exportType; - }*/ - - /** - * This function will process the user input - * - */ - public function userInputProcess($do) - { - global $action_choose,$chpage,$db,$cfgRelation,$cfg,$auto_layout_foreign,$auto_layout_internal,$newpage,$c_table_rows,$query_default_option; - // Now is the time to work on all changes - //echo $do; +{ + /** + * This function will process the user defined pages + * and tables which will be exported as Relational schema + * you can set the table positions on the paper via scratchboard + * for table positions, put the x,y co-ordinates + * + * @param string $do It is hidden value + * @access public + */ + + public function processUserPreferences($do) + { + global $action_choose,$chpage,$db,$cfgRelation,$cfg,$auto_layout_foreign,$auto_layout_internal,$newpage,$c_table_rows,$query_default_option; + if (isset($do)) { switch ($do) { - case 'selectpage': - if ($action_choose=="1") { - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + case 'selectpage': + if ($action_choose=="1") { + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND page_nr = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND page_nr = \'' . PMA_sqlAddslashes($chpage) . '\''; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - unset($chpage); - } - break; - case 'createpage': - - $pdf_page_number = PMA_REL_create_page($newpage, $cfgRelation, $db, $query_default_option); - - // A u t o m a t i c l a y o u t - // ================================ - if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { - $all_tables = array(); - } + unset($chpage); + } + break; + case 'createpage': + $pdf_page_number = PMA_REL_create_page($newpage, $cfgRelation, $db, $query_default_option); - if (isset($auto_layout_foreign)) { - // get the tables list - $tables = PMA_DBI_get_tables_full($db); - // find the ones who support FOREIGN KEY; it's not - // important that we group together InnoDB tables - // and PBXT tables, as this logic is just to put - // the tables on the layout, not to determine relations - $foreignkey_tables = array(); - foreach($tables as $table_name => $table_properties) { + /* + * A u t o m a t i c l a y o u t + * + * There are 2 kinds of relations in PMA + * 1) Internal Relations 2) Foreign Key Relations + */ + if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { + $all_tables = array(); + } + + if (isset($auto_layout_foreign)) { + /* + * get the tables list + * who support FOREIGN KEY, it's not + * important that we group together InnoDB tables + * and PBXT tables, as this logic is just to put + * the tables on the layout, not to determine relations + */ + $tables = PMA_DBI_get_tables_full($db); + $foreignkey_tables = array(); + foreach($tables as $table_name => $table_properties) { if (PMA_foreignkey_supported($table_properties['ENGINE'])) { $foreignkey_tables[] = $table_name; } - } - $all_tables = $foreignkey_tables; + } + $all_tables = $foreignkey_tables; // could be improved by finding the tables which have the // most references keys and placing them at the beginning // of the array (so that they are all center of schema) - unset($tables, $foreignkey_tables); - } // endif auto_layout_foreign + unset($tables, $foreignkey_tables); + } - if (isset($auto_layout_internal)) { - // get the tables that have relations, by descending - // number of links - $master_tables = 'SELECT COUNT(master_table), master_table' + if (isset($auto_layout_internal)) { + /* + * get the tables list who support Internal Relations; + * This type of relations will be created when + * you setup the PMA tables correctly + */ + $master_tables = 'SELECT COUNT(master_table), master_table' . ' FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) . ' WHERE master_db = \'' . $db . '\'' . ' GROUP BY master_table' . ' ORDER BY ' . PMA_backquote('COUNT(master_table)') . ' DESC '; - $master_tables_rs = PMA_query_as_controluser($master_tables, FALSE, $query_default_option); - if ($master_tables_rs && PMA_DBI_num_rows($master_tables_rs) > 0) { - // first put all the master tables at beginning - // of the list, so they are near the center of - // the schema - while (list(, $master_table) = PMA_DBI_fetch_row($master_tables_rs)) { - $all_tables[] = $master_table; - } + $master_tables_rs = PMA_query_as_controluser($master_tables, FALSE, $query_default_option); + if ($master_tables_rs && PMA_DBI_num_rows($master_tables_rs) > 0) { + /* first put all the master tables at beginning + * of the list, so they are near the center of + * the schema + */ + while (list(, $master_table) = PMA_DBI_fetch_row($master_tables_rs)) { + $all_tables[] = $master_table; + } - // then for each master, add its foreigns into an array - // of foreign tables, if not already there - // (a foreign might be foreign for more than - // one table, and might be a master itself) + /* Now for each master, add its foreigns into an array + * of foreign tables, if not already there + * (a foreign might be foreign for more than + * one table, and might be a master itself) + */ - $foreign_tables = array(); - foreach ($all_tables as $master_table) { + $foreign_tables = array(); + foreach ($all_tables as $master_table) { $foreigners = PMA_getForeigners($db, $master_table); foreach ($foreigners as $foreigner) { if (!in_array($foreigner['foreign_table'], $foreign_tables)) { $foreign_tables[] = $foreigner['foreign_table']; } } - } + } - // then merge the arrays - foreach ($foreign_tables as $foreign_table) { + /* + * Now merge the master and foreign arrays/tables + */ + foreach ($foreign_tables as $foreign_table) { if (!in_array($foreign_table, $all_tables)) { $all_tables[] = $foreign_table; } - } - } // endif there are master tables - } // endif auto_layout_internal + } + } // endif there are master tables + } // endif auto_layout_internal - if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { - // now generate the coordinates for the schema, - // in a clockwise spiral - - $pos_x = 300; - $pos_y = 300; - $delta = 110; - $delta_mult = 1.10; - $direction = "right"; - foreach ($all_tables as $current_table) { - - // save current table's coordinates + if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { + /* + * Now generate the coordinates for the schema + * in a clockwise spiral + */ + $pos_x = 300; + $pos_y = 300; + $delta = 110; + $delta_mult = 1.10; + $direction = "right"; + foreach ($all_tables as $current_table) { + /* + * save current table's coordinates + */ $insert_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' . '(db_name, table_name, pdf_page_number, x, y) ' . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($current_table) . '\',' . $pdf_page_number . ',' . $pos_x . ',' . $pos_y . ')'; PMA_query_as_controluser($insert_query, FALSE, $query_default_option); - // compute for the next table + /* + * compute for the next table + */ switch ($direction) { - case 'right': - $pos_x += $delta; - $direction = "down"; - $delta *= $delta_mult; - break; - case 'down': - $pos_y += $delta; - $direction = "left"; - $delta *= $delta_mult; - break; - case 'left': - $pos_x -= $delta; - $direction = "up"; - $delta *= $delta_mult; - break; - case 'up': - $pos_y -= $delta; - $direction = "right"; - $delta *= $delta_mult; - break; + case 'right': + $pos_x += $delta; + $direction = "down"; + $delta *= $delta_mult; + break; + case 'down': + $pos_y += $delta; + $direction = "left"; + $delta *= $delta_mult; + break; + case 'left': + $pos_x -= $delta; + $direction = "up"; + $delta *= $delta_mult; + break; + case 'up': + $pos_y -= $delta; + $direction = "right"; + $delta *= $delta_mult; + break; } // end switch - } // end foreach - } // end if some auto-layout to do + } // end foreach + } // end if some auto-layout to do - $chpage = $pdf_page_number; + $chpage = $pdf_page_number; + break; - break; - - case 'edcoord': - for ($i = 0; $i < $c_table_rows; $i++) { - $arrvalue = 'c_table_' . $i; - global $$arrvalue; - $arrvalue = $$arrvalue; - if (!isset($arrvalue['x']) || $arrvalue['x'] == '') { - $arrvalue['x'] = 0; - } - if (!isset($arrvalue['y']) || $arrvalue['y'] == '') { - $arrvalue['y'] = 0; - } - if (isset($arrvalue['name']) && $arrvalue['name'] != '--') { - $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - // echo $test_query; - $test_rs = PMA_query_as_controluser($test_query, FALSE, $query_default_option); - if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { - if (isset($arrvalue['delete']) && $arrvalue['delete'] == 'y') { - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - } else { - $ch_query = 'UPDATE ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . 'SET x = ' . $arrvalue['x'] . ', y= ' . $arrvalue['y'] - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - } - } else { - $ch_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . '(db_name, table_name, pdf_page_number, x, y) ' - . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($arrvalue['name']) . '\', \'' . PMA_sqlAddslashes($chpage) . '\',' . $arrvalue['x'] . ',' . $arrvalue['y'] . ')'; - } - //echo $ch_query; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - } // end if - } // end for - break; - case 'deleteCrap': - foreach ($delrow as $current_row) { - $d_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' . "\n" - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . "\n" - . ' AND table_name = \'' . PMA_sqlAddslashes($current_row) . '\'' . "\n" - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($d_query, FALSE, $query_default_option); + case 'edcoord': + for ($i = 0; $i < $c_table_rows; $i++) { + $arrvalue = 'c_table_' . $i; + global $$arrvalue; + $arrvalue = $$arrvalue; + if (!isset($arrvalue['x']) || $arrvalue['x'] == '') { + $arrvalue['x'] = 0; } - break; + if (!isset($arrvalue['y']) || $arrvalue['y'] == '') { + $arrvalue['y'] = 0; + } + if (isset($arrvalue['name']) && $arrvalue['name'] != '--') { + $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + // echo $test_query; + $test_rs = PMA_query_as_controluser($test_query, FALSE, $query_default_option); + if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { + if (isset($arrvalue['delete']) && $arrvalue['delete'] == 'y') { + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + } else { + $ch_query = 'UPDATE ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . 'SET x = ' . $arrvalue['x'] . ', y= ' . $arrvalue['y'] + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + } + } else { + $ch_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . '(db_name, table_name, pdf_page_number, x, y) ' + . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($arrvalue['name']) . '\', \'' . PMA_sqlAddslashes($chpage) . '\',' . $arrvalue['x'] . ',' . $arrvalue['y'] . ')'; + } + //echo $ch_query; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + } // end if + } // end for + break; + + case 'deleteCrap': + $this->_deleteTableRows(); + break; + + case 'process_export': + $this->_processExportSchema(); + break; + } // end switch } // end if (isset($do)) } - /** - * This function shows/displays the HTML FORM to create the page - * - */ + /** + * shows/displays the HTML FORM to create the page + * + * @access public + */ + public function createPage() { - global $db,$table; - ?> - <form method="post" action="export_relation_schema.php" name="frm_create_page"> - <fieldset> - <legend> - <?php echo __('Create a page !') . "\n"; ?> - </legend> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="do" value="createpage" /> - <table> - <tr> - <td><label for="id_newpage"><?php echo __('Page name'); ?></label></td> - <td><input type="text" name="newpage" id="id_newpage" size="20" maxlength="50" /></td> - </tr> - <tr> - <td><?php echo __('Automatic layout based on'); ?></td> - <td> - <input type="checkbox" name="auto_layout_internal" id="id_auto_layout_internal" /><label for="id_auto_layout_internal"> - <?php echo __('Internal relations'); ?></label><br /> - <?php - /** - * Check to see whether INNODB and PBXT storage engines are Available in MYSQL PACKAGE - * If available, then provide AutoLayout for Foreign Keys in Schema View - */ - if (PMA_StorageEngine::isValid('InnoDB') || PMA_StorageEngine::isValid('PBXT')) { - ?> - <input type="checkbox" name="auto_layout_foreign" id="id_auto_layout_foreign" /><label for="id_auto_layout_foreign"> - <?php echo __('FOREIGN KEY'); ?></label><br /> - <?php - } + global $db,$table; + ?> + <form method="post" action="export_relation_schema.php" name="frm_create_page"> + <fieldset> + <legend> + <?php echo __('Create a page !') . "\n"; ?> + </legend> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="do" value="createpage" /> + <table> + <tr> + <td><label for="id_newpage"><?php echo __('Page name'); ?></label></td> + <td><input type="text" name="newpage" id="id_newpage" size="20" maxlength="50" /></td> + </tr> + <tr> + <td><?php echo __('Automatic layout based on'); ?></td> + <td> + <input type="checkbox" name="auto_layout_internal" id="id_auto_layout_internal" /><label for="id_auto_layout_internal"> + <?php echo __('Internal relations'); ?></label><br /> + <?php + /* + * Check to see whether INNODB and PBXT storage engines are Available in MYSQL PACKAGE + * If available, then provide AutoLayout for Foreign Keys in Schema View + */ + + if (PMA_StorageEngine::isValid('InnoDB') || PMA_StorageEngine::isValid('PBXT')) { + ?> + <input type="checkbox" name="auto_layout_foreign" id="id_auto_layout_foreign" /><label for="id_auto_layout_foreign"> + <?php echo __('FOREIGN KEY'); ?></label><br /> + <?php + } ?> - </td></tr> - </table> - </fieldset> - <fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /> - </fieldset> - </form> - <?php + </td></tr> + </table> + </fieldset> + <fieldset class="tblFooters"> + <input type="submit" value="<?php echo __('Go'); ?>" /> + </fieldset> + </form> + <?php } - /** - * This function shows/displays the created page names - * All page names in a list - * User can select any page number and edit it using dashboard etc - */ - public function selectPage() - { - global $db,$table,$query_default_option,$cfgRelation; - $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + /** + * shows/displays the created page names in a drop down list + * User can select any page number and edit it using dashboard etc + * + * @access public + */ + + public function selectPage() + { + global $db,$table,$query_default_option,$cfgRelation,$chpage; + $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\''; - //echo $page_query; - $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); - if ($page_rs && PMA_DBI_num_rows($page_rs) > 0) { - ?> - <form method="get" action="export_relation_schema.php" name="frm_select_page"> - <fieldset> - <legend> - <?php echo __('Please select a page to edit') . "\n"; ?> - </legend> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="do" value="selectpage" /> - <select name="chpage" onchange="this.form.submit()"> - <?php - while ($curr_page = PMA_DBI_fetch_assoc($page_rs)) { - echo "\n" . ' ' - . '<option value="' . $curr_page['page_nr'] . '"'; - if (isset($chpage) && $chpage == $curr_page['page_nr']) { - echo ' selected="selected"'; - } - echo '>' . $curr_page['page_nr'] . ': ' . htmlspecialchars($curr_page['page_descr']) . '</option>'; - } // end while - echo "\n"; - ?> - </select> - <?php - $choices = array( - '0' => __('Edit'), - '1' => __('Delete')); - PMA_display_html_radio('action_choose', $choices, '0', false); - unset($choices); - ?> - </fieldset> - <fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /><br /> - </fieldset> - </form> - <?php - } // end IF - echo "\n"; + $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); + if ($page_rs && PMA_DBI_num_rows($page_rs) > 0) { + ?> + <form method="get" action="export_relation_schema.php" name="frm_select_page"> + <fieldset> + <legend> + <?php echo __('Please select a page to edit') . "\n"; ?> + </legend> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="do" value="selectpage" /> + <select name="chpage" onchange="this.form.submit()"> + <option value='0'><?php echo __('Select page'); ?></option> + <?php + while ($curr_page = PMA_DBI_fetch_assoc($page_rs)) { + echo "\n" . ' ' + . '<option value="' . $curr_page['page_nr'] . '"'; + if (isset($chpage) && $chpage == $curr_page['page_nr']) { + echo ' selected="selected"'; + } + echo '>' . $curr_page['page_nr'] . ': ' . htmlspecialchars($curr_page['page_descr']) . '</option>'; + } // end while + echo "\n"; + ?> + </select> + <?php + $choices = array( + '0' => __('Edit'), + '1' => __('Delete') + ); + PMA_display_html_radio('action_choose', $choices, '0', false); + unset($choices); + ?> + </fieldset> + <fieldset class="tblFooters"> + <input type="submit" value="<?php echo __('Go'); ?>" /><br /> + </fieldset> + </form> + <?php + } // end IF + echo "\n"; } // end function - - /** - * A dashboard will also be shown where you can position the tables - */ - public function showTableDashBoard() - { - global $db,$cfgRelation,$table,$cfg,$with_field_names,$with_field_names,$chpage; - // We will need an array of all tables in this db - $selectboxall = array('--'); - $alltab_rs = PMA_DBI_query('SHOW TABLES FROM ' . PMA_backquote($db) . ';', null, PMA_DBI_QUERY_STORE); - while ($val = @PMA_DBI_fetch_row($alltab_rs)) { - $selectboxall[] = $val[0]; - } - - // Now if we already have chosen a page number then we should show the - // tables involved - if (isset($chpage) && $chpage > 0) { - echo "\n"; - ?> - <h2><?php echo __('Select Tables') ;?></h2> - - <?php - $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); - $array_sh_page = array(); - $draginit = ''; - $reset_draginit = ''; - $i = 0; - while ($temp_sh_page = @PMA_DBI_fetch_assoc($page_rs)) { - $array_sh_page[] = $temp_sh_page; - } - - // Display WYSIWYG-PDF parts? + + /** + * A dashboard is displayed to AutoLayout the position of tables + * users can drag n drop the tables and change their positions + * + */ + + public function showTableDashBoard() + { + global $db,$cfgRelation,$table,$cfg,$with_field_names,$chpage; + /* + * We will need an array of all tables in this db + */ + $selectboxall = array('--'); + $alltab_rs = PMA_DBI_query('SHOW TABLES FROM ' . PMA_backquote($db) . ';', null, PMA_DBI_QUERY_STORE); + while ($val = @PMA_DBI_fetch_row($alltab_rs)) { + $selectboxall[] = $val[0]; + } + + /* + * Now if we already have chosen a page number then we should + * show the tables involved + */ + + if (isset($chpage) && $chpage > 0) { + echo "\n"; + ?> + <h2><?php echo __('Select Tables') ;?></h2> + <?php + $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); + $array_sh_page = array(); + $draginit = ''; + $reset_draginit = ''; + $i = 0; + while ($temp_sh_page = @PMA_DBI_fetch_assoc($page_rs)) { + $array_sh_page[] = $temp_sh_page; + } + + /* + * Display WYSIWYG-PDF parts? + */ + if ($cfg['WYSIWYG-PDF']) { if (!isset($_POST['with_field_names']) && !isset($_POST['showwysiwyg'])) { $with_field_names = TRUE; } - ?> - <script type="text/javascript" src="./js/dom-drag.js"></script> - <form method="post" action="export_relation_schema.php" name="dragdrop"> - <input type="button" name="dragdrop" value="<?php echo __('Toggle scratchboard'); ?>" onclick="ToggleDragDrop('pdflayout');" /> - <input type="button" name="dragdropreset" value="<?php echo __('Reset'); ?>" onclick="resetDrag();" /> - </form> - <div id="pdflayout" class="pdflayout" style="visibility: hidden;"> - <?php - foreach ($array_sh_page as $key => $temp_sh_page) { - $drag_x = $temp_sh_page['x']; - $drag_y = $temp_sh_page['y']; - - $draginit .= ' Drag.init(getElement("table_' . $i . '"), null, 0, parseInt(myid.style.width)-2, 0, parseInt(myid.style.height)-5);' . "\n"; - $draginit .= ' getElement("table_' . $i . '").onDrag = function (x, y) { document.edcoord.elements["c_table_' . $i . '[x]"].value = parseInt(x); document.edcoord.elements["c_table_' . $i . '[y]"].value = parseInt(y) }' . "\n"; - $draginit .= ' getElement("table_' . $i . '").style.left = "' . $drag_x . 'px";' . "\n"; - $draginit .= ' getElement("table_' . $i . '").style.top = "' . $drag_y . 'px";' . "\n"; - $reset_draginit .= ' getElement("table_' . $i . '").style.left = "2px";' . "\n"; - $reset_draginit .= ' getElement("table_' . $i . '").style.top = "' . (15 * $i) . 'px";' . "\n"; - $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[x]"].value = "2"' . "\n"; - $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[y]"].value = "' . (15 * $i) . '"' . "\n"; - - $local_query = 'SHOW FIELDS FROM ' - . PMA_backquote($temp_sh_page['table_name']) - . ' FROM ' . PMA_backquote($db); - $fields_rs = PMA_DBI_query($local_query); - unset($local_query); - $fields_cnt = PMA_DBI_num_rows($fields_rs); - - echo '<div id="table_' . $i . '" class="pdflayout_table"><u>' . $temp_sh_page['table_name'] . '</u>'; - if (isset($with_field_names)) { - while ($row = PMA_DBI_fetch_assoc($fields_rs)) { - echo '<br />' . htmlspecialchars($row['Field']) . "\n"; - } + $this->_displayScratchboardTables($array_sh_page,$draginit,$reset_draginit); + } + ?> + + <form method="post" action="export_relation_schema.php" name="edcoord"> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="chpage" value="<?php echo htmlspecialchars($chpage); ?>" /> + <input type="hidden" name="do" value="edcoord" /> + <table border="0"> + <tr> + <th><?php echo __('Table'); ?></th> + <th><?php echo __('Delete'); ?></th> + <th>X</th> + <th>Y</th> + </tr> + <?php + if (isset($ctable)) { + unset($ctable); } - echo '</div>' . "\n"; - PMA_DBI_free_result($fields_rs); - unset($fields_rs); - - $i++; - } - ?> - </div> - <script type="text/javascript"> - //<![CDATA[ - function PDFinit() { - refreshLayout(); - myid = getElement('pdflayout'); - <?php echo $draginit; ?> - } - - function resetDrag() { - <?php echo $reset_draginit; ?> - } - //]]> - </script> - <?php - } // end if WYSIWYG-PDF - ?> - - <form method="post" action="export_relation_schema.php" name="edcoord"> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="chpage" value="<?php echo htmlspecialchars($chpage); ?>" /> - <input type="hidden" name="do" value="edcoord" /> - <table border="0"> - <tr> - <th><?php echo __('Table'); ?></th> - <th><?php echo __('Delete'); ?></th> - <th>X</th> - <th>Y</th> - </tr> - <?php - if (isset($ctable)) { - unset($ctable); - } - - - $i = 0; - $odd_row = true; - foreach ($array_sh_page as $dummy_sh_page => $sh_page) { - $_mtab = $sh_page['table_name']; - $tabExist[$_mtab] = FALSE; - echo "\n" . ' <tr class="'; - if ($odd_row) { - echo 'odd'; - } else { - echo 'even'; - } - echo '">'; - $odd_row != $odd_row; - echo "\n" . ' <td>' - . "\n" . ' <select name="c_table_' . $i . '[name]">'; - foreach ($selectboxall as $key => $value) { - echo "\n" . ' <option value="' . htmlspecialchars($value) . '"'; - if ($value == $sh_page['table_name']) { - echo ' selected="selected"'; - $tabExist[$_mtab] = TRUE; - } - echo '>' . htmlspecialchars($value) . '</option>'; - } // end while - echo "\n" . ' </select>' - . "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'x\', this.value)"' : '') . ' name="c_table_' . $i . '[x]" value="' . $sh_page['x'] . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'y\', this.value)"' : '') . ' name="c_table_' . $i . '[y]" value="' . $sh_page['y'] . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' </tr>'; - $i++; - } // end while - // Do one more empty row - echo "\n" . ' <tr class="'; - if ($odd_row) { - echo 'odd'; - } else { - echo 'even'; - } - $odd_row != $odd_row; - echo '">'; - echo "\n" . ' <td>' - . "\n" . ' <select name="c_table_' . $i . '[name]">'; - foreach ($selectboxall as $key => $value) { - echo "\n" . ' <option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($value) . '</option>'; - } - echo "\n" . ' </select>' - . "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" name="c_table_' . $i . '[x]" value="' . (isset($sh_page['x'])?$sh_page['x']:'') . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" name="c_table_' . $i . '[y]" value="' . (isset($sh_page['y'])?$sh_page['y']:'') . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' </tr>'; - echo "\n" . ' </table>' . "\n"; - - echo "\n" . ' <input type="hidden" name="c_table_rows" value="' . ($i + 1) . '" />'; - echo ($cfg['WYSIWYG-PDF'] ? "\n" . ' <input type="hidden" id="showwysiwyg" name="showwysiwyg" value="' . ((isset($showwysiwyg) && $showwysiwyg == '1') ? '1' : '0') . '" />' : ''); - echo "\n" . ' <input type="checkbox" name="with_field_names" ' . (isset($with_field_names) ? 'checked="checked"' : ''). ' />' . __('Column names') . '<br />'; - echo "\n" . ' <input type="submit" value="' . __('Save') . '" />'; - echo "\n" . '</form>' . "\n\n"; + + $i = 0; + $odd_row = true; + foreach ($array_sh_page as $dummy_sh_page => $sh_page) { + $_mtab = $sh_page['table_name']; + $tabExist[$_mtab] = FALSE; + echo "\n" . ' <tr class="'; + if ($odd_row) { + echo 'odd'; + } else { + echo 'even'; + } + echo '">'; + $odd_row != $odd_row; + echo "\n" . ' <td>' + . "\n" . ' <select name="c_table_' . $i . '[name]">'; + foreach ($selectboxall as $key => $value) { + echo "\n" . ' <option value="' . htmlspecialchars($value) . '"'; + if ($value == $sh_page['table_name']) { + echo ' selected="selected"'; + $tabExist[$_mtab] = TRUE; + } + echo '>' . htmlspecialchars($value) . '</option>'; + } + echo "\n" . ' </select>' + . "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'x\', this.value)"' : '') . ' name="c_table_' . $i . '[x]" value="' . $sh_page['x'] . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'y\', this.value)"' : '') . ' name="c_table_' . $i . '[y]" value="' . $sh_page['y'] . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' </tr>'; + $i++; + } + /* + * Add one more empty row + */ + echo "\n" . ' <tr class="'; + if ($odd_row) { + echo 'odd'; + } else { + echo 'even'; + } + $odd_row != $odd_row; + echo '">'; + echo "\n" . ' <td>' + . "\n" . ' <select name="c_table_' . $i . '[name]">'; + foreach ($selectboxall as $key => $value) { + echo "\n" . ' <option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($value) . '</option>'; + } + echo "\n" . ' </select>' + . "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" name="c_table_' . $i . '[x]" value="' . (isset($sh_page['x'])?$sh_page['x']:'') . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" name="c_table_' . $i . '[y]" value="' . (isset($sh_page['y'])?$sh_page['y']:'') . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' </tr>'; + echo "\n" . ' </table>' . "\n"; + + echo "\n" . ' <input type="hidden" name="c_table_rows" value="' . ($i + 1) . '" />'; + echo ($cfg['WYSIWYG-PDF'] ? "\n" . ' <input type="hidden" id="showwysiwyg" name="showwysiwyg" value="' . ((isset($showwysiwyg) && $showwysiwyg == '1') ? '1' : '0') . '" />' : ''); + echo "\n" . ' <input type="checkbox" name="with_field_names" ' . (isset($with_field_names) ? 'checked="checked"' : ''). ' />' . __('Column names') . '<br />'; + echo "\n" . ' <input type="submit" value="' . __('Save') . '" />'; + echo "\n" . '</form>' . "\n\n"; } // end if - $this->deleteTables($chpage); - - }// end function + $this->_deleteTables($chpage); + } /** * show Export relational schema generation options - */ - public function displaySchemaGenerationOptions() - { - global $cfg,$pmaThemeImage,$db,$test_rs,$chpage; - ?> - <!-- Export relational schema --> - <form method="post" action="process_relation_schema.php"> - <fieldset> - <legend> - <?php - echo PMA_generate_common_hidden_inputs($db); - if ($cfg['PropertiesIconic']) { - echo '<img class="icon" src="' . $pmaThemeImage . 'b_view.png"' + * user can select export type of his own choice + * and the attributes related to it + */ + + public function displaySchemaGenerationOptions() + { + global $cfg,$pmaThemeImage,$db,$test_rs,$chpage; + ?> + <form method="post" action="export_relation_schema.php"> + <fieldset> + <legend> + <?php + echo PMA_generate_common_hidden_inputs($db); + if ($cfg['PropertiesIconic']) { + echo '<img class="icon" src="' . $pmaThemeImage . 'b_view.png"' .' alt="" width="16" height="16" />'; - } - echo __('Display Relational Schema'); - ?>: - </legend> - - <select name="export_type" id="export_type"> - <option value="pdf" selected="selected"><?php echo __('PDF');?></option> - <option value="svg"><?php echo __('SVG');?></option> - <option value="dia"><?php echo __('DIA');?></option> - <option value="visio"><?php echo __('VISIO');?></option> - <option value="eps"><?php echo __('EPS');?></option> - </select> - <label><?php echo __('Select Export Relational Type');?></label><br /> - <?php - if (isset($test_rs)) { - ?> - <label for="pdf_page_number_opt"><?php echo __('Page number:'); ?></label> - <select name="pdf_page_number" id="pdf_page_number_opt"> - <?php - while ($pages = @PMA_DBI_fetch_assoc($test_rs)) { - echo ' <option value="' . $pages['page_nr'] . '">' - . $pages['page_nr'] . ': ' . htmlspecialchars($pages['page_descr']) . '</option>' . "\n"; - } // end while - PMA_DBI_free_result($test_rs); - unset($test_rs); - ?> - </select><br /> - <?php } else { ?> - <input type="hidden" name="pdf_page_number" value="<?php echo htmlspecialchars($chpage); ?>" /> - <?php } ?> - - <input type="checkbox" name="show_grid" id="show_grid_opt" /> - <label for="show_grid_opt"><?php echo __('Show grid'); ?></label><br /> - <input type="checkbox" name="show_color" id="show_color_opt" - checked="checked" /> - <label for="show_color_opt"><?php echo __('Show color'); ?></label><br /> - <input type="checkbox" name="show_table_dimension" id="show_table_dim_opt" /> - <label for="show_table_dim_opt"><?php echo __('Show dimension of tables'); ?> - </label><br /> - <input type="checkbox" name="all_tab_same_wide" id="all_tab_same_wide" /> - <label for="all_tab_same_wide"><?php echo __('Display all tables with the same width'); ?> - </label><br /> - <input type="checkbox" name="with_doc" id="with_doc" checked="checked" /> - <label for="with_doc"><?php echo __('Data Dictionary'); ?></label><br /> - <input type="checkbox" name="show_keys" id="show_keys" /> - <label for="show_keys"><?php echo __('Only show keys'); ?></label><br /> - - <label for="orientation_opt"><?php echo __('Data Dictionary Format'); ?></label> - <select name="orientation" id="orientation_opt"> - <option value="L"><?php echo __('Landscape');?></option> - <option value="P"><?php echo __('Portrait');?></option> - </select><br /> - - <label for="paper_opt"><?php echo __('Paper size'); ?></label> - <select name="paper" id="paper_opt"> - <?php - foreach ($cfg['PDFPageSizes'] as $key => $val) { - echo '<option value="' . $val . '"'; - if ($val == $cfg['PDFDefaultPageSize']) { - echo ' selected="selected"'; - } - echo ' >' . $val . '</option>' . "\n"; - } - ?> - </select> - </fieldset> - <fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /> - </fieldset> - </form> - <?php - }// end function + } + echo __('Display Relational Schema'); + ?>: + </legend> + <select name="export_type" id="export_type"> + <option value="pdf" selected="selected"><?php echo __('PDF');?></option> + <option value="svg"><?php echo __('SVG');?></option> + <option value="dia"><?php echo __('DIA');?></option> + <option value="visio"><?php echo __('VISIO');?></option> + <option value="eps"><?php echo __('EPS');?></option> + </select> + <label><?php echo __('Select Export Relational Type');?></label><br /> + <?php + if (isset($test_rs)) { + ?> + <label for="pdf_page_number_opt"><?php echo __('Page number:'); ?></label> + <select name="pdf_page_number" id="pdf_page_number_opt"> + <?php + while ($pages = @PMA_DBI_fetch_assoc($test_rs)) { + echo ' <option value="' . $pages['page_nr'] . '">' + . $pages['page_nr'] . ': ' . htmlspecialchars($pages['page_descr']) . '</option>' . "\n"; + } // end while + PMA_DBI_free_result($test_rs); + unset($test_rs); + ?> + </select><br /> + <?php } else { ?> + <input type="hidden" name="pdf_page_number" value="<?php echo htmlspecialchars($chpage); ?>" /> + <?php } ?> + <input type="hidden" name="do" value="process_export" /> + <input type="checkbox" name="show_grid" id="show_grid_opt" /> + <label for="show_grid_opt"><?php echo __('Show grid'); ?></label><br /> + <input type="checkbox" name="show_color" id="show_color_opt" checked="checked" /> + <label for="show_color_opt"><?php echo __('Show color'); ?></label><br /> + <input type="checkbox" name="show_table_dimension" id="show_table_dim_opt" /> + <label for="show_table_dim_opt"><?php echo __('Show dimension of tables'); ?> + </label><br /> + <input type="checkbox" name="all_tab_same_wide" id="all_tab_same_wide" /> + <label for="all_tab_same_wide"><?php echo __('Display all tables with the same width'); ?> + </label><br /> + <input type="checkbox" name="with_doc" id="with_doc" checked="checked" /> + <label for="with_doc"><?php echo __('Data Dictionary'); ?></label><br /> + <input type="checkbox" name="show_keys" id="show_keys" /> + <label for="show_keys"><?php echo __('Only show keys'); ?></label><br /> + <label for="orientation_opt"><?php echo __('Data Dictionary Format'); ?></label> + <select name="orientation" id="orientation_opt"> + <option value="L"><?php echo __('Landscape');?></option> + <option value="P"><?php echo __('Portrait');?></option> + </select> + <br /> + + <label for="paper_opt"><?php echo __('Paper size'); ?></label> + <select name="paper" id="paper_opt"> + <?php + foreach ($cfg['PDFPageSizes'] as $key => $val) { + echo '<option value="' . $val . '"'; + if ($val == $cfg['PDFDefaultPageSize']) { + echo ' selected="selected"'; + } + echo ' >' . $val . '</option>' . "\n"; + } + ?> + </select> + </fieldset> + <fieldset class="tblFooters"> + <input type="submit" value="<?php echo __('Go'); ?>" /> + </fieldset> + </form> + <?php + }// end function /** * Check if there are tables that need to be deleted in dashboard, * if there are, ask the user for allowance + * @param string $chpage selected page + * @access private */ - public function deleteTables($chpage) + private function _deleteTables($chpage) { global $db, $table,$tabExist; @@ -632,5 +612,112 @@ class PMA_User_Schema } } } // end function + + /** + * Check if there are tables that need to be deleted in dashboard, + * if there are, ask the user for allowance + */ + + private function _displayScratchboardTables($array_sh_page,$draginit,$reset_draginit) + { + global $with_field_names,$cfg,$db; + ?> + <script type="text/javascript" src="./js/dom-drag.js"></script> + <form method="post" action="export_relation_schema.php" name="dragdrop"> + <input type="button" name="dragdrop" value="<?php echo __('Toggle scratchboard'); ?>" onclick="ToggleDragDrop('pdflayout');" /> + <input type="button" name="dragdropreset" value="<?php echo __('Reset'); ?>" onclick="resetDrag();" /> + </form> + <div id="pdflayout" class="pdflayout" style="visibility: hidden;"> + <?php + foreach ($array_sh_page as $key => $temp_sh_page) { + $drag_x = $temp_sh_page['x']; + $drag_y = $temp_sh_page['y']; + + $draginit .= ' Drag.init(getElement("table_' . $i . '"), null, 0, parseInt(myid.style.width)-2, 0, parseInt(myid.style.height)-5);' . "\n"; + $draginit .= ' getElement("table_' . $i . '").onDrag = function (x, y) { document.edcoord.elements["c_table_' . $i . '[x]"].value = parseInt(x); document.edcoord.elements["c_table_' . $i . '[y]"].value = parseInt(y) }' . "\n"; + $draginit .= ' getElement("table_' . $i . '").style.left = "' . $drag_x . 'px";' . "\n"; + $draginit .= ' getElement("table_' . $i . '").style.top = "' . $drag_y . 'px";' . "\n"; + $reset_draginit .= ' getElement("table_' . $i . '").style.left = "2px";' . "\n"; + $reset_draginit .= ' getElement("table_' . $i . '").style.top = "' . (15 * $i) . 'px";' . "\n"; + $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[x]"].value = "2"' . "\n"; + $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[y]"].value = "' . (15 * $i) . '"' . "\n"; + + $local_query = 'SHOW FIELDS FROM ' + . PMA_backquote($temp_sh_page['table_name']) + . ' FROM ' . PMA_backquote($db); + $fields_rs = PMA_DBI_query($local_query); + unset($local_query); + $fields_cnt = PMA_DBI_num_rows($fields_rs); + + echo '<div id="table_' . $i . '" class="pdflayout_table"><u>' . $temp_sh_page['table_name'] . '</u>'; + if (isset($with_field_names)) { + while ($row = PMA_DBI_fetch_assoc($fields_rs)) { + echo '<br />' . htmlspecialchars($row['Field']) . "\n"; + } + } + echo '</div>' . "\n"; + PMA_DBI_free_result($fields_rs); + unset($fields_rs); + $i++; + } + ?> + </div> + <script type="text/javascript"> + //<![CDATA[ + function PDFinit() { + refreshLayout(); + myid = getElement('pdflayout'); + <?php echo $draginit; ?> + } + + function resetDrag() { + <?php echo $reset_draginit; ?> + } + //]]> + </script> + <?php + } + + /** + * delete the table rows with table co-ordinates + * + * @access private + */ + + private function _deleteTableRows() + { + foreach ($delrow as $current_row) { + $del_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' . "\n" + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . "\n" + . ' AND table_name = \'' . PMA_sqlAddslashes($current_row) . '\'' . "\n" + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + PMA_query_as_controluser($del_query, FALSE, $query_default_option); + } + } + + /** + * get all the export options and verify + * call and include the appropriate Schema Class depending on $export_type + * + * @access private + */ + + private function _processExportSchema() + { + $pdf_page_number = isset($pdf_page_number) ? $pdf_page_number : 1; + $show_grid = (isset($show_grid) && $show_grid == 'on') ? 1 : 0; + $show_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; + $show_table_dimension = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; + $all_table_same_wide = (isset($all_table_same_wide) && $all_table_same_wide == 'on') ? 1 : 0; + $with_doc = (isset($with_doc) && $with_doc == 'on') ? 1 : 0; + $orientation = (isset($orientation) && $orientation == 'P') ? 'P' : 'L'; + $paper = isset($paper) ? $paper : 'A4'; + $show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; + $export_type = isset($export_type) ? $export_type : 'pdf'; // default is PDF + PMA_DBI_select_db($db); + + include("./libraries/schema/".ucfirst($export_type)."_Relation_Schema.class.php"); + $obj_schema = eval("new PMA_".ucfirst($export_type)."_Relation_Schema();"); + } } ?> \ No newline at end of file diff --git a/process_relation_schema.php b/process_relation_schema.php deleted file mode 100644 index b8188f28c..000000000 --- a/process_relation_schema.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -error_reporting(E_ALL | E_WARNING); -/** - * Gets some core scripts - */ -require_once './libraries/common.inc.php'; - -/** - * Settings for relation stuff - */ -require_once './libraries/relation.lib.php'; -require_once './libraries/transformations.lib.php'; -require_once './libraries/Index.class.php'; - -$cfgRelation = PMA_getRelationsParam(); - -/** - * Now in ./libraries/relation.lib.php we check for all tables - * that we need, but if we don't find them we are quiet about it - * so people can work without. - * This page is absolutely useless if you didn't set up your tables - * correctly, so it is a good place to see which tables we can and - * complain ;-) - */ -if (!$cfgRelation['pdfwork']) { - echo '<font color="red">' . __('Error') . '</font><br />' . "\n"; - $url_to_goto = '<a href="' . $cfg['PmaAbsoluteUri'] . 'chk_rel.php?' . $url_query . '">'; - echo sprintf(__('The additional features for working with linked tables have been deactivated. To find out why click %shere%s.'), $url_to_goto, '</a>') . "\n"; -} - -/** - * Main logic - */ -try - { - $pdf_page_number = isset($pdf_page_number) ? $pdf_page_number : 1; - $show_grid = (isset($show_grid) && $show_grid == 'on') ? 1 : 0; - $show_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; - $show_table_dimension = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; - $all_table_same_wide = (isset($all_table_same_wide) && $all_table_same_wide == 'on') ? 1 : 0; - $with_doc = (isset($with_doc) && $with_doc == 'on') ? 1 : 0; - $orientation = (isset($orientation) && $orientation == 'P') ? 'P' : 'L'; - $paper = isset($paper) ? $paper : 'A4'; - $show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; - $export_type = isset($export_type) ? $export_type : 'pdf'; // default is PDF - PMA_DBI_select_db($db); - - switch($export_type) - { - case 'pdf'; - include_once("./libraries/schema/Pdf_Relation_Schema.class.php"); - $obj_schema=new PMA_Pdf_Relation_Schema($pdf_page_number, $show_table_dimension, $show_color, - $show_grid, $all_table_same_wide, $orientation, $paper, - $show_keys - ); - break; - case 'svg'; - include_once("./libraries/schema/Svg_Relation_Schema.class.php"); - $obj_schema=new PMA_Svg_Relation_Schema($pdf_page_number, $show_table_dimension, $show_color, - $all_table_same_wide,$show_keys); - break; - case 'dia'; - include_once("./libraries/schema/Dia_Relation_Schema.class.php"); - $obj_schema=new diaSchema(); - break; - case 'visio'; - include_once("./libraries/schema/Visio_Relation_Schema.class.php"); - $obj_schema=new visioSchema(); - break; - case 'eps'; - include_once("./libraries/schema/Eps_Relation_Schema.class.php"); - $obj_schema=new epsSchema(); - break; - } -} -catch (Exception $e) -{ - print('<pre>'); - print_r($e); - print('</pre>'); -} -?> \ No newline at end of file From ba9924054be621d2bad7d977eb05180c01e31b8c Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Sat, 3 Jul 2010 13:52:43 +0500 Subject: [PATCH 13/48] export_relation_schema.php: commenting improved --- export_relation_schema.php | 13 ++++++++++--- libraries/schema/User_Schema.class.php | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/export_relation_schema.php b/export_relation_schema.php index b8c2a4d17..1c135e5b7 100644 --- a/export_relation_schema.php +++ b/export_relation_schema.php @@ -76,19 +76,26 @@ if ($cfgRelation['pdfwork']) { $user_schema = new PMA_User_Schema(); /** - * This function will process the user input + * This function will process the user defined pages + * and tables which will be exported as Relational schema + * you can set the table positions on the paper via scratchboard + * for table positions, put the x,y co-ordinates + * + * @param string $do It tells what the Schema is supposed to do + * create and select a page, generate schema etc */ $user_schema->processUserPreferences($do); /** - * Now first show some possibility to select a page for the export of relation schema + * Show some possibility to select a page for the export of relation schema + * Lists all pages created before and can select and edit from them */ $user_schema->selectPage(); /** - * Possibility to create a new page: + * Create a new page where relations will be drawn */ $user_schema->createPage(); diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php index 60c703a92..29345a9df 100644 --- a/libraries/schema/User_Schema.class.php +++ b/libraries/schema/User_Schema.class.php @@ -25,7 +25,8 @@ class PMA_User_Schema * you can set the table positions on the paper via scratchboard * for table positions, put the x,y co-ordinates * - * @param string $do It is hidden value + * @param string $do It tells what the Schema is supposed to do + * create and select a page, generate schema etc * @access public */ From 39db171819a65a49699180705b2ecae2968b4c2c Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Sat, 3 Jul 2010 23:50:13 +0500 Subject: [PATCH 14/48] because of changes in User Schema etc PDF Schema wasn't working, It is fixed now --- .../schema/Export_Relation_Schema.class.php | 24 +++++++-- .../schema/Pdf_Relation_Schema.class.php | 50 +++++++------------ libraries/schema/User_Schema.class.php | 19 ++++--- libraries/tcpdf/tcpdf.php | 15 +++--- 4 files changed, 57 insertions(+), 51 deletions(-) diff --git a/libraries/schema/Export_Relation_Schema.class.php b/libraries/schema/Export_Relation_Schema.class.php index 0d0659433..4924914c7 100644 --- a/libraries/schema/Export_Relation_Schema.class.php +++ b/libraries/schema/Export_Relation_Schema.class.php @@ -1,6 +1,22 @@ <?php -// Using Abstract Factory Pattern for exporting relational Schema in different Formats ! -abstract class PMA_Export_Relation_Schema +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * + * @version $Id$ + * @package phpMyAdmin + */ + +/** + * This class is inherited by all schema classes + * It contains those methods which are common in them + * + * @name + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + */ + +class PMA_Export_Relation_Schema { private $_pageTitle; // Title of the page private $_autoLayoutType; // Internal or Foreign Key Relations; @@ -31,7 +47,7 @@ abstract class PMA_Export_Relation_Schema // echo $tab_sql; $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { - $this->PMA_SCHEMA_die('',__('No tables')); + $this->_die('',__('No tables')); } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); } @@ -47,7 +63,7 @@ abstract class PMA_Export_Relation_Schema /** * Displays an error message */ - function PMA_Schema_die($type = '',$error_message = '') + function _die($type = '',$error_message = '') { global $db; diff --git a/libraries/schema/Pdf_Relation_Schema.class.php b/libraries/schema/Pdf_Relation_Schema.class.php index 3a4bc3724..f73f54822 100644 --- a/libraries/schema/Pdf_Relation_Schema.class.php +++ b/libraries/schema/Pdf_Relation_Schema.class.php @@ -219,7 +219,7 @@ class PMA_PDF extends TCPDF { */ function Error($error_message = '') { - PMA_Relation_Schema::PMA_Schema_die($error_message); + PMA_Export_Relation_Schema::_die($error_message); } // end of the "Error()" method function Header() { @@ -822,15 +822,24 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema * @access private * @see PMA_PDF */ - function __construct($page_number, $show_info = 0, $change_color = 0, $show_grid = 0, $all_table_same_wide = 0, $orientation = 'L', $paper = 'A4', $show_keys = 0) + function __construct() { - global $pdf, $db, $cfgRelation, $with_doc; + //global $pdf,$db,$export_type,$cfgRelation,$pdf_page_number,$show_keys,$change_color,$show_table_dimension,$orientation, $with_doc,$show_grid,$paper; + global $pdf,$db,$export_type,$cfgRelation,$pdf_page_number,$show_grid,$show_keys,$change_color,$show_color,$show_info,$all_table_same_wide,$show_table_dimension,$orientation, $with_doc,$show_grid,$paper; + $pdf_page_number = isset($pdf_page_number) ? $pdf_page_number : 1; + $show_grid = (isset($show_grid) && $show_grid == 'on') ? 1 : 0; + $change_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; + $show_info = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; + $all_table_same_wide = (isset($all_table_same_wide) && $all_table_same_wide == 'on') ? 1 : 0; + $with_doc = (isset($with_doc) && $with_doc == 'on') ? 1 : 0; + $orientation = (isset($orientation) && $orientation == 'P') ? 'P' : 'L'; + $paper = isset($paper) ? $paper : 'A4'; + $show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; $this->setSameWidthTables($all_table_same_wide); - // Initializes a new document $pdf = new PMA_PDF('L', 'mm', $paper); - $pdf->SetTitle(sprintf(__('Schema of the %s database - Page %s'), $GLOBALS['db'], $page_number)); + $pdf->SetTitle(sprintf(__('Schema of the %s database - Page %s'), $GLOBALS['db'], $pdf_page_number)); $pdf->setCMargin(0); $pdf->Open(); $pdf->SetAuthor('phpMyAdmin ' . PMA_VERSION); @@ -839,29 +848,10 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema $pdf->AddFont('DejaVuSans', 'B', 'dejavusansb.php'); $pdf->AddFont('DejaVuSerif', '', 'dejavuserif.php'); $pdf->AddFont('DejaVuSerif', 'B', 'dejavuserifb.php'); - // $this->ff = PMA_PDF_FONT; - $pdf->SetFont($this->ff, '', 14); $pdf->SetAutoPageBreak('auto'); - + $alltables = $this->getAllTables($db,$pdf_page_number); - // Gets tables on this page -/* $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = ' . $page_number; - echo $tab_sql; - $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); - if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { - $pdf->PMA_PDF_die(__('No tables')); - // die('No tables'); - } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { - $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); - // $intable = '\'' . implode('\', \'', $alltables) . '\''; - }*/ - $alltables=$this->getAllTables($db,$page_number); - - - // make doc // if ($with_doc) { $pdf->SetAutoPageBreak('auto', 15); $pdf->setCMargin(1); @@ -879,9 +869,8 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema $this->t_marg = 28; $this->b_marg = 28; } - - /* snip */ + /* snip */ foreach ($alltables as $table) { if (!isset($this->tables[$table])) { $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->_tablewidth, $show_keys, $show_info); @@ -937,16 +926,14 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema } // end if } // end while -/* print '<pre>'; - print_r(get_object_vars($this)); - print '</pre>'; - exit();*/ + if ($seen_a_relation) { $this->PMA_RT_drawRelations($change_color); } $this->PMA_RT_drawTables($change_color); + $this->PMA_RT_showRt(); } // end of the "PMA_RT()" Constructor method @@ -1074,6 +1061,7 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema if (empty($filename)) { $filename = $pdf_page_number . '.pdf'; } + //exit(); // $pdf->Output($db . '_' . $filename, TRUE); $pdf->Output($db . '_' . $filename, 'I'); // destination: Inline } // end of the "PMA_RT_showRt()" method diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php index 29345a9df..5c5fd9754 100644 --- a/libraries/schema/User_Schema.class.php +++ b/libraries/schema/User_Schema.class.php @@ -705,16 +705,15 @@ class PMA_User_Schema private function _processExportSchema() { - $pdf_page_number = isset($pdf_page_number) ? $pdf_page_number : 1; - $show_grid = (isset($show_grid) && $show_grid == 'on') ? 1 : 0; - $show_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; - $show_table_dimension = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; - $all_table_same_wide = (isset($all_table_same_wide) && $all_table_same_wide == 'on') ? 1 : 0; - $with_doc = (isset($with_doc) && $with_doc == 'on') ? 1 : 0; - $orientation = (isset($orientation) && $orientation == 'P') ? 'P' : 'L'; - $paper = isset($paper) ? $paper : 'A4'; - $show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; - $export_type = isset($export_type) ? $export_type : 'pdf'; // default is PDF + /** + * Settings for relation stuff + */ + require_once './libraries/transformations.lib.php'; + require_once './libraries/Index.class.php'; + /** + * default is PDF + */ + $export_type = isset($export_type) ? $export_type : 'pdf'; PMA_DBI_select_db($db); include("./libraries/schema/".ucfirst($export_type)."_Relation_Schema.class.php"); diff --git a/libraries/tcpdf/tcpdf.php b/libraries/tcpdf/tcpdf.php index 26f2dc1ba..8656ebc61 100644 --- a/libraries/tcpdf/tcpdf.php +++ b/libraries/tcpdf/tcpdf.php @@ -2977,9 +2977,11 @@ if(!class_exists('TCPDF', false)) { switch($dest) { case 'I': { //Send to standard output - if(ob_get_contents()) { - $this->Error('Some data has already been output, can\'t send PDF file'); - } + //if(ob_get_contents()) { + // $this->Error('Some data has already been output, can\'t send PDF file'); + //} + ob_end_clean(); + ob_start(); if(php_sapi_name()!='cli') { //We send to a browser header('Content-Type: application/pdf'); @@ -2994,9 +2996,10 @@ if(!class_exists('TCPDF', false)) { } case 'D': { //Download file - if(ob_get_contents()) { - $this->Error('Some data has already been output, can\'t send PDF file'); - } + // if(ob_get_contents()) { + // $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); + //} + if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { header('Content-Type: application/force-download'); } else { From cd41a8dc281561d00fe66237fe335d944582ae07 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Thu, 8 Jul 2010 12:05:52 +0500 Subject: [PATCH 15/48] SVG Schema Class --- .../schema/Svg_Relation_Schema.class.php | 768 +++++++++++++++--- 1 file changed, 662 insertions(+), 106 deletions(-) diff --git a/libraries/schema/Svg_Relation_Schema.class.php b/libraries/schema/Svg_Relation_Schema.class.php index c37a86c6b..cc72cfddc 100644 --- a/libraries/schema/Svg_Relation_Schema.class.php +++ b/libraries/schema/Svg_Relation_Schema.class.php @@ -1,127 +1,683 @@ <?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * + * @version $Id$ + * @package phpMyAdmin + */ + include_once("Export_Relation_Schema.class.php"); + +/** + * This Class inherits the XMLwriter class and + * helps in developing structure of SVG Schema + * + * + * @name PMA_SVG + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @see http://php.net/manual/en/book.xmlwriter.php + */ + class PMA_SVG extends XMLWriter { - public $title; - public $author; - public $font; - public $fontSize; - - function __construct() - { - //$writer = new XMLWriter(); - $this->openMemory(); - /* Set indenting using three spaces, so output is formatted */ - $this->setIndent(TRUE); - $this->setIndentString(' '); - /* Create the XML document */ - $this->startDocument('1.0','UTF-8'); - $this->startDtd('svg','-//W3C//DTD SVG 1.1//EN','http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'); - $this->endDtd(); - } - - function setTitle($title) - { - $this->title=$title; - } - - function setAuthor($author) - { - $this->author=$author; - } - - function setFont($font) - { - $this->font=$font; - } - function setFontSize($fontSize) - { - $this->fontSize=$fontSize; - } - - function startSvgDoc($width,$height) - { + public $title; + public $author; + public $font; + public $fontSize; - $this->startElement('svg'); - $this->writeAttribute('width', $width); - $this->writeAttribute('height', $height); - $this->writeAttribute('xmlns', 'http://www.w3.org/2000/svg'); - $this->writeAttribute('version', '1.1'); - } - - function endSvgDoc() - { + function __construct() + { + $this->openMemory(); + /* + * Set indenting using three spaces, + * so output is formatted + */ - $this->endElement(); - $this->endDocument(); - } - - function showOutput() - { + $this->setIndent(TRUE); + $this->setIndentString(' '); + /* + * Create the XML document + */ - header('Content-type: image/svg+xml'); - $output = $this->flush(); - print $output; - } - - function printElement($name,$x,$y,$width='',$height='',$text='',$styles='') - { + $this->startDocument('1.0','UTF-8'); + $this->startDtd('svg','-//W3C//DTD SVG 1.1//EN','http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'); + $this->endDtd(); + } - $this->startElement($name); - $this->writeAttribute('width',$width); - $this->writeAttribute('height',$height); - $this->writeAttribute('x', $x); - $this->writeAttribute('y', $y); - $this->writeAttribute('style', $styles); - if(isset($text)){ - $this->writeAttribute('font-family', $this->font); - $this->writeAttribute('font-size', $this->fontSize); - $this->text($text); - } - $this->endElement(); - } + function setTitle($title) + { + $this->title=$title; + } + + function setAuthor($author) + { + $this->author=$author; + } + + function setFont($font) + { + $this->font=$font; + } + function getFont() + { + return $this->font; + } + + function setFontSize($fontSize) + { + $this->fontSize=$fontSize; + } + + function getFontSize() + { + return $this->fontSize; + } + + function startSvgDoc($width,$height) + { + $this->startElement('svg'); + $this->writeAttribute('width', $width); + $this->writeAttribute('height', $height); + $this->writeAttribute('xmlns', 'http://www.w3.org/2000/svg'); + $this->writeAttribute('version', '1.1'); + } + + function endSvgDoc() + { + $this->endElement(); + $this->endDocument(); + } + + function showOutput() + { + if(ob_end_clean()){ + ob_end_clean(); + //ob_start(); + } + header('Content-type: image/svg+xml'); + $output = $this->flush(); + print $output; + } + + function printElement($name,$x,$y,$width='',$height='',$text='',$styles='') + { + $this->startElement($name); + $this->writeAttribute('width',$width); + $this->writeAttribute('height',$height); + $this->writeAttribute('x', $x); + $this->writeAttribute('y', $y); + $this->writeAttribute('style', $styles); + if(isset($text)){ + $this->writeAttribute('font-family', $this->font); + $this->writeAttribute('font-size', $this->fontSize); + $this->text($text); + } + $this->endElement(); + } + + function printElementLine($name,$x1,$y1,$x2,$y2,$styles) + { + $this->startElement($name); + $this->writeAttribute('x1',$x1); + $this->writeAttribute('y1',$y1); + $this->writeAttribute('x2', $x2); + $this->writeAttribute('y2', $y2); + $this->writeAttribute('style', $styles); + $this->endElement(); + } + + function getStringWidth($text,$font,$fontSize) + { + /* + * Start by counting the width, giving each character a modifying value + */ + $count = 0; + $count = $count + ((strlen($text) - strlen(str_replace(array("i","j","l"),"",$text)))*0.23);//ijl + $count = $count + ((strlen($text) - strlen(str_replace(array("f"),"",$text)))*0.27);//f + $count = $count + ((strlen($text) - strlen(str_replace(array("t","I"),"",$text)))*0.28);//tI + $count = $count + ((strlen($text) - strlen(str_replace(array("r"),"",$text)))*0.34);//r + $count = $count + ((strlen($text) - strlen(str_replace(array("1"),"",$text)))*0.49);//1 + $count = $count + ((strlen($text) - strlen(str_replace(array("c","k","s","v","x","y","z","J"),"",$text)))*0.5);//cksvxyzJ + $count = $count + ((strlen($text) - strlen(str_replace(array("a","b","d","e","g","h","n","o","p","q","u","L","0","2","3","4","5","6","7","8","9"),"",$text)))*0.56);//abdeghnopquL023456789 + $count = $count + ((strlen($text) - strlen(str_replace(array("F","T","Z"),"",$text)))*0.61);//FTZ + $count = $count + ((strlen($text) - strlen(str_replace(array("A","B","E","K","P","S","V","X","Y"),"",$text)))*0.67);//ABEKPSVXY + $count = $count + ((strlen($text) - strlen(str_replace(array("w","C","D","H","N","R","U"),"",$text)))*0.73);//wCDHNRU + $count = $count + ((strlen($text) - strlen(str_replace(array("G","O","Q"),"",$text)))*0.78);//GOQ + $count = $count + ((strlen($text) - strlen(str_replace(array("m","M"),"",$text)))*0.84);//mM + $count = $count + ((strlen($text) - strlen(str_replace("W","",$text)))*.95);//W + $count = $count + ((strlen($text) - strlen(str_replace(" ","",$text)))*.28);//" " + $text = str_replace(" ","",$text);//remove the " "'s + $count = $count + (strlen(preg_replace("/[a-z0-9]/i","",$text))*0.3); //all other chrs + + $modifier = 1; + $font = strtolower($font); + switch($font){ + /* + * no modifier for arial and sans-serif + */ + case 'arial': + case 'sans-serif': + break; + /* + * .92 modifer for time, serif, brushscriptstd, and californian fb + */ + case 'times': + case 'serif': + case 'brushscriptstd': + case 'californian fb': + $modifier = .92; + break; + /* + * 1.23 modifier for broadway + */ + case 'broadway': + $modifier = 1.23; + break; + } + $textWidth = $count*$fontSize; + return ceil($textWidth*$modifier); + } } +/** + * Draws tables schema + */ +class Table_Stats { + /** + * Defines properties + */ + + private $_tableName; + private $_showInfo = false; + + public $width = 0; + public $height; + public $fields = array(); + public $heightCell = 0; + public $x, $y; + public $primary = array(); + + + /** + * The "Table_Stats" constructor + * + * @param string table_name The table name + * @param integer ff The font size + * @param integer samewidth The max. with among tables + * @param boolean show_keys Whether to display keys or not + * @param boolean show_info Whether to display table position or not + * @global object The current SVG image document + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @global array The relations settings + * @global string The current db name + * @access private + * @see PMA_PDF, Table_Stats::Table_Stats_setWidth, + Table_Stats::Table_Stats_setHeight + */ + function __construct($tableName, $font, $fontSize, &$same_wide_width, $showKeys = false, $showInfo = false) + { + global $svg, $pdf_page_number, $cfgRelation, $db; + + $this->_tableName = $tableName; + $sql = 'DESCRIBE ' . PMA_backquote($tableName); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $svg->dieSchema(sprintf(__('The %s table doesn\'t exist!'), $tableName)); + } + + /* + * load fields + * check to see if it will load all fields or only the foreign keys + */ + + if ($showKeys) { + $indexes = PMA_Index::getFromTable($this->_tableName, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $this->_showInfo = $showInfo; + + // height and width + $this->_setHeightTable($fontSize); + + // setWidth must me after setHeight, because title + // can include table height which changes table width + $this->_setWidthTable($font,$fontSize); + if ($same_wide_width < $this->width) { + $same_wide_width = $this->width; + } + + // x and y + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' + . ' AND pdf_page_number = ' . $pdf_page_number; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + + if (!$result || !PMA_DBI_num_rows($result)) { + //$svg->dieSchema(sprintf(__('Please configure the coordinates for table %s'), $tableName)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + // displayfield + $this->displayfield = PMA_getDisplayField($db, $tableName); + // index + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } + } + + /** + * Returns title of the current table, + * title can have the dimensions/co-ordinates of the table + * + * @access private + */ + function _getTitle() + { + return ($this->_showInfo ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->heightCell) : '') . ' ' . $this->_tableName; + } + + /** + * Sets the width of the table + * + * @param string font The font size + * @param integer fontSize The font size + * @global object The current SVG image document + * @access private + * @see PMA_SVG + */ + function _setWidthTable($font,$fontSize) + { + global $svg; + + foreach ($this->fields as $field) { + $this->width = max($this->width, $svg->getStringWidth($field,$font,$fontSize)); + } + $this->width += $svg->getStringWidth(' ',$font,$fontSize); + /* + * it is unknown what value must be added, because + * table title is affected by the tabe width value + */ + while ($this->width < $svg->GetStringWidth($this->_getTitle(),$font,$fontSize)) { + $this->width += 7; + } + } + + /** + * Sets the height of the table + * + * @access private + */ + function _setHeightTable($fontSize) + { + $this->heightCell = $fontSize + 4; + $this->height = (count($this->fields) + 1) * $this->heightCell; + } + + /** + * draw the table + * + * @param boolean showColor Whether to display color + * @global object The current SVG image document + * @access public + * @see PMA_SVG,PMA_SVG::printElement + */ + public function tableDraw($showColor) + { + global $svg; + //echo $this->_tableName.'<br />'; + $svg->printElement('rect',$this->x,$this->y, + $this->width,$this->heightCell, + NULL,'fill:red;stroke:black;' + ); + $svg->printElement('text',$this->x + 5,$this->y+ 14, + $this->width,$this->heightCell, + $this->_getTitle(), + 'fill:none;stroke:black;' + ); + foreach ($this->fields as $field) { + $currentCell+=$this->heightCell; + $showColor='none'; + if ($showColor) { + if (in_array($field, $this->primary)) { + $showColor='#0c0'; + } + if ($field == $this->displayfield) { + $showColor='none'; + } + } + $svg->printElement('rect', $this->x,$this->y + $currentCell, + $this->width, $this->heightCell, + NULL, + 'fill:'.$showColor.';stroke:black;' + ); + $svg->printElement('text', $this->x + 5, $this->y + 14 + $currentCell, + $this->width, $this->heightCell, + $field, + 'fill:none;stroke:black;' + ); + //echo $field.'<br />'; + } + } +} + + +/** + * Draws relation links + * + * @access public + * @see PMA_SVG,PMA_SVG::printElementLine + */ +class Relation_Stats { + /** + * Defines properties + */ + public $xSrc, $ySrc; + public $srcDir ; + public $destDir; + public $xDest, $yDest; + public $wTick = 10; + + /** + * The "Relation_Stats" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @access private + * @see Relation_Stats::_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->_getXy($master_table, $master_field); + $dest_pos = $this->_getXy($foreign_table, $foreign_field); + /* + * [0] is x-left + * [1] is x-right + * [2] is y + */ + $src_left = $src_pos[0] - $this->wTick; + $src_right = $src_pos[1] + $this->wTick; + $dest_left = $dest_pos[0] - $this->wTick; + $dest_right = $dest_pos[1] + $this->wTick; + + $d1 = abs($src_left - $dest_left); + $d2 = abs($src_right - $dest_left); + $d3 = abs($src_left - $dest_right); + $d4 = abs($src_right - $dest_right); + $d = min($d1, $d2, $d3, $d4); + + if ($d == $d1) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d2) { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d3) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } else { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } + $this->ySrc = $src_pos[2]; + $this->y_dest = $dest_pos[2]; + } + + /** + * Gets arrows coordinates + * + * @param string table The current table name + * @param string column The relation column name + * @return array Arrows coordinates + * @access private + */ + function _getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // x_left, x_right, y + return array($table->x, $table->x + $table->width, $table->y + ($pos + 1.5) * $table->heightCell); + } + + /** + * draws relation links and arrows + * shows foreign key relations + * + * @param boolean changeColor Whether to use one color per relation or not + * @global object The current SVG image document + * @access public + * @see PMA_SVG + */ + public function relationDraw($changeColor) + { + global $svg; + + if ($changeColor) { + $listOfColors=array( + 'red', + 'grey', + 'black', + 'yellow', + 'green', + 'cyan', + ' orange' + ); + shuffle($listOfColors); + $color = $listOfColors[0]; + } else { + $color = 'black'; + } + + $svg->printElementLine('line',$this->xSrc,$this->ySrc, + $this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, + 'fill:'.$color.';stroke:black;stroke-width:2;' + ); + $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick, $this->y_dest, + $this->xDest, $this->y_dest, + 'fill:'.$color.';stroke:black;stroke-width:2;' + ); + $svg->printElementLine('line',$this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, + $this->xDest + $this->destDir * $this->wTick, $this->y_dest, + 'fill:'.$color.';stroke:'.$color.';stroke-width:1;' + ); + $root2 = 2 * sqrt(2); + $svg->printElementLine('line',$this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, + $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick , + $this->ySrc + $this->wTick / $root2 , + 'fill:'.$color.';stroke:black;stroke-width:2;' + ); + $svg->printElementLine('line',$this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, + $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick , + $this->ySrc - $this->wTick / $root2 , + 'fill:'.$color.';stroke:black;stroke-width:2;' + ); + $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick / 2 , $this->y_dest , + $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick, + $this->y_dest + $this->wTick / $root2 , + 'fill:'.$color.';stroke:black;stroke-width:2;'); + $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick / 2 , + $this->y_dest , $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick , + $this->y_dest - $this->wTick / $root2 , + 'fill:'.$color.';stroke:black;stroke-width:2;' + ); + } +} +/* +* end of the "Relation_Stats" class +*/ + class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema { - - function __construct($page_number, $show_info = 0, $change_color = 0, $all_table_same_wide = 0, $show_keys = 0) - { - //global $pdf, $db, $cfgRelation, $with_doc; - global $db,$writer; - $svg = new PMA_SVG(); + private $tables = array(); + private $_relations = array(); + private $_xMax = 0; + private $_yMax = 0; + private $scale; + private $_xMin = 100000; + private $_yMin = 100000; + private $t_marg = 10; + private $b_marg = 10; + private $l_marg = 10; + private $r_marg = 10; + private $_tablewidth; + + function __construct() + { + global $svg,$db,$pdf_page_number,$show_info,$show_table_dimension,$show_color,$change_color,$all_table_same_wide; + + $pdf_page_number = isset($pdf_page_number) ? $pdf_page_number : 1; + $show_info = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; + $all_table_same_wide = (isset($all_table_same_wide) && $all_table_same_wide == 'on') ? 1 : 0; + $change_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; + $show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; + $svg = new PMA_SVG(); $this->setSameWidthTables($all_table_same_wide); - - $svg->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $page_number)); - $svg->SetAuthor('phpMyAdmin ' . PMA_VERSION); - $svg->setFont('Arial'); - $svg->setFontSize('16px'); - $svg->startSvgDoc('500px','500px'); - $svg->printElement('rect',0,0,'100','100',NULL,'fill:none;stroke:black;'); - $svg->printElement('text',100,100,'100','100','this is just a test'); - $svg->endSvgDoc(); - $svg->showOutput(); - //echo $svg->getTitle(); - /* $alltables=$this->getAllTables($db,$page_number); - foreach ($alltables AS $table) { + $svg->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $pdf_page_number)); + $svg->SetAuthor('phpMyAdmin ' . PMA_VERSION); + $svg->setFont('Arial'); + $svg->setFontSize('16px'); + $svg->startSvgDoc('1000px','600px'); + $alltables=$this->getAllTables($db,$pdf_page_number); + + foreach ($alltables AS $table) { if (!isset($this->tables[$table])) { - $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->_tablewidth, $show_keys, $show_info); - // $this->tables[$table]=$table; + $this->tables[$table] = new Table_Stats($table,$svg->getFont(),$svg->getFontSize(), $this->_tablewidth, $show_keys, $show_info); } - if ($this->same_wide) { + if ($this->sameWide) { $this->tables[$table]->width = $this->_tablewidth; } - $this->PMA_RT_setMinMax($this->tables[$table]); - }*/ - - -/* print '<pre>'; - print_r(get_object_vars($svg)); - print_r($this); - print '</pre>'; - exit();*/ - } + $this->_setMinMax($this->tables[$table]); + } + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + /* put the foreign table on the schema only if selected + * by the user + * (do not use array_search() because we would have to + * to do a === FALSE and this is not PHP3 compatible) + */ + if (in_array($rel['foreign_table'], $alltables)) { + $this->_addRelation($one_table,$svg->getFont(),$svg->getFontSize(), $master_field, $rel['foreign_table'], $rel['foreign_field'], $show_info); + } + } + } + } + + if ($seen_a_relation) { + $this->_drawRelations($change_color); + } + + $this->_drawTables($change_color); + $svg->endSvgDoc(); + $svg->showOutput(); + // print '<pre>'; + //print_r(get_object_vars($svg)); + //print_r($alltables); + //print_r($this); + //print '</pre>'; + //exit(); + } + + /** + * Sets X and Y minimum and maximum for a table cell + * + * @param string table The table name + * @access private + */ + function _setMinMax($table) + { + $this->_xMax = max($this->_xMax, $table->x + $table->width); + $this->_yMax = max($this->_yMax, $table->y + $table->height); + $this->_xMin = min($this->_xMin, $table->x); + $this->_yMin = min($this->_yMin, $table->y); + } + + /** + * Defines relation objects + * + * @param string masterTable The master table name + * @param string masterField The relation field in the master table + * @param string foreignTable The foreign table name + * @param string foreignField The relation field in the foreign table + * @param boolean showInfo Whether to display table position or not + * @access private + * @see _setMinMax,Table_Stats::__construct(),Relation_Stats::__construct() + */ + function _addRelation($masterTable,$font,$fontSize, $masterField, $foreignTable, $foreignField, $showInfo) + { + if (!isset($this->tables[$masterTable])) { + $this->tables[$masterTable] = new Table_Stats($masterTable,$font,$fontSize, $this->_tablewidth, false, $showInfo); + $this->_setMinMax($this->tables[$masterTable]); + } + if (!isset($this->tables[$foreignTable])) { + $this->tables[$foreignTable] = new Table_Stats($foreignTable,$font,$fontSize, $this->_tablewidth, false, $showInfo); + $this->_setMinMax($this->tables[$foreignTable]); + } + $this->_relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); + } + + /** + * Draws relation arrows and lines + * connects master table's master field to + * foreign table's forein field + * + * @param boolean changeColor Whether to use one color per relation or not + * @access private + * @see Relation_Stats::relationDraw() + */ + private function _drawRelations($changeColor) + { + foreach ($this->_relations as $relation) { + $relation->relationDraw($changeColor); + } + } + + /** + * Draws tables + * + * @param boolean showColor Whether to show color for primary fields or not + * @access private + * @see Table_Stats::Table_Stats_tableDraw() + */ + private function _drawTables($changeColor) + { + global $svg; + foreach ($this->tables as $table) { + $table->tableDraw($changeColor); + } + } } ?> \ No newline at end of file From 1ca552a8e14306b723019b2268c8aab7c20bb06c Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Thu, 8 Jul 2010 12:23:51 +0500 Subject: [PATCH 16/48] SVG Schema Class: spaces around = , fixed. --- .../schema/Svg_Relation_Schema.class.php | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/libraries/schema/Svg_Relation_Schema.class.php b/libraries/schema/Svg_Relation_Schema.class.php index cc72cfddc..726061426 100644 --- a/libraries/schema/Svg_Relation_Schema.class.php +++ b/libraries/schema/Svg_Relation_Schema.class.php @@ -48,17 +48,17 @@ class PMA_SVG extends XMLWriter function setTitle($title) { - $this->title=$title; + $this->title = $title; } function setAuthor($author) { - $this->author=$author; + $this->author = $author; } function setFont($font) { - $this->font=$font; + $this->font = $font; } function getFont() { @@ -67,7 +67,7 @@ class PMA_SVG extends XMLWriter function setFontSize($fontSize) { - $this->fontSize=$fontSize; + $this->fontSize = $fontSize; } function getFontSize() @@ -101,7 +101,7 @@ class PMA_SVG extends XMLWriter print $output; } - function printElement($name,$x,$y,$width='',$height='',$text='',$styles='') + function printElement($name,$x,$y,$width = '',$height = '',$text = '',$styles = '') { $this->startElement($name); $this->writeAttribute('width',$width); @@ -148,7 +148,7 @@ class PMA_SVG extends XMLWriter $count = $count + ((strlen($text) - strlen(str_replace(array("m","M"),"",$text)))*0.84);//mM $count = $count + ((strlen($text) - strlen(str_replace("W","",$text)))*.95);//W $count = $count + ((strlen($text) - strlen(str_replace(" ","",$text)))*.28);//" " - $text = str_replace(" ","",$text);//remove the " "'s + $text = str_replace(" ","",$text);//remove the " "'s $count = $count + (strlen(preg_replace("/[a-z0-9]/i","",$text))*0.3); //all other chrs $modifier = 1; @@ -354,14 +354,14 @@ class Table_Stats { 'fill:none;stroke:black;' ); foreach ($this->fields as $field) { - $currentCell+=$this->heightCell; - $showColor='none'; + $currentCell += $this->heightCell; + $showColor = 'none'; if ($showColor) { if (in_array($field, $this->primary)) { - $showColor='#0c0'; + $showColor = '#0c0'; } if ($field == $this->displayfield) { - $showColor='none'; + $showColor = 'none'; } } $svg->printElement('rect', $this->x,$this->y + $currentCell, @@ -408,46 +408,46 @@ class Relation_Stats { */ function __construct($master_table, $master_field, $foreign_table, $foreign_field) { - $src_pos = $this->_getXy($master_table, $master_field); + $src_pos = $this->_getXy($master_table, $master_field); $dest_pos = $this->_getXy($foreign_table, $foreign_field); /* * [0] is x-left * [1] is x-right * [2] is y */ - $src_left = $src_pos[0] - $this->wTick; - $src_right = $src_pos[1] + $this->wTick; - $dest_left = $dest_pos[0] - $this->wTick; + $src_left = $src_pos[0] - $this->wTick; + $src_right = $src_pos[1] + $this->wTick; + $dest_left = $dest_pos[0] - $this->wTick; $dest_right = $dest_pos[1] + $this->wTick; $d1 = abs($src_left - $dest_left); $d2 = abs($src_right - $dest_left); $d3 = abs($src_left - $dest_right); $d4 = abs($src_right - $dest_right); - $d = min($d1, $d2, $d3, $d4); + $d = min($d1, $d2, $d3, $d4); if ($d == $d1) { - $this->xSrc = $src_pos[0]; - $this->srcDir = -1; - $this->xDest = $dest_pos[0]; + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[0]; $this->destDir = -1; } elseif ($d == $d2) { - $this->xSrc = $src_pos[1]; - $this->srcDir = 1; - $this->xDest = $dest_pos[0]; + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[0]; $this->destDir = -1; } elseif ($d == $d3) { - $this->xSrc = $src_pos[0]; - $this->srcDir = -1; - $this->xDest = $dest_pos[1]; + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[1]; $this->destDir = 1; } else { - $this->xSrc = $src_pos[1]; - $this->srcDir = 1; - $this->xDest = $dest_pos[1]; + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[1]; $this->destDir = 1; } - $this->ySrc = $src_pos[2]; + $this->ySrc = $src_pos[2]; $this->y_dest = $dest_pos[2]; } @@ -480,7 +480,7 @@ class Relation_Stats { global $svg; if ($changeColor) { - $listOfColors=array( + $listOfColors = array( 'red', 'grey', 'black', @@ -554,10 +554,10 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema { global $svg,$db,$pdf_page_number,$show_info,$show_table_dimension,$show_color,$change_color,$all_table_same_wide; - $pdf_page_number = isset($pdf_page_number) ? $pdf_page_number : 1; + $pdf_page_number = isset($pdf_page_number) ? $pdf_page_number : 1; $show_info = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; $all_table_same_wide = (isset($all_table_same_wide) && $all_table_same_wide == 'on') ? 1 : 0; - $change_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; + $change_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; $show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; $svg = new PMA_SVG(); $this->setSameWidthTables($all_table_same_wide); @@ -566,7 +566,7 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema $svg->setFont('Arial'); $svg->setFontSize('16px'); $svg->startSvgDoc('1000px','600px'); - $alltables=$this->getAllTables($db,$pdf_page_number); + $alltables = $this->getAllTables($db,$pdf_page_number); foreach ($alltables AS $table) { if (!isset($this->tables[$table])) { From 07cee82af681ca5602c704e438eff39c83825db3 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Fri, 9 Jul 2010 14:39:03 +0500 Subject: [PATCH 17/48] User Schema Class: big switch into several methods + reduce global variables --- export_relation_schema.php | 20 +- libraries/schema/User_Schema.class.php | 768 +++++++++++++------------ 2 files changed, 411 insertions(+), 377 deletions(-) diff --git a/export_relation_schema.php b/export_relation_schema.php index 1c135e5b7..667845801 100644 --- a/export_relation_schema.php +++ b/export_relation_schema.php @@ -71,7 +71,7 @@ if ($cfgRelation['pdfwork']) { * User object created for presenting the HTML options * so, user can interact with it and perform export of relations schema */ - + require_once './libraries/schema/User_Schema.class.php'; $user_schema = new PMA_User_Schema(); @@ -98,7 +98,7 @@ if ($cfgRelation['pdfwork']) { * Create a new page where relations will be drawn */ - $user_schema->createPage(); + $user_schema->createPage($db); /** * After selection of page or creating a page @@ -110,8 +110,8 @@ if ($cfgRelation['pdfwork']) { if (isset($do) && ($do == 'edcoord' - || ($do == 'selectpage' && isset($chpage) && $chpage != 0) - || ($do == 'createpage' && isset($chpage) && $chpage != 0))) { + || ($do == 'selectpage' && isset($user_schema->choosenPage) && $user_schema->choosenPage != 0) + || ($do == 'createpage' && isset($user_schema->choosenPage) && $user_schema->choosenPage != 0))) { /** * show Export schema generation options @@ -119,12 +119,12 @@ if ($cfgRelation['pdfwork']) { $user_schema->displaySchemaGenerationOptions(); if ((isset($showwysiwyg) && $showwysiwyg == '1')) { - ?> - <script type="text/javascript"> - //<![CDATA[ - ToggleDragDrop('pdflayout'); - //]]> - </script> + ?> + <script type="text/javascript"> + //<![CDATA[ + ToggleDragDrop('pdflayout'); + //]]> + </script> <?php } } // end if diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php index 5c5fd9754..4aae7279e 100644 --- a/libraries/schema/User_Schema.class.php +++ b/libraries/schema/User_Schema.class.php @@ -11,250 +11,84 @@ * about their tables for which they want to export the relational schema * export options are shown to user from they can choose * - * @name + * @name User_Schema * @author Muhammad Adnan <hiddenpearls@gmail.com> * @copyright * @license */ class PMA_User_Schema -{ +{ + + public $choosenPage; + public $autoLayoutForeign; + public $autoLayoutInternal; + public $pageNumber; + public $c_table_rows; + /** * This function will process the user defined pages * and tables which will be exported as Relational schema * you can set the table positions on the paper via scratchboard * for table positions, put the x,y co-ordinates * - * @param string $do It tells what the Schema is supposed to do - * create and select a page, generate schema etc + * @param string $action It tells what the Schema is supposed to do + * create and select a page, generate schema etc * @access public */ - public function processUserPreferences($do) + public function processUserPreferences($action) { - global $action_choose,$chpage,$db,$cfgRelation,$cfg,$auto_layout_foreign,$auto_layout_internal,$newpage,$c_table_rows,$query_default_option; + global $action_choose,$db,$cfgRelation,$cfg,$query_default_option; - if (isset($do)) { - switch ($do) { - case 'selectpage': - if ($action_choose=="1") { - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND page_nr = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - - unset($chpage); - } - break; - case 'createpage': - $pdf_page_number = PMA_REL_create_page($newpage, $cfgRelation, $db, $query_default_option); - - /* - * A u t o m a t i c l a y o u t - * - * There are 2 kinds of relations in PMA - * 1) Internal Relations 2) Foreign Key Relations - */ - if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { - $all_tables = array(); - } - - if (isset($auto_layout_foreign)) { - /* - * get the tables list - * who support FOREIGN KEY, it's not - * important that we group together InnoDB tables - * and PBXT tables, as this logic is just to put - * the tables on the layout, not to determine relations - */ - $tables = PMA_DBI_get_tables_full($db); - $foreignkey_tables = array(); - foreach($tables as $table_name => $table_properties) { - if (PMA_foreignkey_supported($table_properties['ENGINE'])) { - $foreignkey_tables[] = $table_name; - } + if (isset($action)) { + switch ($action) { + case 'selectpage': + $this->choosenPage = $_REQUEST['chpage']; + if ($action_choose=="1") { + $this->deleteCoordinates($db, $cfgRelation, $this->choosenPage, $query_default_option); + $this->deletePages($db, $cfgRelation, $this->choosenPage, $query_default_option); + $this->choosenPage = 0; } - $all_tables = $foreignkey_tables; - // could be improved by finding the tables which have the - // most references keys and placing them at the beginning - // of the array (so that they are all center of schema) - unset($tables, $foreignkey_tables); - } + break; + case 'createpage': + $this->pageNumber = PMA_REL_create_page($_POST['newpage'], $cfgRelation, $db, $query_default_option); + $this->autoLayoutForeign = $_POST['auto_layout_foreign']; + $this->autoLayoutInternal = $_POST['auto_layout_internal']; + $this->processRelations($db, $this->pageNumber,$cfgRelation,$query_default_option); + break; + case 'edcoord': + $this->choosenPage = $_POST['chpage']; + $this->c_table_rows = $_POST['c_table_rows']; + $this->editCoordinates($db, $cfgRelation); + break; + case 'deleteCrap': + $this->_deleteTableRows($delrow,$cfgRelation,$db,$this->choosenPage); + break; + case 'process_export': + $this->_processExportSchema(); + break; - if (isset($auto_layout_internal)) { - /* - * get the tables list who support Internal Relations; - * This type of relations will be created when - * you setup the PMA tables correctly - */ - $master_tables = 'SELECT COUNT(master_table), master_table' - . ' FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) - . ' WHERE master_db = \'' . $db . '\'' - . ' GROUP BY master_table' - . ' ORDER BY ' . PMA_backquote('COUNT(master_table)') . ' DESC '; - $master_tables_rs = PMA_query_as_controluser($master_tables, FALSE, $query_default_option); - if ($master_tables_rs && PMA_DBI_num_rows($master_tables_rs) > 0) { - /* first put all the master tables at beginning - * of the list, so they are near the center of - * the schema - */ - while (list(, $master_table) = PMA_DBI_fetch_row($master_tables_rs)) { - $all_tables[] = $master_table; - } + } // end switch + } // end if (isset($do)) - /* Now for each master, add its foreigns into an array - * of foreign tables, if not already there - * (a foreign might be foreign for more than - * one table, and might be a master itself) - */ - - $foreign_tables = array(); - foreach ($all_tables as $master_table) { - $foreigners = PMA_getForeigners($db, $master_table); - foreach ($foreigners as $foreigner) { - if (!in_array($foreigner['foreign_table'], $foreign_tables)) { - $foreign_tables[] = $foreigner['foreign_table']; - } - } - } - - /* - * Now merge the master and foreign arrays/tables - */ - foreach ($foreign_tables as $foreign_table) { - if (!in_array($foreign_table, $all_tables)) { - $all_tables[] = $foreign_table; - } - } - } // endif there are master tables - } // endif auto_layout_internal - - if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { - /* - * Now generate the coordinates for the schema - * in a clockwise spiral - */ - $pos_x = 300; - $pos_y = 300; - $delta = 110; - $delta_mult = 1.10; - $direction = "right"; - foreach ($all_tables as $current_table) { - /* - * save current table's coordinates - */ - $insert_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . '(db_name, table_name, pdf_page_number, x, y) ' - . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($current_table) . '\',' . $pdf_page_number . ',' . $pos_x . ',' . $pos_y . ')'; - PMA_query_as_controluser($insert_query, FALSE, $query_default_option); - - /* - * compute for the next table - */ - switch ($direction) { - case 'right': - $pos_x += $delta; - $direction = "down"; - $delta *= $delta_mult; - break; - case 'down': - $pos_y += $delta; - $direction = "left"; - $delta *= $delta_mult; - break; - case 'left': - $pos_x -= $delta; - $direction = "up"; - $delta *= $delta_mult; - break; - case 'up': - $pos_y -= $delta; - $direction = "right"; - $delta *= $delta_mult; - break; - } // end switch - } // end foreach - } // end if some auto-layout to do - - $chpage = $pdf_page_number; - break; - - case 'edcoord': - for ($i = 0; $i < $c_table_rows; $i++) { - $arrvalue = 'c_table_' . $i; - global $$arrvalue; - $arrvalue = $$arrvalue; - if (!isset($arrvalue['x']) || $arrvalue['x'] == '') { - $arrvalue['x'] = 0; - } - if (!isset($arrvalue['y']) || $arrvalue['y'] == '') { - $arrvalue['y'] = 0; - } - if (isset($arrvalue['name']) && $arrvalue['name'] != '--') { - $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - // echo $test_query; - $test_rs = PMA_query_as_controluser($test_query, FALSE, $query_default_option); - if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { - if (isset($arrvalue['delete']) && $arrvalue['delete'] == 'y') { - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - } else { - $ch_query = 'UPDATE ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . 'SET x = ' . $arrvalue['x'] . ', y= ' . $arrvalue['y'] - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - } - } else { - $ch_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . '(db_name, table_name, pdf_page_number, x, y) ' - . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($arrvalue['name']) . '\', \'' . PMA_sqlAddslashes($chpage) . '\',' . $arrvalue['x'] . ',' . $arrvalue['y'] . ')'; - } - //echo $ch_query; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - } // end if - } // end for - break; - - case 'deleteCrap': - $this->_deleteTableRows(); - break; - - case 'process_export': - $this->_processExportSchema(); - break; - - } // end switch - } // end if (isset($do)) - - } - + } + /** * shows/displays the HTML FORM to create the page * * @access public */ - public function createPage() - { - global $db,$table; + public function createPage($db) + { ?> <form method="post" action="export_relation_schema.php" name="frm_create_page"> <fieldset> <legend> <?php echo __('Create a page !') . "\n"; ?> </legend> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <?php echo PMA_generate_common_hidden_inputs($db); ?> <input type="hidden" name="do" value="createpage" /> <table> <tr> @@ -278,7 +112,7 @@ class PMA_User_Schema <?php echo __('FOREIGN KEY'); ?></label><br /> <?php } - ?> + ?> </td></tr> </table> </fieldset> @@ -287,23 +121,23 @@ class PMA_User_Schema </fieldset> </form> <?php - } - + } + /** * shows/displays the created page names in a drop down list * User can select any page number and edit it using dashboard etc * - * @access public + * @access public */ public function selectPage() { - global $db,$table,$query_default_option,$cfgRelation,$chpage; + global $db,$table,$query_default_option,$cfgRelation; $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\''; $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); - if ($page_rs && PMA_DBI_num_rows($page_rs) > 0) { - ?> + if ($page_rs && PMA_DBI_num_rows($page_rs) > 0) { + ?> <form method="get" action="export_relation_schema.php" name="frm_select_page"> <fieldset> <legend> @@ -311,13 +145,13 @@ class PMA_User_Schema </legend> <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> <input type="hidden" name="do" value="selectpage" /> - <select name="chpage" onchange="this.form.submit()"> - <option value='0'><?php echo __('Select page'); ?></option> + <select name="chpage" id="chpage" onchange="this.form.submit()"> + <option value="0"><?php echo __('Select page'); ?></option> <?php while ($curr_page = PMA_DBI_fetch_assoc($page_rs)) { echo "\n" . ' ' . '<option value="' . $curr_page['page_nr'] . '"'; - if (isset($chpage) && $chpage == $curr_page['page_nr']) { + if (isset($this->choosenPage) && $this->choosenPage == $curr_page['page_nr']) { echo ' selected="selected"'; } echo '>' . $curr_page['page_nr'] . ': ' . htmlspecialchars($curr_page['page_descr']) . '</option>'; @@ -329,7 +163,7 @@ class PMA_User_Schema $choices = array( '0' => __('Edit'), '1' => __('Delete') - ); + ); PMA_display_html_radio('action_choose', $choices, '0', false); unset($choices); ?> @@ -341,17 +175,17 @@ class PMA_User_Schema <?php } // end IF echo "\n"; - } // end function + } // end function /** * A dashboard is displayed to AutoLayout the position of tables * users can drag n drop the tables and change their positions * - */ + */ public function showTableDashBoard() { - global $db,$cfgRelation,$table,$cfg,$with_field_names,$chpage; + global $db,$cfgRelation,$table,$cfg,$with_field_names; /* * We will need an array of all tables in this db */ @@ -366,14 +200,14 @@ class PMA_User_Schema * show the tables involved */ - if (isset($chpage) && $chpage > 0) { + if (isset($this->choosenPage) && $this->choosenPage > 0) { echo "\n"; ?> - <h2><?php echo __('Select Tables') ;?></h2> + <h2><?php echo __('Select Tables') ;?></h2> <?php $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($this->choosenPage) . '\''; $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); $array_sh_page = array(); $draginit = ''; @@ -382,57 +216,86 @@ class PMA_User_Schema while ($temp_sh_page = @PMA_DBI_fetch_assoc($page_rs)) { $array_sh_page[] = $temp_sh_page; } + /* + * Display WYSIWYG-PDF parts? + */ - /* - * Display WYSIWYG-PDF parts? - */ + if ($cfg['WYSIWYG-PDF']) { + if (!isset($_POST['with_field_names']) && !isset($_POST['showwysiwyg'])) { + $with_field_names = TRUE; + } + $this->_displayScratchboardTables($array_sh_page,$draginit,$reset_draginit); + } + ?> - if ($cfg['WYSIWYG-PDF']) { - if (!isset($_POST['with_field_names']) && !isset($_POST['showwysiwyg'])) { - $with_field_names = TRUE; - } - $this->_displayScratchboardTables($array_sh_page,$draginit,$reset_draginit); - } - ?> + <form method="post" action="export_relation_schema.php" name="edcoord"> + <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> + <input type="hidden" name="chpage" value="<?php echo htmlspecialchars($this->choosenPage); ?>" /> + <input type="hidden" name="do" value="edcoord" /> + <table border="0"> + <tr> + <th><?php echo __('Table'); ?></th> + <th><?php echo __('Delete'); ?></th> + <th>X</th> + <th>Y</th> + </tr> + <?php + if (isset($ctable)) { + unset($ctable); + } - <form method="post" action="export_relation_schema.php" name="edcoord"> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="chpage" value="<?php echo htmlspecialchars($chpage); ?>" /> - <input type="hidden" name="do" value="edcoord" /> - <table border="0"> - <tr> - <th><?php echo __('Table'); ?></th> - <th><?php echo __('Delete'); ?></th> - <th>X</th> - <th>Y</th> - </tr> - <?php - if (isset($ctable)) { - unset($ctable); - } - - $i = 0; - $odd_row = true; - foreach ($array_sh_page as $dummy_sh_page => $sh_page) { - $_mtab = $sh_page['table_name']; - $tabExist[$_mtab] = FALSE; + $i = 0; + $odd_row = true; + foreach ($array_sh_page as $dummy_sh_page => $sh_page) { + $_mtab = $sh_page['table_name']; + $tabExist[$_mtab] = FALSE; + echo "\n" . ' <tr class="'; + if ($odd_row) { + echo 'odd'; + } else { + echo 'even'; + } + echo '">'; + $odd_row != $odd_row; + echo "\n" . ' <td>' + . "\n" . ' <select name="c_table_' . $i . '[name]">'; + foreach ($selectboxall as $key => $value) { + echo "\n" . ' <option value="' . htmlspecialchars($value) . '"'; + if ($value == $sh_page['table_name']) { + echo ' selected="selected"'; + $tabExist[$_mtab] = TRUE; + } + echo '>' . htmlspecialchars($value) . '</option>'; + } + echo "\n" . ' </select>' + . "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'x\', this.value)"' : '') . ' name="c_table_' . $i . '[x]" value="' . $sh_page['x'] . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' <td>' + . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'y\', this.value)"' : '') . ' name="c_table_' . $i . '[y]" value="' . $sh_page['y'] . '" />'; + echo "\n" . ' </td>'; + echo "\n" . ' </tr>'; + $i++; + } + /* + * Add one more empty row + */ echo "\n" . ' <tr class="'; if ($odd_row) { echo 'odd'; } else { echo 'even'; } - echo '">'; $odd_row != $odd_row; + echo '">'; echo "\n" . ' <td>' . "\n" . ' <select name="c_table_' . $i . '[name]">'; foreach ($selectboxall as $key => $value) { - echo "\n" . ' <option value="' . htmlspecialchars($value) . '"'; - if ($value == $sh_page['table_name']) { - echo ' selected="selected"'; - $tabExist[$_mtab] = TRUE; - } - echo '>' . htmlspecialchars($value) . '</option>'; + echo "\n" . ' <option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($value) . '</option>'; } echo "\n" . ' </select>' . "\n" . ' </td>'; @@ -440,60 +303,30 @@ class PMA_User_Schema . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); echo "\n" . ' </td>'; echo "\n" . ' <td>' - . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'x\', this.value)"' : '') . ' name="c_table_' . $i . '[x]" value="' . $sh_page['x'] . '" />'; + . "\n" . ' <input type="text" name="c_table_' . $i . '[x]" value="' . (isset($sh_page['x'])?$sh_page['x']:'') . '" />'; echo "\n" . ' </td>'; echo "\n" . ' <td>' - . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'y\', this.value)"' : '') . ' name="c_table_' . $i . '[y]" value="' . $sh_page['y'] . '" />'; + . "\n" . ' <input type="text" name="c_table_' . $i . '[y]" value="' . (isset($sh_page['y'])?$sh_page['y']:'') . '" />'; echo "\n" . ' </td>'; echo "\n" . ' </tr>'; - $i++; - } - /* - * Add one more empty row - */ - echo "\n" . ' <tr class="'; - if ($odd_row) { - echo 'odd'; - } else { - echo 'even'; - } - $odd_row != $odd_row; - echo '">'; - echo "\n" . ' <td>' - . "\n" . ' <select name="c_table_' . $i . '[name]">'; - foreach ($selectboxall as $key => $value) { - echo "\n" . ' <option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($value) . '</option>'; - } - echo "\n" . ' </select>' - . "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" name="c_table_' . $i . '[x]" value="' . (isset($sh_page['x'])?$sh_page['x']:'') . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" name="c_table_' . $i . '[y]" value="' . (isset($sh_page['y'])?$sh_page['y']:'') . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' </tr>'; - echo "\n" . ' </table>' . "\n"; + echo "\n" . ' </table>' . "\n"; - echo "\n" . ' <input type="hidden" name="c_table_rows" value="' . ($i + 1) . '" />'; - echo ($cfg['WYSIWYG-PDF'] ? "\n" . ' <input type="hidden" id="showwysiwyg" name="showwysiwyg" value="' . ((isset($showwysiwyg) && $showwysiwyg == '1') ? '1' : '0') . '" />' : ''); - echo "\n" . ' <input type="checkbox" name="with_field_names" ' . (isset($with_field_names) ? 'checked="checked"' : ''). ' />' . __('Column names') . '<br />'; - echo "\n" . ' <input type="submit" value="' . __('Save') . '" />'; - echo "\n" . '</form>' . "\n\n"; - } // end if - - $this->_deleteTables($chpage); + echo "\n" . ' <input type="hidden" name="c_table_rows" value="' . ($i + 1) . '" />'; + echo ($cfg['WYSIWYG-PDF'] ? "\n" . ' <input type="hidden" id="showwysiwyg" name="showwysiwyg" value="' . ((isset($showwysiwyg) && $showwysiwyg == '1') ? '1' : '0') . '" />' : ''); + echo "\n" . ' <input type="checkbox" name="with_field_names" ' . (isset($with_field_names) ? 'checked="checked"' : ''). ' />' . __('Column names') . '<br />'; + echo "\n" . ' <input type="submit" value="' . __('Save') . '" />'; + echo "\n" . '</form>' . "\n\n"; + } // end if + + $this->_deleteTables($db, $this->choosenPage, $tabExist); } - - /** - * show Export relational schema generation options + + /** + * show Export relational schema generation options * user can select export type of his own choice * and the attributes related to it - */ - + */ + public function displaySchemaGenerationOptions() { global $cfg,$pmaThemeImage,$db,$test_rs,$chpage; @@ -501,15 +334,15 @@ class PMA_User_Schema <form method="post" action="export_relation_schema.php"> <fieldset> <legend> - <?php - echo PMA_generate_common_hidden_inputs($db); - if ($cfg['PropertiesIconic']) { - echo '<img class="icon" src="' . $pmaThemeImage . 'b_view.png"' - .' alt="" width="16" height="16" />'; - } - echo __('Display Relational Schema'); - ?>: - </legend> + <?php + echo PMA_generate_common_hidden_inputs($db); + if ($cfg['PropertiesIconic']) { + echo '<img class="icon" src="' . $pmaThemeImage . 'b_view.png"' + .' alt="" width="16" height="16" />'; + } + echo __('Display Relational Schema'); + ?>: + </legend> <select name="export_type" id="export_type"> <option value="pdf" selected="selected"><?php echo __('PDF');?></option> <option value="svg"><?php echo __('SVG');?></option> @@ -533,7 +366,7 @@ class PMA_User_Schema ?> </select><br /> <?php } else { ?> - <input type="hidden" name="pdf_page_number" value="<?php echo htmlspecialchars($chpage); ?>" /> + <input type="hidden" name="pdf_page_number" value="<?php echo htmlspecialchars($this->choosenPage); ?>" /> <?php } ?> <input type="hidden" name="do" value="process_export" /> <input type="checkbox" name="show_grid" id="show_grid_opt" /> @@ -543,8 +376,8 @@ class PMA_User_Schema <input type="checkbox" name="show_table_dimension" id="show_table_dim_opt" /> <label for="show_table_dim_opt"><?php echo __('Show dimension of tables'); ?> </label><br /> - <input type="checkbox" name="all_tab_same_wide" id="all_tab_same_wide" /> - <label for="all_tab_same_wide"><?php echo __('Display all tables with the same width'); ?> + <input type="checkbox" name="all_table_same_wide" id="all_table_same_wide" /> + <label for="all_table_same_wide"><?php echo __('Display all tables with the same width'); ?> </label><br /> <input type="checkbox" name="with_doc" id="with_doc" checked="checked" /> <label for="with_doc"><?php echo __('Data Dictionary'); ?></label><br /> @@ -575,44 +408,43 @@ class PMA_User_Schema </fieldset> </form> <?php - }// end function + } - /** - * Check if there are tables that need to be deleted in dashboard, - * if there are, ask the user for allowance - * @param string $chpage selected page - * @access private - */ - private function _deleteTables($chpage) - { - global $db, $table,$tabExist; - - $_strtrans = ''; - $_strname = ''; - $shoot = FALSE; - if (!empty($tabExist) && is_array($tabExist)) { - foreach ($tabExist as $key => $value) { - if (!$value) { - $_strtrans .= '<input type="hidden" name="delrow[]" value="' . htmlspecialchars($key) . '" />' . "\n"; - $_strname .= '<li>' . htmlspecialchars($key) . '</li>' . "\n"; - $shoot = TRUE; - } - } - if ($shoot) { - echo '<form action="export_relation_schema.php" method="post">' . "\n" - . PMA_generate_common_hidden_inputs($db, $table) - . '<input type="hidden" name="do" value="deleteCrap" />' . "\n" - . '<input type="hidden" name="chpage" value="' . htmlspecialchars($chpage) . '" />' . "\n" - . __('The current page has references to tables that no longer exist. Would you like to delete those references?') - . '<ul>' . "\n" - . $_strname - . '</ul>' . "\n" - . $_strtrans - . '<input type="submit" value="' . __('Go') . '" />' . "\n" - . '</form>'; - } - } - } // end function + /** + * Check if there are tables that need to be deleted in dashboard, + * if there are, ask the user for allowance + * @param string $chpage selected page + * @access private + */ + private function _deleteTables($db, $chpage, $tabExist) + { + $_strtrans = ''; + $_strname = ''; + $shoot = FALSE; + if (!empty($tabExist) && is_array($tabExist)) { + foreach ($tabExist as $key => $value) { + if (!$value) { + $_strtrans .= '<input type="hidden" name="delrow[]" value="' . htmlspecialchars($key) . '" />' . "\n"; + $_strname .= '<li>' . htmlspecialchars($key) . '</li>' . "\n"; + $shoot = TRUE; + } + } + if ($shoot) { + echo '<form action="export_relation_schema.php" method="post">' . "\n" + . PMA_generate_common_hidden_inputs($db, $table) + . '<input type="hidden" name="do" value="deleteCrap" />' . "\n" + . '<input type="hidden" name="chpage" value="' . htmlspecialchars($chpage) . '" />' . "\n" + . __('The current page has references to tables that no longer exist. Would you like to delete those references?') + . '<ul>' . "\n" + . $_strname + . '</ul>' . "\n" + . $_strtrans + . '<input type="submit" value="' . __('Go') . '" />' . "\n" + . '</form>'; + } + } + + } /** * Check if there are tables that need to be deleted in dashboard, @@ -630,6 +462,7 @@ class PMA_User_Schema </form> <div id="pdflayout" class="pdflayout" style="visibility: hidden;"> <?php + $i = 0; foreach ($array_sh_page as $key => $temp_sh_page) { $drag_x = $temp_sh_page['x']; $drag_y = $temp_sh_page['y']; @@ -642,14 +475,14 @@ class PMA_User_Schema $reset_draginit .= ' getElement("table_' . $i . '").style.top = "' . (15 * $i) . 'px";' . "\n"; $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[x]"].value = "2"' . "\n"; $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[y]"].value = "' . (15 * $i) . '"' . "\n"; - + $local_query = 'SHOW FIELDS FROM ' . PMA_backquote($temp_sh_page['table_name']) . ' FROM ' . PMA_backquote($db); $fields_rs = PMA_DBI_query($local_query); unset($local_query); $fields_cnt = PMA_DBI_num_rows($fields_rs); - + echo '<div id="table_' . $i . '" class="pdflayout_table"><u>' . $temp_sh_page['table_name'] . '</u>'; if (isset($with_field_names)) { while ($row = PMA_DBI_fetch_assoc($fields_rs)) { @@ -685,13 +518,14 @@ class PMA_User_Schema * @access private */ - private function _deleteTableRows() + private function _deleteTableRows($delrow,$cfgRelation,$db,$chpage) { foreach ($delrow as $current_row) { $del_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' . "\n" . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . "\n" . ' AND table_name = \'' . PMA_sqlAddslashes($current_row) . '\'' . "\n" . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; + echo $del_query; PMA_query_as_controluser($del_query, FALSE, $query_default_option); } } @@ -704,7 +538,7 @@ class PMA_User_Schema */ private function _processExportSchema() - { + { /** * Settings for relation stuff */ @@ -713,11 +547,211 @@ class PMA_User_Schema /** * default is PDF */ + global $db,$export_type; $export_type = isset($export_type) ? $export_type : 'pdf'; PMA_DBI_select_db($db); include("./libraries/schema/".ucfirst($export_type)."_Relation_Schema.class.php"); $obj_schema = eval("new PMA_".ucfirst($export_type)."_Relation_Schema();"); - } + } + + public function deleteCoordinates($db, $cfgRelation, $choosePage, $query_default_option) + { + $query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($choosePage) . '\''; + PMA_query_as_controluser($query, FALSE, $query_default_option); + } + + public function deletePages($db, $cfgRelation, $choosePage, $query_default_option) + { + $query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND page_nr = \'' . PMA_sqlAddslashes($choosePage) . '\''; + PMA_query_as_controluser($query, FALSE, $query_default_option); + } + + public function processRelations($db, $pageNumber, $cfgRelation, $query_default_option) + { + /* + * A u t o m a t i c l a y o u t + * + * There are 2 kinds of relations in PMA + * 1) Internal Relations 2) Foreign Key Relations + */ + if (isset($this->autoLayoutInternal) || isset($this->autoLayoutForeign)) { + $all_tables = array(); + } + + if (isset($this->autoLayoutForeign)) { + /* + * get the tables list + * who support FOREIGN KEY, it's not + * important that we group together InnoDB tables + * and PBXT tables, as this logic is just to put + * the tables on the layout, not to determine relations + */ + $tables = PMA_DBI_get_tables_full($db); + $foreignkey_tables = array(); + foreach($tables as $table_name => $table_properties) { + if (PMA_foreignkey_supported($table_properties['ENGINE'])) { + $foreignkey_tables[] = $table_name; + } + } + $all_tables = $foreignkey_tables; + /* + * could be improved by finding the tables which have the + * most references keys and placing them at the beginning + * of the array (so that they are all center of schema) + */ + unset($tables, $foreignkey_tables); + } + + if (isset($this->autoLayoutInternal)) { + /* + * get the tables list who support Internal Relations; + * This type of relations will be created when + * you setup the PMA tables correctly + */ + $master_tables = 'SELECT COUNT(master_table), master_table' + . ' FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) + . ' WHERE master_db = \'' . $db . '\'' + . ' GROUP BY master_table' + . ' ORDER BY ' . PMA_backquote('COUNT(master_table)') . ' DESC '; + $master_tables_rs = PMA_query_as_controluser($master_tables, FALSE, $query_default_option); + if ($master_tables_rs && PMA_DBI_num_rows($master_tables_rs) > 0) { + /* first put all the master tables at beginning + * of the list, so they are near the center of + * the schema + */ + while (list(, $master_table) = PMA_DBI_fetch_row($master_tables_rs)) { + $all_tables[] = $master_table; + } + + /* Now for each master, add its foreigns into an array + * of foreign tables, if not already there + * (a foreign might be foreign for more than + * one table, and might be a master itself) + */ + + $foreign_tables = array(); + foreach ($all_tables as $master_table) { + $foreigners = PMA_getForeigners($db, $master_table); + foreach ($foreigners as $foreigner) { + if (!in_array($foreigner['foreign_table'], $foreign_tables)) { + $foreign_tables[] = $foreigner['foreign_table']; + } + } + } + + /* + * Now merge the master and foreign arrays/tables + */ + foreach ($foreign_tables as $foreign_table) { + if (!in_array($foreign_table, $all_tables)) { + $all_tables[] = $foreign_table; + } + } + } + } + + if (isset($this->autoLayoutInternal) || isset($this->auto_layout_foreign)) { + $this->addRelationCoordinates($all_tables,$pageNumber,$db, $cfgRelation,$query_default_option); + } + + $this->choosenPage = $pageNumber; + } + + + public function addRelationCoordinates($all_tables,$pageNumber,$db, $cfgRelation,$query_default_option) + { + /* + * Now generate the coordinates for the schema + * in a clockwise spiral and add to co-ordinates table + */ + $pos_x = 300; + $pos_y = 300; + $delta = 110; + $delta_mult = 1.10; + $direction = "right"; + foreach ($all_tables as $current_table) { + /* + * save current table's coordinates + */ + $insert_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . '(db_name, table_name, pdf_page_number, x, y) ' + . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($current_table) . '\',' . $pageNumber . ',' . $pos_x . ',' . $pos_y . ')'; + PMA_query_as_controluser($insert_query, FALSE, $query_default_option); + + /* + * compute for the next table + */ + switch ($direction) { + case 'right': + $pos_x += $delta; + $direction = "down"; + $delta *= $delta_mult; + break; + case 'down': + $pos_y += $delta; + $direction = "left"; + $delta *= $delta_mult; + break; + case 'left': + $pos_x -= $delta; + $direction = "up"; + $delta *= $delta_mult; + break; + case 'up': + $pos_y -= $delta; + $direction = "right"; + $delta *= $delta_mult; + break; + } + } + } + + public function editCoordinates($db, $cfgRelation) + { + for ($i = 0; $i < $this->c_table_rows; $i++) { + $arrvalue = 'c_table_' . $i; + global $$arrvalue; + $arrvalue = $$arrvalue; + if (!isset($arrvalue['x']) || $arrvalue['x'] == '') { + $arrvalue['x'] = 0; + } + if (!isset($arrvalue['y']) || $arrvalue['y'] == '') { + $arrvalue['y'] = 0; + } + if (isset($arrvalue['name']) && $arrvalue['name'] != '--') { + $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($this->choosenPage) . '\''; + $test_rs = PMA_query_as_controluser($test_query, FALSE, $query_default_option); + //echo $test_query; + if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { + if (isset($arrvalue['delete']) && $arrvalue['delete'] == 'y') { + $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($this->choosenPage) . '\''; + } else { + $ch_query = 'UPDATE ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . 'SET x = ' . $arrvalue['x'] . ', y= ' . $arrvalue['y'] + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' + . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($this->choosenPage) . '\''; + } + } else { + $ch_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' + . '(db_name, table_name, pdf_page_number, x, y) ' + . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($arrvalue['name']) . '\', \'' . PMA_sqlAddslashes($this->choosenPage) . '\',' . $arrvalue['x'] . ',' . $arrvalue['y'] . ')'; + } + //echo $ch_query; + PMA_query_as_controluser($ch_query, FALSE, $query_default_option); + } // end if + } // end for + } } ?> \ No newline at end of file From 79c6f847fd9b50c8e25361a482b29ee798c3fb7d Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Fri, 9 Jul 2010 15:15:49 +0500 Subject: [PATCH 18/48] Pdf Schema Class: Reduce global variables + added attributes in Export Relation Schema --- .../schema/Export_Relation_Schema.class.php | 122 +- .../schema/Pdf_Relation_Schema.class.php | 1177 +++++++++-------- 2 files changed, 678 insertions(+), 621 deletions(-) diff --git a/libraries/schema/Export_Relation_Schema.class.php b/libraries/schema/Export_Relation_Schema.class.php index 4924914c7..71f47ab0f 100644 --- a/libraries/schema/Export_Relation_Schema.class.php +++ b/libraries/schema/Export_Relation_Schema.class.php @@ -9,8 +9,9 @@ /** * This class is inherited by all schema classes * It contains those methods which are common in them + * it works like factory pattern * - * @name + * @name Export Relation Schema * @author Muhammad Adnan <hiddenpearls@gmail.com> * @copyright * @license @@ -18,52 +19,95 @@ class PMA_Export_Relation_Schema { - private $_pageTitle; // Title of the page - private $_autoLayoutType; // Internal or Foreign Key Relations; - protected $same_wide; + private $_pageTitle; // Title of the page + //protected $sameWide; + public $showGrid; + public $showColor; + public $tableDimension; + public $sameWide; + public $withDoc; + public $showKeys; + public $orientation; + public $paper; + public $pageNumber; - public function setPageTitle($title) - { - $this->_pageTitle=$title; - } - - public function setExportType($type) - { - $this->_pageTitle=$title; - } - - public function setSameWidthTables($width) - { - $this->same_wide=$width; - } - - public function getAllTables($db,$page_number) - { - global $cfgRelation; - // Get All tables + public function setPageNumber($value) + { + $this->pageNumber = isset($value) ? $value : 1; + } + + public function setShowGrid($value) + { + $this->showGrid = (isset($value) && $value == 'on') ? 1 : 0; + } + + public function setShowColor($value) + { + $this->showColor = (isset($value) && $value == 'on') ? 1 : 0; + } + + public function setTableDimension($value) + { + $this->tableDimension = (isset($value) && $value == 'on') ? 1 : 0; + } + + public function setAllTableSameWidth($value) + { + $this->sameWide = (isset($value) && $value == 'on') ? 1 : 0; + } + + public function setWithDataDictionary($value) + { + $this->withDoc = (isset($value) && $value == 'on') ? 1 : 0; + } + + public function setShowKeys($value) + { + $this->showKeys = (isset($value) && $value == 'on') ? 1 : 0; + } + + public function setOrientation($value) + { + $this->orientation = (isset($value) && $value == 'P') ? 'P' : 'L'; + } + + public function setPaper($value) + { + $this->paper = isset($value) ? $value : 'A4'; + } + + public function setPageTitle($title) + { + $this->_pageTitle=$title; + } + + public function setExportType($value) + { + $this->exportType=$value; + } + + public function getAllTables($db,$pageNumber) + { + global $cfgRelation; + // Get All tables $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = ' . $page_number; - // echo $tab_sql; + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; + // echo $tab_sql; $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { $this->_die('',__('No tables')); - } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { + } + while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); } - return $alltables; - } - - public function initTable() - { - - } - - - /** + return $alltables; + } + + /** * Displays an error message */ - function _die($type = '',$error_message = '') + function dieSchema($type = '',$error_message = '') { global $db; @@ -82,6 +126,6 @@ class PMA_Export_Relation_Schema echo "\n"; require_once './libraries/footer.inc.php'; - } // end of the "PMA_PDF_die()" function + } } ?> \ No newline at end of file diff --git a/libraries/schema/Pdf_Relation_Schema.class.php b/libraries/schema/Pdf_Relation_Schema.class.php index f73f54822..c82fa2a80 100644 --- a/libraries/schema/Pdf_Relation_Schema.class.php +++ b/libraries/schema/Pdf_Relation_Schema.class.php @@ -1,5 +1,13 @@ <?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * + * @version $Id$ + * @package phpMyAdmin + */ + include_once("Export_Relation_Schema.class.php"); + /** * Font used in PDF. * @@ -9,20 +17,25 @@ define('PMA_PDF_FONT', 'DejaVuSans'); require_once './libraries/tcpdf/tcpdf.php'; /** - * Extends the "FPDF" class and prepares the work - * + * Extends the "TCPDF" class and helps + * in developing the structure of PDF Schema Export + * * + * @name PMA_PDF + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license * @access public - * @see FPDF - * @package phpMyAdmin + * @see TCPDF */ -class PMA_PDF extends TCPDF { +class PMA_PDF extends TCPDF +{ /** - * Defines private properties + * Defines properties */ - var $x_min; - var $y_min; - var $l_marg = 10; - var $t_marg = 10; + var $_xMin; + var $_yMin; + var $leftMargin = 10; + var $topMargin = 10; var $scale; var $PMA_links; var $Outlines = array(); @@ -66,24 +79,25 @@ class PMA_PDF extends TCPDF { * Sets the scaling factor, defines minimum coordinates and margins * * @param float scale The scaling factor - * @param float x_min The minimum X coordinate - * @param float y_min The minimum Y coordinate - * @param float l_marg The left margin - * @param float t_marg The top margin + * @param float _xMin The minimum X coordinate + * @param float _yMin The minimum Y coordinate + * @param float leftMargin The left margin + * @param float topMargin The top margin * @access public */ - function PMA_PDF_setScale($scale = 1, $x_min = 0, $y_min = 0, $l_marg = -1, $t_marg = -1) + function PMA_PDF_setScale($scale = 1, $xMin = 0, $yMin = 0, $leftMargin = -1, $topMargin = -1) { $this->scale = $scale; - $this->x_min = $x_min; - $this->y_min = $y_min; - if ($this->l_marg != -1) { - $this->l_marg = $l_marg; + $this->_xMin = $xMin; + $this->_yMin = $yMin; + if ($this->leftMargin != -1) { + $this->leftMargin = $leftMargin; } - if ($this->t_marg != -1) { - $this->t_marg = $t_marg; + if ($this->topMargin != -1) { + $this->topMargin = $topMargin; } - } // end of the "PMA_PDF_setScale" function + } + /** * Outputs a scaled cell * @@ -95,14 +109,15 @@ class PMA_PDF extends TCPDF { * @param string align Align mode * @param integer fill Whether to fill the cell with a color or not * @access public - * @see FPDF::Cell() + * @see TCPDF::Cell() */ function PMA_PDF_cellScale($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '') { $h = $h / $this->scale; $w = $w / $this->scale; $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link); - } // end of the "PMA_PDF_cellScale" function + } + /** * Draws a scaled line * @@ -111,67 +126,72 @@ class PMA_PDF extends TCPDF { * @param float x2 The horizontal position of the ending point * @param float y2 The vertical position of the ending point * @access public - * @see FPDF::Line() + * @see TCPDF::Line() */ function PMA_PDF_lineScale($x1, $y1, $x2, $y2) { - $x1 = ($x1 - $this->x_min) / $this->scale + $this->l_marg; - $y1 = ($y1 - $this->y_min) / $this->scale + $this->t_marg; - $x2 = ($x2 - $this->x_min) / $this->scale + $this->l_marg; - $y2 = ($y2 - $this->y_min) / $this->scale + $this->t_marg; + $x1 = ($x1 - $this->_xMin) / $this->scale + $this->leftMargin; + $y1 = ($y1 - $this->_yMin) / $this->scale + $this->topMargin; + $x2 = ($x2 - $this->_xMin) / $this->scale + $this->leftMargin; + $y2 = ($y2 - $this->_yMin) / $this->scale + $this->topMargin; $this->Line($x1, $y1, $x2, $y2); - } // end of the "PMA_PDF_lineScale" function + } + /** * Sets x and y scaled positions * * @param float x The x position * @param float y The y position * @access public - * @see FPDF::SetXY() + * @see TCPDF::SetXY() */ function PMA_PDF_setXyScale($x, $y) { - $x = ($x - $this->x_min) / $this->scale + $this->l_marg; - $y = ($y - $this->y_min) / $this->scale + $this->t_marg; + $x = ($x - $this->_xMin) / $this->scale + $this->leftMargin; + $y = ($y - $this->_yMin) / $this->scale + $this->topMargin; $this->SetXY($x, $y); - } // end of the "PMA_PDF_setXyScale" function + } + /** * Sets the X scaled positions * * @param float x The x position * @access public - * @see FPDF::SetX() + * @see TCPDF::SetX() */ function PMA_PDF_setXScale($x) { - $x = ($x - $this->x_min) / $this->scale + $this->l_marg; + $x = ($x - $this->_xMin) / $this->scale + $this->leftMargin; $this->SetX($x); - } // end of the "PMA_PDF_setXScale" function + } + /** * Sets the scaled font size * * @param float size The font size (in points) * @access public - * @see FPDF::SetFontSize() + * @see TCPDF::SetFontSize() */ function PMA_PDF_setFontSizeScale($size) { // Set font size in points $size = $size / $this->scale; $this->SetFontSize($size); - } // end of the "PMA_PDF_setFontSizeScale" function + } + /** * Sets the scaled line width * * @param float width The line width * @access public - * @see FPDF::SetLineWidth() + * @see TCPDF::SetLineWidth() */ function PMA_PDF_setLineWidthScale($width) { $width = $width / $this->scale; $this->SetLineWidth($width); - } // end of the "PMA_PDF_setLineWidthScale" function + } + /** * Displays an error message * @@ -210,22 +230,23 @@ class PMA_PDF extends TCPDF { require_once './libraries/footer.inc.php'; }*/ // end of the "PMA_PDF_die()" function /** - * Aliases the "Error()" function from the FPDF class to the + * Aliases the "Error()" function from the TCPDF class to the * "PMA_PDF_die()" one * * @param string error_message the error mesage * @access public - * @see PMA_PDF_die + * @see PMA_Export_Relation_Schema::dieSchema */ function Error($error_message = '') { - PMA_Export_Relation_Schema::_die($error_message); - } // end of the "Error()" method + PMA_Export_Relation_Schema::dieSchema($error_message); + } + function Header() { // We only show this if we find something in the new pdf_pages table - // This function must be named "Header" to work with the FPDF library + // This function must be named "Header" to work with the TCPDF library global $cfgRelation, $db, $pdf_page_number, $with_doc; if ($with_doc) { $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) @@ -239,9 +260,10 @@ class PMA_PDF extends TCPDF { $this->Ln(); } } + function Footer() { - // This function must be named "Footer" to work with the FPDF library + // This function must be named "Footer" to work with the TCPDF library global $with_doc; if ($with_doc) { $this->SetY(-15); @@ -251,6 +273,7 @@ class PMA_PDF extends TCPDF { $this->SetY(20); } } + function Bookmark($txt, $level = 0, $y = 0) { // Add a bookmark @@ -357,6 +380,7 @@ class PMA_PDF extends TCPDF { $this->_out('/PageMode /UseOutlines'); } } + function SetWidths($w) { // column widths @@ -453,161 +477,57 @@ class PMA_PDF extends TCPDF { } return $nl; } -} // end of the "PMA_PDF" class +} /** * Draws tables schema * - * @access private - * @see PMA_RT - * @package phpMyAdmin */ -class PMA_RT_Table { +class Table_Stats { /** - * Defines private properties + * Defines properties */ - var $nb_fiels; - var $table_name; - var $width = 0; - var $height; - var $fields = array(); - var $height_cell = 6; - var $x, $y; - var $primary = array(); - var $show_info = false; + private $_tableName; + private $_showInfo = false; + + public $nb_fiels; + public $width = 0; + public $height; + public $fields = array(); + public $heightCell = 6; + public $x, $y; + public $primary = array(); /** - * Returns title of the current table, - * title can have the dimensions of the table - * - * @access private - */ - function getTitle() - { - return ($this->show_info ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->height) : '') . ' ' . $this->table_name; - } // end of the "getTitle" function - /** - * Sets the width of the table - * - * @param integer ff The font size - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Table_setWidth($ff) - { - global $pdf; - - foreach ($this->fields as $field) { - $this->width = max($this->width, $pdf->GetStringWidth($field)); - } - //echo $this->width.'<br />'; - $this->width += $pdf->GetStringWidth(' '); - //echo $this->width.'<br />'; - $pdf->SetFont($ff, 'B'); - // it is unknown what value must be added, because - // table title is affected by the tabe width value - //echo $this->getTitle(); - while ($this->width < $pdf->GetStringWidth($this->getTitle())) { - $this->width += 5; - } - //echo $this->width.'<br />'; - $pdf->SetFont($ff, ''); - } // end of the "PMA_RT_Table_setWidth()" method - /** - * Sets the height of the table - * - * @access private - */ - function PMA_RT_Table_setHeight() - { - $this->height = (count($this->fields) + 1) * $this->height_cell; - } // end of the "PMA_RT_Table_setHeight()" method - /** - * Do draw the table - * - * @param integer ff The font size - * @param boolean setcolortWhether to display color - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Table_draw($ff, $setcolor = 0) - { - global $pdf, $with_doc; - - $pdf->PMA_PDF_setXyScale($this->x, $this->y); - $pdf->SetFont($ff, 'B'); - if ($setcolor) { - $pdf->SetTextColor(200); - $pdf->SetFillColor(0, 0, 128); - } - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name]['-'], -1); - } else { - $pdf->PMA_links['doc'][$this->table_name]['-'] = ''; - } - - $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, $this->getTitle(), 1, 1, 'C', $setcolor, $pdf->PMA_links['doc'][$this->table_name]['-']); - $pdf->PMA_PDF_setXScale($this->x); - $pdf->SetFont($ff, ''); - $pdf->SetTextColor(0); - $pdf->SetFillColor(255); - - foreach ($this->fields as $field) { - if ($setcolor) { - if (in_array($field, $this->primary)) { - $pdf->SetFillColor(215, 121, 123); - } - if ($field == $this->displayfield) { - $pdf->SetFillColor(142, 159, 224); - } - } - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name][$field], -1); - } else { - $pdf->PMA_links['doc'][$this->table_name][$field] = ''; - } - - $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, ' ' . $field, 1, 1, 'L', $setcolor, $pdf->PMA_links['doc'][$this->table_name][$field]); - $pdf->PMA_PDF_setXScale($this->x); - $pdf->SetFillColor(255); - } // end while - /*if ($pdf->PageNo() > 1) { - $pdf->PMA_PDF_die(__('The scale factor is too small to fit the schema on one page')); - } */ - } // end of the "PMA_RT_Table_draw()" method - /** - * The "PMA_RT_Table" constructor + * The "Table_Stats" constructor * * @param string table_name The table name - * @param integer ff The font size - * @param integer same_width The max. with among tables - * @param boolean show_keys Whether to display keys or not - * @param boolean show_info Whether to display table position or not - * @global object The current PDF document - * @global integer The current page number (from the + * @param integer fontSize The font size + * @param integer pageNumber The current page number (from the * $cfg['Servers'][$i]['table_coords'] table) + * @param integer sameWideWidth The max. with among tables + * @param boolean showKeys Whether to display keys or not + * @param boolean showInfo Whether to display table position or not + * @global object The current PDF document * @global array The relations settings * @global string The current db name - * @access private - * @see PMA_PDF, PMA_RT_Table::PMA_RT_Table_setWidth, - PMA_RT_Table::PMA_RT_Table_setHeight + * @see PMA_PDF, Table_Stats::Table_Stats_setWidth, + Table_Stats::Table_Stats_setHeight */ - function __construct($table_name, $ff, &$same_wide_width, $show_keys = false, $show_info = false) + function __construct($tableName, $fontSize, $pageNumber, &$sameWideWidth, $showKeys = false, $showInfo = false) { - global $pdf, $pdf_page_number, $cfgRelation, $db; + global $pdf, $cfgRelation, $db; - $this->table_name = $table_name; - $sql = 'DESCRIBE ' . PMA_backquote($table_name); + $this->_tableName = $tableName; + $sql = 'DESCRIBE ' . PMA_backquote($tableName); $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { - $pdf->PMA_PDF_die(sprintf(__('The %s table doesn\'t exist!'), $table_name)); + $pdf->Error(sprintf(__('The %s table doesn\'t exist!'), $tableName)); } // load fields //check to see if it will load all fields or only the foreign keys - if ($show_keys) { - $indexes = PMA_Index::getFromTable($this->table_name, $db); + if ($showKeys) { + $indexes = PMA_Index::getFromTable($this->_tableName, $db); $all_columns = array(); foreach ($indexes as $index) { $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); @@ -619,52 +539,157 @@ class PMA_RT_Table { } } - $this->show_info = $show_info; - - // height and width - $this->PMA_RT_Table_setHeight(); - // setWidth must me after setHeight, because title - // can include table height which changes table width - $this->PMA_RT_Table_setWidth($ff); - if ($same_wide_width < $this->width) { - $same_wide_width = $this->width; + $this->_showInfo = $showInfo; + $this->_setHeight(); + /* + * setWidth must me after setHeight, because title + * can include table height which changes table width + */ + $this->_setWidth($fontSize); + if ($sameWideWidth < $this->width) { + $sameWideWidth = $this->width; } - // x and y $sql = 'SELECT x, y FROM ' - . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($table_name) . '\'' - . ' AND pdf_page_number = ' . $pdf_page_number; + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); - if (!$result || !PMA_DBI_num_rows($result)) { - $pdf->PMA_PDF_die(sprintf(__('Please configure the coordinates for table %s'), $table_name)); + $pdf->Error(sprintf(__('Please configure the coordinates for table %s'), $tableName)); } list($this->x, $this->y) = PMA_DBI_fetch_row($result); $this->x = (double) $this->x; $this->y = (double) $this->y; - // displayfield - $this->displayfield = PMA_getDisplayField($db, $table_name); - // index - $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($table_name) . ';', null, PMA_DBI_QUERY_STORE); + /* + * displayfield + */ + $this->displayfield = PMA_getDisplayField($db, $tableName); + /* + * index + */ + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); if (PMA_DBI_num_rows($result) > 0) { while ($row = PMA_DBI_fetch_assoc($result)) { if ($row['Key_name'] == 'PRIMARY') { $this->primary[] = $row['Column_name']; } } - } // end if - } // end of the "PMA_RT_Table()" method -} // end class "PMA_RT_Table" + } + } + + /** + * Returns title of the current table, + * title can have the dimensions of the table + * + * @access private + */ + private function _getTitle() + { + return ($this->_showInfo ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->height) : '') . ' ' . $this->_tableName; + } + + /** + * Sets the width of the table + * + * @param integer fontSize The font size + * @global object The current PDF document + * @access private + * @see PMA_PDF + */ + function _setWidth($fontSize) + { + global $pdf; + + foreach ($this->fields as $field) { + $this->width = max($this->width, $pdf->GetStringWidth($field)); + } + $this->width += $pdf->GetStringWidth(' '); + $pdf->SetFont($fontSize, 'B'); + /* + * it is unknown what value must be added, because + * table title is affected by the tabe width value + */ + while ($this->width < $pdf->GetStringWidth($this->_getTitle())) { + $this->width += 5; + } + $pdf->SetFont($fontSize, ''); + } + + /** + * Sets the height of the table + * + * @access private + */ + private function _setHeight() + { + $this->height = (count($this->fields) + 1) * $this->heightCell; + } + + /** + * Do draw the table + * + * @param integer fontSize The font size + * @param boolean setColor Whether to display color + * @global object The current PDF document + * @access public + * @see PMA_PDF + */ + public function tableDraw($fontSize, $withDoc, $setColor = 0) + { + global $pdf, $withDoc; + + $pdf->PMA_PDF_setXyScale($this->x, $this->y); + $pdf->SetFont($fontSize, 'B'); + if ($setColor) { + $pdf->SetTextColor(200); + $pdf->SetFillColor(0, 0, 128); + } + if ($withDoc) { + $pdf->SetLink($pdf->PMA_links['RT'][$this->_tableName]['-'], -1); + } else { + $pdf->PMA_links['doc'][$this->_tableName]['-'] = ''; + } + + $pdf->PMA_PDF_cellScale($this->width, $this->heightCell, $this->_getTitle(), 1, 1, 'C', $setColor, $pdf->PMA_links['doc'][$this->_tableName]['-']); + $pdf->PMA_PDF_setXScale($this->x); + $pdf->SetFont($fontSize, ''); + $pdf->SetTextColor(0); + $pdf->SetFillColor(255); + + foreach ($this->fields as $field) { + if ($setColor) { + if (in_array($field, $this->primary)) { + $pdf->SetFillColor(215, 121, 123); + } + if ($field == $this->displayfield) { + $pdf->SetFillColor(142, 159, 224); + } + } + if ($withDoc) { + $pdf->SetLink($pdf->PMA_links['RT'][$this->_tableName][$field], -1); + } else { + $pdf->PMA_links['doc'][$this->_tableName][$field] = ''; + } + + $pdf->PMA_PDF_cellScale($this->width, $this->heightCell, ' ' . $field, 1, 1, 'L', $setColor, $pdf->PMA_links['doc'][$this->_tableName][$field]); + $pdf->PMA_PDF_setXScale($this->x); + $pdf->SetFillColor(255); + } + /*if ($pdf->PageNo() > 1) { + $pdf->PMA_PDF_die(__('The scale factor is too small to fit the schema on one page')); + } */ + } +} /** * Draws relation links * * @access private - * @see PMA_RT + * @see * @package phpMyAdmin */ -class PMA_RT_Relation { +class Relation_Stats { /** * Defines private properties */ @@ -682,26 +707,27 @@ class PMA_RT_Relation { * @return array Arrows coordinates * @access private */ - function PMA_RT_Relation_getXy($table, $column) + private function _getXy($table, $column) { $pos = array_search($column, $table->fields); // x_left, x_right, y - return array($table->x, $table->x + + $table->width, $table->y + ($pos + 1.5) * $table->height_cell); - } // end of the "PMA_RT_Relation_getXy()" method + return array($table->x, $table->x + + $table->width, $table->y + ($pos + 1.5) * $table->heightCell); + } + /** * Do draws relation links * - * @param boolean change_color Whether to use one color per relation or not + * @param boolean changeColor Whether to use one color per relation or not * @param integer i The id of the link to draw * @global object The current PDF document - * @access private + * @access public * @see PMA_PDF */ - function PMA_RT_Relation_draw($change_color, $i) + public function relationDraw($changeColor, $i) { global $pdf; - if ($change_color) { + if ($changeColor) { $d = $i % 6; $j = ($i - $d) / 6; $j = $j % 4; @@ -733,21 +759,21 @@ class PMA_RT_Relation { $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest + $this->w_tick / $root2); $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest - $this->w_tick / $root2); $pdf->SetDrawColor(0); - } // end of the "PMA_RT_Relation_draw()" method + } + /** - * The "PMA_RT_Relation" constructor + * The "Relation_Stats" constructor * - * @param string master_table The master table name - * @param string master_field The relation field in the master table - * @param string foreign_table The foreign table name - * @param string foreigh_field The relation field in the foreign table - * @access private - * @see PMA_RT_Relation::PMA_RT_Relation_getXy + * @param string masterTable The master table name + * @param string masterField The relation field in the master table + * @param string foreignTable The foreign table name + * @param string foreignField The relation field in the foreign table + * @see _Relation::_getXy */ - function __construct($master_table, $master_field, $foreign_table, $foreign_field) + function __construct($masterTable, $masterField, $foreignTable, $foreignField) { - $src_pos = $this->PMA_RT_Relation_getXy($master_table, $master_field); - $dest_pos = $this->PMA_RT_Relation_getXy($foreign_table, $foreign_field); + $src_pos = $this->_getXy($masterTable, $masterField); + $dest_pos = $this->_getXy($foreignTable, $foreignField); $src_left = $src_pos[0] - $this->w_tick; $src_right = $src_pos[1] + $this->w_tick; $dest_left = $dest_pos[0] - $this->w_tick; @@ -782,41 +808,32 @@ class PMA_RT_Relation { } $this->y_src = $src_pos[2]; $this->y_dest = $dest_pos[2]; - } // end of the "PMA_RT_Relation()" method -} // end of the "PMA_RT_Relation" class + } +} class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema { - /** - * Defines private properties + /** + * Defines properties */ - private $tables = array(); - private $relations = array(); - private $ff = PMA_PDF_FONT; - private $x_max = 0; - private $y_max = 0; + private $_tables = array(); + private $_relations = array(); + private $_ff = PMA_PDF_FONT; + private $_xMax = 0; + private $_yMax = 0; private $scale; - private $x_min = 100000; - private $y_min = 100000; - private $t_marg = 10; - private $b_marg = 10; - private $l_marg = 10; - private $r_marg = 10; + private $_xMin = 100000; + private $_yMin = 100000; + private $topMargin = 10; + private $bottomMargin = 10; + private $leftMargin = 10; + private $rightMargin = 10; private $_tablewidth; - - /** - * The "PMA_RT" constructor + + /** + * The "PMA_Pdf_Relation_Schema" constructor * - * @param integer page_number The page number to draw (from the - * $cfg['Servers'][$i]['table_coords'] table) - * @param boolean show_info Whether to display table position or not - * @param boolean change_color Was originally whether to use one color per - * relation or not, now enables/disables color - * everywhere, due to some problems printing with color - * @param boolean show_grid Whether to draw grids or not - * @param boolean all_tab_same_wide Whether all tables should have the same width or not - * @param boolean show_keys Wheter to show all field or only the keys - * @global object The current PDF document + * @global object The current PDF Schema document * @global string The current db name * @global array The relations settings * @access private @@ -824,22 +841,22 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema */ function __construct() { - //global $pdf,$db,$export_type,$cfgRelation,$pdf_page_number,$show_keys,$change_color,$show_table_dimension,$orientation, $with_doc,$show_grid,$paper; - global $pdf,$db,$export_type,$cfgRelation,$pdf_page_number,$show_grid,$show_keys,$change_color,$show_color,$show_info,$all_table_same_wide,$show_table_dimension,$orientation, $with_doc,$show_grid,$paper; - $pdf_page_number = isset($pdf_page_number) ? $pdf_page_number : 1; - $show_grid = (isset($show_grid) && $show_grid == 'on') ? 1 : 0; - $change_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; - $show_info = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; - $all_table_same_wide = (isset($all_table_same_wide) && $all_table_same_wide == 'on') ? 1 : 0; - $with_doc = (isset($with_doc) && $with_doc == 'on') ? 1 : 0; - $orientation = (isset($orientation) && $orientation == 'P') ? 'P' : 'L'; - $paper = isset($paper) ? $paper : 'A4'; - $show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; + global $pdf,$db,$cfgRelation; - $this->setSameWidthTables($all_table_same_wide); - // Initializes a new document - $pdf = new PMA_PDF('L', 'mm', $paper); - $pdf->SetTitle(sprintf(__('Schema of the %s database - Page %s'), $GLOBALS['db'], $pdf_page_number)); + $this->setPageNumber($_POST['pdf_page_number']); + $this->setShowGrid($_POST['show_grid']); + $this->setShowColor($_POST['show_color']); + $this->setShowKeys($_POST['show_keys']); + $this->setTableDimension($_POST['show_table_dimension']); + $this->setAllTableSameWidth($_POST['all_table_same_wide']); + $this->setWithDataDictionary($_POST['with_doc']); + $this->setOrientation($_POST['orientation']); + $this->setPaper($_POST['paper']); + $this->setExportType($_POST['export_type']); + + // Initializes a new document + $pdf = new PMA_PDF('L', 'mm', $this->paper); + $pdf->SetTitle(sprintf(__('Schema of the %s database - Page %s'), $GLOBALS['db'], $this->pageNumber)); $pdf->setCMargin(0); $pdf->Open(); $pdf->SetAuthor('phpMyAdmin ' . PMA_VERSION); @@ -848,64 +865,55 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema $pdf->AddFont('DejaVuSans', 'B', 'dejavusansb.php'); $pdf->AddFont('DejaVuSerif', '', 'dejavuserif.php'); $pdf->AddFont('DejaVuSerif', 'B', 'dejavuserifb.php'); - $pdf->SetFont($this->ff, '', 14); + $pdf->SetFont($this->_ff, '', 14); $pdf->SetAutoPageBreak('auto'); - $alltables = $this->getAllTables($db,$pdf_page_number); + $alltables = $this->getAllTables($db,$this->pageNumber); - if ($with_doc) { + if ($this->withDoc) { $pdf->SetAutoPageBreak('auto', 15); $pdf->setCMargin(1); - $this->PMA_RT_DOC($alltables); + $this->dataDictionaryDoc($alltables); $pdf->SetAutoPageBreak('auto'); $pdf->setCMargin(0); } $pdf->Addpage(); - if ($with_doc) { + if ($this->withDoc) { $pdf->SetLink($pdf->PMA_links['RT']['-'], -1); $pdf->Bookmark(__('Relational schema')); $pdf->SetAlias('{00}', $pdf->PageNo()) ; - $this->t_marg = 28; - $this->b_marg = 28; + $this->topMargin = 28; + $this->bottomMargin = 28; } /* snip */ foreach ($alltables as $table) { if (!isset($this->tables[$table])) { - $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->_tablewidth, $show_keys, $show_info); - // $this->tables[$table]=$table; + $this->tables[$table] = new Table_Stats($table, $this->_ff, $this->pageNumber, $this->_tablewidth, $this->showKeys, $this->tableDimension); } - - if ($this->same_wide) { + if ($this->sameWide) { $this->tables[$table]->width = $this->_tablewidth; } - $this->PMA_RT_setMinMax($this->tables[$table]); + $this->_setMinMax($this->tables[$table]); } - + // Defines the scale factor $this->scale = ceil( max( - ($this->x_max - $this->x_min) / ($pdf->getFh() - $this->r_marg - $this->l_marg), - ($this->y_max - $this->y_min) / ($pdf->getFw() - $this->t_marg - $this->b_marg)) + ($this->_xMax - $this->_xMin) / ($pdf->getFh() - $this->rightMargin - $this->leftMargin), + ($this->_yMax - $this->_yMin) / ($pdf->getFw() - $this->topMargin - $this->bottomMargin)) * 100) / 100; - $pdf->PMA_PDF_setScale($this->scale, $this->x_min, $this->y_min, $this->l_marg, $this->t_marg); + $pdf->PMA_PDF_setScale($this->scale, $this->_xMin, $this->_yMin, $this->leftMargin, $this->topMargin); // Builds and save the PDF document $pdf->PMA_PDF_setLineWidthScale(0.1); - if ($show_grid) { + if ($this->showGrid) { $pdf->SetFontSize(10); - $this->PMA_RT_strokeGrid(); + $this->_strokeGrid(); } $pdf->PMA_PDF_setFontSizeScale(14); - // $sql = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) - // . ' WHERE master_db = \'' . PMA_sqlAddslashes($db) . '\' ' - // . ' AND foreign_db = \'' . PMA_sqlAddslashes($db) . '\' ' - // . ' AND master_table IN (' . $intable . ')' - // . ' AND foreign_table IN (' . $intable . ')'; - // $result = PMA_query_as_controluser($sql); - // previous logic was checking master tables and foreign tables // but I think that looping on every table of the pdf page as a master // and finding its foreigns is OK (then we can support innodb) @@ -920,36 +928,39 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema // (do not use array_search() because we would have to // to do a === FALSE and this is not PHP3 compatible) if (in_array($rel['foreign_table'], $alltables)) { - $this->PMA_RT_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'], $show_info); + $this->_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'], $this->tableDimension); } } // end while } // end if } // end while - - - + if ($seen_a_relation) { - $this->PMA_RT_drawRelations($change_color); + $this->_drawRelations($this->showColor); } + /* print '<pre>'; + print_r(get_object_vars($this)); + print_r(get_object_vars($pdf)); + print '</pre>'; + exit(); + */ + $this->_drawTables($this->showColor); + $this->_showOutput($this->pageNumber); + } - $this->PMA_RT_drawTables($change_color); - - - $this->PMA_RT_showRt(); - } // end of the "PMA_RT()" Constructor method - /** + /** * Sets X and Y minimum and maximum for a table cell * - * @param string table The table name + * @param string table The table name of which sets XY co-ordinates * @access private */ - function PMA_RT_setMinMax($table) + private function _setMinMax($table) { - $this->x_max = max($this->x_max, $table->x + $table->width); - $this->y_max = max($this->y_max, $table->y + $table->height); - $this->x_min = min($this->x_min, $table->x); - $this->y_min = min($this->y_min, $table->y); - } // end of the "PMA_RT_setMinMax()" method + $this->_xMax = max($this->_xMax, $table->x + $table->width); + $this->_yMax = max($this->_yMax, $table->y + $table->height); + $this->_xMin = min($this->_xMin, $table->x); + $this->_yMin = min($this->_yMin, $table->y); + } + /** * Defines relation objects * @@ -959,20 +970,21 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema * @param string foreign_field The relation field in the foreign table * @param boolean show_info Whether to display table position or not * @access private - * @see PMA_RT_setMinMax + * @see _setMinMax */ - function PMA_RT_addRelation($master_table, $master_field, $foreign_table, $foreign_field, $show_info) + private function _addRelation($masterTable, $masterField, $foreignTable, $foreignField, $showInfo) { - if (!isset($this->tables[$master_table])) { - $this->tables[$master_table] = new PMA_RT_Table($master_table, $this->ff, $this->_tablewidth, false, $show_info); - $this->PMA_RT_setMinMax($this->tables[$master_table]); + if (!isset($this->tables[$masterTable])) { + $this->tables[$masterTable] = new Table_Stats($masterTable, $this->_ff, $this->pageNumber, $this->_tablewidth, false, $showInfo); + $this->_setMinMax($this->tables[$masterTable]); } - if (!isset($this->tables[$foreign_table])) { - $this->tables[$foreign_table] = new PMA_RT_Table($foreign_table, $this->ff, $this->_tablewidth, false, $show_info); - $this->PMA_RT_setMinMax($this->tables[$foreign_table]); + if (!isset($this->tables[$foreignTable])) { + $this->tables[$foreignTable] = new Table_Stats($foreignTable, $this->_ff, $this->pageNumber, $this->_tablewidth, false, $showInfo); + $this->_setMinMax($this->tables[$foreignTable]); } - $this->relations[] = new PMA_RT_Relation($this->tables[$master_table], $master_field, $this->tables[$foreign_table], $foreign_field); - } // end of the "PMA_RT_addRelation()" method + $this->relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); + } + /** * Draws the grid * @@ -980,7 +992,7 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema * @access private * @see PMA_PDF */ - function PMA_RT_strokeGrid() + private function _strokeGrid() { global $pdf; @@ -992,7 +1004,7 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema // Avoid duplicates if ($l > 0) { $pdf->SetXY(0, $l * 10); - $label = (string) sprintf('%.0f', ($l * 10 - $this->t_marg) * $this->scale + $this->y_min); + $label = (string) sprintf('%.0f', ($l * 10 - $this->topMargin) * $this->scale + $this->_yMin); $pdf->Cell(5, 5, ' ' . $label); } // end if } // end for @@ -1000,40 +1012,44 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema for ($j = 0; $j < 30 ;$j++) { $pdf->line($j * 10, 0, $j * 10, $pdf->getFw()); $pdf->SetXY($j * 10, 0); - $label = (string) sprintf('%.0f', ($j * 10 - $this->l_marg) * $this->scale + $this->x_min); + $label = (string) sprintf('%.0f', ($j * 10 - $this->leftMargin) * $this->scale + $this->_xMin); $pdf->Cell(5, 7, $label); - } // end for - } // end of the "PMA_RT_strokeGrid()" method + } + } + /** * Draws relation arrows * - * @param boolean change_color Whether to use one color per relation or not + * @param boolean changeColor Whether to use one color per relation or not * @access private - * @see PMA_RT_Relation::PMA_RT_Relation_draw() + * @see Relation_Stats::relationdraw() */ - function PMA_RT_drawRelations($change_color) + private function _drawRelations($changeColor) { $i = 0; foreach ($this->relations as $relation) { - $relation->PMA_RT_Relation_draw($change_color, $i); + $relation->relationDraw($changeColor, $i); $i++; - } // end while - } // end of the "PMA_RT_drawRelations()" method + } + } + /** * Draws tables * - * @param boolean draw_color Whether to display table position or not + * @param boolean changeColor Whether to display table position or not * @access private - * @see PMA_RT_Table::PMA_RT_Table_draw() + * @see Table_Stats::tableDraw() */ - function PMA_RT_drawTables($draw_color = 0) + private function _drawTables($changeColor = 0) { foreach ($this->tables as $table) { - $table->PMA_RT_Table_draw($this->ff, $draw_color); + $table->tableDraw($this->_ff, $this->withDoc, $changeColor); } - } // end of the "PMA_RT_drawTables()" method + } + /** - * Ouputs the PDF document to a file + * Ouputs the PDF document to a file + * or sends the output to browser * * @global object The current PDF document * @global string The current database name @@ -1042,285 +1058,282 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema * @access private * @see PMA_PDF */ - function PMA_RT_showRt() + private function _showOutput($pageNumber) { - global $pdf, $db, $pdf_page_number, $cfgRelation; + global $pdf, $db, $cfgRelation; $pdf->SetFontSize(14); $pdf->SetLineWidth(0.2); $pdf->SetDisplayMode('fullpage'); // Get the name of this pdfpage to use as filename (Mike Beck) $_name_sql = 'SELECT page_descr FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE page_nr = ' . $pdf_page_number; + . ' WHERE page_nr = ' . $pageNumber; $_name_rs = PMA_query_as_controluser($_name_sql); if ($_name_rs) { $_name_row = PMA_DBI_fetch_row($_name_rs); $filename = $_name_row[0] . '.pdf'; } - // i don't know if there is a chance for this to happen, but rather be on the safe side: if (empty($filename)) { - $filename = $pdf_page_number . '.pdf'; + $filename = $pageNumber . '.pdf'; } - //exit(); - // $pdf->Output($db . '_' . $filename, TRUE); $pdf->Output($db . '_' . $filename, 'I'); // destination: Inline - } // end of the "PMA_RT_showRt()" method - - function PMA_RT_DOC($alltables) -{ - global $db, $pdf, $orientation, $paper; - // TOC - $pdf->addpage($GLOBALS['orientation']); - $pdf->Cell(0, 9, __('Table of contents'), 1, 0, 'C'); - $pdf->Ln(15); - $i = 1; - foreach ($alltables as $table) { - $pdf->PMA_links['doc'][$table]['-'] = $pdf->AddLink(); - $pdf->SetX(10); - // $pdf->Ln(1); - $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$table]['-']); - $pdf->SetX(10); - $pdf->Cell(0, 6, $i . ' ' . $table, 0, 1, 'L', 0, $pdf->PMA_links['doc'][$table]['-']); - // $pdf->Ln(1); - $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';'); - while ($row = PMA_DBI_fetch_assoc($result)) { - $pdf->SetX(20); - $field_name = $row['Field']; - $pdf->PMA_links['doc'][$table][$field_name] = $pdf->AddLink(); - // $pdf->Cell(0, 6, $field_name,0,1,'L',0, $pdf->PMA_links['doc'][$table][$field_name]); - } - $lasttable = $table; - $i++; } - $pdf->PMA_links['RT']['-'] = $pdf->AddLink(); - $pdf->SetX(10); - $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$lasttable]['-']); - $pdf->SetX(10); - $pdf->Cell(0, 6, $i + 1 . ' ' . __('Relational schema'), 0, 1, 'L', 0, $pdf->PMA_links['RT']['-']); - $z = 0; - foreach ($alltables as $table) { - $z++; + + public function dataDictionaryDoc($alltables) + { + global $db, $pdf, $orientation, $paper; + // TOC $pdf->addpage($GLOBALS['orientation']); - $pdf->Bookmark($table); - $pdf->SetAlias('{' . sprintf("%02d", $z) . '}', $pdf->PageNo()) ; - $pdf->PMA_links['RT'][$table]['-'] = $pdf->AddLink(); - $pdf->SetLink($pdf->PMA_links['doc'][$table]['-'], -1); - $pdf->SetFont('', 'B', 18); - $pdf->Cell(0, 8, $z . ' ' . $table, 1, 1, 'C', 0, $pdf->PMA_links['RT'][$table]['-']); - $pdf->SetFont('', '', 8); - $pdf->ln(); - - $cfgRelation = PMA_getRelationsParam(); - $comments = PMA_getComments($db, $table); - if ($cfgRelation['mimework']) { - $mime_map = PMA_getMIME($db, $table, true); + $pdf->Cell(0, 9, __('Table of contents'), 1, 0, 'C'); + $pdf->Ln(15); + $i = 1; + foreach ($alltables as $table) { + $pdf->PMA_links['doc'][$table]['-'] = $pdf->AddLink(); + $pdf->SetX(10); + // $pdf->Ln(1); + $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$table]['-']); + $pdf->SetX(10); + $pdf->Cell(0, 6, $i . ' ' . $table, 0, 1, 'L', 0, $pdf->PMA_links['doc'][$table]['-']); + // $pdf->Ln(1); + $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';'); + while ($row = PMA_DBI_fetch_assoc($result)) { + $pdf->SetX(20); + $field_name = $row['Field']; + $pdf->PMA_links['doc'][$table][$field_name] = $pdf->AddLink(); + // $pdf->Cell(0, 6, $field_name,0,1,'L',0, $pdf->PMA_links['doc'][$table][$field_name]); + } + $lasttable = $table; + $i++; } + $pdf->PMA_links['RT']['-'] = $pdf->AddLink(); + $pdf->SetX(10); + $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$lasttable]['-']); + $pdf->SetX(10); + $pdf->Cell(0, 6, $i + 1 . ' ' . __('Relational schema'), 0, 1, 'L', 0, $pdf->PMA_links['RT']['-']); + $z = 0; + foreach ($alltables as $table) { + $z++; + $pdf->addpage($GLOBALS['orientation']); + $pdf->Bookmark($table); + $pdf->SetAlias('{' . sprintf("%02d", $z) . '}', $pdf->PageNo()) ; + $pdf->PMA_links['RT'][$table]['-'] = $pdf->AddLink(); + $pdf->SetLink($pdf->PMA_links['doc'][$table]['-'], -1); + $pdf->SetFont('', 'B', 18); + $pdf->Cell(0, 8, $z . ' ' . $table, 1, 1, 'C', 0, $pdf->PMA_links['RT'][$table]['-']); + $pdf->SetFont('', '', 8); + $pdf->ln(); - /** - * Gets table informations - */ - $showtable = PMA_Table::sGetStatusInfo($db, $table); - $num_rows = (isset($showtable['Rows']) ? $showtable['Rows'] : 0); - $show_comment = (isset($showtable['Comment']) ? $showtable['Comment'] : ''); - $create_time = (isset($showtable['Create_time']) ? PMA_localisedDate(strtotime($showtable['Create_time'])) : ''); - $update_time = (isset($showtable['Update_time']) ? PMA_localisedDate(strtotime($showtable['Update_time'])) : ''); - $check_time = (isset($showtable['Check_time']) ? PMA_localisedDate(strtotime($showtable['Check_time'])) : ''); - - /** - * Gets table keys and retains them - */ - $result = PMA_DBI_query('SHOW KEYS FROM ' . PMA_backquote($table) . ';'); - $primary = ''; - $indexes = array(); - $lastIndex = ''; - $indexes_info = array(); - $indexes_data = array(); - $pk_array = array(); // will be use to emphasis prim. keys in the table - // view - while ($row = PMA_DBI_fetch_assoc($result)) { - // Backups the list of primary keys - if ($row['Key_name'] == 'PRIMARY') { - $primary .= $row['Column_name'] . ', '; - $pk_array[$row['Column_name']] = 1; + $cfgRelation = PMA_getRelationsParam(); + $comments = PMA_getComments($db, $table); + if ($cfgRelation['mimework']) { + $mime_map = PMA_getMIME($db, $table, true); } - // Retains keys informations - if ($row['Key_name'] != $lastIndex) { - $indexes[] = $row['Key_name']; - $lastIndex = $row['Key_name']; - } - $indexes_info[$row['Key_name']]['Sequences'][] = $row['Seq_in_index']; - $indexes_info[$row['Key_name']]['Non_unique'] = $row['Non_unique']; - if (isset($row['Cardinality'])) { - $indexes_info[$row['Key_name']]['Cardinality'] = $row['Cardinality']; - } - // I don't know what does following column mean.... - // $indexes_info[$row['Key_name']]['Packed'] = $row['Packed']; - $indexes_info[$row['Key_name']]['Comment'] = $row['Comment']; - $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Column_name'] = $row['Column_name']; - if (isset($row['Sub_part'])) { - $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Sub_part'] = $row['Sub_part']; + /** + * Gets table informations + */ + $showtable = PMA_Table::sGetStatusInfo($db, $table); + $num_rows = (isset($showtable['Rows']) ? $showtable['Rows'] : 0); + $show_comment = (isset($showtable['Comment']) ? $showtable['Comment'] : ''); + $create_time = (isset($showtable['Create_time']) ? PMA_localisedDate(strtotime($showtable['Create_time'])) : ''); + $update_time = (isset($showtable['Update_time']) ? PMA_localisedDate(strtotime($showtable['Update_time'])) : ''); + $check_time = (isset($showtable['Check_time']) ? PMA_localisedDate(strtotime($showtable['Check_time'])) : ''); + + /** + * Gets table keys and retains them + */ + $result = PMA_DBI_query('SHOW KEYS FROM ' . PMA_backquote($table) . ';'); + $primary = ''; + $indexes = array(); + $lastIndex = ''; + $indexes_info = array(); + $indexes_data = array(); + $pk_array = array(); // will be use to emphasis prim. keys in the table + // view + while ($row = PMA_DBI_fetch_assoc($result)) { + // Backups the list of primary keys + if ($row['Key_name'] == 'PRIMARY') { + $primary .= $row['Column_name'] . ', '; + $pk_array[$row['Column_name']] = 1; + } + // Retains keys informations + if ($row['Key_name'] != $lastIndex) { + $indexes[] = $row['Key_name']; + $lastIndex = $row['Key_name']; + } + $indexes_info[$row['Key_name']]['Sequences'][] = $row['Seq_in_index']; + $indexes_info[$row['Key_name']]['Non_unique'] = $row['Non_unique']; + if (isset($row['Cardinality'])) { + $indexes_info[$row['Key_name']]['Cardinality'] = $row['Cardinality']; + } + // I don't know what does following column mean.... + // $indexes_info[$row['Key_name']]['Packed'] = $row['Packed']; + $indexes_info[$row['Key_name']]['Comment'] = $row['Comment']; + + $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Column_name'] = $row['Column_name']; + if (isset($row['Sub_part'])) { + $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Sub_part'] = $row['Sub_part']; + } + } // end while + if ($result) { + PMA_DBI_free_result($result); } - } // end while - if ($result) { - PMA_DBI_free_result($result); - } - /** - * Gets fields properties - */ - $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE); - $fields_cnt = PMA_DBI_num_rows($result); - // Check if we can use Relations (Mike Beck) - if (!empty($cfgRelation['relation'])) { - // Find which tables are related with the current one and write it in - // an array - $res_rel = PMA_getForeigners($db, $table); + /** + * Gets fields properties + */ + $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE); + $fields_cnt = PMA_DBI_num_rows($result); + // Check if we can use Relations (Mike Beck) + if (!empty($cfgRelation['relation'])) { + // Find which tables are related with the current one and write it in + // an array + $res_rel = PMA_getForeigners($db, $table); - if (count($res_rel) > 0) { - $have_rel = true; + if (count($res_rel) > 0) { + $have_rel = true; + } else { + $have_rel = false; + } } else { $have_rel = false; + } // end if + /** + * Displays the comments of the table if MySQL >= 3.23 + */ + + $break = false; + if (!empty($show_comment)) { + $pdf->Cell(0, 3, __('Table comments') . ' : ' . $show_comment, 0, 1); + $break = true; } - } else { - $have_rel = false; - } // end if - /** - * Displays the comments of the table if MySQL >= 3.23 - */ - $break = false; - if (!empty($show_comment)) { - $pdf->Cell(0, 3, __('Table comments') . ' : ' . $show_comment, 0, 1); - $break = true; - } - - if (!empty($create_time)) { - $pdf->Cell(0, 3, __('Creation') . ': ' . $create_time, 0, 1); - $break = true; - } - - if (!empty($update_time)) { - $pdf->Cell(0, 3, __('Last update') . ': ' . $update_time, 0, 1); - $break = true; - } - - if (!empty($check_time)) { - $pdf->Cell(0, 3, __('Last check') . ': ' . $check_time, 0, 1); - $break = true; - } - - if ($break == true) { - $pdf->Cell(0, 3, '', 0, 1); - $pdf->Ln(); - } - - $pdf->SetFont('', 'B'); - if (isset($orientation) && $orientation == 'L') { - $pdf->Cell(25, 8, ucfirst(__('Column')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); - $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Default')), 1, 0, 'C'); - $pdf->Cell(25, 8, ucfirst(__('Extra')), 1, 0, 'C'); - $pdf->Cell(45, 8, ucfirst(__('Links to')), 1, 0, 'C'); - - if ($paper == 'A4') { - $comments_width = 67; - } else { - // this is really intended for 'letter' - /** - * @todo find optimal width for all formats - */ - $comments_width = 50; + if (!empty($create_time)) { + $pdf->Cell(0, 3, __('Creation') . ': ' . $create_time, 0, 1); + $break = true; } - $pdf->Cell($comments_width, 8, ucfirst(__('Comments')), 1, 0, 'C'); - $pdf->Cell(45, 8, 'MIME', 1, 1, 'C'); - $pdf->SetWidths(array(25, 20, 20, 10, 20, 25, 45, $comments_width, 45)); - } else { - $pdf->Cell(20, 8, ucfirst(__('Column')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); - $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); - $pdf->Cell(15, 8, ucfirst(__('Default')), 1, 0, 'C'); - $pdf->Cell(15, 8, ucfirst(__('Extra')), 1, 0, 'C'); - $pdf->Cell(30, 8, ucfirst(__('Links to')), 1, 0, 'C'); - $pdf->Cell(30, 8, ucfirst(__('Comments')), 1, 0, 'C'); - $pdf->Cell(30, 8, 'MIME', 1, 1, 'C'); - $pdf->SetWidths(array(20, 20, 20, 10, 15, 15, 30, 30, 30)); - } - $pdf->SetFont('', ''); - while ($row = PMA_DBI_fetch_assoc($result)) { - $type = $row['Type']; - // reformat mysql query output - // set or enum types: slashes single quotes inside options - if (preg_match('@^(set|enum)\((.+)\)$@i', $type, $tmp)) { - $tmp[2] = substr(preg_replace("@([^,])''@", "\\1\\'", ',' . $tmp[2]), 1); - $type = $tmp[1] . '(' . str_replace(',', ', ', $tmp[2]) . ')'; - $type_nowrap = ''; + if (!empty($update_time)) { + $pdf->Cell(0, 3, __('Last update') . ': ' . $update_time, 0, 1); + $break = true; + } - $binary = 0; - $unsigned = 0; - $zerofill = 0; - } else { - $type_nowrap = ' nowrap="nowrap"'; - $type = preg_replace('@BINARY@i', '', $type); - $type = preg_replace('@ZEROFILL@i', '', $type); - $type = preg_replace('@UNSIGNED@i', '', $type); - if (empty($type)) { - $type = '&nbsp;'; + if (!empty($check_time)) { + $pdf->Cell(0, 3, __('Last check') . ': ' . $check_time, 0, 1); + $break = true; + } + + if ($break == true) { + $pdf->Cell(0, 3, '', 0, 1); + $pdf->Ln(); + } + + $pdf->SetFont('', 'B'); + if (isset($orientation) && $orientation == 'L') { + $pdf->Cell(25, 8, ucfirst(__('Column')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); + $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Default')), 1, 0, 'C'); + $pdf->Cell(25, 8, ucfirst(__('Extra')), 1, 0, 'C'); + $pdf->Cell(45, 8, ucfirst(__('Links to')), 1, 0, 'C'); + + if ($paper == 'A4') { + $comments_width = 67; + } else { + // this is really intended for 'letter' + /** + * @todo find optimal width for all formats + */ + $comments_width = 50; } - - $binary = stristr($row['Type'], 'BINARY'); - $unsigned = stristr($row['Type'], 'UNSIGNED'); - $zerofill = stristr($row['Type'], 'ZEROFILL'); - } - $attribute = ' '; - if ($binary) { - $attribute = 'BINARY'; - } - if ($unsigned) { - $attribute = 'UNSIGNED'; - } - if ($zerofill) { - $attribute = 'UNSIGNED ZEROFILL'; - } - if (!isset($row['Default'])) { - if ($row['Null'] != '' && $row['Null'] != 'NO') { - $row['Default'] = 'NULL'; - } - } - $field_name = $row['Field']; - // $pdf->Ln(); - $pdf->PMA_links['RT'][$table][$field_name] = $pdf->AddLink(); - $pdf->Bookmark($field_name, 1, -1); - $pdf->SetLink($pdf->PMA_links['doc'][$table][$field_name], -1); - $pdf_row = array($field_name, - $type, - $attribute, - ($row['Null'] == '' || $row['Null'] == 'NO') ? __('No') : __('Yes'), - ((isset($row['Default'])) ? $row['Default'] : ''), - $row['Extra'], - ((isset($res_rel[$field_name])) ? $res_rel[$field_name]['foreign_table'] . ' -> ' . $res_rel[$field_name]['foreign_field'] : ''), - ((isset($comments[$field_name])) ? $comments[$field_name] : ''), - ((isset($mime_map) && isset($mime_map[$field_name])) ? str_replace('_', '/', $mime_map[$field_name]['mimetype']) : '') - ); - $links[0] = $pdf->PMA_links['RT'][$table][$field_name]; - if (isset($res_rel[$field_name]['foreign_table']) AND - isset($res_rel[$field_name]['foreign_field']) AND - isset($pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]) - ) - { - $links[6] = $pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]; + $pdf->Cell($comments_width, 8, ucfirst(__('Comments')), 1, 0, 'C'); + $pdf->Cell(45, 8, 'MIME', 1, 1, 'C'); + $pdf->SetWidths(array(25, 20, 20, 10, 20, 25, 45, $comments_width, 45)); } else { - unset($links[6]); + $pdf->Cell(20, 8, ucfirst(__('Column')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); + $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); + $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); + $pdf->Cell(15, 8, ucfirst(__('Default')), 1, 0, 'C'); + $pdf->Cell(15, 8, ucfirst(__('Extra')), 1, 0, 'C'); + $pdf->Cell(30, 8, ucfirst(__('Links to')), 1, 0, 'C'); + $pdf->Cell(30, 8, ucfirst(__('Comments')), 1, 0, 'C'); + $pdf->Cell(30, 8, 'MIME', 1, 1, 'C'); + $pdf->SetWidths(array(20, 20, 20, 10, 15, 15, 30, 30, 30)); } - $pdf->Row($pdf_row, $links); - } // end while - $pdf->SetFont('', '', 14); - PMA_DBI_free_result($result); - } //end each - } + $pdf->SetFont('', ''); + + while ($row = PMA_DBI_fetch_assoc($result)) { + $type = $row['Type']; + // reformat mysql query output + // set or enum types: slashes single quotes inside options + if (preg_match('@^(set|enum)\((.+)\)$@i', $type, $tmp)) { + $tmp[2] = substr(preg_replace("@([^,])''@", "\\1\\'", ',' . $tmp[2]), 1); + $type = $tmp[1] . '(' . str_replace(',', ', ', $tmp[2]) . ')'; + $type_nowrap = ''; + + $binary = 0; + $unsigned = 0; + $zerofill = 0; + } else { + $type_nowrap = ' nowrap="nowrap"'; + $type = preg_replace('@BINARY@i', '', $type); + $type = preg_replace('@ZEROFILL@i', '', $type); + $type = preg_replace('@UNSIGNED@i', '', $type); + if (empty($type)) { + $type = '&nbsp;'; + } + + $binary = stristr($row['Type'], 'BINARY'); + $unsigned = stristr($row['Type'], 'UNSIGNED'); + $zerofill = stristr($row['Type'], 'ZEROFILL'); + } + $attribute = ' '; + if ($binary) { + $attribute = 'BINARY'; + } + if ($unsigned) { + $attribute = 'UNSIGNED'; + } + if ($zerofill) { + $attribute = 'UNSIGNED ZEROFILL'; + } + if (!isset($row['Default'])) { + if ($row['Null'] != '' && $row['Null'] != 'NO') { + $row['Default'] = 'NULL'; + } + } + $field_name = $row['Field']; + // $pdf->Ln(); + $pdf->PMA_links['RT'][$table][$field_name] = $pdf->AddLink(); + $pdf->Bookmark($field_name, 1, -1); + $pdf->SetLink($pdf->PMA_links['doc'][$table][$field_name], -1); + $pdf_row = array($field_name, + $type, + $attribute, + ($row['Null'] == '' || $row['Null'] == 'NO') ? __('No') : __('Yes'), + ((isset($row['Default'])) ? $row['Default'] : ''), + $row['Extra'], + ((isset($res_rel[$field_name])) ? $res_rel[$field_name]['foreign_table'] . ' -> ' . $res_rel[$field_name]['foreign_field'] : ''), + ((isset($comments[$field_name])) ? $comments[$field_name] : ''), + ((isset($mime_map) && isset($mime_map[$field_name])) ? str_replace('_', '/', $mime_map[$field_name]['mimetype']) : '') + ); + $links[0] = $pdf->PMA_links['RT'][$table][$field_name]; + if (isset($res_rel[$field_name]['foreign_table']) AND + isset($res_rel[$field_name]['foreign_field']) AND + isset($pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]) + ) + { + $links[6] = $pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]; + } else { + unset($links[6]); + } + $pdf->Row($pdf_row, $links); + } // end while + $pdf->SetFont('', '', 14); + PMA_DBI_free_result($result); + } //end each + } } ?> \ No newline at end of file From 459eeb12300e8c10f750bab57320634406db2c03 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Fri, 9 Jul 2010 15:31:18 +0500 Subject: [PATCH 19/48] TCPDF Class: tabs removed --- libraries/tcpdf/tcpdf.php | 13404 ++++++++++++++++++------------------ 1 file changed, 6701 insertions(+), 6703 deletions(-) diff --git a/libraries/tcpdf/tcpdf.php b/libraries/tcpdf/tcpdf.php index 8656ebc61..0c1fbdd0f 100644 --- a/libraries/tcpdf/tcpdf.php +++ b/libraries/tcpdf/tcpdf.php @@ -95,6772 +95,6770 @@ //require_once(dirname(__FILE__).'/config/tcpdf_config.php'); if(!class_exists('TCPDF', false)) { - /** - * define default PDF document producer - */ - define('PDF_PRODUCER','TCPDF 2.2.002 (http://tcpdf.sf.net)'); - - /** - * This is a PHP5 class for generating PDF files on-the-fly without requiring external extensions.<br> - * TCPDF project (http://tcpdf.sourceforge.net) has been originally derived from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org).<br> - * To add your own TTF fonts please read /fonts/README.TXT - * @name TCPDF - * @package com.tecnick.tcpdf - * @version 2.2.002 - * @author Nicola Asuni - * @link http://tcpdf.sourceforge.net - * @license http://www.gnu.org/copyleft/lesser.html LGPL - */ - class TCPDF { - - // Private or Protected properties + /** + * define default PDF document producer + */ + define('PDF_PRODUCER','TCPDF 2.2.002 (http://tcpdf.sf.net)'); + + /** + * This is a PHP5 class for generating PDF files on-the-fly without requiring external extensions.<br> + * TCPDF project (http://tcpdf.sourceforge.net) has been originally derived from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org).<br> + * To add your own TTF fonts please read /fonts/README.TXT + * @name TCPDF + * @package com.tecnick.tcpdf + * @version 2.2.002 + * @author Nicola Asuni + * @link http://tcpdf.sourceforge.net + * @license http://www.gnu.org/copyleft/lesser.html LGPL + */ + class TCPDF { + + // Private or Protected properties - /** - * @var current page number - * @access protected - */ - protected $page; - - /** - * @var current object number - * @access protected - */ - protected $n; + /** + * @var current page number + * @access protected + */ + protected $page; + + /** + * @var current object number + * @access protected + */ + protected $n; - /** - * @var array of object offsets - * @access protected - */ - protected $offsets; + /** + * @var array of object offsets + * @access protected + */ + protected $offsets; - /** - * @var buffer holding in-memory PDF - * @access protected - */ - protected $buffer; + /** + * @var buffer holding in-memory PDF + * @access protected + */ + protected $buffer; - /** - * @var array containing pages - * @access protected - */ - protected $pages; + /** + * @var array containing pages + * @access protected + */ + protected $pages; - /** - * @var current document state - * @access protected - */ - protected $state; + /** + * @var current document state + * @access protected + */ + protected $state; - /** - * @var compression flag - * @access protected - */ - protected $compress; + /** + * @var compression flag + * @access protected + */ + protected $compress; - /** - * @var default page orientation (P = Portrait, L = Landscape) - * @access protected - */ - protected $DefOrientation; + /** + * @var default page orientation (P = Portrait, L = Landscape) + * @access protected + */ + protected $DefOrientation; - /** - * @var current page orientation (P = Portrait, L = Landscape) - * @access protected - */ - protected $CurOrientation; + /** + * @var current page orientation (P = Portrait, L = Landscape) + * @access protected + */ + protected $CurOrientation; - /** - * @var array indicating page orientation changes - * @access protected - */ - protected $OrientationChanges; + /** + * @var array indicating page orientation changes + * @access protected + */ + protected $OrientationChanges; - /** - * @var scale factor (number of points in user unit) - * @access protected - */ - protected $k; + /** + * @var scale factor (number of points in user unit) + * @access protected + */ + protected $k; - /** - * @var width of page format in points - * @access protected - */ - protected $fwPt; + /** + * @var width of page format in points + * @access protected + */ + protected $fwPt; - /** - * @var height of page format in points - * @access protected - */ - protected $fhPt; + /** + * @var height of page format in points + * @access protected + */ + protected $fhPt; - /** - * @var width of page format in user unit - * @access protected - */ - protected $fw; + /** + * @var width of page format in user unit + * @access protected + */ + protected $fw; - /** - * @var height of page format in user unit - * @access protected - */ - protected $fh; + /** + * @var height of page format in user unit + * @access protected + */ + protected $fh; - /** - * @var current width of page in points - * @access protected - */ - protected $wPt; + /** + * @var current width of page in points + * @access protected + */ + protected $wPt; - /** - * @var current height of page in points - * @access protected - */ - protected $hPt; + /** + * @var current height of page in points + * @access protected + */ + protected $hPt; - /** - * @var current width of page in user unit - * @access protected - */ - protected $w; + /** + * @var current width of page in user unit + * @access protected + */ + protected $w; - /** - * @var current height of page in user unit - * @access protected - */ - protected $h; + /** + * @var current height of page in user unit + * @access protected + */ + protected $h; - /** - * @var left margin - * @access protected - */ - protected $lMargin; + /** + * @var left margin + * @access protected + */ + protected $lMargin; - /** - * @var top margin - * @access protected - */ - protected $tMargin; + /** + * @var top margin + * @access protected + */ + protected $tMargin; - /** - * @var right margin - * @access protected - */ - protected $rMargin; + /** + * @var right margin + * @access protected + */ + protected $rMargin; - /** - * @var page break margin - * @access protected - */ - protected $bMargin; + /** + * @var page break margin + * @access protected + */ + protected $bMargin; - /** - * @var cell internal padding - * @access protected - */ - protected $cMargin; + /** + * @var cell internal padding + * @access protected + */ + protected $cMargin; - /** - * @var current horizontal position in user unit for cell positioning - * @access protected - */ - protected $x; + /** + * @var current horizontal position in user unit for cell positioning + * @access protected + */ + protected $x; - /** - * @var current vertical position in user unit for cell positioning - * @access protected - */ - protected $y; + /** + * @var current vertical position in user unit for cell positioning + * @access protected + */ + protected $y; - /** - * @var height of last cell printed - * @access protected - */ - protected $lasth; + /** + * @var height of last cell printed + * @access protected + */ + protected $lasth; - /** - * @var line width in user unit - * @access protected - */ - protected $LineWidth; + /** + * @var line width in user unit + * @access protected + */ + protected $LineWidth; - /** - * @var array of standard font names - * @access protected - */ - protected $CoreFonts; + /** + * @var array of standard font names + * @access protected + */ + protected $CoreFonts; - /** - * @var array of used fonts - * @access protected - */ - protected $fonts; + /** + * @var array of used fonts + * @access protected + */ + protected $fonts; - /** - * @var array of font files - * @access protected - */ - protected $FontFiles; + /** + * @var array of font files + * @access protected + */ + protected $FontFiles; - /** - * @var array of encoding differences - * @access protected - */ - protected $diffs; + /** + * @var array of encoding differences + * @access protected + */ + protected $diffs; - /** - * @var array of used images - * @access protected - */ - protected $images; + /** + * @var array of used images + * @access protected + */ + protected $images; - /** - * @var array of links in pages - * @access protected - */ - protected $PageLinks; + /** + * @var array of links in pages + * @access protected + */ + protected $PageLinks; - /** - * @var array of internal links - * @access protected - */ - protected $links; + /** + * @var array of internal links + * @access protected + */ + protected $links; - /** - * @var current font family - * @access protected - */ - protected $FontFamily; + /** + * @var current font family + * @access protected + */ + protected $FontFamily; - /** - * @var current font style - * @access protected - */ - protected $FontStyle; + /** + * @var current font style + * @access protected + */ + protected $FontStyle; - /** - * @var underlining flag - * @access protected - */ - protected $underline; + /** + * @var underlining flag + * @access protected + */ + protected $underline; - /** - * @var current font info - * @access protected - */ - protected $CurrentFont; + /** + * @var current font info + * @access protected + */ + protected $CurrentFont; - /** - * @var current font size in points - * @access protected - */ - protected $FontSizePt; + /** + * @var current font size in points + * @access protected + */ + protected $FontSizePt; - /** - * @var current font size in user unit - * @access protected - */ - protected $FontSize; + /** + * @var current font size in user unit + * @access protected + */ + protected $FontSize; - /** - * @var commands for drawing color - * @access protected - */ - protected $DrawColor; + /** + * @var commands for drawing color + * @access protected + */ + protected $DrawColor; - /** - * @var commands for filling color - * @access protected - */ - protected $FillColor; + /** + * @var commands for filling color + * @access protected + */ + protected $FillColor; - /** - * @var commands for text color - * @access protected - */ - protected $TextColor; + /** + * @var commands for text color + * @access protected + */ + protected $TextColor; - /** - * @var indicates whether fill and text colors are different - * @access protected - */ - protected $ColorFlag; + /** + * @var indicates whether fill and text colors are different + * @access protected + */ + protected $ColorFlag; - /** - * @var word spacing - * @access protected - */ - protected $ws; + /** + * @var word spacing + * @access protected + */ + protected $ws; - /** - * @var automatic page breaking - * @access protected - */ - protected $AutoPageBreak; + /** + * @var automatic page breaking + * @access protected + */ + protected $AutoPageBreak; - /** - * @var threshold used to trigger page breaks - * @access protected - */ - protected $PageBreakTrigger; + /** + * @var threshold used to trigger page breaks + * @access protected + */ + protected $PageBreakTrigger; - /** - * @var flag set when processing footer - * @access protected - */ - protected $InFooter; + /** + * @var flag set when processing footer + * @access protected + */ + protected $InFooter; - /** - * @var zoom display mode - * @access protected - */ - protected $ZoomMode; + /** + * @var zoom display mode + * @access protected + */ + protected $ZoomMode; - /** - * @var layout display mode - * @access protected - */ - protected $LayoutMode; + /** + * @var layout display mode + * @access protected + */ + protected $LayoutMode; - /** - * @var title - * @access protected - */ - protected $title; + /** + * @var title + * @access protected + */ + protected $title; - /** - * @var subject - * @access protected - */ - protected $subject; + /** + * @var subject + * @access protected + */ + protected $subject; - /** - * @var author - * @access protected - */ - protected $author; + /** + * @var author + * @access protected + */ + protected $author; - /** - * @var keywords - * @access protected - */ - protected $keywords; + /** + * @var keywords + * @access protected + */ + protected $keywords; - /** - * @var creator - * @access protected - */ - protected $creator; + /** + * @var creator + * @access protected + */ + protected $creator; - /** - * @var alias for total number of pages - * @access protected - */ - protected $AliasNbPages; + /** + * @var alias for total number of pages + * @access protected + */ + protected $AliasNbPages; - /** - * @var right-bottom corner X coordinate of inserted image - * @since 2002-07-31 - * @author Nicola Asuni - * @access protected - */ - protected $img_rb_x; + /** + * @var right-bottom corner X coordinate of inserted image + * @since 2002-07-31 + * @author Nicola Asuni + * @access protected + */ + protected $img_rb_x; - /** - * @var right-bottom corner Y coordinate of inserted image - * @since 2002-07-31 - * @author Nicola Asuni - * @access protected - */ - protected $img_rb_y; + /** + * @var right-bottom corner Y coordinate of inserted image + * @since 2002-07-31 + * @author Nicola Asuni + * @access protected + */ + protected $img_rb_y; - /** - * @var image scale factor - * @since 2004-06-14 - * @author Nicola Asuni - * @access protected - */ - protected $imgscale = 1; + /** + * @var image scale factor + * @since 2004-06-14 + * @author Nicola Asuni + * @access protected + */ + protected $imgscale = 1; - /** - * @var boolean set to true when the input text is unicode (require unicode fonts) - * @since 2005-01-02 - * @author Nicola Asuni - * @access protected - */ - protected $isunicode = false; + /** + * @var boolean set to true when the input text is unicode (require unicode fonts) + * @since 2005-01-02 + * @author Nicola Asuni + * @access protected + */ + protected $isunicode = false; - /** - * @var PDF version - * @since 1.5.3 - * @access protected - */ - protected $PDFVersion = "1.5"; - - - // ---------------------- - - /** - * @var Minimum distance between header and top page margin. - * @access private - */ - private $header_margin; - - /** - * @var Minimum distance between footer and bottom page margin. - * @access private - */ - private $footer_margin; - - /** - * @var original left margin value - * @access private - * @since 1.53.0.TC013 - */ - private $original_lMargin; - - /** - * @var original right margin value - * @access private - * @since 1.53.0.TC013 - */ - private $original_rMargin; - - /** - * @var Header font. - * @access private - */ - private $header_font; - - /** - * @var Footer font. - * @access private - */ - private $footer_font; - - /** - * @var Language templates. - * @access private - */ - private $l; - - /** - * @var Barcode to print on page footer (only if set). - * @access private - */ - private $barcode = false; - - /** - * @var If true prints header - * @access private - */ - private $print_header = true; - - /** - * @var If true prints footer. - * @access private - */ - private $print_footer = true; - - /** - * @var Header width (0 = full page width). - * @access private - */ - private $header_width = 0; - - /** - * @var Header image logo. - * @access private - */ - private $header_logo = ""; - - /** - * @var Header image logo width in mm. - * @access private - */ - private $header_logo_width = 30; - - /** - * @var String to print as title on document header. - * @access private - */ - private $header_title = ""; - - /** - * @var String to print on document header. - * @access private - */ - private $header_string = ""; - - /** - * @var Default number of columns for html table. - * @access private - */ - private $default_table_columns = 4; - - - // variables for html parser - - /** - * @var HTML PARSER: store current link. - * @access private - */ - private $HREF; - - /** - * @var HTML PARSER: store font list. - * @access private - */ - private $fontList; - - /** - * @var HTML PARSER: true when font attribute is set. - * @access private - */ - private $issetfont; - - /** - * @var HTML PARSER: true when color attribute is set. - * @access private - */ - private $issetcolor; - - /** - * @var HTML PARSER: true in case of ordered list (OL), false otherwise. - * @access private - */ - private $listordered = false; - - /** - * @var HTML PARSER: count list items. - * @access private - */ - private $listcount = 0; - - /** - * @var HTML PARSER: size of table border. - * @access private - */ - private $tableborder = 0; - - /** - * @var HTML PARSER: true at the beginning of table. - * @access private - */ - private $tdbegin = false; - - /** - * @var HTML PARSER: table width. - * @access private - */ - private $tdwidth = 0; - - /** - * @var HTML PARSER: table height. - * @access private - */ - private $tdheight = 0; - - /** - * @var HTML PARSER: table align. - * @access private - */ - private $tdalign = "L"; - - /** - * @var HTML PARSER: table background color. - * @access private - */ - private $tdbgcolor = false; - - /** - * @var Store temporary font size in points. - * @access private - */ - private $tempfontsize = 10; - - /** - * @var Bold font style status. - * @access private - */ - private $b; - - /** - * @var Underlined font style status. - * @access private - */ - private $u; - - /** - * @var Italic font style status. - * @access private - */ - private $i; - - /** - * @var spacer for LI tags. - * @access private - */ - private $lispacer = ""; - - /** - * @var default encoding - * @access private - * @since 1.53.0.TC010 - */ - private $encoding = "UTF-8"; - - /** - * @var PHP internal encoding - * @access private - * @since 1.53.0.TC016 - */ - private $internal_encoding; - - /** - * @var store previous fill color as RGB array - * @access private - * @since 1.53.0.TC017 - */ - private $prevFillColor = array(255,255,255); - - /** - * @var store previous text color as RGB array - * @access private - * @since 1.53.0.TC017 - */ - private $prevTextColor = array(0,0,0); - - /** - * @var store previous font family - * @access private - * @since 1.53.0.TC017 - */ - private $prevFontFamily; - - /** - * @var store previous font style - * @access private - * @since 1.53.0.TC017 - */ - private $prevFontStyle; - - /** - * @var indicates if the document language is Right-To-Left - * @access private - * @since 2.0.000 - */ - private $rtl = false; - - /** - * @var used to force RTL or LTR string inversion - * @access private - * @since 2.0.000 - */ - private $tmprtl = false; - - // --- Variables used for document encryption: - - /** - * Indicates whether document is protected - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $encrypted; - - /** - * U entry in pdf document - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $Uvalue; - - /** - * O entry in pdf document - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $Ovalue; - - /** - * P entry in pdf document - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $Pvalue; - - /** - * encryption object id - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $enc_obj_id; - - /** - * last RC4 key encrypted (cached for optimisation) - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $last_rc4_key; - - /** - * last RC4 computed key - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $last_rc4_key_c; - - // --- bookmark --- - - /** - * Outlines for bookmark - * @access private - * @since 2.1.002 (2008-02-12) - */ - private $outlines = array(); - - /** - * Outline root for bookmark - * @access private - * @since 2.1.002 (2008-02-12) - */ - private $OutlineRoot; - - - // --- javascript and form --- - - /** - * javascript code - * @access private - * @since 2.1.002 (2008-02-12) - */ - private $javascript = ""; - - /** - * javascript counter - * @access private - * @since 2.1.002 (2008-02-12) - */ + /** + * @var PDF version + * @since 1.5.3 + * @access protected + */ + protected $PDFVersion = "1.5"; + + + // ---------------------- + + /** + * @var Minimum distance between header and top page margin. + * @access private + */ + private $header_margin; + + /** + * @var Minimum distance between footer and bottom page margin. + * @access private + */ + private $footer_margin; + + /** + * @var original left margin value + * @access private + * @since 1.53.0.TC013 + */ + private $original_lMargin; + + /** + * @var original right margin value + * @access private + * @since 1.53.0.TC013 + */ + private $original_rMargin; + + /** + * @var Header font. + * @access private + */ + private $header_font; + + /** + * @var Footer font. + * @access private + */ + private $footer_font; + + /** + * @var Language templates. + * @access private + */ + private $l; + + /** + * @var Barcode to print on page footer (only if set). + * @access private + */ + private $barcode = false; + + /** + * @var If true prints header + * @access private + */ + private $print_header = true; + + /** + * @var If true prints footer. + * @access private + */ + private $print_footer = true; + + /** + * @var Header width (0 = full page width). + * @access private + */ + private $header_width = 0; + + /** + * @var Header image logo. + * @access private + */ + private $header_logo = ""; + + /** + * @var Header image logo width in mm. + * @access private + */ + private $header_logo_width = 30; + + /** + * @var String to print as title on document header. + * @access private + */ + private $header_title = ""; + + /** + * @var String to print on document header. + * @access private + */ + private $header_string = ""; + + /** + * @var Default number of columns for html table. + * @access private + */ + private $default_table_columns = 4; + + + // variables for html parser + + /** + * @var HTML PARSER: store current link. + * @access private + */ + private $HREF; + + /** + * @var HTML PARSER: store font list. + * @access private + */ + private $fontList; + + /** + * @var HTML PARSER: true when font attribute is set. + * @access private + */ + private $issetfont; + + /** + * @var HTML PARSER: true when color attribute is set. + * @access private + */ + private $issetcolor; + + /** + * @var HTML PARSER: true in case of ordered list (OL), false otherwise. + * @access private + */ + private $listordered = false; + + /** + * @var HTML PARSER: count list items. + * @access private + */ + private $listcount = 0; + + /** + * @var HTML PARSER: size of table border. + * @access private + */ + private $tableborder = 0; + + /** + * @var HTML PARSER: true at the beginning of table. + * @access private + */ + private $tdbegin = false; + + /** + * @var HTML PARSER: table width. + * @access private + */ + private $tdwidth = 0; + + /** + * @var HTML PARSER: table height. + * @access private + */ + private $tdheight = 0; + + /** + * @var HTML PARSER: table align. + * @access private + */ + private $tdalign = "L"; + + /** + * @var HTML PARSER: table background color. + * @access private + */ + private $tdbgcolor = false; + + /** + * @var Store temporary font size in points. + * @access private + */ + private $tempfontsize = 10; + + /** + * @var Bold font style status. + * @access private + */ + private $b; + + /** + * @var Underlined font style status. + * @access private + */ + private $u; + + /** + * @var Italic font style status. + * @access private + */ + private $i; + + /** + * @var spacer for LI tags. + * @access private + */ + private $lispacer = ""; + + /** + * @var default encoding + * @access private + * @since 1.53.0.TC010 + */ + private $encoding = "UTF-8"; + + /** + * @var PHP internal encoding + * @access private + * @since 1.53.0.TC016 + */ + private $internal_encoding; + + /** + * @var store previous fill color as RGB array + * @access private + * @since 1.53.0.TC017 + */ + private $prevFillColor = array(255,255,255); + + /** + * @var store previous text color as RGB array + * @access private + * @since 1.53.0.TC017 + */ + private $prevTextColor = array(0,0,0); + + /** + * @var store previous font family + * @access private + * @since 1.53.0.TC017 + */ + private $prevFontFamily; + + /** + * @var store previous font style + * @access private + * @since 1.53.0.TC017 + */ + private $prevFontStyle; + + /** + * @var indicates if the document language is Right-To-Left + * @access private + * @since 2.0.000 + */ + private $rtl = false; + + /** + * @var used to force RTL or LTR string inversion + * @access private + * @since 2.0.000 + */ + private $tmprtl = false; + + // --- Variables used for document encryption: + + /** + * Indicates whether document is protected + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $encrypted; + + /** + * U entry in pdf document + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $Uvalue; + + /** + * O entry in pdf document + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $Ovalue; + + /** + * P entry in pdf document + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $Pvalue; + + /** + * encryption object id + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $enc_obj_id; + + /** + * last RC4 key encrypted (cached for optimisation) + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $last_rc4_key; + + /** + * last RC4 computed key + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $last_rc4_key_c; + + // --- bookmark --- + + /** + * Outlines for bookmark + * @access private + * @since 2.1.002 (2008-02-12) + */ + private $outlines = array(); + + /** + * Outline root for bookmark + * @access private + * @since 2.1.002 (2008-02-12) + */ + private $OutlineRoot; + + + // --- javascript and form --- + + /** + * javascript code + * @access private + * @since 2.1.002 (2008-02-12) + */ + private $javascript = ""; + + /** + * javascript counter + * @access private + * @since 2.1.002 (2008-02-12) + */ private $n_js; - //------------------------------------------------------------ - // Public methods - //------------------------------------------------------------ - - /** - * This is the class constructor. - * It allows to set up the page format, the orientation and - * the measure unit used in all the methods (except for the font sizes). - * @since 1.0 - * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul> - * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. - * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul> - * @param boolean $unicode TRUE means that the input text is unicode (default = true) - * @param String $encoding charset encoding; default is UTF-8 - */ - public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding="UTF-8") { - - /* Set internal character encoding to ASCII */ - if (function_exists("mb_internal_encoding") AND mb_internal_encoding()) { - $this->internal_encoding = mb_internal_encoding(); - mb_internal_encoding("ASCII"); - } - - // set language direction - $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; - $this->tmprtl = false; - - //Some checks - $this->_dochecks(); - - //Initialization of properties - $this->isunicode=$unicode; - $this->page=0; - $this->n=2; - $this->buffer=''; - $this->pages=array(); - $this->OrientationChanges=array(); - $this->state=0; - $this->fonts=array(); - $this->FontFiles=array(); - $this->diffs=array(); - $this->images=array(); - $this->links=array(); - $this->InFooter=false; - $this->lasth=0; - $this->FontFamily=''; - $this->FontStyle=''; - $this->FontSizePt=12; - $this->underline=false; - $this->DrawColor='0 G'; - $this->FillColor='0 g'; - $this->TextColor='0 g'; - $this->ColorFlag=false; - $this->ws=0; - // encryption values - $this->encrypted=false; - $this->last_rc4_key=''; - $this->padding="\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A"; - - //Standard Unicode fonts - $this->CoreFonts=array( - 'courier'=>'Courier', - 'courierB'=>'Courier-Bold', - 'courierI'=>'Courier-Oblique', - 'courierBI'=>'Courier-BoldOblique', - 'helvetica'=>'Helvetica', - 'helveticaB'=>'Helvetica-Bold', - 'helveticaI'=>'Helvetica-Oblique', - 'helveticaBI'=>'Helvetica-BoldOblique', - 'times'=>'Times-Roman', - 'timesB'=>'Times-Bold', - 'timesI'=>'Times-Italic', - 'timesBI'=>'Times-BoldItalic', - 'symbol'=>'Symbol', - 'zapfdingbats'=>'ZapfDingbats' - ); - - //Scale factor - switch (strtolower($unit)){ - case 'pt': {$this->k=1; break;} - case 'mm': {$this->k=72/25.4; break;} - case 'cm': {$this->k=72/2.54; break;} - case 'in': {$this->k=72; break;} - default : {$this->Error('Incorrect unit: '.$unit); break;} - } - - //Page format - if(is_string($format)) { - // Page formats (45 standard ISO paper formats and 4 american common formats). - // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm) - switch (strtoupper($format)){ - case '4A0': {$format = array(4767.87,6740.79); break;} - case '2A0': {$format = array(3370.39,4767.87); break;} - case 'A0': {$format = array(2383.94,3370.39); break;} - case 'A1': {$format = array(1683.78,2383.94); break;} - case 'A2': {$format = array(1190.55,1683.78); break;} - case 'A3': {$format = array(841.89,1190.55); break;} - case 'A4': default: {$format = array(595.28,841.89); break;} - case 'A5': {$format = array(419.53,595.28); break;} - case 'A6': {$format = array(297.64,419.53); break;} - case 'A7': {$format = array(209.76,297.64); break;} - case 'A8': {$format = array(147.40,209.76); break;} - case 'A9': {$format = array(104.88,147.40); break;} - case 'A10': {$format = array(73.70,104.88); break;} - case 'B0': {$format = array(2834.65,4008.19); break;} - case 'B1': {$format = array(2004.09,2834.65); break;} - case 'B2': {$format = array(1417.32,2004.09); break;} - case 'B3': {$format = array(1000.63,1417.32); break;} - case 'B4': {$format = array(708.66,1000.63); break;} - case 'B5': {$format = array(498.90,708.66); break;} - case 'B6': {$format = array(354.33,498.90); break;} - case 'B7': {$format = array(249.45,354.33); break;} - case 'B8': {$format = array(175.75,249.45); break;} - case 'B9': {$format = array(124.72,175.75); break;} - case 'B10': {$format = array(87.87,124.72); break;} - case 'C0': {$format = array(2599.37,3676.54); break;} - case 'C1': {$format = array(1836.85,2599.37); break;} - case 'C2': {$format = array(1298.27,1836.85); break;} - case 'C3': {$format = array(918.43,1298.27); break;} - case 'C4': {$format = array(649.13,918.43); break;} - case 'C5': {$format = array(459.21,649.13); break;} - case 'C6': {$format = array(323.15,459.21); break;} - case 'C7': {$format = array(229.61,323.15); break;} - case 'C8': {$format = array(161.57,229.61); break;} - case 'C9': {$format = array(113.39,161.57); break;} - case 'C10': {$format = array(79.37,113.39); break;} - case 'RA0': {$format = array(2437.80,3458.27); break;} - case 'RA1': {$format = array(1729.13,2437.80); break;} - case 'RA2': {$format = array(1218.90,1729.13); break;} - case 'RA3': {$format = array(864.57,1218.90); break;} - case 'RA4': {$format = array(609.45,864.57); break;} - case 'SRA0': {$format = array(2551.18,3628.35); break;} - case 'SRA1': {$format = array(1814.17,2551.18); break;} - case 'SRA2': {$format = array(1275.59,1814.17); break;} - case 'SRA3': {$format = array(907.09,1275.59); break;} - case 'SRA4': {$format = array(637.80,907.09); break;} - case 'LETTER': {$format = array(612.00,792.00); break;} - case 'LEGAL': {$format = array(612.00,1008.00); break;} - case 'EXECUTIVE': {$format = array(521.86,756.00); break;} - case 'FOLIO': {$format = array(612.00,936.00); break;} - // default: {$this->Error('Unknown page format: '.$format); break;} - } - $this->fwPt=$format[0]; - $this->fhPt=$format[1]; - } - else { - $this->fwPt=$format[0]*$this->k; - $this->fhPt=$format[1]*$this->k; - } - - $this->fw=$this->fwPt/$this->k; - $this->fh=$this->fhPt/$this->k; - - //Page orientation - $orientation=strtolower($orientation); - if($orientation=='p' or $orientation=='portrait') { - $this->DefOrientation='P'; - $this->wPt=$this->fwPt; - $this->hPt=$this->fhPt; - } - elseif($orientation=='l' or $orientation=='landscape') { - $this->DefOrientation='L'; - $this->wPt=$this->fhPt; - $this->hPt=$this->fwPt; - } - else { - $this->Error('Incorrect orientation: '.$orientation); - } - - $this->CurOrientation=$this->DefOrientation; - $this->w=$this->wPt/$this->k; - $this->h=$this->hPt/$this->k; - //Page margins (1 cm) - $margin=28.35/$this->k; - $this->SetMargins($margin,$margin); - //Interior cell margin (1 mm) - $this->cMargin=$margin/10; - //Line width (0.2 mm) - $this->LineWidth=.567/$this->k; - //Automatic page break - $this->SetAutoPageBreak(true,2*$margin); - //Full width display mode - $this->SetDisplayMode('fullwidth'); - //Compression - $this->SetCompression(true); - //Set default PDF version number - $this->PDFVersion = "1.5"; - - $this->encoding = $encoding; - $this->b = 0; - $this->i = 0; - $this->u = 0; - $this->HREF = ''; - $this->fontlist = array("arial", "times", "courier", "helvetica", "symbol"); - $this->issetfont = false; - $this->issetcolor = false; - $this->tableborder = 0; - $this->tdbegin = false; - $this->tdwidth= 0; - $this->tdheight = 0; - if($this->rtl) { - $this->tdalign = "R"; - } else { - $this->tdalign = "L"; - } - $this->tdbgcolor = false; - - $this->SetFillColor(200, 200, 200, true); - $this->SetTextColor(0, 0, 0, true); - } - - /** - * Default destructor. - * @since 1.53.0.TC016 - */ - public function __destruct() { - // restore internal encoding - if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) { - mb_internal_encoding($this->internal_encoding); - } - } - - /** - * Enable or disable Right-To-Left language mode - * @param Boolean $enable if true enable Right-To-Left language mode. - * @since 2.0.000 (2008-01-03) - */ - public function setRTL($enable) { - $this->rtl = $enable ? true : false; - $this->tmprtl = false; - } - - /** - * Force temporary RTL language direction - * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL - * @since 2.1.000 (2008-01-09) - */ - public function setTempRTL($mode) { - switch ($mode) { - case false: - case 'L': - case 'R': { - $this->tmprtl = $mode; - } - } - } - - /** - * Set the last cell height. - * @param float $h cell height. - * @author Nicola Asuni - * @since 1.53.0.TC034 - */ - public function setLastH($h) { - $this->lasth=$h; - } - - /** - * Set the image scale. - * @param float $scale image scale. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function setImageScale($scale) { - $this->imgscale=$scale; - } - - /** - * Returns the image scale. - * @return float image scale. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function getImageScale() { - return $this->imgscale; - } - - /** - * Returns the page width in units. - * @return int page width. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function getPageWidth() { - return $this->w; - } - - /** - * Returns the page height in units. - * @return int page height. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function getPageHeight() { - return $this->h; - } - - /** - * Returns the page break margin. - * @return int page break margin. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function getBreakMargin() { - return $this->bMargin; - } - - /** - * Returns the scale factor (number of points in user unit). - * @return int scale factor. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function getScaleFactor() { - return $this->k; - } - - /** - * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them. - * @param float $left Left margin. - * @param float $top Top margin. - * @param float $right Right margin. Default value is the left one. - * @since 1.0 - * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() - */ - public function SetMargins($left, $top, $right=-1) { - //Set left, top and right margins - $this->lMargin=$left; - $this->tMargin=$top; - if($right==-1) { - $right=$left; - } - $this->rMargin=$right; - } - - /** - * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin. - * @param float $margin The margin. - * @since 1.4 - * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() - */ - public function SetLeftMargin($margin) { - //Set left margin - $this->lMargin=$margin; - if(($this->page > 0) AND ($this->x < $margin)) { - $this->x = $margin; - } - } - - /** - * Defines the top margin. The method can be called before creating the first page. - * @param float $margin The margin. - * @since 1.5 - * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() - */ - public function SetTopMargin($margin) { - //Set top margin - $this->tMargin=$margin; - if(($this->page > 0) AND ($this->y < $margin)) { - $this->y = $margin; - } - } - - /** - * Defines the right margin. The method can be called before creating the first page. - * @param float $margin The margin. - * @since 1.5 - * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() - */ - public function SetRightMargin($margin) { - $this->rMargin=$margin; - if(($this->page > 0) AND ($this->x > ($this->w - $margin))) { - $this->x = $this->w - $margin; - } - } - - /** - * Set the internal Cell padding. - * @param float $pad internal padding. - * @since 2.1.000 (2008-01-09) - * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() - */ - public function SetCellPadding($pad) { - $this->cMargin=$pad; - } - - /** - * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm. - * @param boolean $auto Boolean indicating if mode should be on or off. - * @param float $margin Distance from the bottom of the page. - * @since 1.0 - * @see Cell(), MultiCell(), AcceptPageBreak() - */ - public function SetAutoPageBreak($auto, $margin=0) { - //Set auto page break mode and triggering margin - $this->AutoPageBreak = $auto; - $this->bMargin = $margin; - $this->PageBreakTrigger = $this->h - $margin; - } - - /** - * Defines the way the document is to be displayed by the viewer. The zoom level can be set: pages can be displayed entirely on screen, occupy the full width of the window, use real size, be scaled by a specific zooming factor or use viewer default (configured in the Preferences menu of Acrobat). The page layout can be specified too: single at once, continuous display, two columns or viewer default. By default, documents use the full width mode with continuous display. - * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul> - * @param string $layout The page layout. Possible values are:<ul><li>single: displays one page at once</li><li>continuous: displays pages continuously (default)</li><li>two: displays two pages on two columns</li><li>default: uses viewer default mode</li></ul> - * @since 1.2 - */ - public function SetDisplayMode($zoom, $layout='continuous') { - //Set display mode in viewer - if($zoom=='fullpage' or $zoom=='fullwidth' or $zoom=='real' or $zoom=='default' or !is_string($zoom)) { - $this->ZoomMode=$zoom; - } - else { - $this->Error('Incorrect zoom display mode: '.$zoom); - } - if($layout=='single' or $layout=='continuous' or $layout=='two' or $layout=='default') { - $this->LayoutMode=$layout; - } - else { - $this->Error('Incorrect layout display mode: '.$layout); - } - } - - /** - * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default. - * Note: the Zlib extension is required for this feature. If not present, compression will be turned off. - * @param boolean $compress Boolean indicating if compression must be enabled. - * @since 1.4 - */ - public function SetCompression($compress) { - //Set page compression - if(function_exists('gzcompress')) { - $this->compress=$compress; - } - else { - $this->compress=false; - } - } - - /** - * Defines the title of the document. - * @param string $title The title. - * @since 1.2 - * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() - */ - public function SetTitle($title) { - //Title of document - $this->title=$title; - } - - /** - * Defines the subject of the document. - * @param string $subject The subject. - * @since 1.2 - * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() - */ - public function SetSubject($subject) { - //Subject of document - $this->subject=$subject; - } - - /** - * Defines the author of the document. - * @param string $author The name of the author. - * @since 1.2 - * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() - */ - public function SetAuthor($author) { - //Author of document - $this->author=$author; - } - - /** - * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. - * @param string $keywords The list of keywords. - * @since 1.2 - * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() - */ - public function SetKeywords($keywords) { - //Keywords of document - $this->keywords=$keywords; - } - - /** - * Defines the creator of the document. This is typically the name of the application that generates the PDF. - * @param string $creator The name of the creator. - * @since 1.2 - * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() - */ - public function SetCreator($creator) { - //Creator of document - $this->creator=$creator; - } - - /** - * Defines an alias for the total number of pages. It will be substituted as the document is closed.<br /> - * <b>Example:</b><br /> - * <pre> - * class PDF extends TCPDF { - * public function Footer() { - * //Go to 1.5 cm from bottom - * $this->SetY(-15); - * //Select Arial italic 8 - * $this->SetFont('Arial','I',8); - * //Print current and total page numbers - * $this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C'); - * } - * } - * $pdf=new PDF(); - * $pdf->AliasNbPages(); - * </pre> - * @param string $alias The alias. Default value: {nb}. - * @since 1.4 - * @see PageNo(), Footer() - */ - public function AliasNbPages($alias='{nb}') { - //Define an alias for total number of pages - $this->AliasNbPages = $this->_escapetext($alias); - } - - /** - * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid. - * 2004-06-11 :: Nicola Asuni : changed bold tag with strong - * @param string $msg The error message - * @since 1.0 - */ - public function Error($msg) { - //Fatal error - die('<strong>TCPDF error: </strong>'.$msg); - } - - /** - * This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically. - * Note: no page is created by this method - * @since 1.0 - * @see AddPage(), Close() - */ - public function Open() { - //Begin document - $this->state=1; - } - - /** - * Terminates the PDF document. It is not necessary to call this method explicitly because Output() does it automatically. If the document contains no page, AddPage() is called to prevent from getting an invalid document. - * @since 1.0 - * @see Open(), Output() - */ - public function Close() { - //Terminate document - if($this->state==3) { - return; - } - if($this->page==0) { - $this->AddPage(); - } - //Page footer - $this->InFooter=true; - $this->Footer(); - $this->InFooter=false; - //Close page - $this->_endpage(); - //Close document - $this->_enddoc(); - } - - /** - * Reset pointer to the last document page. - * @since 2.0.000 (2008-01-04) - * @see setPage(), getPage(), getNumPages() - */ - public function lastPage() { - $this->page = count($this->pages); - } - - /** - * Move pointer to the apecified document page. - * @param int $pnum page number - * @since 2.1.000 (2008-01-07) - * @see getPage(), lastpage(), getNumPages() - */ - public function setPage($pnum) { - if(($pnum > 0) AND ($pnum <= count($this->pages))) { - $this->page = $pnum; - } - } - - /** - * Get current document page number. - * @return int page number - * @since 2.1.000 (2008-01-07) - * @see setPage(), lastpage(), getNumPages() - */ - public function getPage() { - return $this->page; - } - - - /** - * Get the total number of insered pages. - * @return int number of pages - * @since 2.1.000 (2008-01-07) - * @see setPage(), getPage(), lastpage() - */ - public function getNumPages() { - return count($this->pages); - } - - /** - * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer. Then the page is added, the current position set to the top-left corner according to the left and top margins, and Header() is called to display the header. - * The font which was set before calling is automatically restored. There is no need to call SetFont() again if you want to continue with the same font. The same is true for colors and line width. - * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. - * @param string $orientation Page orientation. Possible values are (case insensitive):<ul><li>P or Portrait</li><li>L or Landscape</li></ul> The default value is the one passed to the constructor. - * @since 1.0 - * @see TCPDF(), Header(), Footer(), SetMargins() - */ - public function AddPage($orientation='') { - if (count($this->pages) > ($this->page + 1)) { - // this page has been already added - $this->page++; - return; - } - //Start a new page - if($this->state==0) { - $this->Open(); - } - $family=$this->FontFamily; - $style=$this->FontStyle.($this->underline ? 'U' : ''); - $size=$this->FontSizePt; - $lw=$this->LineWidth; - $dc=$this->DrawColor; - $fc=$this->FillColor; - $tc=$this->TextColor; - $cf=$this->ColorFlag; - if($this->page>0) { - //Page footer - $this->InFooter=true; - $this->Footer(); - $this->InFooter=false; - //Close page - $this->_endpage(); - } - //Start new page - $this->_beginpage($orientation); - //Set line cap style to square - $this->_out('2 J'); - //Set line width - $this->LineWidth=$lw; - $this->_out(sprintf('%.2f w',$lw*$this->k)); - //Set font - if($family) { - $this->SetFont($family,$style,$size); - } - //Set colors - $this->DrawColor=$dc; - if($dc!='0 G') { - $this->_out($dc); - } - $this->FillColor=$fc; - if($fc!='0 g') { - $this->_out($fc); - } - $this->TextColor=$tc; - $this->ColorFlag=$cf; - //Page header - $this->Header(); - //Restore line width - if($this->LineWidth!=$lw) { - $this->LineWidth=$lw; - $this->_out(sprintf('%.2f w',$lw*$this->k)); - } - //Restore font - if($family) { - $this->SetFont($family,$style,$size); - } - //Restore colors - if($this->DrawColor!=$dc) { - $this->DrawColor=$dc; - $this->_out($dc); - } - if($this->FillColor!=$fc) { - $this->FillColor=$fc; - $this->_out($fc); - } - $this->TextColor=$tc; - $this->ColorFlag=$cf; - } - - /** - * Set header data. - * @param string $ln header image logo - * @param string $lw header image logo width in mm - * @param string $ht string to print as title on document header - * @param string $hs string to print on document header - */ - public function setHeaderData($ln="", $lw=0, $ht="", $hs="") { - $this->header_logo = $ln; - $this->header_logo_width = $lw; - $this->header_title = $ht; - $this->header_string = $hs; - } - - /** - * Set header margin. - * (minimum distance between header and top page margin) - * @param int $hm distance in millimeters - */ - public function setHeaderMargin($hm=10) { - $this->header_margin = $hm; - } - - /** - * Set footer margin. - * (minimum distance between footer and bottom page margin) - * @param int $fm distance in millimeters - */ - public function setFooterMargin($fm=10) { - $this->footer_margin = $fm; - } - - /** - * Set a flag to print page header. - * @param boolean $val set to true to print the page header (default), false otherwise. - */ - public function setPrintHeader($val=true) { - $this->print_header = $val; - } - - /** - * Set a flag to print page footer. - * @param boolean $value set to true to print the page footer (default), false otherwise. - */ - public function setPrintFooter($val=true) { - $this->print_footer = $val; - } - - /** - * This method is used to render the page header. - * It is automatically called by AddPage() and could be overwritten in your own inherited class. - */ - public function Header() { - if ($this->print_header) { - - if (!isset($this->original_lMargin)) { - $this->original_lMargin = $this->lMargin; - } - if (!isset($this->original_rMargin)) { - $this->original_rMargin = $this->rMargin; - } - - //set current position - if ($this->rtl) { - $this->SetXY($this->original_rMargin, $this->header_margin); - } else { - $this->SetXY($this->original_lMargin, $this->header_margin); - } - - if (($this->header_logo) AND ($this->header_logo != K_BLANK_IMAGE)) { - $this->Image(K_PATH_IMAGES.$this->header_logo, $this->GetX(), $this->header_margin, $this->header_logo_width); - } else { - $this->img_rb_x = $this->GetX(); - $this->img_rb_y = $this->GetY(); - } - - $cell_height = round((K_CELL_HEIGHT_RATIO * $this->header_font[2]) / $this->k, 2); - // set starting margin for text data cell - if ($this->rtl) { - $header_x = $this->original_rMargin + ($this->header_logo_width * 1.1); - } else { - $header_x = $this->original_lMargin + ($this->header_logo_width * 1.1); - } - - // header title - $this->SetFont($this->header_font[0], 'B', $this->header_font[2] + 1); - $this->SetX($header_x); - $this->Cell($this->header_width, $cell_height, $this->header_title, 0, 1, ''); - - // header string - $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); - $this->SetX($header_x); - $this->MultiCell($this->header_width, $cell_height, $this->header_string, 0, '', 0); - - // print an ending header line - if (empty($this->header_width)) { - //set style for cell border - $prevlinewidth = $this->GetLineWidth(); - $line_width = 0.3; - $this->SetLineWidth($line_width); - $this->SetDrawColor(0, 0, 0); - $this->SetY(1 + max($this->img_rb_y, $this->GetY())); - if ($this->rtl) { - $this->SetX($this->original_rMargin); - } else { - $this->SetX($this->original_lMargin); - } - $this->Cell(0, 0, '', 'T', 0, 'C'); - $this->SetLineWidth($prevlinewidth); - } - - //restore position - if ($this->rtl) { - $this->SetXY($this->original_rMargin, $this->tMargin); - } else { - $this->SetXY($this->original_lMargin, $this->tMargin); - } - } - } - - /** - * This method is used to render the page footer. - * It is automatically called by AddPage() and could be overwritten in your own inherited class. - */ - public function Footer() { - if ($this->print_footer) { - - if (!isset($this->original_lMargin)) { - $this->original_lMargin = $this->lMargin; - } - if (!isset($this->original_rMargin)) { - $this->original_rMargin = $this->rMargin; - } - - //set font - $this->SetFont($this->footer_font[0], $this->footer_font[1] , $this->footer_font[2]); - //set style for cell border - $prevlinewidth = $this->GetLineWidth(); - $line_width = 0.3; - $this->SetLineWidth($line_width); - $this->SetDrawColor(0, 0, 0); - - $footer_height = round((K_CELL_HEIGHT_RATIO * $this->footer_font[2]) / $this->k, 2); //footer height - //get footer y position - $footer_y = $this->h - $this->footer_margin - $footer_height; - //set current position - if ($this->rtl) { - $this->SetXY($this->original_rMargin, $footer_y); - } else { - $this->SetXY($this->original_lMargin, $footer_y); - } - - //print document barcode - if ($this->barcode) { - $this->Ln(); - $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin)/3); //max width - $this->writeBarcode($this->GetX(), $footer_y + $line_width, $barcode_width, $footer_height - $line_width, "C128B", false, false, 2, $this->barcode); - } - - $pagenumtxt = $this->l['w_page']." ".$this->PageNo().' / {nb}'; - - $this->SetY($footer_y); - - //Print page number - if ($this->rtl) { - $this->SetX($this->original_rMargin); - $this->Cell(0, $footer_height, $pagenumtxt, 'T', 0, 'L'); - } else { - $this->SetX($this->original_lMargin); - $this->Cell(0, $footer_height, $pagenumtxt, 'T', 0, 'R'); - } - // restore line width - $this->SetLineWidth($prevlinewidth); - } - } - - /** - * Returns the current page number. - * @return int page number - * @since 1.0 - * @see AliasNbPages() - */ - public function PageNo() { - //Get current page number - return $this->page; - } - - /** - * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. - * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 - * @param int $g Green component (between 0 and 255) - * @param int $b Blue component (between 0 and 255) - * @since 1.3 - * @see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() - */ - public function SetDrawColor($r, $g=-1, $b=-1) { - //Set color for all stroking operations - if(($r==0 and $g==0 and $b==0) or $g==-1) { - $this->DrawColor=sprintf('%.3f G',$r/255); - } - else { - $this->DrawColor=sprintf('%.3f %.3f %.3f RG',$r/255,$g/255,$b/255); - } - if($this->page>0) { - $this->_out($this->DrawColor); - } - } - - /** - * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. - * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 - * @param int $g Green component (between 0 and 255) - * @param int $b Blue component (between 0 and 255) - * @param boolean $storeprev if true stores the RGB array on $prevFillColor variable. - * @since 1.3 - * @see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() - */ - public function SetFillColor($r, $g=-1, $b=-1, $storeprev=false) { - //Set color for all filling operations - if(($r==0 and $g==0 and $b==0) or $g==-1) { - $this->FillColor=sprintf('%.3f g',$r/255); - } - else { - $this->FillColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); - } - $this->ColorFlag=($this->FillColor!=$this->TextColor); - if($this->page>0) { - $this->_out($this->FillColor); - } - if ($storeprev) { - // store color as previous value - $this->prevFillColor = array($r, $g, $b); - } - } - - /** - * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. - * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 - * @param int $g Green component (between 0 and 255) - * @param int $b Blue component (between 0 and 255) - * @param boolean $storeprev if true stores the RGB array on $prevTextColor variable. - * @since 1.3 - * @see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() - */ - public function SetTextColor($r, $g=-1, $b=-1, $storeprev=false) { - //Set color for text - if(($r==0 and $g==0 and $b==0) or $g==-1) { - $this->TextColor=sprintf('%.3f g',$r/255); - } - else { - $this->TextColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); - } - $this->ColorFlag=($this->FillColor!=$this->TextColor); - if ($storeprev) { - // store color as previous value - $this->prevTextColor = array($r, $g, $b); - } - } - - /** - * Returns the length of a string in user unit. A font must be selected.<br> - * Support UTF-8 Unicode [Nicola Asuni, 2005-01-02] - * @param string $s The string whose length is to be computed - * @return int - * @since 1.2 - */ - public function GetStringWidth($s) { - //Get width of a string in the current font - $s = (string)$s; - $cw = &$this->CurrentFont['cw']; - $w = 0; - if($this->isunicode) { - $unicode = $this->UTF8StringToArray($s); - foreach($unicode as $char) { - if (isset($cw[$char])) { - $w+=$cw[$char]; - } elseif(isset($cw[ord($char)])) { - $w+=$cw[ord($char)]; - } elseif(isset($cw[chr($char)])) { - $w+=$cw[chr($char)]; - } elseif(isset($this->CurrentFont['desc']['MissingWidth'])) { - $w += $this->CurrentFont['desc']['MissingWidth']; // set default size - } else { - $w += 500; - } - } - } else { - $l = strlen($s); - for($i=0; $i < $l; $i++) { - if (isset($cw[$s{$i}])) { - $w += $cw[$s{$i}]; - } else if (isset($cw[ord($s{$i})])) { - $w += $cw[ord($s{$i})]; - } - } - } - return ($w * $this->FontSize / 1000); - } - - /** - * Returns the numbero of characters in a string. - * @param string $s The input string. - * @return int number of characters - * @since 2.0.0001 (2008-01-07) - */ - public function GetNumChars($s) { - if($this->isunicode) { - return count($this->UTF8StringToArray($s)); - } - return strlen($s); - } - - /** - * Imports a TrueType or Type1 font and makes it available. It is necessary to generate a font definition file first with the makefont.php utility. The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated. - * Support UTF-8 Unicode [Nicola Asuni, 2005-01-02]. - * <b>Example</b>:<br /> - * <pre> - * $pdf->AddFont('Comic','I'); - * // is equivalent to: - * $pdf->AddFont('Comic','I','comici.php'); - * </pre> - * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. - * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul> - * @param string $file The font definition file. By default, the name is built from the family and style, in lower case with no space. - * @since 1.5 - * @see SetFont() - */ - public function AddFont($family, $style='', $file='') { - if(empty($family)) { - return; - } - - //Add a TrueType or Type1 font - $family = strtolower($family); - if((!$this->isunicode) AND ($family == 'arial')) { - $family = 'helvetica'; - } - - $style=strtoupper($style); - $style=str_replace('U','',$style); - if($style == 'IB') { - $style = 'BI'; - } - - $fontkey = $family.$style; - // check if the font has been already added - if(isset($this->fonts[$fontkey])) { - return; - } - - if($file=='') { - $file = str_replace(' ', '', $family).strtolower($style).'.php'; - } - if(!file_exists($this->_getfontpath().$file)) { - // try to load the basic file without styles - $file = str_replace(' ', '', $family).'.php'; - } - - include($this->_getfontpath().$file); - - if(!isset($name) AND !isset($fpdf_charwidths)) { - $this->Error('Could not include font definition file'); - } - - $i = count($this->fonts)+1; - - if($this->isunicode) { - $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file, 'ctg'=>$ctg); - $fpdf_charwidths[$fontkey] = $cw; - } else { - $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$this->CoreFonts[$fontkey], 'up'=>-100, 'ut'=>50, 'cw'=>$fpdf_charwidths[$fontkey]); - } - - if(isset($diff) AND (!empty($diff))) { - //Search existing encodings - $d=0; - $nb=count($this->diffs); - for($i=1;$i<=$nb;$i++) { - if($this->diffs[$i]==$diff) { - $d=$i; - break; - } - } - if($d==0) { - $d=$nb+1; - $this->diffs[$d]=$diff; - } - $this->fonts[$fontkey]['diff']=$d; - } - if(!empty($file)) { - if((strcasecmp($type,"TrueType") == 0) OR (strcasecmp($type,"TrueTypeUnicode") == 0)) { - $this->FontFiles[$file]=array('length1'=>$originalsize); - } - else { - $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2); - } - } - } - - /** - * Sets the font used to print character strings. It is mandatory to call this method at least once before printing text or the resulting document would not be valid. - * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). - * The method can be called before the first page is created and the font is retained from page to page. - If you just wish to change the current font size, it is simpler to call SetFontSize(). - * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br /> - * Example for the last case (note the trailing slash):<br /> - * <pre> - * define('K_PATH_FONTS','/home/www/font/'); - * require('tcpdf.php'); - * - * //Times regular 12 - * $pdf->SetFont('Times'); - * //Arial bold 14 - * $pdf->SetFont('Arial','B',14); - * //Removes bold - * $pdf->SetFont(''); - * //Times bold, italic and underlined 14 - * $pdf->SetFont('Times','BIU'); - * </pre><br /> - * If the file corresponding to the requested font is not found, the error "Could not include font metric file" is generated. - * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard families (case insensitive):<ul><li>Courier (fixed-width)</li><li>Helvetica or Arial (synonymous; sans serif)</li><li>Times (serif)</li><li>Symbol (symbolic)</li><li>ZapfDingbats (symbolic)</li></ul>It is also possible to pass an empty string. In that case, the current family is retained. - * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li></ul>or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats - * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12 - * @since 1.0 - * @see AddFont(), SetFontSize(), Cell(), MultiCell(), Write() - */ - public function SetFont($family, $style='', $size=0) { - // save previous values - $this->prevFontFamily = $this->FontFamily; - $this->prevFontStyle = $this->FontStyle; - - //Select a font; size given in points - global $fpdf_charwidths; - - $family=strtolower($family); - if($family=='') { - $family=$this->FontFamily; - } - if((!$this->isunicode) AND ($family == 'arial')) { - $family = 'helvetica'; - } - elseif(($family=="symbol") OR ($family=="zapfdingbats")) { - $style=''; - } - $style=strtoupper($style); - - if(strpos($style,'U')!==false) { - $this->underline=true; - $style=str_replace('U','',$style); - } - else { - $this->underline=false; - } - if($style=='IB') { - $style='BI'; - } - if($size==0) { - $size=$this->FontSizePt; - } - - // try to add font (if not already added) - if($this->isunicode) { - $this->AddFont($family, $style); - } - - //Test if font is already selected - if(($this->FontFamily == $family) AND ($this->FontStyle == $style) AND ($this->FontSizePt == $size)) { - return; - } - - $fontkey = $family.$style; - //if(!isset($this->fonts[$fontkey]) AND isset($this->fonts[$family])) { - // $style=''; - //} - - //Test if used for the first time - if(!isset($this->fonts[$fontkey])) { - //Check if one of the standard fonts - if(isset($this->CoreFonts[$fontkey])) { - if(!isset($fpdf_charwidths[$fontkey])) { - //Load metric file - $file = $family; - if(($family!='symbol') AND ($family!='zapfdingbats')) { - $file .= strtolower($style); - } - if(!file_exists($this->_getfontpath().$file.'.php')) { - // try to load the basic file without styles - $file = $family; - $fontkey = $family; - } - include($this->_getfontpath().$file.'.php'); - if (($this->isunicode AND !isset($ctg)) OR ((!$this->isunicode) AND (!isset($fpdf_charwidths[$fontkey]))) ) { - $this->Error("Could not include font metric file [".$fontkey."]: ".$this->_getfontpath().$file.".php"); - } - } - $i = count($this->fonts) + 1; - - if($this->isunicode) { - $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file, 'ctg'=>$ctg); - $fpdf_charwidths[$fontkey] = $cw; - } else { - $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$this->CoreFonts[$fontkey], 'up'=>-100, 'ut'=>50, 'cw'=>$fpdf_charwidths[$fontkey]); - } - } - else { - $this->Error('Undefined font: '.$family.' '.$style); - } - } - //Select it - $this->FontFamily = $family; - $this->FontStyle = $style; - $this->FontSizePt = $size; - $this->FontSize = $size / $this->k; - $this->CurrentFont = &$this->fonts[$fontkey]; - if($this->page>0) { - $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); - } - } - - /** - * Defines the size of the current font. - * @param float $size The size (in points) - * @since 1.0 - * @see SetFont() - */ - public function SetFontSize($size) { - //Set font size in points - if($this->FontSizePt==$size) { - return; - } - $this->FontSizePt = $size; - $this->FontSize = $size / $this->k; - if($this->page > 0) { - $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); - } - } - - /** - * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br /> - * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). - * @since 1.5 - * @see Cell(), Write(), Image(), Link(), SetLink() - */ - public function AddLink() { - //Create a new internal link - $n=count($this->links)+1; - $this->links[$n]=array(0,0); - return $n; - } - - /** - * Defines the page and position a link points to - * @param int $link The link identifier returned by AddLink() - * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) - * @param int $page Number of target page; -1 indicates the current page. This is the default value - * @since 1.5 - * @see AddLink() - */ - public function SetLink($link, $y=0, $page=-1) { - //Set destination of internal link - if($y==-1) { - $y=$this->y; - } - if($page==-1) { - $page=$this->page; - } - $this->links[$link]=array($page,$y); - } - - /** - * Puts a link on a rectangular area of the page. Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image. - * @param float $x Abscissa of the upper-left corner of the rectangle (or upper-right for RTL languages) - * @param float $y Ordinate of the upper-left corner of the rectangle (or upper-right for RTL languages) - * @param float $w Width of the rectangle - * @param float $h Height of the rectangle - * @param mixed $link URL or identifier returned by AddLink() - * @since 1.5 - * @see AddLink(), Cell(), Write(), Image() - */ - public function Link($x, $y, $w, $h, $link) { - $this->PageLinks[$this->page][] = array($x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h*$this->k, $link); - } - - /** - * Prints a character string. The origin is on the left of the first charcter, on the baseline. This method allows to place a string precisely on the page, but it is usually easier to use Cell(), MultiCell() or Write() which are the standard methods to print text. - * @param float $x Abscissa of the origin - * @param float $y Ordinate of the origin - * @param string $txt String to print - * @since 1.0 - * @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write() - */ - public function Text($x, $y, $txt) { - //Output a string - if($this->rtl) { - $xr = $this->w - $x - $this->GetStringWidth($txt); - } else { - $xr = $x; - } - $s = sprintf('BT %.2f %.2f Td (%s) Tj ET', $xr * $this->k, ($this->h-$y) * $this->k, $this->_escapetext($txt)); - if($this->underline AND ($txt!='')) { - $s .= ' '.$this->_dounderline($xr, $y, $txt); - } - if($this->ColorFlag) { - $s='q '.$this->TextColor.' '.$s.' Q'; - } - $this->_out($s); - } - - /** - * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br /> - * This method is called automatically and should not be called directly by the application.<br /> - * <b>Example:</b><br /> - * The method is overriden in an inherited class in order to obtain a 3 column layout:<br /> - * <pre> - * class PDF extends TCPDF { - * var $col=0; - * - * public function SetCol($col) { - * //Move position to a column - * $this->col=$col; - * $x=10+$col*65; - * $this->SetLeftMargin($x); - * $this->SetX($x); - * } - * - * public function AcceptPageBreak() { - * if($this->col<2) { - * //Go to next column - * $this->SetCol($this->col+1); - * $this->SetY(10); - * return false; - * } - * else { - * //Go back to first column and issue page break - * $this->SetCol(0); - * return true; - * } - * } - * } - * - * $pdf=new PDF(); - * $pdf->Open(); - * $pdf->AddPage(); - * $pdf->SetFont('Arial','',12); - * for($i=1;$i<=300;$i++) { - * $pdf->Cell(0,5,"Line $i",0,1); - * } - * $pdf->Output(); - * </pre> - * @return boolean - * @since 1.4 - * @see SetAutoPageBreak() - */ - public function AcceptPageBreak() { - //Accept automatic page break or not - return $this->AutoPageBreak; - } - - /** - * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br /> - * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. - * @param float $w Cell width. If 0, the cell extends up to the right margin. - * @param float $h Cell height. Default value: 0. - * @param string $txt String to print. Default value: empty string. - * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> - * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> - Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. - * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> - * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. - * @param mixed $link URL or identifier returned by AddLink(). - * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul> - * @since 1.0 - * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() - */ - public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) { - //Output a cell - $k=$this->k; - if(($this->y + $h) > $this->PageBreakTrigger AND empty($this->InFooter) AND $this->AcceptPageBreak()) { - //Automatic page break - $x = $this->x; - $ws = $this->ws; - if($ws > 0) { - $this->ws = 0; - $this->_out('0 Tw'); - } - $this->AddPage($this->CurOrientation); - $this->x = $x; - if($ws > 0) { - $this->ws = $ws; - $this->_out(sprintf('%.3f Tw',$ws * $k)); - } - } - if($w == 0) { - if ($this->rtl) { - $w = $this->x - $this->lMargin; - } else { - $w = $this->w - $this->rMargin - $this->x; - } - } - $s = ''; - if(($fill == 1) OR ($border == 1)) { - if($fill == 1) { - $op = ($border == 1) ? 'B' : 'f'; - } else { - $op = 'S'; - } - if ($this->rtl) { - $xk = ($this->x - $w) * $k; - } else { - $xk = $this->x * $k; - } - $s .= sprintf('%.2f %.2f %.2f %.2f re %s ', $xk, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op); - } - if(is_string($border)) { - $x=$this->x; - $y=$this->y; - if(strpos($border,'L')!==false) { - if ($this->rtl) { - $xk = ($x - $w) * $k; - } else { - $xk = $x * $k; - } - $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xk,($this->h-($y+$h))*$k); - } - if(strpos($border,'T')!==false) { - if ($this->rtl) { - $xk = ($x - $w) * $k; - $xwk = $x * $k; - } else { - $xk = $x * $k; - $xwk = ($x + $w) * $k; - } - $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xwk,($this->h-$y)*$k); - } - if(strpos($border,'R')!==false) { - if ($this->rtl) { - $xk = $x * $k; - } else { - $xk = ($x + $w) * $k; - } - $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xk,($this->h-($y+$h))*$k); - } - if(strpos($border,'B')!==false) { - if ($this->rtl) { - $xk = ($x - $w) * $k; - $xwk = $x * $k; - } else { - $xk = $x * $k; - $xwk = ($x + $w) * $k; - } - $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-($y+$h))*$k,$xwk,($this->h-($y+$h))*$k); - } - } - if($txt != '') { - // text lenght - $width = $this->GetStringWidth($txt); - // ratio between cell lenght and text lenght - $ratio = ($w - (2 * $this->cMargin)) / $width; - - // stretch text if requested - if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) { - if ($stretch > 2) { - // spacing - //Calculate character spacing in points - $char_space = ($w - (2 * $this->cMargin) - $width) / max($this->GetNumChars($s)-1,1) * $this->k; - //Set character spacing - $this->_out(sprintf('BT %.2f Tc ET', $char_space)); - } else { - // scaling - //Calculate horizontal scaling - $horiz_scale = $ratio*100.0; - //Set horizontal scaling - $this->_out(sprintf('BT %.2f Tz ET', $horiz_scale)); - } - $align = ''; - $width = $w - (2 * $this->cMargin); - } else { - $stretch == 0; - } - - if($align == 'L') { - if ($this->rtl) { - $dx = $w - $this->cMargin - $width; - } else { - $dx = $this->cMargin; - } - } elseif($align == 'R') { - if ($this->rtl) { - $dx = $this->cMargin; - } else { - $dx = $w - $this->cMargin - $width; - } - } elseif($align=='C') { - $dx = ($w - $width)/2; - } elseif($align=='J') { - if ($this->rtl) { - $dx = $w - $this->cMargin - $width; - } else { - $dx = $this->cMargin; - } - } else { - $dx = $this->cMargin; - } - if($this->ColorFlag) { - $s .= 'q '.$this->TextColor.' '; - } - $txt2 = $this->_escapetext($txt); - if ($this->rtl) { - $xdk = ($this->x - $dx - $width) * $k; - } else { - $xdk = ($this->x + $dx) * $k; - } - // 2008-02-16 Jacek Czekaj - multibyte justification - if ($align == 'J') { - // count number of spaces - $ns = substr_count($txt, ' '); - // get string width without spaces - $width = $this->GetStringWidth(str_replace(' ', '', $txt)); - // set word position to be used with TJ operator - $txt2 = str_replace(chr(0).' ', ') '. -2830*($w-$width-2)/($ns?$ns:1)/$this->FontSize/$this->k . ' (', $txt2); - } - $s.=sprintf('BT %.2f %.2f Td [(%s)] TJ ET', $xdk, ($this->h - ($this->y + 0.5 * $h + 0.3 * $this->FontSize)) * $k, $txt2); - //$s.=sprintf('BT %.2f %.2f Td (%s) Tj ET', $xdk, ($this->h - ($this->y + 0.5 * $h + 0.3 * $this->FontSize)) * $k, $txt2); - if($this->underline) { - if ($this->rtl) { - $xdx = $this->x - $dx - $width; - } else { - $xdx = $this->x + $dx; - } - $s.=' '.$this->_dounderline($xdx, $this->y + 0.5 * $h + 0.3 * $this->FontSize, $txt); - } - if($this->ColorFlag) { - $s.=' Q'; - } - if($link) { - if ($this->rtl) { - $xdx = $this->x - $dx - $width; - } else { - $xdx = $this->x + $dx; - } - $this->Link($xdx, $this->y + 0.5 * $h - 0.5 * $this->FontSize, $width, $this->FontSize, $link); - } - } - - // output cell - if($s) { - // output cell - $this->_out($s); - // reset text stretching - if($stretch > 2) { - //Reset character horizontal spacing - $this->_out('BT 0 Tc ET'); - } elseif($stretch > 0) { - //Reset character horizontal scaling - $this->_out('BT 100 Tz ET'); - } - } - - $this->lasth = $h; - if($ln>0) { - //Go to the beginning of the next line - $this->y += $h; - if($ln == 1) { - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - } else { - $this->x = $this->lMargin; - } - } - } else { - // go left or right by case - if ($this->rtl) { - $this->x -= $w; - } else { - $this->x += $w; - } - } - } - - /** - * This method allows printing text with line breaks. They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br /> - * Text can be aligned, centered or justified. The cell block can be framed and the background painted. - * @param float $w Width of cells. If 0, they extend up to the right margin of the page. - * @param float $h Height of cells. - * @param string $txt String to print - * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> - * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value)</li></ul> - * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. - * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul> - * @return int number of cells (number of lines) - * @since 1.3 - * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() - */ - public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1) { - - // save current position - $prevx = $this->x; - $prevy = $this->y; - - // get current page number - $startpage = $this->page; - - // calculate remaining vertical space on first page ($startpage) - $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin(); - - //Output text with automatic or explicit line breaks - $cw = &$this->CurrentFont['cw']; - - if($w == 0) { - if ($this->rtl) { - $w = $this->x - $this->lMargin; - } else { - $w = $this->w - $this->rMargin - $this->x; - } - } - - $wmax = ($w - 2 * $this->cMargin); - - $s = str_replace("\r", '', $txt); // remove carriage returns - $nb = strlen($s); - - $sep=-1; - $i=0; - $j=0; - $l=0; - $ns=0; - $nl=1; - while($i < $nb) { - //Get next character - $c = $s{$i}; - if(preg_match("/[\n]/u", $c)) { - //Explicit line break - if($this->ws > 0) { - $this->ws = 0; - $this->_out('0 Tw'); - } - $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); - $i++; - $sep=-1; - $j=$i; - $l=0; - $ns=0; - $nl++; - continue; - } - if(preg_match("/[ ]/u", $c)) { - $sep = $i; - $ls = $l; - $ns++; - } - - $l = $this->GetStringWidth(substr($s, $j, $i-$j)); - - if($l > $wmax) { - //Automatic line break - if($sep == -1) { - if($i == $j) { - $i++; - } - if($this->ws > 0) { - $this->ws = 0; - $this->_out('0 Tw'); - } - $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); - } - else { - if($align=='J') { - $this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0; - $this->_out(sprintf('%.3f Tw', $this->ws * $this->k)); - } - $this->Cell($w, $h, substr($s, $j, $sep-$j), 0, 2, $align, $fill, ''); - $i = $sep + 1; - } - $sep=-1; - $j=$i; - $l=0; - $ns=0; - $nl++; - } - else { - $i++; - } - } - //Last chunk - if($this->ws>0) { - $this->ws=0; - $this->_out('0 Tw'); - } - if($align == "J") { - $align = "L"; - } - $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); - - // Get end-of-text Y position - $currentY = $this->GetY(); - // get latest page number - $endpage = $this->page; - - if (!empty($border)) { - // check if a new page has been created - if ($endpage > $startpage) { - // design borders around HTML cells. - for ($page=$startpage; $page<=$endpage; $page++) { - $this->page = $page; - if ($page==$startpage) { - $this->y = $this->getPageHeight() - $restspace - $this->getBreakMargin(); - $h = $restspace - 1; - } elseif ($page==$endpage) { - $this->y = $this->tMargin; // put cursor at the beginning of text - $h = $currentY - $this->tMargin; - } else { - $this->y = $this->tMargin; // put cursor at the beginning of text - $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); - } - $this->Cell($w, $h, "", $border, 1, '', 0); - } - } else { - $h = max($h, ($currentY - $prevy)); - $this->y = $prevy; // put cursor at the beginning of text - // design a cell around the text - $this->Cell($w, $h, "", $border, 1, '', 0); - } - } - - // move cursor to specified position - if($ln>0) { - //Go to the beginning of the next line - $this->SetY($currentY); - if($ln == 2) { - $this->SetX($prevx + $w); - } - } else { - // go left or right by case - $this->page = $startpage; - $this->y = $prevy; - $this->SetX($prevx + $w); - } - - return $nl; - } - - /** - * This method prints text from the current position. When the right margin is reached (or the \n character is met) a line break occurs and text continues from the left margin. Upon method exit, the current position is left just at the end of the text. It is possible to put a link on the text.<br /> - * <b>Example:</b><br /> - * <pre> - * //Begin with regular font - * $pdf->SetFont('Arial','',14); - * $pdf->Write(5,'Visit '); - * //Then put a blue underlined link - * $pdf->SetTextColor(0,0,255); - * $pdf->SetFont('','U'); - * $pdf->Write(5,'www.tecnick.com','http://www.tecnick.com'); - * </pre> - * @param float $h Line height - * @param string $txt String to print - * @param mixed $link URL or identifier returned by AddLink() - * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. - * @since 1.5 - * @see SetFont(), SetTextColor(), AddLink(), MultiCell(), SetAutoPageBreak() - */ - public function Write($h, $txt, $link='', $fill=0) { - //Output text in flowing mode - $cw = &$this->CurrentFont['cw']; - // calculating remaining line width ($w) - if ($this->rtl) { - $w = $this->x - $this->lMargin; - } else { - $w = $this->w - $this->rMargin - $this->x; - } - $wmax = $w - 2 * $this->cMargin; - $s = str_replace("\r", '', $txt); - $nb = strlen($s); - // handle single space character - if(($nb==1) AND preg_match("/[ ]/u", $s)) { - if ($this->rtl) { - $this->x -= $this->GetStringWidth($s); - } else { - $this->x += $this->GetStringWidth($s); - } - return; - } - $sep=-1; - $i=0; - $j=0; - $l=0; - $nl=1; - while($i<$nb) { - //Get next character - $c=$s{$i}; - if(preg_match("/[\n]/u", $c)) { - //Explicit line break - $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, '', $fill, $link); - $i++; - $sep = -1; - $j = $i; - $l = 0; - if($nl == 1) { - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - $w = $this->x - $this->lMargin; - } - else { - $this->x = $this->lMargin; - $w = $this->w - $this->rMargin - $this->x; - } - $wmax = ($w - 2 * $this->cMargin); - } - $nl++; - continue; - } - if(preg_match("/[ ]/u", $c)) { - $sep= $i; - } - $l = $this->GetStringWidth(substr($s, $j, $i-$j)); - if($l > $wmax) { - //Automatic line break (word wrapping) - if($sep == -1) { - if((!$this->rtl) AND ($this->x > $this->lMargin)) { - //Move to next line - $this->x = $this->lMargin; - $this->y += $h; - $w=$this->w - $this->rMargin - $this->x; - $wmax=($w - 2 * $this->cMargin); - $i++; - $nl++; - continue; - } - if($this->rtl AND ($this->x < $this->rMargin)) { - //Move to next line - $this->x = $this->w - $this->rMargin; - $this->y += $h; - $w=$this->x - $this->lMargin; - $wmax=($w - 2 * $this->cMargin); - $i++; - $nl++; - continue; - } - if($i==$j) { - $i++; - } - $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, '', $fill, $link); - } - else { - $this->Cell($w, $h, substr($s, $j, $sep-$j), 0, 2, '', $fill, $link); - $i=$sep+1; - } - $sep = -1; - $j = $i; - $l = 0; - if($nl==1) { - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - $w = $this->x - $this->lMargin; - } else { - $this->x = $this->lMargin; - $w = $this->w - $this->rMargin - $this->x; - } - $wmax = ($w - 2 * $this->cMargin); - } - $nl++; - } - else { - $i++; - } - } - - //Last chunk - if($i!=$j) { - $this->Cell($this->GetStringWidth(substr($s, $j)) + 2 * $this->cMargin, $h, substr($s, $j), 0, 0, '', $fill, $link); - } - } - - /** - * Puts an image in the page. The upper-left corner must be given. The dimensions can be specified in different ways:<ul><li>explicit width and height (expressed in user unit)</li><li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li><li>no explicit dimension, in which case the image is put at 72 dpi</li></ul> - * Supported formats are JPEG and PNG. - * For JPEG, all flavors are allowed:<ul><li>gray scales</li><li>true colors (24 bits)</li><li>CMYK (32 bits)</li></ul> - * For PNG, are allowed:<ul><li>gray scales on at most 8 bits (256 levels)</li><li>indexed colors</li><li>true colors (24 bits)</li></ul> - * If a transparent color is defined, it will be taken into account (but will be only interpreted by Acrobat 4 and above).<br /> - * The format can be specified explicitly or inferred from the file extension.<br /> - * It is possible to put a link on the image.<br /> - * Remark: if an image is used several times, only one copy will be embedded in the file.<br /> - * @param string $file Name of the file containing the image. - * @param float $x Abscissa of the upper-left corner. - * @param float $y Ordinate of the upper-left corner. - * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. - * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. - * @param string $type Image format. Possible values are (case insensitive): JPG, JPEG, PNG. If not specified, the type is inferred from the file extension. - * @param mixed $link URL or identifier returned by AddLink(). - * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> - * @since 1.1 - * @see AddLink() - */ - public function Image($file, $x, $y, $w=0, $h=0, $type='', $link='', $align='') { - //Put an image on the page - if(!isset($this->images[$file])) { - //First use of image, get info - if($type == '') { - $pos = strrpos($file,'.'); - if(empty($pos)) { - $this->Error('Image file has no extension and no type was specified: '.$file); - } - $type = substr($file, $pos+1); - } - $type = strtolower($type); - $mqr = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - if($type == 'jpg' or $type == 'jpeg') { - $info=$this->_parsejpg($file); - } elseif($type == 'gif') { - $info=$this->_parsegif($file); - } elseif($type == 'png') { - $info=$this->_parsepng($file); - }else { - //Allow for additional formats - $mtd='_parse'.$type; - if(!method_exists($this,$mtd)) { - $this->Error('Unsupported image type: '.$type); - } - $info=$this->$mtd($file); - } - if($info === false) { - //If false, we cannot process image - return; - } - set_magic_quotes_runtime($mqr); - $info['i']=count($this->images)+1; - $this->images[$file]=$info; - } - else { - $info=$this->images[$file]; - } - //Automatic width and height calculation if needed - if(($w == 0) and ($h == 0)) { - //Put image at 72 dpi - // 2004-06-14 :: Nicola Asuni, scale factor where added - $w = $info['w'] / ($this->imgscale * $this->k); - $h = $info['h'] / ($this->imgscale * $this->k); - } - if($w == 0) { - $w = $h * $info['w'] / $info['h']; - } - if($h == 0) { - $h = $w * $info['h'] / $info['w']; - } - - // 2007-10-19 Warren Sherliker - // Check whether we need a new page first as this does not fit - // Copied from Cell() - if((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) { - // Automatic page break - $this->AddPage($this->CurOrientation); - // Reset coordinates to top fo next page - $x = $this->GetX(); - $y = $this->GetY(); - } - // 2007-10-19 Warren Sherliker: End Edit - - // set bottomcoordinates - $this->img_rb_y = $y + $h; - if ($this->rtl) { - $ximg = ($this->w - $x -$w); - // set left side coordinate - $this->img_rb_x = $ximg; - } else { - $ximg = $x; - // set right side coordinate - $this->img_rb_x = $ximg + $w; - } - $xkimg = $ximg * $this->k; - $this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', $w*$this->k, $h*$this->k, $xkimg, ($this->h-($y+$h))*$this->k, $info['i'])); - - if($link) { - $this->Link($ximg, $y, $w, $h, $link); - } - - // set pointer to align the successive text/objects - switch($align) { - case 'T':{ - $this->y = $y; - $this->x = $this->img_rb_x; - break; - } - case 'M':{ - $this->y = $y + round($h/2); - $this->x = $this->img_rb_x; - break; - } - case 'B':{ - $this->y = $this->img_rb_y; - $this->x = $this->img_rb_x; - break; - } - case 'N':{ - $this->SetY($this->img_rb_y); - break; - } - default:{ - break; - } - } - } - - - /** - * Performs a line break. The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. - * @param float $h The height of the break. By default, the value equals the height of the last printed cell. - * @since 1.0 - * @see Cell() - */ - public function Ln($h='') { - //Line feed; default value is last cell height - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - } else { - $this->x = $this->lMargin; - } - if(is_string($h)) { - $this->y += $this->lasth; - } else { - $this->y += $h; - } - } - - /** - * Returns the relative X value of current position. - * The value is relative to the left border for LTR languages and to the right border for RTL languages. - * @return float - * @since 1.2 - * @see SetX(), GetY(), SetY() - */ - public function GetX() { - //Get x position - if ($this->rtl) { - return ($this->w - $this->x); - } else { - return $this->x; - } - } - - /** - * Returns the absolute X value of current position. - * @return float - * @since 1.2 - * @see SetX(), GetY(), SetY() - */ - public function GetAbsX() { - return $this->x; - } - - /** - * Returns the ordinate of the current position. - * @return float - * @since 1.0 - * @see SetY(), GetX(), SetX() - */ - public function GetY() { - //Get y position - return $this->y; - } - - /** - * Defines the abscissa of the current position. - * If the passed value is negative, it is relative to the right of the page (or left if language is RTL). - * @param float $x The value of the abscissa. - * @since 1.2 - * @see GetX(), GetY(), SetY(), SetXY() - */ - public function SetX($x) { - //Set x position - if ($this->rtl) { - if($x >= 0) { - $this->x = $this->w - $x; - } else { - $this->x = abs($x); - } - } else { - if($x >= 0) { - $this->x = $x; - } else { - $this->x = $this->w + $x; - } - } - } - - /** - * Moves the current abscissa back to the left margin and sets the ordinate. - * If the passed value is negative, it is relative to the bottom of the page. - * @param float $y The value of the ordinate. - * @since 1.0 - * @see GetX(), GetY(), SetY(), SetXY() - */ - public function SetY($y) { - //Set y position and reset x - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - } else { - $this->x = $this->lMargin; - } - if($y >= 0) { - $this->y = $y; - } else { - $this->y = $this->h + $y; - } - } - - - /** - * Defines the abscissa and ordinate of the current position. If the passed values are negative, they are relative respectively to the right and bottom of the page. - * @param float $x The value of the abscissa - * @param float $y The value of the ordinate - * @since 1.2 - * @see SetX(), SetY() - */ - public function SetXY($x, $y) { - //Set x and y positions - $this->SetY($y); - $this->SetX($x); - } - - /** - * Send the document to a given destination: string, local file or browser. In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br /> - * The method first calls Close() if necessary to terminate the document. - * @param string $name The name of the file. If not given, the document will be sent to the browser (destination I) with the name doc.pdf. - * @param string $dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser. The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li></ul>If the parameter is not specified but a name is given, destination is F. If no parameter is specified at all, destination is I.<br />Note: for compatibility with previous versions, a boolean value is also accepted (false for F and true for D). - * @since 1.0 - * @see Close() - */ - public function Output($name='',$dest='') { - //Output PDF to some destination - //Finish document if necessary - if($this->state < 3) { - $this->Close(); - } - //Normalize parameters - if(is_bool($dest)) { - $dest=$dest ? 'D' : 'F'; - } - $dest=strtoupper($dest); - if($dest=='') { - if($name=='') { - $name='doc.pdf'; - $dest='I'; - } else { - $dest='F'; - } - } - switch($dest) { - case 'I': { - //Send to standard output - //if(ob_get_contents()) { - // $this->Error('Some data has already been output, can\'t send PDF file'); - //} - ob_end_clean(); - ob_start(); - if(php_sapi_name()!='cli') { - //We send to a browser - header('Content-Type: application/pdf'); - if(headers_sent()) { - $this->Error('Some data has already been output to browser, can\'t send PDF file'); - } - header('Content-Length: '.strlen($this->buffer)); - header('Content-disposition: inline; filename="'.$name.'"'); - } - echo $this->buffer; - break; - } - case 'D': { - //Download file - // if(ob_get_contents()) { - // $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); - //} - - if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { - header('Content-Type: application/force-download'); - } else { - header('Content-Type: application/octet-stream'); - } - if(headers_sent()) { - $this->Error('Some data has already been output to browser, can\'t send PDF file'); - } - header('Content-Length: '.strlen($this->buffer)); - header('Content-disposition: attachment; filename="'.$name.'"'); - echo $this->buffer; - break; - } - case 'F': { - //Save to local file - $f=fopen($name,'wb'); - if(!$f) { - $this->Error('Unable to create output file: '.$name); - } - fwrite($f,$this->buffer,strlen($this->buffer)); - fclose($f); - break; - } - case 'S': { - //Return as a string - return $this->buffer; - } - default: { - $this->Error('Incorrect output destination: '.$dest); - } - } - return ''; - } - - // Protected methods - - /** - * Check for locale-related bug - * @access protected - */ - protected function _dochecks() { - //Check for locale-related bug - if(1.1==1) { - $this->Error('Don\'t alter the locale before including class file'); - } - //Check for decimal separator - if(sprintf('%.1f',1.0)!='1.0') { - setlocale(LC_NUMERIC,'C'); - } - } - - /** - * Return fonts path - * @access protected - */ - protected function _getfontpath() { - if(!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/font')) { - define('K_PATH_FONTS', dirname(__FILE__).'/font/'); - } - return defined('K_PATH_FONTS') ? K_PATH_FONTS : ''; - } - - /** - * Start document - * @access protected - */ - protected function _begindoc() { - //Start document - $this->state=1; - $this->_out('%PDF-'.$this->PDFVersion); - } - - /** - * _putpages - * @access protected - */ - protected function _putpages() { - $nb = $this->page; - if(!empty($this->AliasNbPages)) { - $nbstr = $this->UTF8ToUTF16BE($nb, false); - //Replace number of pages - for($n=1;$n<=$nb;$n++) { - $this->pages[$n]=str_replace($this->AliasNbPages, $nbstr, $this->pages[$n]); - } - } - if($this->DefOrientation=='P') { - $wPt=$this->fwPt; - $hPt=$this->fhPt; - } - else { - $wPt=$this->fhPt; - $hPt=$this->fwPt; - } - $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; - for($n=1;$n<=$nb;$n++) { - //Page - $this->_newobj(); - $this->_out('<</Type /Page'); - $this->_out('/Parent 1 0 R'); - if(isset($this->OrientationChanges[$n])) { - $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$hPt,$wPt)); - } - $this->_out('/Resources 2 0 R'); - if(isset($this->PageLinks[$n])) { - //Links - $annots='/Annots ['; - foreach($this->PageLinks[$n] as $pl) { - $rect=sprintf('%.2f %.2f %.2f %.2f',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]); - $annots.='<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] '; - if(is_string($pl[4])) { - $annots.='/A <</S /URI /URI '.$this->_uristring($pl[4]).'>>>>'; - } - else { - $l=$this->links[$pl[4]]; - $h=isset($this->OrientationChanges[$l[0]]) ? $wPt : $hPt; - $annots.=sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>',1+2*$l[0],$h-$l[1]*$this->k); - } - } - $this->_out($annots.']'); - } - $this->_out('/Contents '.($this->n+1).' 0 R>>'); - $this->_out('endobj'); - //Page content - $p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n]; - $this->_newobj(); - $this->_out('<<'.$filter.'/Length '.strlen($p).'>>'); - $this->_putstream($p); - $this->_out('endobj'); - } - //Pages root - $this->offsets[1]=strlen($this->buffer); - $this->_out('1 0 obj'); - $this->_out('<</Type /Pages'); - $kids='/Kids ['; - for($i=0;$i<$nb;$i++) { - $kids.=(3+2*$i).' 0 R '; - } - $this->_out($kids.']'); - $this->_out('/Count '.$nb); - $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$wPt,$hPt)); - $this->_out('>>'); - $this->_out('endobj'); - } - - /** - * Adds fonts - * _putfonts - * @access protected - */ - protected function _putfonts() { - $nf=$this->n; - foreach($this->diffs as $diff) { - //Encodings - $this->_newobj(); - $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>'); - $this->_out('endobj'); - } - $mqr=get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - foreach($this->FontFiles as $file=>$info) { - //Font file embedding - $this->_newobj(); - $this->FontFiles[$file]['n']=$this->n; - $font=''; - $f=fopen($this->_getfontpath().strtolower($file),'rb',1); - if(!$f) { - $this->Error('Font file not found: '.$file); - } - while(!feof($f)) { - $font .= fread($f, 8192); - } - fclose($f); - $compressed=(substr($file,-2)=='.z'); - if(!$compressed && isset($info['length2'])) { - $header=(ord($font{0})==128); - if($header) { - //Strip first binary header - $font=substr($font,6); - } - if($header && ord($font{$info['length1']})==128) { - //Strip second binary header - $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6); - } - } - $this->_out('<</Length '.strlen($font)); - if($compressed) { - $this->_out('/Filter /FlateDecode'); - } - $this->_out('/Length1 '.$info['length1']); - if(isset($info['length2'])) { - $this->_out('/Length2 '.$info['length2'].' /Length3 0'); - } - $this->_out('>>'); - $this->_putstream($font); - $this->_out('endobj'); - } - set_magic_quotes_runtime($mqr); - foreach($this->fonts as $k=>$font) { - //Font objects - $this->fonts[$k]['n']=$this->n+1; - $type=$font['type']; - $name=$font['name']; - if($type=='core') { - //Standard font - $this->_newobj(); - $this->_out('<</Type /Font'); - $this->_out('/BaseFont /'.$name); - $this->_out('/Subtype /Type1'); - if($name!='Symbol' && $name!='ZapfDingbats') { - $this->_out('/Encoding /WinAnsiEncoding'); - } - $this->_out('>>'); - $this->_out('endobj'); - } elseif($type=='Type1' OR $type=='TrueType') { - //Additional Type1 or TrueType font - $this->_newobj(); - $this->_out('<</Type /Font'); - $this->_out('/BaseFont /'.$name); - $this->_out('/Subtype /'.$type); - $this->_out('/FirstChar 32 /LastChar 255'); - $this->_out('/Widths '.($this->n+1).' 0 R'); - $this->_out('/FontDescriptor '.($this->n+2).' 0 R'); - if($font['enc']) { - if(isset($font['diff'])) { - $this->_out('/Encoding '.($nf+$font['diff']).' 0 R'); - } else { - $this->_out('/Encoding /WinAnsiEncoding'); - } - } - $this->_out('>>'); - $this->_out('endobj'); - //Widths - $this->_newobj(); - $cw=&$font['cw']; - $s='['; - for($i=32;$i<=255;$i++) { - $s.=$cw[chr($i)].' '; - } - $this->_out($s.']'); - $this->_out('endobj'); - //Descriptor - $this->_newobj(); - $s='<</Type /FontDescriptor /FontName /'.$name; - foreach($font['desc'] as $k=>$v) { - $s.=' /'.$k.' '.$v; - } - $file = $font['file']; - if($file) { - $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R'; - } - $this->_out($s.'>>'); - $this->_out('endobj'); - } else { - //Allow for additional types - $mtd='_put'.strtolower($type); - if(!method_exists($this, $mtd)) { - $this->Error('Unsupported font type: '.$type); - } - $this->$mtd($font); - } - } - } - - /** - * _putimages - * @access protected - */ - protected function _putimages() { - $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; - reset($this->images); - while(list($file,$info)=each($this->images)) { - $this->_newobj(); - $this->images[$file]['n']=$this->n; - $this->_out('<</Type /XObject'); - $this->_out('/Subtype /Image'); - $this->_out('/Width '.$info['w']); - $this->_out('/Height '.$info['h']); - - if (isset($info["masked"])) { - $this->_out('/SMask '.($this->n-1).' 0 R'); - } - - if($info['cs']=='Indexed') { - $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); - } - else { - $this->_out('/ColorSpace /'.$info['cs']); - if($info['cs']=='DeviceCMYK') { - $this->_out('/Decode [1 0 1 0 1 0 1 0]'); - } - } - $this->_out('/BitsPerComponent '.$info['bpc']); - if(isset($info['f'])) { - $this->_out('/Filter /'.$info['f']); - } - if(isset($info['parms'])) { - $this->_out($info['parms']); - } - if(isset($info['trns']) and is_array($info['trns'])) { - $trns=''; - for($i=0;$i<count($info['trns']);$i++) { - $trns.=$info['trns'][$i].' '.$info['trns'][$i].' '; - } - $this->_out('/Mask ['.$trns.']'); - } - $this->_out('/Length '.strlen($info['data']).'>>'); - $this->_putstream($info['data']); - unset($this->images[$file]['data']); - $this->_out('endobj'); - //Palette - if($info['cs']=='Indexed') { - $this->_newobj(); - $pal=($this->compress) ? gzcompress($info['pal']) : $info['pal']; - $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>'); - $this->_putstream($pal); - $this->_out('endobj'); - } - } - } - - /** - * _putxobjectdict - * @access protected - */ - function _putxobjectdict() { - foreach($this->images as $image) { - $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); - } - } - - /** - * _putresourcedict - * @access protected - */ - function _putresourcedict(){ - $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); - $this->_out('/Font <<'); - foreach($this->fonts as $font) { - $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R'); - } - $this->_out('>>'); - $this->_out('/XObject <<'); - $this->_putxobjectdict(); - $this->_out('>>'); - } - - /** - * _putresources - * @access protected - */ - function _putresources() { - $this->_putfonts(); - $this->_putimages(); - //Resource dictionary - $this->offsets[2]=strlen($this->buffer); - $this->_out('2 0 obj'); - $this->_out('<<'); - $this->_putresourcedict(); - $this->_out('>>'); - $this->_out('endobj'); - $this->_putjavascript(); - $this->_putbookmarks(); - // encryption - if ($this->encrypted) { - $this->_newobj(); - $this->enc_obj_id = $this->n; - $this->_out('<<'); - $this->_putencryption(); - $this->_out('>>'); - $this->_out('endobj'); - } - } - - /** - * _putinfo - * Adds some meta information - * @access protected - */ - protected function _putinfo() { - $this->_out('/CreationDate ('.$this->_escape('D:'.date('YmdHis')).')'); - $this->_out('/ModDate ('.$this->_escape('D:'.date('YmdHis')).')'); - $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER)); - if(!empty($this->title)) { - $this->_out('/Title '.$this->_textstring($this->title)); - } - if(!empty($this->subject)) { - $this->_out('/Subject '.$this->_textstring($this->subject)); - } - if(!empty($this->author)) { - $this->_out('/Author '.$this->_textstring($this->author)); - } - if(!empty($this->keywords)) { - $this->_out('/Keywords '.$this->_textstring($this->keywords)); - } - if(!empty($this->creator)) { - $this->_out('/Creator '.$this->_textstring($this->creator)); - } - } - - /** - * _putcatalog - * @access protected - */ - protected function _putcatalog() { - $this->_out('/Type /Catalog'); - $this->_out('/Pages 1 0 R'); - if($this->ZoomMode=='fullpage') { - $this->_out('/OpenAction [3 0 R /Fit]'); - } - elseif($this->ZoomMode=='fullwidth') { - $this->_out('/OpenAction [3 0 R /FitH null]'); - } - elseif($this->ZoomMode=='real') { - $this->_out('/OpenAction [3 0 R /XYZ null null 1]'); - } - elseif(!is_string($this->ZoomMode)) { - $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode/100).']'); - } - if($this->LayoutMode=='single') { - $this->_out('/PageLayout /SinglePage'); - } - elseif($this->LayoutMode=='continuous') { - $this->_out('/PageLayout /OneColumn'); - } - elseif($this->LayoutMode=='two') { - $this->_out('/PageLayout /TwoColumnLeft'); - } - if (!empty($this->javascript)) { - $this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>'); - } - if(count($this->outlines)>0) { - $this->_out('/Outlines '.$this->OutlineRoot.' 0 R'); - $this->_out('/PageMode /UseOutlines'); - } - if($this->rtl) { - $this->_out('/ViewerPreferences << /Direction /R2L >>'); - } - } - - /** - * _puttrailer - * @access protected - */ - protected function _puttrailer() { - $this->_out('/Size '.($this->n+1)); - $this->_out('/Root '.$this->n.' 0 R'); - $this->_out('/Info '.($this->n-1).' 0 R'); - if ($this->encrypted) { - $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R'); - $this->_out('/ID [()()]'); - } - } - - /** - * _putheader - * @access protected - */ - function _putheader() { - $this->_out('%PDF-'.$this->PDFVersion); - } - - /** - * _enddoc - * @access protected - */ - protected function _enddoc() { - $this->_putheader(); - $this->_putpages(); - $this->_putresources(); - //Info - $this->_newobj(); - $this->_out('<<'); - $this->_putinfo(); - $this->_out('>>'); - $this->_out('endobj'); - //Catalog - $this->_newobj(); - $this->_out('<<'); - $this->_putcatalog(); - $this->_out('>>'); - $this->_out('endobj'); - //Cross-ref - $o=strlen($this->buffer); - $this->_out('xref'); - $this->_out('0 '.($this->n+1)); - $this->_out('0000000000 65535 f '); - for($i=1;$i<=$this->n;$i++) { - $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i])); - } - //Trailer - $this->_out('trailer'); - $this->_out('<<'); - $this->_puttrailer(); - $this->_out('>>'); - $this->_out('startxref'); - $this->_out($o); - $this->_out('%%EOF'); - $this->state=3; - } - - /** - * _beginpage - * @access protected - */ - protected function _beginpage($orientation) { - $this->page++; - $this->pages[$this->page]=''; - $this->state=2; - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - } else { - $this->x = $this->lMargin; - } - $this->y = $this->tMargin; - $this->FontFamily=''; - //Page orientation - if(empty($orientation)) { - $orientation=$this->DefOrientation; - } - else { - $orientation=strtoupper($orientation{0}); - if($orientation!=$this->DefOrientation) { - $this->OrientationChanges[$this->page]=true; - } - } - if($orientation!=$this->CurOrientation) { - //Change orientation - if($orientation=='P') { - $this->wPt=$this->fwPt; - $this->hPt=$this->fhPt; - $this->w=$this->fw; - $this->h=$this->fh; - } - else { - $this->wPt=$this->fhPt; - $this->hPt=$this->fwPt; - $this->w=$this->fh; - $this->h=$this->fw; - } - $this->PageBreakTrigger=$this->h-$this->bMargin; - $this->CurOrientation=$orientation; - } - } - - /** - * End of page contents - * @access protected - */ - protected function _endpage() { - $this->state=1; - } - - /** - * Begin a new object - * @access protected - */ - protected function _newobj() { - $this->n++; - $this->offsets[$this->n]=strlen($this->buffer); - $this->_out($this->n.' 0 obj'); - } - - /** - * Underline text - * @param int $x X coordinate - * @param int $y Y coordinate - * @param string $txt text to underline - * @access protected - */ - protected function _dounderline($x, $y, $txt) { - $up = $this->CurrentFont['up']; - $ut = $this->CurrentFont['ut']; - $w = $this->GetStringWidth($txt) + $this->ws * substr_count($txt,' '); - return sprintf('%.2f %.2f %.2f %.2f re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt); - } - - - // REWRITTEN by Warren Sherliker wsherliker@gmail.com - // altered to allow compatibility with all sorts of image formats including gif. - // Can easily extend to work with others - // such as gd xbm etc. which are all supported by php 5+ - // (Requires GD library) - - /** - * Extract info from a JPEG file - * @param string $file image file to parse - * @return string - * @access protected - */ - protected function _parsejpg($file) { - if(!function_exists('imagecreatefromjpeg')) { - // GD is not installed, try legacy method - return $this->_legacyparsejpg($file); - } - $a=getimagesize($file); - if(empty($a)) { - $this->Error('Missing or incorrect image file: '.$file); - } - if($a[2]!=2) { - $this->Error('Not a JPEG file: '.$file); - } - $jpeg = imagecreatefromjpeg($file); - return $this->outputjpg($file, $jpeg); - } - - /** - * Extract info from a GIF file - * @param string $file image file to parse - * @return string - * @access protected - */ - protected function _parsegif($file) { - if(!function_exists('imagecreatefromgif')) { - // PDF doesn't support native GIF and GD is not installed - return false; - } - $a=getimagesize($file); - if(empty($a)) { - $this->Error('Missing or incorrect image file: '.$file); - } - if($a[2]!=1) { - $this->Error('Not a GIF file: '.$file); - } - // Temporary convert file to jpg and then delete this temp data file - $gif = imagecreatefromgif($file); - return $this->toJPEG($file, $gif); - } - - /** - * Extract info from a PNG file - * @param string $file image file to parse - * @return string - * @access protected - */ - protected function _parsepng($file) { - if(!function_exists('imagecreatefrompng')) { - // GD is not installed, try legacy method - return $this->_legacyparsepng($file); - } - $f=fopen($file,'rb'); - if(empty($f)) { - $this->Error('Can\'t open image file: '.$file); - } - //Check signature - if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { - $this->Error('Not a PNG file: '.$file); - } - //Read header chunk - fread($f,4); - if(fread($f,4)!='IHDR') { - $this->Error('Incorrect PNG file: '.$file); - } - // Temporary convert file to jpg and then delete this temp data file - $a=getimagesize($file); - $png = imagecreatefrompng($file); - return $this->toJPEG($file, $png); - } - - /** - * Extract info from a JPEG file without using GD - * @param string $file image file to parse - * @return string - * @access protected - */ - protected function _legacyparsejpg($file) { - $a=GetImageSize($file); - if(empty($a)) { - $this->Error('Missing or incorrect image file: '.$file); - } - if($a[2]!=2) { - $this->Error('Not a JPEG file: '.$file); - } - if(!isset($a['channels']) or $a['channels']==3) { - $colspace='DeviceRGB'; - } - elseif($a['channels']==4) { - $colspace='DeviceCMYK'; - } - else { - $colspace='DeviceGray'; - } - $bpc=isset($a['bits']) ? $a['bits'] : 8; - //Read whole file - $f=fopen($file,'rb'); - $data=''; - while(!feof($f)) { - $data.=fread($f,4096); - } - fclose($f); - return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data); - } - - /** - * Extract info from a PNG file without using GD - * @param string $file image file to parse - * @return string - * @access protected - */ - protected function _legacyparsepng($file) { - $f=fopen($file,'rb'); - if(empty($f)) { - $this->Error('Can\'t open image file: '.$file); - } - //Check signature - if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { - $this->Error('Not a PNG file: '.$file); - } - //Read header chunk - fread($f,4); - if(fread($f,4)!='IHDR') { - $this->Error('Incorrect PNG file: '.$file); - } - $w=$this->_freadint($f); - $h=$this->_freadint($f); - $bpc=ord(fread($f,1)); - if($bpc>8) { - $this->Error('16-bit depth not supported: '.$file); - } - $ct=ord(fread($f,1)); - if($ct==0) { - $colspace='DeviceGray'; - } - elseif($ct==2) { - $colspace='DeviceRGB'; - } - elseif($ct==3) { - $colspace='Indexed'; - } - else { - $this->Error('Alpha channel not supported: '.$file); - } - if(ord(fread($f,1))!=0) { - $this->Error('Unknown compression method: '.$file); - } - if(ord(fread($f,1))!=0) { - $this->Error('Unknown filter method: '.$file); - } - if(ord(fread($f,1))!=0) { - $this->Error('Interlacing not supported: '.$file); - } - fread($f,4); - $parms='/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>'; - //Scan chunks looking for palette, transparency and image data - $pal=''; - $trns=''; - $data=''; - do { - $n=$this->_freadint($f); - $type=fread($f,4); - if($type=='PLTE') { - //Read palette - $pal=fread($f,$n); - fread($f,4); - } - elseif($type=='tRNS') { - //Read transparency info - $t=fread($f,$n); - if($ct==0) { - $trns=array(ord(substr($t,1,1))); - } - elseif($ct==2) { - $trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1))); - } - else { - $pos=strpos($t,chr(0)); - if($pos!==false) { - $trns=array($pos); - } - } - fread($f,4); - } - elseif($type=='IDAT') { - //Read image data block - $data.=fread($f,$n); - fread($f,4); - } - elseif($type=='IEND') { - break; - } - else { - fread($f,$n+4); - } - } - while($n); - if($colspace=='Indexed' and empty($pal)) { - $this->Error('Missing palette in '.$file); - } - fclose($f); - return array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'parms'=>$parms, 'pal'=>$pal, 'trns'=>$trns, 'data'=>$data); - } - - /** - * Convert the loaded php image to a JPEG and then return a structure for the PDF creator. - * @param string $file Image file name. - * @param image $image Image object. - * return image JPEG image object. - * @access protected - */ - protected function toJPEG($file, $image) { - if ($image) { - // output - $tempname = tempnam(K_PATH_CACHE,'jpg'); - imagejpeg($image, $tempname, 100); - imagedestroy($image); - $retvars = $this->outputjpg($tempname); - // tidy up by removing temporary image - unlink($tempname); - return $retvars; - } else { - $this->Error('Can\'t open image file: '.$file); - } - } - - /** - * Get a JPEG filename and return a structure for the PDF creator. - * @param string $filename JPEG file name. - * @return array structure containing the image data - * @access protected - */ - protected function outputjpg($filename) { - $a=getimagesize($filename); - - if(!isset($a['channels']) or $a['channels']==3) { - $colspace='DeviceRGB'; - } - elseif($a['channels']==4) { - $colspace='DeviceCMYK'; - } - else { - $colspace='DeviceGray'; - } - $bpc=isset($a['bits']) ? $a['bits'] : 8; - //Read whole file - - $f=fopen($filename,'rb'); - $data=''; - while(!feof($f)) { - $data.=fread($f,4096); - } - fclose($f); - - return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data); - } - - /// END OF REWRITE BY Warren Sherliker wsherliker@gmail.com - - /** - * Read a 4-byte integer from file - * @param string $f file name. - * @return 4-byte integer - * @access protected - */ - protected function _freadint($f) { - $a=unpack('Ni',fread($f,4)); - return $a['i']; - } - - /** - * Format a text string for meta information - * @param string $s string to escape. - * @return string escaped string. - * @access protected - */ - protected function _textstring($s) { - if($this->isunicode) { - //Convert string to UTF-16BE - $s = $this->UTF8ToUTF16BE($s, true); - } - if ($this->encrypted) { - $s = $this->_RC4($this->_objectkey($this->n), $s); - } - return '('. $this->_escape($s).')'; - } - - /** - * Format an URI string - * @param string $s string to escape. - * @return string escaped string. - * @access protected - */ - protected function _uristring($s) { - if ($this->encrypted) { - $s = $this->_RC4($this->_objectkey($this->n), $s); - } - return '('.$this->_escape($s).')'; - } - - /** - * Format a text string - * @param string $s string to escape. - * @return string escaped string. - * @access protected - */ - protected function _escapetext($s) { - if($this->isunicode) { - //Convert string to UTF-16BE and reverse RTL language - $s = $this->utf8StrRev($s, false, $this->tmprtl); - } - return $this->_escape($s); - } - - /** - * Add \ before \, ( and ) - * @param string $s string to escape. - * @return string escaped string. - * @access protected - */ - protected function _escape($s) { - // the chr(13) substitution fixes the Bugs item #1421290. - return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); - } - - /** - * Output a stream. - * @param string $s string to output. - * @access protected - */ - protected function _putstream($s) { - if ($this->encrypted) { - $s = $this->_RC4($this->_objectkey($this->n), $s); - } - $this->_out('stream'); - $this->_out($s); - $this->_out('endstream'); - } - - /** - * Output a string to the document. - * @param string $s string to output. - * @access protected - */ - protected function _out($s) { - if($this->state==2) { - $this->pages[$this->page] .= $s."\n"; - } - else { - $this->buffer .= $s."\n"; - } - } - - /** - * Adds unicode fonts.<br> - * Based on PDF Reference 1.3 (section 5) - * @access protected - * @author Nicola Asuni - * @since 1.52.0.TC005 (2005-01-05) - */ - protected function _puttruetypeunicode($font) { - // Type0 Font - // A composite font composed of other fonts, organized hierarchically - $this->_newobj(); - $this->_out('<</Type /Font'); - $this->_out('/Subtype /Type0'); - $this->_out('/BaseFont /'.$font['name'].''); - $this->_out('/Encoding /Identity-H'); //The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values. - $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]'); - $this->_out('/ToUnicode '.($this->n + 2).' 0 R'); - $this->_out('>>'); - $this->_out('endobj'); - - // CIDFontType2 - // A CIDFont whose glyph descriptions are based on TrueType font technology - $this->_newobj(); - $this->_out('<</Type /Font'); - $this->_out('/Subtype /CIDFontType2'); - $this->_out('/BaseFont /'.$font['name'].''); - $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R'); - $this->_out('/FontDescriptor '.($this->n + 3).' 0 R'); - if (isset($font['desc']['MissingWidth'])){ - $this->_out('/DW '.$font['desc']['MissingWidth'].''); // The default width for glyphs in the CIDFont MissingWidth - } - $w = ""; - foreach ($font['cw'] as $cid => $width) { - $w .= ''.$cid.' ['.$width.'] '; // define a specific width for each individual CID - } - $this->_out('/W ['.$w.']'); // A description of the widths for the glyphs in the CIDFont - $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R'); - $this->_out('>>'); - $this->_out('endobj'); - - // ToUnicode - // is a stream object that contains the definition of the CMap - // (PDF Reference 1.3 chap. 5.9) - $this->_newobj(); - $this->_out('<</Length 345>>'); - $this->_out('stream'); - $this->_out('/CIDInit /ProcSet findresource begin'); - $this->_out('12 dict begin'); - $this->_out('begincmap'); - $this->_out('/CIDSystemInfo'); - $this->_out('<</Registry (Adobe)'); - $this->_out('/Ordering (UCS)'); - $this->_out('/Supplement 0'); - $this->_out('>> def'); - $this->_out('/CMapName /Adobe-Identity-UCS def'); - $this->_out('/CMapType 2 def'); - $this->_out('1 begincodespacerange'); - $this->_out('<0000> <FFFF>'); - $this->_out('endcodespacerange'); - $this->_out('1 beginbfrange'); - $this->_out('<0000> <FFFF> <0000>'); - $this->_out('endbfrange'); - $this->_out('endcmap'); - $this->_out('CMapName currentdict /CMap defineresource pop'); - $this->_out('end'); - $this->_out('end'); - $this->_out('endstream'); - $this->_out('endobj'); - - // CIDSystemInfo dictionary - // A dictionary containing entries that define the character collection of the CIDFont. - $this->_newobj(); - $this->_out('<</Registry (Adobe)'); // A string identifying an issuer of character collections - $this->_out('/Ordering (UCS)'); // A string that uniquely names a character collection issued by a specific registry - $this->_out('/Supplement 0'); // The supplement number of the character collection. - $this->_out('>>'); - $this->_out('endobj'); - - // Font descriptor - // A font descriptor describing the CIDFont default metrics other than its glyph widths - $this->_newobj(); - $this->_out('<</Type /FontDescriptor'); - $this->_out('/FontName /'.$font['name']); - foreach ($font['desc'] as $key => $value) { - $this->_out('/'.$key.' '.$value); - } - if ($font['file']) { - // A stream containing a TrueType font program - $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'); - } - $this->_out('>>'); - $this->_out('endobj'); - - // Embed CIDToGIDMap - // A specification of the mapping from CIDs to glyph indices - $this->_newobj(); - $ctgfile = $this->_getfontpath().strtolower($font['ctg']); - if(!file_exists($ctgfile)) { - $this->Error('Font file not found: '.$ctgfile); - } - $size = filesize($ctgfile); - $this->_out('<</Length '.$size.''); - if(substr($ctgfile, -2) == '.z') { // check file extension - /* Decompresses data encoded using the public-domain - zlib/deflate compression method, reproducing the - original text or binary data */ - $this->_out('/Filter /FlateDecode'); - } - $this->_out('>>'); - $this->_putstream(file_get_contents($ctgfile)); - $this->_out('endobj'); - } - - /** - * Converts UTF-8 strings to codepoints array.<br> - * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br> - * Based on: http://www.faqs.org/rfcs/rfc3629.html - * <pre> - * Char. number range | UTF-8 octet sequence - * (hexadecimal) | (binary) - * --------------------+----------------------------------------------- - * 0000 0000-0000 007F | 0xxxxxxx - * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx - * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx - * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * --------------------------------------------------------------------- - * - * ABFN notation: - * --------------------------------------------------------------------- - * UTF8-octets = *( UTF8-char ) - * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 - * UTF8-1 = %x00-7F - * UTF8-2 = %xC2-DF UTF8-tail - * - * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / - * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) - * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / - * %xF4 %x80-8F 2( UTF8-tail ) - * UTF8-tail = %x80-BF - * --------------------------------------------------------------------- - * </pre> - * @param string $str string to process. - * @return array containing codepoints (UTF-8 characters values) - * @access protected - * @author Nicola Asuni - * @since 1.53.0.TC005 (2005-01-05) - */ - protected function UTF8StringToArray($str) { - if(!$this->isunicode) { - return $str; // string is not in unicode - } - $unicode = array(); // array containing unicode values - $bytes = array(); // array containing single character byte sequences - $numbytes = 1; // number of octetc needed to represent the UTF-8 character - - $str .= ""; // force $str to be a string - $length = strlen($str); - - for($i = 0; $i < $length; $i++) { - $char = ord($str{$i}); // get one string character at time - if(count($bytes) == 0) { // get starting octect - if ($char <= 0x7F) { - $unicode[] = $char; // use the character "as is" because is ASCII - $numbytes = 1; - } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) - $bytes[] = ($char - 0xC0) << 0x06; - $numbytes = 2; - } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) - $bytes[] = ($char - 0xE0) << 0x0C; - $numbytes = 3; - } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) - $bytes[] = ($char - 0xF0) << 0x12; - $numbytes = 4; - } else { - // use replacement character for other invalid sequences - $unicode[] = 0xFFFD; - $bytes = array(); - $numbytes = 1; - } - } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN - $bytes[] = $char - 0x80; - if (count($bytes) == $numbytes) { - // compose UTF-8 bytes to a single unicode value - $char = $bytes[0]; - for($j = 1; $j < $numbytes; $j++) { - $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); - } - if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) { - /* The definition of UTF-8 prohibits encoding character numbers between - U+D800 and U+DFFF, which are reserved for use with the UTF-16 - encoding form (as surrogate pairs) and do not directly represent - characters. */ - $unicode[] = 0xFFFD; // use replacement character - } - else { - $unicode[] = $char; // add char to array - } - // reset data for next char - $bytes = array(); - $numbytes = 1; - } - } else { - // use replacement character for other invalid sequences - $unicode[] = 0xFFFD; - $bytes = array(); - $numbytes = 1; - } - } - return $unicode; - } - - /** - * Converts UTF-8 strings to UTF16-BE.<br> - * @param string $str string to process. - * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) - * @return string - * @access protected - * @author Nicola Asuni - * @since 1.53.0.TC005 (2005-01-05) - * @uses UTF8StringToArray(), arrUTF8ToUTF16BE() - */ - protected function UTF8ToUTF16BE($str, $setbom=true) { - if(!$this->isunicode) { - return $str; // string is not in unicode - } - $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values - return $this->arrUTF8ToUTF16BE($unicode, $setbom); - } - - /** - * Converts array of UTF-8 characters to UTF16-BE string.<br> - * Based on: http://www.faqs.org/rfcs/rfc2781.html - * <pre> - * Encoding UTF-16: - * - * Encoding of a single character from an ISO 10646 character value to - * UTF-16 proceeds as follows. Let U be the character number, no greater - * than 0x10FFFF. - * - * 1) If U < 0x10000, encode U as a 16-bit unsigned integer and - * terminate. - * - * 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, - * U' must be less than or equal to 0xFFFFF. That is, U' can be - * represented in 20 bits. - * - * 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and - * 0xDC00, respectively. These integers each have 10 bits free to - * encode the character value, for a total of 20 bits. - * - * 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order - * bits of W1 and the 10 low-order bits of U' to the 10 low-order - * bits of W2. Terminate. - * - * Graphically, steps 2 through 4 look like: - * U' = yyyyyyyyyyxxxxxxxxxx - * W1 = 110110yyyyyyyyyy - * W2 = 110111xxxxxxxxxx - * </pre> - * @param array $unicode array containing UTF-8 unicode values - * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) - * @return string - * @access protected - * @author Nicola Asuni - * @since 2.1.000 (2008-01-08) - * @see UTF8ToUTF16BE() - */ - protected function arrUTF8ToUTF16BE($unicode, $setbom=true) { - $outstr = ""; // string to be returned - if ($setbom) { - $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) - } - foreach($unicode as $char) { - if($char == 0xFFFD) { - $outstr .= "\xFF\xFD"; // replacement character - } elseif ($char < 0x10000) { - $outstr .= chr($char >> 0x08); - $outstr .= chr($char & 0xFF); - } else { - $char -= 0x10000; - $w1 = 0xD800 | ($char >> 0x10); - $w2 = 0xDC00 | ($char & 0x3FF); - $outstr .= chr($w1 >> 0x08); - $outstr .= chr($w1 & 0xFF); - $outstr .= chr($w2 >> 0x08); - $outstr .= chr($w2 & 0xFF); - } - } - return $outstr; - } - // ==================================================== - - /** - * Set header font. - * @param array $font font - * @since 1.1 - */ - public function setHeaderFont($font) { - $this->header_font = $font; - } - - /** - * Set footer font. - * @param array $font font - * @since 1.1 - */ - public function setFooterFont($font) { - $this->footer_font = $font; - } - - /** - * Set language array. - * @param array $language - * @since 1.1 - */ - public function setLanguageArray($language) { - $this->l = $language; - $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; - } - - /** - * Set document barcode. - * @param string $bc barcode - */ - public function setBarcode($bc="") { - $this->barcode = $bc; - } - - /** - * Print Barcode. - * @param int $x x position in user units - * @param int $y y position in user units - * @param int $w width in user units - * @param int $h height position in user units - * @param string $type type of barcode (I25, C128A, C128B, C128C, C39) - * @param string $style barcode style - * @param string $font font for text - * @param int $xres x resolution - * @param string $code code to print - */ - public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) { - require_once(dirname(__FILE__)."/barcode/barcode.php"); - require_once(dirname(__FILE__)."/barcode/i25object.php"); - require_once(dirname(__FILE__)."/barcode/c39object.php"); - require_once(dirname(__FILE__)."/barcode/c128aobject.php"); - require_once(dirname(__FILE__)."/barcode/c128bobject.php"); - require_once(dirname(__FILE__)."/barcode/c128cobject.php"); - - if (empty($code)) { - return; - } - - if (empty($style)) { - $style = BCS_ALIGN_LEFT; - $style |= BCS_IMAGE_PNG; - $style |= BCS_TRANSPARENT; - //$style |= BCS_BORDER; - //$style |= BCS_DRAW_TEXT; - //$style |= BCS_STRETCH_TEXT; - //$style |= BCS_REVERSE_COLOR; - } - if (empty($font)) {$font = BCD_DEFAULT_FONT;} - if (empty($xres)) {$xres = BCD_DEFAULT_XRES;} - - $scale_factor = 1.5 * $xres * $this->k; - $bc_w = round($w * $scale_factor); //width in points - $bc_h = round($h * $scale_factor); //height in points - - switch (strtoupper($type)) { - case "I25": { - $obj = new I25Object($bc_w, $bc_h, $style, $code); - break; - } - case "C128A": { - $obj = new C128AObject($bc_w, $bc_h, $style, $code); - break; - } - default: - case "C128B": { - $obj = new C128BObject($bc_w, $bc_h, $style, $code); - break; - } - case "C128C": { - $obj = new C128CObject($bc_w, $bc_h, $style, $code); - break; - } - case "C39": { - $obj = new C39Object($bc_w, $bc_h, $style, $code); - break; - } - } - - $obj->SetFont($font); - $obj->DrawObject($xres); - - //use a temporary file.... - $tmpName = tempnam(K_PATH_CACHE,'img'); - imagepng($obj->getImage(), $tmpName); - $this->Image($tmpName, $x, $y, $w, $h, 'png'); - $obj->DestroyObject(); - unset($obj); - unlink($tmpName); - } - - /** - * Returns the PDF data. - */ - public function getPDFData() { - if($this->state < 3) { - $this->Close(); - } - return $this->buffer; - } - - // --- HTML PARSER FUNCTIONS --- - - /** - * Allows to preserve some HTML formatting.<br /> - * Supports: h1, h2, h3, h4, h5, h6, b, u, i, a, img, p, br, strong, em, font, blockquote, li, ul, ol, hr, td, th, tr, table, sup, sub, small - * @param string $html text to display - * @param boolean $ln if true add a new line after text (default = true) - * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. - * @param boolean $reseth if true reset the last cell height (default false). - * @param boolean $cell if true add the default cMargin space to each Write. - */ - public function writeHTML($html, $ln=true, $fill=0, $reseth=false, $cell=false) { - - // store some variables - $html=strip_tags($html,"<h1><h2><h3><h4><h5><h6><b><u><i><a><img><p><br><br/><strong><em><font><blockquote><li><ul><ol><hr><td><th><tr><table><sup><sub><small><span><div>"); //remove all unsupported tags - //replace carriage returns, newlines and tabs - $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " "); - $html = strtr($html, $repTable); - $pattern = '/(<[^>]+>)/Uu'; - $a = preg_split($pattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); //explodes the string - - if ((empty($this->lasth))OR ($reseth)) { - //set row height - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - } - - foreach($a as $key=>$element) { - if (!preg_match($pattern, $element)) { - //Text - if($this->HREF) { - $this->addHtmlLink($this->HREF, $element, $fill); - } elseif($this->tdbegin) { - if((strlen(trim($element)) > 0) AND ($element != "&nbsp;")) { - $this->Cell($this->tdwidth, $this->tdheight, $this->unhtmlentities($element), $this->tableborder, '', $this->tdalign, $this->tdbgcolor); - } elseif($element == "&nbsp;") { - $this->Cell($this->tdwidth, $this->tdheight, '', $this->tableborder, '', $this->tdalign, $this->tdbgcolor); - } - } else { - - $ctmpmargin = $this->cMargin; - if(!$cell) { - $this->cMargin = 0; - } - $this->Write($this->lasth, stripslashes($this->unhtmlentities($element)), '', $fill, 0); - $this->cMargin = $ctmpmargin; - } - } else { - $element = substr($element, 1, -1); - //Tag - if($element{0}=='/') { - $this->closedHTMLTagHandler(strtolower(substr($element, 1))); - } - else { - //Extract attributes - // get tag name - preg_match('/([a-zA-Z0-9]*)/', $element, $tag); - $tag = strtolower($tag[0]); - // get attributes - preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER); - $attr = array(); // reset attribute array - while(list($id,$name)=each($attr_array[1])) { - $attr[strtolower($name)] = $attr_array[2][$id]; - } - $this->openHTMLTagHandler($tag, $attr, $fill); - } - } - } - if ($ln) { - $this->Ln($this->lasth); - } - } - - /** - * Prints a cell (rectangular area) with optional borders, background color and html text string. The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br /> - * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. - * @param float $w Cell width. If 0, the cell extends up to the right margin. - * @param float $h Cell minimum height. The cell extends automatically if needed. - * @param float $x upper-left corner X coordinate - * @param float $y upper-left corner Y coordinate - * @param string $html html text to print. Default value: empty string. - * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> - * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> - Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. - * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. - * @param boolean $reseth if true reset the last cell height (default false). - * @see Cell() - */ - public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=false) { - - if ((empty($this->lasth))OR ($reseth)) { - //set row height - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - } - - // get current page number - $startpage = $this->page; - - if (!empty($y)) { - $this->SetY($y); - } else { - $y = $this->GetY(); - } - if (!empty($x)) { - $this->SetX($x); - } else { - $x = $this->GetX(); - } - - if(empty($w)) { - if ($this->rtl) { - $w = $this->x - $this->lMargin; - } else { - $w = $this->w - $this->rMargin - $this->x; - } - } - - // store original margin values - $lMargin = $this->lMargin; - $rMargin = $this->rMargin; - - // set new margin values - if ($this->rtl) { - $this->SetLeftMargin($this->x - $w); - $this->SetRightMargin($this->w - $this->x); - } else { - $this->SetLeftMargin($this->x); - $this->SetRightMargin($this->w - $this->x - $w); - } - - // calculate remaining vertical space on first page ($startpage) - $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin(); - - // Write HTML text - $this->writeHTML($html, true, $fill, $reseth, true); - - // Get end-of-text Y position - $currentY = $this->GetY(); - // get latest page number - $endpage = $this->page; - - if (!empty($border)) { - // check if a new page has been created - if ($endpage > $startpage) { - // design borders around HTML cells. - for ($page=$startpage; $page<=$endpage; $page++) { - $this->page = $page; - if ($page==$startpage) { - $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin()); - $h = $restspace - 1; - } elseif ($page==$endpage) { - $this->SetY($this->tMargin); // put cursor at the beginning of text - $h = $currentY - $this->tMargin; - } else { - $this->SetY($this->tMargin); // put cursor at the beginning of text - $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); - } - $this->Cell($w, $h, "", $border, 1, '', 0); - } - } else { - $h = max($h, ($currentY - $y)); - $this->SetY($y); // put cursor at the beginning of text - // design a cell around the text - $this->Cell($w, $h, "", $border, 1, '', 0); - } - } - - // restore original margin values - $this->SetLeftMargin($lMargin); - $this->SetRightMargin($rMargin); - - - if($ln>0) { - //Go to the beginning of the next line - $this->SetY($currentY); - if($ln == 2) { - $this->SetX($x + $w); - } - } else { - // go left or right by case - $this->page = $startpage; - $this->y = $y; - $this->SetX($x + $w); - } - - } - - /** - * Process opening tags. - * @param string $tag tag name (in uppercase) - * @param string $attr tag attribute (in uppercase) - * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. - * @access private - */ - private function openHTMLTagHandler($tag, $attr, $fill=0) { - // check for text direction attribute - if (isset($attr['dir'])) { - $this->tmprtl = $attr['dir']=='rtl' ? 'R' : 'L'; - } else { - $this->tmprtl = false; - } - //Opening tag - switch($tag) { - case 'table': { - if ((isset($attr['border'])) AND ($attr['border'] != '')) { - $this->tableborder = $attr['border']; - } - else { - $this->tableborder = 0; - } - break; - } - case 'tr': { - break; - } - case 'td': - case 'th': { - if ((isset($attr['width'])) AND ($attr['width'] != '')) { - $this->tdwidth = ($attr['width']/4); - } - else { - $this->tdwidth = (($this->w - $this->lMargin - $this->rMargin) / $this->default_table_columns); - } - if ((isset($attr['height'])) AND ($attr['height'] != '')) { - $this->tdheight=($attr['height'] / $this->k); - } - else { - $this->tdheight = $this->lasth; - } - if ((isset($attr['align'])) AND ($attr['align'] != '')) { - switch ($attr['align']) { - case 'center': { - $this->tdalign = "C"; - break; - } - case 'right': { - $this->tdalign = "R"; - break; - } - default: - case 'left': { - $this->tdalign = "L"; - break; - } - } - } else { - if($this->rtl) { - $this->tdalign = "R"; - } else { - $this->tdalign = "L"; - } - } - if ((isset($attr['bgcolor'])) AND ($attr['bgcolor'] != '')) { - $coul = $this->convertColorHexToDec($attr['bgcolor']); - $this->SetFillColor($coul['R'], $coul['G'], $coul['B']); - $this->tdbgcolor=true; - } - $this->tdbegin=true; - break; - } - case 'hr': { - $this->Ln(); - if ((isset($attr['width'])) AND ($attr['width'] != '')) { - $hrWidth = $attr['width']; - } - else { - $hrWidth = $this->w - $this->lMargin - $this->rMargin; - } - $x = $this->GetX(); - $y = $this->GetY(); - $this->GetLineWidth(); - $prevlinewidth = $this->SetLineWidth(0.2); - $this->Line($x, $y, $x + $hrWidth, $y); - $this->SetLineWidth($prevlinewidth); - $this->Ln(); - break; - } - case 'strong': { - $this->setStyle('b', true); - break; - } - case 'em': { - $this->setStyle('i', true); - break; - } - case 'b': - case 'i': - case 'u': { - $this->setStyle($tag, true); - break; - } - case 'a': { - $this->HREF = $attr['href']; - break; - } - case 'img': { - if(isset($attr['src'])) { - // replace relative path with real server path - if ($attr['src'][0] == '/') { - $attr['src'] = $_SERVER['DOCUMENT_ROOT'].$attr['src']; - } - $attr['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $attr['src']); - if(!isset($attr['width'])) { - $attr['width'] = 0; - } - if(!isset($attr['height'])) { - $attr['height'] = 0; - } - if(!isset($attr['align'])) { - $align = 'N'; - } else { - switch($attr['align']) { - case 'top':{ - $align = 'T'; - break; - } - case 'middle':{ - $align = 'M'; - break; - } - case 'bottom':{ - $align = 'B'; - break; - } - default:{ - $align = 'N'; - break; - } - } - } - $this->Image($attr['src'], $this->GetX(),$this->GetY(), $this->pixelsToMillimeters($attr['width']), $this->pixelsToMillimeters($attr['height']), '', '', $align); - - } - break; - } - case 'ul': { - $this->listordered = false; - $this->listcount = 0; - break; - } - case 'ol': { - $this->listordered = true; - $this->listcount = 0; - break; - } - case 'li': { - $this->Ln(); - if ($this->listordered) { - if (isset($attr['value'])) { - $this->listcount = intval($attr['value']); - } - $this->lispacer = " ".(++$this->listcount).". "; - } else { - //unordered list simbol - $this->lispacer = " - "; - } - $rtldir = $this->tmprtl; - $this->tmprtl = false; - $this->Write($this->lasth, $this->lispacer, '', $fill); - $this->tmprtl = $rtldir; - break; - } - case 'blockquote': - case 'br': { - $this->Ln(); - if(strlen($this->lispacer) > 0) { - if ($this->rtl) { - $this->x -= $this->GetStringWidth($this->lispacer); - } else { - $this->x += $this->GetStringWidth($this->lispacer); - } - } - break; - } - case 'p': { - $this->Ln(); - $this->Ln(); - break; - } - case 'sup': { - $currentFontSize = $this->FontSize; - $this->tempfontsize = $this->FontSizePt; - $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); - $this->SetXY($this->GetX(), $this->GetY() - (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); - break; - } - case 'sub': { - $currentFontSize = $this->FontSize; - $this->tempfontsize = $this->FontSizePt; - $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); - $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); - break; - } - case 'small': { - $currentFontSize = $this->FontSize; - $this->tempfontsize = $this->FontSizePt; - $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); - $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)/3)); - break; - } - case 'font': { - if (isset($attr['color']) AND $attr['color']!='') { - $coul = $this->convertColorHexToDec($attr['color']); - $this->SetTextColor($coul['R'],$coul['G'],$coul['B']); - $this->issetcolor=true; - } - if (isset($attr['face']) and in_array(strtolower($attr['face']), $this->fontlist)) { - $this->SetFont(strtolower($attr['face'])); - $this->issetfont=true; - } - if (isset($attr['size'])) { - $headsize = intval($attr['size']); - } else { - $headsize = 0; - } - $currentFontSize = $this->FontSize; - $this->tempfontsize = $this->FontSizePt; - $this->SetFontSize($this->FontSizePt + $headsize); - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - break; - } - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': { - $headsize = (4 - substr($tag, 1)) * 2; - $currentFontSize = $this->FontSize; - $this->tempfontsize = $this->FontSizePt; - $this->SetFontSize($this->FontSizePt + $headsize); - $this->setStyle('b', true); - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - break; - } - } - } - - /** - * Process closing tags. - * @param string $tag tag name (in uppercase) - * @access private - */ - private function closedHTMLTagHandler($tag) { - //Closing tag - switch($tag) { - case 'td': - case 'th': { - $this->tdbegin = false; - $this->tdwidth = 0; - $this->tdheight = 0; - if($this->rtl) { - $this->tdalign = "R"; - } else { - $this->tdalign = "L"; - } - $this->tdbgcolor = false; - $this->SetFillColor($this->prevFillColor[0], $this->prevFillColor[1], $this->prevFillColor[2]); - break; - } - case 'tr': { - $this->Ln(); - break; - } - case 'table': { - $this->tableborder=0; - break; - } - case 'strong': { - $this->setStyle('b', false); - break; - } - case 'em': { - $this->setStyle('i', false); - break; - } - case 'b': - case 'i': - case 'u': { - $this->setStyle($tag, false); - break; - } - case 'a': { - $this->HREF = ''; - break; - } - case 'sup': { - $currentFontSize = $this->FontSize; - $this->SetFontSize($this->tempfontsize); - $this->tempfontsize = $this->FontSizePt; - $this->SetXY($this->GetX(), $this->GetY() - (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); - break; - } - case 'sub': { - $currentFontSize = $this->FontSize; - $this->SetFontSize($this->tempfontsize); - $this->tempfontsize = $this->FontSizePt; - $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); - break; - } - case 'small': { - $currentFontSize = $this->FontSize; - $this->SetFontSize($this->tempfontsize); - $this->tempfontsize = $this->FontSizePt; - $this->SetXY($this->GetX(), $this->GetY() - (($this->FontSize - $currentFontSize)/3)); - break; - } - case 'font': { - if ($this->issetcolor == true) { - $this->SetTextColor($this->prevTextColor[0], $this->prevTextColor[1], $this->prevTextColor[2]); - } - if ($this->issetfont) { - $this->FontFamily = $this->prevFontFamily; - $this->FontStyle = $this->prevFontStyle; - $this->SetFont($this->FontFamily); - $this->issetfont = false; - } - $currentFontSize = $this->FontSize; - $this->SetFontSize($this->tempfontsize); - $this->tempfontsize = $this->FontSizePt; - //$this->TextColor = $this->prevTextColor; - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - break; - } - case 'p': { - $this->Ln(); - $this->Ln(); - break; - } - case 'ul': - case 'ol': { - $this->Ln(); - $this->Ln(); - break; - } - case 'li': { - $this->lispacer = ""; - break; - } - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': { - $currentFontSize = $this->FontSize; - $this->SetFontSize($this->tempfontsize); - $this->tempfontsize = $this->FontSizePt; - $this->setStyle('b', false); - $this->Ln(); - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - break; - } - default : { - break; - } - } - $this->tmprtl = false; - } - - /** - * Sets font style. - * @param string $tag tag name (in lowercase) - * @param boolean $enable - * @access private - */ - private function setStyle($tag, $enable) { - //Modify style and select corresponding font - $this->$tag += ($enable ? 1 : -1); - $style=''; - foreach(array('b', 'i', 'u') as $s) { - if($this->$s > 0) { - $style .= $s; - } - } - $this->SetFont('', $style); - } - - /** - * Output anchor link. - * @param string $url link URL - * @param string $name link name - * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. - * @access public - */ - public function addHtmlLink($url, $name, $fill=0) { - //Put a hyperlink - $this->SetTextColor(0, 0, 255); - $this->setStyle('u', true); - $this->Write($this->lasth, $name, $url, $fill); - $this->setStyle('u', false); - $this->SetTextColor(0); - } - - /** - * Returns an associative array (keys: R,G,B) from - * a hex html code (e.g. #3FE5AA). - * @param string $color hexadecimal html color [#rrggbb] - * @return array - * @access private - */ - private function convertColorHexToDec($color = "#000000"){ - $tbl_color = array(); - $tbl_color['R'] = hexdec(substr($color, 1, 2)); - $tbl_color['G'] = hexdec(substr($color, 3, 2)); - $tbl_color['B'] = hexdec(substr($color, 5, 2)); - return $tbl_color; - } - - /** - * Converts pixels to millimeters in 72 dpi. - * @param int $px pixels - * @return float millimeters - * @access private - */ - private function pixelsToMillimeters($px){ - return $px * 25.4 / 72; - } - - /** - * Reverse function for htmlentities. - * Convert entities in UTF-8. - * - * @param $text_to_convert Text to convert. - * @return string converted - */ - public function unhtmlentities($text_to_convert) { - return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); - } - - // ENCRYPTION METHODS ---------------------------------- - // SINCE 2.0.000 (2008-01-02) - /** - * Compute encryption key depending on object number where the encrypted data is stored - * @param int $n object number - * @since 2.0.000 (2008-01-02) - */ - protected function _objectkey($n) { - return substr($this->_md5_16($this->encryption_key.pack('VXxx',$n)),0,10); - } - - /** - * Put encryption on PDF document - * @since 2.0.000 (2008-01-02) - */ - protected function _putencryption() { - $this->_out('/Filter /Standard'); - $this->_out('/V 1'); - $this->_out('/R 2'); - $this->_out('/O ('.$this->_escape($this->Ovalue).')'); - $this->_out('/U ('.$this->_escape($this->Uvalue).')'); - $this->_out('/P '.$this->Pvalue); - } - - /** - * Returns the input text exrypted using RC4 algorithm and the specified key. - * RC4 is the standard encryption algorithm used in PDF format - * @param string $key encryption key - * @param String $text input text to be encrypted - * @return String encrypted text - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - protected function _RC4($key, $text) { - if ($this->last_rc4_key != $key) { - $k = str_repeat($key, 256/strlen($key)+1); - $rc4 = range(0,255); - $j = 0; - for ($i=0; $i<256; $i++) { - $t = $rc4[$i]; - $j = ($j + $t + ord($k{$i})) % 256; - $rc4[$i] = $rc4[$j]; - $rc4[$j] = $t; - } - $this->last_rc4_key = $key; - $this->last_rc4_key_c = $rc4; - } else { - $rc4 = $this->last_rc4_key_c; - } - $len = strlen($text); - $a = 0; - $b = 0; - $out = ''; - for ($i=0; $i<$len; $i++) { - $a = ($a+1)%256; - $t= $rc4[$a]; - $b = ($b+$t)%256; - $rc4[$a] = $rc4[$b]; - $rc4[$b] = $t; - $k = $rc4[($rc4[$a]+$rc4[$b])%256]; - $out.=chr(ord($text{$i}) ^ $k); - } - return $out; - } - - /** - * Encrypts a string using MD5 and returns it's value as a binary string. - * @param string $str input string - * @return String MD5 encrypted binary string - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - protected function _md5_16($str) { - return pack('H*',md5($str)); - } - - /** - * Compute O value (used for RC4 encryption) - * @param String $user_pass user password - * @param String $owner_pass user password - * @return String O value - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - protected function _Ovalue($user_pass, $owner_pass) { - $tmp = $this->_md5_16($owner_pass); - $owner_RC4_key = substr($tmp,0,5); - return $this->_RC4($owner_RC4_key, $user_pass); - } - - /** - * Compute U value (used for RC4 encryption) - * @return String U value - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - protected function _Uvalue() { - return $this->_RC4($this->encryption_key, $this->padding); - } - - /** - * Compute encryption key - * @param String $user_pass user password - * @param String $owner_pass user password - * @param String $protection protection type - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - protected function _generateencryptionkey($user_pass, $owner_pass, $protection) { - // Pad passwords - $user_pass = substr($user_pass.$this->padding,0,32); - $owner_pass = substr($owner_pass.$this->padding,0,32); - // Compute O value - $this->Ovalue = $this->_Ovalue($user_pass,$owner_pass); - // Compute encyption key - $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF"); - $this->encryption_key = substr($tmp,0,5); - // Compute U value - $this->Uvalue = $this->_Uvalue(); - // Compute P value - $this->Pvalue = -(($protection^255)+1); - } - - /** - * Set document protection - * The permission array is composed of values taken from the following ones: - * - copy: copy text and images to the clipboard - * - print: print the document - * - modify: modify it (except for annotations and forms) - * - annot-forms: add annotations and forms - * Remark: the protection against modification is for people who have the full Acrobat product. - * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access. - * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts. - * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms) - * @param String $user_pass user password. Empty by default. - * @param String $owner_pass owner password. If not specified, a random value is used. - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - public function SetProtection($permissions=array(),$user_pass='',$owner_pass=null) { - $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32); - $protection = 192; - foreach($permissions as $permission) { - if (!isset($options[$permission])) { - $this->Error('Incorrect permission: '.$permission); - } - $protection += $options[$permission]; - } - if ($owner_pass === null) { - $owner_pass = uniqid(rand()); - } - $this->encrypted = true; - $this->_generateencryptionkey($user_pass, $owner_pass, $protection); - } - - // END OF ENCRYPTION FUNCTIONS ------------------------- - - // START TRANSFORMATIONS SECTION ----------------------- - // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni - - /** - * Starts a 2D tranformation saving current graphic state. - * This function must be called before scaling, mirroring, translation, rotation and skewing. - * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function StartTransform() { - $this->_out('q'); - } - - /** - * Stops a 2D tranformation restoring previous graphic state. - * This function must be called after scaling, mirroring, translation, rotation and skewing. - * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function StopTransform() { - $this->_out('Q'); - } - /** - * Horizontal Scaling. - * @param float $s_x scaling factor for width as percent. 0 is not allowed. - * @param int $x abscissa of the scaling center. Default is current x position - * @param int $y ordinate of the scaling center. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function ScaleX($s_x, $x='', $y=''){ - $this->Scale($s_x, 100, $x, $y); - } - - /** - * Vertical Scaling. - * @param float $s_y scaling factor for height as percent. 0 is not allowed. - * @param int $x abscissa of the scaling center. Default is current x position - * @param int $y ordinate of the scaling center. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function ScaleY($s_y, $x='', $y=''){ - $this->Scale(100, $s_y, $x, $y); - } - - /** - * Vertical and horizontal proportional Scaling. - * @param float $s scaling factor for width and height as percent. 0 is not allowed. - * @param int $x abscissa of the scaling center. Default is current x position - * @param int $y ordinate of the scaling center. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function ScaleXY($s, $x='', $y=''){ - $this->Scale($s, $s, $x, $y); - } - - /** - * Vertical and horizontal non-proportional Scaling. - * @param float $s_x scaling factor for width as percent. 0 is not allowed. - * @param float $s_y scaling factor for height as percent. 0 is not allowed. - * @param int $x abscissa of the scaling center. Default is current x position - * @param int $y ordinate of the scaling center. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function Scale($s_x, $s_y, $x='', $y=''){ - if($x === '') { - $x=$this->x; - } - if($y === '') { - $y=$this->y; - } - if($this->rtl) { - $x = $this->w - $x; - } - if($s_x == 0 OR $s_y == 0) - $this->Error('Please use values unequal to zero for Scaling'); - $y=($this->h-$y)*$this->k; - $x*=$this->k; - //calculate elements of transformation matrix - $s_x/=100; - $s_y/=100; - $tm[0]=$s_x; - $tm[1]=0; - $tm[2]=0; - $tm[3]=$s_y; - $tm[4]=$x*(1-$s_x); - $tm[5]=$y*(1-$s_y); - //scale the coordinate system - $this->Transform($tm); - } - - /** - * Horizontal Mirroring. - * @param int $x abscissa of the point. Default is current x position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function MirrorH($x=''){ - $this->Scale(-100, 100, $x); - } - - /** - * Verical Mirroring. - * @param int $y ordinate of the point. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function MirrorV($y=''){ - $this->Scale(100, -100, '', $y); - } - - /** - * Point reflection mirroring. - * @param int $x abscissa of the point. Default is current x position - * @param int $y ordinate of the point. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function MirrorP($x='',$y=''){ - $this->Scale(-100, -100, $x, $y); - } - - /** - * Reflection against a straight line through point (x, y) with the gradient angle (angle). - * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line). - * @param int $x abscissa of the point. Default is current x position - * @param int $y ordinate of the point. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function MirrorL($angle=0, $x='',$y=''){ - $this->Scale(-100, 100, $x, $y); - $this->Rotate(-2*($angle-90),$x,$y); - } - - /** - * Translate graphic object horizontally. - * @param int $t_x movement to the right - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function TranslateX($t_x){ - $this->Translate($t_x, 0); - } - - /** - * Translate graphic object vertically. - * @param int $t_y movement to the bottom - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function TranslateY($t_y){ - $this->Translate(0, $t_y, $x, $y); - } - - /** - * Translate graphic object horizontally and vertically. - * @param int $t_x movement to the right - * @param int $t_y movement to the bottom - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function Translate($t_x, $t_y){ - if($this->rtl) { - $t_x = -$t_x; - } - //calculate elements of transformation matrix - $tm[0]=1; - $tm[1]=0; - $tm[2]=0; - $tm[3]=1; - $tm[4]=$t_x*$this->k; - $tm[5]=-$t_y*$this->k; - //translate the coordinate system - $this->Transform($tm); - } - - /** - * Rotate object. - * @param float $angle angle in degrees for counter-clockwise rotation - * @param int $x abscissa of the rotation center. Default is current x position - * @param int $y ordinate of the rotation center. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function Rotate($angle, $x='', $y=''){ - if($x === '') { - $x=$this->x; - } - if($y === '') { - $y=$this->y; - } - if($this->rtl) { - $x = $this->w - $x; - $angle = -$angle; - } - $y=($this->h-$y)*$this->k; - $x*=$this->k; - //calculate elements of transformation matrix - $tm[0]=cos(deg2rad($angle)); - $tm[1]=sin(deg2rad($angle)); - $tm[2]=-$tm[1]; - $tm[3]=$tm[0]; - $tm[4]=$x+$tm[1]*$y-$tm[0]*$x; - $tm[5]=$y-$tm[0]*$y-$tm[1]*$x; - //rotate the coordinate system around ($x,$y) - $this->Transform($tm); - } - - /** - * Skew horizontally. - * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) - * @param int $x abscissa of the skewing center. default is current x position - * @param int $y ordinate of the skewing center. default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function SkewX($angle_x, $x='', $y=''){ - $this->Skew($angle_x, 0, $x, $y); - } - - /** - * Skew vertically. - * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) - * @param int $x abscissa of the skewing center. default is current x position - * @param int $y ordinate of the skewing center. default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function SkewY($angle_y, $x='', $y=''){ - $this->Skew(0, $angle_y, $x, $y); - } - - /** - * Skew. - * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) - * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) - * @param int $x abscissa of the skewing center. default is current x position - * @param int $y ordinate of the skewing center. default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function Skew($angle_x, $angle_y, $x='', $y=''){ - if($x === '') { - $x=$this->x; - } - if($y === '') { - $y=$this->y; - } - if($this->rtl) { - $x = $this->w - $x; - $angle_x = -$angle_x; - } - if($angle_x <= -90 OR $angle_x >= 90 OR $angle_y <= -90 OR $angle_y >= 90) - $this->Error('Please use values between -90� and 90� for skewing'); - $x*=$this->k; - $y=($this->h-$y)*$this->k; - //calculate elements of transformation matrix - $tm[0]=1; - $tm[1]=tan(deg2rad($angle_y)); - $tm[2]=tan(deg2rad($angle_x)); - $tm[3]=1; - $tm[4]=-$tm[2]*$y; - $tm[5]=-$tm[1]*$x; - //skew the coordinate system - $this->Transform($tm); - } - - /** - * Apply graphic transformations. - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - protected function Transform($tm){ - $this->_out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', $tm[0],$tm[1],$tm[2],$tm[3],$tm[4],$tm[5])); - } - - // END TRANSFORMATIONS SECTION ------------------------- - - - // START GRAPHIC FUNCTIONS SECTION --------------------- - // The following section is based on the code provided by David Hernandez Sanz - - /** - * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page. - * @param float $width The width. - * @since 1.0 - * @see Line(), Rect(), Cell(), MultiCell() - */ - public function SetLineWidth($width) { - //Set line width - $this->LineWidth=$width; - if($this->page>0) { - $this->_out(sprintf('%.2f w',$width*$this->k)); - } - } - - /** - * Returns the current the line width. - * @return int Line width - * @since 2.1.000 (2008-01-07) - * @see Line(), SetLineWidth() - */ - public function GetLineWidth() { - return $this->LineWidth; - } - - /** - * Set line style. - * - * @param array $style Line style. Array with keys among the following: - * <ul> - * <li>width (float): Width of the line in user units.</li> - * <li>cap (string): Type of cap to put on the line. Possible values are: - * butt, round, square. The difference between "square" and "butt" is that - * "square" projects a flat end past the end of the line.</li> - * <li>join (string): Type of join. Possible values are: miter, round, - * bevel.</li> - * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with - * series of length values, which are the lengths of the on and off dashes. - * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on, - * 1 off, 2 on, 1 off, ...</li> - * <li>phase (integer): Modifier on the dash pattern which is used to shift - * the point at which the pattern starts.</li> - * <li>color (array): Draw color. Format: array(red, green, blue).</li> - * </ul> - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function SetLineStyle($style) { - extract($style); - if (isset($width)) { - $width_prev = $this->LineWidth; - $this->SetLineWidth($width); - $this->LineWidth = $width_prev; - } - if (isset($cap)) { - $ca = array("butt" => 0, "round"=> 1, "square" => 2); - if (isset($ca[$cap])) { - $this->_out($ca[$cap] . " J"); - } - } - if (isset($join)) { - $ja = array("miter" => 0, "round" => 1, "bevel" => 2); - if (isset($ja[$join])) { - $this->_out($ja[$join] . " j"); - } - } - if (isset($dash)) { - $dash_string = ""; - if ($dash) { - // phpMyAdmin change - if (preg_match("/^.+,/", $dash)) { - $tab = explode(",", $dash); - } else { - $tab = array($dash); - } - $dash_string = ""; - foreach ($tab as $i => $v) { - if ($i) { - $dash_string .= " "; - } - $dash_string .= sprintf("%.2f", $v); - } - } - if (!isset($phase) OR !$dash) { - $phase = 0; - } - $this->_out(sprintf("[%s] %.2f d", $dash_string, $phase)); - } - if (isset($color)) { - list($r, $g, $b) = $color; - $this->SetDrawColor($r, $g, $b); - } - } - - /* - * Set a draw point. - * @param float $x Abscissa of point. - * @param float $y Ordinate of point. - * @access private - * @since 2.1.000 (2008-01-08) - */ - protected function _outPoint($x, $y) { - if($this->rtl) { - $x = $this->w - $x; - } - $this->_out(sprintf("%.2f %.2f m", $x * $this->k, ($this->h - $y) * $this->k)); - } - - /* - * Draws a line from last draw point. - * @param float $x Abscissa of end point. - * @param float $y Ordinate of end point. - * @access private - * @since 2.1.000 (2008-01-08) - */ - protected function _outLine($x, $y) { - if($this->rtl) { - $x = $this->w - $x; - } - $this->_out(sprintf("%.2f %.2f l", $x * $this->k, ($this->h - $y) * $this->k)); - } - - /** - * Draws a rectangle. - * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). - * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). - * @param float $w Width. - * @param float $h Height. - * @param string $op options - * @access protected - * @since 2.1.000 (2008-01-08) - */ - protected function _outRect($x, $y, $w, $h, $op) { - if($this->rtl) { - $x = $this->w - $x - $w; - } - $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op)); - } - - /* - * Draws a Bezier curve from last draw point. - * The Bezier curve is a tangent to the line between the control points at either end of the curve. - * @param float $x1 Abscissa of control point 1. - * @param float $y1 Ordinate of control point 1. - * @param float $x2 Abscissa of control point 2. - * @param float $y2 Ordinate of control point 2. - * @param float $x3 Abscissa of end point. - * @param float $y3 Ordinate of end point. - * @access private - * @since 2.1.000 (2008-01-08) - */ - protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { - if($this->rtl) { - $x1 = $this->w - $x1; - $x2 = $this->w - $x2; - $x3 = $this->w - $x3; - } - $this->_out(sprintf("%.2f %.2f %.2f %.2f %.2f %.2f c", $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); - } - - /** - * Draws a line between two points. - * @param float $x1 Abscissa of first point. - * @param float $y1 Ordinate of first point. - * @param float $x2 Abscissa of second point. - * @param float $y2 Ordinate of second point. - * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @access public - * @since 1.0 - * @see SetLineWidth(), SetDrawColor(), SetLineStyle() - */ - public function Line($x1, $y1, $x2, $y2, $style = array()) { - if ($style) { - $this->SetLineStyle($style); - } - $this->_outPoint($x1, $y1); - $this->_outLine($x2, $y2); - $this->_out(" S"); - } - - /** - * Draws a rectangle. - * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). - * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). - * @param float $w Width. - * @param float $h Height. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $border_style Border style of rectangle. Array with keys among the following: - * <ul> - * <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li> - * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li> - * </ul> - * If a key is not present or is null, not draws the border. Default value: default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @since 1.0 - * @see SetLineStyle() - */ - public function Rect($x, $y, $w, $h, $style='', $border_style = array(), $fill_color = array()) { - if (!(false === strpos($style, "F")) AND $fill_color) { - list($r, $g, $b) = $fill_color; - $this->SetFillColor($r, $g, $b); - } - switch ($style) { - case "F": { - $op='f'; - $border_style = array(); - $this->_outRect($x, $y, $w, $h, $op); - break; - } - case "DF": - case "FD": { - if (!$border_style OR isset($border_style["all"])) { - $op='B'; - if (isset($border_style["all"])) { - $this->SetLineStyle($border_style["all"]); - $border_style = array(); - } - } else { - $op='f'; - } - $this->_outRect($x, $y, $w, $h, $op); - break; - } - default: { - $op='S'; - if (!$border_style OR isset($border_style["all"])) { - if (isset($border_style["all"]) && $border_style["all"]) { - $this->SetLineStyle($border_style["all"]); - $border_style = array(); - } - $this->_outRect($x, $y, $w, $h, $op); - } - break; - } - } - if ($border_style) { - $border_style2 = array(); - foreach ($border_style as $line => $value) { - $lenght = strlen($line); - for ($i = 0; $i < $lenght; $i++) { - $border_style2[$line[$i]] = $value; - } - } - $border_style = $border_style2; - if (isset($border_style["L"]) && $border_style["L"]) { - $this->Line($x, $y, $x, $y + $h, $border_style["L"]); - } - if (isset($border_style["T"]) && $border_style["T"]) { - $this->Line($x, $y, $x + $w, $y, $border_style["T"]); - } - if (isset($border_style["R"]) && $border_style["R"]) { - $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style["R"]); - } - if (isset($border_style["B"]) && $border_style["B"]) { - $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style["B"]); - } - } - } - - - /** - * Draws a Bezier curve. - * The Bezier curve is a tangent to the line between the control points at - * either end of the curve. - * @param float $x0 Abscissa of start point. - * @param float $y0 Ordinate of start point. - * @param float $x1 Abscissa of control point 1. - * @param float $y1 Ordinate of control point 1. - * @param float $x2 Abscissa of control point 2. - * @param float $y2 Ordinate of control point 2. - * @param float $x3 Abscissa of end point. - * @param float $y3 Ordinate of end point. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @see SetLineStyle() - * @since 2.1.000 (2008-01-08) - */ - public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style = "", $line_style = array(), $fill_color = array()) { - if (!(false === strpos($style, "F")) AND $fill_color) { - list($r, $g, $b) = $fill_color; - $this->SetFillColor($r, $g, $b); - } - switch ($style) { - case "F": { - $op = "f"; - $line_style = array(); - break; - } - case "FD": - case "DF": { - $op = "B"; - break; - } - default: { - $op = "S"; - break; - } - } - if ($line_style) { - $this->SetLineStyle($line_style); - } - $this->_outPoint($x0, $y0); - $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); - $this->_out($op); - } - - /** - * Draws an ellipse. - * An ellipse is formed from n Bezier curves. - * @param float $x0 Abscissa of center point. - * @param float $y0 Ordinate of center point. - * @param float $rx Horizontal radius. - * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0. - * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. - * @param float $astart: Angle start of draw line. Default value: 0. - * @param float $afinish: Angle finish of draw line. Default value: 360. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * <li>C: Draw close.</li> - * </ul> - * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @param integer $nc Number of curves used in ellipse. Default value: 8. - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function Ellipse($x0, $y0, $rx, $ry = 0, $angle = 0, $astart = 0, $afinish = 360, $style = "", $line_style = array(), $fill_color = array(), $nc = 8) { - if ($angle) { - $this->StartTransform(); - $this->Rotate($angle, $x0, $y0); - $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc); - $this->StopTransform(); - return; - } - if ($rx) { - if (!(false === strpos($style, "F")) AND $fill_color) { - list($r, $g, $b) = $fill_color; - $this->SetFillColor($r, $g, $b); - } - switch ($style) { - case "F": { - $op = "f"; - $line_style = array(); - break; - } - case "FD": - case "DF": { - $op = "B"; - break; - } - case "C": { - $op = "s"; // Small "s" signifies closing the path as well - break; - } - default: { - $op = "S"; - break; - } - } - if ($line_style) { - $this->SetLineStyle($line_style); - } - if (!$ry) { - $ry = $rx; - } - $rx *= $this->k; - $ry *= $this->k; - if ($nc < 2){ - $nc = 2; - } - $astart = deg2rad((float) $astart); - $afinish = deg2rad((float) $afinish); - $total_angle = $afinish - $astart; - $dt = $total_angle / $nc; - $dtm = $dt/3; - $x0 *= $this->k; - $y0 = ($this->h - $y0) * $this->k; - $t1 = $astart; - $a0 = $x0 + ($rx * cos($t1)); - $b0 = $y0 + ($ry * sin($t1)); - $c0 = -$rx * sin($t1); - $d0 = $ry * cos($t1); - $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k)); - for ($i = 1; $i <= $nc; $i++) { - // Draw this bit of the total curve - $t1 = ($i * $dt) + $astart; - $a1 = $x0 + ($rx * cos($t1)); - $b1 = $y0 + ($ry * sin($t1)); - $c1 = -$rx * sin($t1); - $d1 = $ry * cos($t1); - $this->_outCurve(($a0 + ($c0 * $dtm)) / $this->k, $this->h - (($b0 + ($d0 * $dtm)) / $this->k), ($a1 - ($c1 * $dtm)) / $this->k, $this->h - (($b1 - ($d1 * $dtm)) / $this->k), $a1 / $this->k, $this->h - ($b1 / $this->k)); - $a0 = $a1; - $b0 = $b1; - $c0 = $c1; - $d0 = $d1; - } - $this->_out($op); - } - } - - /** - * Draws a circle. - * A circle is formed from n Bezier curves. - * @param float $x0 Abscissa of center point. - * @param float $y0 Ordinate of center point. - * @param float $r Radius. - * @param float $astart: Angle start of draw line. Default value: 0. - * @param float $afinish: Angle finish of draw line. Default value: 360. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * <li>C: Draw close.</li> - * </ul> - * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @param integer $nc Number of curves used in circle. Default value: 8. - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function Circle($x0, $y0, $r, $astart = 0, $afinish = 360, $style = "", $line_style = array(), $fill_color = array(), $nc = 8) { - $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc); - } - - /** - * Draws a polygon. - * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $line_style Line style of polygon. Array with keys among the following: - * <ul> - * <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li> - * <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li> - * </ul> - * If a key is not present or is null, not draws the line. Default value is default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function Polygon($p, $style = "", $line_style = array(), $fill_color = array()) { - $np = count($p) / 2; - if (!(false === strpos($style, "F")) AND $fill_color) { - list($r, $g, $b) = $fill_color; - $this->SetFillColor($r, $g, $b); - } - switch ($style) { - case "F": { - $line_style = array(); - $op = "f"; - break; - } - case "FD": - case "DF": { - $op = "B"; - break; - } - default: { - $op = "S"; - break; - } - } - $draw = true; - if ($line_style) { - if (isset($line_style["all"])) { - $this->SetLineStyle($line_style["all"]); - } - else { // 0 .. (np - 1), op = {B, S} - $draw = false; - if ("B" == $op) { - $op = "f"; - $this->_outPoint($p[0], $p[1]); - for ($i = 2; $i < ($np * 2); $i = $i + 2) { - $this->_outLine($p[$i], $p[$i + 1]); - } - $this->_outLine($p[0], $p[1]); - $this->_out($op); - } - $p[$np * 2] = $p[0]; - $p[($np * 2) + 1] = $p[1]; - for ($i = 0; $i < $np; $i++) { - if (isset($line_style[$i])) { - $this->Line($p[$i * 2], $p[($i * 2) + 1], $p[($i * 2) + 2], $p[($i * 2) + 3], $line_style[$i]); - } - } - } - } - if ($draw) { - $this->_outPoint($p[0], $p[1]); - for ($i = 2; $i < ($np * 2); $i = $i + 2) { - $this->_outLine($p[$i], $p[$i + 1]); - } - $this->_outLine($p[0], $p[1]); - $this->_out($op); - } - } - - /** - * Draws a regular polygon. - * @param float $x0 Abscissa of center point. - * @param float $y0 Ordinate of center point. - * @param float $r: Radius of inscribed circle. - * @param integer $ns Number of sides. - * @param float $angle Angle oriented (anti-clockwise). Default value: 0. - * @param boolean $draw_circle Draw inscribed circle or not. Default value: false. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $line_style Line style of polygon sides. Array with keys among the following: - * <ul> - * <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li> - * <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> - * </ul> - * If a key is not present or is null, not draws the side. Default value is default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function RegularPolygon($x0, $y0, $r, $ns, $angle = 0, $draw_circle = false, $style = "", $line_style = array(), $fill_color = array(), $circle_style = "", $circle_outLine_style = array(), $circle_fill_color = array()) { - if (3 > $ns) { - $ns = 3; - } - if ($draw_circle) { - $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); - } - $p = array(); - for ($i = 0; $i < $ns; $i++) { - $a = $angle + ($i * 360 / $ns); - $a_rad = deg2rad((float) $a); - $p[] = $x0 + ($r * sin($a_rad)); - $p[] = $y0 + ($r * cos($a_rad)); - } - $this->Polygon($p, $style, $line_style, $fill_color); - } - - /** - * Draws a star polygon - * @param float $x0 Abscissa of center point. - * @param float $y0 Ordinate of center point. - * @param float $r Radius of inscribed circle. - * @param integer $nv Number of vertices. - * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon). - * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. - * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $line_style Line style of polygon sides. Array with keys among the following: - * <ul> - * <li>all: Line style of all sides. Array like for - * {@link SetLineStyle SetLineStyle}.</li> - * <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> - * </ul> - * If a key is not present or is null, not draws the side. Default value is default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle = 0, $draw_circle = false, $style = "", $line_style = array(), $fill_color = array(), $circle_style = "", $circle_outLine_style = array(), $circle_fill_color = array()) { - if (2 > $nv) { - $nv = 2; - } - if ($draw_circle) { - $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); - } - $p2 = array(); - $visited = array(); - for ($i = 0; $i < $nv; $i++) { - $a = $angle + ($i * 360 / $nv); - $a_rad = deg2rad((float) $a); - $p2[] = $x0 + ($r * sin($a_rad)); - $p2[] = $y0 + ($r * cos($a_rad)); - $visited[] = false; - } - $p = array(); - $i = 0; - do { - $p[] = $p2[$i * 2]; - $p[] = $p2[($i * 2) + 1]; - $visited[$i] = true; - $i += $ng; - $i %= $nv; - } while (!$visited[$i]); - $this->Polygon($p, $style, $line_style, $fill_color); - } - - /** - * Draws a rounded rectangle. - * @param float $x Abscissa of upper-left corner. - * @param float $y Ordinate of upper-left corner. - * @param float $w Width. - * @param float $h Height. - * @param float $r Radius of the rounded corners. - * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left. Default value: all rounded corner ("1111"). - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function RoundedRect($x, $y, $w, $h, $r, $round_corner = "1111", $style = "", $border_style = array(), $fill_color = array()) { - if ("0000" == $round_corner) { // Not rounded - $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); - } else { // Rounded - if (!(false === strpos($style, "F")) AND $fill_color) { - list($red, $g, $b) = $fill_color; - $this->SetFillColor($red, $g, $b); - } - switch ($style) { - case "F": { - $border_style = array(); - $op = "f"; - break; - } - case "FD": - case "DF": { - $op = "B"; - break; - } - default: { - $op = "S"; - break; - } - } - if ($border_style) { - $this->SetLineStyle($border_style); - } - $MyArc = 4 / 3 * (sqrt(2) - 1); - $this->_outPoint($x + $r, $y); - $xc = $x + $w - $r; - $yc = $y + $r; - $this->_outLine($xc, $y); - if ($round_corner[0]) { - $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc); - } else { - $this->_outLine($x + $w, $y); - } - $xc = $x + $w - $r; - $yc = $y + $h - $r; - $this->_outLine($x + $w, $yc); - if ($round_corner[1]) { - $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r); - } else { - $this->_outLine($x + $w, $y + $h); - } - $xc = $x + $r; - $yc = $y + $h - $r; - $this->_outLine($xc, $y + $h); - if ($round_corner[2]) { - $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc); - } else { - $this->_outLine($x, $y + $h); - } - $xc = $x + $r; - $yc = $y + $r; - $this->_outLine($x, $yc); - if ($round_corner[3]) { - $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r); - } else { - $this->_outLine($x, $y); - $this->_outLine($x + $r, $y); - } - $this->_out($op); - } - } - - // END GRAPHIC FUNCTIONS SECTION ----------------------- - - // BIDIRECTIONAL TEXT SECTION -------------------------- - - /** - * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). - * @param string $str string to manipulate. - * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR - * @return string - * @author Nicola Asuni - * @since 2.1.000 (2008-01-08) - */ - protected function utf8StrRev($str, $setbom=false, $forcertl=false) { - global $unicode,$unicode_mirror, $unicode_arlet; - require_once(dirname(__FILE__).'/unicode_data.php'); - - // paragraph embedding level - $pel = 0; - // max level - $maxlevel = 0; - - // check if string contains arabic text - if (preg_match(K_RE_PATTERN_ARABIC, $str)) { - $arabic = true; - } else { - $arabic = false; - } - - // check if string contains RTL text - if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) { - return $this->UTF8ToUTF16BE($str, false); - } - - // convert string to array of unicode chars - $ta = $this->UTF8StringToArray($str); - // get number of chars - $numchars = count($ta); - - if ($forcertl == 'R') { - $pel = 1; - } elseif ($forcertl == 'L') { - $pel = 0; - } else { - // P2. In each paragraph, find the first character of type L, AL, or R. - // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero. - for ($i=0; $i < $numchars; $i++) { - $type = $unicode[$ta[$i]]; - if ($type == 'L') { - $pel = 0; - break; - } elseif (($type == 'AL') OR ($type == 'R')) { - $pel = 1; - break; - } - } - } - - // Current Embedding Level - $cel = $pel; - // directional override status - $dos = 'N'; - $remember = array(); - // start-of-level-run - $sor = $pel % 2 ? 'R' : 'L'; - $eor = $sor; - - //$levels = array(array('level' => $cel, 'sor' => $sor, 'eor' => '', 'chars' => array())); - //$current_level = &$levels[count( $levels )-1]; - - // Array of characters data - $chardata = Array(); - - // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase. - // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached. - for ($i=0; $i < $numchars; $i++) { - if ($ta[$i] == K_RLE) { - // X2. With each RLE, compute the least greater odd embedding level. - // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. - // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. - $next_level = $cel + ($cel % 2) + 1; - if ($next_level < 62) { - $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos); - $cel = $next_level; - $dos = 'N'; - $sor = $eor; - $eor = $cel % 2 ? 'R' : 'L'; - } - } elseif ($ta[$i] == K_LRE) { - // X3. With each LRE, compute the least greater even embedding level. - // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. - // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. - $next_level = $cel + 2 - ($cel % 2); - if ( $next_level < 62 ) { - $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos); - $cel = $next_level; - $dos = 'N'; - $sor = $eor; - $eor = $cel % 2 ? 'R' : 'L'; - } - } elseif ($ta[$i] == K_RLO) { - // X4. With each RLO, compute the least greater odd embedding level. - // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left. - // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. - $next_level = $cel + ($cel % 2) + 1; - if ($next_level < 62) { - $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos); - $cel = $next_level; - $dos = 'R'; - $sor = $eor; - $eor = $cel % 2 ? 'R' : 'L'; - } - } elseif ($ta[$i] == K_LRO) { - // X5. With each LRO, compute the least greater even embedding level. - // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right. - // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. - $next_level = $cel + 2 - ($cel % 2); - if ( $next_level < 62 ) { - $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos); - $cel = $next_level; - $dos = 'L'; - $sor = $eor; - $eor = $cel % 2 ? 'R' : 'L'; - } - } elseif ($ta[$i] == K_PDF) { - // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override. - if (count($remember)) { - $last = count($remember ) - 1; - if (($remember[$last]['num'] == K_RLE) OR - ($remember[$last]['num'] == K_LRE) OR - ($remember[$last]['num'] == K_RLO) OR - ($remember[$last]['num'] == K_LRO)) { - $match = array_pop($remember); - $cel = $match['cel']; - $dos = $match['dos']; - $sor = $eor; - $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L'; - } - } - } elseif (($ta[$i] != K_RLE) AND - ($ta[$i] != K_LRE) AND - ($ta[$i] != K_RLO) AND - ($ta[$i] != K_LRO) AND - ($ta[$i] != K_PDF)) { - // X6. For all types besides RLE, LRE, RLO, LRO, and PDF: - // a. Set the level of the current character to the current embedding level. - // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status. - if ($dos != 'N') { - $chardir = $dos; - } else { - $chardir = $unicode[$ta[$i]]; - } - // stores string characters and other information - $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor); - } - } // end for each char - - // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding. - // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes. - // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the “other” run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L. - - // 3.3.3 Resolving Weak Types - // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used. - // Nonspacing marks are now resolved based on the previous characters. - $numchars = count($chardata); - - // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor. - $prevlevel = -1; // track level changes - $levcount = 0; // counts consecutive chars at the same level - for ($i=0; $i < $numchars; $i++) { - if ($chardata[$i]['type'] == 'NSM') { - if ($levcount) { - $chardata[$i]['type'] = $chardata[$i]['sor']; - } elseif ($i > 0) { - $chardata[$i]['type'] = $chardata[($i-1)]['type']; - } - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if ($chardata[$i]['char'] == 'EN') { - for ($j=$levcount; $j >= 0; $j--) { - if ($chardata[$j]['type'] == 'AL') { - $chardata[$i]['type'] = 'AN'; - } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) { - break; - } - } - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // W3. Change all ALs to R. - for ($i=0; $i < $numchars; $i++) { - if ($chardata[$i]['type'] == 'AL') { - $chardata[$i]['type'] = 'R'; - } - } - - // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { - if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { - $chardata[$i]['type'] = 'EN'; - } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { - $chardata[$i]['type'] = 'EN'; - } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) { - $chardata[$i]['type'] = 'AN'; - } - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if($chardata[$i]['type'] == 'ET') { - if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) { - $chardata[$i]['type'] = 'EN'; - } else { - $j = $i+1; - while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) { - if ($chardata[$j]['type'] == 'EN') { - $chardata[$i]['type'] = 'EN'; - break; - } elseif ($chardata[$j]['type'] != 'ET') { - break; - } - $j++; - } - } - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // W6. Otherwise, separators and terminators change to Other Neutral. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) { - $chardata[$i]['type'] = 'ON'; - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if ($chardata[$i]['char'] == 'EN') { - for ($j=$levcount; $j >= 0; $j--) { - if ($chardata[$j]['type'] == 'L') { - $chardata[$i]['type'] = 'L'; - } elseif ($chardata[$j]['type'] == 'R') { - break; - } - } - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { - if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { - $chardata[$i]['type'] = 'L'; - } elseif (($chardata[$i]['type'] == 'N') AND - (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND - (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { - $chardata[$i]['type'] = 'R'; - } elseif ($chardata[$i]['type'] == 'N') { - // N2. Any remaining neutrals take the embedding direction - $chardata[$i]['type'] = $chardata[$i]['sor']; - } - } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { - // first char - if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { - $chardata[$i]['type'] = 'L'; - } elseif (($chardata[$i]['type'] == 'N') AND - (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND - (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { - $chardata[$i]['type'] = 'R'; - } elseif ($chardata[$i]['type'] == 'N') { - // N2. Any remaining neutrals take the embedding direction - $chardata[$i]['type'] = $chardata[$i]['sor']; - } - } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) { - //last char - if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) { - $chardata[$i]['type'] = 'L'; - } elseif (($chardata[$i]['type'] == 'N') AND - (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND - (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) { - $chardata[$i]['type'] = 'R'; - } elseif ($chardata[$i]['type'] == 'N') { - // N2. Any remaining neutrals take the embedding direction - $chardata[$i]['type'] = $chardata[$i]['sor']; - } - } elseif ($chardata[$i]['type'] == 'N') { - // N2. Any remaining neutrals take the embedding direction - $chardata[$i]['type'] = $chardata[$i]['sor']; - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels. - // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level. - for ($i=0; $i < $numchars; $i++) { - $odd = $chardata[$i]['level'] % 2; - if ($odd) { - if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){ - $chardata[$i]['level'] += 1; - } - } else { - if ($chardata[$i]['type'] == 'R') { - $chardata[$i]['level'] += 1; - } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){ - $chardata[$i]['level'] += 2; - } - } - $maxlevel = max($chardata[$i]['level'],$maxlevel); - } - - // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level: - // 1. Segment separators, - // 2. Paragraph separators, - // 3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and - // 4. Any sequence of white space characters at the end of the line. - for ($i=0; $i < $numchars; $i++) { - if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) { - $chardata[$i]['level'] = $pel; - } elseif ($chardata[$i]['type'] == 'WS') { - $j = $i+1; - while ($j < $numchars) { - if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR - (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) { - $chardata[$i]['level'] = $pel;; - break; - } elseif ($chardata[$j]['type'] != 'WS') { - break; - } - $j++; - } - } - } - - // Arabic Shaping - // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run. - if ($arabic) { - for ($i=0; $i < $numchars; $i++) { - if ($unicode[$chardata[$i]['char']] == 'AL') { - if (($i > 0) AND (($i+1) < $numchars) AND - ($unicode[$chardata[($i-1)]['char']] == 'AL') AND - ($unicode[$chardata[($i+1)]['char']] == 'AL') AND - ($chardata[($i-1)]['type'] == $chardata[$i]['type']) AND - ($chardata[($i+1)]['type'] == $chardata[$i]['type'])) { - // medial - if (isset($unicode_arlet[$chardata[$i]['char']][3])) { - $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][3]; - } - } elseif ((($i+1) < $numchars) AND - ($unicode[$chardata[($i+1)]['char']] == 'AL') AND - ($chardata[($i+1)]['type'] == $chardata[$i]['type'])) { - // initial - if (isset($unicode_arlet[$chardata[$i]['char']][2])) { - $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][2]; - } - } elseif (($i > 0) AND - ($unicode[$chardata[($i-1)]['char']] == 'AL') AND - ($chardata[($i-1)]['type'] == $chardata[$i]['type'])) { - // final - if (isset($unicode_arlet[$chardata[$i]['char']][1])) { - $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][1]; - } - } elseif (isset($unicode_arlet[$chardata[$i]['char']][0])) { - // isolated - $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][0]; - } - } - } - } - - // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher. - for ($j=$maxlevel; $j > 0; $j--) { - $ordarray = Array(); - $revarr = Array(); - $onlevel = false; - for ($i=0; $i < $numchars; $i++) { - if ($chardata[$i]['level'] >= $j) { - $onlevel = true; - if (isset($unicode_mirror[$chardata[$i]['char']])) { - // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true. - $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']]; - } - $revarr[] = $chardata[$i]; - } else { - if($onlevel) { - $revarr = array_reverse($revarr); - $ordarray = array_merge($ordarray, $revarr); - $revarr = Array(); - $onlevel = false; - } - $ordarray[] = $chardata[$i]; - } - } - if($onlevel) { - $revarr = array_reverse($revarr); - $ordarray = array_merge($ordarray, $revarr); - } - $chardata = $ordarray; - } - - $ordarray = array(); - for ($i=0; $i < $numchars; $i++) { - $ordarray[] = $chardata[$i]['char']; - } - - return $this->arrUTF8ToUTF16BE($ordarray, $setbom); - } - - // END OF BIDIRECTIONAL TEXT SECTION ------------------- - - /* - * Adds a bookmark. - * @param string $txt bookmark description. - * @param int $level bookmark level. - * @param float $y Ordinate of the boorkmark position (default = -1 = current position). - * @access public - * @author Olivier Plathey, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function Bookmark($txt, $level=0, $y=-1) { - if($y == -1) { - $y = $this->GetY(); - } - $this->outlines[]=array('t'=>$txt,'l'=>$level,'y'=>$y,'p'=>$this->PageNo()); - } - - /* - * Create a bookmark PDF string. - * @access private - * @author Olivier Plathey, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - private function _putbookmarks() { - $nb = count($this->outlines); - if($nb == 0) { - return; - } - $lru = array(); - $level = 0; - foreach($this->outlines as $i=>$o) { - if($o['l'] > 0) { - $parent = $lru[$o['l'] - 1]; - //Set parent and last pointers - $this->outlines[$i]['parent'] = $parent; - $this->outlines[$parent]['last'] = $i; - if($o['l'] > $level) { - //Level increasing: set first pointer - $this->outlines[$parent]['first'] = $i; - } - } else { - $this->outlines[$i]['parent']=$nb; - } - if($o['l']<=$level and $i>0) { - //Set prev and next pointers - $prev = $lru[$o['l']]; - $this->outlines[$prev]['next'] = $i; - $this->outlines[$i]['prev'] = $prev; - } - $lru[$o['l']] = $i; - $level = $o['l']; - } - //Outline items - $n = $this->n+1; - foreach($this->outlines as $i=>$o) { - $this->_newobj(); - $this->_out('<</Title '.$this->_textstring($o['t'])); - $this->_out('/Parent '.($n+$o['parent']).' 0 R'); - if(isset($o['prev'])) - $this->_out('/Prev '.($n+$o['prev']).' 0 R'); - if(isset($o['next'])) - $this->_out('/Next '.($n+$o['next']).' 0 R'); - if(isset($o['first'])) - $this->_out('/First '.($n+$o['first']).' 0 R'); - if(isset($o['last'])) - $this->_out('/Last '.($n+$o['last']).' 0 R'); - $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]',1+2*$o['p'],($this->h-$o['y'])*$this->k)); - $this->_out('/Count 0>>'); - $this->_out('endobj'); - } - //Outline root - $this->_newobj(); - $this->OutlineRoot=$this->n; - $this->_out('<</Type /Outlines /First '.$n.' 0 R'); - $this->_out('/Last '.($n+$lru[0]).' 0 R>>'); - $this->_out('endobj'); - } - - - // --- JAVASCRIPT - FORMS ------------------------------ - - /* - * Adds a javascript - * @access public - * @author Johannes Güntert, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function IncludeJS($script) { - $this->javascript .= $script; - } - - /* - * Create a javascript PDF string. - * @access private - * @author Johannes Güntert, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - private function _putjavascript() { - if (empty($this->javascript)) { - return; - } - $this->_newobj(); - $this->n_js = $this->n; - $this->_out('<<'); - $this->_out('/Names [(EmbeddedJS) '.($this->n+1).' 0 R ]'); - $this->_out('>>'); - $this->_out('endobj'); - $this->_newobj(); - $this->_out('<<'); - $this->_out('/S /JavaScript'); - $this->_out('/JS '.$this->_textstring($this->javascript)); - $this->_out('>>'); - $this->_out('endobj'); - } - - /* - * Convert color to javascript color. - * @param string $color color name or #RRGGBB - * @access private - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - private function _JScolor($color) { - static $aColors = array('transparent','black','white','red','green','blue','cyan','magenta','yellow','dkGray','gray','ltGray'); - if(substr($color,0,1) == '#') { - return sprintf("['RGB',%.3f,%.3f,%.3f]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255); - } - if(!in_array($color,$aColors)) { - $this->Error('Invalid color: '.$color); - } - return 'color.'.$color; - } - - /* - * Adds a javascript form field. - * @param string $type field type - * @param string $name field name - * @param int $x horizontal position - * @param int $y vertical position - * @param int $w width - * @param int $h height - * @param string $prop properties - * @access private - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - private function _addfield($type, $name, $x, $y, $w, $h, $prop) { - $k = $this->k; - $this->javascript .= sprintf("f=addField('%s','%s',%d,[%.2f,%.2f,%.2f,%.2f]);",$name,$type,$this->PageNo()-1,$x*$k,($this->h-$y)*$k+1,($x+$w)*$k,($this->h-$y-$h)*$k+1); - $this->javascript .= 'f.textSize='.$this->FontSizePt.';'; - if(isset($prop['value'])) { - $this->javascript .= "f.value='".addslashes($prop['value'])."';"; - } - if(isset($prop['TextColor'])) { - $this->javascript .= 'f.textColor='.$this->_JScolor($prop['TextColor']).';'; - } - if(isset($prop['FillColor'])) { - $this->javascript .= 'f.fillColor='.$this->_JScolor($prop['FillColor']).';'; - } - if(isset($prop['BorderColor'])) { - $this->javascript .= 'f.strokeColor='.$this->_JScolor($prop['BorderColor']).';'; - } - if(isset($prop['BorderStyle'])) { - $this->javascript .= "f.borderStyle='".$prop['BorderStyle']."';"; - } - $this->x+=$w; - } - - /* - * Creates a text field - * @param string $name field name - * @param int $w width - * @param int $h height - * @param string $prop properties. The value property allows to set the initial value. The multiline property allows to define the field as multiline. - * @access public - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function TextField($name, $w, $h, $prop=array()) { - $this->_addfield('text',$name,$this->x,$this->y,$w,$h,$prop); - if(isset($prop['multiline']) AND $prop['multiline']) { - $this->javascript .= 'f.multiline=true;'; - } - } - - /* - * Creates a Combo-box field - * @param string $name field name - * @param int $w width - * @param int $h height - * @param array $values array containing the list of values. - * @param string $prop properties. - * @access public - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function ComboBox($name, $w, $h, $values, $prop=array()) { - $this->_addfield('combobox',$name,$this->x,$this->y,$w,$h,$prop); - $s = ''; - foreach($values as $value) { - $s .= "'".addslashes($value)."',"; - } - $this->javascript .= 'f.setItems(['.substr($s,0,-1).']);'; - } - - /* - * Creates a CheckBox field - * @param string $name field name - * @param int $w width - * @param boolean $checked define the initial state (default = false). - * @param string $prop properties. - * @access public - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function CheckBox($name, $w, $checked=false, $prop=array()) { - $prop['value'] = ($checked ? 'Yes' : 'Off'); - if(!isset($prop['BorderColor'])) { - $prop['BorderColor']='black'; - } - $this->_addfield('checkbox',$name,$this->x,$this->y,$w,$w,$prop); - } - - /* - * Creates a button field - * @param string $name field name - * @param int $w width - * @param int $h height - * @param string $caption caption. - * @param string $action action triggered by the button (JavaScript code). - * @param string $prop properties. - * @access public - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function Button($name, $w, $h, $caption, $action, $prop=array()) { - if(!isset($prop['BorderColor'])) { - $prop['BorderColor']='black'; - } - $prop['BorderStyle']='beveled'; - $this->_addfield('button',$name,$this->x,$this->y,$w,$h,$prop); - $this->javascript .= "f.buttonSetCaption('".addslashes($caption)."');"; - $this->javascript .= "f.setAction('MouseUp','".addslashes($action)."');"; - $this->javascript .= "f.highlight='push';"; - $this->javascript .= 'f.print=false;'; - } - - // END JAVASCRIPT - FORMS ------------------------------ - - } // END OF TCPDF CLASS - - //Handle special IE contype request - if(isset($_SERVER['HTTP_USER_AGENT']) AND ($_SERVER['HTTP_USER_AGENT']=='contype')) { - header('Content-Type: application/pdf'); - exit; - } - + //------------------------------------------------------------ + // Public methods + //------------------------------------------------------------ + + /** + * This is the class constructor. + * It allows to set up the page format, the orientation and + * the measure unit used in all the methods (except for the font sizes). + * @since 1.0 + * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul> + * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. + * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul> + * @param boolean $unicode TRUE means that the input text is unicode (default = true) + * @param String $encoding charset encoding; default is UTF-8 + */ + public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding="UTF-8") { + + /* Set internal character encoding to ASCII */ + if (function_exists("mb_internal_encoding") AND mb_internal_encoding()) { + $this->internal_encoding = mb_internal_encoding(); + mb_internal_encoding("ASCII"); + } + + // set language direction + $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; + $this->tmprtl = false; + + //Some checks + $this->_dochecks(); + + //Initialization of properties + $this->isunicode=$unicode; + $this->page=0; + $this->n=2; + $this->buffer=''; + $this->pages=array(); + $this->OrientationChanges=array(); + $this->state=0; + $this->fonts=array(); + $this->FontFiles=array(); + $this->diffs=array(); + $this->images=array(); + $this->links=array(); + $this->InFooter=false; + $this->lasth=0; + $this->FontFamily=''; + $this->FontStyle=''; + $this->FontSizePt=12; + $this->underline=false; + $this->DrawColor='0 G'; + $this->FillColor='0 g'; + $this->TextColor='0 g'; + $this->ColorFlag=false; + $this->ws=0; + // encryption values + $this->encrypted=false; + $this->last_rc4_key=''; + $this->padding="\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A"; + + //Standard Unicode fonts + $this->CoreFonts=array( + 'courier'=>'Courier', + 'courierB'=>'Courier-Bold', + 'courierI'=>'Courier-Oblique', + 'courierBI'=>'Courier-BoldOblique', + 'helvetica'=>'Helvetica', + 'helveticaB'=>'Helvetica-Bold', + 'helveticaI'=>'Helvetica-Oblique', + 'helveticaBI'=>'Helvetica-BoldOblique', + 'times'=>'Times-Roman', + 'timesB'=>'Times-Bold', + 'timesI'=>'Times-Italic', + 'timesBI'=>'Times-BoldItalic', + 'symbol'=>'Symbol', + 'zapfdingbats'=>'ZapfDingbats' + ); + + //Scale factor + switch (strtolower($unit)){ + case 'pt': {$this->k=1; break;} + case 'mm': {$this->k=72/25.4; break;} + case 'cm': {$this->k=72/2.54; break;} + case 'in': {$this->k=72; break;} + default : {$this->Error('Incorrect unit: '.$unit); break;} + } + + //Page format + if(is_string($format)) { + // Page formats (45 standard ISO paper formats and 4 american common formats). + // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm) + switch (strtoupper($format)){ + case '4A0': {$format = array(4767.87,6740.79); break;} + case '2A0': {$format = array(3370.39,4767.87); break;} + case 'A0': {$format = array(2383.94,3370.39); break;} + case 'A1': {$format = array(1683.78,2383.94); break;} + case 'A2': {$format = array(1190.55,1683.78); break;} + case 'A3': {$format = array(841.89,1190.55); break;} + case 'A4': default: {$format = array(595.28,841.89); break;} + case 'A5': {$format = array(419.53,595.28); break;} + case 'A6': {$format = array(297.64,419.53); break;} + case 'A7': {$format = array(209.76,297.64); break;} + case 'A8': {$format = array(147.40,209.76); break;} + case 'A9': {$format = array(104.88,147.40); break;} + case 'A10': {$format = array(73.70,104.88); break;} + case 'B0': {$format = array(2834.65,4008.19); break;} + case 'B1': {$format = array(2004.09,2834.65); break;} + case 'B2': {$format = array(1417.32,2004.09); break;} + case 'B3': {$format = array(1000.63,1417.32); break;} + case 'B4': {$format = array(708.66,1000.63); break;} + case 'B5': {$format = array(498.90,708.66); break;} + case 'B6': {$format = array(354.33,498.90); break;} + case 'B7': {$format = array(249.45,354.33); break;} + case 'B8': {$format = array(175.75,249.45); break;} + case 'B9': {$format = array(124.72,175.75); break;} + case 'B10': {$format = array(87.87,124.72); break;} + case 'C0': {$format = array(2599.37,3676.54); break;} + case 'C1': {$format = array(1836.85,2599.37); break;} + case 'C2': {$format = array(1298.27,1836.85); break;} + case 'C3': {$format = array(918.43,1298.27); break;} + case 'C4': {$format = array(649.13,918.43); break;} + case 'C5': {$format = array(459.21,649.13); break;} + case 'C6': {$format = array(323.15,459.21); break;} + case 'C7': {$format = array(229.61,323.15); break;} + case 'C8': {$format = array(161.57,229.61); break;} + case 'C9': {$format = array(113.39,161.57); break;} + case 'C10': {$format = array(79.37,113.39); break;} + case 'RA0': {$format = array(2437.80,3458.27); break;} + case 'RA1': {$format = array(1729.13,2437.80); break;} + case 'RA2': {$format = array(1218.90,1729.13); break;} + case 'RA3': {$format = array(864.57,1218.90); break;} + case 'RA4': {$format = array(609.45,864.57); break;} + case 'SRA0': {$format = array(2551.18,3628.35); break;} + case 'SRA1': {$format = array(1814.17,2551.18); break;} + case 'SRA2': {$format = array(1275.59,1814.17); break;} + case 'SRA3': {$format = array(907.09,1275.59); break;} + case 'SRA4': {$format = array(637.80,907.09); break;} + case 'LETTER': {$format = array(612.00,792.00); break;} + case 'LEGAL': {$format = array(612.00,1008.00); break;} + case 'EXECUTIVE': {$format = array(521.86,756.00); break;} + case 'FOLIO': {$format = array(612.00,936.00); break;} + // default: {$this->Error('Unknown page format: '.$format); break;} + } + $this->fwPt=$format[0]; + $this->fhPt=$format[1]; + } + else { + $this->fwPt=$format[0]*$this->k; + $this->fhPt=$format[1]*$this->k; + } + + $this->fw=$this->fwPt/$this->k; + $this->fh=$this->fhPt/$this->k; + + //Page orientation + $orientation=strtolower($orientation); + if($orientation=='p' or $orientation=='portrait') { + $this->DefOrientation='P'; + $this->wPt=$this->fwPt; + $this->hPt=$this->fhPt; + } + elseif($orientation=='l' or $orientation=='landscape') { + $this->DefOrientation='L'; + $this->wPt=$this->fhPt; + $this->hPt=$this->fwPt; + } + else { + $this->Error('Incorrect orientation: '.$orientation); + } + + $this->CurOrientation=$this->DefOrientation; + $this->w=$this->wPt/$this->k; + $this->h=$this->hPt/$this->k; + //Page margins (1 cm) + $margin=28.35/$this->k; + $this->SetMargins($margin,$margin); + //Interior cell margin (1 mm) + $this->cMargin=$margin/10; + //Line width (0.2 mm) + $this->LineWidth=.567/$this->k; + //Automatic page break + $this->SetAutoPageBreak(true,2*$margin); + //Full width display mode + $this->SetDisplayMode('fullwidth'); + //Compression + $this->SetCompression(true); + //Set default PDF version number + $this->PDFVersion = "1.5"; + + $this->encoding = $encoding; + $this->b = 0; + $this->i = 0; + $this->u = 0; + $this->HREF = ''; + $this->fontlist = array("arial", "times", "courier", "helvetica", "symbol"); + $this->issetfont = false; + $this->issetcolor = false; + $this->tableborder = 0; + $this->tdbegin = false; + $this->tdwidth= 0; + $this->tdheight = 0; + if($this->rtl) { + $this->tdalign = "R"; + } else { + $this->tdalign = "L"; + } + $this->tdbgcolor = false; + + $this->SetFillColor(200, 200, 200, true); + $this->SetTextColor(0, 0, 0, true); + } + + /** + * Default destructor. + * @since 1.53.0.TC016 + */ + public function __destruct() { + // restore internal encoding + if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) { + mb_internal_encoding($this->internal_encoding); + } + } + + /** + * Enable or disable Right-To-Left language mode + * @param Boolean $enable if true enable Right-To-Left language mode. + * @since 2.0.000 (2008-01-03) + */ + public function setRTL($enable) { + $this->rtl = $enable ? true : false; + $this->tmprtl = false; + } + + /** + * Force temporary RTL language direction + * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL + * @since 2.1.000 (2008-01-09) + */ + public function setTempRTL($mode) { + switch ($mode) { + case false: + case 'L': + case 'R': { + $this->tmprtl = $mode; + } + } + } + + /** + * Set the last cell height. + * @param float $h cell height. + * @author Nicola Asuni + * @since 1.53.0.TC034 + */ + public function setLastH($h) { + $this->lasth=$h; + } + + /** + * Set the image scale. + * @param float $scale image scale. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function setImageScale($scale) { + $this->imgscale=$scale; + } + + /** + * Returns the image scale. + * @return float image scale. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function getImageScale() { + return $this->imgscale; + } + + /** + * Returns the page width in units. + * @return int page width. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function getPageWidth() { + return $this->w; + } + + /** + * Returns the page height in units. + * @return int page height. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function getPageHeight() { + return $this->h; + } + + /** + * Returns the page break margin. + * @return int page break margin. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function getBreakMargin() { + return $this->bMargin; + } + + /** + * Returns the scale factor (number of points in user unit). + * @return int scale factor. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function getScaleFactor() { + return $this->k; + } + + /** + * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them. + * @param float $left Left margin. + * @param float $top Top margin. + * @param float $right Right margin. Default value is the left one. + * @since 1.0 + * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() + */ + public function SetMargins($left, $top, $right=-1) { + //Set left, top and right margins + $this->lMargin=$left; + $this->tMargin=$top; + if($right==-1) { + $right=$left; + } + $this->rMargin=$right; + } + + /** + * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin. + * @param float $margin The margin. + * @since 1.4 + * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() + */ + public function SetLeftMargin($margin) { + //Set left margin + $this->lMargin=$margin; + if(($this->page > 0) AND ($this->x < $margin)) { + $this->x = $margin; + } + } + + /** + * Defines the top margin. The method can be called before creating the first page. + * @param float $margin The margin. + * @since 1.5 + * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() + */ + public function SetTopMargin($margin) { + //Set top margin + $this->tMargin=$margin; + if(($this->page > 0) AND ($this->y < $margin)) { + $this->y = $margin; + } + } + + /** + * Defines the right margin. The method can be called before creating the first page. + * @param float $margin The margin. + * @since 1.5 + * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() + */ + public function SetRightMargin($margin) { + $this->rMargin=$margin; + if(($this->page > 0) AND ($this->x > ($this->w - $margin))) { + $this->x = $this->w - $margin; + } + } + + /** + * Set the internal Cell padding. + * @param float $pad internal padding. + * @since 2.1.000 (2008-01-09) + * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() + */ + public function SetCellPadding($pad) { + $this->cMargin=$pad; + } + + /** + * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm. + * @param boolean $auto Boolean indicating if mode should be on or off. + * @param float $margin Distance from the bottom of the page. + * @since 1.0 + * @see Cell(), MultiCell(), AcceptPageBreak() + */ + public function SetAutoPageBreak($auto, $margin=0) { + //Set auto page break mode and triggering margin + $this->AutoPageBreak = $auto; + $this->bMargin = $margin; + $this->PageBreakTrigger = $this->h - $margin; + } + + /** + * Defines the way the document is to be displayed by the viewer. The zoom level can be set: pages can be displayed entirely on screen, occupy the full width of the window, use real size, be scaled by a specific zooming factor or use viewer default (configured in the Preferences menu of Acrobat). The page layout can be specified too: single at once, continuous display, two columns or viewer default. By default, documents use the full width mode with continuous display. + * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul> + * @param string $layout The page layout. Possible values are:<ul><li>single: displays one page at once</li><li>continuous: displays pages continuously (default)</li><li>two: displays two pages on two columns</li><li>default: uses viewer default mode</li></ul> + * @since 1.2 + */ + public function SetDisplayMode($zoom, $layout='continuous') { + //Set display mode in viewer + if($zoom=='fullpage' or $zoom=='fullwidth' or $zoom=='real' or $zoom=='default' or !is_string($zoom)) { + $this->ZoomMode=$zoom; + } + else { + $this->Error('Incorrect zoom display mode: '.$zoom); + } + if($layout=='single' or $layout=='continuous' or $layout=='two' or $layout=='default') { + $this->LayoutMode=$layout; + } + else { + $this->Error('Incorrect layout display mode: '.$layout); + } + } + + /** + * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default. + * Note: the Zlib extension is required for this feature. If not present, compression will be turned off. + * @param boolean $compress Boolean indicating if compression must be enabled. + * @since 1.4 + */ + public function SetCompression($compress) { + //Set page compression + if(function_exists('gzcompress')) { + $this->compress=$compress; + } + else { + $this->compress=false; + } + } + + /** + * Defines the title of the document. + * @param string $title The title. + * @since 1.2 + * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() + */ + public function SetTitle($title) { + //Title of document + $this->title=$title; + } + + /** + * Defines the subject of the document. + * @param string $subject The subject. + * @since 1.2 + * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() + */ + public function SetSubject($subject) { + //Subject of document + $this->subject=$subject; + } + + /** + * Defines the author of the document. + * @param string $author The name of the author. + * @since 1.2 + * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() + */ + public function SetAuthor($author) { + //Author of document + $this->author=$author; + } + + /** + * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. + * @param string $keywords The list of keywords. + * @since 1.2 + * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() + */ + public function SetKeywords($keywords) { + //Keywords of document + $this->keywords=$keywords; + } + + /** + * Defines the creator of the document. This is typically the name of the application that generates the PDF. + * @param string $creator The name of the creator. + * @since 1.2 + * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() + */ + public function SetCreator($creator) { + //Creator of document + $this->creator=$creator; + } + + /** + * Defines an alias for the total number of pages. It will be substituted as the document is closed.<br /> + * <b>Example:</b><br /> + * <pre> + * class PDF extends TCPDF { + * public function Footer() { + * //Go to 1.5 cm from bottom + * $this->SetY(-15); + * //Select Arial italic 8 + * $this->SetFont('Arial','I',8); + * //Print current and total page numbers + * $this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C'); + * } + * } + * $pdf=new PDF(); + * $pdf->AliasNbPages(); + * </pre> + * @param string $alias The alias. Default value: {nb}. + * @since 1.4 + * @see PageNo(), Footer() + */ + public function AliasNbPages($alias='{nb}') { + //Define an alias for total number of pages + $this->AliasNbPages = $this->_escapetext($alias); + } + + /** + * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid. + * 2004-06-11 :: Nicola Asuni : changed bold tag with strong + * @param string $msg The error message + * @since 1.0 + */ + public function Error($msg) { + //Fatal error + die('<strong>TCPDF error: </strong>'.$msg); + } + + /** + * This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically. + * Note: no page is created by this method + * @since 1.0 + * @see AddPage(), Close() + */ + public function Open() { + //Begin document + $this->state=1; + } + + /** + * Terminates the PDF document. It is not necessary to call this method explicitly because Output() does it automatically. If the document contains no page, AddPage() is called to prevent from getting an invalid document. + * @since 1.0 + * @see Open(), Output() + */ + public function Close() { + //Terminate document + if($this->state==3) { + return; + } + if($this->page==0) { + $this->AddPage(); + } + //Page footer + $this->InFooter=true; + $this->Footer(); + $this->InFooter=false; + //Close page + $this->_endpage(); + //Close document + $this->_enddoc(); + } + + /** + * Reset pointer to the last document page. + * @since 2.0.000 (2008-01-04) + * @see setPage(), getPage(), getNumPages() + */ + public function lastPage() { + $this->page = count($this->pages); + } + + /** + * Move pointer to the apecified document page. + * @param int $pnum page number + * @since 2.1.000 (2008-01-07) + * @see getPage(), lastpage(), getNumPages() + */ + public function setPage($pnum) { + if(($pnum > 0) AND ($pnum <= count($this->pages))) { + $this->page = $pnum; + } + } + + /** + * Get current document page number. + * @return int page number + * @since 2.1.000 (2008-01-07) + * @see setPage(), lastpage(), getNumPages() + */ + public function getPage() { + return $this->page; + } + + + /** + * Get the total number of insered pages. + * @return int number of pages + * @since 2.1.000 (2008-01-07) + * @see setPage(), getPage(), lastpage() + */ + public function getNumPages() { + return count($this->pages); + } + + /** + * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer. Then the page is added, the current position set to the top-left corner according to the left and top margins, and Header() is called to display the header. + * The font which was set before calling is automatically restored. There is no need to call SetFont() again if you want to continue with the same font. The same is true for colors and line width. + * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. + * @param string $orientation Page orientation. Possible values are (case insensitive):<ul><li>P or Portrait</li><li>L or Landscape</li></ul> The default value is the one passed to the constructor. + * @since 1.0 + * @see TCPDF(), Header(), Footer(), SetMargins() + */ + public function AddPage($orientation='') { + if (count($this->pages) > ($this->page + 1)) { + // this page has been already added + $this->page++; + return; + } + //Start a new page + if($this->state==0) { + $this->Open(); + } + $family=$this->FontFamily; + $style=$this->FontStyle.($this->underline ? 'U' : ''); + $size=$this->FontSizePt; + $lw=$this->LineWidth; + $dc=$this->DrawColor; + $fc=$this->FillColor; + $tc=$this->TextColor; + $cf=$this->ColorFlag; + if($this->page>0) { + //Page footer + $this->InFooter=true; + $this->Footer(); + $this->InFooter=false; + //Close page + $this->_endpage(); + } + //Start new page + $this->_beginpage($orientation); + //Set line cap style to square + $this->_out('2 J'); + //Set line width + $this->LineWidth=$lw; + $this->_out(sprintf('%.2f w',$lw*$this->k)); + //Set font + if($family) { + $this->SetFont($family,$style,$size); + } + //Set colors + $this->DrawColor=$dc; + if($dc!='0 G') { + $this->_out($dc); + } + $this->FillColor=$fc; + if($fc!='0 g') { + $this->_out($fc); + } + $this->TextColor=$tc; + $this->ColorFlag=$cf; + //Page header + $this->Header(); + //Restore line width + if($this->LineWidth!=$lw) { + $this->LineWidth=$lw; + $this->_out(sprintf('%.2f w',$lw*$this->k)); + } + //Restore font + if($family) { + $this->SetFont($family,$style,$size); + } + //Restore colors + if($this->DrawColor!=$dc) { + $this->DrawColor=$dc; + $this->_out($dc); + } + if($this->FillColor!=$fc) { + $this->FillColor=$fc; + $this->_out($fc); + } + $this->TextColor=$tc; + $this->ColorFlag=$cf; + } + + /** + * Set header data. + * @param string $ln header image logo + * @param string $lw header image logo width in mm + * @param string $ht string to print as title on document header + * @param string $hs string to print on document header + */ + public function setHeaderData($ln="", $lw=0, $ht="", $hs="") { + $this->header_logo = $ln; + $this->header_logo_width = $lw; + $this->header_title = $ht; + $this->header_string = $hs; + } + + /** + * Set header margin. + * (minimum distance between header and top page margin) + * @param int $hm distance in millimeters + */ + public function setHeaderMargin($hm=10) { + $this->header_margin = $hm; + } + + /** + * Set footer margin. + * (minimum distance between footer and bottom page margin) + * @param int $fm distance in millimeters + */ + public function setFooterMargin($fm=10) { + $this->footer_margin = $fm; + } + + /** + * Set a flag to print page header. + * @param boolean $val set to true to print the page header (default), false otherwise. + */ + public function setPrintHeader($val=true) { + $this->print_header = $val; + } + + /** + * Set a flag to print page footer. + * @param boolean $value set to true to print the page footer (default), false otherwise. + */ + public function setPrintFooter($val=true) { + $this->print_footer = $val; + } + + /** + * This method is used to render the page header. + * It is automatically called by AddPage() and could be overwritten in your own inherited class. + */ + public function Header() { + if ($this->print_header) { + + if (!isset($this->original_lMargin)) { + $this->original_lMargin = $this->lMargin; + } + if (!isset($this->original_rMargin)) { + $this->original_rMargin = $this->rMargin; + } + + //set current position + if ($this->rtl) { + $this->SetXY($this->original_rMargin, $this->header_margin); + } else { + $this->SetXY($this->original_lMargin, $this->header_margin); + } + + if (($this->header_logo) AND ($this->header_logo != K_BLANK_IMAGE)) { + $this->Image(K_PATH_IMAGES.$this->header_logo, $this->GetX(), $this->header_margin, $this->header_logo_width); + } else { + $this->img_rb_x = $this->GetX(); + $this->img_rb_y = $this->GetY(); + } + + $cell_height = round((K_CELL_HEIGHT_RATIO * $this->header_font[2]) / $this->k, 2); + // set starting margin for text data cell + if ($this->rtl) { + $header_x = $this->original_rMargin + ($this->header_logo_width * 1.1); + } else { + $header_x = $this->original_lMargin + ($this->header_logo_width * 1.1); + } + + // header title + $this->SetFont($this->header_font[0], 'B', $this->header_font[2] + 1); + $this->SetX($header_x); + $this->Cell($this->header_width, $cell_height, $this->header_title, 0, 1, ''); + + // header string + $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); + $this->SetX($header_x); + $this->MultiCell($this->header_width, $cell_height, $this->header_string, 0, '', 0); + + // print an ending header line + if (empty($this->header_width)) { + //set style for cell border + $prevlinewidth = $this->GetLineWidth(); + $line_width = 0.3; + $this->SetLineWidth($line_width); + $this->SetDrawColor(0, 0, 0); + $this->SetY(1 + max($this->img_rb_y, $this->GetY())); + if ($this->rtl) { + $this->SetX($this->original_rMargin); + } else { + $this->SetX($this->original_lMargin); + } + $this->Cell(0, 0, '', 'T', 0, 'C'); + $this->SetLineWidth($prevlinewidth); + } + + //restore position + if ($this->rtl) { + $this->SetXY($this->original_rMargin, $this->tMargin); + } else { + $this->SetXY($this->original_lMargin, $this->tMargin); + } + } + } + + /** + * This method is used to render the page footer. + * It is automatically called by AddPage() and could be overwritten in your own inherited class. + */ + public function Footer() { + if ($this->print_footer) { + + if (!isset($this->original_lMargin)) { + $this->original_lMargin = $this->lMargin; + } + if (!isset($this->original_rMargin)) { + $this->original_rMargin = $this->rMargin; + } + + //set font + $this->SetFont($this->footer_font[0], $this->footer_font[1] , $this->footer_font[2]); + //set style for cell border + $prevlinewidth = $this->GetLineWidth(); + $line_width = 0.3; + $this->SetLineWidth($line_width); + $this->SetDrawColor(0, 0, 0); + + $footer_height = round((K_CELL_HEIGHT_RATIO * $this->footer_font[2]) / $this->k, 2); //footer height + //get footer y position + $footer_y = $this->h - $this->footer_margin - $footer_height; + //set current position + if ($this->rtl) { + $this->SetXY($this->original_rMargin, $footer_y); + } else { + $this->SetXY($this->original_lMargin, $footer_y); + } + + //print document barcode + if ($this->barcode) { + $this->Ln(); + $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin)/3); //max width + $this->writeBarcode($this->GetX(), $footer_y + $line_width, $barcode_width, $footer_height - $line_width, "C128B", false, false, 2, $this->barcode); + } + + $pagenumtxt = $this->l['w_page']." ".$this->PageNo().' / {nb}'; + + $this->SetY($footer_y); + + //Print page number + if ($this->rtl) { + $this->SetX($this->original_rMargin); + $this->Cell(0, $footer_height, $pagenumtxt, 'T', 0, 'L'); + } else { + $this->SetX($this->original_lMargin); + $this->Cell(0, $footer_height, $pagenumtxt, 'T', 0, 'R'); + } + // restore line width + $this->SetLineWidth($prevlinewidth); + } + } + + /** + * Returns the current page number. + * @return int page number + * @since 1.0 + * @see AliasNbPages() + */ + public function PageNo() { + //Get current page number + return $this->page; + } + + /** + * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. + * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 + * @param int $g Green component (between 0 and 255) + * @param int $b Blue component (between 0 and 255) + * @since 1.3 + * @see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() + */ + public function SetDrawColor($r, $g=-1, $b=-1) { + //Set color for all stroking operations + if(($r==0 and $g==0 and $b==0) or $g==-1) { + $this->DrawColor=sprintf('%.3f G',$r/255); + } + else { + $this->DrawColor=sprintf('%.3f %.3f %.3f RG',$r/255,$g/255,$b/255); + } + if($this->page>0) { + $this->_out($this->DrawColor); + } + } + + /** + * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. + * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 + * @param int $g Green component (between 0 and 255) + * @param int $b Blue component (between 0 and 255) + * @param boolean $storeprev if true stores the RGB array on $prevFillColor variable. + * @since 1.3 + * @see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() + */ + public function SetFillColor($r, $g=-1, $b=-1, $storeprev=false) { + //Set color for all filling operations + if(($r==0 and $g==0 and $b==0) or $g==-1) { + $this->FillColor=sprintf('%.3f g',$r/255); + } + else { + $this->FillColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); + } + $this->ColorFlag=($this->FillColor!=$this->TextColor); + if($this->page>0) { + $this->_out($this->FillColor); + } + if ($storeprev) { + // store color as previous value + $this->prevFillColor = array($r, $g, $b); + } + } + + /** + * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. + * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 + * @param int $g Green component (between 0 and 255) + * @param int $b Blue component (between 0 and 255) + * @param boolean $storeprev if true stores the RGB array on $prevTextColor variable. + * @since 1.3 + * @see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() + */ + public function SetTextColor($r, $g=-1, $b=-1, $storeprev=false) { + //Set color for text + if(($r==0 and $g==0 and $b==0) or $g==-1) { + $this->TextColor=sprintf('%.3f g',$r/255); + } + else { + $this->TextColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); + } + $this->ColorFlag=($this->FillColor!=$this->TextColor); + if ($storeprev) { + // store color as previous value + $this->prevTextColor = array($r, $g, $b); + } + } + + /** + * Returns the length of a string in user unit. A font must be selected.<br> + * Support UTF-8 Unicode [Nicola Asuni, 2005-01-02] + * @param string $s The string whose length is to be computed + * @return int + * @since 1.2 + */ + public function GetStringWidth($s) { + //Get width of a string in the current font + $s = (string)$s; + $cw = &$this->CurrentFont['cw']; + $w = 0; + if($this->isunicode) { + $unicode = $this->UTF8StringToArray($s); + foreach($unicode as $char) { + if (isset($cw[$char])) { + $w+=$cw[$char]; + } elseif(isset($cw[ord($char)])) { + $w+=$cw[ord($char)]; + } elseif(isset($cw[chr($char)])) { + $w+=$cw[chr($char)]; + } elseif(isset($this->CurrentFont['desc']['MissingWidth'])) { + $w += $this->CurrentFont['desc']['MissingWidth']; // set default size + } else { + $w += 500; + } + } + } else { + $l = strlen($s); + for($i=0; $i < $l; $i++) { + if (isset($cw[$s{$i}])) { + $w += $cw[$s{$i}]; + } else if (isset($cw[ord($s{$i})])) { + $w += $cw[ord($s{$i})]; + } + } + } + return ($w * $this->FontSize / 1000); + } + + /** + * Returns the numbero of characters in a string. + * @param string $s The input string. + * @return int number of characters + * @since 2.0.0001 (2008-01-07) + */ + public function GetNumChars($s) { + if($this->isunicode) { + return count($this->UTF8StringToArray($s)); + } + return strlen($s); + } + + /** + * Imports a TrueType or Type1 font and makes it available. It is necessary to generate a font definition file first with the makefont.php utility. The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated. + * Support UTF-8 Unicode [Nicola Asuni, 2005-01-02]. + * <b>Example</b>:<br /> + * <pre> + * $pdf->AddFont('Comic','I'); + * // is equivalent to: + * $pdf->AddFont('Comic','I','comici.php'); + * </pre> + * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. + * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul> + * @param string $file The font definition file. By default, the name is built from the family and style, in lower case with no space. + * @since 1.5 + * @see SetFont() + */ + public function AddFont($family, $style='', $file='') { + if(empty($family)) { + return; + } + + //Add a TrueType or Type1 font + $family = strtolower($family); + if((!$this->isunicode) AND ($family == 'arial')) { + $family = 'helvetica'; + } + + $style=strtoupper($style); + $style=str_replace('U','',$style); + if($style == 'IB') { + $style = 'BI'; + } + + $fontkey = $family.$style; + // check if the font has been already added + if(isset($this->fonts[$fontkey])) { + return; + } + + if($file=='') { + $file = str_replace(' ', '', $family).strtolower($style).'.php'; + } + if(!file_exists($this->_getfontpath().$file)) { + // try to load the basic file without styles + $file = str_replace(' ', '', $family).'.php'; + } + + include($this->_getfontpath().$file); + + if(!isset($name) AND !isset($fpdf_charwidths)) { + $this->Error('Could not include font definition file'); + } + + $i = count($this->fonts)+1; + + if($this->isunicode) { + $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file, 'ctg'=>$ctg); + $fpdf_charwidths[$fontkey] = $cw; + } else { + $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$this->CoreFonts[$fontkey], 'up'=>-100, 'ut'=>50, 'cw'=>$fpdf_charwidths[$fontkey]); + } + + if(isset($diff) AND (!empty($diff))) { + //Search existing encodings + $d=0; + $nb=count($this->diffs); + for($i=1;$i<=$nb;$i++) { + if($this->diffs[$i]==$diff) { + $d=$i; + break; + } + } + if($d==0) { + $d=$nb+1; + $this->diffs[$d]=$diff; + } + $this->fonts[$fontkey]['diff']=$d; + } + if(!empty($file)) { + if((strcasecmp($type,"TrueType") == 0) OR (strcasecmp($type,"TrueTypeUnicode") == 0)) { + $this->FontFiles[$file]=array('length1'=>$originalsize); + } + else { + $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2); + } + } + } + + /** + * Sets the font used to print character strings. It is mandatory to call this method at least once before printing text or the resulting document would not be valid. + * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). + * The method can be called before the first page is created and the font is retained from page to page. + If you just wish to change the current font size, it is simpler to call SetFontSize(). + * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br /> + * Example for the last case (note the trailing slash):<br /> + * <pre> + * define('K_PATH_FONTS','/home/www/font/'); + * require('tcpdf.php'); + * + * //Times regular 12 + * $pdf->SetFont('Times'); + * //Arial bold 14 + * $pdf->SetFont('Arial','B',14); + * //Removes bold + * $pdf->SetFont(''); + * //Times bold, italic and underlined 14 + * $pdf->SetFont('Times','BIU'); + * </pre><br /> + * If the file corresponding to the requested font is not found, the error "Could not include font metric file" is generated. + * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard families (case insensitive):<ul><li>Courier (fixed-width)</li><li>Helvetica or Arial (synonymous; sans serif)</li><li>Times (serif)</li><li>Symbol (symbolic)</li><li>ZapfDingbats (symbolic)</li></ul>It is also possible to pass an empty string. In that case, the current family is retained. + * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li></ul>or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats + * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12 + * @since 1.0 + * @see AddFont(), SetFontSize(), Cell(), MultiCell(), Write() + */ + public function SetFont($family, $style='', $size=0) { + // save previous values + $this->prevFontFamily = $this->FontFamily; + $this->prevFontStyle = $this->FontStyle; + + //Select a font; size given in points + global $fpdf_charwidths; + + $family=strtolower($family); + if($family=='') { + $family=$this->FontFamily; + } + if((!$this->isunicode) AND ($family == 'arial')) { + $family = 'helvetica'; + } + elseif(($family=="symbol") OR ($family=="zapfdingbats")) { + $style=''; + } + $style=strtoupper($style); + + if(strpos($style,'U')!==false) { + $this->underline=true; + $style=str_replace('U','',$style); + } + else { + $this->underline=false; + } + if($style=='IB') { + $style='BI'; + } + if($size==0) { + $size=$this->FontSizePt; + } + + // try to add font (if not already added) + if($this->isunicode) { + $this->AddFont($family, $style); + } + + //Test if font is already selected + if(($this->FontFamily == $family) AND ($this->FontStyle == $style) AND ($this->FontSizePt == $size)) { + return; + } + + $fontkey = $family.$style; + //if(!isset($this->fonts[$fontkey]) AND isset($this->fonts[$family])) { + // $style=''; + //} + + //Test if used for the first time + if(!isset($this->fonts[$fontkey])) { + //Check if one of the standard fonts + if(isset($this->CoreFonts[$fontkey])) { + if(!isset($fpdf_charwidths[$fontkey])) { + //Load metric file + $file = $family; + if(($family!='symbol') AND ($family!='zapfdingbats')) { + $file .= strtolower($style); + } + if(!file_exists($this->_getfontpath().$file.'.php')) { + // try to load the basic file without styles + $file = $family; + $fontkey = $family; + } + include($this->_getfontpath().$file.'.php'); + if (($this->isunicode AND !isset($ctg)) OR ((!$this->isunicode) AND (!isset($fpdf_charwidths[$fontkey]))) ) { + $this->Error("Could not include font metric file [".$fontkey."]: ".$this->_getfontpath().$file.".php"); + } + } + $i = count($this->fonts) + 1; + + if($this->isunicode) { + $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file, 'ctg'=>$ctg); + $fpdf_charwidths[$fontkey] = $cw; + } else { + $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$this->CoreFonts[$fontkey], 'up'=>-100, 'ut'=>50, 'cw'=>$fpdf_charwidths[$fontkey]); + } + } + else { + $this->Error('Undefined font: '.$family.' '.$style); + } + } + //Select it + $this->FontFamily = $family; + $this->FontStyle = $style; + $this->FontSizePt = $size; + $this->FontSize = $size / $this->k; + $this->CurrentFont = &$this->fonts[$fontkey]; + if($this->page>0) { + $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); + } + } + + /** + * Defines the size of the current font. + * @param float $size The size (in points) + * @since 1.0 + * @see SetFont() + */ + public function SetFontSize($size) { + //Set font size in points + if($this->FontSizePt==$size) { + return; + } + $this->FontSizePt = $size; + $this->FontSize = $size / $this->k; + if($this->page > 0) { + $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); + } + } + + /** + * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br /> + * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). + * @since 1.5 + * @see Cell(), Write(), Image(), Link(), SetLink() + */ + public function AddLink() { + //Create a new internal link + $n=count($this->links)+1; + $this->links[$n]=array(0,0); + return $n; + } + + /** + * Defines the page and position a link points to + * @param int $link The link identifier returned by AddLink() + * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) + * @param int $page Number of target page; -1 indicates the current page. This is the default value + * @since 1.5 + * @see AddLink() + */ + public function SetLink($link, $y=0, $page=-1) { + //Set destination of internal link + if($y==-1) { + $y=$this->y; + } + if($page==-1) { + $page=$this->page; + } + $this->links[$link]=array($page,$y); + } + + /** + * Puts a link on a rectangular area of the page. Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image. + * @param float $x Abscissa of the upper-left corner of the rectangle (or upper-right for RTL languages) + * @param float $y Ordinate of the upper-left corner of the rectangle (or upper-right for RTL languages) + * @param float $w Width of the rectangle + * @param float $h Height of the rectangle + * @param mixed $link URL or identifier returned by AddLink() + * @since 1.5 + * @see AddLink(), Cell(), Write(), Image() + */ + public function Link($x, $y, $w, $h, $link) { + $this->PageLinks[$this->page][] = array($x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h*$this->k, $link); + } + + /** + * Prints a character string. The origin is on the left of the first charcter, on the baseline. This method allows to place a string precisely on the page, but it is usually easier to use Cell(), MultiCell() or Write() which are the standard methods to print text. + * @param float $x Abscissa of the origin + * @param float $y Ordinate of the origin + * @param string $txt String to print + * @since 1.0 + * @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write() + */ + public function Text($x, $y, $txt) { + //Output a string + if($this->rtl) { + $xr = $this->w - $x - $this->GetStringWidth($txt); + } else { + $xr = $x; + } + $s = sprintf('BT %.2f %.2f Td (%s) Tj ET', $xr * $this->k, ($this->h-$y) * $this->k, $this->_escapetext($txt)); + if($this->underline AND ($txt!='')) { + $s .= ' '.$this->_dounderline($xr, $y, $txt); + } + if($this->ColorFlag) { + $s='q '.$this->TextColor.' '.$s.' Q'; + } + $this->_out($s); + } + + /** + * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br /> + * This method is called automatically and should not be called directly by the application.<br /> + * <b>Example:</b><br /> + * The method is overriden in an inherited class in order to obtain a 3 column layout:<br /> + * <pre> + * class PDF extends TCPDF { + * var $col=0; + * + * public function SetCol($col) { + * //Move position to a column + * $this->col=$col; + * $x=10+$col*65; + * $this->SetLeftMargin($x); + * $this->SetX($x); + * } + * + * public function AcceptPageBreak() { + * if($this->col<2) { + * //Go to next column + * $this->SetCol($this->col+1); + * $this->SetY(10); + * return false; + * } + * else { + * //Go back to first column and issue page break + * $this->SetCol(0); + * return true; + * } + * } + * } + * + * $pdf=new PDF(); + * $pdf->Open(); + * $pdf->AddPage(); + * $pdf->SetFont('Arial','',12); + * for($i=1;$i<=300;$i++) { + * $pdf->Cell(0,5,"Line $i",0,1); + * } + * $pdf->Output(); + * </pre> + * @return boolean + * @since 1.4 + * @see SetAutoPageBreak() + */ + public function AcceptPageBreak() { + //Accept automatic page break or not + return $this->AutoPageBreak; + } + + /** + * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br /> + * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. + * @param float $w Cell width. If 0, the cell extends up to the right margin. + * @param float $h Cell height. Default value: 0. + * @param string $txt String to print. Default value: empty string. + * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> + * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> + Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. + * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> + * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * @param mixed $link URL or identifier returned by AddLink(). + * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul> + * @since 1.0 + * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() + */ + public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) { + //Output a cell + $k=$this->k; + if(($this->y + $h) > $this->PageBreakTrigger AND empty($this->InFooter) AND $this->AcceptPageBreak()) { + //Automatic page break + $x = $this->x; + $ws = $this->ws; + if($ws > 0) { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->AddPage($this->CurOrientation); + $this->x = $x; + if($ws > 0) { + $this->ws = $ws; + $this->_out(sprintf('%.3f Tw',$ws * $k)); + } + } + if($w == 0) { + if ($this->rtl) { + $w = $this->x - $this->lMargin; + } else { + $w = $this->w - $this->rMargin - $this->x; + } + } + $s = ''; + if(($fill == 1) OR ($border == 1)) { + if($fill == 1) { + $op = ($border == 1) ? 'B' : 'f'; + } else { + $op = 'S'; + } + if ($this->rtl) { + $xk = ($this->x - $w) * $k; + } else { + $xk = $this->x * $k; + } + $s .= sprintf('%.2f %.2f %.2f %.2f re %s ', $xk, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op); + } + if(is_string($border)) { + $x=$this->x; + $y=$this->y; + if(strpos($border,'L')!==false) { + if ($this->rtl) { + $xk = ($x - $w) * $k; + } else { + $xk = $x * $k; + } + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xk,($this->h-($y+$h))*$k); + } + if(strpos($border,'T')!==false) { + if ($this->rtl) { + $xk = ($x - $w) * $k; + $xwk = $x * $k; + } else { + $xk = $x * $k; + $xwk = ($x + $w) * $k; + } + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xwk,($this->h-$y)*$k); + } + if(strpos($border,'R')!==false) { + if ($this->rtl) { + $xk = $x * $k; + } else { + $xk = ($x + $w) * $k; + } + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xk,($this->h-($y+$h))*$k); + } + if(strpos($border,'B')!==false) { + if ($this->rtl) { + $xk = ($x - $w) * $k; + $xwk = $x * $k; + } else { + $xk = $x * $k; + $xwk = ($x + $w) * $k; + } + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-($y+$h))*$k,$xwk,($this->h-($y+$h))*$k); + } + } + if($txt != '') { + // text lenght + $width = $this->GetStringWidth($txt); + // ratio between cell lenght and text lenght + $ratio = ($w - (2 * $this->cMargin)) / $width; + + // stretch text if requested + if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) { + if ($stretch > 2) { + // spacing + //Calculate character spacing in points + $char_space = ($w - (2 * $this->cMargin) - $width) / max($this->GetNumChars($s)-1,1) * $this->k; + //Set character spacing + $this->_out(sprintf('BT %.2f Tc ET', $char_space)); + } else { + // scaling + //Calculate horizontal scaling + $horiz_scale = $ratio*100.0; + //Set horizontal scaling + $this->_out(sprintf('BT %.2f Tz ET', $horiz_scale)); + } + $align = ''; + $width = $w - (2 * $this->cMargin); + } else { + $stretch == 0; + } + + if($align == 'L') { + if ($this->rtl) { + $dx = $w - $this->cMargin - $width; + } else { + $dx = $this->cMargin; + } + } elseif($align == 'R') { + if ($this->rtl) { + $dx = $this->cMargin; + } else { + $dx = $w - $this->cMargin - $width; + } + } elseif($align=='C') { + $dx = ($w - $width)/2; + } elseif($align=='J') { + if ($this->rtl) { + $dx = $w - $this->cMargin - $width; + } else { + $dx = $this->cMargin; + } + } else { + $dx = $this->cMargin; + } + if($this->ColorFlag) { + $s .= 'q '.$this->TextColor.' '; + } + $txt2 = $this->_escapetext($txt); + if ($this->rtl) { + $xdk = ($this->x - $dx - $width) * $k; + } else { + $xdk = ($this->x + $dx) * $k; + } + // 2008-02-16 Jacek Czekaj - multibyte justification + if ($align == 'J') { + // count number of spaces + $ns = substr_count($txt, ' '); + // get string width without spaces + $width = $this->GetStringWidth(str_replace(' ', '', $txt)); + // set word position to be used with TJ operator + $txt2 = str_replace(chr(0).' ', ') '. -2830*($w-$width-2)/($ns?$ns:1)/$this->FontSize/$this->k . ' (', $txt2); + } + $s.=sprintf('BT %.2f %.2f Td [(%s)] TJ ET', $xdk, ($this->h - ($this->y + 0.5 * $h + 0.3 * $this->FontSize)) * $k, $txt2); + //$s.=sprintf('BT %.2f %.2f Td (%s) Tj ET', $xdk, ($this->h - ($this->y + 0.5 * $h + 0.3 * $this->FontSize)) * $k, $txt2); + if($this->underline) { + if ($this->rtl) { + $xdx = $this->x - $dx - $width; + } else { + $xdx = $this->x + $dx; + } + $s.=' '.$this->_dounderline($xdx, $this->y + 0.5 * $h + 0.3 * $this->FontSize, $txt); + } + if($this->ColorFlag) { + $s.=' Q'; + } + if($link) { + if ($this->rtl) { + $xdx = $this->x - $dx - $width; + } else { + $xdx = $this->x + $dx; + } + $this->Link($xdx, $this->y + 0.5 * $h - 0.5 * $this->FontSize, $width, $this->FontSize, $link); + } + } + + // output cell + if($s) { + // output cell + $this->_out($s); + // reset text stretching + if($stretch > 2) { + //Reset character horizontal spacing + $this->_out('BT 0 Tc ET'); + } elseif($stretch > 0) { + //Reset character horizontal scaling + $this->_out('BT 100 Tz ET'); + } + } + + $this->lasth = $h; + if($ln>0) { + //Go to the beginning of the next line + $this->y += $h; + if($ln == 1) { + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + } else { + $this->x = $this->lMargin; + } + } + } else { + // go left or right by case + if ($this->rtl) { + $this->x -= $w; + } else { + $this->x += $w; + } + } + } + + /** + * This method allows printing text with line breaks. They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br /> + * Text can be aligned, centered or justified. The cell block can be framed and the background painted. + * @param float $w Width of cells. If 0, they extend up to the right margin of the page. + * @param float $h Height of cells. + * @param string $txt String to print + * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> + * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value)</li></ul> + * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul> + * @return int number of cells (number of lines) + * @since 1.3 + * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() + */ + public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1) { + + // save current position + $prevx = $this->x; + $prevy = $this->y; + + // get current page number + $startpage = $this->page; + + // calculate remaining vertical space on first page ($startpage) + $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin(); + + //Output text with automatic or explicit line breaks + $cw = &$this->CurrentFont['cw']; + + if($w == 0) { + if ($this->rtl) { + $w = $this->x - $this->lMargin; + } else { + $w = $this->w - $this->rMargin - $this->x; + } + } + + $wmax = ($w - 2 * $this->cMargin); + + $s = str_replace("\r", '', $txt); // remove carriage returns + $nb = strlen($s); + + $sep=-1; + $i=0; + $j=0; + $l=0; + $ns=0; + $nl=1; + while($i < $nb) { + //Get next character + $c = $s{$i}; + if(preg_match("/[\n]/u", $c)) { + //Explicit line break + if($this->ws > 0) { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); + $i++; + $sep=-1; + $j=$i; + $l=0; + $ns=0; + $nl++; + continue; + } + if(preg_match("/[ ]/u", $c)) { + $sep = $i; + $ls = $l; + $ns++; + } + + $l = $this->GetStringWidth(substr($s, $j, $i-$j)); + + if($l > $wmax) { + //Automatic line break + if($sep == -1) { + if($i == $j) { + $i++; + } + if($this->ws > 0) { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); + } + else { + if($align=='J') { + $this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0; + $this->_out(sprintf('%.3f Tw', $this->ws * $this->k)); + } + $this->Cell($w, $h, substr($s, $j, $sep-$j), 0, 2, $align, $fill, ''); + $i = $sep + 1; + } + $sep=-1; + $j=$i; + $l=0; + $ns=0; + $nl++; + } + else { + $i++; + } + } + //Last chunk + if($this->ws>0) { + $this->ws=0; + $this->_out('0 Tw'); + } + if($align == "J") { + $align = "L"; + } + $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); + + // Get end-of-text Y position + $currentY = $this->GetY(); + // get latest page number + $endpage = $this->page; + + if (!empty($border)) { + // check if a new page has been created + if ($endpage > $startpage) { + // design borders around HTML cells. + for ($page=$startpage; $page<=$endpage; $page++) { + $this->page = $page; + if ($page==$startpage) { + $this->y = $this->getPageHeight() - $restspace - $this->getBreakMargin(); + $h = $restspace - 1; + } elseif ($page==$endpage) { + $this->y = $this->tMargin; // put cursor at the beginning of text + $h = $currentY - $this->tMargin; + } else { + $this->y = $this->tMargin; // put cursor at the beginning of text + $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); + } + $this->Cell($w, $h, "", $border, 1, '', 0); + } + } else { + $h = max($h, ($currentY - $prevy)); + $this->y = $prevy; // put cursor at the beginning of text + // design a cell around the text + $this->Cell($w, $h, "", $border, 1, '', 0); + } + } + + // move cursor to specified position + if($ln>0) { + //Go to the beginning of the next line + $this->SetY($currentY); + if($ln == 2) { + $this->SetX($prevx + $w); + } + } else { + // go left or right by case + $this->page = $startpage; + $this->y = $prevy; + $this->SetX($prevx + $w); + } + + return $nl; + } + + /** + * This method prints text from the current position. When the right margin is reached (or the \n character is met) a line break occurs and text continues from the left margin. Upon method exit, the current position is left just at the end of the text. It is possible to put a link on the text.<br /> + * <b>Example:</b><br /> + * <pre> + * //Begin with regular font + * $pdf->SetFont('Arial','',14); + * $pdf->Write(5,'Visit '); + * //Then put a blue underlined link + * $pdf->SetTextColor(0,0,255); + * $pdf->SetFont('','U'); + * $pdf->Write(5,'www.tecnick.com','http://www.tecnick.com'); + * </pre> + * @param float $h Line height + * @param string $txt String to print + * @param mixed $link URL or identifier returned by AddLink() + * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. + * @since 1.5 + * @see SetFont(), SetTextColor(), AddLink(), MultiCell(), SetAutoPageBreak() + */ + public function Write($h, $txt, $link='', $fill=0) { + //Output text in flowing mode + $cw = &$this->CurrentFont['cw']; + // calculating remaining line width ($w) + if ($this->rtl) { + $w = $this->x - $this->lMargin; + } else { + $w = $this->w - $this->rMargin - $this->x; + } + $wmax = $w - 2 * $this->cMargin; + $s = str_replace("\r", '', $txt); + $nb = strlen($s); + // handle single space character + if(($nb==1) AND preg_match("/[ ]/u", $s)) { + if ($this->rtl) { + $this->x -= $this->GetStringWidth($s); + } else { + $this->x += $this->GetStringWidth($s); + } + return; + } + $sep=-1; + $i=0; + $j=0; + $l=0; + $nl=1; + while($i<$nb) { + //Get next character + $c=$s{$i}; + if(preg_match("/[\n]/u", $c)) { + //Explicit line break + $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, '', $fill, $link); + $i++; + $sep = -1; + $j = $i; + $l = 0; + if($nl == 1) { + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + $w = $this->x - $this->lMargin; + } + else { + $this->x = $this->lMargin; + $w = $this->w - $this->rMargin - $this->x; + } + $wmax = ($w - 2 * $this->cMargin); + } + $nl++; + continue; + } + if(preg_match("/[ ]/u", $c)) { + $sep= $i; + } + $l = $this->GetStringWidth(substr($s, $j, $i-$j)); + if($l > $wmax) { + //Automatic line break (word wrapping) + if($sep == -1) { + if((!$this->rtl) AND ($this->x > $this->lMargin)) { + //Move to next line + $this->x = $this->lMargin; + $this->y += $h; + $w=$this->w - $this->rMargin - $this->x; + $wmax=($w - 2 * $this->cMargin); + $i++; + $nl++; + continue; + } + if($this->rtl AND ($this->x < $this->rMargin)) { + //Move to next line + $this->x = $this->w - $this->rMargin; + $this->y += $h; + $w=$this->x - $this->lMargin; + $wmax=($w - 2 * $this->cMargin); + $i++; + $nl++; + continue; + } + if($i==$j) { + $i++; + } + $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, '', $fill, $link); + } + else { + $this->Cell($w, $h, substr($s, $j, $sep-$j), 0, 2, '', $fill, $link); + $i=$sep+1; + } + $sep = -1; + $j = $i; + $l = 0; + if($nl==1) { + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + $w = $this->x - $this->lMargin; + } else { + $this->x = $this->lMargin; + $w = $this->w - $this->rMargin - $this->x; + } + $wmax = ($w - 2 * $this->cMargin); + } + $nl++; + } + else { + $i++; + } + } + + //Last chunk + if($i!=$j) { + $this->Cell($this->GetStringWidth(substr($s, $j)) + 2 * $this->cMargin, $h, substr($s, $j), 0, 0, '', $fill, $link); + } + } + + /** + * Puts an image in the page. The upper-left corner must be given. The dimensions can be specified in different ways:<ul><li>explicit width and height (expressed in user unit)</li><li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li><li>no explicit dimension, in which case the image is put at 72 dpi</li></ul> + * Supported formats are JPEG and PNG. + * For JPEG, all flavors are allowed:<ul><li>gray scales</li><li>true colors (24 bits)</li><li>CMYK (32 bits)</li></ul> + * For PNG, are allowed:<ul><li>gray scales on at most 8 bits (256 levels)</li><li>indexed colors</li><li>true colors (24 bits)</li></ul> + * If a transparent color is defined, it will be taken into account (but will be only interpreted by Acrobat 4 and above).<br /> + * The format can be specified explicitly or inferred from the file extension.<br /> + * It is possible to put a link on the image.<br /> + * Remark: if an image is used several times, only one copy will be embedded in the file.<br /> + * @param string $file Name of the file containing the image. + * @param float $x Abscissa of the upper-left corner. + * @param float $y Ordinate of the upper-left corner. + * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. + * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. + * @param string $type Image format. Possible values are (case insensitive): JPG, JPEG, PNG. If not specified, the type is inferred from the file extension. + * @param mixed $link URL or identifier returned by AddLink(). + * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> + * @since 1.1 + * @see AddLink() + */ + public function Image($file, $x, $y, $w=0, $h=0, $type='', $link='', $align='') { + //Put an image on the page + if(!isset($this->images[$file])) { + //First use of image, get info + if($type == '') { + $pos = strrpos($file,'.'); + if(empty($pos)) { + $this->Error('Image file has no extension and no type was specified: '.$file); + } + $type = substr($file, $pos+1); + } + $type = strtolower($type); + $mqr = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + if($type == 'jpg' or $type == 'jpeg') { + $info=$this->_parsejpg($file); + } elseif($type == 'gif') { + $info=$this->_parsegif($file); + } elseif($type == 'png') { + $info=$this->_parsepng($file); + }else { + //Allow for additional formats + $mtd='_parse'.$type; + if(!method_exists($this,$mtd)) { + $this->Error('Unsupported image type: '.$type); + } + $info=$this->$mtd($file); + } + if($info === false) { + //If false, we cannot process image + return; + } + set_magic_quotes_runtime($mqr); + $info['i']=count($this->images)+1; + $this->images[$file]=$info; + } + else { + $info=$this->images[$file]; + } + //Automatic width and height calculation if needed + if(($w == 0) and ($h == 0)) { + //Put image at 72 dpi + // 2004-06-14 :: Nicola Asuni, scale factor where added + $w = $info['w'] / ($this->imgscale * $this->k); + $h = $info['h'] / ($this->imgscale * $this->k); + } + if($w == 0) { + $w = $h * $info['w'] / $info['h']; + } + if($h == 0) { + $h = $w * $info['h'] / $info['w']; + } + + // 2007-10-19 Warren Sherliker + // Check whether we need a new page first as this does not fit + // Copied from Cell() + if((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) { + // Automatic page break + $this->AddPage($this->CurOrientation); + // Reset coordinates to top fo next page + $x = $this->GetX(); + $y = $this->GetY(); + } + // 2007-10-19 Warren Sherliker: End Edit + + // set bottomcoordinates + $this->img_rb_y = $y + $h; + if ($this->rtl) { + $ximg = ($this->w - $x -$w); + // set left side coordinate + $this->img_rb_x = $ximg; + } else { + $ximg = $x; + // set right side coordinate + $this->img_rb_x = $ximg + $w; + } + $xkimg = $ximg * $this->k; + $this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', $w*$this->k, $h*$this->k, $xkimg, ($this->h-($y+$h))*$this->k, $info['i'])); + + if($link) { + $this->Link($ximg, $y, $w, $h, $link); + } + + // set pointer to align the successive text/objects + switch($align) { + case 'T':{ + $this->y = $y; + $this->x = $this->img_rb_x; + break; + } + case 'M':{ + $this->y = $y + round($h/2); + $this->x = $this->img_rb_x; + break; + } + case 'B':{ + $this->y = $this->img_rb_y; + $this->x = $this->img_rb_x; + break; + } + case 'N':{ + $this->SetY($this->img_rb_y); + break; + } + default:{ + break; + } + } + } + + + /** + * Performs a line break. The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. + * @param float $h The height of the break. By default, the value equals the height of the last printed cell. + * @since 1.0 + * @see Cell() + */ + public function Ln($h='') { + //Line feed; default value is last cell height + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + } else { + $this->x = $this->lMargin; + } + if(is_string($h)) { + $this->y += $this->lasth; + } else { + $this->y += $h; + } + } + + /** + * Returns the relative X value of current position. + * The value is relative to the left border for LTR languages and to the right border for RTL languages. + * @return float + * @since 1.2 + * @see SetX(), GetY(), SetY() + */ + public function GetX() { + //Get x position + if ($this->rtl) { + return ($this->w - $this->x); + } else { + return $this->x; + } + } + + /** + * Returns the absolute X value of current position. + * @return float + * @since 1.2 + * @see SetX(), GetY(), SetY() + */ + public function GetAbsX() { + return $this->x; + } + + /** + * Returns the ordinate of the current position. + * @return float + * @since 1.0 + * @see SetY(), GetX(), SetX() + */ + public function GetY() { + //Get y position + return $this->y; + } + + /** + * Defines the abscissa of the current position. + * If the passed value is negative, it is relative to the right of the page (or left if language is RTL). + * @param float $x The value of the abscissa. + * @since 1.2 + * @see GetX(), GetY(), SetY(), SetXY() + */ + public function SetX($x) { + //Set x position + if ($this->rtl) { + if($x >= 0) { + $this->x = $this->w - $x; + } else { + $this->x = abs($x); + } + } else { + if($x >= 0) { + $this->x = $x; + } else { + $this->x = $this->w + $x; + } + } + } + + /** + * Moves the current abscissa back to the left margin and sets the ordinate. + * If the passed value is negative, it is relative to the bottom of the page. + * @param float $y The value of the ordinate. + * @since 1.0 + * @see GetX(), GetY(), SetY(), SetXY() + */ + public function SetY($y) { + //Set y position and reset x + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + } else { + $this->x = $this->lMargin; + } + if($y >= 0) { + $this->y = $y; + } else { + $this->y = $this->h + $y; + } + } + + + /** + * Defines the abscissa and ordinate of the current position. If the passed values are negative, they are relative respectively to the right and bottom of the page. + * @param float $x The value of the abscissa + * @param float $y The value of the ordinate + * @since 1.2 + * @see SetX(), SetY() + */ + public function SetXY($x, $y) { + //Set x and y positions + $this->SetY($y); + $this->SetX($x); + } + + /** + * Send the document to a given destination: string, local file or browser. In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br /> + * The method first calls Close() if necessary to terminate the document. + * @param string $name The name of the file. If not given, the document will be sent to the browser (destination I) with the name doc.pdf. + * @param string $dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser. The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li></ul>If the parameter is not specified but a name is given, destination is F. If no parameter is specified at all, destination is I.<br />Note: for compatibility with previous versions, a boolean value is also accepted (false for F and true for D). + * @since 1.0 + * @see Close() + */ + public function Output($name='',$dest='') { + //Output PDF to some destination + //Finish document if necessary + if($this->state < 3) { + $this->Close(); + } + //Normalize parameters + if(is_bool($dest)) { + $dest=$dest ? 'D' : 'F'; + } + $dest=strtoupper($dest); + if($dest=='') { + if($name=='') { + $name='doc.pdf'; + $dest='I'; + } else { + $dest='F'; + } + } + switch($dest) { + case 'I': { + //Send to standard output + //if(ob_get_contents()) { + // $this->Error('Some data has already been output, can\'t send PDF file'); + //} + ob_end_clean(); + ob_start(); + if(php_sapi_name()!='cli') { + //We send to a browser + header('Content-Type: application/pdf'); + if(headers_sent()) { + $this->Error('Some data has already been output to browser, can\'t send PDF file'); + } + header('Content-Length: '.strlen($this->buffer)); + header('Content-disposition: inline; filename="'.$name.'"'); + } + echo $this->buffer; + break; + } + case 'D': { + //Download file + // if(ob_get_contents()) { + // $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); + //} + + if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { + header('Content-Type: application/force-download'); + } else { + header('Content-Type: application/octet-stream'); + } + if(headers_sent()) { + $this->Error('Some data has already been output to browser, can\'t send PDF file'); + } + header('Content-Length: '.strlen($this->buffer)); + header('Content-disposition: attachment; filename="'.$name.'"'); + echo $this->buffer; + break; + } + case 'F': { + //Save to local file + $f=fopen($name,'wb'); + if(!$f) { + $this->Error('Unable to create output file: '.$name); + } + fwrite($f,$this->buffer,strlen($this->buffer)); + fclose($f); + break; + } + case 'S': { + //Return as a string + return $this->buffer; + } + default: { + $this->Error('Incorrect output destination: '.$dest); + } + } + return ''; + } + + // Protected methods + + /** + * Check for locale-related bug + * @access protected + */ + protected function _dochecks() { + //Check for locale-related bug + if(1.1==1) { + $this->Error('Don\'t alter the locale before including class file'); + } + //Check for decimal separator + if(sprintf('%.1f',1.0)!='1.0') { + setlocale(LC_NUMERIC,'C'); + } + } + + /** + * Return fonts path + * @access protected + */ + protected function _getfontpath() { + if(!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/font')) { + define('K_PATH_FONTS', dirname(__FILE__).'/font/'); + } + return defined('K_PATH_FONTS') ? K_PATH_FONTS : ''; + } + + /** + * Start document + * @access protected + */ + protected function _begindoc() { + //Start document + $this->state=1; + $this->_out('%PDF-'.$this->PDFVersion); + } + + /** + * _putpages + * @access protected + */ + protected function _putpages() { + $nb = $this->page; + if(!empty($this->AliasNbPages)) { + $nbstr = $this->UTF8ToUTF16BE($nb, false); + //Replace number of pages + for($n=1;$n<=$nb;$n++) { + $this->pages[$n]=str_replace($this->AliasNbPages, $nbstr, $this->pages[$n]); + } + } + if($this->DefOrientation=='P') { + $wPt=$this->fwPt; + $hPt=$this->fhPt; + } + else { + $wPt=$this->fhPt; + $hPt=$this->fwPt; + } + $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + for($n=1;$n<=$nb;$n++) { + //Page + $this->_newobj(); + $this->_out('<</Type /Page'); + $this->_out('/Parent 1 0 R'); + if(isset($this->OrientationChanges[$n])) { + $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$hPt,$wPt)); + } + $this->_out('/Resources 2 0 R'); + if(isset($this->PageLinks[$n])) { + //Links + $annots='/Annots ['; + foreach($this->PageLinks[$n] as $pl) { + $rect=sprintf('%.2f %.2f %.2f %.2f',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]); + $annots.='<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] '; + if(is_string($pl[4])) { + $annots.='/A <</S /URI /URI '.$this->_uristring($pl[4]).'>>>>'; + } + else { + $l=$this->links[$pl[4]]; + $h=isset($this->OrientationChanges[$l[0]]) ? $wPt : $hPt; + $annots.=sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>',1+2*$l[0],$h-$l[1]*$this->k); + } + } + $this->_out($annots.']'); + } + $this->_out('/Contents '.($this->n+1).' 0 R>>'); + $this->_out('endobj'); + //Page content + $p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n]; + $this->_newobj(); + $this->_out('<<'.$filter.'/Length '.strlen($p).'>>'); + $this->_putstream($p); + $this->_out('endobj'); + } + //Pages root + $this->offsets[1]=strlen($this->buffer); + $this->_out('1 0 obj'); + $this->_out('<</Type /Pages'); + $kids='/Kids ['; + for($i=0;$i<$nb;$i++) { + $kids.=(3+2*$i).' 0 R '; + } + $this->_out($kids.']'); + $this->_out('/Count '.$nb); + $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$wPt,$hPt)); + $this->_out('>>'); + $this->_out('endobj'); + } + + /** + * Adds fonts + * _putfonts + * @access protected + */ + protected function _putfonts() { + $nf=$this->n; + foreach($this->diffs as $diff) { + //Encodings + $this->_newobj(); + $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>'); + $this->_out('endobj'); + } + $mqr=get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + foreach($this->FontFiles as $file=>$info) { + //Font file embedding + $this->_newobj(); + $this->FontFiles[$file]['n']=$this->n; + $font=''; + $f=fopen($this->_getfontpath().strtolower($file),'rb',1); + if(!$f) { + $this->Error('Font file not found: '.$file); + } + while(!feof($f)) { + $font .= fread($f, 8192); + } + fclose($f); + $compressed=(substr($file,-2)=='.z'); + if(!$compressed && isset($info['length2'])) { + $header=(ord($font{0})==128); + if($header) { + //Strip first binary header + $font=substr($font,6); + } + if($header && ord($font{$info['length1']})==128) { + //Strip second binary header + $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6); + } + } + $this->_out('<</Length '.strlen($font)); + if($compressed) { + $this->_out('/Filter /FlateDecode'); + } + $this->_out('/Length1 '.$info['length1']); + if(isset($info['length2'])) { + $this->_out('/Length2 '.$info['length2'].' /Length3 0'); + } + $this->_out('>>'); + $this->_putstream($font); + $this->_out('endobj'); + } + set_magic_quotes_runtime($mqr); + foreach($this->fonts as $k=>$font) { + //Font objects + $this->fonts[$k]['n']=$this->n+1; + $type=$font['type']; + $name=$font['name']; + if($type=='core') { + //Standard font + $this->_newobj(); + $this->_out('<</Type /Font'); + $this->_out('/BaseFont /'.$name); + $this->_out('/Subtype /Type1'); + if($name!='Symbol' && $name!='ZapfDingbats') { + $this->_out('/Encoding /WinAnsiEncoding'); + } + $this->_out('>>'); + $this->_out('endobj'); + } elseif($type=='Type1' OR $type=='TrueType') { + //Additional Type1 or TrueType font + $this->_newobj(); + $this->_out('<</Type /Font'); + $this->_out('/BaseFont /'.$name); + $this->_out('/Subtype /'.$type); + $this->_out('/FirstChar 32 /LastChar 255'); + $this->_out('/Widths '.($this->n+1).' 0 R'); + $this->_out('/FontDescriptor '.($this->n+2).' 0 R'); + if($font['enc']) { + if(isset($font['diff'])) { + $this->_out('/Encoding '.($nf+$font['diff']).' 0 R'); + } else { + $this->_out('/Encoding /WinAnsiEncoding'); + } + } + $this->_out('>>'); + $this->_out('endobj'); + //Widths + $this->_newobj(); + $cw=&$font['cw']; + $s='['; + for($i=32;$i<=255;$i++) { + $s.=$cw[chr($i)].' '; + } + $this->_out($s.']'); + $this->_out('endobj'); + //Descriptor + $this->_newobj(); + $s='<</Type /FontDescriptor /FontName /'.$name; + foreach($font['desc'] as $k=>$v) { + $s.=' /'.$k.' '.$v; + } + $file = $font['file']; + if($file) { + $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R'; + } + $this->_out($s.'>>'); + $this->_out('endobj'); + } else { + //Allow for additional types + $mtd='_put'.strtolower($type); + if(!method_exists($this, $mtd)) { + $this->Error('Unsupported font type: '.$type); + } + $this->$mtd($font); + } + } + } + + /** + * _putimages + * @access protected + */ + protected function _putimages() { + $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + reset($this->images); + while(list($file,$info)=each($this->images)) { + $this->_newobj(); + $this->images[$file]['n']=$this->n; + $this->_out('<</Type /XObject'); + $this->_out('/Subtype /Image'); + $this->_out('/Width '.$info['w']); + $this->_out('/Height '.$info['h']); + + if (isset($info["masked"])) { + $this->_out('/SMask '.($this->n-1).' 0 R'); + } + + if($info['cs']=='Indexed') { + $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); + } + else { + $this->_out('/ColorSpace /'.$info['cs']); + if($info['cs']=='DeviceCMYK') { + $this->_out('/Decode [1 0 1 0 1 0 1 0]'); + } + } + $this->_out('/BitsPerComponent '.$info['bpc']); + if(isset($info['f'])) { + $this->_out('/Filter /'.$info['f']); + } + if(isset($info['parms'])) { + $this->_out($info['parms']); + } + if(isset($info['trns']) and is_array($info['trns'])) { + $trns=''; + for($i=0;$i<count($info['trns']);$i++) { + $trns.=$info['trns'][$i].' '.$info['trns'][$i].' '; + } + $this->_out('/Mask ['.$trns.']'); + } + $this->_out('/Length '.strlen($info['data']).'>>'); + $this->_putstream($info['data']); + unset($this->images[$file]['data']); + $this->_out('endobj'); + //Palette + if($info['cs']=='Indexed') { + $this->_newobj(); + $pal=($this->compress) ? gzcompress($info['pal']) : $info['pal']; + $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>'); + $this->_putstream($pal); + $this->_out('endobj'); + } + } + } + + /** + * _putxobjectdict + * @access protected + */ + function _putxobjectdict() { + foreach($this->images as $image) { + $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); + } + } + + /** + * _putresourcedict + * @access protected + */ + function _putresourcedict(){ + $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); + $this->_out('/Font <<'); + foreach($this->fonts as $font) { + $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R'); + } + $this->_out('>>'); + $this->_out('/XObject <<'); + $this->_putxobjectdict(); + $this->_out('>>'); + } + + /** + * _putresources + * @access protected + */ + function _putresources() { + $this->_putfonts(); + $this->_putimages(); + //Resource dictionary + $this->offsets[2]=strlen($this->buffer); + $this->_out('2 0 obj'); + $this->_out('<<'); + $this->_putresourcedict(); + $this->_out('>>'); + $this->_out('endobj'); + $this->_putjavascript(); + $this->_putbookmarks(); + // encryption + if ($this->encrypted) { + $this->_newobj(); + $this->enc_obj_id = $this->n; + $this->_out('<<'); + $this->_putencryption(); + $this->_out('>>'); + $this->_out('endobj'); + } + } + + /** + * _putinfo + * Adds some meta information + * @access protected + */ + protected function _putinfo() { + $this->_out('/CreationDate ('.$this->_escape('D:'.date('YmdHis')).')'); + $this->_out('/ModDate ('.$this->_escape('D:'.date('YmdHis')).')'); + $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER)); + if(!empty($this->title)) { + $this->_out('/Title '.$this->_textstring($this->title)); + } + if(!empty($this->subject)) { + $this->_out('/Subject '.$this->_textstring($this->subject)); + } + if(!empty($this->author)) { + $this->_out('/Author '.$this->_textstring($this->author)); + } + if(!empty($this->keywords)) { + $this->_out('/Keywords '.$this->_textstring($this->keywords)); + } + if(!empty($this->creator)) { + $this->_out('/Creator '.$this->_textstring($this->creator)); + } + } + + /** + * _putcatalog + * @access protected + */ + protected function _putcatalog() { + $this->_out('/Type /Catalog'); + $this->_out('/Pages 1 0 R'); + if($this->ZoomMode=='fullpage') { + $this->_out('/OpenAction [3 0 R /Fit]'); + } + elseif($this->ZoomMode=='fullwidth') { + $this->_out('/OpenAction [3 0 R /FitH null]'); + } + elseif($this->ZoomMode=='real') { + $this->_out('/OpenAction [3 0 R /XYZ null null 1]'); + } + elseif(!is_string($this->ZoomMode)) { + $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode/100).']'); + } + if($this->LayoutMode=='single') { + $this->_out('/PageLayout /SinglePage'); + } + elseif($this->LayoutMode=='continuous') { + $this->_out('/PageLayout /OneColumn'); + } + elseif($this->LayoutMode=='two') { + $this->_out('/PageLayout /TwoColumnLeft'); + } + if (!empty($this->javascript)) { + $this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>'); + } + if(count($this->outlines)>0) { + $this->_out('/Outlines '.$this->OutlineRoot.' 0 R'); + $this->_out('/PageMode /UseOutlines'); + } + if($this->rtl) { + $this->_out('/ViewerPreferences << /Direction /R2L >>'); + } + } + + /** + * _puttrailer + * @access protected + */ + protected function _puttrailer() { + $this->_out('/Size '.($this->n+1)); + $this->_out('/Root '.$this->n.' 0 R'); + $this->_out('/Info '.($this->n-1).' 0 R'); + if ($this->encrypted) { + $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R'); + $this->_out('/ID [()()]'); + } + } + + /** + * _putheader + * @access protected + */ + function _putheader() { + $this->_out('%PDF-'.$this->PDFVersion); + } + + /** + * _enddoc + * @access protected + */ + protected function _enddoc() { + $this->_putheader(); + $this->_putpages(); + $this->_putresources(); + //Info + $this->_newobj(); + $this->_out('<<'); + $this->_putinfo(); + $this->_out('>>'); + $this->_out('endobj'); + //Catalog + $this->_newobj(); + $this->_out('<<'); + $this->_putcatalog(); + $this->_out('>>'); + $this->_out('endobj'); + //Cross-ref + $o=strlen($this->buffer); + $this->_out('xref'); + $this->_out('0 '.($this->n+1)); + $this->_out('0000000000 65535 f '); + for($i=1;$i<=$this->n;$i++) { + $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i])); + } + //Trailer + $this->_out('trailer'); + $this->_out('<<'); + $this->_puttrailer(); + $this->_out('>>'); + $this->_out('startxref'); + $this->_out($o); + $this->_out('%%EOF'); + $this->state=3; + } + + /** + * _beginpage + * @access protected + */ + protected function _beginpage($orientation) { + $this->page++; + $this->pages[$this->page]=''; + $this->state=2; + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + } else { + $this->x = $this->lMargin; + } + $this->y = $this->tMargin; + $this->FontFamily=''; + //Page orientation + if(empty($orientation)) { + $orientation=$this->DefOrientation; + } + else { + $orientation=strtoupper($orientation{0}); + if($orientation!=$this->DefOrientation) { + $this->OrientationChanges[$this->page]=true; + } + } + if($orientation!=$this->CurOrientation) { + //Change orientation + if($orientation=='P') { + $this->wPt=$this->fwPt; + $this->hPt=$this->fhPt; + $this->w=$this->fw; + $this->h=$this->fh; + } + else { + $this->wPt=$this->fhPt; + $this->hPt=$this->fwPt; + $this->w=$this->fh; + $this->h=$this->fw; + } + $this->PageBreakTrigger=$this->h-$this->bMargin; + $this->CurOrientation=$orientation; + } + } + + /** + * End of page contents + * @access protected + */ + protected function _endpage() { + $this->state=1; + } + + /** + * Begin a new object + * @access protected + */ + protected function _newobj() { + $this->n++; + $this->offsets[$this->n]=strlen($this->buffer); + $this->_out($this->n.' 0 obj'); + } + + /** + * Underline text + * @param int $x X coordinate + * @param int $y Y coordinate + * @param string $txt text to underline + * @access protected + */ + protected function _dounderline($x, $y, $txt) { + $up = $this->CurrentFont['up']; + $ut = $this->CurrentFont['ut']; + $w = $this->GetStringWidth($txt) + $this->ws * substr_count($txt,' '); + return sprintf('%.2f %.2f %.2f %.2f re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt); + } + + + // REWRITTEN by Warren Sherliker wsherliker@gmail.com + // altered to allow compatibility with all sorts of image formats including gif. + // Can easily extend to work with others + // such as gd xbm etc. which are all supported by php 5+ + // (Requires GD library) + + /** + * Extract info from a JPEG file + * @param string $file image file to parse + * @return string + * @access protected + */ + protected function _parsejpg($file) { + if(!function_exists('imagecreatefromjpeg')) { + // GD is not installed, try legacy method + return $this->_legacyparsejpg($file); + } + $a=getimagesize($file); + if(empty($a)) { + $this->Error('Missing or incorrect image file: '.$file); + } + if($a[2]!=2) { + $this->Error('Not a JPEG file: '.$file); + } + $jpeg = imagecreatefromjpeg($file); + return $this->outputjpg($file, $jpeg); + } + + /** + * Extract info from a GIF file + * @param string $file image file to parse + * @return string + * @access protected + */ + protected function _parsegif($file) { + if(!function_exists('imagecreatefromgif')) { + // PDF doesn't support native GIF and GD is not installed + return false; + } + $a=getimagesize($file); + if(empty($a)) { + $this->Error('Missing or incorrect image file: '.$file); + } + if($a[2]!=1) { + $this->Error('Not a GIF file: '.$file); + } + // Temporary convert file to jpg and then delete this temp data file + $gif = imagecreatefromgif($file); + return $this->toJPEG($file, $gif); + } + + /** + * Extract info from a PNG file + * @param string $file image file to parse + * @return string + * @access protected + */ + protected function _parsepng($file) { + if(!function_exists('imagecreatefrompng')) { + // GD is not installed, try legacy method + return $this->_legacyparsepng($file); + } + $f=fopen($file,'rb'); + if(empty($f)) { + $this->Error('Can\'t open image file: '.$file); + } + //Check signature + if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { + $this->Error('Not a PNG file: '.$file); + } + //Read header chunk + fread($f,4); + if(fread($f,4)!='IHDR') { + $this->Error('Incorrect PNG file: '.$file); + } + // Temporary convert file to jpg and then delete this temp data file + $a=getimagesize($file); + $png = imagecreatefrompng($file); + return $this->toJPEG($file, $png); + } + + /** + * Extract info from a JPEG file without using GD + * @param string $file image file to parse + * @return string + * @access protected + */ + protected function _legacyparsejpg($file) { + $a=GetImageSize($file); + if(empty($a)) { + $this->Error('Missing or incorrect image file: '.$file); + } + if($a[2]!=2) { + $this->Error('Not a JPEG file: '.$file); + } + if(!isset($a['channels']) or $a['channels']==3) { + $colspace='DeviceRGB'; + } + elseif($a['channels']==4) { + $colspace='DeviceCMYK'; + } + else { + $colspace='DeviceGray'; + } + $bpc=isset($a['bits']) ? $a['bits'] : 8; + //Read whole file + $f=fopen($file,'rb'); + $data=''; + while(!feof($f)) { + $data.=fread($f,4096); + } + fclose($f); + return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data); + } + + /** + * Extract info from a PNG file without using GD + * @param string $file image file to parse + * @return string + * @access protected + */ + protected function _legacyparsepng($file) { + $f=fopen($file,'rb'); + if(empty($f)) { + $this->Error('Can\'t open image file: '.$file); + } + //Check signature + if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { + $this->Error('Not a PNG file: '.$file); + } + //Read header chunk + fread($f,4); + if(fread($f,4)!='IHDR') { + $this->Error('Incorrect PNG file: '.$file); + } + $w=$this->_freadint($f); + $h=$this->_freadint($f); + $bpc=ord(fread($f,1)); + if($bpc>8) { + $this->Error('16-bit depth not supported: '.$file); + } + $ct=ord(fread($f,1)); + if($ct==0) { + $colspace='DeviceGray'; + } + elseif($ct==2) { + $colspace='DeviceRGB'; + } + elseif($ct==3) { + $colspace='Indexed'; + } + else { + $this->Error('Alpha channel not supported: '.$file); + } + if(ord(fread($f,1))!=0) { + $this->Error('Unknown compression method: '.$file); + } + if(ord(fread($f,1))!=0) { + $this->Error('Unknown filter method: '.$file); + } + if(ord(fread($f,1))!=0) { + $this->Error('Interlacing not supported: '.$file); + } + fread($f,4); + $parms='/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>'; + //Scan chunks looking for palette, transparency and image data + $pal=''; + $trns=''; + $data=''; + do { + $n=$this->_freadint($f); + $type=fread($f,4); + if($type=='PLTE') { + //Read palette + $pal=fread($f,$n); + fread($f,4); + } + elseif($type=='tRNS') { + //Read transparency info + $t=fread($f,$n); + if($ct==0) { + $trns=array(ord(substr($t,1,1))); + } + elseif($ct==2) { + $trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1))); + } + else { + $pos=strpos($t,chr(0)); + if($pos!==false) { + $trns=array($pos); + } + } + fread($f,4); + } + elseif($type=='IDAT') { + //Read image data block + $data.=fread($f,$n); + fread($f,4); + } + elseif($type=='IEND') { + break; + } + else { + fread($f,$n+4); + } + } + while($n); + if($colspace=='Indexed' and empty($pal)) { + $this->Error('Missing palette in '.$file); + } + fclose($f); + return array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'parms'=>$parms, 'pal'=>$pal, 'trns'=>$trns, 'data'=>$data); + } + + /** + * Convert the loaded php image to a JPEG and then return a structure for the PDF creator. + * @param string $file Image file name. + * @param image $image Image object. + * return image JPEG image object. + * @access protected + */ + protected function toJPEG($file, $image) { + if ($image) { + // output + $tempname = tempnam(K_PATH_CACHE,'jpg'); + imagejpeg($image, $tempname, 100); + imagedestroy($image); + $retvars = $this->outputjpg($tempname); + // tidy up by removing temporary image + unlink($tempname); + return $retvars; + } else { + $this->Error('Can\'t open image file: '.$file); + } + } + + /** + * Get a JPEG filename and return a structure for the PDF creator. + * @param string $filename JPEG file name. + * @return array structure containing the image data + * @access protected + */ + protected function outputjpg($filename) { + $a=getimagesize($filename); + + if(!isset($a['channels']) or $a['channels']==3) { + $colspace='DeviceRGB'; + } + elseif($a['channels']==4) { + $colspace='DeviceCMYK'; + } + else { + $colspace='DeviceGray'; + } + $bpc=isset($a['bits']) ? $a['bits'] : 8; + //Read whole file + + $f=fopen($filename,'rb'); + $data=''; + while(!feof($f)) { + $data.=fread($f,4096); + } + fclose($f); + + return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data); + } + + /// END OF REWRITE BY Warren Sherliker wsherliker@gmail.com + + /** + * Read a 4-byte integer from file + * @param string $f file name. + * @return 4-byte integer + * @access protected + */ + protected function _freadint($f) { + $a=unpack('Ni',fread($f,4)); + return $a['i']; + } + + /** + * Format a text string for meta information + * @param string $s string to escape. + * @return string escaped string. + * @access protected + */ + protected function _textstring($s) { + if($this->isunicode) { + //Convert string to UTF-16BE + $s = $this->UTF8ToUTF16BE($s, true); + } + if ($this->encrypted) { + $s = $this->_RC4($this->_objectkey($this->n), $s); + } + return '('. $this->_escape($s).')'; + } + + /** + * Format an URI string + * @param string $s string to escape. + * @return string escaped string. + * @access protected + */ + protected function _uristring($s) { + if ($this->encrypted) { + $s = $this->_RC4($this->_objectkey($this->n), $s); + } + return '('.$this->_escape($s).')'; + } + + /** + * Format a text string + * @param string $s string to escape. + * @return string escaped string. + * @access protected + */ + protected function _escapetext($s) { + if($this->isunicode) { + //Convert string to UTF-16BE and reverse RTL language + $s = $this->utf8StrRev($s, false, $this->tmprtl); + } + return $this->_escape($s); + } + + /** + * Add \ before \, ( and ) + * @param string $s string to escape. + * @return string escaped string. + * @access protected + */ + protected function _escape($s) { + // the chr(13) substitution fixes the Bugs item #1421290. + return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); + } + + /** + * Output a stream. + * @param string $s string to output. + * @access protected + */ + protected function _putstream($s) { + if ($this->encrypted) { + $s = $this->_RC4($this->_objectkey($this->n), $s); + } + $this->_out('stream'); + $this->_out($s); + $this->_out('endstream'); + } + + /** + * Output a string to the document. + * @param string $s string to output. + * @access protected + */ + protected function _out($s) { + if($this->state==2) { + $this->pages[$this->page] .= $s."\n"; + } + else { + $this->buffer .= $s."\n"; + } + } + + /** + * Adds unicode fonts.<br> + * Based on PDF Reference 1.3 (section 5) + * @access protected + * @author Nicola Asuni + * @since 1.52.0.TC005 (2005-01-05) + */ + protected function _puttruetypeunicode($font) { + // Type0 Font + // A composite font composed of other fonts, organized hierarchically + $this->_newobj(); + $this->_out('<</Type /Font'); + $this->_out('/Subtype /Type0'); + $this->_out('/BaseFont /'.$font['name'].''); + $this->_out('/Encoding /Identity-H'); //The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values. + $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]'); + $this->_out('/ToUnicode '.($this->n + 2).' 0 R'); + $this->_out('>>'); + $this->_out('endobj'); + + // CIDFontType2 + // A CIDFont whose glyph descriptions are based on TrueType font technology + $this->_newobj(); + $this->_out('<</Type /Font'); + $this->_out('/Subtype /CIDFontType2'); + $this->_out('/BaseFont /'.$font['name'].''); + $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R'); + $this->_out('/FontDescriptor '.($this->n + 3).' 0 R'); + if (isset($font['desc']['MissingWidth'])){ + $this->_out('/DW '.$font['desc']['MissingWidth'].''); // The default width for glyphs in the CIDFont MissingWidth + } + $w = ""; + foreach ($font['cw'] as $cid => $width) { + $w .= ''.$cid.' ['.$width.'] '; // define a specific width for each individual CID + } + $this->_out('/W ['.$w.']'); // A description of the widths for the glyphs in the CIDFont + $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R'); + $this->_out('>>'); + $this->_out('endobj'); + + // ToUnicode + // is a stream object that contains the definition of the CMap + // (PDF Reference 1.3 chap. 5.9) + $this->_newobj(); + $this->_out('<</Length 345>>'); + $this->_out('stream'); + $this->_out('/CIDInit /ProcSet findresource begin'); + $this->_out('12 dict begin'); + $this->_out('begincmap'); + $this->_out('/CIDSystemInfo'); + $this->_out('<</Registry (Adobe)'); + $this->_out('/Ordering (UCS)'); + $this->_out('/Supplement 0'); + $this->_out('>> def'); + $this->_out('/CMapName /Adobe-Identity-UCS def'); + $this->_out('/CMapType 2 def'); + $this->_out('1 begincodespacerange'); + $this->_out('<0000> <FFFF>'); + $this->_out('endcodespacerange'); + $this->_out('1 beginbfrange'); + $this->_out('<0000> <FFFF> <0000>'); + $this->_out('endbfrange'); + $this->_out('endcmap'); + $this->_out('CMapName currentdict /CMap defineresource pop'); + $this->_out('end'); + $this->_out('end'); + $this->_out('endstream'); + $this->_out('endobj'); + + // CIDSystemInfo dictionary + // A dictionary containing entries that define the character collection of the CIDFont. + $this->_newobj(); + $this->_out('<</Registry (Adobe)'); // A string identifying an issuer of character collections + $this->_out('/Ordering (UCS)'); // A string that uniquely names a character collection issued by a specific registry + $this->_out('/Supplement 0'); // The supplement number of the character collection. + $this->_out('>>'); + $this->_out('endobj'); + + // Font descriptor + // A font descriptor describing the CIDFont default metrics other than its glyph widths + $this->_newobj(); + $this->_out('<</Type /FontDescriptor'); + $this->_out('/FontName /'.$font['name']); + foreach ($font['desc'] as $key => $value) { + $this->_out('/'.$key.' '.$value); + } + if ($font['file']) { + // A stream containing a TrueType font program + $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'); + } + $this->_out('>>'); + $this->_out('endobj'); + + // Embed CIDToGIDMap + // A specification of the mapping from CIDs to glyph indices + $this->_newobj(); + $ctgfile = $this->_getfontpath().strtolower($font['ctg']); + if(!file_exists($ctgfile)) { + $this->Error('Font file not found: '.$ctgfile); + } + $size = filesize($ctgfile); + $this->_out('<</Length '.$size.''); + if(substr($ctgfile, -2) == '.z') { // check file extension + /* Decompresses data encoded using the public-domain + zlib/deflate compression method, reproducing the + original text or binary data */ + $this->_out('/Filter /FlateDecode'); + } + $this->_out('>>'); + $this->_putstream(file_get_contents($ctgfile)); + $this->_out('endobj'); + } + + /** + * Converts UTF-8 strings to codepoints array.<br> + * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br> + * Based on: http://www.faqs.org/rfcs/rfc3629.html + * <pre> + * Char. number range | UTF-8 octet sequence + * (hexadecimal) | (binary) + * --------------------+----------------------------------------------- + * 0000 0000-0000 007F | 0xxxxxxx + * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * --------------------------------------------------------------------- + * + * ABFN notation: + * --------------------------------------------------------------------- + * UTF8-octets = *( UTF8-char ) + * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 + * UTF8-1 = %x00-7F + * UTF8-2 = %xC2-DF UTF8-tail + * + * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / + * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) + * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / + * %xF4 %x80-8F 2( UTF8-tail ) + * UTF8-tail = %x80-BF + * --------------------------------------------------------------------- + * </pre> + * @param string $str string to process. + * @return array containing codepoints (UTF-8 characters values) + * @access protected + * @author Nicola Asuni + * @since 1.53.0.TC005 (2005-01-05) + */ + protected function UTF8StringToArray($str) { + if(!$this->isunicode) { + return $str; // string is not in unicode + } + $unicode = array(); // array containing unicode values + $bytes = array(); // array containing single character byte sequences + $numbytes = 1; // number of octetc needed to represent the UTF-8 character + + $str .= ""; // force $str to be a string + $length = strlen($str); + + for($i = 0; $i < $length; $i++) { + $char = ord($str{$i}); // get one string character at time + if(count($bytes) == 0) { // get starting octect + if ($char <= 0x7F) { + $unicode[] = $char; // use the character "as is" because is ASCII + $numbytes = 1; + } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) + $bytes[] = ($char - 0xC0) << 0x06; + $numbytes = 2; + } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) + $bytes[] = ($char - 0xE0) << 0x0C; + $numbytes = 3; + } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) + $bytes[] = ($char - 0xF0) << 0x12; + $numbytes = 4; + } else { + // use replacement character for other invalid sequences + $unicode[] = 0xFFFD; + $bytes = array(); + $numbytes = 1; + } + } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN + $bytes[] = $char - 0x80; + if (count($bytes) == $numbytes) { + // compose UTF-8 bytes to a single unicode value + $char = $bytes[0]; + for($j = 1; $j < $numbytes; $j++) { + $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); + } + if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) { + /* The definition of UTF-8 prohibits encoding character numbers between + U+D800 and U+DFFF, which are reserved for use with the UTF-16 + encoding form (as surrogate pairs) and do not directly represent + characters. */ + $unicode[] = 0xFFFD; // use replacement character + } + else { + $unicode[] = $char; // add char to array + } + // reset data for next char + $bytes = array(); + $numbytes = 1; + } + } else { + // use replacement character for other invalid sequences + $unicode[] = 0xFFFD; + $bytes = array(); + $numbytes = 1; + } + } + return $unicode; + } + + /** + * Converts UTF-8 strings to UTF16-BE.<br> + * @param string $str string to process. + * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) + * @return string + * @access protected + * @author Nicola Asuni + * @since 1.53.0.TC005 (2005-01-05) + * @uses UTF8StringToArray(), arrUTF8ToUTF16BE() + */ + protected function UTF8ToUTF16BE($str, $setbom=true) { + if(!$this->isunicode) { + return $str; // string is not in unicode + } + $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values + return $this->arrUTF8ToUTF16BE($unicode, $setbom); + } + + /** + * Converts array of UTF-8 characters to UTF16-BE string.<br> + * Based on: http://www.faqs.org/rfcs/rfc2781.html + * <pre> + * Encoding UTF-16: + * + * Encoding of a single character from an ISO 10646 character value to + * UTF-16 proceeds as follows. Let U be the character number, no greater + * than 0x10FFFF. + * + * 1) If U < 0x10000, encode U as a 16-bit unsigned integer and + * terminate. + * + * 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, + * U' must be less than or equal to 0xFFFFF. That is, U' can be + * represented in 20 bits. + * + * 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and + * 0xDC00, respectively. These integers each have 10 bits free to + * encode the character value, for a total of 20 bits. + * + * 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order + * bits of W1 and the 10 low-order bits of U' to the 10 low-order + * bits of W2. Terminate. + * + * Graphically, steps 2 through 4 look like: + * U' = yyyyyyyyyyxxxxxxxxxx + * W1 = 110110yyyyyyyyyy + * W2 = 110111xxxxxxxxxx + * </pre> + * @param array $unicode array containing UTF-8 unicode values + * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) + * @return string + * @access protected + * @author Nicola Asuni + * @since 2.1.000 (2008-01-08) + * @see UTF8ToUTF16BE() + */ + protected function arrUTF8ToUTF16BE($unicode, $setbom=true) { + $outstr = ""; // string to be returned + if ($setbom) { + $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) + } + foreach($unicode as $char) { + if($char == 0xFFFD) { + $outstr .= "\xFF\xFD"; // replacement character + } elseif ($char < 0x10000) { + $outstr .= chr($char >> 0x08); + $outstr .= chr($char & 0xFF); + } else { + $char -= 0x10000; + $w1 = 0xD800 | ($char >> 0x10); + $w2 = 0xDC00 | ($char & 0x3FF); + $outstr .= chr($w1 >> 0x08); + $outstr .= chr($w1 & 0xFF); + $outstr .= chr($w2 >> 0x08); + $outstr .= chr($w2 & 0xFF); + } + } + return $outstr; + } + // ==================================================== + + /** + * Set header font. + * @param array $font font + * @since 1.1 + */ + public function setHeaderFont($font) { + $this->header_font = $font; + } + + /** + * Set footer font. + * @param array $font font + * @since 1.1 + */ + public function setFooterFont($font) { + $this->footer_font = $font; + } + + /** + * Set language array. + * @param array $language + * @since 1.1 + */ + public function setLanguageArray($language) { + $this->l = $language; + $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; + } + + /** + * Set document barcode. + * @param string $bc barcode + */ + public function setBarcode($bc="") { + $this->barcode = $bc; + } + + /** + * Print Barcode. + * @param int $x x position in user units + * @param int $y y position in user units + * @param int $w width in user units + * @param int $h height position in user units + * @param string $type type of barcode (I25, C128A, C128B, C128C, C39) + * @param string $style barcode style + * @param string $font font for text + * @param int $xres x resolution + * @param string $code code to print + */ + public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) { + require_once(dirname(__FILE__)."/barcode/barcode.php"); + require_once(dirname(__FILE__)."/barcode/i25object.php"); + require_once(dirname(__FILE__)."/barcode/c39object.php"); + require_once(dirname(__FILE__)."/barcode/c128aobject.php"); + require_once(dirname(__FILE__)."/barcode/c128bobject.php"); + require_once(dirname(__FILE__)."/barcode/c128cobject.php"); + + if (empty($code)) { + return; + } + + if (empty($style)) { + $style = BCS_ALIGN_LEFT; + $style |= BCS_IMAGE_PNG; + $style |= BCS_TRANSPARENT; + //$style |= BCS_BORDER; + //$style |= BCS_DRAW_TEXT; + //$style |= BCS_STRETCH_TEXT; + //$style |= BCS_REVERSE_COLOR; + } + if (empty($font)) {$font = BCD_DEFAULT_FONT;} + if (empty($xres)) {$xres = BCD_DEFAULT_XRES;} + + $scale_factor = 1.5 * $xres * $this->k; + $bc_w = round($w * $scale_factor); //width in points + $bc_h = round($h * $scale_factor); //height in points + + switch (strtoupper($type)) { + case "I25": { + $obj = new I25Object($bc_w, $bc_h, $style, $code); + break; + } + case "C128A": { + $obj = new C128AObject($bc_w, $bc_h, $style, $code); + break; + } + default: + case "C128B": { + $obj = new C128BObject($bc_w, $bc_h, $style, $code); + break; + } + case "C128C": { + $obj = new C128CObject($bc_w, $bc_h, $style, $code); + break; + } + case "C39": { + $obj = new C39Object($bc_w, $bc_h, $style, $code); + break; + } + } + + $obj->SetFont($font); + $obj->DrawObject($xres); + + //use a temporary file.... + $tmpName = tempnam(K_PATH_CACHE,'img'); + imagepng($obj->getImage(), $tmpName); + $this->Image($tmpName, $x, $y, $w, $h, 'png'); + $obj->DestroyObject(); + unset($obj); + unlink($tmpName); + } + + /** + * Returns the PDF data. + */ + public function getPDFData() { + if($this->state < 3) { + $this->Close(); + } + return $this->buffer; + } + + // --- HTML PARSER FUNCTIONS --- + + /** + * Allows to preserve some HTML formatting.<br /> + * Supports: h1, h2, h3, h4, h5, h6, b, u, i, a, img, p, br, strong, em, font, blockquote, li, ul, ol, hr, td, th, tr, table, sup, sub, small + * @param string $html text to display + * @param boolean $ln if true add a new line after text (default = true) + * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. + * @param boolean $reseth if true reset the last cell height (default false). + * @param boolean $cell if true add the default cMargin space to each Write. + */ + public function writeHTML($html, $ln=true, $fill=0, $reseth=false, $cell=false) { + + // store some variables + $html=strip_tags($html,"<h1><h2><h3><h4><h5><h6><b><u><i><a><img><p><br><br/><strong><em><font><blockquote><li><ul><ol><hr><td><th><tr><table><sup><sub><small><span><div>"); //remove all unsupported tags + //replace carriage returns, newlines and tabs + $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " "); + $html = strtr($html, $repTable); + $pattern = '/(<[^>]+>)/Uu'; + $a = preg_split($pattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); //explodes the string + + if ((empty($this->lasth))OR ($reseth)) { + //set row height + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + } + + foreach($a as $key=>$element) { + if (!preg_match($pattern, $element)) { + //Text + if($this->HREF) { + $this->addHtmlLink($this->HREF, $element, $fill); + } elseif($this->tdbegin) { + if((strlen(trim($element)) > 0) AND ($element != "&nbsp;")) { + $this->Cell($this->tdwidth, $this->tdheight, $this->unhtmlentities($element), $this->tableborder, '', $this->tdalign, $this->tdbgcolor); + } elseif($element == "&nbsp;") { + $this->Cell($this->tdwidth, $this->tdheight, '', $this->tableborder, '', $this->tdalign, $this->tdbgcolor); + } + } else { + + $ctmpmargin = $this->cMargin; + if(!$cell) { + $this->cMargin = 0; + } + $this->Write($this->lasth, stripslashes($this->unhtmlentities($element)), '', $fill, 0); + $this->cMargin = $ctmpmargin; + } + } else { + $element = substr($element, 1, -1); + //Tag + if($element{0}=='/') { + $this->closedHTMLTagHandler(strtolower(substr($element, 1))); + } + else { + //Extract attributes + // get tag name + preg_match('/([a-zA-Z0-9]*)/', $element, $tag); + $tag = strtolower($tag[0]); + // get attributes + preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER); + $attr = array(); // reset attribute array + while(list($id,$name)=each($attr_array[1])) { + $attr[strtolower($name)] = $attr_array[2][$id]; + } + $this->openHTMLTagHandler($tag, $attr, $fill); + } + } + } + if ($ln) { + $this->Ln($this->lasth); + } + } + + /** + * Prints a cell (rectangular area) with optional borders, background color and html text string. The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br /> + * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. + * @param float $w Cell width. If 0, the cell extends up to the right margin. + * @param float $h Cell minimum height. The cell extends automatically if needed. + * @param float $x upper-left corner X coordinate + * @param float $y upper-left corner Y coordinate + * @param string $html html text to print. Default value: empty string. + * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> + * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> + Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. + * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * @param boolean $reseth if true reset the last cell height (default false). + * @see Cell() + */ + public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=false) { + + if ((empty($this->lasth))OR ($reseth)) { + //set row height + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + } + + // get current page number + $startpage = $this->page; + + if (!empty($y)) { + $this->SetY($y); + } else { + $y = $this->GetY(); + } + if (!empty($x)) { + $this->SetX($x); + } else { + $x = $this->GetX(); + } + + if(empty($w)) { + if ($this->rtl) { + $w = $this->x - $this->lMargin; + } else { + $w = $this->w - $this->rMargin - $this->x; + } + } + + // store original margin values + $lMargin = $this->lMargin; + $rMargin = $this->rMargin; + + // set new margin values + if ($this->rtl) { + $this->SetLeftMargin($this->x - $w); + $this->SetRightMargin($this->w - $this->x); + } else { + $this->SetLeftMargin($this->x); + $this->SetRightMargin($this->w - $this->x - $w); + } + + // calculate remaining vertical space on first page ($startpage) + $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin(); + + // Write HTML text + $this->writeHTML($html, true, $fill, $reseth, true); + + // Get end-of-text Y position + $currentY = $this->GetY(); + // get latest page number + $endpage = $this->page; + + if (!empty($border)) { + // check if a new page has been created + if ($endpage > $startpage) { + // design borders around HTML cells. + for ($page=$startpage; $page<=$endpage; $page++) { + $this->page = $page; + if ($page==$startpage) { + $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin()); + $h = $restspace - 1; + } elseif ($page==$endpage) { + $this->SetY($this->tMargin); // put cursor at the beginning of text + $h = $currentY - $this->tMargin; + } else { + $this->SetY($this->tMargin); // put cursor at the beginning of text + $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); + } + $this->Cell($w, $h, "", $border, 1, '', 0); + } + } else { + $h = max($h, ($currentY - $y)); + $this->SetY($y); // put cursor at the beginning of text + // design a cell around the text + $this->Cell($w, $h, "", $border, 1, '', 0); + } + } + + // restore original margin values + $this->SetLeftMargin($lMargin); + $this->SetRightMargin($rMargin); + + + if($ln>0) { + //Go to the beginning of the next line + $this->SetY($currentY); + if($ln == 2) { + $this->SetX($x + $w); + } + } else { + // go left or right by case + $this->page = $startpage; + $this->y = $y; + $this->SetX($x + $w); + } + + } + + /** + * Process opening tags. + * @param string $tag tag name (in uppercase) + * @param string $attr tag attribute (in uppercase) + * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * @access private + */ + private function openHTMLTagHandler($tag, $attr, $fill=0) { + // check for text direction attribute + if (isset($attr['dir'])) { + $this->tmprtl = $attr['dir']=='rtl' ? 'R' : 'L'; + } else { + $this->tmprtl = false; + } + //Opening tag + switch($tag) { + case 'table': { + if ((isset($attr['border'])) AND ($attr['border'] != '')) { + $this->tableborder = $attr['border']; + } + else { + $this->tableborder = 0; + } + break; + } + case 'tr': { + break; + } + case 'td': + case 'th': { + if ((isset($attr['width'])) AND ($attr['width'] != '')) { + $this->tdwidth = ($attr['width']/4); + } + else { + $this->tdwidth = (($this->w - $this->lMargin - $this->rMargin) / $this->default_table_columns); + } + if ((isset($attr['height'])) AND ($attr['height'] != '')) { + $this->tdheight=($attr['height'] / $this->k); + } + else { + $this->tdheight = $this->lasth; + } + if ((isset($attr['align'])) AND ($attr['align'] != '')) { + switch ($attr['align']) { + case 'center': { + $this->tdalign = "C"; + break; + } + case 'right': { + $this->tdalign = "R"; + break; + } + default: + case 'left': { + $this->tdalign = "L"; + break; + } + } + } else { + if($this->rtl) { + $this->tdalign = "R"; + } else { + $this->tdalign = "L"; + } + } + if ((isset($attr['bgcolor'])) AND ($attr['bgcolor'] != '')) { + $coul = $this->convertColorHexToDec($attr['bgcolor']); + $this->SetFillColor($coul['R'], $coul['G'], $coul['B']); + $this->tdbgcolor=true; + } + $this->tdbegin=true; + break; + } + case 'hr': { + $this->Ln(); + if ((isset($attr['width'])) AND ($attr['width'] != '')) { + $hrWidth = $attr['width']; + } + else { + $hrWidth = $this->w - $this->lMargin - $this->rMargin; + } + $x = $this->GetX(); + $y = $this->GetY(); + $this->GetLineWidth(); + $prevlinewidth = $this->SetLineWidth(0.2); + $this->Line($x, $y, $x + $hrWidth, $y); + $this->SetLineWidth($prevlinewidth); + $this->Ln(); + break; + } + case 'strong': { + $this->setStyle('b', true); + break; + } + case 'em': { + $this->setStyle('i', true); + break; + } + case 'b': + case 'i': + case 'u': { + $this->setStyle($tag, true); + break; + } + case 'a': { + $this->HREF = $attr['href']; + break; + } + case 'img': { + if(isset($attr['src'])) { + // replace relative path with real server path + if ($attr['src'][0] == '/') { + $attr['src'] = $_SERVER['DOCUMENT_ROOT'].$attr['src']; + } + $attr['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $attr['src']); + if(!isset($attr['width'])) { + $attr['width'] = 0; + } + if(!isset($attr['height'])) { + $attr['height'] = 0; + } + if(!isset($attr['align'])) { + $align = 'N'; + } else { + switch($attr['align']) { + case 'top':{ + $align = 'T'; + break; + } + case 'middle':{ + $align = 'M'; + break; + } + case 'bottom':{ + $align = 'B'; + break; + } + default:{ + $align = 'N'; + break; + } + } + } + $this->Image($attr['src'], $this->GetX(),$this->GetY(), $this->pixelsToMillimeters($attr['width']), $this->pixelsToMillimeters($attr['height']), '', '', $align); + + } + break; + } + case 'ul': { + $this->listordered = false; + $this->listcount = 0; + break; + } + case 'ol': { + $this->listordered = true; + $this->listcount = 0; + break; + } + case 'li': { + $this->Ln(); + if ($this->listordered) { + if (isset($attr['value'])) { + $this->listcount = intval($attr['value']); + } + $this->lispacer = " ".(++$this->listcount).". "; + } else { + //unordered list simbol + $this->lispacer = " - "; + } + $rtldir = $this->tmprtl; + $this->tmprtl = false; + $this->Write($this->lasth, $this->lispacer, '', $fill); + $this->tmprtl = $rtldir; + break; + } + case 'blockquote': + case 'br': { + $this->Ln(); + if(strlen($this->lispacer) > 0) { + if ($this->rtl) { + $this->x -= $this->GetStringWidth($this->lispacer); + } else { + $this->x += $this->GetStringWidth($this->lispacer); + } + } + break; + } + case 'p': { + $this->Ln(); + $this->Ln(); + break; + } + case 'sup': { + $currentFontSize = $this->FontSize; + $this->tempfontsize = $this->FontSizePt; + $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); + $this->SetXY($this->GetX(), $this->GetY() - (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); + break; + } + case 'sub': { + $currentFontSize = $this->FontSize; + $this->tempfontsize = $this->FontSizePt; + $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); + $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); + break; + } + case 'small': { + $currentFontSize = $this->FontSize; + $this->tempfontsize = $this->FontSizePt; + $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); + $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)/3)); + break; + } + case 'font': { + if (isset($attr['color']) AND $attr['color']!='') { + $coul = $this->convertColorHexToDec($attr['color']); + $this->SetTextColor($coul['R'],$coul['G'],$coul['B']); + $this->issetcolor=true; + } + if (isset($attr['face']) and in_array(strtolower($attr['face']), $this->fontlist)) { + $this->SetFont(strtolower($attr['face'])); + $this->issetfont=true; + } + if (isset($attr['size'])) { + $headsize = intval($attr['size']); + } else { + $headsize = 0; + } + $currentFontSize = $this->FontSize; + $this->tempfontsize = $this->FontSizePt; + $this->SetFontSize($this->FontSizePt + $headsize); + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + break; + } + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': { + $headsize = (4 - substr($tag, 1)) * 2; + $currentFontSize = $this->FontSize; + $this->tempfontsize = $this->FontSizePt; + $this->SetFontSize($this->FontSizePt + $headsize); + $this->setStyle('b', true); + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + break; + } + } + } + + /** + * Process closing tags. + * @param string $tag tag name (in uppercase) + * @access private + */ + private function closedHTMLTagHandler($tag) { + //Closing tag + switch($tag) { + case 'td': + case 'th': { + $this->tdbegin = false; + $this->tdwidth = 0; + $this->tdheight = 0; + if($this->rtl) { + $this->tdalign = "R"; + } else { + $this->tdalign = "L"; + } + $this->tdbgcolor = false; + $this->SetFillColor($this->prevFillColor[0], $this->prevFillColor[1], $this->prevFillColor[2]); + break; + } + case 'tr': { + $this->Ln(); + break; + } + case 'table': { + $this->tableborder=0; + break; + } + case 'strong': { + $this->setStyle('b', false); + break; + } + case 'em': { + $this->setStyle('i', false); + break; + } + case 'b': + case 'i': + case 'u': { + $this->setStyle($tag, false); + break; + } + case 'a': { + $this->HREF = ''; + break; + } + case 'sup': { + $currentFontSize = $this->FontSize; + $this->SetFontSize($this->tempfontsize); + $this->tempfontsize = $this->FontSizePt; + $this->SetXY($this->GetX(), $this->GetY() - (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); + break; + } + case 'sub': { + $currentFontSize = $this->FontSize; + $this->SetFontSize($this->tempfontsize); + $this->tempfontsize = $this->FontSizePt; + $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); + break; + } + case 'small': { + $currentFontSize = $this->FontSize; + $this->SetFontSize($this->tempfontsize); + $this->tempfontsize = $this->FontSizePt; + $this->SetXY($this->GetX(), $this->GetY() - (($this->FontSize - $currentFontSize)/3)); + break; + } + case 'font': { + if ($this->issetcolor == true) { + $this->SetTextColor($this->prevTextColor[0], $this->prevTextColor[1], $this->prevTextColor[2]); + } + if ($this->issetfont) { + $this->FontFamily = $this->prevFontFamily; + $this->FontStyle = $this->prevFontStyle; + $this->SetFont($this->FontFamily); + $this->issetfont = false; + } + $currentFontSize = $this->FontSize; + $this->SetFontSize($this->tempfontsize); + $this->tempfontsize = $this->FontSizePt; + //$this->TextColor = $this->prevTextColor; + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + break; + } + case 'p': { + $this->Ln(); + $this->Ln(); + break; + } + case 'ul': + case 'ol': { + $this->Ln(); + $this->Ln(); + break; + } + case 'li': { + $this->lispacer = ""; + break; + } + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': { + $currentFontSize = $this->FontSize; + $this->SetFontSize($this->tempfontsize); + $this->tempfontsize = $this->FontSizePt; + $this->setStyle('b', false); + $this->Ln(); + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + break; + } + default : { + break; + } + } + $this->tmprtl = false; + } + + /** + * Sets font style. + * @param string $tag tag name (in lowercase) + * @param boolean $enable + * @access private + */ + private function setStyle($tag, $enable) { + //Modify style and select corresponding font + $this->$tag += ($enable ? 1 : -1); + $style=''; + foreach(array('b', 'i', 'u') as $s) { + if($this->$s > 0) { + $style .= $s; + } + } + $this->SetFont('', $style); + } + + /** + * Output anchor link. + * @param string $url link URL + * @param string $name link name + * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * @access public + */ + public function addHtmlLink($url, $name, $fill=0) { + //Put a hyperlink + $this->SetTextColor(0, 0, 255); + $this->setStyle('u', true); + $this->Write($this->lasth, $name, $url, $fill); + $this->setStyle('u', false); + $this->SetTextColor(0); + } + + /** + * Returns an associative array (keys: R,G,B) from + * a hex html code (e.g. #3FE5AA). + * @param string $color hexadecimal html color [#rrggbb] + * @return array + * @access private + */ + private function convertColorHexToDec($color = "#000000"){ + $tbl_color = array(); + $tbl_color['R'] = hexdec(substr($color, 1, 2)); + $tbl_color['G'] = hexdec(substr($color, 3, 2)); + $tbl_color['B'] = hexdec(substr($color, 5, 2)); + return $tbl_color; + } + + /** + * Converts pixels to millimeters in 72 dpi. + * @param int $px pixels + * @return float millimeters + * @access private + */ + private function pixelsToMillimeters($px){ + return $px * 25.4 / 72; + } + + /** + * Reverse function for htmlentities. + * Convert entities in UTF-8. + * + * @param $text_to_convert Text to convert. + * @return string converted + */ + public function unhtmlentities($text_to_convert) { + return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); + } + + // ENCRYPTION METHODS ---------------------------------- + // SINCE 2.0.000 (2008-01-02) + /** + * Compute encryption key depending on object number where the encrypted data is stored + * @param int $n object number + * @since 2.0.000 (2008-01-02) + */ + protected function _objectkey($n) { + return substr($this->_md5_16($this->encryption_key.pack('VXxx',$n)),0,10); + } + + /** + * Put encryption on PDF document + * @since 2.0.000 (2008-01-02) + */ + protected function _putencryption() { + $this->_out('/Filter /Standard'); + $this->_out('/V 1'); + $this->_out('/R 2'); + $this->_out('/O ('.$this->_escape($this->Ovalue).')'); + $this->_out('/U ('.$this->_escape($this->Uvalue).')'); + $this->_out('/P '.$this->Pvalue); + } + + /** + * Returns the input text exrypted using RC4 algorithm and the specified key. + * RC4 is the standard encryption algorithm used in PDF format + * @param string $key encryption key + * @param String $text input text to be encrypted + * @return String encrypted text + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + protected function _RC4($key, $text) { + if ($this->last_rc4_key != $key) { + $k = str_repeat($key, 256/strlen($key)+1); + $rc4 = range(0,255); + $j = 0; + for ($i=0; $i<256; $i++) { + $t = $rc4[$i]; + $j = ($j + $t + ord($k{$i})) % 256; + $rc4[$i] = $rc4[$j]; + $rc4[$j] = $t; + } + $this->last_rc4_key = $key; + $this->last_rc4_key_c = $rc4; + } else { + $rc4 = $this->last_rc4_key_c; + } + $len = strlen($text); + $a = 0; + $b = 0; + $out = ''; + for ($i=0; $i<$len; $i++) { + $a = ($a+1)%256; + $t= $rc4[$a]; + $b = ($b+$t)%256; + $rc4[$a] = $rc4[$b]; + $rc4[$b] = $t; + $k = $rc4[($rc4[$a]+$rc4[$b])%256]; + $out.=chr(ord($text{$i}) ^ $k); + } + return $out; + } + + /** + * Encrypts a string using MD5 and returns it's value as a binary string. + * @param string $str input string + * @return String MD5 encrypted binary string + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + protected function _md5_16($str) { + return pack('H*',md5($str)); + } + + /** + * Compute O value (used for RC4 encryption) + * @param String $user_pass user password + * @param String $owner_pass user password + * @return String O value + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + protected function _Ovalue($user_pass, $owner_pass) { + $tmp = $this->_md5_16($owner_pass); + $owner_RC4_key = substr($tmp,0,5); + return $this->_RC4($owner_RC4_key, $user_pass); + } + + /** + * Compute U value (used for RC4 encryption) + * @return String U value + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + protected function _Uvalue() { + return $this->_RC4($this->encryption_key, $this->padding); + } + + /** + * Compute encryption key + * @param String $user_pass user password + * @param String $owner_pass user password + * @param String $protection protection type + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + protected function _generateencryptionkey($user_pass, $owner_pass, $protection) { + // Pad passwords + $user_pass = substr($user_pass.$this->padding,0,32); + $owner_pass = substr($owner_pass.$this->padding,0,32); + // Compute O value + $this->Ovalue = $this->_Ovalue($user_pass,$owner_pass); + // Compute encyption key + $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF"); + $this->encryption_key = substr($tmp,0,5); + // Compute U value + $this->Uvalue = $this->_Uvalue(); + // Compute P value + $this->Pvalue = -(($protection^255)+1); + } + + /** + * Set document protection + * The permission array is composed of values taken from the following ones: + * - copy: copy text and images to the clipboard + * - print: print the document + * - modify: modify it (except for annotations and forms) + * - annot-forms: add annotations and forms + * Remark: the protection against modification is for people who have the full Acrobat product. + * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access. + * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts. + * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms) + * @param String $user_pass user password. Empty by default. + * @param String $owner_pass owner password. If not specified, a random value is used. + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + public function SetProtection($permissions=array(),$user_pass='',$owner_pass=null) { + $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32); + $protection = 192; + foreach($permissions as $permission) { + if (!isset($options[$permission])) { + $this->Error('Incorrect permission: '.$permission); + } + $protection += $options[$permission]; + } + if ($owner_pass === null) { + $owner_pass = uniqid(rand()); + } + $this->encrypted = true; + $this->_generateencryptionkey($user_pass, $owner_pass, $protection); + } + + // END OF ENCRYPTION FUNCTIONS ------------------------- + + // START TRANSFORMATIONS SECTION ----------------------- + // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni + + /** + * Starts a 2D tranformation saving current graphic state. + * This function must be called before scaling, mirroring, translation, rotation and skewing. + * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function StartTransform() { + $this->_out('q'); + } + + /** + * Stops a 2D tranformation restoring previous graphic state. + * This function must be called after scaling, mirroring, translation, rotation and skewing. + * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function StopTransform() { + $this->_out('Q'); + } + + /** + * Horizontal Scaling. + * @param float $s_x scaling factor for width as percent. 0 is not allowed. + * @param int $x abscissa of the scaling center. Default is current x position + * @param int $y ordinate of the scaling center. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function ScaleX($s_x, $x='', $y=''){ + $this->Scale($s_x, 100, $x, $y); + } + + /** + * Vertical Scaling. + * @param float $s_y scaling factor for height as percent. 0 is not allowed. + * @param int $x abscissa of the scaling center. Default is current x position + * @param int $y ordinate of the scaling center. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function ScaleY($s_y, $x='', $y=''){ + $this->Scale(100, $s_y, $x, $y); + } + + /** + * Vertical and horizontal proportional Scaling. + * @param float $s scaling factor for width and height as percent. 0 is not allowed. + * @param int $x abscissa of the scaling center. Default is current x position + * @param int $y ordinate of the scaling center. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function ScaleXY($s, $x='', $y=''){ + $this->Scale($s, $s, $x, $y); + } + + /** + * Vertical and horizontal non-proportional Scaling. + * @param float $s_x scaling factor for width as percent. 0 is not allowed. + * @param float $s_y scaling factor for height as percent. 0 is not allowed. + * @param int $x abscissa of the scaling center. Default is current x position + * @param int $y ordinate of the scaling center. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function Scale($s_x, $s_y, $x='', $y=''){ + if($x === '') { + $x=$this->x; + } + if($y === '') { + $y=$this->y; + } + if($this->rtl) { + $x = $this->w - $x; + } + if($s_x == 0 OR $s_y == 0) + $this->Error('Please use values unequal to zero for Scaling'); + $y=($this->h-$y)*$this->k; + $x*=$this->k; + //calculate elements of transformation matrix + $s_x/=100; + $s_y/=100; + $tm[0]=$s_x; + $tm[1]=0; + $tm[2]=0; + $tm[3]=$s_y; + $tm[4]=$x*(1-$s_x); + $tm[5]=$y*(1-$s_y); + //scale the coordinate system + $this->Transform($tm); + } + + /** + * Horizontal Mirroring. + * @param int $x abscissa of the point. Default is current x position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function MirrorH($x=''){ + $this->Scale(-100, 100, $x); + } + + /** + * Verical Mirroring. + * @param int $y ordinate of the point. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function MirrorV($y=''){ + $this->Scale(100, -100, '', $y); + } + + /** + * Point reflection mirroring. + * @param int $x abscissa of the point. Default is current x position + * @param int $y ordinate of the point. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function MirrorP($x='',$y=''){ + $this->Scale(-100, -100, $x, $y); + } + + /** + * Reflection against a straight line through point (x, y) with the gradient angle (angle). + * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line). + * @param int $x abscissa of the point. Default is current x position + * @param int $y ordinate of the point. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function MirrorL($angle=0, $x='',$y=''){ + $this->Scale(-100, 100, $x, $y); + $this->Rotate(-2*($angle-90),$x,$y); + } + + /** + * Translate graphic object horizontally. + * @param int $t_x movement to the right + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function TranslateX($t_x){ + $this->Translate($t_x, 0); + } + + /** + * Translate graphic object vertically. + * @param int $t_y movement to the bottom + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function TranslateY($t_y){ + $this->Translate(0, $t_y, $x, $y); + } + + /** + * Translate graphic object horizontally and vertically. + * @param int $t_x movement to the right + * @param int $t_y movement to the bottom + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function Translate($t_x, $t_y){ + if($this->rtl) { + $t_x = -$t_x; + } + //calculate elements of transformation matrix + $tm[0]=1; + $tm[1]=0; + $tm[2]=0; + $tm[3]=1; + $tm[4]=$t_x*$this->k; + $tm[5]=-$t_y*$this->k; + //translate the coordinate system + $this->Transform($tm); + } + + /** + * Rotate object. + * @param float $angle angle in degrees for counter-clockwise rotation + * @param int $x abscissa of the rotation center. Default is current x position + * @param int $y ordinate of the rotation center. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function Rotate($angle, $x='', $y=''){ + if($x === '') { + $x=$this->x; + } + if($y === '') { + $y=$this->y; + } + if($this->rtl) { + $x = $this->w - $x; + $angle = -$angle; + } + $y=($this->h-$y)*$this->k; + $x*=$this->k; + //calculate elements of transformation matrix + $tm[0]=cos(deg2rad($angle)); + $tm[1]=sin(deg2rad($angle)); + $tm[2]=-$tm[1]; + $tm[3]=$tm[0]; + $tm[4]=$x+$tm[1]*$y-$tm[0]*$x; + $tm[5]=$y-$tm[0]*$y-$tm[1]*$x; + //rotate the coordinate system around ($x,$y) + $this->Transform($tm); + } + + /** + * Skew horizontally. + * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) + * @param int $x abscissa of the skewing center. default is current x position + * @param int $y ordinate of the skewing center. default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function SkewX($angle_x, $x='', $y=''){ + $this->Skew($angle_x, 0, $x, $y); + } + + /** + * Skew vertically. + * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) + * @param int $x abscissa of the skewing center. default is current x position + * @param int $y ordinate of the skewing center. default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function SkewY($angle_y, $x='', $y=''){ + $this->Skew(0, $angle_y, $x, $y); + } + + /** + * Skew. + * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) + * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) + * @param int $x abscissa of the skewing center. default is current x position + * @param int $y ordinate of the skewing center. default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function Skew($angle_x, $angle_y, $x='', $y=''){ + if($x === '') { + $x=$this->x; + } + if($y === '') { + $y=$this->y; + } + if($this->rtl) { + $x = $this->w - $x; + $angle_x = -$angle_x; + } + if($angle_x <= -90 OR $angle_x >= 90 OR $angle_y <= -90 OR $angle_y >= 90) + $this->Error('Please use values between -90� and 90� for skewing'); + $x*=$this->k; + $y=($this->h-$y)*$this->k; + //calculate elements of transformation matrix + $tm[0]=1; + $tm[1]=tan(deg2rad($angle_y)); + $tm[2]=tan(deg2rad($angle_x)); + $tm[3]=1; + $tm[4]=-$tm[2]*$y; + $tm[5]=-$tm[1]*$x; + //skew the coordinate system + $this->Transform($tm); + } + + /** + * Apply graphic transformations. + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + protected function Transform($tm){ + $this->_out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', $tm[0],$tm[1],$tm[2],$tm[3],$tm[4],$tm[5])); + } + + // END TRANSFORMATIONS SECTION ------------------------- + + // START GRAPHIC FUNCTIONS SECTION --------------------- + // The following section is based on the code provided by David Hernandez Sanz + + /** + * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page. + * @param float $width The width. + * @since 1.0 + * @see Line(), Rect(), Cell(), MultiCell() + */ + public function SetLineWidth($width) { + //Set line width + $this->LineWidth=$width; + if($this->page>0) { + $this->_out(sprintf('%.2f w',$width*$this->k)); + } + } + + /** + * Returns the current the line width. + * @return int Line width + * @since 2.1.000 (2008-01-07) + * @see Line(), SetLineWidth() + */ + public function GetLineWidth() { + return $this->LineWidth; + } + + /** + * Set line style. + * + * @param array $style Line style. Array with keys among the following: + * <ul> + * <li>width (float): Width of the line in user units.</li> + * <li>cap (string): Type of cap to put on the line. Possible values are: + * butt, round, square. The difference between "square" and "butt" is that + * "square" projects a flat end past the end of the line.</li> + * <li>join (string): Type of join. Possible values are: miter, round, + * bevel.</li> + * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with + * series of length values, which are the lengths of the on and off dashes. + * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on, + * 1 off, 2 on, 1 off, ...</li> + * <li>phase (integer): Modifier on the dash pattern which is used to shift + * the point at which the pattern starts.</li> + * <li>color (array): Draw color. Format: array(red, green, blue).</li> + * </ul> + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function SetLineStyle($style) { + extract($style); + if (isset($width)) { + $width_prev = $this->LineWidth; + $this->SetLineWidth($width); + $this->LineWidth = $width_prev; + } + if (isset($cap)) { + $ca = array("butt" => 0, "round"=> 1, "square" => 2); + if (isset($ca[$cap])) { + $this->_out($ca[$cap] . " J"); + } + } + if (isset($join)) { + $ja = array("miter" => 0, "round" => 1, "bevel" => 2); + if (isset($ja[$join])) { + $this->_out($ja[$join] . " j"); + } + } + if (isset($dash)) { + $dash_string = ""; + if ($dash) { + // phpMyAdmin change + if (preg_match("/^.+,/", $dash)) { + $tab = explode(",", $dash); + } else { + $tab = array($dash); + } + $dash_string = ""; + foreach ($tab as $i => $v) { + if ($i) { + $dash_string .= " "; + } + $dash_string .= sprintf("%.2f", $v); + } + } + if (!isset($phase) OR !$dash) { + $phase = 0; + } + $this->_out(sprintf("[%s] %.2f d", $dash_string, $phase)); + } + if (isset($color)) { + list($r, $g, $b) = $color; + $this->SetDrawColor($r, $g, $b); + } + } + + /* + * Set a draw point. + * @param float $x Abscissa of point. + * @param float $y Ordinate of point. + * @access private + * @since 2.1.000 (2008-01-08) + */ + protected function _outPoint($x, $y) { + if($this->rtl) { + $x = $this->w - $x; + } + $this->_out(sprintf("%.2f %.2f m", $x * $this->k, ($this->h - $y) * $this->k)); + } + + /* + * Draws a line from last draw point. + * @param float $x Abscissa of end point. + * @param float $y Ordinate of end point. + * @access private + * @since 2.1.000 (2008-01-08) + */ + protected function _outLine($x, $y) { + if($this->rtl) { + $x = $this->w - $x; + } + $this->_out(sprintf("%.2f %.2f l", $x * $this->k, ($this->h - $y) * $this->k)); + } + + /** + * Draws a rectangle. + * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). + * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). + * @param float $w Width. + * @param float $h Height. + * @param string $op options + * @access protected + * @since 2.1.000 (2008-01-08) + */ + protected function _outRect($x, $y, $w, $h, $op) { + if($this->rtl) { + $x = $this->w - $x - $w; + } + $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op)); + } + + /* + * Draws a Bezier curve from last draw point. + * The Bezier curve is a tangent to the line between the control points at either end of the curve. + * @param float $x1 Abscissa of control point 1. + * @param float $y1 Ordinate of control point 1. + * @param float $x2 Abscissa of control point 2. + * @param float $y2 Ordinate of control point 2. + * @param float $x3 Abscissa of end point. + * @param float $y3 Ordinate of end point. + * @access private + * @since 2.1.000 (2008-01-08) + */ + protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { + if($this->rtl) { + $x1 = $this->w - $x1; + $x2 = $this->w - $x2; + $x3 = $this->w - $x3; + } + $this->_out(sprintf("%.2f %.2f %.2f %.2f %.2f %.2f c", $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); + } + + /** + * Draws a line between two points. + * @param float $x1 Abscissa of first point. + * @param float $y1 Ordinate of first point. + * @param float $x2 Abscissa of second point. + * @param float $y2 Ordinate of second point. + * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @access public + * @since 1.0 + * @see SetLineWidth(), SetDrawColor(), SetLineStyle() + */ + public function Line($x1, $y1, $x2, $y2, $style = array()) { + if ($style) { + $this->SetLineStyle($style); + } + $this->_outPoint($x1, $y1); + $this->_outLine($x2, $y2); + $this->_out(" S"); + } + + /** + * Draws a rectangle. + * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). + * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). + * @param float $w Width. + * @param float $h Height. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $border_style Border style of rectangle. Array with keys among the following: + * <ul> + * <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li> + * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li> + * </ul> + * If a key is not present or is null, not draws the border. Default value: default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @since 1.0 + * @see SetLineStyle() + */ + public function Rect($x, $y, $w, $h, $style='', $border_style = array(), $fill_color = array()) { + if (!(false === strpos($style, "F")) AND $fill_color) { + list($r, $g, $b) = $fill_color; + $this->SetFillColor($r, $g, $b); + } + switch ($style) { + case "F": { + $op='f'; + $border_style = array(); + $this->_outRect($x, $y, $w, $h, $op); + break; + } + case "DF": + case "FD": { + if (!$border_style OR isset($border_style["all"])) { + $op='B'; + if (isset($border_style["all"])) { + $this->SetLineStyle($border_style["all"]); + $border_style = array(); + } + } else { + $op='f'; + } + $this->_outRect($x, $y, $w, $h, $op); + break; + } + default: { + $op='S'; + if (!$border_style OR isset($border_style["all"])) { + if (isset($border_style["all"]) && $border_style["all"]) { + $this->SetLineStyle($border_style["all"]); + $border_style = array(); + } + $this->_outRect($x, $y, $w, $h, $op); + } + break; + } + } + if ($border_style) { + $border_style2 = array(); + foreach ($border_style as $line => $value) { + $lenght = strlen($line); + for ($i = 0; $i < $lenght; $i++) { + $border_style2[$line[$i]] = $value; + } + } + $border_style = $border_style2; + if (isset($border_style["L"]) && $border_style["L"]) { + $this->Line($x, $y, $x, $y + $h, $border_style["L"]); + } + if (isset($border_style["T"]) && $border_style["T"]) { + $this->Line($x, $y, $x + $w, $y, $border_style["T"]); + } + if (isset($border_style["R"]) && $border_style["R"]) { + $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style["R"]); + } + if (isset($border_style["B"]) && $border_style["B"]) { + $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style["B"]); + } + } + } + + /** + * Draws a Bezier curve. + * The Bezier curve is a tangent to the line between the control points at + * either end of the curve. + * @param float $x0 Abscissa of start point. + * @param float $y0 Ordinate of start point. + * @param float $x1 Abscissa of control point 1. + * @param float $y1 Ordinate of control point 1. + * @param float $x2 Abscissa of control point 2. + * @param float $y2 Ordinate of control point 2. + * @param float $x3 Abscissa of end point. + * @param float $y3 Ordinate of end point. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @see SetLineStyle() + * @since 2.1.000 (2008-01-08) + */ + public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style = "", $line_style = array(), $fill_color = array()) { + if (!(false === strpos($style, "F")) AND $fill_color) { + list($r, $g, $b) = $fill_color; + $this->SetFillColor($r, $g, $b); + } + switch ($style) { + case "F": { + $op = "f"; + $line_style = array(); + break; + } + case "FD": + case "DF": { + $op = "B"; + break; + } + default: { + $op = "S"; + break; + } + } + if ($line_style) { + $this->SetLineStyle($line_style); + } + $this->_outPoint($x0, $y0); + $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); + $this->_out($op); + } + + /** + * Draws an ellipse. + * An ellipse is formed from n Bezier curves. + * @param float $x0 Abscissa of center point. + * @param float $y0 Ordinate of center point. + * @param float $rx Horizontal radius. + * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0. + * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. + * @param float $astart: Angle start of draw line. Default value: 0. + * @param float $afinish: Angle finish of draw line. Default value: 360. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * <li>C: Draw close.</li> + * </ul> + * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @param integer $nc Number of curves used in ellipse. Default value: 8. + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function Ellipse($x0, $y0, $rx, $ry = 0, $angle = 0, $astart = 0, $afinish = 360, $style = "", $line_style = array(), $fill_color = array(), $nc = 8) { + if ($angle) { + $this->StartTransform(); + $this->Rotate($angle, $x0, $y0); + $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc); + $this->StopTransform(); + return; + } + if ($rx) { + if (!(false === strpos($style, "F")) AND $fill_color) { + list($r, $g, $b) = $fill_color; + $this->SetFillColor($r, $g, $b); + } + switch ($style) { + case "F": { + $op = "f"; + $line_style = array(); + break; + } + case "FD": + case "DF": { + $op = "B"; + break; + } + case "C": { + $op = "s"; // Small "s" signifies closing the path as well + break; + } + default: { + $op = "S"; + break; + } + } + if ($line_style) { + $this->SetLineStyle($line_style); + } + if (!$ry) { + $ry = $rx; + } + $rx *= $this->k; + $ry *= $this->k; + if ($nc < 2){ + $nc = 2; + } + $astart = deg2rad((float) $astart); + $afinish = deg2rad((float) $afinish); + $total_angle = $afinish - $astart; + $dt = $total_angle / $nc; + $dtm = $dt/3; + $x0 *= $this->k; + $y0 = ($this->h - $y0) * $this->k; + $t1 = $astart; + $a0 = $x0 + ($rx * cos($t1)); + $b0 = $y0 + ($ry * sin($t1)); + $c0 = -$rx * sin($t1); + $d0 = $ry * cos($t1); + $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k)); + for ($i = 1; $i <= $nc; $i++) { + // Draw this bit of the total curve + $t1 = ($i * $dt) + $astart; + $a1 = $x0 + ($rx * cos($t1)); + $b1 = $y0 + ($ry * sin($t1)); + $c1 = -$rx * sin($t1); + $d1 = $ry * cos($t1); + $this->_outCurve(($a0 + ($c0 * $dtm)) / $this->k, $this->h - (($b0 + ($d0 * $dtm)) / $this->k), ($a1 - ($c1 * $dtm)) / $this->k, $this->h - (($b1 - ($d1 * $dtm)) / $this->k), $a1 / $this->k, $this->h - ($b1 / $this->k)); + $a0 = $a1; + $b0 = $b1; + $c0 = $c1; + $d0 = $d1; + } + $this->_out($op); + } + } + + /** + * Draws a circle. + * A circle is formed from n Bezier curves. + * @param float $x0 Abscissa of center point. + * @param float $y0 Ordinate of center point. + * @param float $r Radius. + * @param float $astart: Angle start of draw line. Default value: 0. + * @param float $afinish: Angle finish of draw line. Default value: 360. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * <li>C: Draw close.</li> + * </ul> + * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @param integer $nc Number of curves used in circle. Default value: 8. + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function Circle($x0, $y0, $r, $astart = 0, $afinish = 360, $style = "", $line_style = array(), $fill_color = array(), $nc = 8) { + $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc); + } + + /** + * Draws a polygon. + * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $line_style Line style of polygon. Array with keys among the following: + * <ul> + * <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li> + * <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li> + * </ul> + * If a key is not present or is null, not draws the line. Default value is default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function Polygon($p, $style = "", $line_style = array(), $fill_color = array()) { + $np = count($p) / 2; + if (!(false === strpos($style, "F")) AND $fill_color) { + list($r, $g, $b) = $fill_color; + $this->SetFillColor($r, $g, $b); + } + switch ($style) { + case "F": { + $line_style = array(); + $op = "f"; + break; + } + case "FD": + case "DF": { + $op = "B"; + break; + } + default: { + $op = "S"; + break; + } + } + $draw = true; + if ($line_style) { + if (isset($line_style["all"])) { + $this->SetLineStyle($line_style["all"]); + } + else { // 0 .. (np - 1), op = {B, S} + $draw = false; + if ("B" == $op) { + $op = "f"; + $this->_outPoint($p[0], $p[1]); + for ($i = 2; $i < ($np * 2); $i = $i + 2) { + $this->_outLine($p[$i], $p[$i + 1]); + } + $this->_outLine($p[0], $p[1]); + $this->_out($op); + } + $p[$np * 2] = $p[0]; + $p[($np * 2) + 1] = $p[1]; + for ($i = 0; $i < $np; $i++) { + if (isset($line_style[$i])) { + $this->Line($p[$i * 2], $p[($i * 2) + 1], $p[($i * 2) + 2], $p[($i * 2) + 3], $line_style[$i]); + } + } + } + } + if ($draw) { + $this->_outPoint($p[0], $p[1]); + for ($i = 2; $i < ($np * 2); $i = $i + 2) { + $this->_outLine($p[$i], $p[$i + 1]); + } + $this->_outLine($p[0], $p[1]); + $this->_out($op); + } + } + + /** + * Draws a regular polygon. + * @param float $x0 Abscissa of center point. + * @param float $y0 Ordinate of center point. + * @param float $r: Radius of inscribed circle. + * @param integer $ns Number of sides. + * @param float $angle Angle oriented (anti-clockwise). Default value: 0. + * @param boolean $draw_circle Draw inscribed circle or not. Default value: false. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $line_style Line style of polygon sides. Array with keys among the following: + * <ul> + * <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li> + * <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> + * </ul> + * If a key is not present or is null, not draws the side. Default value is default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function RegularPolygon($x0, $y0, $r, $ns, $angle = 0, $draw_circle = false, $style = "", $line_style = array(), $fill_color = array(), $circle_style = "", $circle_outLine_style = array(), $circle_fill_color = array()) { + if (3 > $ns) { + $ns = 3; + } + if ($draw_circle) { + $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); + } + $p = array(); + for ($i = 0; $i < $ns; $i++) { + $a = $angle + ($i * 360 / $ns); + $a_rad = deg2rad((float) $a); + $p[] = $x0 + ($r * sin($a_rad)); + $p[] = $y0 + ($r * cos($a_rad)); + } + $this->Polygon($p, $style, $line_style, $fill_color); + } + + /** + * Draws a star polygon + * @param float $x0 Abscissa of center point. + * @param float $y0 Ordinate of center point. + * @param float $r Radius of inscribed circle. + * @param integer $nv Number of vertices. + * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon). + * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. + * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $line_style Line style of polygon sides. Array with keys among the following: + * <ul> + * <li>all: Line style of all sides. Array like for + * {@link SetLineStyle SetLineStyle}.</li> + * <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> + * </ul> + * If a key is not present or is null, not draws the side. Default value is default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle = 0, $draw_circle = false, $style = "", $line_style = array(), $fill_color = array(), $circle_style = "", $circle_outLine_style = array(), $circle_fill_color = array()) { + if (2 > $nv) { + $nv = 2; + } + if ($draw_circle) { + $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); + } + $p2 = array(); + $visited = array(); + for ($i = 0; $i < $nv; $i++) { + $a = $angle + ($i * 360 / $nv); + $a_rad = deg2rad((float) $a); + $p2[] = $x0 + ($r * sin($a_rad)); + $p2[] = $y0 + ($r * cos($a_rad)); + $visited[] = false; + } + $p = array(); + $i = 0; + do { + $p[] = $p2[$i * 2]; + $p[] = $p2[($i * 2) + 1]; + $visited[$i] = true; + $i += $ng; + $i %= $nv; + } while (!$visited[$i]); + $this->Polygon($p, $style, $line_style, $fill_color); + } + + /** + * Draws a rounded rectangle. + * @param float $x Abscissa of upper-left corner. + * @param float $y Ordinate of upper-left corner. + * @param float $w Width. + * @param float $h Height. + * @param float $r Radius of the rounded corners. + * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left. Default value: all rounded corner ("1111"). + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function RoundedRect($x, $y, $w, $h, $r, $round_corner = "1111", $style = "", $border_style = array(), $fill_color = array()) { + if ("0000" == $round_corner) { // Not rounded + $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); + } else { // Rounded + if (!(false === strpos($style, "F")) AND $fill_color) { + list($red, $g, $b) = $fill_color; + $this->SetFillColor($red, $g, $b); + } + switch ($style) { + case "F": { + $border_style = array(); + $op = "f"; + break; + } + case "FD": + case "DF": { + $op = "B"; + break; + } + default: { + $op = "S"; + break; + } + } + if ($border_style) { + $this->SetLineStyle($border_style); + } + $MyArc = 4 / 3 * (sqrt(2) - 1); + $this->_outPoint($x + $r, $y); + $xc = $x + $w - $r; + $yc = $y + $r; + $this->_outLine($xc, $y); + if ($round_corner[0]) { + $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc); + } else { + $this->_outLine($x + $w, $y); + } + $xc = $x + $w - $r; + $yc = $y + $h - $r; + $this->_outLine($x + $w, $yc); + if ($round_corner[1]) { + $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r); + } else { + $this->_outLine($x + $w, $y + $h); + } + $xc = $x + $r; + $yc = $y + $h - $r; + $this->_outLine($xc, $y + $h); + if ($round_corner[2]) { + $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc); + } else { + $this->_outLine($x, $y + $h); + } + $xc = $x + $r; + $yc = $y + $r; + $this->_outLine($x, $yc); + if ($round_corner[3]) { + $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r); + } else { + $this->_outLine($x, $y); + $this->_outLine($x + $r, $y); + } + $this->_out($op); + } + } + + // END GRAPHIC FUNCTIONS SECTION ----------------------- + + // BIDIRECTIONAL TEXT SECTION -------------------------- + + /** + * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). + * @param string $str string to manipulate. + * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR + * @return string + * @author Nicola Asuni + * @since 2.1.000 (2008-01-08) + */ + protected function utf8StrRev($str, $setbom=false, $forcertl=false) { + global $unicode,$unicode_mirror, $unicode_arlet; + require_once(dirname(__FILE__).'/unicode_data.php'); + + // paragraph embedding level + $pel = 0; + // max level + $maxlevel = 0; + + // check if string contains arabic text + if (preg_match(K_RE_PATTERN_ARABIC, $str)) { + $arabic = true; + } else { + $arabic = false; + } + + // check if string contains RTL text + if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) { + return $this->UTF8ToUTF16BE($str, false); + } + + // convert string to array of unicode chars + $ta = $this->UTF8StringToArray($str); + // get number of chars + $numchars = count($ta); + + if ($forcertl == 'R') { + $pel = 1; + } elseif ($forcertl == 'L') { + $pel = 0; + } else { + // P2. In each paragraph, find the first character of type L, AL, or R. + // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero. + for ($i=0; $i < $numchars; $i++) { + $type = $unicode[$ta[$i]]; + if ($type == 'L') { + $pel = 0; + break; + } elseif (($type == 'AL') OR ($type == 'R')) { + $pel = 1; + break; + } + } + } + + // Current Embedding Level + $cel = $pel; + // directional override status + $dos = 'N'; + $remember = array(); + // start-of-level-run + $sor = $pel % 2 ? 'R' : 'L'; + $eor = $sor; + + //$levels = array(array('level' => $cel, 'sor' => $sor, 'eor' => '', 'chars' => array())); + //$current_level = &$levels[count( $levels )-1]; + + // Array of characters data + $chardata = Array(); + + // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase. + // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached. + for ($i=0; $i < $numchars; $i++) { + if ($ta[$i] == K_RLE) { + // X2. With each RLE, compute the least greater odd embedding level. + // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. + // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. + $next_level = $cel + ($cel % 2) + 1; + if ($next_level < 62) { + $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos); + $cel = $next_level; + $dos = 'N'; + $sor = $eor; + $eor = $cel % 2 ? 'R' : 'L'; + } + } elseif ($ta[$i] == K_LRE) { + // X3. With each LRE, compute the least greater even embedding level. + // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. + // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. + $next_level = $cel + 2 - ($cel % 2); + if ( $next_level < 62 ) { + $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos); + $cel = $next_level; + $dos = 'N'; + $sor = $eor; + $eor = $cel % 2 ? 'R' : 'L'; + } + } elseif ($ta[$i] == K_RLO) { + // X4. With each RLO, compute the least greater odd embedding level. + // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left. + // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. + $next_level = $cel + ($cel % 2) + 1; + if ($next_level < 62) { + $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos); + $cel = $next_level; + $dos = 'R'; + $sor = $eor; + $eor = $cel % 2 ? 'R' : 'L'; + } + } elseif ($ta[$i] == K_LRO) { + // X5. With each LRO, compute the least greater even embedding level. + // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right. + // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. + $next_level = $cel + 2 - ($cel % 2); + if ( $next_level < 62 ) { + $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos); + $cel = $next_level; + $dos = 'L'; + $sor = $eor; + $eor = $cel % 2 ? 'R' : 'L'; + } + } elseif ($ta[$i] == K_PDF) { + // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override. + if (count($remember)) { + $last = count($remember ) - 1; + if (($remember[$last]['num'] == K_RLE) OR + ($remember[$last]['num'] == K_LRE) OR + ($remember[$last]['num'] == K_RLO) OR + ($remember[$last]['num'] == K_LRO)) { + $match = array_pop($remember); + $cel = $match['cel']; + $dos = $match['dos']; + $sor = $eor; + $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L'; + } + } + } elseif (($ta[$i] != K_RLE) AND + ($ta[$i] != K_LRE) AND + ($ta[$i] != K_RLO) AND + ($ta[$i] != K_LRO) AND + ($ta[$i] != K_PDF)) { + // X6. For all types besides RLE, LRE, RLO, LRO, and PDF: + // a. Set the level of the current character to the current embedding level. + // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status. + if ($dos != 'N') { + $chardir = $dos; + } else { + $chardir = $unicode[$ta[$i]]; + } + // stores string characters and other information + $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor); + } + } // end for each char + + // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding. + // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes. + // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the “other” run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L. + + // 3.3.3 Resolving Weak Types + // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used. + // Nonspacing marks are now resolved based on the previous characters. + $numchars = count($chardata); + + // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor. + $prevlevel = -1; // track level changes + $levcount = 0; // counts consecutive chars at the same level + for ($i=0; $i < $numchars; $i++) { + if ($chardata[$i]['type'] == 'NSM') { + if ($levcount) { + $chardata[$i]['type'] = $chardata[$i]['sor']; + } elseif ($i > 0) { + $chardata[$i]['type'] = $chardata[($i-1)]['type']; + } + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if ($chardata[$i]['char'] == 'EN') { + for ($j=$levcount; $j >= 0; $j--) { + if ($chardata[$j]['type'] == 'AL') { + $chardata[$i]['type'] = 'AN'; + } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) { + break; + } + } + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // W3. Change all ALs to R. + for ($i=0; $i < $numchars; $i++) { + if ($chardata[$i]['type'] == 'AL') { + $chardata[$i]['type'] = 'R'; + } + } + + // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { + if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { + $chardata[$i]['type'] = 'EN'; + } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { + $chardata[$i]['type'] = 'EN'; + } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) { + $chardata[$i]['type'] = 'AN'; + } + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if($chardata[$i]['type'] == 'ET') { + if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) { + $chardata[$i]['type'] = 'EN'; + } else { + $j = $i+1; + while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) { + if ($chardata[$j]['type'] == 'EN') { + $chardata[$i]['type'] = 'EN'; + break; + } elseif ($chardata[$j]['type'] != 'ET') { + break; + } + $j++; + } + } + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // W6. Otherwise, separators and terminators change to Other Neutral. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) { + $chardata[$i]['type'] = 'ON'; + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if ($chardata[$i]['char'] == 'EN') { + for ($j=$levcount; $j >= 0; $j--) { + if ($chardata[$j]['type'] == 'L') { + $chardata[$i]['type'] = 'L'; + } elseif ($chardata[$j]['type'] == 'R') { + break; + } + } + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { + if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { + $chardata[$i]['type'] = 'L'; + } elseif (($chardata[$i]['type'] == 'N') AND + (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND + (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { + $chardata[$i]['type'] = 'R'; + } elseif ($chardata[$i]['type'] == 'N') { + // N2. Any remaining neutrals take the embedding direction + $chardata[$i]['type'] = $chardata[$i]['sor']; + } + } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { + // first char + if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { + $chardata[$i]['type'] = 'L'; + } elseif (($chardata[$i]['type'] == 'N') AND + (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND + (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { + $chardata[$i]['type'] = 'R'; + } elseif ($chardata[$i]['type'] == 'N') { + // N2. Any remaining neutrals take the embedding direction + $chardata[$i]['type'] = $chardata[$i]['sor']; + } + } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) { + //last char + if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) { + $chardata[$i]['type'] = 'L'; + } elseif (($chardata[$i]['type'] == 'N') AND + (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND + (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) { + $chardata[$i]['type'] = 'R'; + } elseif ($chardata[$i]['type'] == 'N') { + // N2. Any remaining neutrals take the embedding direction + $chardata[$i]['type'] = $chardata[$i]['sor']; + } + } elseif ($chardata[$i]['type'] == 'N') { + // N2. Any remaining neutrals take the embedding direction + $chardata[$i]['type'] = $chardata[$i]['sor']; + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels. + // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level. + for ($i=0; $i < $numchars; $i++) { + $odd = $chardata[$i]['level'] % 2; + if ($odd) { + if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){ + $chardata[$i]['level'] += 1; + } + } else { + if ($chardata[$i]['type'] == 'R') { + $chardata[$i]['level'] += 1; + } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){ + $chardata[$i]['level'] += 2; + } + } + $maxlevel = max($chardata[$i]['level'],$maxlevel); + } + + // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level: + // 1. Segment separators, + // 2. Paragraph separators, + // 3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and + // 4. Any sequence of white space characters at the end of the line. + for ($i=0; $i < $numchars; $i++) { + if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) { + $chardata[$i]['level'] = $pel; + } elseif ($chardata[$i]['type'] == 'WS') { + $j = $i+1; + while ($j < $numchars) { + if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR + (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) { + $chardata[$i]['level'] = $pel;; + break; + } elseif ($chardata[$j]['type'] != 'WS') { + break; + } + $j++; + } + } + } + + // Arabic Shaping + // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run. + if ($arabic) { + for ($i=0; $i < $numchars; $i++) { + if ($unicode[$chardata[$i]['char']] == 'AL') { + if (($i > 0) AND (($i+1) < $numchars) AND + ($unicode[$chardata[($i-1)]['char']] == 'AL') AND + ($unicode[$chardata[($i+1)]['char']] == 'AL') AND + ($chardata[($i-1)]['type'] == $chardata[$i]['type']) AND + ($chardata[($i+1)]['type'] == $chardata[$i]['type'])) { + // medial + if (isset($unicode_arlet[$chardata[$i]['char']][3])) { + $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][3]; + } + } elseif ((($i+1) < $numchars) AND + ($unicode[$chardata[($i+1)]['char']] == 'AL') AND + ($chardata[($i+1)]['type'] == $chardata[$i]['type'])) { + // initial + if (isset($unicode_arlet[$chardata[$i]['char']][2])) { + $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][2]; + } + } elseif (($i > 0) AND + ($unicode[$chardata[($i-1)]['char']] == 'AL') AND + ($chardata[($i-1)]['type'] == $chardata[$i]['type'])) { + // final + if (isset($unicode_arlet[$chardata[$i]['char']][1])) { + $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][1]; + } + } elseif (isset($unicode_arlet[$chardata[$i]['char']][0])) { + // isolated + $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][0]; + } + } + } + } + + // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher. + for ($j=$maxlevel; $j > 0; $j--) { + $ordarray = Array(); + $revarr = Array(); + $onlevel = false; + for ($i=0; $i < $numchars; $i++) { + if ($chardata[$i]['level'] >= $j) { + $onlevel = true; + if (isset($unicode_mirror[$chardata[$i]['char']])) { + // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true. + $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']]; + } + $revarr[] = $chardata[$i]; + } else { + if($onlevel) { + $revarr = array_reverse($revarr); + $ordarray = array_merge($ordarray, $revarr); + $revarr = Array(); + $onlevel = false; + } + $ordarray[] = $chardata[$i]; + } + } + if($onlevel) { + $revarr = array_reverse($revarr); + $ordarray = array_merge($ordarray, $revarr); + } + $chardata = $ordarray; + } + + $ordarray = array(); + for ($i=0; $i < $numchars; $i++) { + $ordarray[] = $chardata[$i]['char']; + } + + return $this->arrUTF8ToUTF16BE($ordarray, $setbom); + } + + // END OF BIDIRECTIONAL TEXT SECTION ------------------- + + /* + * Adds a bookmark. + * @param string $txt bookmark description. + * @param int $level bookmark level. + * @param float $y Ordinate of the boorkmark position (default = -1 = current position). + * @access public + * @author Olivier Plathey, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function Bookmark($txt, $level=0, $y=-1) { + if($y == -1) { + $y = $this->GetY(); + } + $this->outlines[]=array('t'=>$txt,'l'=>$level,'y'=>$y,'p'=>$this->PageNo()); + } + + /* + * Create a bookmark PDF string. + * @access private + * @author Olivier Plathey, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + private function _putbookmarks() { + $nb = count($this->outlines); + if($nb == 0) { + return; + } + $lru = array(); + $level = 0; + foreach($this->outlines as $i=>$o) { + if($o['l'] > 0) { + $parent = $lru[$o['l'] - 1]; + //Set parent and last pointers + $this->outlines[$i]['parent'] = $parent; + $this->outlines[$parent]['last'] = $i; + if($o['l'] > $level) { + //Level increasing: set first pointer + $this->outlines[$parent]['first'] = $i; + } + } else { + $this->outlines[$i]['parent']=$nb; + } + if($o['l']<=$level and $i>0) { + //Set prev and next pointers + $prev = $lru[$o['l']]; + $this->outlines[$prev]['next'] = $i; + $this->outlines[$i]['prev'] = $prev; + } + $lru[$o['l']] = $i; + $level = $o['l']; + } + //Outline items + $n = $this->n+1; + foreach($this->outlines as $i=>$o) { + $this->_newobj(); + $this->_out('<</Title '.$this->_textstring($o['t'])); + $this->_out('/Parent '.($n+$o['parent']).' 0 R'); + if(isset($o['prev'])) + $this->_out('/Prev '.($n+$o['prev']).' 0 R'); + if(isset($o['next'])) + $this->_out('/Next '.($n+$o['next']).' 0 R'); + if(isset($o['first'])) + $this->_out('/First '.($n+$o['first']).' 0 R'); + if(isset($o['last'])) + $this->_out('/Last '.($n+$o['last']).' 0 R'); + $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]',1+2*$o['p'],($this->h-$o['y'])*$this->k)); + $this->_out('/Count 0>>'); + $this->_out('endobj'); + } + //Outline root + $this->_newobj(); + $this->OutlineRoot=$this->n; + $this->_out('<</Type /Outlines /First '.$n.' 0 R'); + $this->_out('/Last '.($n+$lru[0]).' 0 R>>'); + $this->_out('endobj'); + } + + // --- JAVASCRIPT - FORMS ------------------------------ + + /* + * Adds a javascript + * @access public + * @author Johannes Güntert, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function IncludeJS($script) { + $this->javascript .= $script; + } + + /* + * Create a javascript PDF string. + * @access private + * @author Johannes Güntert, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + private function _putjavascript() { + if (empty($this->javascript)) { + return; + } + $this->_newobj(); + $this->n_js = $this->n; + $this->_out('<<'); + $this->_out('/Names [(EmbeddedJS) '.($this->n+1).' 0 R ]'); + $this->_out('>>'); + $this->_out('endobj'); + $this->_newobj(); + $this->_out('<<'); + $this->_out('/S /JavaScript'); + $this->_out('/JS '.$this->_textstring($this->javascript)); + $this->_out('>>'); + $this->_out('endobj'); + } + + /* + * Convert color to javascript color. + * @param string $color color name or #RRGGBB + * @access private + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + private function _JScolor($color) { + static $aColors = array('transparent','black','white','red','green','blue','cyan','magenta','yellow','dkGray','gray','ltGray'); + if(substr($color,0,1) == '#') { + return sprintf("['RGB',%.3f,%.3f,%.3f]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255); + } + if(!in_array($color,$aColors)) { + $this->Error('Invalid color: '.$color); + } + return 'color.'.$color; + } + + /* + * Adds a javascript form field. + * @param string $type field type + * @param string $name field name + * @param int $x horizontal position + * @param int $y vertical position + * @param int $w width + * @param int $h height + * @param string $prop properties + * @access private + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + private function _addfield($type, $name, $x, $y, $w, $h, $prop) { + $k = $this->k; + $this->javascript .= sprintf("f=addField('%s','%s',%d,[%.2f,%.2f,%.2f,%.2f]);",$name,$type,$this->PageNo()-1,$x*$k,($this->h-$y)*$k+1,($x+$w)*$k,($this->h-$y-$h)*$k+1); + $this->javascript .= 'f.textSize='.$this->FontSizePt.';'; + if(isset($prop['value'])) { + $this->javascript .= "f.value='".addslashes($prop['value'])."';"; + } + if(isset($prop['TextColor'])) { + $this->javascript .= 'f.textColor='.$this->_JScolor($prop['TextColor']).';'; + } + if(isset($prop['FillColor'])) { + $this->javascript .= 'f.fillColor='.$this->_JScolor($prop['FillColor']).';'; + } + if(isset($prop['BorderColor'])) { + $this->javascript .= 'f.strokeColor='.$this->_JScolor($prop['BorderColor']).';'; + } + if(isset($prop['BorderStyle'])) { + $this->javascript .= "f.borderStyle='".$prop['BorderStyle']."';"; + } + $this->x+=$w; + } + + /* + * Creates a text field + * @param string $name field name + * @param int $w width + * @param int $h height + * @param string $prop properties. The value property allows to set the initial value. The multiline property allows to define the field as multiline. + * @access public + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function TextField($name, $w, $h, $prop=array()) { + $this->_addfield('text',$name,$this->x,$this->y,$w,$h,$prop); + if(isset($prop['multiline']) AND $prop['multiline']) { + $this->javascript .= 'f.multiline=true;'; + } + } + + /* + * Creates a Combo-box field + * @param string $name field name + * @param int $w width + * @param int $h height + * @param array $values array containing the list of values. + * @param string $prop properties. + * @access public + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function ComboBox($name, $w, $h, $values, $prop=array()) { + $this->_addfield('combobox',$name,$this->x,$this->y,$w,$h,$prop); + $s = ''; + foreach($values as $value) { + $s .= "'".addslashes($value)."',"; + } + $this->javascript .= 'f.setItems(['.substr($s,0,-1).']);'; + } + + /* + * Creates a CheckBox field + * @param string $name field name + * @param int $w width + * @param boolean $checked define the initial state (default = false). + * @param string $prop properties. + * @access public + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function CheckBox($name, $w, $checked=false, $prop=array()) { + $prop['value'] = ($checked ? 'Yes' : 'Off'); + if(!isset($prop['BorderColor'])) { + $prop['BorderColor']='black'; + } + $this->_addfield('checkbox',$name,$this->x,$this->y,$w,$w,$prop); + } + + /* + * Creates a button field + * @param string $name field name + * @param int $w width + * @param int $h height + * @param string $caption caption. + * @param string $action action triggered by the button (JavaScript code). + * @param string $prop properties. + * @access public + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function Button($name, $w, $h, $caption, $action, $prop=array()) { + if(!isset($prop['BorderColor'])) { + $prop['BorderColor']='black'; + } + $prop['BorderStyle']='beveled'; + $this->_addfield('button',$name,$this->x,$this->y,$w,$h,$prop); + $this->javascript .= "f.buttonSetCaption('".addslashes($caption)."');"; + $this->javascript .= "f.setAction('MouseUp','".addslashes($action)."');"; + $this->javascript .= "f.highlight='push';"; + $this->javascript .= 'f.print=false;'; + } + + // END JAVASCRIPT - FORMS ------------------------------ + + } // END OF TCPDF CLASS + + //Handle special IE contype request + if(isset($_SERVER['HTTP_USER_AGENT']) AND ($_SERVER['HTTP_USER_AGENT']=='contype')) { + header('Content-Type: application/pdf'); + exit; + } + } //============================================================+ // END OF FILE From 9fb8619f2ace178e5d4d4dc055ce7e5358b9bcd8 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Sat, 10 Jul 2010 15:44:17 +0500 Subject: [PATCH 20/48] PDF Schema File: Relation_Stats class structured --- .../schema/Pdf_Relation_Schema.class.php | 156 +++++++++--------- 1 file changed, 82 insertions(+), 74 deletions(-) diff --git a/libraries/schema/Pdf_Relation_Schema.class.php b/libraries/schema/Pdf_Relation_Schema.class.php index c82fa2a80..286c81dfc 100644 --- a/libraries/schema/Pdf_Relation_Schema.class.php +++ b/libraries/schema/Pdf_Relation_Schema.class.php @@ -19,7 +19,7 @@ require_once './libraries/tcpdf/tcpdf.php'; /** * Extends the "TCPDF" class and helps * in developing the structure of PDF Schema Export - * * + * * @name PMA_PDF * @author Muhammad Adnan <hiddenpearls@gmail.com> * @copyright @@ -685,19 +685,73 @@ class Table_Stats { /** * Draws relation links * - * @access private - * @see - * @package phpMyAdmin + * @access public + * @see PMA_PDF */ -class Relation_Stats { +class Relation_Stats +{ /** - * Defines private properties + * Defines properties */ - var $x_src, $y_src; - var $src_dir ; - var $dest_dir; - var $x_dest, $y_dest; - var $w_tick = 5; + public $xSrc, $ySrc; + public $srcDir; + public $destDir; + public $xDest, $yDest; + public $wTick = 5; + + /** + * The "Relation_Stats" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @see Relation_Stats::_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->_getXy($master_table, $master_field); + $dest_pos = $this->_getXy($foreign_table, $foreign_field); + /* + * [0] is x-left + * [1] is x-right + * [2] is y + */ + $src_left = $src_pos[0] - $this->wTick; + $src_right = $src_pos[1] + $this->wTick; + $dest_left = $dest_pos[0] - $this->wTick; + $dest_right = $dest_pos[1] + $this->wTick; + + $d1 = abs($src_left - $dest_left); + $d2 = abs($src_right - $dest_left); + $d3 = abs($src_left - $dest_right); + $d4 = abs($src_right - $dest_right); + $d = min($d1, $d2, $d3, $d4); + + if ($d == $d1) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d2) { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d3) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } else { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } + $this->ySrc = $src_pos[2]; + $this->yDest = $dest_pos[2]; + } /** * Gets arrows coordinates @@ -715,7 +769,8 @@ class Relation_Stats { } /** - * Do draws relation links + * draws relation links and arrows + * shows foreign key relations * * @param boolean changeColor Whether to use one color per relation or not * @param integer i The id of the link to draw @@ -745,69 +800,22 @@ class Relation_Stats { $pdf->SetDrawColor($a * 255 * $e, $b * 255 * $e, $c * 255 * $e); } else { $pdf->SetDrawColor(0); - } // end if... else... - $pdf->PMA_PDF_setLineWidthScale(0.2); - $pdf->PMA_PDF_lineScale($this->x_src, $this->y_src, $this->x_src + $this->src_dir * $this->w_tick, $this->y_src); - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest, $this->x_dest, $this->y_dest); - $pdf->PMA_PDF_setLineWidthScale(0.1); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick, $this->y_src, $this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest); - // arrow - $root2 = 2 * sqrt(2); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src + $this->w_tick / $root2); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src - $this->w_tick / $root2); - - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest + $this->w_tick / $root2); - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest - $this->w_tick / $root2); - $pdf->SetDrawColor(0); - } - - /** - * The "Relation_Stats" constructor - * - * @param string masterTable The master table name - * @param string masterField The relation field in the master table - * @param string foreignTable The foreign table name - * @param string foreignField The relation field in the foreign table - * @see _Relation::_getXy - */ - function __construct($masterTable, $masterField, $foreignTable, $foreignField) - { - $src_pos = $this->_getXy($masterTable, $masterField); - $dest_pos = $this->_getXy($foreignTable, $foreignField); - $src_left = $src_pos[0] - $this->w_tick; - $src_right = $src_pos[1] + $this->w_tick; - $dest_left = $dest_pos[0] - $this->w_tick; - $dest_right = $dest_pos[1] + $this->w_tick; - - $d1 = abs($src_left - $dest_left); - $d2 = abs($src_right - $dest_left); - $d3 = abs($src_left - $dest_right); - $d4 = abs($src_right - $dest_right); - $d = min($d1, $d2, $d3, $d4); - - if ($d == $d1) { - $this->x_src = $src_pos[0]; - $this->src_dir = -1; - $this->x_dest = $dest_pos[0]; - $this->dest_dir = -1; - } elseif ($d == $d2) { - $this->x_src = $src_pos[1]; - $this->src_dir = 1; - $this->x_dest = $dest_pos[0]; - $this->dest_dir = -1; - } elseif ($d == $d3) { - $this->x_src = $src_pos[0]; - $this->src_dir = -1; - $this->x_dest = $dest_pos[1]; - $this->dest_dir = 1; - } else { - $this->x_src = $src_pos[1]; - $this->src_dir = 1; - $this->x_dest = $dest_pos[1]; - $this->dest_dir = 1; } - $this->y_src = $src_pos[2]; - $this->y_dest = $dest_pos[2]; + $pdf->PMA_PDF_setLineWidthScale(0.2); + $pdf->PMA_PDF_lineScale($this->xSrc, $this->ySrc, $this->xSrc + $this->srcDir * $this->wTick, $this->ySrc); + $pdf->PMA_PDF_lineScale($this->xDest + $this->destDir * $this->wTick, $this->yDest, $this->xDest, $this->yDest); + $pdf->PMA_PDF_setLineWidthScale(0.1); + $pdf->PMA_PDF_lineScale($this->xSrc + $this->srcDir * $this->wTick, $this->ySrc, $this->xDest + $this->destDir * $this->wTick, $this->yDest); + /* + * Draws arrows -> + */ + $root2 = 2 * sqrt(2); + $pdf->PMA_PDF_lineScale($this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick, $this->ySrc + $this->wTick / $root2); + $pdf->PMA_PDF_lineScale($this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick, $this->ySrc - $this->wTick / $root2); + + $pdf->PMA_PDF_lineScale($this->xDest + $this->destDir * $this->wTick / 2, $this->yDest, $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick, $this->yDest + $this->wTick / $root2); + $pdf->PMA_PDF_lineScale($this->xDest + $this->destDir * $this->wTick / 2, $this->yDest, $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick, $this->yDest - $this->wTick / $root2); + $pdf->SetDrawColor(0); } } From f9c29173bfb080910e15ac46c6a34f8be8aac51e Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 14 Jul 2010 02:32:57 +0500 Subject: [PATCH 21/48] Dia Schem Class: documented + implemented --- .../schema/Dia_Relation_Schema.class.php | 726 +++++++++++++++++- 1 file changed, 720 insertions(+), 6 deletions(-) diff --git a/libraries/schema/Dia_Relation_Schema.class.php b/libraries/schema/Dia_Relation_Schema.class.php index 30982a308..79141f7af 100644 --- a/libraries/schema/Dia_Relation_Schema.class.php +++ b/libraries/schema/Dia_Relation_Schema.class.php @@ -1,18 +1,732 @@ <?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * + * @version $Id$ + * @package phpMyAdmin + */ + include_once("Export_Relation_Schema.class.php"); +/** + * This Class inherits the XMLwriter class and + * helps in developing structure of DIA Schema Export + * + * @name PMA_DIA + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @access public + * @see http://php.net/manual/en/book.xmlwriter.php + */ + class PMA_DIA extends XMLWriter { public $title; - public $author; - public $font; - public $fontSize; - - + public $author; + public $font; + public $fontSize; + + function __construct() + { + $this->openMemory(); + /* + * Set indenting using three spaces, + * so output is formatted + */ + + $this->setIndent(TRUE); + $this->setIndentString(' '); + /* + * Create the XML document + */ + + $this->startDocument('1.0','UTF-8'); + } + + function startDiaDoc($paper,$topMargin,$bottomMargin,$leftMargin,$rightMargin,$portrait) + { + if($portrait == 'P'){ + $isPortrait='true'; + }else{ + $isPortrait='false'; + } + $this->startElement('dia:diagram'); + $this->writeAttribute('xmlns:dia', 'http://www.lysator.liu.se/~alla/dia/'); + $this->startElement('dia:diagramdata'); + $this->writeRaw ( + '<dia:attribute name="background"> + <dia:color val="#ffffff"/> + </dia:attribute> + <dia:attribute name="pagebreak"> + <dia:color val="#000099"/> + </dia:attribute> + <dia:attribute name="paper"> + <dia:composite type="paper"> + <dia:attribute name="name"> + <dia:string>#'.$paper.'#</dia:string> + </dia:attribute> + <dia:attribute name="tmargin"> + <dia:real val="'.$topMargin.'"/> + </dia:attribute> + <dia:attribute name="bmargin"> + <dia:real val="'.$bottomMargin.'"/> + </dia:attribute> + <dia:attribute name="lmargin"> + <dia:real val="'.$leftMargin.'"/> + </dia:attribute> + <dia:attribute name="rmargin"> + <dia:real val="'.$rightMargin.'"/> + </dia:attribute> + <dia:attribute name="is_portrait"> + <dia:boolean val="'.$isPortrait.'"/> + </dia:attribute> + <dia:attribute name="scaling"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="fitto"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + </dia:attribute> + <dia:attribute name="grid"> + <dia:composite type="grid"> + <dia:attribute name="width_x"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="width_y"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="visible_x"> + <dia:int val="1"/> + </dia:attribute> + <dia:attribute name="visible_y"> + <dia:int val="1"/> + </dia:attribute> + <dia:composite type="color"/> + </dia:composite> + </dia:attribute> + <dia:attribute name="color"> + <dia:color val="#d8e5e5"/> + </dia:attribute> + <dia:attribute name="guides"> + <dia:composite type="guides"> + <dia:attribute name="hguides"/> + <dia:attribute name="vguides"/> + </dia:composite> + </dia:attribute>'); + $this->endElement(); + $this->startElement('dia:layer'); + $this->writeAttribute('name', 'Background'); + $this->writeAttribute('visible', 'true'); + $this->writeAttribute('active', 'true'); + + } + + function endDiaDoc() + { + $this->endElement(); + $this->endDocument(); + } + + function showOutput() + { + if(ob_end_clean()){ + ob_end_clean(); + //ob_start(); + } + //header('Content-type: text/xml'); + header('Content-Disposition: attachment; filename="downloaded.dia"'); + $output = $this->flush(); + print $output; + } } +/** + * Table preferences/statistics + * + * This class preserves the table co-ordinates,fields + * and helps in drawing/generating the Tables in dia XML document. + * + * @name Table_Stats + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @see PMA_DIA + */ +class Table_Stats +{ + /** + * Defines properties + */ + public $tableName; + public $fields = array(); + public $x, $y; + public $primary = array(); + public $tableId; + public $tableColor; + + /** + * The "Table_Stats" constructor + * + * @param string table_name The table name + * @param integer pageNumber The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @param boolean showKeys Whether to display ONLY keys or not + * @return void + * @global object The current dia document + * @global array The relations settings + * @global string The current db name + * @see PMA_DIA + */ + function __construct($tableName, $pageNumber, $showKeys = false) + { + global $dia, $cfgRelation, $db; + + $this->tableName = $tableName; + $sql = 'DESCRIBE ' . PMA_backquote($tableName); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + //$dia->Error(sprintf(__('The %s table doesn\'t exist!'), $tableName)); + } + /* + * load fields + * check to see if it will load all fields or only the foreign keys + */ + if ($showKeys) { + $indexes = PMA_Index::getFromTable($this->tableName, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + // $dia->Error(sprintf(__('Please configure the coordinates for table %s'), $tableName)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + /* + * displayfield + */ + $this->displayfield = PMA_getDisplayField($db, $tableName); + /* + * index + */ + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } + /** + * Every object in Dia document needs an ID to identify + * so, we used a static variable to keep the things unique + */ + PMA_Dia_Relation_Schema::$objectId += 1; + $this->tableId = PMA_Dia_Relation_Schema::$objectId; + } + + /** + * Do draw the table + * + * Tables are generated using object type Database - Table + * primary fields are underlined in tables. Dia object + * is used to generate the XML of Dia Document. Database Table + * Object and their attributes are involved in the combination + * of displaing Database - Table on Dia Document. + + * @param boolean changeColor Whether to show color for tables text or not + if changeColor is true then an array of $listOfColors + will be used to choose the random colors for tables text + we can change/add more colors to this array + @return void + * @global object The current Dia document + * @access public + * @see PMA_DIA + */ + public function tableDraw($changeColor) + { + global $dia; + + if ($changeColor) { + $listOfColors = array( + 'FF0000', + '000099', + '00FF00' + ); + shuffle($listOfColors); + $this->tableColor = '#'.$listOfColors[0].''; + } else { + $this->tableColor = '#000000'; + } + + $dia->startElement('dia:object'); + $dia->writeAttribute('type', 'Database - Table'); + $dia->writeAttribute('version', '0'); + $dia->writeAttribute('id', ''.$this->tableId.''); + $dia->writeRaw( + '<dia:attribute name="obj_pos"> + <dia:point val="'.($this->x * 0.026458333).','.($this->y * 0.026458333).'"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="'.($this->x * 0.026458333).','.($this->y * 0.026458333).';9.97,9.2"/> + </dia:attribute> + <dia:attribute name="meta"> + <dia:composite type="dict"/> + </dia:attribute> + <dia:attribute name="elem_corner"> + <dia:point val="'.($this->x * 0.026458333).','.($this->y * 0.026458333).'"/> + </dia:attribute> + <dia:attribute name="elem_width"> + <dia:real val="5.9199999999999999"/> + </dia:attribute> + <dia:attribute name="elem_height"> + <dia:real val="3.5"/> + </dia:attribute> + <dia:attribute name="text_colour"> + <dia:color val="'.$this->tableColor.'"/> + </dia:attribute> + <dia:attribute name="line_colour"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="fill_colour"> + <dia:color val="#ffffff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="name"> + <dia:string>#'.$this->_tableName.'#</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visible_comment"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="tagging_comment"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="underline_primary_key"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="bold_primary_keys"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="normal_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="name_font"> + <dia:font family="sans" style="80" name="Helvetica-Bold"/> + </dia:attribute> + <dia:attribute name="comment_font"> + <dia:font family="sans" style="0" name="Helvetica"/> + </dia:attribute> + <dia:attribute name="normal_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="name_font_height"> + <dia:real val="0.69999999999999996"/> + </dia:attribute> + <dia:attribute name="comment_font_height"> + <dia:real val="0.69999999999999996"/> + </dia:attribute>' + ); + + $dia->startElement('dia:attribute'); + $dia->writeAttribute('name', 'attributes'); + + foreach ($this->fields as $field) { + $dia->writeRaw( + '<dia:composite type="table_attribute"> + <dia:attribute name="name"> + <dia:string>#'.$field.'#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute>' + ); + unset($pm); + $pm = 'false'; + if (in_array($field, $this->primary)) { + $pm = 'true'; + } + if ($field == $this->displayfield) { + $pm = 'false'; + } + $dia->writeRaw( + '<dia:attribute name="primary_key"> + <dia:boolean val="'.$pm.'"/> + </dia:attribute> + <dia:attribute name="nullable"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="unique"> + <dia:boolean val="'.$pm.'"/> + </dia:attribute> + </dia:composite>' + ); + } + $dia->endElement(); + $dia->endElement(); + } +} + +/** + * Relation preferences/statistics + * + * This class fetches the table master and foreign fields positions + * and helps in generating the Table references and then connects + * master table's master field to foreign table's foreign key + * in dia XML document. + * + * @name Relation_Stats + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @see PMA_DIA + */ +class Relation_Stats +{ + /** + * Defines properties + */ + public $srcConnPointsRight; + public $srcConnPointsLeft; + public $destConnPointsRight; + public $destConnPointsLeft; + public $masterTableId; + public $foreignTableId; + public $masterTablePos; + public $foreignTablePos; + public $referenceColor; + + /** + * The "Relation_Stats" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @return void + * @see Relation_Stats::_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->_getXy($master_table, $master_field); + $dest_pos = $this->_getXy($foreign_table, $foreign_field); + $this->srcConnPointsLeft = $src_pos[0]; + $this->srcConnPointsRight = $src_pos[1]; + $this->destConnPointsLeft = $dest_pos[0]; + $this->destConnPointsRight = $dest_pos[1]; + $this->masterTablePos = $src_pos[2]; + $this->foreignTablePos = $dest_pos[2]; + $this->masterTableId = $master_table->tableId; + $this->foreignTableId = $foreign_table->tableId; + } + + /** + * Each Table object have connection points + * which is used to connect to other objects in Dia + * we detect the position of key in fields and + * then determines its left and right connection + * points. + * + * @param string table The current table name + * @param string column The relation column name + * @return array Table right,left connection points and key position + * @access private + */ + private function _getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // left, right, position + $value = 12; + if($pos != 0) + { + return array($pos + $value + $pos, $pos + $value + $pos + 1, $pos); + } + return array($pos + $value , $pos + $value + 1, $pos); + } + + /** + * Draws relation references + * + * connects master table's master field to foreign table's + * forein field using Dia object type Database - Reference + * Dia object is used to generate the XML of Dia Document. + * Database reference Object and their attributes are involved + * in the combination of displaing Database - reference on Dia Document. + * + * @param boolean changeColor Whether to use one color per relation or not + if changeColor is true then an array of $listOfColors + will be used to choose the random colors for references + lines. we can change/add more colors to this array + * @return void + * @global object The current Dia document + * @access public + * @see PMA_PDF + */ + public function relationDraw($changeColor) + { + global $dia; + + PMA_Dia_Relation_Schema::$objectId += 1; + /* + * if source connection points and destination connection + * points are same then return it false and don't draw that + * relation + */ + if ( $this->srcConnPointsRight == $this->destConnPointsRight ){ + if ( $this->srcConnPointsLeft == $this->destConnPointsLeft ){ + return false; + } + } + + if ($changeColor) { + $listOfColors = array( + 'FF0000', + '000099', + '00FF00' + ); + shuffle($listOfColors); + $this->referenceColor = '#'.$listOfColors[0].''; + } else { + $this->referenceColor = '#000000'; + } + + $dia->writeRaw( + '<dia:object type="Database - Reference" version="0" id="'.PMA_Dia_Relation_Schema::$objectId.'"> + <dia:attribute name="obj_pos"> + <dia:point val="3.27,18.9198"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="2.27,8.7175;17.7679,18.9198"/> + </dia:attribute> + <dia:attribute name="meta"> + <dia:composite type="dict"/> + </dia:attribute> + <dia:attribute name="orth_points"> + <dia:point val="3.27,18.9198"/> + <dia:point val="2.27,18.9198"/> + <dia:point val="2.27,14.1286"/> + <dia:point val="17.7679,14.1286"/> + <dia:point val="17.7679,9.3375"/> + <dia:point val="16.7679,9.3375"/> + </dia:attribute> + <dia:attribute name="orth_orient"> + <dia:enum val="0"/> + <dia:enum val="1"/> + <dia:enum val="0"/> + <dia:enum val="1"/> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="orth_autoroute"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="text_colour"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="line_colour"> + <dia:color val="'.$this->referenceColor.'"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_style"> + <dia:enum val="0"/> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="corner_radius"> + <dia:real val="0"/> + </dia:attribute> + <dia:attribute name="end_arrow"> + <dia:enum val="22"/> + </dia:attribute> + <dia:attribute name="end_arrow_length"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:attribute name="end_arrow_width"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:attribute name="start_point_desc"> + <dia:string>#1#</dia:string> + </dia:attribute> + <dia:attribute name="end_point_desc"> + <dia:string>#n#</dia:string> + </dia:attribute> + <dia:attribute name="normal_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="normal_font_height"> + <dia:real val="0.59999999999999998"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="'.$this->masterTableId.'" connection="'.$this->srcConnPointsRight.'"/> + <dia:connection handle="1" to="'.$this->foreignTableId.'" connection="'.$this->destConnPointsRight.'"/> + </dia:connections> + </dia:object>' + ); + } +} + +/** + * Dia Relation Schema Class + * + * Purpose of this class is to generate the Dia XML Document + * which is used for representing the database diagrams in Dia IDE + * This class uses Database Table and Reference Objects of Dia and with + * the combination of these objects actually helps in preparing Dia XML. + * + * Dia XML is generated by using XMLWriter php extension and this class + * inherits Export_Relation_Schema class has common functionality added + * to this class + * + * @name Dia_Relation_Schema + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + */ class PMA_Dia_Relation_Schema extends PMA_Export_Relation_Schema { - + /** + * Defines properties + */ + private $_tables = array(); + private $_relations = array(); + private $_topMargin = 2.8222000598907471; + private $_bottomMargin = 2.8222000598907471; + private $_leftMargin = 2.8222000598907471; + private $_rightMargin = 2.8222000598907471; + public static $objectId = 0; + + function __construct() + { + global $dia,$db; + + $this->setPageNumber($_POST['pdf_page_number']); + $this->setShowGrid($_POST['show_grid']); + $this->setShowColor($_POST['show_color']); + $this->setShowKeys($_POST['show_keys']); + $this->setOrientation($_POST['orientation']); + $this->setPaper($_POST['paper']); + $this->setExportType($_POST['export_type']); + + $dia = new PMA_DIA(); + $dia->startDiaDoc($this->paper,$this->_topMargin,$this->_bottomMargin,$this->_leftMargin,$this->_rightMargin,$this->orientation); + $alltables = $this->getAllTables($db,$this->pageNumber); + foreach ($alltables as $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new Table_Stats($table, $this->pageNumber, $this->showKeys); + } + } + + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + /* put the foreign table on the schema only if selected + * by the user + * (do not use array_search() because we would have to + * to do a === FALSE and this is not PHP3 compatible) + */ + if (in_array($rel['foreign_table'], $alltables)) { + $this->_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'],$this->showKeys); + } + } + } + } + $this->_drawTables($this->showColor); + + if ($seen_a_relation) { + $this->_drawRelations($this->showColor); + } + $dia->endDiaDoc(); + $dia->showOutput(); + exit(); + print '<pre>'; + print_r(get_object_vars($dia)); + print_r(get_object_vars($this)); + print '</pre>'; + } + + /** + * Defines relation objects + * + * @param string masterTable The master table name + * @param string masterField The relation field in the master table + * @param string foreignTable The foreign table name + * @param string foreignField The relation field in the foreign table + * @return void + * @access private + * @see Table_Stats::__construct(),Relation_Stats::__construct() + */ + private function _addRelation($masterTable, $masterField, $foreignTable, $foreignField, $showKeys) + { + if (!isset($this->tables[$masterTable])) { + $this->tables[$masterTable] = new Table_Stats($masterTable, $this->pageNumber, $showKeys); + } + if (!isset($this->tables[$foreignTable])) { + $this->tables[$foreignTable] = new Table_Stats($foreignTable, $this->pageNumber, $showKeys); + } + $this->_relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); + } + + /** + * Draws relation references + * + * connects master table's master field to + * foreign table's forein field using Dia object + * type Database - Reference + * + * @param boolean changeColor Whether to use one color per relation or not + * @return void + * @access private + * @see Relation_Stats::relationDraw() + */ + private function _drawRelations($changeColor) + { + foreach ($this->_relations as $relation) { + $relation->relationDraw($changeColor); + } + } + + /** + * Draws tables + * + * Tables are generated using Dia object type Database - Table + * primary fields are underlined and bold in tables + * + * @param boolean changeColor Whether to show color for tables text or not + * @return void + * @access private + * @see Table_Stats::tableDraw() + */ + private function _drawTables($changeColor) + { + foreach ($this->tables as $table) { + $table->tableDraw($changeColor); + } + } } ?> \ No newline at end of file From 45399cbadd638b30cea9473e834201b963afa31d Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 14 Jul 2010 06:10:05 +0500 Subject: [PATCH 22/48] Dia Schema Class: download fileName added + improved documentation --- .../schema/Dia_Relation_Schema.class.php | 70 ++++++++++++++++--- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/libraries/schema/Dia_Relation_Schema.class.php b/libraries/schema/Dia_Relation_Schema.class.php index 79141f7af..a35a39418 100644 --- a/libraries/schema/Dia_Relation_Schema.class.php +++ b/libraries/schema/Dia_Relation_Schema.class.php @@ -26,7 +26,15 @@ class PMA_DIA extends XMLWriter public $author; public $font; public $fontSize; - + + /** + * The "PMA_DIA" constructor + * + * Upon instantiation This starts writing the Dia XML document + * + * @return void + * @see PMA_DIA,Table_Stats,Relation_Stats + */ function __construct() { $this->openMemory(); @@ -44,6 +52,24 @@ class PMA_DIA extends XMLWriter $this->startDocument('1.0','UTF-8'); } + /** + * Starts Dia Document + * + * dia document starts by first initializing dia:diagram tag + * then dia:diagramdata contains all the attributes that needed + * to define the document, then finally a Layer starts which + * holds all the objects. + * + * @param string paper The size of the paper/document + * @param float topMargin top margin of the paper/document in cm + * @param float bottomMargin bottom margin of the paper/document in cm + * @param float leftMargin left margin of the paper/document in cm + * @param float rightMargin right margin of the paper/document in cm + * @param string portrait document will be portrait or landscape + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),XMLWriter::writeRaw() + */ function startDiaDoc($paper,$topMargin,$bottomMargin,$leftMargin,$rightMargin,$portrait) { if($portrait == 'P'){ @@ -123,20 +149,35 @@ class PMA_DIA extends XMLWriter } + /** + * Ends Dia Document + * + * @return void + * @access public + * @see XMLWriter::endElement(),XMLWriter::endDocument() + */ function endDiaDoc() { $this->endElement(); $this->endDocument(); } - function showOutput() + /** + * Output Dia Document for download + * + * @param string fileName name of the dia document + * @return void + * @access public + * @see XMLWriter::flush() + */ + function showOutput($fileName) { if(ob_end_clean()){ ob_end_clean(); //ob_start(); } //header('Content-type: text/xml'); - header('Content-Disposition: attachment; filename="downloaded.dia"'); + header('Content-Disposition: attachment; filename="'.$fileName.'.dia"'); $output = $this->flush(); print $output; } @@ -173,7 +214,7 @@ class Table_Stats * @param integer pageNumber The current page number (from the * $cfg['Servers'][$i]['table_coords'] table) * @param boolean showKeys Whether to display ONLY keys or not - * @return void + * @return void * @global object The current dia document * @global array The relations settings * @global string The current db name @@ -426,7 +467,7 @@ class Relation_Stats * @param string master_field The relation field in the master table * @param string foreign_table The foreign table name * @param string foreigh_field The relation field in the foreign table - * @return void + * @return void * @see Relation_Stats::_getXy */ function __construct($master_table, $master_field, $foreign_table, $foreign_field) @@ -445,10 +486,10 @@ class Relation_Stats /** * Each Table object have connection points - * which is used to connect to other objects in Dia - * we detect the position of key in fields and - * then determines its left and right connection - * points. + * which is used to connect to other objects in Dia + * we detect the position of key in fields and + * then determines its left and right connection + * points. * * @param string table The current table name * @param string column The relation column name @@ -618,6 +659,15 @@ class PMA_Dia_Relation_Schema extends PMA_Export_Relation_Schema private $_rightMargin = 2.8222000598907471; public static $objectId = 0; + /** + * The "PMA_Dia_Relation_Schema" constructor + * + * Upon instantiation This outputs the Dia XML document + * that user can download + * + * @return void + * @see PMA_DIA,Table_Stats,Relation_Stats + */ function __construct() { global $dia,$db; @@ -662,7 +712,7 @@ class PMA_Dia_Relation_Schema extends PMA_Export_Relation_Schema $this->_drawRelations($this->showColor); } $dia->endDiaDoc(); - $dia->showOutput(); + $dia->showOutput($db.'-'.$this->pageNumber); exit(); print '<pre>'; print_r(get_object_vars($dia)); From 6fc9576f3ed0da066365eb8dfb6b804d04748c4e Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 14 Jul 2010 20:36:13 +0500 Subject: [PATCH 23/48] Svg Schema Class: removed global vars + improved documentation --- .../schema/Svg_Relation_Schema.class.php | 288 ++++++++++++++---- 1 file changed, 225 insertions(+), 63 deletions(-) diff --git a/libraries/schema/Svg_Relation_Schema.class.php b/libraries/schema/Svg_Relation_Schema.class.php index 726061426..996801019 100644 --- a/libraries/schema/Svg_Relation_Schema.class.php +++ b/libraries/schema/Svg_Relation_Schema.class.php @@ -10,13 +10,13 @@ include_once("Export_Relation_Schema.class.php"); /** * This Class inherits the XMLwriter class and - * helps in developing structure of SVG Schema - * + * helps in developing structure of SVG Schema Export * * @name PMA_SVG * @author Muhammad Adnan <hiddenpearls@gmail.com> * @copyright * @license + * @access public * @see http://php.net/manual/en/book.xmlwriter.php */ @@ -27,6 +27,14 @@ class PMA_SVG extends XMLWriter public $font; public $fontSize; + /** + * The "PMA_SVG" constructor + * + * Upon instantiation This starts writing the Svg XML document + * + * @return void + * @see XMLWriter::openMemory(),XMLWriter::setIndent(),XMLWriter::startDocument() + */ function __construct() { $this->openMemory(); @@ -43,38 +51,92 @@ class PMA_SVG extends XMLWriter $this->startDocument('1.0','UTF-8'); $this->startDtd('svg','-//W3C//DTD SVG 1.1//EN','http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'); - $this->endDtd(); + $this->endDtd(); } - function setTitle($title) + /** + * Set document title + * + * @param string value sets the title text + * @return void + * @access public + */ + function setTitle($value) { - $this->title = $title; - } - - function setAuthor($author) - { - $this->author = $author; + $this->title = $value; } - function setFont($font) + /** + * Set document author + * + * @param string value sets the author + * @return void + * @access public + */ + function setAuthor($value) { - $this->font = $font; + $this->author = $value; } + + /** + * Set document font + * + * @param string value sets the font e.g Arial, Sans-serif etc + * @return void + * @access public + */ + function setFont($value) + { + $this->font = $value; + } + + /** + * Get document font + * + * @return string returns the font name + * @access public + */ function getFont() { return $this->font; } - function setFontSize($fontSize) + /** + * Set document font size + * + * @param string value sets the font size in pixels + * @return void + * @access public + */ + function setFontSize($value) { - $this->fontSize = $fontSize; + $this->fontSize = $value; } + /** + * Get document font size + * + * @return string returns the font size + * @access public + */ function getFontSize() { return $this->fontSize; } + /** + * Starts Svg Document + * + * svg document starts by first initializing svg tag + * which contains all the attributes and namespace that needed + * to define the svg document + * + * @param integer width total width of the Svg document + * @param integer height total height of the Svg document + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute() + */ function startSvgDoc($width,$height) { $this->startElement('svg'); @@ -84,23 +146,65 @@ class PMA_SVG extends XMLWriter $this->writeAttribute('version', '1.1'); } + /** + * Ends Svg Document + * + * @return void + * @access public + * @see XMLWriter::endElement(),XMLWriter::endDocument() + */ function endSvgDoc() { $this->endElement(); $this->endDocument(); } - function showOutput() + /** + * output Svg Document + * + * svg document prompted to the user for download + * Svg document saved in .svg extension and can be + * easily changeable by using any svg IDE + * + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute() + */ + function showOutput($fileName) { if(ob_end_clean()){ ob_end_clean(); //ob_start(); } header('Content-type: image/svg+xml'); + //header('Content-Disposition: attachment; filename="downloaded.svg"'); $output = $this->flush(); print $output; } + /** + * Draws Svg elements + * + * SVG has some predefined shape elements like rectangle & text + * and other elements who have x,y co-ordinates are drawn. + * specify their width and height and can give styles too. + * + * @param string name Svg element name + * @param integer x The x attribute defines the left position of the element + (e.g. x="0" places the element 0 pixels from the left of + the browser window) + * @param integer y The y attribute defines the top position of the element + (e.g. y="0" places the element 0 pixels from the top of + the browser window) + * @param integer width The width attribute defines the width the element + * @param integer height The height attribute defines the height the element + * @param string text The text attribute defines the text the element + * @param string styles The style attribute defines the style the element + styles can be defined like CSS styles + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),XMLWriter::text(),XMLWriter::endElement() + */ function printElement($name,$x,$y,$width = '',$height = '',$text = '',$styles = '') { $this->startElement($name); @@ -117,6 +221,24 @@ class PMA_SVG extends XMLWriter $this->endElement(); } + /** + * Draws Svg Line element + * + * Svg line element is drawn for connecting the tables. + * arrows are also drawn by specify its start and ending + * co-ordinates + * + * @param string name Svg element name i.e line + * @param integer x1 The x1 attribute defines the start of the line on the x-axis + * @param integer y1 The y1 attribute defines the start of the line on the y-axis + * @param integer x2 The x2 attribute defines the end of the line on the x-axis + * @param integer y2 The y2 attribute defines the end of the line on the y-axis + * @param string styles The style attribute defines the style the element + styles can be defined like CSS styles + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),XMLWriter::endElement() + */ function printElementLine($name,$x1,$y1,$x2,$y2,$styles) { $this->startElement($name); @@ -128,6 +250,21 @@ class PMA_SVG extends XMLWriter $this->endElement(); } + /** + * get width of string/text + * + * Svg text element width is calcualted depending on font name + * and font size. It is very important to know the width of text + * because rectangle is drawn around it. + * + * This is a bit hardcore method. I didn't found any other than this. + * + * @param string text string that width will be calculated + * @param integer font name of the font like Arial,sans-serif etc + * @param integer fontSize size of font + * @return integer width of the text + * @access public + */ function getStringWidth($text,$font,$fontSize) { /* @@ -180,10 +317,12 @@ class PMA_SVG extends XMLWriter return ceil($textWidth*$modifier); } } + /** * Draws tables schema */ -class Table_Stats { +class Table_Stats +{ /** * Defines properties */ @@ -198,7 +337,6 @@ class Table_Stats { public $x, $y; public $primary = array(); - /** * The "Table_Stats" constructor * @@ -216,9 +354,9 @@ class Table_Stats { * @see PMA_PDF, Table_Stats::Table_Stats_setWidth, Table_Stats::Table_Stats_setHeight */ - function __construct($tableName, $font, $fontSize, &$same_wide_width, $showKeys = false, $showInfo = false) + function __construct($tableName, $font, $fontSize, $pageNumber, &$same_wide_width, $showKeys = false, $showInfo = false) { - global $svg, $pdf_page_number, $cfgRelation, $db; + global $svg, $cfgRelation, $db; $this->_tableName = $tableName; $sql = 'DESCRIBE ' . PMA_backquote($tableName); @@ -262,7 +400,7 @@ class Table_Stats { . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' - . ' AND pdf_page_number = ' . $pdf_page_number; + . ' AND pdf_page_number = ' . $pageNumber; $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { @@ -290,7 +428,7 @@ class Table_Stats { * * @access private */ - function _getTitle() + private function _getTitle() { return ($this->_showInfo ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->heightCell) : '') . ' ' . $this->_tableName; } @@ -316,7 +454,7 @@ class Table_Stats { * it is unknown what value must be added, because * table title is affected by the tabe width value */ - while ($this->width < $svg->GetStringWidth($this->_getTitle(),$font,$fontSize)) { + while ($this->width < $svg->getStringWidth($this->_getTitle(),$font,$fontSize)) { $this->width += 7; } } @@ -374,7 +512,6 @@ class Table_Stats { $field, 'fill:none;stroke:black;' ); - //echo $field.'<br />'; } } } @@ -386,7 +523,8 @@ class Table_Stats { * @access public * @see PMA_SVG,PMA_SVG::printElementLine */ -class Relation_Stats { +class Relation_Stats +{ /** * Defines properties */ @@ -403,7 +541,6 @@ class Relation_Stats { * @param string master_field The relation field in the master table * @param string foreign_table The foreign table name * @param string foreigh_field The relation field in the foreign table - * @access private * @see Relation_Stats::_getXy */ function __construct($master_table, $master_field, $foreign_table, $foreign_field) @@ -448,7 +585,7 @@ class Relation_Stats { $this->destDir = 1; } $this->ySrc = $src_pos[2]; - $this->y_dest = $dest_pos[2]; + $this->yDest = $dest_pos[2]; } /** @@ -499,12 +636,12 @@ class Relation_Stats { $this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, 'fill:'.$color.';stroke:black;stroke-width:2;' ); - $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick, $this->y_dest, - $this->xDest, $this->y_dest, + $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick, $this->yDest, + $this->xDest, $this->yDest, 'fill:'.$color.';stroke:black;stroke-width:2;' ); $svg->printElementLine('line',$this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, - $this->xDest + $this->destDir * $this->wTick, $this->y_dest, + $this->xDest + $this->destDir * $this->wTick, $this->yDest, 'fill:'.$color.';stroke:'.$color.';stroke-width:1;' ); $root2 = 2 * sqrt(2); @@ -518,13 +655,13 @@ class Relation_Stats { $this->ySrc - $this->wTick / $root2 , 'fill:'.$color.';stroke:black;stroke-width:2;' ); - $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick / 2 , $this->y_dest , + $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick / 2 , $this->yDest , $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick, - $this->y_dest + $this->wTick / $root2 , + $this->yDest + $this->wTick / $root2 , 'fill:'.$color.';stroke:black;stroke-width:2;'); $svg->printElementLine('line',$this->xDest + $this->destDir * $this->wTick / 2 , - $this->y_dest , $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick , - $this->y_dest - $this->wTick / $root2 , + $this->yDest , $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick , + $this->yDest - $this->wTick / $root2 , 'fill:'.$color.';stroke:black;stroke-width:2;' ); } @@ -533,7 +670,23 @@ class Relation_Stats { * end of the "Relation_Stats" class */ - +/** + * Svg Relation Schema Class + * + * Purpose of this class is to generate the SVG XML Document + * which is used for representing the database diagrams as image in Dia IDE + * This class uses Database Table and Reference Objects of Dia and with + * the combination of these objects actually helps in preparing Dia XML. + * + * Dia XML is generated by using XMLWriter php extension and this class + * inherits Export_Relation_Schema class has common functionality added + * to this class + * + * @name Dia_Relation_Schema + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + */ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema { @@ -549,28 +702,38 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema private $l_marg = 10; private $r_marg = 10; private $_tablewidth; - + + /** + * The "PMA_Svg_Relation_Schema" constructor + * + * Upon instantiation This starts writing the SVG XML document + * user will be prompted for download as .svg extension + * + * @return void + * @see PMA_SVG + */ function __construct() { - global $svg,$db,$pdf_page_number,$show_info,$show_table_dimension,$show_color,$change_color,$all_table_same_wide; + global $svg,$db; + + $this->setPageNumber($_POST['pdf_page_number']); + $this->setShowColor($_POST['show_color']); + $this->setShowKeys($_POST['show_keys']); + $this->setTableDimension($_POST['show_table_dimension']); + $this->setAllTableSameWidth($_POST['all_table_same_wide']); + $this->setExportType($_POST['export_type']); - $pdf_page_number = isset($pdf_page_number) ? $pdf_page_number : 1; - $show_info = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; - $all_table_same_wide = (isset($all_table_same_wide) && $all_table_same_wide == 'on') ? 1 : 0; - $change_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; - $show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; $svg = new PMA_SVG(); - $this->setSameWidthTables($all_table_same_wide); - $svg->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $pdf_page_number)); + $svg->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $this->pageNumber)); $svg->SetAuthor('phpMyAdmin ' . PMA_VERSION); $svg->setFont('Arial'); $svg->setFontSize('16px'); $svg->startSvgDoc('1000px','600px'); - $alltables = $this->getAllTables($db,$pdf_page_number); + $alltables = $this->getAllTables($db,$this->pageNumber); foreach ($alltables AS $table) { if (!isset($this->tables[$table])) { - $this->tables[$table] = new Table_Stats($table,$svg->getFont(),$svg->getFontSize(), $this->_tablewidth, $show_keys, $show_info); + $this->tables[$table] = new Table_Stats($table,$svg->getFont(),$svg->getFontSize(), $this->pageNumber, $this->_tablewidth, $this->showKeys, $this->tableDimension); } if ($this->sameWide) { @@ -590,25 +753,25 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema * to do a === FALSE and this is not PHP3 compatible) */ if (in_array($rel['foreign_table'], $alltables)) { - $this->_addRelation($one_table,$svg->getFont(),$svg->getFontSize(), $master_field, $rel['foreign_table'], $rel['foreign_field'], $show_info); + $this->_addRelation($one_table,$svg->getFont(),$svg->getFontSize(), $master_field, $rel['foreign_table'], $rel['foreign_field'], $this->tableDimension); } } } } - if ($seen_a_relation) { - $this->_drawRelations($change_color); + $this->_drawRelations($this->showColor); } - $this->_drawTables($change_color); + $this->_drawTables($this->showColor); $svg->endSvgDoc(); - $svg->showOutput(); - // print '<pre>'; - //print_r(get_object_vars($svg)); + $svg->showOutput($db.'-'.$this->pageNumber); + exit(); + print '<pre>'; + print_r(get_object_vars($svg)); //print_r($alltables); - //print_r($this); - //print '</pre>'; - //exit(); + print_r($this); + print '</pre>'; + } /** @@ -617,7 +780,7 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema * @param string table The table name * @access private */ - function _setMinMax($table) + private function _setMinMax($table) { $this->_xMax = max($this->_xMax, $table->x + $table->width); $this->_yMax = max($this->_yMax, $table->y + $table->height); @@ -636,19 +799,19 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema * @access private * @see _setMinMax,Table_Stats::__construct(),Relation_Stats::__construct() */ - function _addRelation($masterTable,$font,$fontSize, $masterField, $foreignTable, $foreignField, $showInfo) + private function _addRelation($masterTable,$font,$fontSize, $masterField, $foreignTable, $foreignField, $showInfo) { if (!isset($this->tables[$masterTable])) { - $this->tables[$masterTable] = new Table_Stats($masterTable,$font,$fontSize, $this->_tablewidth, false, $showInfo); + $this->tables[$masterTable] = new Table_Stats($masterTable, $font, $fontSize, $this->pageNumber, $this->_tablewidth, false, $showInfo); $this->_setMinMax($this->tables[$masterTable]); } if (!isset($this->tables[$foreignTable])) { - $this->tables[$foreignTable] = new Table_Stats($foreignTable,$font,$fontSize, $this->_tablewidth, false, $showInfo); + $this->tables[$foreignTable] = new Table_Stats($foreignTable,$font,$fontSize,$this->pageNumber, $this->_tablewidth, false, $showInfo); $this->_setMinMax($this->tables[$foreignTable]); } $this->_relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); } - + /** * Draws relation arrows and lines * connects master table's master field to @@ -664,17 +827,16 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema $relation->relationDraw($changeColor); } } - + /** * Draws tables * - * @param boolean showColor Whether to show color for primary fields or not + * @param boolean changeColor Whether to show color for primary fields or not * @access private * @see Table_Stats::Table_Stats_tableDraw() */ private function _drawTables($changeColor) { - global $svg; foreach ($this->tables as $table) { $table->tableDraw($changeColor); } From 2ca3d4d8f444d5dc9f4ea632015fbfadbb9481c4 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 14 Jul 2010 22:46:51 +0500 Subject: [PATCH 24/48] Export Relation Schema Class: Improved documentation + handling requests --- .../schema/Export_Relation_Schema.class.php | 115 ++++++++++++++---- 1 file changed, 91 insertions(+), 24 deletions(-) diff --git a/libraries/schema/Export_Relation_Schema.class.php b/libraries/schema/Export_Relation_Schema.class.php index 71f47ab0f..d88d33a50 100644 --- a/libraries/schema/Export_Relation_Schema.class.php +++ b/libraries/schema/Export_Relation_Schema.class.php @@ -19,8 +19,7 @@ class PMA_Export_Relation_Schema { - private $_pageTitle; // Title of the page - //protected $sameWide; + private $_pageTitle; public $showGrid; public $showColor; public $tableDimension; @@ -31,61 +30,139 @@ class PMA_Export_Relation_Schema public $paper; public $pageNumber; + /** + * Set Page Number + * + * @param integer value Page Number of the document to be created + * @return void + * @access public + */ public function setPageNumber($value) { $this->pageNumber = isset($value) ? $value : 1; } - + + /** + * Set Show Grid + * + * @param boolean value show grid of the document or not + * @return void + * @access public + */ public function setShowGrid($value) { $this->showGrid = (isset($value) && $value == 'on') ? 1 : 0; } - + public function setShowColor($value) { $this->showColor = (isset($value) && $value == 'on') ? 1 : 0; } - + + /** + * Set Table Dimension + * + * @param boolean value show table co-ordinates or not + * @return void + * @access public + */ public function setTableDimension($value) { $this->tableDimension = (isset($value) && $value == 'on') ? 1 : 0; } - + + /** + * Set same width of All Tables + * + * @param boolean value set same width of all tables or not + * @return void + * @access public + */ public function setAllTableSameWidth($value) { $this->sameWide = (isset($value) && $value == 'on') ? 1 : 0; } - + + /** + * Set Data Dictionary + * + * @param boolean value show selected database data dictionary or not + * @return void + * @access public + */ public function setWithDataDictionary($value) { $this->withDoc = (isset($value) && $value == 'on') ? 1 : 0; } - + + /** + * Set Show only keys + * + * @param boolean value show only keys or not + * @return void + * @access public + */ public function setShowKeys($value) { $this->showKeys = (isset($value) && $value == 'on') ? 1 : 0; } - + + /** + * Set Orientation + * + * @param string value Orientation will be portrait or landscape + * @return void + * @access public + */ public function setOrientation($value) { $this->orientation = (isset($value) && $value == 'P') ? 'P' : 'L'; } - + + /** + * Set type of paper + * + * @param string value paper type can be A4 etc + * @return void + * @access public + */ public function setPaper($value) { $this->paper = isset($value) ? $value : 'A4'; } - + + /** + * Set title of the page + * + * @param string value title of the page displayed at top of the document + * @return void + * @access public + */ public function setPageTitle($title) { $this->_pageTitle=$title; } - + + /** + * Set type of export relational schema + * + * @param string value can be pdf,svg,dia,visio,eps etc + * @return void + * @access public + */ public function setExportType($value) { $this->exportType=$value; } + /** + * get all tables involved or included in page + * + * @param string db name of the database + * @param integer pageNumber page number whose tables will be fetched in an array + * @return Array an array of tables + * @access public + */ public function getAllTables($db,$pageNumber) { global $cfgRelation; @@ -93,7 +170,7 @@ class PMA_Export_Relation_Schema $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . ' AND pdf_page_number = ' . $pageNumber; - // echo $tab_sql; + $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { $this->_die('',__('No tables')); @@ -109,23 +186,13 @@ class PMA_Export_Relation_Schema */ function dieSchema($type = '',$error_message = '') { - global $db; - - require_once './libraries/header.inc.php'; - - echo "<p><strong> {$type} " . __("SCHEMA ERROR") . "</strong></p>" . "\n"; + echo "<p><strong>" . __("SCHEMA ERROR: ") . $type ."</strong></p>" . "\n"; if (!empty($error_message)) { $error_message = htmlspecialchars($error_message); } echo '<p>' . "\n"; echo ' ' . $error_message . "\n"; echo '</p>' . "\n"; - - echo '<a href="export_relation_schema.php?' . PMA_generate_common_url($db) - . '">' . __('Back') . '</a>'; - echo "\n"; - - require_once './libraries/footer.inc.php'; } } ?> \ No newline at end of file From 4adc0c15cd1d03c51b88bbd792aea3bb1e57ed09 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Thu, 15 Jul 2010 00:59:30 +0500 Subject: [PATCH 25/48] User Schema + pdf Relation Schema Class: Improved documentation --- .../schema/Pdf_Relation_Schema.class.php | 6 +- libraries/schema/User_Schema.class.php | 89 +++++++++++++++---- 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/libraries/schema/Pdf_Relation_Schema.class.php b/libraries/schema/Pdf_Relation_Schema.class.php index 286c81dfc..9b88e382e 100644 --- a/libraries/schema/Pdf_Relation_Schema.class.php +++ b/libraries/schema/Pdf_Relation_Schema.class.php @@ -483,7 +483,8 @@ class PMA_PDF extends TCPDF * Draws tables schema * */ -class Table_Stats { +class Table_Stats +{ /** * Defines properties */ @@ -1084,7 +1085,7 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema if (empty($filename)) { $filename = $pageNumber . '.pdf'; } - $pdf->Output($db . '_' . $filename, 'I'); // destination: Inline + $pdf->Output($db . '_' . $filename, 'D'); // destination: download } public function dataDictionaryDoc($alltables) @@ -1206,6 +1207,7 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema } else { $have_rel = false; } // end if + /** * Displays the comments of the table if MySQL >= 3.23 */ diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php index 4aae7279e..1e4151f7b 100644 --- a/libraries/schema/User_Schema.class.php +++ b/libraries/schema/User_Schema.class.php @@ -60,7 +60,7 @@ class PMA_User_Schema case 'edcoord': $this->choosenPage = $_POST['chpage']; $this->c_table_rows = $_POST['c_table_rows']; - $this->editCoordinates($db, $cfgRelation); + $this->_editCoordinates($db, $cfgRelation); break; case 'deleteCrap': $this->_deleteTableRows($delrow,$cfgRelation,$db,$this->choosenPage); @@ -73,13 +73,14 @@ class PMA_User_Schema } // end if (isset($do)) } - + /** * shows/displays the HTML FORM to create the page * + * @param string db name of the selected database + * @return void * @access public */ - public function createPage($db) { ?> @@ -122,14 +123,14 @@ class PMA_User_Schema </form> <?php } - + /** * shows/displays the created page names in a drop down list * User can select any page number and edit it using dashboard etc * + * @return void * @access public */ - public function selectPage() { global $db,$table,$query_default_option,$cfgRelation; @@ -181,8 +182,9 @@ class PMA_User_Schema * A dashboard is displayed to AutoLayout the position of tables * users can drag n drop the tables and change their positions * - */ - + * @return void + * @access public + */ public function showTableDashBoard() { global $db,$cfgRelation,$table,$cfg,$with_field_names; @@ -317,7 +319,7 @@ class PMA_User_Schema echo "\n" . ' <input type="submit" value="' . __('Save') . '" />'; echo "\n" . '</form>' . "\n\n"; } // end if - + $this->_deleteTables($db, $this->choosenPage, $tabExist); } @@ -325,6 +327,9 @@ class PMA_User_Schema * show Export relational schema generation options * user can select export type of his own choice * and the attributes related to it + * + * @return void + * @access public */ public function displaySchemaGenerationOptions() @@ -413,7 +418,11 @@ class PMA_User_Schema /** * Check if there are tables that need to be deleted in dashboard, * if there are, ask the user for allowance - * @param string $chpage selected page + * + * @param string db name of database selected + * @param integer chpage selected page + * @param array tabExist + * @return void * @access private */ private function _deleteTables($db, $chpage, $tabExist) @@ -449,8 +458,10 @@ class PMA_User_Schema /** * Check if there are tables that need to be deleted in dashboard, * if there are, ask the user for allowance + * + * @return void + * @access private */ - private function _displayScratchboardTables($array_sh_page,$draginit,$reset_draginit) { global $with_field_names,$cfg,$db; @@ -514,10 +525,14 @@ class PMA_User_Schema /** * delete the table rows with table co-ordinates - * + * + * @param int delrow delete selected table from list of tables + * @param array cfgRelation relation settings + * @param string db database name + * @param integer chpage selected page for adding relations etc + * @return void * @access private */ - private function _deleteTableRows($delrow,$cfgRelation,$db,$chpage) { foreach ($delrow as $current_row) { @@ -534,9 +549,9 @@ class PMA_User_Schema * get all the export options and verify * call and include the appropriate Schema Class depending on $export_type * + * @return void * @access private */ - private function _processExportSchema() { /** @@ -555,6 +570,15 @@ class PMA_User_Schema $obj_schema = eval("new PMA_".ucfirst($export_type)."_Relation_Schema();"); } + /** + * delete X and Y coordinates + * + * @param string db The database name + * @param array cfgRelation relation settings + * @param integer choosePage selected page for adding relations etc + * @return void + * @access private + */ public function deleteCoordinates($db, $cfgRelation, $choosePage, $query_default_option) { $query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) @@ -563,6 +587,15 @@ class PMA_User_Schema PMA_query_as_controluser($query, FALSE, $query_default_option); } + /** + * delete pages + * + * @param string db The database name + * @param array cfgRelation relation settings + * @param integer choosePage selected page for adding relations etc + * @return void + * @access private + */ public function deletePages($db, $cfgRelation, $choosePage, $query_default_option) { $query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) @@ -571,6 +604,15 @@ class PMA_User_Schema PMA_query_as_controluser($query, FALSE, $query_default_option); } + /** + * process internal and foreign key relations + * + * @param string db The database name + * @param array cfgRelation relation settings + * @param integer pageNumber document number/Id + * @return void + * @access private + */ public function processRelations($db, $pageNumber, $cfgRelation, $query_default_option) { /* @@ -662,7 +704,16 @@ class PMA_User_Schema $this->choosenPage = $pageNumber; } - + /** + * Add X and Y coordinates for a table + * + * @param string db The database name + * @param array cfgRelation relation settings + * @param integer pageNumber document number/Id + * @param array all_tables A list of all tables involved + * @return void + * @access private + */ public function addRelationCoordinates($all_tables,$pageNumber,$db, $cfgRelation,$query_default_option) { /* @@ -711,7 +762,15 @@ class PMA_User_Schema } } - public function editCoordinates($db, $cfgRelation) + /** + * update X and Y coordinates for a table + * + * @param string db The database name + * @param array cfgRelation relation settings + * @return void + * @access private + */ + private function _editCoordinates($db, $cfgRelation) { for ($i = 0; $i < $this->c_table_rows; $i++) { $arrvalue = 'c_table_' . $i; From 5a884f2f4dd655d32ac338b2b210bd867e546847 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Thu, 15 Jul 2010 01:01:16 +0500 Subject: [PATCH 26/48] export_relation_schema.php: improved documentation --- export_relation_schema.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/export_relation_schema.php b/export_relation_schema.php index 667845801..b21ed8030 100644 --- a/export_relation_schema.php +++ b/export_relation_schema.php @@ -85,7 +85,7 @@ if ($cfgRelation['pdfwork']) { * create and select a page, generate schema etc */ - $user_schema->processUserPreferences($do); + $user_schema->processUserPreferences($_REQUEST['do']); /** * Show some possibility to select a page for the export of relation schema @@ -108,10 +108,10 @@ if ($cfgRelation['pdfwork']) { $user_schema->showTableDashBoard(); - if (isset($do) - && ($do == 'edcoord' - || ($do == 'selectpage' && isset($user_schema->choosenPage) && $user_schema->choosenPage != 0) - || ($do == 'createpage' && isset($user_schema->choosenPage) && $user_schema->choosenPage != 0))) { + if (isset($_REQUEST['do']) + && ($_REQUEST['do'] == 'edcoord' + || ($_REQUEST['do']== 'selectpage' && isset($user_schema->choosenPage) && $user_schema->choosenPage != 0) + || ($_REQUEST['do'] == 'createpage' && isset($user_schema->choosenPage) && $user_schema->choosenPage != 0))) { /** * show Export schema generation options @@ -130,7 +130,6 @@ if ($cfgRelation['pdfwork']) { } // end if } // end if ($cfgRelation['pdfwork']) - /** * Displays the footer */ From ac3909d990a2a4e7f65e1aecdd2a25dc6d99d5cf Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Tue, 20 Jul 2010 16:22:50 +0500 Subject: [PATCH 27/48] TCPDF Class [Tabs removed Commit]: Reverted back --- libraries/tcpdf/tcpdf.php | 13404 ++++++++++++++++++------------------ 1 file changed, 6703 insertions(+), 6701 deletions(-) diff --git a/libraries/tcpdf/tcpdf.php b/libraries/tcpdf/tcpdf.php index 0c1fbdd0f..8656ebc61 100644 --- a/libraries/tcpdf/tcpdf.php +++ b/libraries/tcpdf/tcpdf.php @@ -95,6770 +95,6772 @@ //require_once(dirname(__FILE__).'/config/tcpdf_config.php'); if(!class_exists('TCPDF', false)) { - /** - * define default PDF document producer - */ - define('PDF_PRODUCER','TCPDF 2.2.002 (http://tcpdf.sf.net)'); - - /** - * This is a PHP5 class for generating PDF files on-the-fly without requiring external extensions.<br> - * TCPDF project (http://tcpdf.sourceforge.net) has been originally derived from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org).<br> - * To add your own TTF fonts please read /fonts/README.TXT - * @name TCPDF - * @package com.tecnick.tcpdf - * @version 2.2.002 - * @author Nicola Asuni - * @link http://tcpdf.sourceforge.net - * @license http://www.gnu.org/copyleft/lesser.html LGPL - */ - class TCPDF { - - // Private or Protected properties + /** + * define default PDF document producer + */ + define('PDF_PRODUCER','TCPDF 2.2.002 (http://tcpdf.sf.net)'); + + /** + * This is a PHP5 class for generating PDF files on-the-fly without requiring external extensions.<br> + * TCPDF project (http://tcpdf.sourceforge.net) has been originally derived from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org).<br> + * To add your own TTF fonts please read /fonts/README.TXT + * @name TCPDF + * @package com.tecnick.tcpdf + * @version 2.2.002 + * @author Nicola Asuni + * @link http://tcpdf.sourceforge.net + * @license http://www.gnu.org/copyleft/lesser.html LGPL + */ + class TCPDF { + + // Private or Protected properties - /** - * @var current page number - * @access protected - */ - protected $page; - - /** - * @var current object number - * @access protected - */ - protected $n; + /** + * @var current page number + * @access protected + */ + protected $page; + + /** + * @var current object number + * @access protected + */ + protected $n; - /** - * @var array of object offsets - * @access protected - */ - protected $offsets; + /** + * @var array of object offsets + * @access protected + */ + protected $offsets; - /** - * @var buffer holding in-memory PDF - * @access protected - */ - protected $buffer; + /** + * @var buffer holding in-memory PDF + * @access protected + */ + protected $buffer; - /** - * @var array containing pages - * @access protected - */ - protected $pages; + /** + * @var array containing pages + * @access protected + */ + protected $pages; - /** - * @var current document state - * @access protected - */ - protected $state; + /** + * @var current document state + * @access protected + */ + protected $state; - /** - * @var compression flag - * @access protected - */ - protected $compress; + /** + * @var compression flag + * @access protected + */ + protected $compress; - /** - * @var default page orientation (P = Portrait, L = Landscape) - * @access protected - */ - protected $DefOrientation; + /** + * @var default page orientation (P = Portrait, L = Landscape) + * @access protected + */ + protected $DefOrientation; - /** - * @var current page orientation (P = Portrait, L = Landscape) - * @access protected - */ - protected $CurOrientation; + /** + * @var current page orientation (P = Portrait, L = Landscape) + * @access protected + */ + protected $CurOrientation; - /** - * @var array indicating page orientation changes - * @access protected - */ - protected $OrientationChanges; + /** + * @var array indicating page orientation changes + * @access protected + */ + protected $OrientationChanges; - /** - * @var scale factor (number of points in user unit) - * @access protected - */ - protected $k; + /** + * @var scale factor (number of points in user unit) + * @access protected + */ + protected $k; - /** - * @var width of page format in points - * @access protected - */ - protected $fwPt; + /** + * @var width of page format in points + * @access protected + */ + protected $fwPt; - /** - * @var height of page format in points - * @access protected - */ - protected $fhPt; + /** + * @var height of page format in points + * @access protected + */ + protected $fhPt; - /** - * @var width of page format in user unit - * @access protected - */ - protected $fw; + /** + * @var width of page format in user unit + * @access protected + */ + protected $fw; - /** - * @var height of page format in user unit - * @access protected - */ - protected $fh; + /** + * @var height of page format in user unit + * @access protected + */ + protected $fh; - /** - * @var current width of page in points - * @access protected - */ - protected $wPt; + /** + * @var current width of page in points + * @access protected + */ + protected $wPt; - /** - * @var current height of page in points - * @access protected - */ - protected $hPt; + /** + * @var current height of page in points + * @access protected + */ + protected $hPt; - /** - * @var current width of page in user unit - * @access protected - */ - protected $w; + /** + * @var current width of page in user unit + * @access protected + */ + protected $w; - /** - * @var current height of page in user unit - * @access protected - */ - protected $h; + /** + * @var current height of page in user unit + * @access protected + */ + protected $h; - /** - * @var left margin - * @access protected - */ - protected $lMargin; + /** + * @var left margin + * @access protected + */ + protected $lMargin; - /** - * @var top margin - * @access protected - */ - protected $tMargin; + /** + * @var top margin + * @access protected + */ + protected $tMargin; - /** - * @var right margin - * @access protected - */ - protected $rMargin; + /** + * @var right margin + * @access protected + */ + protected $rMargin; - /** - * @var page break margin - * @access protected - */ - protected $bMargin; + /** + * @var page break margin + * @access protected + */ + protected $bMargin; - /** - * @var cell internal padding - * @access protected - */ - protected $cMargin; + /** + * @var cell internal padding + * @access protected + */ + protected $cMargin; - /** - * @var current horizontal position in user unit for cell positioning - * @access protected - */ - protected $x; + /** + * @var current horizontal position in user unit for cell positioning + * @access protected + */ + protected $x; - /** - * @var current vertical position in user unit for cell positioning - * @access protected - */ - protected $y; + /** + * @var current vertical position in user unit for cell positioning + * @access protected + */ + protected $y; - /** - * @var height of last cell printed - * @access protected - */ - protected $lasth; + /** + * @var height of last cell printed + * @access protected + */ + protected $lasth; - /** - * @var line width in user unit - * @access protected - */ - protected $LineWidth; + /** + * @var line width in user unit + * @access protected + */ + protected $LineWidth; - /** - * @var array of standard font names - * @access protected - */ - protected $CoreFonts; + /** + * @var array of standard font names + * @access protected + */ + protected $CoreFonts; - /** - * @var array of used fonts - * @access protected - */ - protected $fonts; + /** + * @var array of used fonts + * @access protected + */ + protected $fonts; - /** - * @var array of font files - * @access protected - */ - protected $FontFiles; + /** + * @var array of font files + * @access protected + */ + protected $FontFiles; - /** - * @var array of encoding differences - * @access protected - */ - protected $diffs; + /** + * @var array of encoding differences + * @access protected + */ + protected $diffs; - /** - * @var array of used images - * @access protected - */ - protected $images; + /** + * @var array of used images + * @access protected + */ + protected $images; - /** - * @var array of links in pages - * @access protected - */ - protected $PageLinks; + /** + * @var array of links in pages + * @access protected + */ + protected $PageLinks; - /** - * @var array of internal links - * @access protected - */ - protected $links; + /** + * @var array of internal links + * @access protected + */ + protected $links; - /** - * @var current font family - * @access protected - */ - protected $FontFamily; + /** + * @var current font family + * @access protected + */ + protected $FontFamily; - /** - * @var current font style - * @access protected - */ - protected $FontStyle; + /** + * @var current font style + * @access protected + */ + protected $FontStyle; - /** - * @var underlining flag - * @access protected - */ - protected $underline; + /** + * @var underlining flag + * @access protected + */ + protected $underline; - /** - * @var current font info - * @access protected - */ - protected $CurrentFont; + /** + * @var current font info + * @access protected + */ + protected $CurrentFont; - /** - * @var current font size in points - * @access protected - */ - protected $FontSizePt; + /** + * @var current font size in points + * @access protected + */ + protected $FontSizePt; - /** - * @var current font size in user unit - * @access protected - */ - protected $FontSize; + /** + * @var current font size in user unit + * @access protected + */ + protected $FontSize; - /** - * @var commands for drawing color - * @access protected - */ - protected $DrawColor; + /** + * @var commands for drawing color + * @access protected + */ + protected $DrawColor; - /** - * @var commands for filling color - * @access protected - */ - protected $FillColor; + /** + * @var commands for filling color + * @access protected + */ + protected $FillColor; - /** - * @var commands for text color - * @access protected - */ - protected $TextColor; + /** + * @var commands for text color + * @access protected + */ + protected $TextColor; - /** - * @var indicates whether fill and text colors are different - * @access protected - */ - protected $ColorFlag; + /** + * @var indicates whether fill and text colors are different + * @access protected + */ + protected $ColorFlag; - /** - * @var word spacing - * @access protected - */ - protected $ws; + /** + * @var word spacing + * @access protected + */ + protected $ws; - /** - * @var automatic page breaking - * @access protected - */ - protected $AutoPageBreak; + /** + * @var automatic page breaking + * @access protected + */ + protected $AutoPageBreak; - /** - * @var threshold used to trigger page breaks - * @access protected - */ - protected $PageBreakTrigger; + /** + * @var threshold used to trigger page breaks + * @access protected + */ + protected $PageBreakTrigger; - /** - * @var flag set when processing footer - * @access protected - */ - protected $InFooter; + /** + * @var flag set when processing footer + * @access protected + */ + protected $InFooter; - /** - * @var zoom display mode - * @access protected - */ - protected $ZoomMode; + /** + * @var zoom display mode + * @access protected + */ + protected $ZoomMode; - /** - * @var layout display mode - * @access protected - */ - protected $LayoutMode; + /** + * @var layout display mode + * @access protected + */ + protected $LayoutMode; - /** - * @var title - * @access protected - */ - protected $title; + /** + * @var title + * @access protected + */ + protected $title; - /** - * @var subject - * @access protected - */ - protected $subject; + /** + * @var subject + * @access protected + */ + protected $subject; - /** - * @var author - * @access protected - */ - protected $author; + /** + * @var author + * @access protected + */ + protected $author; - /** - * @var keywords - * @access protected - */ - protected $keywords; + /** + * @var keywords + * @access protected + */ + protected $keywords; - /** - * @var creator - * @access protected - */ - protected $creator; + /** + * @var creator + * @access protected + */ + protected $creator; - /** - * @var alias for total number of pages - * @access protected - */ - protected $AliasNbPages; + /** + * @var alias for total number of pages + * @access protected + */ + protected $AliasNbPages; - /** - * @var right-bottom corner X coordinate of inserted image - * @since 2002-07-31 - * @author Nicola Asuni - * @access protected - */ - protected $img_rb_x; + /** + * @var right-bottom corner X coordinate of inserted image + * @since 2002-07-31 + * @author Nicola Asuni + * @access protected + */ + protected $img_rb_x; - /** - * @var right-bottom corner Y coordinate of inserted image - * @since 2002-07-31 - * @author Nicola Asuni - * @access protected - */ - protected $img_rb_y; + /** + * @var right-bottom corner Y coordinate of inserted image + * @since 2002-07-31 + * @author Nicola Asuni + * @access protected + */ + protected $img_rb_y; - /** - * @var image scale factor - * @since 2004-06-14 - * @author Nicola Asuni - * @access protected - */ - protected $imgscale = 1; + /** + * @var image scale factor + * @since 2004-06-14 + * @author Nicola Asuni + * @access protected + */ + protected $imgscale = 1; - /** - * @var boolean set to true when the input text is unicode (require unicode fonts) - * @since 2005-01-02 - * @author Nicola Asuni - * @access protected - */ - protected $isunicode = false; + /** + * @var boolean set to true when the input text is unicode (require unicode fonts) + * @since 2005-01-02 + * @author Nicola Asuni + * @access protected + */ + protected $isunicode = false; - /** - * @var PDF version - * @since 1.5.3 - * @access protected - */ - protected $PDFVersion = "1.5"; - - - // ---------------------- - - /** - * @var Minimum distance between header and top page margin. - * @access private - */ - private $header_margin; - - /** - * @var Minimum distance between footer and bottom page margin. - * @access private - */ - private $footer_margin; - - /** - * @var original left margin value - * @access private - * @since 1.53.0.TC013 - */ - private $original_lMargin; - - /** - * @var original right margin value - * @access private - * @since 1.53.0.TC013 - */ - private $original_rMargin; - - /** - * @var Header font. - * @access private - */ - private $header_font; - - /** - * @var Footer font. - * @access private - */ - private $footer_font; - - /** - * @var Language templates. - * @access private - */ - private $l; - - /** - * @var Barcode to print on page footer (only if set). - * @access private - */ - private $barcode = false; - - /** - * @var If true prints header - * @access private - */ - private $print_header = true; - - /** - * @var If true prints footer. - * @access private - */ - private $print_footer = true; - - /** - * @var Header width (0 = full page width). - * @access private - */ - private $header_width = 0; - - /** - * @var Header image logo. - * @access private - */ - private $header_logo = ""; - - /** - * @var Header image logo width in mm. - * @access private - */ - private $header_logo_width = 30; - - /** - * @var String to print as title on document header. - * @access private - */ - private $header_title = ""; - - /** - * @var String to print on document header. - * @access private - */ - private $header_string = ""; - - /** - * @var Default number of columns for html table. - * @access private - */ - private $default_table_columns = 4; - - - // variables for html parser - - /** - * @var HTML PARSER: store current link. - * @access private - */ - private $HREF; - - /** - * @var HTML PARSER: store font list. - * @access private - */ - private $fontList; - - /** - * @var HTML PARSER: true when font attribute is set. - * @access private - */ - private $issetfont; - - /** - * @var HTML PARSER: true when color attribute is set. - * @access private - */ - private $issetcolor; - - /** - * @var HTML PARSER: true in case of ordered list (OL), false otherwise. - * @access private - */ - private $listordered = false; - - /** - * @var HTML PARSER: count list items. - * @access private - */ - private $listcount = 0; - - /** - * @var HTML PARSER: size of table border. - * @access private - */ - private $tableborder = 0; - - /** - * @var HTML PARSER: true at the beginning of table. - * @access private - */ - private $tdbegin = false; - - /** - * @var HTML PARSER: table width. - * @access private - */ - private $tdwidth = 0; - - /** - * @var HTML PARSER: table height. - * @access private - */ - private $tdheight = 0; - - /** - * @var HTML PARSER: table align. - * @access private - */ - private $tdalign = "L"; - - /** - * @var HTML PARSER: table background color. - * @access private - */ - private $tdbgcolor = false; - - /** - * @var Store temporary font size in points. - * @access private - */ - private $tempfontsize = 10; - - /** - * @var Bold font style status. - * @access private - */ - private $b; - - /** - * @var Underlined font style status. - * @access private - */ - private $u; - - /** - * @var Italic font style status. - * @access private - */ - private $i; - - /** - * @var spacer for LI tags. - * @access private - */ - private $lispacer = ""; - - /** - * @var default encoding - * @access private - * @since 1.53.0.TC010 - */ - private $encoding = "UTF-8"; - - /** - * @var PHP internal encoding - * @access private - * @since 1.53.0.TC016 - */ - private $internal_encoding; - - /** - * @var store previous fill color as RGB array - * @access private - * @since 1.53.0.TC017 - */ - private $prevFillColor = array(255,255,255); - - /** - * @var store previous text color as RGB array - * @access private - * @since 1.53.0.TC017 - */ - private $prevTextColor = array(0,0,0); - - /** - * @var store previous font family - * @access private - * @since 1.53.0.TC017 - */ - private $prevFontFamily; - - /** - * @var store previous font style - * @access private - * @since 1.53.0.TC017 - */ - private $prevFontStyle; - - /** - * @var indicates if the document language is Right-To-Left - * @access private - * @since 2.0.000 - */ - private $rtl = false; - - /** - * @var used to force RTL or LTR string inversion - * @access private - * @since 2.0.000 - */ - private $tmprtl = false; - - // --- Variables used for document encryption: - - /** - * Indicates whether document is protected - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $encrypted; - - /** - * U entry in pdf document - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $Uvalue; - - /** - * O entry in pdf document - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $Ovalue; - - /** - * P entry in pdf document - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $Pvalue; - - /** - * encryption object id - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $enc_obj_id; - - /** - * last RC4 key encrypted (cached for optimisation) - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $last_rc4_key; - - /** - * last RC4 computed key - * @access private - * @since 2.0.000 (2008-01-02) - */ - private $last_rc4_key_c; - - // --- bookmark --- - - /** - * Outlines for bookmark - * @access private - * @since 2.1.002 (2008-02-12) - */ - private $outlines = array(); - - /** - * Outline root for bookmark - * @access private - * @since 2.1.002 (2008-02-12) - */ - private $OutlineRoot; - - - // --- javascript and form --- - - /** - * javascript code - * @access private - * @since 2.1.002 (2008-02-12) - */ - private $javascript = ""; - - /** - * javascript counter - * @access private - * @since 2.1.002 (2008-02-12) - */ + /** + * @var PDF version + * @since 1.5.3 + * @access protected + */ + protected $PDFVersion = "1.5"; + + + // ---------------------- + + /** + * @var Minimum distance between header and top page margin. + * @access private + */ + private $header_margin; + + /** + * @var Minimum distance between footer and bottom page margin. + * @access private + */ + private $footer_margin; + + /** + * @var original left margin value + * @access private + * @since 1.53.0.TC013 + */ + private $original_lMargin; + + /** + * @var original right margin value + * @access private + * @since 1.53.0.TC013 + */ + private $original_rMargin; + + /** + * @var Header font. + * @access private + */ + private $header_font; + + /** + * @var Footer font. + * @access private + */ + private $footer_font; + + /** + * @var Language templates. + * @access private + */ + private $l; + + /** + * @var Barcode to print on page footer (only if set). + * @access private + */ + private $barcode = false; + + /** + * @var If true prints header + * @access private + */ + private $print_header = true; + + /** + * @var If true prints footer. + * @access private + */ + private $print_footer = true; + + /** + * @var Header width (0 = full page width). + * @access private + */ + private $header_width = 0; + + /** + * @var Header image logo. + * @access private + */ + private $header_logo = ""; + + /** + * @var Header image logo width in mm. + * @access private + */ + private $header_logo_width = 30; + + /** + * @var String to print as title on document header. + * @access private + */ + private $header_title = ""; + + /** + * @var String to print on document header. + * @access private + */ + private $header_string = ""; + + /** + * @var Default number of columns for html table. + * @access private + */ + private $default_table_columns = 4; + + + // variables for html parser + + /** + * @var HTML PARSER: store current link. + * @access private + */ + private $HREF; + + /** + * @var HTML PARSER: store font list. + * @access private + */ + private $fontList; + + /** + * @var HTML PARSER: true when font attribute is set. + * @access private + */ + private $issetfont; + + /** + * @var HTML PARSER: true when color attribute is set. + * @access private + */ + private $issetcolor; + + /** + * @var HTML PARSER: true in case of ordered list (OL), false otherwise. + * @access private + */ + private $listordered = false; + + /** + * @var HTML PARSER: count list items. + * @access private + */ + private $listcount = 0; + + /** + * @var HTML PARSER: size of table border. + * @access private + */ + private $tableborder = 0; + + /** + * @var HTML PARSER: true at the beginning of table. + * @access private + */ + private $tdbegin = false; + + /** + * @var HTML PARSER: table width. + * @access private + */ + private $tdwidth = 0; + + /** + * @var HTML PARSER: table height. + * @access private + */ + private $tdheight = 0; + + /** + * @var HTML PARSER: table align. + * @access private + */ + private $tdalign = "L"; + + /** + * @var HTML PARSER: table background color. + * @access private + */ + private $tdbgcolor = false; + + /** + * @var Store temporary font size in points. + * @access private + */ + private $tempfontsize = 10; + + /** + * @var Bold font style status. + * @access private + */ + private $b; + + /** + * @var Underlined font style status. + * @access private + */ + private $u; + + /** + * @var Italic font style status. + * @access private + */ + private $i; + + /** + * @var spacer for LI tags. + * @access private + */ + private $lispacer = ""; + + /** + * @var default encoding + * @access private + * @since 1.53.0.TC010 + */ + private $encoding = "UTF-8"; + + /** + * @var PHP internal encoding + * @access private + * @since 1.53.0.TC016 + */ + private $internal_encoding; + + /** + * @var store previous fill color as RGB array + * @access private + * @since 1.53.0.TC017 + */ + private $prevFillColor = array(255,255,255); + + /** + * @var store previous text color as RGB array + * @access private + * @since 1.53.0.TC017 + */ + private $prevTextColor = array(0,0,0); + + /** + * @var store previous font family + * @access private + * @since 1.53.0.TC017 + */ + private $prevFontFamily; + + /** + * @var store previous font style + * @access private + * @since 1.53.0.TC017 + */ + private $prevFontStyle; + + /** + * @var indicates if the document language is Right-To-Left + * @access private + * @since 2.0.000 + */ + private $rtl = false; + + /** + * @var used to force RTL or LTR string inversion + * @access private + * @since 2.0.000 + */ + private $tmprtl = false; + + // --- Variables used for document encryption: + + /** + * Indicates whether document is protected + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $encrypted; + + /** + * U entry in pdf document + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $Uvalue; + + /** + * O entry in pdf document + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $Ovalue; + + /** + * P entry in pdf document + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $Pvalue; + + /** + * encryption object id + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $enc_obj_id; + + /** + * last RC4 key encrypted (cached for optimisation) + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $last_rc4_key; + + /** + * last RC4 computed key + * @access private + * @since 2.0.000 (2008-01-02) + */ + private $last_rc4_key_c; + + // --- bookmark --- + + /** + * Outlines for bookmark + * @access private + * @since 2.1.002 (2008-02-12) + */ + private $outlines = array(); + + /** + * Outline root for bookmark + * @access private + * @since 2.1.002 (2008-02-12) + */ + private $OutlineRoot; + + + // --- javascript and form --- + + /** + * javascript code + * @access private + * @since 2.1.002 (2008-02-12) + */ + private $javascript = ""; + + /** + * javascript counter + * @access private + * @since 2.1.002 (2008-02-12) + */ private $n_js; - //------------------------------------------------------------ - // Public methods - //------------------------------------------------------------ - - /** - * This is the class constructor. - * It allows to set up the page format, the orientation and - * the measure unit used in all the methods (except for the font sizes). - * @since 1.0 - * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul> - * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. - * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul> - * @param boolean $unicode TRUE means that the input text is unicode (default = true) - * @param String $encoding charset encoding; default is UTF-8 - */ - public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding="UTF-8") { - - /* Set internal character encoding to ASCII */ - if (function_exists("mb_internal_encoding") AND mb_internal_encoding()) { - $this->internal_encoding = mb_internal_encoding(); - mb_internal_encoding("ASCII"); - } - - // set language direction - $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; - $this->tmprtl = false; - - //Some checks - $this->_dochecks(); - - //Initialization of properties - $this->isunicode=$unicode; - $this->page=0; - $this->n=2; - $this->buffer=''; - $this->pages=array(); - $this->OrientationChanges=array(); - $this->state=0; - $this->fonts=array(); - $this->FontFiles=array(); - $this->diffs=array(); - $this->images=array(); - $this->links=array(); - $this->InFooter=false; - $this->lasth=0; - $this->FontFamily=''; - $this->FontStyle=''; - $this->FontSizePt=12; - $this->underline=false; - $this->DrawColor='0 G'; - $this->FillColor='0 g'; - $this->TextColor='0 g'; - $this->ColorFlag=false; - $this->ws=0; - // encryption values - $this->encrypted=false; - $this->last_rc4_key=''; - $this->padding="\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A"; - - //Standard Unicode fonts - $this->CoreFonts=array( - 'courier'=>'Courier', - 'courierB'=>'Courier-Bold', - 'courierI'=>'Courier-Oblique', - 'courierBI'=>'Courier-BoldOblique', - 'helvetica'=>'Helvetica', - 'helveticaB'=>'Helvetica-Bold', - 'helveticaI'=>'Helvetica-Oblique', - 'helveticaBI'=>'Helvetica-BoldOblique', - 'times'=>'Times-Roman', - 'timesB'=>'Times-Bold', - 'timesI'=>'Times-Italic', - 'timesBI'=>'Times-BoldItalic', - 'symbol'=>'Symbol', - 'zapfdingbats'=>'ZapfDingbats' - ); - - //Scale factor - switch (strtolower($unit)){ - case 'pt': {$this->k=1; break;} - case 'mm': {$this->k=72/25.4; break;} - case 'cm': {$this->k=72/2.54; break;} - case 'in': {$this->k=72; break;} - default : {$this->Error('Incorrect unit: '.$unit); break;} - } - - //Page format - if(is_string($format)) { - // Page formats (45 standard ISO paper formats and 4 american common formats). - // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm) - switch (strtoupper($format)){ - case '4A0': {$format = array(4767.87,6740.79); break;} - case '2A0': {$format = array(3370.39,4767.87); break;} - case 'A0': {$format = array(2383.94,3370.39); break;} - case 'A1': {$format = array(1683.78,2383.94); break;} - case 'A2': {$format = array(1190.55,1683.78); break;} - case 'A3': {$format = array(841.89,1190.55); break;} - case 'A4': default: {$format = array(595.28,841.89); break;} - case 'A5': {$format = array(419.53,595.28); break;} - case 'A6': {$format = array(297.64,419.53); break;} - case 'A7': {$format = array(209.76,297.64); break;} - case 'A8': {$format = array(147.40,209.76); break;} - case 'A9': {$format = array(104.88,147.40); break;} - case 'A10': {$format = array(73.70,104.88); break;} - case 'B0': {$format = array(2834.65,4008.19); break;} - case 'B1': {$format = array(2004.09,2834.65); break;} - case 'B2': {$format = array(1417.32,2004.09); break;} - case 'B3': {$format = array(1000.63,1417.32); break;} - case 'B4': {$format = array(708.66,1000.63); break;} - case 'B5': {$format = array(498.90,708.66); break;} - case 'B6': {$format = array(354.33,498.90); break;} - case 'B7': {$format = array(249.45,354.33); break;} - case 'B8': {$format = array(175.75,249.45); break;} - case 'B9': {$format = array(124.72,175.75); break;} - case 'B10': {$format = array(87.87,124.72); break;} - case 'C0': {$format = array(2599.37,3676.54); break;} - case 'C1': {$format = array(1836.85,2599.37); break;} - case 'C2': {$format = array(1298.27,1836.85); break;} - case 'C3': {$format = array(918.43,1298.27); break;} - case 'C4': {$format = array(649.13,918.43); break;} - case 'C5': {$format = array(459.21,649.13); break;} - case 'C6': {$format = array(323.15,459.21); break;} - case 'C7': {$format = array(229.61,323.15); break;} - case 'C8': {$format = array(161.57,229.61); break;} - case 'C9': {$format = array(113.39,161.57); break;} - case 'C10': {$format = array(79.37,113.39); break;} - case 'RA0': {$format = array(2437.80,3458.27); break;} - case 'RA1': {$format = array(1729.13,2437.80); break;} - case 'RA2': {$format = array(1218.90,1729.13); break;} - case 'RA3': {$format = array(864.57,1218.90); break;} - case 'RA4': {$format = array(609.45,864.57); break;} - case 'SRA0': {$format = array(2551.18,3628.35); break;} - case 'SRA1': {$format = array(1814.17,2551.18); break;} - case 'SRA2': {$format = array(1275.59,1814.17); break;} - case 'SRA3': {$format = array(907.09,1275.59); break;} - case 'SRA4': {$format = array(637.80,907.09); break;} - case 'LETTER': {$format = array(612.00,792.00); break;} - case 'LEGAL': {$format = array(612.00,1008.00); break;} - case 'EXECUTIVE': {$format = array(521.86,756.00); break;} - case 'FOLIO': {$format = array(612.00,936.00); break;} - // default: {$this->Error('Unknown page format: '.$format); break;} - } - $this->fwPt=$format[0]; - $this->fhPt=$format[1]; - } - else { - $this->fwPt=$format[0]*$this->k; - $this->fhPt=$format[1]*$this->k; - } - - $this->fw=$this->fwPt/$this->k; - $this->fh=$this->fhPt/$this->k; - - //Page orientation - $orientation=strtolower($orientation); - if($orientation=='p' or $orientation=='portrait') { - $this->DefOrientation='P'; - $this->wPt=$this->fwPt; - $this->hPt=$this->fhPt; - } - elseif($orientation=='l' or $orientation=='landscape') { - $this->DefOrientation='L'; - $this->wPt=$this->fhPt; - $this->hPt=$this->fwPt; - } - else { - $this->Error('Incorrect orientation: '.$orientation); - } - - $this->CurOrientation=$this->DefOrientation; - $this->w=$this->wPt/$this->k; - $this->h=$this->hPt/$this->k; - //Page margins (1 cm) - $margin=28.35/$this->k; - $this->SetMargins($margin,$margin); - //Interior cell margin (1 mm) - $this->cMargin=$margin/10; - //Line width (0.2 mm) - $this->LineWidth=.567/$this->k; - //Automatic page break - $this->SetAutoPageBreak(true,2*$margin); - //Full width display mode - $this->SetDisplayMode('fullwidth'); - //Compression - $this->SetCompression(true); - //Set default PDF version number - $this->PDFVersion = "1.5"; - - $this->encoding = $encoding; - $this->b = 0; - $this->i = 0; - $this->u = 0; - $this->HREF = ''; - $this->fontlist = array("arial", "times", "courier", "helvetica", "symbol"); - $this->issetfont = false; - $this->issetcolor = false; - $this->tableborder = 0; - $this->tdbegin = false; - $this->tdwidth= 0; - $this->tdheight = 0; - if($this->rtl) { - $this->tdalign = "R"; - } else { - $this->tdalign = "L"; - } - $this->tdbgcolor = false; - - $this->SetFillColor(200, 200, 200, true); - $this->SetTextColor(0, 0, 0, true); - } - - /** - * Default destructor. - * @since 1.53.0.TC016 - */ - public function __destruct() { - // restore internal encoding - if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) { - mb_internal_encoding($this->internal_encoding); - } - } - - /** - * Enable or disable Right-To-Left language mode - * @param Boolean $enable if true enable Right-To-Left language mode. - * @since 2.0.000 (2008-01-03) - */ - public function setRTL($enable) { - $this->rtl = $enable ? true : false; - $this->tmprtl = false; - } - - /** - * Force temporary RTL language direction - * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL - * @since 2.1.000 (2008-01-09) - */ - public function setTempRTL($mode) { - switch ($mode) { - case false: - case 'L': - case 'R': { - $this->tmprtl = $mode; - } - } - } - - /** - * Set the last cell height. - * @param float $h cell height. - * @author Nicola Asuni - * @since 1.53.0.TC034 - */ - public function setLastH($h) { - $this->lasth=$h; - } - - /** - * Set the image scale. - * @param float $scale image scale. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function setImageScale($scale) { - $this->imgscale=$scale; - } - - /** - * Returns the image scale. - * @return float image scale. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function getImageScale() { - return $this->imgscale; - } - - /** - * Returns the page width in units. - * @return int page width. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function getPageWidth() { - return $this->w; - } - - /** - * Returns the page height in units. - * @return int page height. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function getPageHeight() { - return $this->h; - } - - /** - * Returns the page break margin. - * @return int page break margin. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function getBreakMargin() { - return $this->bMargin; - } - - /** - * Returns the scale factor (number of points in user unit). - * @return int scale factor. - * @author Nicola Asuni - * @since 1.5.2 - */ - public function getScaleFactor() { - return $this->k; - } - - /** - * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them. - * @param float $left Left margin. - * @param float $top Top margin. - * @param float $right Right margin. Default value is the left one. - * @since 1.0 - * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() - */ - public function SetMargins($left, $top, $right=-1) { - //Set left, top and right margins - $this->lMargin=$left; - $this->tMargin=$top; - if($right==-1) { - $right=$left; - } - $this->rMargin=$right; - } - - /** - * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin. - * @param float $margin The margin. - * @since 1.4 - * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() - */ - public function SetLeftMargin($margin) { - //Set left margin - $this->lMargin=$margin; - if(($this->page > 0) AND ($this->x < $margin)) { - $this->x = $margin; - } - } - - /** - * Defines the top margin. The method can be called before creating the first page. - * @param float $margin The margin. - * @since 1.5 - * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() - */ - public function SetTopMargin($margin) { - //Set top margin - $this->tMargin=$margin; - if(($this->page > 0) AND ($this->y < $margin)) { - $this->y = $margin; - } - } - - /** - * Defines the right margin. The method can be called before creating the first page. - * @param float $margin The margin. - * @since 1.5 - * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() - */ - public function SetRightMargin($margin) { - $this->rMargin=$margin; - if(($this->page > 0) AND ($this->x > ($this->w - $margin))) { - $this->x = $this->w - $margin; - } - } - - /** - * Set the internal Cell padding. - * @param float $pad internal padding. - * @since 2.1.000 (2008-01-09) - * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() - */ - public function SetCellPadding($pad) { - $this->cMargin=$pad; - } - - /** - * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm. - * @param boolean $auto Boolean indicating if mode should be on or off. - * @param float $margin Distance from the bottom of the page. - * @since 1.0 - * @see Cell(), MultiCell(), AcceptPageBreak() - */ - public function SetAutoPageBreak($auto, $margin=0) { - //Set auto page break mode and triggering margin - $this->AutoPageBreak = $auto; - $this->bMargin = $margin; - $this->PageBreakTrigger = $this->h - $margin; - } - - /** - * Defines the way the document is to be displayed by the viewer. The zoom level can be set: pages can be displayed entirely on screen, occupy the full width of the window, use real size, be scaled by a specific zooming factor or use viewer default (configured in the Preferences menu of Acrobat). The page layout can be specified too: single at once, continuous display, two columns or viewer default. By default, documents use the full width mode with continuous display. - * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul> - * @param string $layout The page layout. Possible values are:<ul><li>single: displays one page at once</li><li>continuous: displays pages continuously (default)</li><li>two: displays two pages on two columns</li><li>default: uses viewer default mode</li></ul> - * @since 1.2 - */ - public function SetDisplayMode($zoom, $layout='continuous') { - //Set display mode in viewer - if($zoom=='fullpage' or $zoom=='fullwidth' or $zoom=='real' or $zoom=='default' or !is_string($zoom)) { - $this->ZoomMode=$zoom; - } - else { - $this->Error('Incorrect zoom display mode: '.$zoom); - } - if($layout=='single' or $layout=='continuous' or $layout=='two' or $layout=='default') { - $this->LayoutMode=$layout; - } - else { - $this->Error('Incorrect layout display mode: '.$layout); - } - } - - /** - * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default. - * Note: the Zlib extension is required for this feature. If not present, compression will be turned off. - * @param boolean $compress Boolean indicating if compression must be enabled. - * @since 1.4 - */ - public function SetCompression($compress) { - //Set page compression - if(function_exists('gzcompress')) { - $this->compress=$compress; - } - else { - $this->compress=false; - } - } - - /** - * Defines the title of the document. - * @param string $title The title. - * @since 1.2 - * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() - */ - public function SetTitle($title) { - //Title of document - $this->title=$title; - } - - /** - * Defines the subject of the document. - * @param string $subject The subject. - * @since 1.2 - * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() - */ - public function SetSubject($subject) { - //Subject of document - $this->subject=$subject; - } - - /** - * Defines the author of the document. - * @param string $author The name of the author. - * @since 1.2 - * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() - */ - public function SetAuthor($author) { - //Author of document - $this->author=$author; - } - - /** - * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. - * @param string $keywords The list of keywords. - * @since 1.2 - * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() - */ - public function SetKeywords($keywords) { - //Keywords of document - $this->keywords=$keywords; - } - - /** - * Defines the creator of the document. This is typically the name of the application that generates the PDF. - * @param string $creator The name of the creator. - * @since 1.2 - * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() - */ - public function SetCreator($creator) { - //Creator of document - $this->creator=$creator; - } - - /** - * Defines an alias for the total number of pages. It will be substituted as the document is closed.<br /> - * <b>Example:</b><br /> - * <pre> - * class PDF extends TCPDF { - * public function Footer() { - * //Go to 1.5 cm from bottom - * $this->SetY(-15); - * //Select Arial italic 8 - * $this->SetFont('Arial','I',8); - * //Print current and total page numbers - * $this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C'); - * } - * } - * $pdf=new PDF(); - * $pdf->AliasNbPages(); - * </pre> - * @param string $alias The alias. Default value: {nb}. - * @since 1.4 - * @see PageNo(), Footer() - */ - public function AliasNbPages($alias='{nb}') { - //Define an alias for total number of pages - $this->AliasNbPages = $this->_escapetext($alias); - } - - /** - * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid. - * 2004-06-11 :: Nicola Asuni : changed bold tag with strong - * @param string $msg The error message - * @since 1.0 - */ - public function Error($msg) { - //Fatal error - die('<strong>TCPDF error: </strong>'.$msg); - } - - /** - * This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically. - * Note: no page is created by this method - * @since 1.0 - * @see AddPage(), Close() - */ - public function Open() { - //Begin document - $this->state=1; - } - - /** - * Terminates the PDF document. It is not necessary to call this method explicitly because Output() does it automatically. If the document contains no page, AddPage() is called to prevent from getting an invalid document. - * @since 1.0 - * @see Open(), Output() - */ - public function Close() { - //Terminate document - if($this->state==3) { - return; - } - if($this->page==0) { - $this->AddPage(); - } - //Page footer - $this->InFooter=true; - $this->Footer(); - $this->InFooter=false; - //Close page - $this->_endpage(); - //Close document - $this->_enddoc(); - } - - /** - * Reset pointer to the last document page. - * @since 2.0.000 (2008-01-04) - * @see setPage(), getPage(), getNumPages() - */ - public function lastPage() { - $this->page = count($this->pages); - } - - /** - * Move pointer to the apecified document page. - * @param int $pnum page number - * @since 2.1.000 (2008-01-07) - * @see getPage(), lastpage(), getNumPages() - */ - public function setPage($pnum) { - if(($pnum > 0) AND ($pnum <= count($this->pages))) { - $this->page = $pnum; - } - } - - /** - * Get current document page number. - * @return int page number - * @since 2.1.000 (2008-01-07) - * @see setPage(), lastpage(), getNumPages() - */ - public function getPage() { - return $this->page; - } - - - /** - * Get the total number of insered pages. - * @return int number of pages - * @since 2.1.000 (2008-01-07) - * @see setPage(), getPage(), lastpage() - */ - public function getNumPages() { - return count($this->pages); - } - - /** - * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer. Then the page is added, the current position set to the top-left corner according to the left and top margins, and Header() is called to display the header. - * The font which was set before calling is automatically restored. There is no need to call SetFont() again if you want to continue with the same font. The same is true for colors and line width. - * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. - * @param string $orientation Page orientation. Possible values are (case insensitive):<ul><li>P or Portrait</li><li>L or Landscape</li></ul> The default value is the one passed to the constructor. - * @since 1.0 - * @see TCPDF(), Header(), Footer(), SetMargins() - */ - public function AddPage($orientation='') { - if (count($this->pages) > ($this->page + 1)) { - // this page has been already added - $this->page++; - return; - } - //Start a new page - if($this->state==0) { - $this->Open(); - } - $family=$this->FontFamily; - $style=$this->FontStyle.($this->underline ? 'U' : ''); - $size=$this->FontSizePt; - $lw=$this->LineWidth; - $dc=$this->DrawColor; - $fc=$this->FillColor; - $tc=$this->TextColor; - $cf=$this->ColorFlag; - if($this->page>0) { - //Page footer - $this->InFooter=true; - $this->Footer(); - $this->InFooter=false; - //Close page - $this->_endpage(); - } - //Start new page - $this->_beginpage($orientation); - //Set line cap style to square - $this->_out('2 J'); - //Set line width - $this->LineWidth=$lw; - $this->_out(sprintf('%.2f w',$lw*$this->k)); - //Set font - if($family) { - $this->SetFont($family,$style,$size); - } - //Set colors - $this->DrawColor=$dc; - if($dc!='0 G') { - $this->_out($dc); - } - $this->FillColor=$fc; - if($fc!='0 g') { - $this->_out($fc); - } - $this->TextColor=$tc; - $this->ColorFlag=$cf; - //Page header - $this->Header(); - //Restore line width - if($this->LineWidth!=$lw) { - $this->LineWidth=$lw; - $this->_out(sprintf('%.2f w',$lw*$this->k)); - } - //Restore font - if($family) { - $this->SetFont($family,$style,$size); - } - //Restore colors - if($this->DrawColor!=$dc) { - $this->DrawColor=$dc; - $this->_out($dc); - } - if($this->FillColor!=$fc) { - $this->FillColor=$fc; - $this->_out($fc); - } - $this->TextColor=$tc; - $this->ColorFlag=$cf; - } - - /** - * Set header data. - * @param string $ln header image logo - * @param string $lw header image logo width in mm - * @param string $ht string to print as title on document header - * @param string $hs string to print on document header - */ - public function setHeaderData($ln="", $lw=0, $ht="", $hs="") { - $this->header_logo = $ln; - $this->header_logo_width = $lw; - $this->header_title = $ht; - $this->header_string = $hs; - } - - /** - * Set header margin. - * (minimum distance between header and top page margin) - * @param int $hm distance in millimeters - */ - public function setHeaderMargin($hm=10) { - $this->header_margin = $hm; - } - - /** - * Set footer margin. - * (minimum distance between footer and bottom page margin) - * @param int $fm distance in millimeters - */ - public function setFooterMargin($fm=10) { - $this->footer_margin = $fm; - } - - /** - * Set a flag to print page header. - * @param boolean $val set to true to print the page header (default), false otherwise. - */ - public function setPrintHeader($val=true) { - $this->print_header = $val; - } - - /** - * Set a flag to print page footer. - * @param boolean $value set to true to print the page footer (default), false otherwise. - */ - public function setPrintFooter($val=true) { - $this->print_footer = $val; - } - - /** - * This method is used to render the page header. - * It is automatically called by AddPage() and could be overwritten in your own inherited class. - */ - public function Header() { - if ($this->print_header) { - - if (!isset($this->original_lMargin)) { - $this->original_lMargin = $this->lMargin; - } - if (!isset($this->original_rMargin)) { - $this->original_rMargin = $this->rMargin; - } - - //set current position - if ($this->rtl) { - $this->SetXY($this->original_rMargin, $this->header_margin); - } else { - $this->SetXY($this->original_lMargin, $this->header_margin); - } - - if (($this->header_logo) AND ($this->header_logo != K_BLANK_IMAGE)) { - $this->Image(K_PATH_IMAGES.$this->header_logo, $this->GetX(), $this->header_margin, $this->header_logo_width); - } else { - $this->img_rb_x = $this->GetX(); - $this->img_rb_y = $this->GetY(); - } - - $cell_height = round((K_CELL_HEIGHT_RATIO * $this->header_font[2]) / $this->k, 2); - // set starting margin for text data cell - if ($this->rtl) { - $header_x = $this->original_rMargin + ($this->header_logo_width * 1.1); - } else { - $header_x = $this->original_lMargin + ($this->header_logo_width * 1.1); - } - - // header title - $this->SetFont($this->header_font[0], 'B', $this->header_font[2] + 1); - $this->SetX($header_x); - $this->Cell($this->header_width, $cell_height, $this->header_title, 0, 1, ''); - - // header string - $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); - $this->SetX($header_x); - $this->MultiCell($this->header_width, $cell_height, $this->header_string, 0, '', 0); - - // print an ending header line - if (empty($this->header_width)) { - //set style for cell border - $prevlinewidth = $this->GetLineWidth(); - $line_width = 0.3; - $this->SetLineWidth($line_width); - $this->SetDrawColor(0, 0, 0); - $this->SetY(1 + max($this->img_rb_y, $this->GetY())); - if ($this->rtl) { - $this->SetX($this->original_rMargin); - } else { - $this->SetX($this->original_lMargin); - } - $this->Cell(0, 0, '', 'T', 0, 'C'); - $this->SetLineWidth($prevlinewidth); - } - - //restore position - if ($this->rtl) { - $this->SetXY($this->original_rMargin, $this->tMargin); - } else { - $this->SetXY($this->original_lMargin, $this->tMargin); - } - } - } - - /** - * This method is used to render the page footer. - * It is automatically called by AddPage() and could be overwritten in your own inherited class. - */ - public function Footer() { - if ($this->print_footer) { - - if (!isset($this->original_lMargin)) { - $this->original_lMargin = $this->lMargin; - } - if (!isset($this->original_rMargin)) { - $this->original_rMargin = $this->rMargin; - } - - //set font - $this->SetFont($this->footer_font[0], $this->footer_font[1] , $this->footer_font[2]); - //set style for cell border - $prevlinewidth = $this->GetLineWidth(); - $line_width = 0.3; - $this->SetLineWidth($line_width); - $this->SetDrawColor(0, 0, 0); - - $footer_height = round((K_CELL_HEIGHT_RATIO * $this->footer_font[2]) / $this->k, 2); //footer height - //get footer y position - $footer_y = $this->h - $this->footer_margin - $footer_height; - //set current position - if ($this->rtl) { - $this->SetXY($this->original_rMargin, $footer_y); - } else { - $this->SetXY($this->original_lMargin, $footer_y); - } - - //print document barcode - if ($this->barcode) { - $this->Ln(); - $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin)/3); //max width - $this->writeBarcode($this->GetX(), $footer_y + $line_width, $barcode_width, $footer_height - $line_width, "C128B", false, false, 2, $this->barcode); - } - - $pagenumtxt = $this->l['w_page']." ".$this->PageNo().' / {nb}'; - - $this->SetY($footer_y); - - //Print page number - if ($this->rtl) { - $this->SetX($this->original_rMargin); - $this->Cell(0, $footer_height, $pagenumtxt, 'T', 0, 'L'); - } else { - $this->SetX($this->original_lMargin); - $this->Cell(0, $footer_height, $pagenumtxt, 'T', 0, 'R'); - } - // restore line width - $this->SetLineWidth($prevlinewidth); - } - } - - /** - * Returns the current page number. - * @return int page number - * @since 1.0 - * @see AliasNbPages() - */ - public function PageNo() { - //Get current page number - return $this->page; - } - - /** - * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. - * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 - * @param int $g Green component (between 0 and 255) - * @param int $b Blue component (between 0 and 255) - * @since 1.3 - * @see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() - */ - public function SetDrawColor($r, $g=-1, $b=-1) { - //Set color for all stroking operations - if(($r==0 and $g==0 and $b==0) or $g==-1) { - $this->DrawColor=sprintf('%.3f G',$r/255); - } - else { - $this->DrawColor=sprintf('%.3f %.3f %.3f RG',$r/255,$g/255,$b/255); - } - if($this->page>0) { - $this->_out($this->DrawColor); - } - } - - /** - * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. - * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 - * @param int $g Green component (between 0 and 255) - * @param int $b Blue component (between 0 and 255) - * @param boolean $storeprev if true stores the RGB array on $prevFillColor variable. - * @since 1.3 - * @see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() - */ - public function SetFillColor($r, $g=-1, $b=-1, $storeprev=false) { - //Set color for all filling operations - if(($r==0 and $g==0 and $b==0) or $g==-1) { - $this->FillColor=sprintf('%.3f g',$r/255); - } - else { - $this->FillColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); - } - $this->ColorFlag=($this->FillColor!=$this->TextColor); - if($this->page>0) { - $this->_out($this->FillColor); - } - if ($storeprev) { - // store color as previous value - $this->prevFillColor = array($r, $g, $b); - } - } - - /** - * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. - * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 - * @param int $g Green component (between 0 and 255) - * @param int $b Blue component (between 0 and 255) - * @param boolean $storeprev if true stores the RGB array on $prevTextColor variable. - * @since 1.3 - * @see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() - */ - public function SetTextColor($r, $g=-1, $b=-1, $storeprev=false) { - //Set color for text - if(($r==0 and $g==0 and $b==0) or $g==-1) { - $this->TextColor=sprintf('%.3f g',$r/255); - } - else { - $this->TextColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); - } - $this->ColorFlag=($this->FillColor!=$this->TextColor); - if ($storeprev) { - // store color as previous value - $this->prevTextColor = array($r, $g, $b); - } - } - - /** - * Returns the length of a string in user unit. A font must be selected.<br> - * Support UTF-8 Unicode [Nicola Asuni, 2005-01-02] - * @param string $s The string whose length is to be computed - * @return int - * @since 1.2 - */ - public function GetStringWidth($s) { - //Get width of a string in the current font - $s = (string)$s; - $cw = &$this->CurrentFont['cw']; - $w = 0; - if($this->isunicode) { - $unicode = $this->UTF8StringToArray($s); - foreach($unicode as $char) { - if (isset($cw[$char])) { - $w+=$cw[$char]; - } elseif(isset($cw[ord($char)])) { - $w+=$cw[ord($char)]; - } elseif(isset($cw[chr($char)])) { - $w+=$cw[chr($char)]; - } elseif(isset($this->CurrentFont['desc']['MissingWidth'])) { - $w += $this->CurrentFont['desc']['MissingWidth']; // set default size - } else { - $w += 500; - } - } - } else { - $l = strlen($s); - for($i=0; $i < $l; $i++) { - if (isset($cw[$s{$i}])) { - $w += $cw[$s{$i}]; - } else if (isset($cw[ord($s{$i})])) { - $w += $cw[ord($s{$i})]; - } - } - } - return ($w * $this->FontSize / 1000); - } - - /** - * Returns the numbero of characters in a string. - * @param string $s The input string. - * @return int number of characters - * @since 2.0.0001 (2008-01-07) - */ - public function GetNumChars($s) { - if($this->isunicode) { - return count($this->UTF8StringToArray($s)); - } - return strlen($s); - } - - /** - * Imports a TrueType or Type1 font and makes it available. It is necessary to generate a font definition file first with the makefont.php utility. The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated. - * Support UTF-8 Unicode [Nicola Asuni, 2005-01-02]. - * <b>Example</b>:<br /> - * <pre> - * $pdf->AddFont('Comic','I'); - * // is equivalent to: - * $pdf->AddFont('Comic','I','comici.php'); - * </pre> - * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. - * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul> - * @param string $file The font definition file. By default, the name is built from the family and style, in lower case with no space. - * @since 1.5 - * @see SetFont() - */ - public function AddFont($family, $style='', $file='') { - if(empty($family)) { - return; - } - - //Add a TrueType or Type1 font - $family = strtolower($family); - if((!$this->isunicode) AND ($family == 'arial')) { - $family = 'helvetica'; - } - - $style=strtoupper($style); - $style=str_replace('U','',$style); - if($style == 'IB') { - $style = 'BI'; - } - - $fontkey = $family.$style; - // check if the font has been already added - if(isset($this->fonts[$fontkey])) { - return; - } - - if($file=='') { - $file = str_replace(' ', '', $family).strtolower($style).'.php'; - } - if(!file_exists($this->_getfontpath().$file)) { - // try to load the basic file without styles - $file = str_replace(' ', '', $family).'.php'; - } - - include($this->_getfontpath().$file); - - if(!isset($name) AND !isset($fpdf_charwidths)) { - $this->Error('Could not include font definition file'); - } - - $i = count($this->fonts)+1; - - if($this->isunicode) { - $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file, 'ctg'=>$ctg); - $fpdf_charwidths[$fontkey] = $cw; - } else { - $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$this->CoreFonts[$fontkey], 'up'=>-100, 'ut'=>50, 'cw'=>$fpdf_charwidths[$fontkey]); - } - - if(isset($diff) AND (!empty($diff))) { - //Search existing encodings - $d=0; - $nb=count($this->diffs); - for($i=1;$i<=$nb;$i++) { - if($this->diffs[$i]==$diff) { - $d=$i; - break; - } - } - if($d==0) { - $d=$nb+1; - $this->diffs[$d]=$diff; - } - $this->fonts[$fontkey]['diff']=$d; - } - if(!empty($file)) { - if((strcasecmp($type,"TrueType") == 0) OR (strcasecmp($type,"TrueTypeUnicode") == 0)) { - $this->FontFiles[$file]=array('length1'=>$originalsize); - } - else { - $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2); - } - } - } - - /** - * Sets the font used to print character strings. It is mandatory to call this method at least once before printing text or the resulting document would not be valid. - * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). - * The method can be called before the first page is created and the font is retained from page to page. - If you just wish to change the current font size, it is simpler to call SetFontSize(). - * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br /> - * Example for the last case (note the trailing slash):<br /> - * <pre> - * define('K_PATH_FONTS','/home/www/font/'); - * require('tcpdf.php'); - * - * //Times regular 12 - * $pdf->SetFont('Times'); - * //Arial bold 14 - * $pdf->SetFont('Arial','B',14); - * //Removes bold - * $pdf->SetFont(''); - * //Times bold, italic and underlined 14 - * $pdf->SetFont('Times','BIU'); - * </pre><br /> - * If the file corresponding to the requested font is not found, the error "Could not include font metric file" is generated. - * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard families (case insensitive):<ul><li>Courier (fixed-width)</li><li>Helvetica or Arial (synonymous; sans serif)</li><li>Times (serif)</li><li>Symbol (symbolic)</li><li>ZapfDingbats (symbolic)</li></ul>It is also possible to pass an empty string. In that case, the current family is retained. - * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li></ul>or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats - * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12 - * @since 1.0 - * @see AddFont(), SetFontSize(), Cell(), MultiCell(), Write() - */ - public function SetFont($family, $style='', $size=0) { - // save previous values - $this->prevFontFamily = $this->FontFamily; - $this->prevFontStyle = $this->FontStyle; - - //Select a font; size given in points - global $fpdf_charwidths; - - $family=strtolower($family); - if($family=='') { - $family=$this->FontFamily; - } - if((!$this->isunicode) AND ($family == 'arial')) { - $family = 'helvetica'; - } - elseif(($family=="symbol") OR ($family=="zapfdingbats")) { - $style=''; - } - $style=strtoupper($style); - - if(strpos($style,'U')!==false) { - $this->underline=true; - $style=str_replace('U','',$style); - } - else { - $this->underline=false; - } - if($style=='IB') { - $style='BI'; - } - if($size==0) { - $size=$this->FontSizePt; - } - - // try to add font (if not already added) - if($this->isunicode) { - $this->AddFont($family, $style); - } - - //Test if font is already selected - if(($this->FontFamily == $family) AND ($this->FontStyle == $style) AND ($this->FontSizePt == $size)) { - return; - } - - $fontkey = $family.$style; - //if(!isset($this->fonts[$fontkey]) AND isset($this->fonts[$family])) { - // $style=''; - //} - - //Test if used for the first time - if(!isset($this->fonts[$fontkey])) { - //Check if one of the standard fonts - if(isset($this->CoreFonts[$fontkey])) { - if(!isset($fpdf_charwidths[$fontkey])) { - //Load metric file - $file = $family; - if(($family!='symbol') AND ($family!='zapfdingbats')) { - $file .= strtolower($style); - } - if(!file_exists($this->_getfontpath().$file.'.php')) { - // try to load the basic file without styles - $file = $family; - $fontkey = $family; - } - include($this->_getfontpath().$file.'.php'); - if (($this->isunicode AND !isset($ctg)) OR ((!$this->isunicode) AND (!isset($fpdf_charwidths[$fontkey]))) ) { - $this->Error("Could not include font metric file [".$fontkey."]: ".$this->_getfontpath().$file.".php"); - } - } - $i = count($this->fonts) + 1; - - if($this->isunicode) { - $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file, 'ctg'=>$ctg); - $fpdf_charwidths[$fontkey] = $cw; - } else { - $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$this->CoreFonts[$fontkey], 'up'=>-100, 'ut'=>50, 'cw'=>$fpdf_charwidths[$fontkey]); - } - } - else { - $this->Error('Undefined font: '.$family.' '.$style); - } - } - //Select it - $this->FontFamily = $family; - $this->FontStyle = $style; - $this->FontSizePt = $size; - $this->FontSize = $size / $this->k; - $this->CurrentFont = &$this->fonts[$fontkey]; - if($this->page>0) { - $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); - } - } - - /** - * Defines the size of the current font. - * @param float $size The size (in points) - * @since 1.0 - * @see SetFont() - */ - public function SetFontSize($size) { - //Set font size in points - if($this->FontSizePt==$size) { - return; - } - $this->FontSizePt = $size; - $this->FontSize = $size / $this->k; - if($this->page > 0) { - $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); - } - } - - /** - * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br /> - * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). - * @since 1.5 - * @see Cell(), Write(), Image(), Link(), SetLink() - */ - public function AddLink() { - //Create a new internal link - $n=count($this->links)+1; - $this->links[$n]=array(0,0); - return $n; - } - - /** - * Defines the page and position a link points to - * @param int $link The link identifier returned by AddLink() - * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) - * @param int $page Number of target page; -1 indicates the current page. This is the default value - * @since 1.5 - * @see AddLink() - */ - public function SetLink($link, $y=0, $page=-1) { - //Set destination of internal link - if($y==-1) { - $y=$this->y; - } - if($page==-1) { - $page=$this->page; - } - $this->links[$link]=array($page,$y); - } - - /** - * Puts a link on a rectangular area of the page. Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image. - * @param float $x Abscissa of the upper-left corner of the rectangle (or upper-right for RTL languages) - * @param float $y Ordinate of the upper-left corner of the rectangle (or upper-right for RTL languages) - * @param float $w Width of the rectangle - * @param float $h Height of the rectangle - * @param mixed $link URL or identifier returned by AddLink() - * @since 1.5 - * @see AddLink(), Cell(), Write(), Image() - */ - public function Link($x, $y, $w, $h, $link) { - $this->PageLinks[$this->page][] = array($x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h*$this->k, $link); - } - - /** - * Prints a character string. The origin is on the left of the first charcter, on the baseline. This method allows to place a string precisely on the page, but it is usually easier to use Cell(), MultiCell() or Write() which are the standard methods to print text. - * @param float $x Abscissa of the origin - * @param float $y Ordinate of the origin - * @param string $txt String to print - * @since 1.0 - * @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write() - */ - public function Text($x, $y, $txt) { - //Output a string - if($this->rtl) { - $xr = $this->w - $x - $this->GetStringWidth($txt); - } else { - $xr = $x; - } - $s = sprintf('BT %.2f %.2f Td (%s) Tj ET', $xr * $this->k, ($this->h-$y) * $this->k, $this->_escapetext($txt)); - if($this->underline AND ($txt!='')) { - $s .= ' '.$this->_dounderline($xr, $y, $txt); - } - if($this->ColorFlag) { - $s='q '.$this->TextColor.' '.$s.' Q'; - } - $this->_out($s); - } - - /** - * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br /> - * This method is called automatically and should not be called directly by the application.<br /> - * <b>Example:</b><br /> - * The method is overriden in an inherited class in order to obtain a 3 column layout:<br /> - * <pre> - * class PDF extends TCPDF { - * var $col=0; - * - * public function SetCol($col) { - * //Move position to a column - * $this->col=$col; - * $x=10+$col*65; - * $this->SetLeftMargin($x); - * $this->SetX($x); - * } - * - * public function AcceptPageBreak() { - * if($this->col<2) { - * //Go to next column - * $this->SetCol($this->col+1); - * $this->SetY(10); - * return false; - * } - * else { - * //Go back to first column and issue page break - * $this->SetCol(0); - * return true; - * } - * } - * } - * - * $pdf=new PDF(); - * $pdf->Open(); - * $pdf->AddPage(); - * $pdf->SetFont('Arial','',12); - * for($i=1;$i<=300;$i++) { - * $pdf->Cell(0,5,"Line $i",0,1); - * } - * $pdf->Output(); - * </pre> - * @return boolean - * @since 1.4 - * @see SetAutoPageBreak() - */ - public function AcceptPageBreak() { - //Accept automatic page break or not - return $this->AutoPageBreak; - } - - /** - * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br /> - * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. - * @param float $w Cell width. If 0, the cell extends up to the right margin. - * @param float $h Cell height. Default value: 0. - * @param string $txt String to print. Default value: empty string. - * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> - * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> - Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. - * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> - * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. - * @param mixed $link URL or identifier returned by AddLink(). - * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul> - * @since 1.0 - * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() - */ - public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) { - //Output a cell - $k=$this->k; - if(($this->y + $h) > $this->PageBreakTrigger AND empty($this->InFooter) AND $this->AcceptPageBreak()) { - //Automatic page break - $x = $this->x; - $ws = $this->ws; - if($ws > 0) { - $this->ws = 0; - $this->_out('0 Tw'); - } - $this->AddPage($this->CurOrientation); - $this->x = $x; - if($ws > 0) { - $this->ws = $ws; - $this->_out(sprintf('%.3f Tw',$ws * $k)); - } - } - if($w == 0) { - if ($this->rtl) { - $w = $this->x - $this->lMargin; - } else { - $w = $this->w - $this->rMargin - $this->x; - } - } - $s = ''; - if(($fill == 1) OR ($border == 1)) { - if($fill == 1) { - $op = ($border == 1) ? 'B' : 'f'; - } else { - $op = 'S'; - } - if ($this->rtl) { - $xk = ($this->x - $w) * $k; - } else { - $xk = $this->x * $k; - } - $s .= sprintf('%.2f %.2f %.2f %.2f re %s ', $xk, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op); - } - if(is_string($border)) { - $x=$this->x; - $y=$this->y; - if(strpos($border,'L')!==false) { - if ($this->rtl) { - $xk = ($x - $w) * $k; - } else { - $xk = $x * $k; - } - $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xk,($this->h-($y+$h))*$k); - } - if(strpos($border,'T')!==false) { - if ($this->rtl) { - $xk = ($x - $w) * $k; - $xwk = $x * $k; - } else { - $xk = $x * $k; - $xwk = ($x + $w) * $k; - } - $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xwk,($this->h-$y)*$k); - } - if(strpos($border,'R')!==false) { - if ($this->rtl) { - $xk = $x * $k; - } else { - $xk = ($x + $w) * $k; - } - $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xk,($this->h-($y+$h))*$k); - } - if(strpos($border,'B')!==false) { - if ($this->rtl) { - $xk = ($x - $w) * $k; - $xwk = $x * $k; - } else { - $xk = $x * $k; - $xwk = ($x + $w) * $k; - } - $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-($y+$h))*$k,$xwk,($this->h-($y+$h))*$k); - } - } - if($txt != '') { - // text lenght - $width = $this->GetStringWidth($txt); - // ratio between cell lenght and text lenght - $ratio = ($w - (2 * $this->cMargin)) / $width; - - // stretch text if requested - if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) { - if ($stretch > 2) { - // spacing - //Calculate character spacing in points - $char_space = ($w - (2 * $this->cMargin) - $width) / max($this->GetNumChars($s)-1,1) * $this->k; - //Set character spacing - $this->_out(sprintf('BT %.2f Tc ET', $char_space)); - } else { - // scaling - //Calculate horizontal scaling - $horiz_scale = $ratio*100.0; - //Set horizontal scaling - $this->_out(sprintf('BT %.2f Tz ET', $horiz_scale)); - } - $align = ''; - $width = $w - (2 * $this->cMargin); - } else { - $stretch == 0; - } - - if($align == 'L') { - if ($this->rtl) { - $dx = $w - $this->cMargin - $width; - } else { - $dx = $this->cMargin; - } - } elseif($align == 'R') { - if ($this->rtl) { - $dx = $this->cMargin; - } else { - $dx = $w - $this->cMargin - $width; - } - } elseif($align=='C') { - $dx = ($w - $width)/2; - } elseif($align=='J') { - if ($this->rtl) { - $dx = $w - $this->cMargin - $width; - } else { - $dx = $this->cMargin; - } - } else { - $dx = $this->cMargin; - } - if($this->ColorFlag) { - $s .= 'q '.$this->TextColor.' '; - } - $txt2 = $this->_escapetext($txt); - if ($this->rtl) { - $xdk = ($this->x - $dx - $width) * $k; - } else { - $xdk = ($this->x + $dx) * $k; - } - // 2008-02-16 Jacek Czekaj - multibyte justification - if ($align == 'J') { - // count number of spaces - $ns = substr_count($txt, ' '); - // get string width without spaces - $width = $this->GetStringWidth(str_replace(' ', '', $txt)); - // set word position to be used with TJ operator - $txt2 = str_replace(chr(0).' ', ') '. -2830*($w-$width-2)/($ns?$ns:1)/$this->FontSize/$this->k . ' (', $txt2); - } - $s.=sprintf('BT %.2f %.2f Td [(%s)] TJ ET', $xdk, ($this->h - ($this->y + 0.5 * $h + 0.3 * $this->FontSize)) * $k, $txt2); - //$s.=sprintf('BT %.2f %.2f Td (%s) Tj ET', $xdk, ($this->h - ($this->y + 0.5 * $h + 0.3 * $this->FontSize)) * $k, $txt2); - if($this->underline) { - if ($this->rtl) { - $xdx = $this->x - $dx - $width; - } else { - $xdx = $this->x + $dx; - } - $s.=' '.$this->_dounderline($xdx, $this->y + 0.5 * $h + 0.3 * $this->FontSize, $txt); - } - if($this->ColorFlag) { - $s.=' Q'; - } - if($link) { - if ($this->rtl) { - $xdx = $this->x - $dx - $width; - } else { - $xdx = $this->x + $dx; - } - $this->Link($xdx, $this->y + 0.5 * $h - 0.5 * $this->FontSize, $width, $this->FontSize, $link); - } - } - - // output cell - if($s) { - // output cell - $this->_out($s); - // reset text stretching - if($stretch > 2) { - //Reset character horizontal spacing - $this->_out('BT 0 Tc ET'); - } elseif($stretch > 0) { - //Reset character horizontal scaling - $this->_out('BT 100 Tz ET'); - } - } - - $this->lasth = $h; - if($ln>0) { - //Go to the beginning of the next line - $this->y += $h; - if($ln == 1) { - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - } else { - $this->x = $this->lMargin; - } - } - } else { - // go left or right by case - if ($this->rtl) { - $this->x -= $w; - } else { - $this->x += $w; - } - } - } - - /** - * This method allows printing text with line breaks. They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br /> - * Text can be aligned, centered or justified. The cell block can be framed and the background painted. - * @param float $w Width of cells. If 0, they extend up to the right margin of the page. - * @param float $h Height of cells. - * @param string $txt String to print - * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> - * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value)</li></ul> - * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. - * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul> - * @return int number of cells (number of lines) - * @since 1.3 - * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() - */ - public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1) { - - // save current position - $prevx = $this->x; - $prevy = $this->y; - - // get current page number - $startpage = $this->page; - - // calculate remaining vertical space on first page ($startpage) - $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin(); - - //Output text with automatic or explicit line breaks - $cw = &$this->CurrentFont['cw']; - - if($w == 0) { - if ($this->rtl) { - $w = $this->x - $this->lMargin; - } else { - $w = $this->w - $this->rMargin - $this->x; - } - } - - $wmax = ($w - 2 * $this->cMargin); - - $s = str_replace("\r", '', $txt); // remove carriage returns - $nb = strlen($s); - - $sep=-1; - $i=0; - $j=0; - $l=0; - $ns=0; - $nl=1; - while($i < $nb) { - //Get next character - $c = $s{$i}; - if(preg_match("/[\n]/u", $c)) { - //Explicit line break - if($this->ws > 0) { - $this->ws = 0; - $this->_out('0 Tw'); - } - $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); - $i++; - $sep=-1; - $j=$i; - $l=0; - $ns=0; - $nl++; - continue; - } - if(preg_match("/[ ]/u", $c)) { - $sep = $i; - $ls = $l; - $ns++; - } - - $l = $this->GetStringWidth(substr($s, $j, $i-$j)); - - if($l > $wmax) { - //Automatic line break - if($sep == -1) { - if($i == $j) { - $i++; - } - if($this->ws > 0) { - $this->ws = 0; - $this->_out('0 Tw'); - } - $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); - } - else { - if($align=='J') { - $this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0; - $this->_out(sprintf('%.3f Tw', $this->ws * $this->k)); - } - $this->Cell($w, $h, substr($s, $j, $sep-$j), 0, 2, $align, $fill, ''); - $i = $sep + 1; - } - $sep=-1; - $j=$i; - $l=0; - $ns=0; - $nl++; - } - else { - $i++; - } - } - //Last chunk - if($this->ws>0) { - $this->ws=0; - $this->_out('0 Tw'); - } - if($align == "J") { - $align = "L"; - } - $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); - - // Get end-of-text Y position - $currentY = $this->GetY(); - // get latest page number - $endpage = $this->page; - - if (!empty($border)) { - // check if a new page has been created - if ($endpage > $startpage) { - // design borders around HTML cells. - for ($page=$startpage; $page<=$endpage; $page++) { - $this->page = $page; - if ($page==$startpage) { - $this->y = $this->getPageHeight() - $restspace - $this->getBreakMargin(); - $h = $restspace - 1; - } elseif ($page==$endpage) { - $this->y = $this->tMargin; // put cursor at the beginning of text - $h = $currentY - $this->tMargin; - } else { - $this->y = $this->tMargin; // put cursor at the beginning of text - $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); - } - $this->Cell($w, $h, "", $border, 1, '', 0); - } - } else { - $h = max($h, ($currentY - $prevy)); - $this->y = $prevy; // put cursor at the beginning of text - // design a cell around the text - $this->Cell($w, $h, "", $border, 1, '', 0); - } - } - - // move cursor to specified position - if($ln>0) { - //Go to the beginning of the next line - $this->SetY($currentY); - if($ln == 2) { - $this->SetX($prevx + $w); - } - } else { - // go left or right by case - $this->page = $startpage; - $this->y = $prevy; - $this->SetX($prevx + $w); - } - - return $nl; - } - - /** - * This method prints text from the current position. When the right margin is reached (or the \n character is met) a line break occurs and text continues from the left margin. Upon method exit, the current position is left just at the end of the text. It is possible to put a link on the text.<br /> - * <b>Example:</b><br /> - * <pre> - * //Begin with regular font - * $pdf->SetFont('Arial','',14); - * $pdf->Write(5,'Visit '); - * //Then put a blue underlined link - * $pdf->SetTextColor(0,0,255); - * $pdf->SetFont('','U'); - * $pdf->Write(5,'www.tecnick.com','http://www.tecnick.com'); - * </pre> - * @param float $h Line height - * @param string $txt String to print - * @param mixed $link URL or identifier returned by AddLink() - * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. - * @since 1.5 - * @see SetFont(), SetTextColor(), AddLink(), MultiCell(), SetAutoPageBreak() - */ - public function Write($h, $txt, $link='', $fill=0) { - //Output text in flowing mode - $cw = &$this->CurrentFont['cw']; - // calculating remaining line width ($w) - if ($this->rtl) { - $w = $this->x - $this->lMargin; - } else { - $w = $this->w - $this->rMargin - $this->x; - } - $wmax = $w - 2 * $this->cMargin; - $s = str_replace("\r", '', $txt); - $nb = strlen($s); - // handle single space character - if(($nb==1) AND preg_match("/[ ]/u", $s)) { - if ($this->rtl) { - $this->x -= $this->GetStringWidth($s); - } else { - $this->x += $this->GetStringWidth($s); - } - return; - } - $sep=-1; - $i=0; - $j=0; - $l=0; - $nl=1; - while($i<$nb) { - //Get next character - $c=$s{$i}; - if(preg_match("/[\n]/u", $c)) { - //Explicit line break - $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, '', $fill, $link); - $i++; - $sep = -1; - $j = $i; - $l = 0; - if($nl == 1) { - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - $w = $this->x - $this->lMargin; - } - else { - $this->x = $this->lMargin; - $w = $this->w - $this->rMargin - $this->x; - } - $wmax = ($w - 2 * $this->cMargin); - } - $nl++; - continue; - } - if(preg_match("/[ ]/u", $c)) { - $sep= $i; - } - $l = $this->GetStringWidth(substr($s, $j, $i-$j)); - if($l > $wmax) { - //Automatic line break (word wrapping) - if($sep == -1) { - if((!$this->rtl) AND ($this->x > $this->lMargin)) { - //Move to next line - $this->x = $this->lMargin; - $this->y += $h; - $w=$this->w - $this->rMargin - $this->x; - $wmax=($w - 2 * $this->cMargin); - $i++; - $nl++; - continue; - } - if($this->rtl AND ($this->x < $this->rMargin)) { - //Move to next line - $this->x = $this->w - $this->rMargin; - $this->y += $h; - $w=$this->x - $this->lMargin; - $wmax=($w - 2 * $this->cMargin); - $i++; - $nl++; - continue; - } - if($i==$j) { - $i++; - } - $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, '', $fill, $link); - } - else { - $this->Cell($w, $h, substr($s, $j, $sep-$j), 0, 2, '', $fill, $link); - $i=$sep+1; - } - $sep = -1; - $j = $i; - $l = 0; - if($nl==1) { - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - $w = $this->x - $this->lMargin; - } else { - $this->x = $this->lMargin; - $w = $this->w - $this->rMargin - $this->x; - } - $wmax = ($w - 2 * $this->cMargin); - } - $nl++; - } - else { - $i++; - } - } - - //Last chunk - if($i!=$j) { - $this->Cell($this->GetStringWidth(substr($s, $j)) + 2 * $this->cMargin, $h, substr($s, $j), 0, 0, '', $fill, $link); - } - } - - /** - * Puts an image in the page. The upper-left corner must be given. The dimensions can be specified in different ways:<ul><li>explicit width and height (expressed in user unit)</li><li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li><li>no explicit dimension, in which case the image is put at 72 dpi</li></ul> - * Supported formats are JPEG and PNG. - * For JPEG, all flavors are allowed:<ul><li>gray scales</li><li>true colors (24 bits)</li><li>CMYK (32 bits)</li></ul> - * For PNG, are allowed:<ul><li>gray scales on at most 8 bits (256 levels)</li><li>indexed colors</li><li>true colors (24 bits)</li></ul> - * If a transparent color is defined, it will be taken into account (but will be only interpreted by Acrobat 4 and above).<br /> - * The format can be specified explicitly or inferred from the file extension.<br /> - * It is possible to put a link on the image.<br /> - * Remark: if an image is used several times, only one copy will be embedded in the file.<br /> - * @param string $file Name of the file containing the image. - * @param float $x Abscissa of the upper-left corner. - * @param float $y Ordinate of the upper-left corner. - * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. - * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. - * @param string $type Image format. Possible values are (case insensitive): JPG, JPEG, PNG. If not specified, the type is inferred from the file extension. - * @param mixed $link URL or identifier returned by AddLink(). - * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> - * @since 1.1 - * @see AddLink() - */ - public function Image($file, $x, $y, $w=0, $h=0, $type='', $link='', $align='') { - //Put an image on the page - if(!isset($this->images[$file])) { - //First use of image, get info - if($type == '') { - $pos = strrpos($file,'.'); - if(empty($pos)) { - $this->Error('Image file has no extension and no type was specified: '.$file); - } - $type = substr($file, $pos+1); - } - $type = strtolower($type); - $mqr = get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - if($type == 'jpg' or $type == 'jpeg') { - $info=$this->_parsejpg($file); - } elseif($type == 'gif') { - $info=$this->_parsegif($file); - } elseif($type == 'png') { - $info=$this->_parsepng($file); - }else { - //Allow for additional formats - $mtd='_parse'.$type; - if(!method_exists($this,$mtd)) { - $this->Error('Unsupported image type: '.$type); - } - $info=$this->$mtd($file); - } - if($info === false) { - //If false, we cannot process image - return; - } - set_magic_quotes_runtime($mqr); - $info['i']=count($this->images)+1; - $this->images[$file]=$info; - } - else { - $info=$this->images[$file]; - } - //Automatic width and height calculation if needed - if(($w == 0) and ($h == 0)) { - //Put image at 72 dpi - // 2004-06-14 :: Nicola Asuni, scale factor where added - $w = $info['w'] / ($this->imgscale * $this->k); - $h = $info['h'] / ($this->imgscale * $this->k); - } - if($w == 0) { - $w = $h * $info['w'] / $info['h']; - } - if($h == 0) { - $h = $w * $info['h'] / $info['w']; - } - - // 2007-10-19 Warren Sherliker - // Check whether we need a new page first as this does not fit - // Copied from Cell() - if((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) { - // Automatic page break - $this->AddPage($this->CurOrientation); - // Reset coordinates to top fo next page - $x = $this->GetX(); - $y = $this->GetY(); - } - // 2007-10-19 Warren Sherliker: End Edit - - // set bottomcoordinates - $this->img_rb_y = $y + $h; - if ($this->rtl) { - $ximg = ($this->w - $x -$w); - // set left side coordinate - $this->img_rb_x = $ximg; - } else { - $ximg = $x; - // set right side coordinate - $this->img_rb_x = $ximg + $w; - } - $xkimg = $ximg * $this->k; - $this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', $w*$this->k, $h*$this->k, $xkimg, ($this->h-($y+$h))*$this->k, $info['i'])); - - if($link) { - $this->Link($ximg, $y, $w, $h, $link); - } - - // set pointer to align the successive text/objects - switch($align) { - case 'T':{ - $this->y = $y; - $this->x = $this->img_rb_x; - break; - } - case 'M':{ - $this->y = $y + round($h/2); - $this->x = $this->img_rb_x; - break; - } - case 'B':{ - $this->y = $this->img_rb_y; - $this->x = $this->img_rb_x; - break; - } - case 'N':{ - $this->SetY($this->img_rb_y); - break; - } - default:{ - break; - } - } - } - - - /** - * Performs a line break. The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. - * @param float $h The height of the break. By default, the value equals the height of the last printed cell. - * @since 1.0 - * @see Cell() - */ - public function Ln($h='') { - //Line feed; default value is last cell height - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - } else { - $this->x = $this->lMargin; - } - if(is_string($h)) { - $this->y += $this->lasth; - } else { - $this->y += $h; - } - } - - /** - * Returns the relative X value of current position. - * The value is relative to the left border for LTR languages and to the right border for RTL languages. - * @return float - * @since 1.2 - * @see SetX(), GetY(), SetY() - */ - public function GetX() { - //Get x position - if ($this->rtl) { - return ($this->w - $this->x); - } else { - return $this->x; - } - } - - /** - * Returns the absolute X value of current position. - * @return float - * @since 1.2 - * @see SetX(), GetY(), SetY() - */ - public function GetAbsX() { - return $this->x; - } - - /** - * Returns the ordinate of the current position. - * @return float - * @since 1.0 - * @see SetY(), GetX(), SetX() - */ - public function GetY() { - //Get y position - return $this->y; - } - - /** - * Defines the abscissa of the current position. - * If the passed value is negative, it is relative to the right of the page (or left if language is RTL). - * @param float $x The value of the abscissa. - * @since 1.2 - * @see GetX(), GetY(), SetY(), SetXY() - */ - public function SetX($x) { - //Set x position - if ($this->rtl) { - if($x >= 0) { - $this->x = $this->w - $x; - } else { - $this->x = abs($x); - } - } else { - if($x >= 0) { - $this->x = $x; - } else { - $this->x = $this->w + $x; - } - } - } - - /** - * Moves the current abscissa back to the left margin and sets the ordinate. - * If the passed value is negative, it is relative to the bottom of the page. - * @param float $y The value of the ordinate. - * @since 1.0 - * @see GetX(), GetY(), SetY(), SetXY() - */ - public function SetY($y) { - //Set y position and reset x - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - } else { - $this->x = $this->lMargin; - } - if($y >= 0) { - $this->y = $y; - } else { - $this->y = $this->h + $y; - } - } - - - /** - * Defines the abscissa and ordinate of the current position. If the passed values are negative, they are relative respectively to the right and bottom of the page. - * @param float $x The value of the abscissa - * @param float $y The value of the ordinate - * @since 1.2 - * @see SetX(), SetY() - */ - public function SetXY($x, $y) { - //Set x and y positions - $this->SetY($y); - $this->SetX($x); - } - - /** - * Send the document to a given destination: string, local file or browser. In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br /> - * The method first calls Close() if necessary to terminate the document. - * @param string $name The name of the file. If not given, the document will be sent to the browser (destination I) with the name doc.pdf. - * @param string $dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser. The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li></ul>If the parameter is not specified but a name is given, destination is F. If no parameter is specified at all, destination is I.<br />Note: for compatibility with previous versions, a boolean value is also accepted (false for F and true for D). - * @since 1.0 - * @see Close() - */ - public function Output($name='',$dest='') { - //Output PDF to some destination - //Finish document if necessary - if($this->state < 3) { - $this->Close(); - } - //Normalize parameters - if(is_bool($dest)) { - $dest=$dest ? 'D' : 'F'; - } - $dest=strtoupper($dest); - if($dest=='') { - if($name=='') { - $name='doc.pdf'; - $dest='I'; - } else { - $dest='F'; - } - } - switch($dest) { - case 'I': { - //Send to standard output - //if(ob_get_contents()) { - // $this->Error('Some data has already been output, can\'t send PDF file'); - //} - ob_end_clean(); - ob_start(); - if(php_sapi_name()!='cli') { - //We send to a browser - header('Content-Type: application/pdf'); - if(headers_sent()) { - $this->Error('Some data has already been output to browser, can\'t send PDF file'); - } - header('Content-Length: '.strlen($this->buffer)); - header('Content-disposition: inline; filename="'.$name.'"'); - } - echo $this->buffer; - break; - } - case 'D': { - //Download file - // if(ob_get_contents()) { - // $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); - //} - - if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { - header('Content-Type: application/force-download'); - } else { - header('Content-Type: application/octet-stream'); - } - if(headers_sent()) { - $this->Error('Some data has already been output to browser, can\'t send PDF file'); - } - header('Content-Length: '.strlen($this->buffer)); - header('Content-disposition: attachment; filename="'.$name.'"'); - echo $this->buffer; - break; - } - case 'F': { - //Save to local file - $f=fopen($name,'wb'); - if(!$f) { - $this->Error('Unable to create output file: '.$name); - } - fwrite($f,$this->buffer,strlen($this->buffer)); - fclose($f); - break; - } - case 'S': { - //Return as a string - return $this->buffer; - } - default: { - $this->Error('Incorrect output destination: '.$dest); - } - } - return ''; - } - - // Protected methods - - /** - * Check for locale-related bug - * @access protected - */ - protected function _dochecks() { - //Check for locale-related bug - if(1.1==1) { - $this->Error('Don\'t alter the locale before including class file'); - } - //Check for decimal separator - if(sprintf('%.1f',1.0)!='1.0') { - setlocale(LC_NUMERIC,'C'); - } - } - - /** - * Return fonts path - * @access protected - */ - protected function _getfontpath() { - if(!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/font')) { - define('K_PATH_FONTS', dirname(__FILE__).'/font/'); - } - return defined('K_PATH_FONTS') ? K_PATH_FONTS : ''; - } - - /** - * Start document - * @access protected - */ - protected function _begindoc() { - //Start document - $this->state=1; - $this->_out('%PDF-'.$this->PDFVersion); - } - - /** - * _putpages - * @access protected - */ - protected function _putpages() { - $nb = $this->page; - if(!empty($this->AliasNbPages)) { - $nbstr = $this->UTF8ToUTF16BE($nb, false); - //Replace number of pages - for($n=1;$n<=$nb;$n++) { - $this->pages[$n]=str_replace($this->AliasNbPages, $nbstr, $this->pages[$n]); - } - } - if($this->DefOrientation=='P') { - $wPt=$this->fwPt; - $hPt=$this->fhPt; - } - else { - $wPt=$this->fhPt; - $hPt=$this->fwPt; - } - $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; - for($n=1;$n<=$nb;$n++) { - //Page - $this->_newobj(); - $this->_out('<</Type /Page'); - $this->_out('/Parent 1 0 R'); - if(isset($this->OrientationChanges[$n])) { - $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$hPt,$wPt)); - } - $this->_out('/Resources 2 0 R'); - if(isset($this->PageLinks[$n])) { - //Links - $annots='/Annots ['; - foreach($this->PageLinks[$n] as $pl) { - $rect=sprintf('%.2f %.2f %.2f %.2f',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]); - $annots.='<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] '; - if(is_string($pl[4])) { - $annots.='/A <</S /URI /URI '.$this->_uristring($pl[4]).'>>>>'; - } - else { - $l=$this->links[$pl[4]]; - $h=isset($this->OrientationChanges[$l[0]]) ? $wPt : $hPt; - $annots.=sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>',1+2*$l[0],$h-$l[1]*$this->k); - } - } - $this->_out($annots.']'); - } - $this->_out('/Contents '.($this->n+1).' 0 R>>'); - $this->_out('endobj'); - //Page content - $p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n]; - $this->_newobj(); - $this->_out('<<'.$filter.'/Length '.strlen($p).'>>'); - $this->_putstream($p); - $this->_out('endobj'); - } - //Pages root - $this->offsets[1]=strlen($this->buffer); - $this->_out('1 0 obj'); - $this->_out('<</Type /Pages'); - $kids='/Kids ['; - for($i=0;$i<$nb;$i++) { - $kids.=(3+2*$i).' 0 R '; - } - $this->_out($kids.']'); - $this->_out('/Count '.$nb); - $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$wPt,$hPt)); - $this->_out('>>'); - $this->_out('endobj'); - } - - /** - * Adds fonts - * _putfonts - * @access protected - */ - protected function _putfonts() { - $nf=$this->n; - foreach($this->diffs as $diff) { - //Encodings - $this->_newobj(); - $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>'); - $this->_out('endobj'); - } - $mqr=get_magic_quotes_runtime(); - set_magic_quotes_runtime(0); - foreach($this->FontFiles as $file=>$info) { - //Font file embedding - $this->_newobj(); - $this->FontFiles[$file]['n']=$this->n; - $font=''; - $f=fopen($this->_getfontpath().strtolower($file),'rb',1); - if(!$f) { - $this->Error('Font file not found: '.$file); - } - while(!feof($f)) { - $font .= fread($f, 8192); - } - fclose($f); - $compressed=(substr($file,-2)=='.z'); - if(!$compressed && isset($info['length2'])) { - $header=(ord($font{0})==128); - if($header) { - //Strip first binary header - $font=substr($font,6); - } - if($header && ord($font{$info['length1']})==128) { - //Strip second binary header - $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6); - } - } - $this->_out('<</Length '.strlen($font)); - if($compressed) { - $this->_out('/Filter /FlateDecode'); - } - $this->_out('/Length1 '.$info['length1']); - if(isset($info['length2'])) { - $this->_out('/Length2 '.$info['length2'].' /Length3 0'); - } - $this->_out('>>'); - $this->_putstream($font); - $this->_out('endobj'); - } - set_magic_quotes_runtime($mqr); - foreach($this->fonts as $k=>$font) { - //Font objects - $this->fonts[$k]['n']=$this->n+1; - $type=$font['type']; - $name=$font['name']; - if($type=='core') { - //Standard font - $this->_newobj(); - $this->_out('<</Type /Font'); - $this->_out('/BaseFont /'.$name); - $this->_out('/Subtype /Type1'); - if($name!='Symbol' && $name!='ZapfDingbats') { - $this->_out('/Encoding /WinAnsiEncoding'); - } - $this->_out('>>'); - $this->_out('endobj'); - } elseif($type=='Type1' OR $type=='TrueType') { - //Additional Type1 or TrueType font - $this->_newobj(); - $this->_out('<</Type /Font'); - $this->_out('/BaseFont /'.$name); - $this->_out('/Subtype /'.$type); - $this->_out('/FirstChar 32 /LastChar 255'); - $this->_out('/Widths '.($this->n+1).' 0 R'); - $this->_out('/FontDescriptor '.($this->n+2).' 0 R'); - if($font['enc']) { - if(isset($font['diff'])) { - $this->_out('/Encoding '.($nf+$font['diff']).' 0 R'); - } else { - $this->_out('/Encoding /WinAnsiEncoding'); - } - } - $this->_out('>>'); - $this->_out('endobj'); - //Widths - $this->_newobj(); - $cw=&$font['cw']; - $s='['; - for($i=32;$i<=255;$i++) { - $s.=$cw[chr($i)].' '; - } - $this->_out($s.']'); - $this->_out('endobj'); - //Descriptor - $this->_newobj(); - $s='<</Type /FontDescriptor /FontName /'.$name; - foreach($font['desc'] as $k=>$v) { - $s.=' /'.$k.' '.$v; - } - $file = $font['file']; - if($file) { - $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R'; - } - $this->_out($s.'>>'); - $this->_out('endobj'); - } else { - //Allow for additional types - $mtd='_put'.strtolower($type); - if(!method_exists($this, $mtd)) { - $this->Error('Unsupported font type: '.$type); - } - $this->$mtd($font); - } - } - } - - /** - * _putimages - * @access protected - */ - protected function _putimages() { - $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; - reset($this->images); - while(list($file,$info)=each($this->images)) { - $this->_newobj(); - $this->images[$file]['n']=$this->n; - $this->_out('<</Type /XObject'); - $this->_out('/Subtype /Image'); - $this->_out('/Width '.$info['w']); - $this->_out('/Height '.$info['h']); - - if (isset($info["masked"])) { - $this->_out('/SMask '.($this->n-1).' 0 R'); - } - - if($info['cs']=='Indexed') { - $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); - } - else { - $this->_out('/ColorSpace /'.$info['cs']); - if($info['cs']=='DeviceCMYK') { - $this->_out('/Decode [1 0 1 0 1 0 1 0]'); - } - } - $this->_out('/BitsPerComponent '.$info['bpc']); - if(isset($info['f'])) { - $this->_out('/Filter /'.$info['f']); - } - if(isset($info['parms'])) { - $this->_out($info['parms']); - } - if(isset($info['trns']) and is_array($info['trns'])) { - $trns=''; - for($i=0;$i<count($info['trns']);$i++) { - $trns.=$info['trns'][$i].' '.$info['trns'][$i].' '; - } - $this->_out('/Mask ['.$trns.']'); - } - $this->_out('/Length '.strlen($info['data']).'>>'); - $this->_putstream($info['data']); - unset($this->images[$file]['data']); - $this->_out('endobj'); - //Palette - if($info['cs']=='Indexed') { - $this->_newobj(); - $pal=($this->compress) ? gzcompress($info['pal']) : $info['pal']; - $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>'); - $this->_putstream($pal); - $this->_out('endobj'); - } - } - } - - /** - * _putxobjectdict - * @access protected - */ - function _putxobjectdict() { - foreach($this->images as $image) { - $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); - } - } - - /** - * _putresourcedict - * @access protected - */ - function _putresourcedict(){ - $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); - $this->_out('/Font <<'); - foreach($this->fonts as $font) { - $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R'); - } - $this->_out('>>'); - $this->_out('/XObject <<'); - $this->_putxobjectdict(); - $this->_out('>>'); - } - - /** - * _putresources - * @access protected - */ - function _putresources() { - $this->_putfonts(); - $this->_putimages(); - //Resource dictionary - $this->offsets[2]=strlen($this->buffer); - $this->_out('2 0 obj'); - $this->_out('<<'); - $this->_putresourcedict(); - $this->_out('>>'); - $this->_out('endobj'); - $this->_putjavascript(); - $this->_putbookmarks(); - // encryption - if ($this->encrypted) { - $this->_newobj(); - $this->enc_obj_id = $this->n; - $this->_out('<<'); - $this->_putencryption(); - $this->_out('>>'); - $this->_out('endobj'); - } - } - - /** - * _putinfo - * Adds some meta information - * @access protected - */ - protected function _putinfo() { - $this->_out('/CreationDate ('.$this->_escape('D:'.date('YmdHis')).')'); - $this->_out('/ModDate ('.$this->_escape('D:'.date('YmdHis')).')'); - $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER)); - if(!empty($this->title)) { - $this->_out('/Title '.$this->_textstring($this->title)); - } - if(!empty($this->subject)) { - $this->_out('/Subject '.$this->_textstring($this->subject)); - } - if(!empty($this->author)) { - $this->_out('/Author '.$this->_textstring($this->author)); - } - if(!empty($this->keywords)) { - $this->_out('/Keywords '.$this->_textstring($this->keywords)); - } - if(!empty($this->creator)) { - $this->_out('/Creator '.$this->_textstring($this->creator)); - } - } - - /** - * _putcatalog - * @access protected - */ - protected function _putcatalog() { - $this->_out('/Type /Catalog'); - $this->_out('/Pages 1 0 R'); - if($this->ZoomMode=='fullpage') { - $this->_out('/OpenAction [3 0 R /Fit]'); - } - elseif($this->ZoomMode=='fullwidth') { - $this->_out('/OpenAction [3 0 R /FitH null]'); - } - elseif($this->ZoomMode=='real') { - $this->_out('/OpenAction [3 0 R /XYZ null null 1]'); - } - elseif(!is_string($this->ZoomMode)) { - $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode/100).']'); - } - if($this->LayoutMode=='single') { - $this->_out('/PageLayout /SinglePage'); - } - elseif($this->LayoutMode=='continuous') { - $this->_out('/PageLayout /OneColumn'); - } - elseif($this->LayoutMode=='two') { - $this->_out('/PageLayout /TwoColumnLeft'); - } - if (!empty($this->javascript)) { - $this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>'); - } - if(count($this->outlines)>0) { - $this->_out('/Outlines '.$this->OutlineRoot.' 0 R'); - $this->_out('/PageMode /UseOutlines'); - } - if($this->rtl) { - $this->_out('/ViewerPreferences << /Direction /R2L >>'); - } - } - - /** - * _puttrailer - * @access protected - */ - protected function _puttrailer() { - $this->_out('/Size '.($this->n+1)); - $this->_out('/Root '.$this->n.' 0 R'); - $this->_out('/Info '.($this->n-1).' 0 R'); - if ($this->encrypted) { - $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R'); - $this->_out('/ID [()()]'); - } - } - - /** - * _putheader - * @access protected - */ - function _putheader() { - $this->_out('%PDF-'.$this->PDFVersion); - } - - /** - * _enddoc - * @access protected - */ - protected function _enddoc() { - $this->_putheader(); - $this->_putpages(); - $this->_putresources(); - //Info - $this->_newobj(); - $this->_out('<<'); - $this->_putinfo(); - $this->_out('>>'); - $this->_out('endobj'); - //Catalog - $this->_newobj(); - $this->_out('<<'); - $this->_putcatalog(); - $this->_out('>>'); - $this->_out('endobj'); - //Cross-ref - $o=strlen($this->buffer); - $this->_out('xref'); - $this->_out('0 '.($this->n+1)); - $this->_out('0000000000 65535 f '); - for($i=1;$i<=$this->n;$i++) { - $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i])); - } - //Trailer - $this->_out('trailer'); - $this->_out('<<'); - $this->_puttrailer(); - $this->_out('>>'); - $this->_out('startxref'); - $this->_out($o); - $this->_out('%%EOF'); - $this->state=3; - } - - /** - * _beginpage - * @access protected - */ - protected function _beginpage($orientation) { - $this->page++; - $this->pages[$this->page]=''; - $this->state=2; - if ($this->rtl) { - $this->x = $this->w - $this->rMargin; - } else { - $this->x = $this->lMargin; - } - $this->y = $this->tMargin; - $this->FontFamily=''; - //Page orientation - if(empty($orientation)) { - $orientation=$this->DefOrientation; - } - else { - $orientation=strtoupper($orientation{0}); - if($orientation!=$this->DefOrientation) { - $this->OrientationChanges[$this->page]=true; - } - } - if($orientation!=$this->CurOrientation) { - //Change orientation - if($orientation=='P') { - $this->wPt=$this->fwPt; - $this->hPt=$this->fhPt; - $this->w=$this->fw; - $this->h=$this->fh; - } - else { - $this->wPt=$this->fhPt; - $this->hPt=$this->fwPt; - $this->w=$this->fh; - $this->h=$this->fw; - } - $this->PageBreakTrigger=$this->h-$this->bMargin; - $this->CurOrientation=$orientation; - } - } - - /** - * End of page contents - * @access protected - */ - protected function _endpage() { - $this->state=1; - } - - /** - * Begin a new object - * @access protected - */ - protected function _newobj() { - $this->n++; - $this->offsets[$this->n]=strlen($this->buffer); - $this->_out($this->n.' 0 obj'); - } - - /** - * Underline text - * @param int $x X coordinate - * @param int $y Y coordinate - * @param string $txt text to underline - * @access protected - */ - protected function _dounderline($x, $y, $txt) { - $up = $this->CurrentFont['up']; - $ut = $this->CurrentFont['ut']; - $w = $this->GetStringWidth($txt) + $this->ws * substr_count($txt,' '); - return sprintf('%.2f %.2f %.2f %.2f re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt); - } - - - // REWRITTEN by Warren Sherliker wsherliker@gmail.com - // altered to allow compatibility with all sorts of image formats including gif. - // Can easily extend to work with others - // such as gd xbm etc. which are all supported by php 5+ - // (Requires GD library) - - /** - * Extract info from a JPEG file - * @param string $file image file to parse - * @return string - * @access protected - */ - protected function _parsejpg($file) { - if(!function_exists('imagecreatefromjpeg')) { - // GD is not installed, try legacy method - return $this->_legacyparsejpg($file); - } - $a=getimagesize($file); - if(empty($a)) { - $this->Error('Missing or incorrect image file: '.$file); - } - if($a[2]!=2) { - $this->Error('Not a JPEG file: '.$file); - } - $jpeg = imagecreatefromjpeg($file); - return $this->outputjpg($file, $jpeg); - } - - /** - * Extract info from a GIF file - * @param string $file image file to parse - * @return string - * @access protected - */ - protected function _parsegif($file) { - if(!function_exists('imagecreatefromgif')) { - // PDF doesn't support native GIF and GD is not installed - return false; - } - $a=getimagesize($file); - if(empty($a)) { - $this->Error('Missing or incorrect image file: '.$file); - } - if($a[2]!=1) { - $this->Error('Not a GIF file: '.$file); - } - // Temporary convert file to jpg and then delete this temp data file - $gif = imagecreatefromgif($file); - return $this->toJPEG($file, $gif); - } - - /** - * Extract info from a PNG file - * @param string $file image file to parse - * @return string - * @access protected - */ - protected function _parsepng($file) { - if(!function_exists('imagecreatefrompng')) { - // GD is not installed, try legacy method - return $this->_legacyparsepng($file); - } - $f=fopen($file,'rb'); - if(empty($f)) { - $this->Error('Can\'t open image file: '.$file); - } - //Check signature - if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { - $this->Error('Not a PNG file: '.$file); - } - //Read header chunk - fread($f,4); - if(fread($f,4)!='IHDR') { - $this->Error('Incorrect PNG file: '.$file); - } - // Temporary convert file to jpg and then delete this temp data file - $a=getimagesize($file); - $png = imagecreatefrompng($file); - return $this->toJPEG($file, $png); - } - - /** - * Extract info from a JPEG file without using GD - * @param string $file image file to parse - * @return string - * @access protected - */ - protected function _legacyparsejpg($file) { - $a=GetImageSize($file); - if(empty($a)) { - $this->Error('Missing or incorrect image file: '.$file); - } - if($a[2]!=2) { - $this->Error('Not a JPEG file: '.$file); - } - if(!isset($a['channels']) or $a['channels']==3) { - $colspace='DeviceRGB'; - } - elseif($a['channels']==4) { - $colspace='DeviceCMYK'; - } - else { - $colspace='DeviceGray'; - } - $bpc=isset($a['bits']) ? $a['bits'] : 8; - //Read whole file - $f=fopen($file,'rb'); - $data=''; - while(!feof($f)) { - $data.=fread($f,4096); - } - fclose($f); - return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data); - } - - /** - * Extract info from a PNG file without using GD - * @param string $file image file to parse - * @return string - * @access protected - */ - protected function _legacyparsepng($file) { - $f=fopen($file,'rb'); - if(empty($f)) { - $this->Error('Can\'t open image file: '.$file); - } - //Check signature - if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { - $this->Error('Not a PNG file: '.$file); - } - //Read header chunk - fread($f,4); - if(fread($f,4)!='IHDR') { - $this->Error('Incorrect PNG file: '.$file); - } - $w=$this->_freadint($f); - $h=$this->_freadint($f); - $bpc=ord(fread($f,1)); - if($bpc>8) { - $this->Error('16-bit depth not supported: '.$file); - } - $ct=ord(fread($f,1)); - if($ct==0) { - $colspace='DeviceGray'; - } - elseif($ct==2) { - $colspace='DeviceRGB'; - } - elseif($ct==3) { - $colspace='Indexed'; - } - else { - $this->Error('Alpha channel not supported: '.$file); - } - if(ord(fread($f,1))!=0) { - $this->Error('Unknown compression method: '.$file); - } - if(ord(fread($f,1))!=0) { - $this->Error('Unknown filter method: '.$file); - } - if(ord(fread($f,1))!=0) { - $this->Error('Interlacing not supported: '.$file); - } - fread($f,4); - $parms='/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>'; - //Scan chunks looking for palette, transparency and image data - $pal=''; - $trns=''; - $data=''; - do { - $n=$this->_freadint($f); - $type=fread($f,4); - if($type=='PLTE') { - //Read palette - $pal=fread($f,$n); - fread($f,4); - } - elseif($type=='tRNS') { - //Read transparency info - $t=fread($f,$n); - if($ct==0) { - $trns=array(ord(substr($t,1,1))); - } - elseif($ct==2) { - $trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1))); - } - else { - $pos=strpos($t,chr(0)); - if($pos!==false) { - $trns=array($pos); - } - } - fread($f,4); - } - elseif($type=='IDAT') { - //Read image data block - $data.=fread($f,$n); - fread($f,4); - } - elseif($type=='IEND') { - break; - } - else { - fread($f,$n+4); - } - } - while($n); - if($colspace=='Indexed' and empty($pal)) { - $this->Error('Missing palette in '.$file); - } - fclose($f); - return array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'parms'=>$parms, 'pal'=>$pal, 'trns'=>$trns, 'data'=>$data); - } - - /** - * Convert the loaded php image to a JPEG and then return a structure for the PDF creator. - * @param string $file Image file name. - * @param image $image Image object. - * return image JPEG image object. - * @access protected - */ - protected function toJPEG($file, $image) { - if ($image) { - // output - $tempname = tempnam(K_PATH_CACHE,'jpg'); - imagejpeg($image, $tempname, 100); - imagedestroy($image); - $retvars = $this->outputjpg($tempname); - // tidy up by removing temporary image - unlink($tempname); - return $retvars; - } else { - $this->Error('Can\'t open image file: '.$file); - } - } - - /** - * Get a JPEG filename and return a structure for the PDF creator. - * @param string $filename JPEG file name. - * @return array structure containing the image data - * @access protected - */ - protected function outputjpg($filename) { - $a=getimagesize($filename); - - if(!isset($a['channels']) or $a['channels']==3) { - $colspace='DeviceRGB'; - } - elseif($a['channels']==4) { - $colspace='DeviceCMYK'; - } - else { - $colspace='DeviceGray'; - } - $bpc=isset($a['bits']) ? $a['bits'] : 8; - //Read whole file - - $f=fopen($filename,'rb'); - $data=''; - while(!feof($f)) { - $data.=fread($f,4096); - } - fclose($f); - - return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data); - } - - /// END OF REWRITE BY Warren Sherliker wsherliker@gmail.com - - /** - * Read a 4-byte integer from file - * @param string $f file name. - * @return 4-byte integer - * @access protected - */ - protected function _freadint($f) { - $a=unpack('Ni',fread($f,4)); - return $a['i']; - } - - /** - * Format a text string for meta information - * @param string $s string to escape. - * @return string escaped string. - * @access protected - */ - protected function _textstring($s) { - if($this->isunicode) { - //Convert string to UTF-16BE - $s = $this->UTF8ToUTF16BE($s, true); - } - if ($this->encrypted) { - $s = $this->_RC4($this->_objectkey($this->n), $s); - } - return '('. $this->_escape($s).')'; - } - - /** - * Format an URI string - * @param string $s string to escape. - * @return string escaped string. - * @access protected - */ - protected function _uristring($s) { - if ($this->encrypted) { - $s = $this->_RC4($this->_objectkey($this->n), $s); - } - return '('.$this->_escape($s).')'; - } - - /** - * Format a text string - * @param string $s string to escape. - * @return string escaped string. - * @access protected - */ - protected function _escapetext($s) { - if($this->isunicode) { - //Convert string to UTF-16BE and reverse RTL language - $s = $this->utf8StrRev($s, false, $this->tmprtl); - } - return $this->_escape($s); - } - - /** - * Add \ before \, ( and ) - * @param string $s string to escape. - * @return string escaped string. - * @access protected - */ - protected function _escape($s) { - // the chr(13) substitution fixes the Bugs item #1421290. - return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); - } - - /** - * Output a stream. - * @param string $s string to output. - * @access protected - */ - protected function _putstream($s) { - if ($this->encrypted) { - $s = $this->_RC4($this->_objectkey($this->n), $s); - } - $this->_out('stream'); - $this->_out($s); - $this->_out('endstream'); - } - - /** - * Output a string to the document. - * @param string $s string to output. - * @access protected - */ - protected function _out($s) { - if($this->state==2) { - $this->pages[$this->page] .= $s."\n"; - } - else { - $this->buffer .= $s."\n"; - } - } - - /** - * Adds unicode fonts.<br> - * Based on PDF Reference 1.3 (section 5) - * @access protected - * @author Nicola Asuni - * @since 1.52.0.TC005 (2005-01-05) - */ - protected function _puttruetypeunicode($font) { - // Type0 Font - // A composite font composed of other fonts, organized hierarchically - $this->_newobj(); - $this->_out('<</Type /Font'); - $this->_out('/Subtype /Type0'); - $this->_out('/BaseFont /'.$font['name'].''); - $this->_out('/Encoding /Identity-H'); //The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values. - $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]'); - $this->_out('/ToUnicode '.($this->n + 2).' 0 R'); - $this->_out('>>'); - $this->_out('endobj'); - - // CIDFontType2 - // A CIDFont whose glyph descriptions are based on TrueType font technology - $this->_newobj(); - $this->_out('<</Type /Font'); - $this->_out('/Subtype /CIDFontType2'); - $this->_out('/BaseFont /'.$font['name'].''); - $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R'); - $this->_out('/FontDescriptor '.($this->n + 3).' 0 R'); - if (isset($font['desc']['MissingWidth'])){ - $this->_out('/DW '.$font['desc']['MissingWidth'].''); // The default width for glyphs in the CIDFont MissingWidth - } - $w = ""; - foreach ($font['cw'] as $cid => $width) { - $w .= ''.$cid.' ['.$width.'] '; // define a specific width for each individual CID - } - $this->_out('/W ['.$w.']'); // A description of the widths for the glyphs in the CIDFont - $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R'); - $this->_out('>>'); - $this->_out('endobj'); - - // ToUnicode - // is a stream object that contains the definition of the CMap - // (PDF Reference 1.3 chap. 5.9) - $this->_newobj(); - $this->_out('<</Length 345>>'); - $this->_out('stream'); - $this->_out('/CIDInit /ProcSet findresource begin'); - $this->_out('12 dict begin'); - $this->_out('begincmap'); - $this->_out('/CIDSystemInfo'); - $this->_out('<</Registry (Adobe)'); - $this->_out('/Ordering (UCS)'); - $this->_out('/Supplement 0'); - $this->_out('>> def'); - $this->_out('/CMapName /Adobe-Identity-UCS def'); - $this->_out('/CMapType 2 def'); - $this->_out('1 begincodespacerange'); - $this->_out('<0000> <FFFF>'); - $this->_out('endcodespacerange'); - $this->_out('1 beginbfrange'); - $this->_out('<0000> <FFFF> <0000>'); - $this->_out('endbfrange'); - $this->_out('endcmap'); - $this->_out('CMapName currentdict /CMap defineresource pop'); - $this->_out('end'); - $this->_out('end'); - $this->_out('endstream'); - $this->_out('endobj'); - - // CIDSystemInfo dictionary - // A dictionary containing entries that define the character collection of the CIDFont. - $this->_newobj(); - $this->_out('<</Registry (Adobe)'); // A string identifying an issuer of character collections - $this->_out('/Ordering (UCS)'); // A string that uniquely names a character collection issued by a specific registry - $this->_out('/Supplement 0'); // The supplement number of the character collection. - $this->_out('>>'); - $this->_out('endobj'); - - // Font descriptor - // A font descriptor describing the CIDFont default metrics other than its glyph widths - $this->_newobj(); - $this->_out('<</Type /FontDescriptor'); - $this->_out('/FontName /'.$font['name']); - foreach ($font['desc'] as $key => $value) { - $this->_out('/'.$key.' '.$value); - } - if ($font['file']) { - // A stream containing a TrueType font program - $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'); - } - $this->_out('>>'); - $this->_out('endobj'); - - // Embed CIDToGIDMap - // A specification of the mapping from CIDs to glyph indices - $this->_newobj(); - $ctgfile = $this->_getfontpath().strtolower($font['ctg']); - if(!file_exists($ctgfile)) { - $this->Error('Font file not found: '.$ctgfile); - } - $size = filesize($ctgfile); - $this->_out('<</Length '.$size.''); - if(substr($ctgfile, -2) == '.z') { // check file extension - /* Decompresses data encoded using the public-domain - zlib/deflate compression method, reproducing the - original text or binary data */ - $this->_out('/Filter /FlateDecode'); - } - $this->_out('>>'); - $this->_putstream(file_get_contents($ctgfile)); - $this->_out('endobj'); - } - - /** - * Converts UTF-8 strings to codepoints array.<br> - * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br> - * Based on: http://www.faqs.org/rfcs/rfc3629.html - * <pre> - * Char. number range | UTF-8 octet sequence - * (hexadecimal) | (binary) - * --------------------+----------------------------------------------- - * 0000 0000-0000 007F | 0xxxxxxx - * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx - * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx - * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * --------------------------------------------------------------------- - * - * ABFN notation: - * --------------------------------------------------------------------- - * UTF8-octets = *( UTF8-char ) - * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 - * UTF8-1 = %x00-7F - * UTF8-2 = %xC2-DF UTF8-tail - * - * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / - * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) - * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / - * %xF4 %x80-8F 2( UTF8-tail ) - * UTF8-tail = %x80-BF - * --------------------------------------------------------------------- - * </pre> - * @param string $str string to process. - * @return array containing codepoints (UTF-8 characters values) - * @access protected - * @author Nicola Asuni - * @since 1.53.0.TC005 (2005-01-05) - */ - protected function UTF8StringToArray($str) { - if(!$this->isunicode) { - return $str; // string is not in unicode - } - $unicode = array(); // array containing unicode values - $bytes = array(); // array containing single character byte sequences - $numbytes = 1; // number of octetc needed to represent the UTF-8 character - - $str .= ""; // force $str to be a string - $length = strlen($str); - - for($i = 0; $i < $length; $i++) { - $char = ord($str{$i}); // get one string character at time - if(count($bytes) == 0) { // get starting octect - if ($char <= 0x7F) { - $unicode[] = $char; // use the character "as is" because is ASCII - $numbytes = 1; - } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) - $bytes[] = ($char - 0xC0) << 0x06; - $numbytes = 2; - } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) - $bytes[] = ($char - 0xE0) << 0x0C; - $numbytes = 3; - } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) - $bytes[] = ($char - 0xF0) << 0x12; - $numbytes = 4; - } else { - // use replacement character for other invalid sequences - $unicode[] = 0xFFFD; - $bytes = array(); - $numbytes = 1; - } - } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN - $bytes[] = $char - 0x80; - if (count($bytes) == $numbytes) { - // compose UTF-8 bytes to a single unicode value - $char = $bytes[0]; - for($j = 1; $j < $numbytes; $j++) { - $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); - } - if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) { - /* The definition of UTF-8 prohibits encoding character numbers between - U+D800 and U+DFFF, which are reserved for use with the UTF-16 - encoding form (as surrogate pairs) and do not directly represent - characters. */ - $unicode[] = 0xFFFD; // use replacement character - } - else { - $unicode[] = $char; // add char to array - } - // reset data for next char - $bytes = array(); - $numbytes = 1; - } - } else { - // use replacement character for other invalid sequences - $unicode[] = 0xFFFD; - $bytes = array(); - $numbytes = 1; - } - } - return $unicode; - } - - /** - * Converts UTF-8 strings to UTF16-BE.<br> - * @param string $str string to process. - * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) - * @return string - * @access protected - * @author Nicola Asuni - * @since 1.53.0.TC005 (2005-01-05) - * @uses UTF8StringToArray(), arrUTF8ToUTF16BE() - */ - protected function UTF8ToUTF16BE($str, $setbom=true) { - if(!$this->isunicode) { - return $str; // string is not in unicode - } - $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values - return $this->arrUTF8ToUTF16BE($unicode, $setbom); - } - - /** - * Converts array of UTF-8 characters to UTF16-BE string.<br> - * Based on: http://www.faqs.org/rfcs/rfc2781.html - * <pre> - * Encoding UTF-16: - * - * Encoding of a single character from an ISO 10646 character value to - * UTF-16 proceeds as follows. Let U be the character number, no greater - * than 0x10FFFF. - * - * 1) If U < 0x10000, encode U as a 16-bit unsigned integer and - * terminate. - * - * 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, - * U' must be less than or equal to 0xFFFFF. That is, U' can be - * represented in 20 bits. - * - * 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and - * 0xDC00, respectively. These integers each have 10 bits free to - * encode the character value, for a total of 20 bits. - * - * 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order - * bits of W1 and the 10 low-order bits of U' to the 10 low-order - * bits of W2. Terminate. - * - * Graphically, steps 2 through 4 look like: - * U' = yyyyyyyyyyxxxxxxxxxx - * W1 = 110110yyyyyyyyyy - * W2 = 110111xxxxxxxxxx - * </pre> - * @param array $unicode array containing UTF-8 unicode values - * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) - * @return string - * @access protected - * @author Nicola Asuni - * @since 2.1.000 (2008-01-08) - * @see UTF8ToUTF16BE() - */ - protected function arrUTF8ToUTF16BE($unicode, $setbom=true) { - $outstr = ""; // string to be returned - if ($setbom) { - $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) - } - foreach($unicode as $char) { - if($char == 0xFFFD) { - $outstr .= "\xFF\xFD"; // replacement character - } elseif ($char < 0x10000) { - $outstr .= chr($char >> 0x08); - $outstr .= chr($char & 0xFF); - } else { - $char -= 0x10000; - $w1 = 0xD800 | ($char >> 0x10); - $w2 = 0xDC00 | ($char & 0x3FF); - $outstr .= chr($w1 >> 0x08); - $outstr .= chr($w1 & 0xFF); - $outstr .= chr($w2 >> 0x08); - $outstr .= chr($w2 & 0xFF); - } - } - return $outstr; - } - // ==================================================== - - /** - * Set header font. - * @param array $font font - * @since 1.1 - */ - public function setHeaderFont($font) { - $this->header_font = $font; - } - - /** - * Set footer font. - * @param array $font font - * @since 1.1 - */ - public function setFooterFont($font) { - $this->footer_font = $font; - } - - /** - * Set language array. - * @param array $language - * @since 1.1 - */ - public function setLanguageArray($language) { - $this->l = $language; - $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; - } - - /** - * Set document barcode. - * @param string $bc barcode - */ - public function setBarcode($bc="") { - $this->barcode = $bc; - } - - /** - * Print Barcode. - * @param int $x x position in user units - * @param int $y y position in user units - * @param int $w width in user units - * @param int $h height position in user units - * @param string $type type of barcode (I25, C128A, C128B, C128C, C39) - * @param string $style barcode style - * @param string $font font for text - * @param int $xres x resolution - * @param string $code code to print - */ - public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) { - require_once(dirname(__FILE__)."/barcode/barcode.php"); - require_once(dirname(__FILE__)."/barcode/i25object.php"); - require_once(dirname(__FILE__)."/barcode/c39object.php"); - require_once(dirname(__FILE__)."/barcode/c128aobject.php"); - require_once(dirname(__FILE__)."/barcode/c128bobject.php"); - require_once(dirname(__FILE__)."/barcode/c128cobject.php"); - - if (empty($code)) { - return; - } - - if (empty($style)) { - $style = BCS_ALIGN_LEFT; - $style |= BCS_IMAGE_PNG; - $style |= BCS_TRANSPARENT; - //$style |= BCS_BORDER; - //$style |= BCS_DRAW_TEXT; - //$style |= BCS_STRETCH_TEXT; - //$style |= BCS_REVERSE_COLOR; - } - if (empty($font)) {$font = BCD_DEFAULT_FONT;} - if (empty($xres)) {$xres = BCD_DEFAULT_XRES;} - - $scale_factor = 1.5 * $xres * $this->k; - $bc_w = round($w * $scale_factor); //width in points - $bc_h = round($h * $scale_factor); //height in points - - switch (strtoupper($type)) { - case "I25": { - $obj = new I25Object($bc_w, $bc_h, $style, $code); - break; - } - case "C128A": { - $obj = new C128AObject($bc_w, $bc_h, $style, $code); - break; - } - default: - case "C128B": { - $obj = new C128BObject($bc_w, $bc_h, $style, $code); - break; - } - case "C128C": { - $obj = new C128CObject($bc_w, $bc_h, $style, $code); - break; - } - case "C39": { - $obj = new C39Object($bc_w, $bc_h, $style, $code); - break; - } - } - - $obj->SetFont($font); - $obj->DrawObject($xres); - - //use a temporary file.... - $tmpName = tempnam(K_PATH_CACHE,'img'); - imagepng($obj->getImage(), $tmpName); - $this->Image($tmpName, $x, $y, $w, $h, 'png'); - $obj->DestroyObject(); - unset($obj); - unlink($tmpName); - } - - /** - * Returns the PDF data. - */ - public function getPDFData() { - if($this->state < 3) { - $this->Close(); - } - return $this->buffer; - } - - // --- HTML PARSER FUNCTIONS --- - - /** - * Allows to preserve some HTML formatting.<br /> - * Supports: h1, h2, h3, h4, h5, h6, b, u, i, a, img, p, br, strong, em, font, blockquote, li, ul, ol, hr, td, th, tr, table, sup, sub, small - * @param string $html text to display - * @param boolean $ln if true add a new line after text (default = true) - * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. - * @param boolean $reseth if true reset the last cell height (default false). - * @param boolean $cell if true add the default cMargin space to each Write. - */ - public function writeHTML($html, $ln=true, $fill=0, $reseth=false, $cell=false) { - - // store some variables - $html=strip_tags($html,"<h1><h2><h3><h4><h5><h6><b><u><i><a><img><p><br><br/><strong><em><font><blockquote><li><ul><ol><hr><td><th><tr><table><sup><sub><small><span><div>"); //remove all unsupported tags - //replace carriage returns, newlines and tabs - $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " "); - $html = strtr($html, $repTable); - $pattern = '/(<[^>]+>)/Uu'; - $a = preg_split($pattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); //explodes the string - - if ((empty($this->lasth))OR ($reseth)) { - //set row height - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - } - - foreach($a as $key=>$element) { - if (!preg_match($pattern, $element)) { - //Text - if($this->HREF) { - $this->addHtmlLink($this->HREF, $element, $fill); - } elseif($this->tdbegin) { - if((strlen(trim($element)) > 0) AND ($element != "&nbsp;")) { - $this->Cell($this->tdwidth, $this->tdheight, $this->unhtmlentities($element), $this->tableborder, '', $this->tdalign, $this->tdbgcolor); - } elseif($element == "&nbsp;") { - $this->Cell($this->tdwidth, $this->tdheight, '', $this->tableborder, '', $this->tdalign, $this->tdbgcolor); - } - } else { - - $ctmpmargin = $this->cMargin; - if(!$cell) { - $this->cMargin = 0; - } - $this->Write($this->lasth, stripslashes($this->unhtmlentities($element)), '', $fill, 0); - $this->cMargin = $ctmpmargin; - } - } else { - $element = substr($element, 1, -1); - //Tag - if($element{0}=='/') { - $this->closedHTMLTagHandler(strtolower(substr($element, 1))); - } - else { - //Extract attributes - // get tag name - preg_match('/([a-zA-Z0-9]*)/', $element, $tag); - $tag = strtolower($tag[0]); - // get attributes - preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER); - $attr = array(); // reset attribute array - while(list($id,$name)=each($attr_array[1])) { - $attr[strtolower($name)] = $attr_array[2][$id]; - } - $this->openHTMLTagHandler($tag, $attr, $fill); - } - } - } - if ($ln) { - $this->Ln($this->lasth); - } - } - - /** - * Prints a cell (rectangular area) with optional borders, background color and html text string. The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br /> - * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. - * @param float $w Cell width. If 0, the cell extends up to the right margin. - * @param float $h Cell minimum height. The cell extends automatically if needed. - * @param float $x upper-left corner X coordinate - * @param float $y upper-left corner Y coordinate - * @param string $html html text to print. Default value: empty string. - * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> - * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> - Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. - * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. - * @param boolean $reseth if true reset the last cell height (default false). - * @see Cell() - */ - public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=false) { - - if ((empty($this->lasth))OR ($reseth)) { - //set row height - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - } - - // get current page number - $startpage = $this->page; - - if (!empty($y)) { - $this->SetY($y); - } else { - $y = $this->GetY(); - } - if (!empty($x)) { - $this->SetX($x); - } else { - $x = $this->GetX(); - } - - if(empty($w)) { - if ($this->rtl) { - $w = $this->x - $this->lMargin; - } else { - $w = $this->w - $this->rMargin - $this->x; - } - } - - // store original margin values - $lMargin = $this->lMargin; - $rMargin = $this->rMargin; - - // set new margin values - if ($this->rtl) { - $this->SetLeftMargin($this->x - $w); - $this->SetRightMargin($this->w - $this->x); - } else { - $this->SetLeftMargin($this->x); - $this->SetRightMargin($this->w - $this->x - $w); - } - - // calculate remaining vertical space on first page ($startpage) - $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin(); - - // Write HTML text - $this->writeHTML($html, true, $fill, $reseth, true); - - // Get end-of-text Y position - $currentY = $this->GetY(); - // get latest page number - $endpage = $this->page; - - if (!empty($border)) { - // check if a new page has been created - if ($endpage > $startpage) { - // design borders around HTML cells. - for ($page=$startpage; $page<=$endpage; $page++) { - $this->page = $page; - if ($page==$startpage) { - $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin()); - $h = $restspace - 1; - } elseif ($page==$endpage) { - $this->SetY($this->tMargin); // put cursor at the beginning of text - $h = $currentY - $this->tMargin; - } else { - $this->SetY($this->tMargin); // put cursor at the beginning of text - $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); - } - $this->Cell($w, $h, "", $border, 1, '', 0); - } - } else { - $h = max($h, ($currentY - $y)); - $this->SetY($y); // put cursor at the beginning of text - // design a cell around the text - $this->Cell($w, $h, "", $border, 1, '', 0); - } - } - - // restore original margin values - $this->SetLeftMargin($lMargin); - $this->SetRightMargin($rMargin); - - - if($ln>0) { - //Go to the beginning of the next line - $this->SetY($currentY); - if($ln == 2) { - $this->SetX($x + $w); - } - } else { - // go left or right by case - $this->page = $startpage; - $this->y = $y; - $this->SetX($x + $w); - } - - } - - /** - * Process opening tags. - * @param string $tag tag name (in uppercase) - * @param string $attr tag attribute (in uppercase) - * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. - * @access private - */ - private function openHTMLTagHandler($tag, $attr, $fill=0) { - // check for text direction attribute - if (isset($attr['dir'])) { - $this->tmprtl = $attr['dir']=='rtl' ? 'R' : 'L'; - } else { - $this->tmprtl = false; - } - //Opening tag - switch($tag) { - case 'table': { - if ((isset($attr['border'])) AND ($attr['border'] != '')) { - $this->tableborder = $attr['border']; - } - else { - $this->tableborder = 0; - } - break; - } - case 'tr': { - break; - } - case 'td': - case 'th': { - if ((isset($attr['width'])) AND ($attr['width'] != '')) { - $this->tdwidth = ($attr['width']/4); - } - else { - $this->tdwidth = (($this->w - $this->lMargin - $this->rMargin) / $this->default_table_columns); - } - if ((isset($attr['height'])) AND ($attr['height'] != '')) { - $this->tdheight=($attr['height'] / $this->k); - } - else { - $this->tdheight = $this->lasth; - } - if ((isset($attr['align'])) AND ($attr['align'] != '')) { - switch ($attr['align']) { - case 'center': { - $this->tdalign = "C"; - break; - } - case 'right': { - $this->tdalign = "R"; - break; - } - default: - case 'left': { - $this->tdalign = "L"; - break; - } - } - } else { - if($this->rtl) { - $this->tdalign = "R"; - } else { - $this->tdalign = "L"; - } - } - if ((isset($attr['bgcolor'])) AND ($attr['bgcolor'] != '')) { - $coul = $this->convertColorHexToDec($attr['bgcolor']); - $this->SetFillColor($coul['R'], $coul['G'], $coul['B']); - $this->tdbgcolor=true; - } - $this->tdbegin=true; - break; - } - case 'hr': { - $this->Ln(); - if ((isset($attr['width'])) AND ($attr['width'] != '')) { - $hrWidth = $attr['width']; - } - else { - $hrWidth = $this->w - $this->lMargin - $this->rMargin; - } - $x = $this->GetX(); - $y = $this->GetY(); - $this->GetLineWidth(); - $prevlinewidth = $this->SetLineWidth(0.2); - $this->Line($x, $y, $x + $hrWidth, $y); - $this->SetLineWidth($prevlinewidth); - $this->Ln(); - break; - } - case 'strong': { - $this->setStyle('b', true); - break; - } - case 'em': { - $this->setStyle('i', true); - break; - } - case 'b': - case 'i': - case 'u': { - $this->setStyle($tag, true); - break; - } - case 'a': { - $this->HREF = $attr['href']; - break; - } - case 'img': { - if(isset($attr['src'])) { - // replace relative path with real server path - if ($attr['src'][0] == '/') { - $attr['src'] = $_SERVER['DOCUMENT_ROOT'].$attr['src']; - } - $attr['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $attr['src']); - if(!isset($attr['width'])) { - $attr['width'] = 0; - } - if(!isset($attr['height'])) { - $attr['height'] = 0; - } - if(!isset($attr['align'])) { - $align = 'N'; - } else { - switch($attr['align']) { - case 'top':{ - $align = 'T'; - break; - } - case 'middle':{ - $align = 'M'; - break; - } - case 'bottom':{ - $align = 'B'; - break; - } - default:{ - $align = 'N'; - break; - } - } - } - $this->Image($attr['src'], $this->GetX(),$this->GetY(), $this->pixelsToMillimeters($attr['width']), $this->pixelsToMillimeters($attr['height']), '', '', $align); - - } - break; - } - case 'ul': { - $this->listordered = false; - $this->listcount = 0; - break; - } - case 'ol': { - $this->listordered = true; - $this->listcount = 0; - break; - } - case 'li': { - $this->Ln(); - if ($this->listordered) { - if (isset($attr['value'])) { - $this->listcount = intval($attr['value']); - } - $this->lispacer = " ".(++$this->listcount).". "; - } else { - //unordered list simbol - $this->lispacer = " - "; - } - $rtldir = $this->tmprtl; - $this->tmprtl = false; - $this->Write($this->lasth, $this->lispacer, '', $fill); - $this->tmprtl = $rtldir; - break; - } - case 'blockquote': - case 'br': { - $this->Ln(); - if(strlen($this->lispacer) > 0) { - if ($this->rtl) { - $this->x -= $this->GetStringWidth($this->lispacer); - } else { - $this->x += $this->GetStringWidth($this->lispacer); - } - } - break; - } - case 'p': { - $this->Ln(); - $this->Ln(); - break; - } - case 'sup': { - $currentFontSize = $this->FontSize; - $this->tempfontsize = $this->FontSizePt; - $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); - $this->SetXY($this->GetX(), $this->GetY() - (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); - break; - } - case 'sub': { - $currentFontSize = $this->FontSize; - $this->tempfontsize = $this->FontSizePt; - $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); - $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); - break; - } - case 'small': { - $currentFontSize = $this->FontSize; - $this->tempfontsize = $this->FontSizePt; - $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); - $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)/3)); - break; - } - case 'font': { - if (isset($attr['color']) AND $attr['color']!='') { - $coul = $this->convertColorHexToDec($attr['color']); - $this->SetTextColor($coul['R'],$coul['G'],$coul['B']); - $this->issetcolor=true; - } - if (isset($attr['face']) and in_array(strtolower($attr['face']), $this->fontlist)) { - $this->SetFont(strtolower($attr['face'])); - $this->issetfont=true; - } - if (isset($attr['size'])) { - $headsize = intval($attr['size']); - } else { - $headsize = 0; - } - $currentFontSize = $this->FontSize; - $this->tempfontsize = $this->FontSizePt; - $this->SetFontSize($this->FontSizePt + $headsize); - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - break; - } - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': { - $headsize = (4 - substr($tag, 1)) * 2; - $currentFontSize = $this->FontSize; - $this->tempfontsize = $this->FontSizePt; - $this->SetFontSize($this->FontSizePt + $headsize); - $this->setStyle('b', true); - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - break; - } - } - } - - /** - * Process closing tags. - * @param string $tag tag name (in uppercase) - * @access private - */ - private function closedHTMLTagHandler($tag) { - //Closing tag - switch($tag) { - case 'td': - case 'th': { - $this->tdbegin = false; - $this->tdwidth = 0; - $this->tdheight = 0; - if($this->rtl) { - $this->tdalign = "R"; - } else { - $this->tdalign = "L"; - } - $this->tdbgcolor = false; - $this->SetFillColor($this->prevFillColor[0], $this->prevFillColor[1], $this->prevFillColor[2]); - break; - } - case 'tr': { - $this->Ln(); - break; - } - case 'table': { - $this->tableborder=0; - break; - } - case 'strong': { - $this->setStyle('b', false); - break; - } - case 'em': { - $this->setStyle('i', false); - break; - } - case 'b': - case 'i': - case 'u': { - $this->setStyle($tag, false); - break; - } - case 'a': { - $this->HREF = ''; - break; - } - case 'sup': { - $currentFontSize = $this->FontSize; - $this->SetFontSize($this->tempfontsize); - $this->tempfontsize = $this->FontSizePt; - $this->SetXY($this->GetX(), $this->GetY() - (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); - break; - } - case 'sub': { - $currentFontSize = $this->FontSize; - $this->SetFontSize($this->tempfontsize); - $this->tempfontsize = $this->FontSizePt; - $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); - break; - } - case 'small': { - $currentFontSize = $this->FontSize; - $this->SetFontSize($this->tempfontsize); - $this->tempfontsize = $this->FontSizePt; - $this->SetXY($this->GetX(), $this->GetY() - (($this->FontSize - $currentFontSize)/3)); - break; - } - case 'font': { - if ($this->issetcolor == true) { - $this->SetTextColor($this->prevTextColor[0], $this->prevTextColor[1], $this->prevTextColor[2]); - } - if ($this->issetfont) { - $this->FontFamily = $this->prevFontFamily; - $this->FontStyle = $this->prevFontStyle; - $this->SetFont($this->FontFamily); - $this->issetfont = false; - } - $currentFontSize = $this->FontSize; - $this->SetFontSize($this->tempfontsize); - $this->tempfontsize = $this->FontSizePt; - //$this->TextColor = $this->prevTextColor; - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - break; - } - case 'p': { - $this->Ln(); - $this->Ln(); - break; - } - case 'ul': - case 'ol': { - $this->Ln(); - $this->Ln(); - break; - } - case 'li': { - $this->lispacer = ""; - break; - } - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': { - $currentFontSize = $this->FontSize; - $this->SetFontSize($this->tempfontsize); - $this->tempfontsize = $this->FontSizePt; - $this->setStyle('b', false); - $this->Ln(); - $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; - break; - } - default : { - break; - } - } - $this->tmprtl = false; - } - - /** - * Sets font style. - * @param string $tag tag name (in lowercase) - * @param boolean $enable - * @access private - */ - private function setStyle($tag, $enable) { - //Modify style and select corresponding font - $this->$tag += ($enable ? 1 : -1); - $style=''; - foreach(array('b', 'i', 'u') as $s) { - if($this->$s > 0) { - $style .= $s; - } - } - $this->SetFont('', $style); - } - - /** - * Output anchor link. - * @param string $url link URL - * @param string $name link name - * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. - * @access public - */ - public function addHtmlLink($url, $name, $fill=0) { - //Put a hyperlink - $this->SetTextColor(0, 0, 255); - $this->setStyle('u', true); - $this->Write($this->lasth, $name, $url, $fill); - $this->setStyle('u', false); - $this->SetTextColor(0); - } - - /** - * Returns an associative array (keys: R,G,B) from - * a hex html code (e.g. #3FE5AA). - * @param string $color hexadecimal html color [#rrggbb] - * @return array - * @access private - */ - private function convertColorHexToDec($color = "#000000"){ - $tbl_color = array(); - $tbl_color['R'] = hexdec(substr($color, 1, 2)); - $tbl_color['G'] = hexdec(substr($color, 3, 2)); - $tbl_color['B'] = hexdec(substr($color, 5, 2)); - return $tbl_color; - } - - /** - * Converts pixels to millimeters in 72 dpi. - * @param int $px pixels - * @return float millimeters - * @access private - */ - private function pixelsToMillimeters($px){ - return $px * 25.4 / 72; - } - - /** - * Reverse function for htmlentities. - * Convert entities in UTF-8. - * - * @param $text_to_convert Text to convert. - * @return string converted - */ - public function unhtmlentities($text_to_convert) { - return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); - } - - // ENCRYPTION METHODS ---------------------------------- - // SINCE 2.0.000 (2008-01-02) - /** - * Compute encryption key depending on object number where the encrypted data is stored - * @param int $n object number - * @since 2.0.000 (2008-01-02) - */ - protected function _objectkey($n) { - return substr($this->_md5_16($this->encryption_key.pack('VXxx',$n)),0,10); - } - - /** - * Put encryption on PDF document - * @since 2.0.000 (2008-01-02) - */ - protected function _putencryption() { - $this->_out('/Filter /Standard'); - $this->_out('/V 1'); - $this->_out('/R 2'); - $this->_out('/O ('.$this->_escape($this->Ovalue).')'); - $this->_out('/U ('.$this->_escape($this->Uvalue).')'); - $this->_out('/P '.$this->Pvalue); - } - - /** - * Returns the input text exrypted using RC4 algorithm and the specified key. - * RC4 is the standard encryption algorithm used in PDF format - * @param string $key encryption key - * @param String $text input text to be encrypted - * @return String encrypted text - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - protected function _RC4($key, $text) { - if ($this->last_rc4_key != $key) { - $k = str_repeat($key, 256/strlen($key)+1); - $rc4 = range(0,255); - $j = 0; - for ($i=0; $i<256; $i++) { - $t = $rc4[$i]; - $j = ($j + $t + ord($k{$i})) % 256; - $rc4[$i] = $rc4[$j]; - $rc4[$j] = $t; - } - $this->last_rc4_key = $key; - $this->last_rc4_key_c = $rc4; - } else { - $rc4 = $this->last_rc4_key_c; - } - $len = strlen($text); - $a = 0; - $b = 0; - $out = ''; - for ($i=0; $i<$len; $i++) { - $a = ($a+1)%256; - $t= $rc4[$a]; - $b = ($b+$t)%256; - $rc4[$a] = $rc4[$b]; - $rc4[$b] = $t; - $k = $rc4[($rc4[$a]+$rc4[$b])%256]; - $out.=chr(ord($text{$i}) ^ $k); - } - return $out; - } - - /** - * Encrypts a string using MD5 and returns it's value as a binary string. - * @param string $str input string - * @return String MD5 encrypted binary string - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - protected function _md5_16($str) { - return pack('H*',md5($str)); - } - - /** - * Compute O value (used for RC4 encryption) - * @param String $user_pass user password - * @param String $owner_pass user password - * @return String O value - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - protected function _Ovalue($user_pass, $owner_pass) { - $tmp = $this->_md5_16($owner_pass); - $owner_RC4_key = substr($tmp,0,5); - return $this->_RC4($owner_RC4_key, $user_pass); - } - - /** - * Compute U value (used for RC4 encryption) - * @return String U value - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - protected function _Uvalue() { - return $this->_RC4($this->encryption_key, $this->padding); - } - - /** - * Compute encryption key - * @param String $user_pass user password - * @param String $owner_pass user password - * @param String $protection protection type - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - protected function _generateencryptionkey($user_pass, $owner_pass, $protection) { - // Pad passwords - $user_pass = substr($user_pass.$this->padding,0,32); - $owner_pass = substr($owner_pass.$this->padding,0,32); - // Compute O value - $this->Ovalue = $this->_Ovalue($user_pass,$owner_pass); - // Compute encyption key - $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF"); - $this->encryption_key = substr($tmp,0,5); - // Compute U value - $this->Uvalue = $this->_Uvalue(); - // Compute P value - $this->Pvalue = -(($protection^255)+1); - } - - /** - * Set document protection - * The permission array is composed of values taken from the following ones: - * - copy: copy text and images to the clipboard - * - print: print the document - * - modify: modify it (except for annotations and forms) - * - annot-forms: add annotations and forms - * Remark: the protection against modification is for people who have the full Acrobat product. - * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access. - * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts. - * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms) - * @param String $user_pass user password. Empty by default. - * @param String $owner_pass owner password. If not specified, a random value is used. - * @since 2.0.000 (2008-01-02) - * @author Klemen Vodopivec - */ - public function SetProtection($permissions=array(),$user_pass='',$owner_pass=null) { - $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32); - $protection = 192; - foreach($permissions as $permission) { - if (!isset($options[$permission])) { - $this->Error('Incorrect permission: '.$permission); - } - $protection += $options[$permission]; - } - if ($owner_pass === null) { - $owner_pass = uniqid(rand()); - } - $this->encrypted = true; - $this->_generateencryptionkey($user_pass, $owner_pass, $protection); - } - - // END OF ENCRYPTION FUNCTIONS ------------------------- - - // START TRANSFORMATIONS SECTION ----------------------- - // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni - - /** - * Starts a 2D tranformation saving current graphic state. - * This function must be called before scaling, mirroring, translation, rotation and skewing. - * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function StartTransform() { - $this->_out('q'); - } - - /** - * Stops a 2D tranformation restoring previous graphic state. - * This function must be called after scaling, mirroring, translation, rotation and skewing. - * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function StopTransform() { - $this->_out('Q'); - } - - /** - * Horizontal Scaling. - * @param float $s_x scaling factor for width as percent. 0 is not allowed. - * @param int $x abscissa of the scaling center. Default is current x position - * @param int $y ordinate of the scaling center. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function ScaleX($s_x, $x='', $y=''){ - $this->Scale($s_x, 100, $x, $y); - } - - /** - * Vertical Scaling. - * @param float $s_y scaling factor for height as percent. 0 is not allowed. - * @param int $x abscissa of the scaling center. Default is current x position - * @param int $y ordinate of the scaling center. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function ScaleY($s_y, $x='', $y=''){ - $this->Scale(100, $s_y, $x, $y); - } - - /** - * Vertical and horizontal proportional Scaling. - * @param float $s scaling factor for width and height as percent. 0 is not allowed. - * @param int $x abscissa of the scaling center. Default is current x position - * @param int $y ordinate of the scaling center. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function ScaleXY($s, $x='', $y=''){ - $this->Scale($s, $s, $x, $y); - } - - /** - * Vertical and horizontal non-proportional Scaling. - * @param float $s_x scaling factor for width as percent. 0 is not allowed. - * @param float $s_y scaling factor for height as percent. 0 is not allowed. - * @param int $x abscissa of the scaling center. Default is current x position - * @param int $y ordinate of the scaling center. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function Scale($s_x, $s_y, $x='', $y=''){ - if($x === '') { - $x=$this->x; - } - if($y === '') { - $y=$this->y; - } - if($this->rtl) { - $x = $this->w - $x; - } - if($s_x == 0 OR $s_y == 0) - $this->Error('Please use values unequal to zero for Scaling'); - $y=($this->h-$y)*$this->k; - $x*=$this->k; - //calculate elements of transformation matrix - $s_x/=100; - $s_y/=100; - $tm[0]=$s_x; - $tm[1]=0; - $tm[2]=0; - $tm[3]=$s_y; - $tm[4]=$x*(1-$s_x); - $tm[5]=$y*(1-$s_y); - //scale the coordinate system - $this->Transform($tm); - } - - /** - * Horizontal Mirroring. - * @param int $x abscissa of the point. Default is current x position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function MirrorH($x=''){ - $this->Scale(-100, 100, $x); - } - - /** - * Verical Mirroring. - * @param int $y ordinate of the point. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function MirrorV($y=''){ - $this->Scale(100, -100, '', $y); - } - - /** - * Point reflection mirroring. - * @param int $x abscissa of the point. Default is current x position - * @param int $y ordinate of the point. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function MirrorP($x='',$y=''){ - $this->Scale(-100, -100, $x, $y); - } - - /** - * Reflection against a straight line through point (x, y) with the gradient angle (angle). - * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line). - * @param int $x abscissa of the point. Default is current x position - * @param int $y ordinate of the point. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function MirrorL($angle=0, $x='',$y=''){ - $this->Scale(-100, 100, $x, $y); - $this->Rotate(-2*($angle-90),$x,$y); - } - - /** - * Translate graphic object horizontally. - * @param int $t_x movement to the right - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function TranslateX($t_x){ - $this->Translate($t_x, 0); - } - - /** - * Translate graphic object vertically. - * @param int $t_y movement to the bottom - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function TranslateY($t_y){ - $this->Translate(0, $t_y, $x, $y); - } - - /** - * Translate graphic object horizontally and vertically. - * @param int $t_x movement to the right - * @param int $t_y movement to the bottom - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function Translate($t_x, $t_y){ - if($this->rtl) { - $t_x = -$t_x; - } - //calculate elements of transformation matrix - $tm[0]=1; - $tm[1]=0; - $tm[2]=0; - $tm[3]=1; - $tm[4]=$t_x*$this->k; - $tm[5]=-$t_y*$this->k; - //translate the coordinate system - $this->Transform($tm); - } - - /** - * Rotate object. - * @param float $angle angle in degrees for counter-clockwise rotation - * @param int $x abscissa of the rotation center. Default is current x position - * @param int $y ordinate of the rotation center. Default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function Rotate($angle, $x='', $y=''){ - if($x === '') { - $x=$this->x; - } - if($y === '') { - $y=$this->y; - } - if($this->rtl) { - $x = $this->w - $x; - $angle = -$angle; - } - $y=($this->h-$y)*$this->k; - $x*=$this->k; - //calculate elements of transformation matrix - $tm[0]=cos(deg2rad($angle)); - $tm[1]=sin(deg2rad($angle)); - $tm[2]=-$tm[1]; - $tm[3]=$tm[0]; - $tm[4]=$x+$tm[1]*$y-$tm[0]*$x; - $tm[5]=$y-$tm[0]*$y-$tm[1]*$x; - //rotate the coordinate system around ($x,$y) - $this->Transform($tm); - } - - /** - * Skew horizontally. - * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) - * @param int $x abscissa of the skewing center. default is current x position - * @param int $y ordinate of the skewing center. default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function SkewX($angle_x, $x='', $y=''){ - $this->Skew($angle_x, 0, $x, $y); - } - - /** - * Skew vertically. - * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) - * @param int $x abscissa of the skewing center. default is current x position - * @param int $y ordinate of the skewing center. default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function SkewY($angle_y, $x='', $y=''){ - $this->Skew(0, $angle_y, $x, $y); - } - - /** - * Skew. - * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) - * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) - * @param int $x abscissa of the skewing center. default is current x position - * @param int $y ordinate of the skewing center. default is current y position - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - public function Skew($angle_x, $angle_y, $x='', $y=''){ - if($x === '') { - $x=$this->x; - } - if($y === '') { - $y=$this->y; - } - if($this->rtl) { - $x = $this->w - $x; - $angle_x = -$angle_x; - } - if($angle_x <= -90 OR $angle_x >= 90 OR $angle_y <= -90 OR $angle_y >= 90) - $this->Error('Please use values between -90� and 90� for skewing'); - $x*=$this->k; - $y=($this->h-$y)*$this->k; - //calculate elements of transformation matrix - $tm[0]=1; - $tm[1]=tan(deg2rad($angle_y)); - $tm[2]=tan(deg2rad($angle_x)); - $tm[3]=1; - $tm[4]=-$tm[2]*$y; - $tm[5]=-$tm[1]*$x; - //skew the coordinate system - $this->Transform($tm); - } - - /** - * Apply graphic transformations. - * @since 2.1.000 (2008-01-07) - * @see StartTransform(), StopTransform() - */ - protected function Transform($tm){ - $this->_out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', $tm[0],$tm[1],$tm[2],$tm[3],$tm[4],$tm[5])); - } - - // END TRANSFORMATIONS SECTION ------------------------- - - // START GRAPHIC FUNCTIONS SECTION --------------------- - // The following section is based on the code provided by David Hernandez Sanz - - /** - * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page. - * @param float $width The width. - * @since 1.0 - * @see Line(), Rect(), Cell(), MultiCell() - */ - public function SetLineWidth($width) { - //Set line width - $this->LineWidth=$width; - if($this->page>0) { - $this->_out(sprintf('%.2f w',$width*$this->k)); - } - } - - /** - * Returns the current the line width. - * @return int Line width - * @since 2.1.000 (2008-01-07) - * @see Line(), SetLineWidth() - */ - public function GetLineWidth() { - return $this->LineWidth; - } - - /** - * Set line style. - * - * @param array $style Line style. Array with keys among the following: - * <ul> - * <li>width (float): Width of the line in user units.</li> - * <li>cap (string): Type of cap to put on the line. Possible values are: - * butt, round, square. The difference between "square" and "butt" is that - * "square" projects a flat end past the end of the line.</li> - * <li>join (string): Type of join. Possible values are: miter, round, - * bevel.</li> - * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with - * series of length values, which are the lengths of the on and off dashes. - * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on, - * 1 off, 2 on, 1 off, ...</li> - * <li>phase (integer): Modifier on the dash pattern which is used to shift - * the point at which the pattern starts.</li> - * <li>color (array): Draw color. Format: array(red, green, blue).</li> - * </ul> - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function SetLineStyle($style) { - extract($style); - if (isset($width)) { - $width_prev = $this->LineWidth; - $this->SetLineWidth($width); - $this->LineWidth = $width_prev; - } - if (isset($cap)) { - $ca = array("butt" => 0, "round"=> 1, "square" => 2); - if (isset($ca[$cap])) { - $this->_out($ca[$cap] . " J"); - } - } - if (isset($join)) { - $ja = array("miter" => 0, "round" => 1, "bevel" => 2); - if (isset($ja[$join])) { - $this->_out($ja[$join] . " j"); - } - } - if (isset($dash)) { - $dash_string = ""; - if ($dash) { - // phpMyAdmin change - if (preg_match("/^.+,/", $dash)) { - $tab = explode(",", $dash); - } else { - $tab = array($dash); - } - $dash_string = ""; - foreach ($tab as $i => $v) { - if ($i) { - $dash_string .= " "; - } - $dash_string .= sprintf("%.2f", $v); - } - } - if (!isset($phase) OR !$dash) { - $phase = 0; - } - $this->_out(sprintf("[%s] %.2f d", $dash_string, $phase)); - } - if (isset($color)) { - list($r, $g, $b) = $color; - $this->SetDrawColor($r, $g, $b); - } - } - - /* - * Set a draw point. - * @param float $x Abscissa of point. - * @param float $y Ordinate of point. - * @access private - * @since 2.1.000 (2008-01-08) - */ - protected function _outPoint($x, $y) { - if($this->rtl) { - $x = $this->w - $x; - } - $this->_out(sprintf("%.2f %.2f m", $x * $this->k, ($this->h - $y) * $this->k)); - } - - /* - * Draws a line from last draw point. - * @param float $x Abscissa of end point. - * @param float $y Ordinate of end point. - * @access private - * @since 2.1.000 (2008-01-08) - */ - protected function _outLine($x, $y) { - if($this->rtl) { - $x = $this->w - $x; - } - $this->_out(sprintf("%.2f %.2f l", $x * $this->k, ($this->h - $y) * $this->k)); - } - - /** - * Draws a rectangle. - * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). - * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). - * @param float $w Width. - * @param float $h Height. - * @param string $op options - * @access protected - * @since 2.1.000 (2008-01-08) - */ - protected function _outRect($x, $y, $w, $h, $op) { - if($this->rtl) { - $x = $this->w - $x - $w; - } - $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op)); - } - - /* - * Draws a Bezier curve from last draw point. - * The Bezier curve is a tangent to the line between the control points at either end of the curve. - * @param float $x1 Abscissa of control point 1. - * @param float $y1 Ordinate of control point 1. - * @param float $x2 Abscissa of control point 2. - * @param float $y2 Ordinate of control point 2. - * @param float $x3 Abscissa of end point. - * @param float $y3 Ordinate of end point. - * @access private - * @since 2.1.000 (2008-01-08) - */ - protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { - if($this->rtl) { - $x1 = $this->w - $x1; - $x2 = $this->w - $x2; - $x3 = $this->w - $x3; - } - $this->_out(sprintf("%.2f %.2f %.2f %.2f %.2f %.2f c", $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); - } - - /** - * Draws a line between two points. - * @param float $x1 Abscissa of first point. - * @param float $y1 Ordinate of first point. - * @param float $x2 Abscissa of second point. - * @param float $y2 Ordinate of second point. - * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @access public - * @since 1.0 - * @see SetLineWidth(), SetDrawColor(), SetLineStyle() - */ - public function Line($x1, $y1, $x2, $y2, $style = array()) { - if ($style) { - $this->SetLineStyle($style); - } - $this->_outPoint($x1, $y1); - $this->_outLine($x2, $y2); - $this->_out(" S"); - } - - /** - * Draws a rectangle. - * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). - * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). - * @param float $w Width. - * @param float $h Height. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $border_style Border style of rectangle. Array with keys among the following: - * <ul> - * <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li> - * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li> - * </ul> - * If a key is not present or is null, not draws the border. Default value: default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @since 1.0 - * @see SetLineStyle() - */ - public function Rect($x, $y, $w, $h, $style='', $border_style = array(), $fill_color = array()) { - if (!(false === strpos($style, "F")) AND $fill_color) { - list($r, $g, $b) = $fill_color; - $this->SetFillColor($r, $g, $b); - } - switch ($style) { - case "F": { - $op='f'; - $border_style = array(); - $this->_outRect($x, $y, $w, $h, $op); - break; - } - case "DF": - case "FD": { - if (!$border_style OR isset($border_style["all"])) { - $op='B'; - if (isset($border_style["all"])) { - $this->SetLineStyle($border_style["all"]); - $border_style = array(); - } - } else { - $op='f'; - } - $this->_outRect($x, $y, $w, $h, $op); - break; - } - default: { - $op='S'; - if (!$border_style OR isset($border_style["all"])) { - if (isset($border_style["all"]) && $border_style["all"]) { - $this->SetLineStyle($border_style["all"]); - $border_style = array(); - } - $this->_outRect($x, $y, $w, $h, $op); - } - break; - } - } - if ($border_style) { - $border_style2 = array(); - foreach ($border_style as $line => $value) { - $lenght = strlen($line); - for ($i = 0; $i < $lenght; $i++) { - $border_style2[$line[$i]] = $value; - } - } - $border_style = $border_style2; - if (isset($border_style["L"]) && $border_style["L"]) { - $this->Line($x, $y, $x, $y + $h, $border_style["L"]); - } - if (isset($border_style["T"]) && $border_style["T"]) { - $this->Line($x, $y, $x + $w, $y, $border_style["T"]); - } - if (isset($border_style["R"]) && $border_style["R"]) { - $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style["R"]); - } - if (isset($border_style["B"]) && $border_style["B"]) { - $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style["B"]); - } - } - } - - /** - * Draws a Bezier curve. - * The Bezier curve is a tangent to the line between the control points at - * either end of the curve. - * @param float $x0 Abscissa of start point. - * @param float $y0 Ordinate of start point. - * @param float $x1 Abscissa of control point 1. - * @param float $y1 Ordinate of control point 1. - * @param float $x2 Abscissa of control point 2. - * @param float $y2 Ordinate of control point 2. - * @param float $x3 Abscissa of end point. - * @param float $y3 Ordinate of end point. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @see SetLineStyle() - * @since 2.1.000 (2008-01-08) - */ - public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style = "", $line_style = array(), $fill_color = array()) { - if (!(false === strpos($style, "F")) AND $fill_color) { - list($r, $g, $b) = $fill_color; - $this->SetFillColor($r, $g, $b); - } - switch ($style) { - case "F": { - $op = "f"; - $line_style = array(); - break; - } - case "FD": - case "DF": { - $op = "B"; - break; - } - default: { - $op = "S"; - break; - } - } - if ($line_style) { - $this->SetLineStyle($line_style); - } - $this->_outPoint($x0, $y0); - $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); - $this->_out($op); - } - - /** - * Draws an ellipse. - * An ellipse is formed from n Bezier curves. - * @param float $x0 Abscissa of center point. - * @param float $y0 Ordinate of center point. - * @param float $rx Horizontal radius. - * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0. - * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. - * @param float $astart: Angle start of draw line. Default value: 0. - * @param float $afinish: Angle finish of draw line. Default value: 360. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * <li>C: Draw close.</li> - * </ul> - * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @param integer $nc Number of curves used in ellipse. Default value: 8. - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function Ellipse($x0, $y0, $rx, $ry = 0, $angle = 0, $astart = 0, $afinish = 360, $style = "", $line_style = array(), $fill_color = array(), $nc = 8) { - if ($angle) { - $this->StartTransform(); - $this->Rotate($angle, $x0, $y0); - $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc); - $this->StopTransform(); - return; - } - if ($rx) { - if (!(false === strpos($style, "F")) AND $fill_color) { - list($r, $g, $b) = $fill_color; - $this->SetFillColor($r, $g, $b); - } - switch ($style) { - case "F": { - $op = "f"; - $line_style = array(); - break; - } - case "FD": - case "DF": { - $op = "B"; - break; - } - case "C": { - $op = "s"; // Small "s" signifies closing the path as well - break; - } - default: { - $op = "S"; - break; - } - } - if ($line_style) { - $this->SetLineStyle($line_style); - } - if (!$ry) { - $ry = $rx; - } - $rx *= $this->k; - $ry *= $this->k; - if ($nc < 2){ - $nc = 2; - } - $astart = deg2rad((float) $astart); - $afinish = deg2rad((float) $afinish); - $total_angle = $afinish - $astart; - $dt = $total_angle / $nc; - $dtm = $dt/3; - $x0 *= $this->k; - $y0 = ($this->h - $y0) * $this->k; - $t1 = $astart; - $a0 = $x0 + ($rx * cos($t1)); - $b0 = $y0 + ($ry * sin($t1)); - $c0 = -$rx * sin($t1); - $d0 = $ry * cos($t1); - $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k)); - for ($i = 1; $i <= $nc; $i++) { - // Draw this bit of the total curve - $t1 = ($i * $dt) + $astart; - $a1 = $x0 + ($rx * cos($t1)); - $b1 = $y0 + ($ry * sin($t1)); - $c1 = -$rx * sin($t1); - $d1 = $ry * cos($t1); - $this->_outCurve(($a0 + ($c0 * $dtm)) / $this->k, $this->h - (($b0 + ($d0 * $dtm)) / $this->k), ($a1 - ($c1 * $dtm)) / $this->k, $this->h - (($b1 - ($d1 * $dtm)) / $this->k), $a1 / $this->k, $this->h - ($b1 / $this->k)); - $a0 = $a1; - $b0 = $b1; - $c0 = $c1; - $d0 = $d1; - } - $this->_out($op); - } - } - - /** - * Draws a circle. - * A circle is formed from n Bezier curves. - * @param float $x0 Abscissa of center point. - * @param float $y0 Ordinate of center point. - * @param float $r Radius. - * @param float $astart: Angle start of draw line. Default value: 0. - * @param float $afinish: Angle finish of draw line. Default value: 360. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * <li>C: Draw close.</li> - * </ul> - * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @param integer $nc Number of curves used in circle. Default value: 8. - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function Circle($x0, $y0, $r, $astart = 0, $afinish = 360, $style = "", $line_style = array(), $fill_color = array(), $nc = 8) { - $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc); - } - - /** - * Draws a polygon. - * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $line_style Line style of polygon. Array with keys among the following: - * <ul> - * <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li> - * <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li> - * </ul> - * If a key is not present or is null, not draws the line. Default value is default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function Polygon($p, $style = "", $line_style = array(), $fill_color = array()) { - $np = count($p) / 2; - if (!(false === strpos($style, "F")) AND $fill_color) { - list($r, $g, $b) = $fill_color; - $this->SetFillColor($r, $g, $b); - } - switch ($style) { - case "F": { - $line_style = array(); - $op = "f"; - break; - } - case "FD": - case "DF": { - $op = "B"; - break; - } - default: { - $op = "S"; - break; - } - } - $draw = true; - if ($line_style) { - if (isset($line_style["all"])) { - $this->SetLineStyle($line_style["all"]); - } - else { // 0 .. (np - 1), op = {B, S} - $draw = false; - if ("B" == $op) { - $op = "f"; - $this->_outPoint($p[0], $p[1]); - for ($i = 2; $i < ($np * 2); $i = $i + 2) { - $this->_outLine($p[$i], $p[$i + 1]); - } - $this->_outLine($p[0], $p[1]); - $this->_out($op); - } - $p[$np * 2] = $p[0]; - $p[($np * 2) + 1] = $p[1]; - for ($i = 0; $i < $np; $i++) { - if (isset($line_style[$i])) { - $this->Line($p[$i * 2], $p[($i * 2) + 1], $p[($i * 2) + 2], $p[($i * 2) + 3], $line_style[$i]); - } - } - } - } - if ($draw) { - $this->_outPoint($p[0], $p[1]); - for ($i = 2; $i < ($np * 2); $i = $i + 2) { - $this->_outLine($p[$i], $p[$i + 1]); - } - $this->_outLine($p[0], $p[1]); - $this->_out($op); - } - } - - /** - * Draws a regular polygon. - * @param float $x0 Abscissa of center point. - * @param float $y0 Ordinate of center point. - * @param float $r: Radius of inscribed circle. - * @param integer $ns Number of sides. - * @param float $angle Angle oriented (anti-clockwise). Default value: 0. - * @param boolean $draw_circle Draw inscribed circle or not. Default value: false. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $line_style Line style of polygon sides. Array with keys among the following: - * <ul> - * <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li> - * <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> - * </ul> - * If a key is not present or is null, not draws the side. Default value is default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function RegularPolygon($x0, $y0, $r, $ns, $angle = 0, $draw_circle = false, $style = "", $line_style = array(), $fill_color = array(), $circle_style = "", $circle_outLine_style = array(), $circle_fill_color = array()) { - if (3 > $ns) { - $ns = 3; - } - if ($draw_circle) { - $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); - } - $p = array(); - for ($i = 0; $i < $ns; $i++) { - $a = $angle + ($i * 360 / $ns); - $a_rad = deg2rad((float) $a); - $p[] = $x0 + ($r * sin($a_rad)); - $p[] = $y0 + ($r * cos($a_rad)); - } - $this->Polygon($p, $style, $line_style, $fill_color); - } - - /** - * Draws a star polygon - * @param float $x0 Abscissa of center point. - * @param float $y0 Ordinate of center point. - * @param float $r Radius of inscribed circle. - * @param integer $nv Number of vertices. - * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon). - * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. - * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false. - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $line_style Line style of polygon sides. Array with keys among the following: - * <ul> - * <li>all: Line style of all sides. Array like for - * {@link SetLineStyle SetLineStyle}.</li> - * <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> - * </ul> - * If a key is not present or is null, not draws the side. Default value is default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle = 0, $draw_circle = false, $style = "", $line_style = array(), $fill_color = array(), $circle_style = "", $circle_outLine_style = array(), $circle_fill_color = array()) { - if (2 > $nv) { - $nv = 2; - } - if ($draw_circle) { - $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); - } - $p2 = array(); - $visited = array(); - for ($i = 0; $i < $nv; $i++) { - $a = $angle + ($i * 360 / $nv); - $a_rad = deg2rad((float) $a); - $p2[] = $x0 + ($r * sin($a_rad)); - $p2[] = $y0 + ($r * cos($a_rad)); - $visited[] = false; - } - $p = array(); - $i = 0; - do { - $p[] = $p2[$i * 2]; - $p[] = $p2[($i * 2) + 1]; - $visited[$i] = true; - $i += $ng; - $i %= $nv; - } while (!$visited[$i]); - $this->Polygon($p, $style, $line_style, $fill_color); - } - - /** - * Draws a rounded rectangle. - * @param float $x Abscissa of upper-left corner. - * @param float $y Ordinate of upper-left corner. - * @param float $w Width. - * @param float $h Height. - * @param float $r Radius of the rounded corners. - * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left. Default value: all rounded corner ("1111"). - * @param string $style Style of rendering. Possible values are: - * <ul> - * <li>D or empty string: Draw (default).</li> - * <li>F: Fill.</li> - * <li>DF or FD: Draw and fill.</li> - * </ul> - * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). - * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). - * @access public - * @since 2.1.000 (2008-01-08) - */ - public function RoundedRect($x, $y, $w, $h, $r, $round_corner = "1111", $style = "", $border_style = array(), $fill_color = array()) { - if ("0000" == $round_corner) { // Not rounded - $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); - } else { // Rounded - if (!(false === strpos($style, "F")) AND $fill_color) { - list($red, $g, $b) = $fill_color; - $this->SetFillColor($red, $g, $b); - } - switch ($style) { - case "F": { - $border_style = array(); - $op = "f"; - break; - } - case "FD": - case "DF": { - $op = "B"; - break; - } - default: { - $op = "S"; - break; - } - } - if ($border_style) { - $this->SetLineStyle($border_style); - } - $MyArc = 4 / 3 * (sqrt(2) - 1); - $this->_outPoint($x + $r, $y); - $xc = $x + $w - $r; - $yc = $y + $r; - $this->_outLine($xc, $y); - if ($round_corner[0]) { - $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc); - } else { - $this->_outLine($x + $w, $y); - } - $xc = $x + $w - $r; - $yc = $y + $h - $r; - $this->_outLine($x + $w, $yc); - if ($round_corner[1]) { - $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r); - } else { - $this->_outLine($x + $w, $y + $h); - } - $xc = $x + $r; - $yc = $y + $h - $r; - $this->_outLine($xc, $y + $h); - if ($round_corner[2]) { - $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc); - } else { - $this->_outLine($x, $y + $h); - } - $xc = $x + $r; - $yc = $y + $r; - $this->_outLine($x, $yc); - if ($round_corner[3]) { - $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r); - } else { - $this->_outLine($x, $y); - $this->_outLine($x + $r, $y); - } - $this->_out($op); - } - } - - // END GRAPHIC FUNCTIONS SECTION ----------------------- - - // BIDIRECTIONAL TEXT SECTION -------------------------- - - /** - * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). - * @param string $str string to manipulate. - * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR - * @return string - * @author Nicola Asuni - * @since 2.1.000 (2008-01-08) - */ - protected function utf8StrRev($str, $setbom=false, $forcertl=false) { - global $unicode,$unicode_mirror, $unicode_arlet; - require_once(dirname(__FILE__).'/unicode_data.php'); - - // paragraph embedding level - $pel = 0; - // max level - $maxlevel = 0; - - // check if string contains arabic text - if (preg_match(K_RE_PATTERN_ARABIC, $str)) { - $arabic = true; - } else { - $arabic = false; - } - - // check if string contains RTL text - if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) { - return $this->UTF8ToUTF16BE($str, false); - } - - // convert string to array of unicode chars - $ta = $this->UTF8StringToArray($str); - // get number of chars - $numchars = count($ta); - - if ($forcertl == 'R') { - $pel = 1; - } elseif ($forcertl == 'L') { - $pel = 0; - } else { - // P2. In each paragraph, find the first character of type L, AL, or R. - // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero. - for ($i=0; $i < $numchars; $i++) { - $type = $unicode[$ta[$i]]; - if ($type == 'L') { - $pel = 0; - break; - } elseif (($type == 'AL') OR ($type == 'R')) { - $pel = 1; - break; - } - } - } - - // Current Embedding Level - $cel = $pel; - // directional override status - $dos = 'N'; - $remember = array(); - // start-of-level-run - $sor = $pel % 2 ? 'R' : 'L'; - $eor = $sor; - - //$levels = array(array('level' => $cel, 'sor' => $sor, 'eor' => '', 'chars' => array())); - //$current_level = &$levels[count( $levels )-1]; - - // Array of characters data - $chardata = Array(); - - // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase. - // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached. - for ($i=0; $i < $numchars; $i++) { - if ($ta[$i] == K_RLE) { - // X2. With each RLE, compute the least greater odd embedding level. - // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. - // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. - $next_level = $cel + ($cel % 2) + 1; - if ($next_level < 62) { - $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos); - $cel = $next_level; - $dos = 'N'; - $sor = $eor; - $eor = $cel % 2 ? 'R' : 'L'; - } - } elseif ($ta[$i] == K_LRE) { - // X3. With each LRE, compute the least greater even embedding level. - // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. - // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. - $next_level = $cel + 2 - ($cel % 2); - if ( $next_level < 62 ) { - $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos); - $cel = $next_level; - $dos = 'N'; - $sor = $eor; - $eor = $cel % 2 ? 'R' : 'L'; - } - } elseif ($ta[$i] == K_RLO) { - // X4. With each RLO, compute the least greater odd embedding level. - // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left. - // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. - $next_level = $cel + ($cel % 2) + 1; - if ($next_level < 62) { - $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos); - $cel = $next_level; - $dos = 'R'; - $sor = $eor; - $eor = $cel % 2 ? 'R' : 'L'; - } - } elseif ($ta[$i] == K_LRO) { - // X5. With each LRO, compute the least greater even embedding level. - // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right. - // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. - $next_level = $cel + 2 - ($cel % 2); - if ( $next_level < 62 ) { - $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos); - $cel = $next_level; - $dos = 'L'; - $sor = $eor; - $eor = $cel % 2 ? 'R' : 'L'; - } - } elseif ($ta[$i] == K_PDF) { - // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override. - if (count($remember)) { - $last = count($remember ) - 1; - if (($remember[$last]['num'] == K_RLE) OR - ($remember[$last]['num'] == K_LRE) OR - ($remember[$last]['num'] == K_RLO) OR - ($remember[$last]['num'] == K_LRO)) { - $match = array_pop($remember); - $cel = $match['cel']; - $dos = $match['dos']; - $sor = $eor; - $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L'; - } - } - } elseif (($ta[$i] != K_RLE) AND - ($ta[$i] != K_LRE) AND - ($ta[$i] != K_RLO) AND - ($ta[$i] != K_LRO) AND - ($ta[$i] != K_PDF)) { - // X6. For all types besides RLE, LRE, RLO, LRO, and PDF: - // a. Set the level of the current character to the current embedding level. - // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status. - if ($dos != 'N') { - $chardir = $dos; - } else { - $chardir = $unicode[$ta[$i]]; - } - // stores string characters and other information - $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor); - } - } // end for each char - - // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding. - // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes. - // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the “other” run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L. - - // 3.3.3 Resolving Weak Types - // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used. - // Nonspacing marks are now resolved based on the previous characters. - $numchars = count($chardata); - - // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor. - $prevlevel = -1; // track level changes - $levcount = 0; // counts consecutive chars at the same level - for ($i=0; $i < $numchars; $i++) { - if ($chardata[$i]['type'] == 'NSM') { - if ($levcount) { - $chardata[$i]['type'] = $chardata[$i]['sor']; - } elseif ($i > 0) { - $chardata[$i]['type'] = $chardata[($i-1)]['type']; - } - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if ($chardata[$i]['char'] == 'EN') { - for ($j=$levcount; $j >= 0; $j--) { - if ($chardata[$j]['type'] == 'AL') { - $chardata[$i]['type'] = 'AN'; - } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) { - break; - } - } - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // W3. Change all ALs to R. - for ($i=0; $i < $numchars; $i++) { - if ($chardata[$i]['type'] == 'AL') { - $chardata[$i]['type'] = 'R'; - } - } - - // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { - if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { - $chardata[$i]['type'] = 'EN'; - } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { - $chardata[$i]['type'] = 'EN'; - } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) { - $chardata[$i]['type'] = 'AN'; - } - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if($chardata[$i]['type'] == 'ET') { - if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) { - $chardata[$i]['type'] = 'EN'; - } else { - $j = $i+1; - while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) { - if ($chardata[$j]['type'] == 'EN') { - $chardata[$i]['type'] = 'EN'; - break; - } elseif ($chardata[$j]['type'] != 'ET') { - break; - } - $j++; - } - } - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // W6. Otherwise, separators and terminators change to Other Neutral. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) { - $chardata[$i]['type'] = 'ON'; - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if ($chardata[$i]['char'] == 'EN') { - for ($j=$levcount; $j >= 0; $j--) { - if ($chardata[$j]['type'] == 'L') { - $chardata[$i]['type'] = 'L'; - } elseif ($chardata[$j]['type'] == 'R') { - break; - } - } - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries. - $prevlevel = -1; - $levcount = 0; - for ($i=0; $i < $numchars; $i++) { - if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { - if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { - $chardata[$i]['type'] = 'L'; - } elseif (($chardata[$i]['type'] == 'N') AND - (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND - (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { - $chardata[$i]['type'] = 'R'; - } elseif ($chardata[$i]['type'] == 'N') { - // N2. Any remaining neutrals take the embedding direction - $chardata[$i]['type'] = $chardata[$i]['sor']; - } - } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { - // first char - if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { - $chardata[$i]['type'] = 'L'; - } elseif (($chardata[$i]['type'] == 'N') AND - (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND - (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { - $chardata[$i]['type'] = 'R'; - } elseif ($chardata[$i]['type'] == 'N') { - // N2. Any remaining neutrals take the embedding direction - $chardata[$i]['type'] = $chardata[$i]['sor']; - } - } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) { - //last char - if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) { - $chardata[$i]['type'] = 'L'; - } elseif (($chardata[$i]['type'] == 'N') AND - (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND - (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) { - $chardata[$i]['type'] = 'R'; - } elseif ($chardata[$i]['type'] == 'N') { - // N2. Any remaining neutrals take the embedding direction - $chardata[$i]['type'] = $chardata[$i]['sor']; - } - } elseif ($chardata[$i]['type'] == 'N') { - // N2. Any remaining neutrals take the embedding direction - $chardata[$i]['type'] = $chardata[$i]['sor']; - } - if ($chardata[$i]['level'] != $prevlevel) { - $levcount = 0; - } else { - $levcount++; - } - $prevlevel = $chardata[$i]['level']; - } - - // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels. - // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level. - for ($i=0; $i < $numchars; $i++) { - $odd = $chardata[$i]['level'] % 2; - if ($odd) { - if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){ - $chardata[$i]['level'] += 1; - } - } else { - if ($chardata[$i]['type'] == 'R') { - $chardata[$i]['level'] += 1; - } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){ - $chardata[$i]['level'] += 2; - } - } - $maxlevel = max($chardata[$i]['level'],$maxlevel); - } - - // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level: - // 1. Segment separators, - // 2. Paragraph separators, - // 3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and - // 4. Any sequence of white space characters at the end of the line. - for ($i=0; $i < $numchars; $i++) { - if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) { - $chardata[$i]['level'] = $pel; - } elseif ($chardata[$i]['type'] == 'WS') { - $j = $i+1; - while ($j < $numchars) { - if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR - (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) { - $chardata[$i]['level'] = $pel;; - break; - } elseif ($chardata[$j]['type'] != 'WS') { - break; - } - $j++; - } - } - } - - // Arabic Shaping - // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run. - if ($arabic) { - for ($i=0; $i < $numchars; $i++) { - if ($unicode[$chardata[$i]['char']] == 'AL') { - if (($i > 0) AND (($i+1) < $numchars) AND - ($unicode[$chardata[($i-1)]['char']] == 'AL') AND - ($unicode[$chardata[($i+1)]['char']] == 'AL') AND - ($chardata[($i-1)]['type'] == $chardata[$i]['type']) AND - ($chardata[($i+1)]['type'] == $chardata[$i]['type'])) { - // medial - if (isset($unicode_arlet[$chardata[$i]['char']][3])) { - $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][3]; - } - } elseif ((($i+1) < $numchars) AND - ($unicode[$chardata[($i+1)]['char']] == 'AL') AND - ($chardata[($i+1)]['type'] == $chardata[$i]['type'])) { - // initial - if (isset($unicode_arlet[$chardata[$i]['char']][2])) { - $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][2]; - } - } elseif (($i > 0) AND - ($unicode[$chardata[($i-1)]['char']] == 'AL') AND - ($chardata[($i-1)]['type'] == $chardata[$i]['type'])) { - // final - if (isset($unicode_arlet[$chardata[$i]['char']][1])) { - $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][1]; - } - } elseif (isset($unicode_arlet[$chardata[$i]['char']][0])) { - // isolated - $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][0]; - } - } - } - } - - // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher. - for ($j=$maxlevel; $j > 0; $j--) { - $ordarray = Array(); - $revarr = Array(); - $onlevel = false; - for ($i=0; $i < $numchars; $i++) { - if ($chardata[$i]['level'] >= $j) { - $onlevel = true; - if (isset($unicode_mirror[$chardata[$i]['char']])) { - // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true. - $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']]; - } - $revarr[] = $chardata[$i]; - } else { - if($onlevel) { - $revarr = array_reverse($revarr); - $ordarray = array_merge($ordarray, $revarr); - $revarr = Array(); - $onlevel = false; - } - $ordarray[] = $chardata[$i]; - } - } - if($onlevel) { - $revarr = array_reverse($revarr); - $ordarray = array_merge($ordarray, $revarr); - } - $chardata = $ordarray; - } - - $ordarray = array(); - for ($i=0; $i < $numchars; $i++) { - $ordarray[] = $chardata[$i]['char']; - } - - return $this->arrUTF8ToUTF16BE($ordarray, $setbom); - } - - // END OF BIDIRECTIONAL TEXT SECTION ------------------- - - /* - * Adds a bookmark. - * @param string $txt bookmark description. - * @param int $level bookmark level. - * @param float $y Ordinate of the boorkmark position (default = -1 = current position). - * @access public - * @author Olivier Plathey, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function Bookmark($txt, $level=0, $y=-1) { - if($y == -1) { - $y = $this->GetY(); - } - $this->outlines[]=array('t'=>$txt,'l'=>$level,'y'=>$y,'p'=>$this->PageNo()); - } - - /* - * Create a bookmark PDF string. - * @access private - * @author Olivier Plathey, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - private function _putbookmarks() { - $nb = count($this->outlines); - if($nb == 0) { - return; - } - $lru = array(); - $level = 0; - foreach($this->outlines as $i=>$o) { - if($o['l'] > 0) { - $parent = $lru[$o['l'] - 1]; - //Set parent and last pointers - $this->outlines[$i]['parent'] = $parent; - $this->outlines[$parent]['last'] = $i; - if($o['l'] > $level) { - //Level increasing: set first pointer - $this->outlines[$parent]['first'] = $i; - } - } else { - $this->outlines[$i]['parent']=$nb; - } - if($o['l']<=$level and $i>0) { - //Set prev and next pointers - $prev = $lru[$o['l']]; - $this->outlines[$prev]['next'] = $i; - $this->outlines[$i]['prev'] = $prev; - } - $lru[$o['l']] = $i; - $level = $o['l']; - } - //Outline items - $n = $this->n+1; - foreach($this->outlines as $i=>$o) { - $this->_newobj(); - $this->_out('<</Title '.$this->_textstring($o['t'])); - $this->_out('/Parent '.($n+$o['parent']).' 0 R'); - if(isset($o['prev'])) - $this->_out('/Prev '.($n+$o['prev']).' 0 R'); - if(isset($o['next'])) - $this->_out('/Next '.($n+$o['next']).' 0 R'); - if(isset($o['first'])) - $this->_out('/First '.($n+$o['first']).' 0 R'); - if(isset($o['last'])) - $this->_out('/Last '.($n+$o['last']).' 0 R'); - $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]',1+2*$o['p'],($this->h-$o['y'])*$this->k)); - $this->_out('/Count 0>>'); - $this->_out('endobj'); - } - //Outline root - $this->_newobj(); - $this->OutlineRoot=$this->n; - $this->_out('<</Type /Outlines /First '.$n.' 0 R'); - $this->_out('/Last '.($n+$lru[0]).' 0 R>>'); - $this->_out('endobj'); - } - - // --- JAVASCRIPT - FORMS ------------------------------ - - /* - * Adds a javascript - * @access public - * @author Johannes Güntert, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function IncludeJS($script) { - $this->javascript .= $script; - } - - /* - * Create a javascript PDF string. - * @access private - * @author Johannes Güntert, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - private function _putjavascript() { - if (empty($this->javascript)) { - return; - } - $this->_newobj(); - $this->n_js = $this->n; - $this->_out('<<'); - $this->_out('/Names [(EmbeddedJS) '.($this->n+1).' 0 R ]'); - $this->_out('>>'); - $this->_out('endobj'); - $this->_newobj(); - $this->_out('<<'); - $this->_out('/S /JavaScript'); - $this->_out('/JS '.$this->_textstring($this->javascript)); - $this->_out('>>'); - $this->_out('endobj'); - } - - /* - * Convert color to javascript color. - * @param string $color color name or #RRGGBB - * @access private - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - private function _JScolor($color) { - static $aColors = array('transparent','black','white','red','green','blue','cyan','magenta','yellow','dkGray','gray','ltGray'); - if(substr($color,0,1) == '#') { - return sprintf("['RGB',%.3f,%.3f,%.3f]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255); - } - if(!in_array($color,$aColors)) { - $this->Error('Invalid color: '.$color); - } - return 'color.'.$color; - } - - /* - * Adds a javascript form field. - * @param string $type field type - * @param string $name field name - * @param int $x horizontal position - * @param int $y vertical position - * @param int $w width - * @param int $h height - * @param string $prop properties - * @access private - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - private function _addfield($type, $name, $x, $y, $w, $h, $prop) { - $k = $this->k; - $this->javascript .= sprintf("f=addField('%s','%s',%d,[%.2f,%.2f,%.2f,%.2f]);",$name,$type,$this->PageNo()-1,$x*$k,($this->h-$y)*$k+1,($x+$w)*$k,($this->h-$y-$h)*$k+1); - $this->javascript .= 'f.textSize='.$this->FontSizePt.';'; - if(isset($prop['value'])) { - $this->javascript .= "f.value='".addslashes($prop['value'])."';"; - } - if(isset($prop['TextColor'])) { - $this->javascript .= 'f.textColor='.$this->_JScolor($prop['TextColor']).';'; - } - if(isset($prop['FillColor'])) { - $this->javascript .= 'f.fillColor='.$this->_JScolor($prop['FillColor']).';'; - } - if(isset($prop['BorderColor'])) { - $this->javascript .= 'f.strokeColor='.$this->_JScolor($prop['BorderColor']).';'; - } - if(isset($prop['BorderStyle'])) { - $this->javascript .= "f.borderStyle='".$prop['BorderStyle']."';"; - } - $this->x+=$w; - } - - /* - * Creates a text field - * @param string $name field name - * @param int $w width - * @param int $h height - * @param string $prop properties. The value property allows to set the initial value. The multiline property allows to define the field as multiline. - * @access public - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function TextField($name, $w, $h, $prop=array()) { - $this->_addfield('text',$name,$this->x,$this->y,$w,$h,$prop); - if(isset($prop['multiline']) AND $prop['multiline']) { - $this->javascript .= 'f.multiline=true;'; - } - } - - /* - * Creates a Combo-box field - * @param string $name field name - * @param int $w width - * @param int $h height - * @param array $values array containing the list of values. - * @param string $prop properties. - * @access public - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function ComboBox($name, $w, $h, $values, $prop=array()) { - $this->_addfield('combobox',$name,$this->x,$this->y,$w,$h,$prop); - $s = ''; - foreach($values as $value) { - $s .= "'".addslashes($value)."',"; - } - $this->javascript .= 'f.setItems(['.substr($s,0,-1).']);'; - } - - /* - * Creates a CheckBox field - * @param string $name field name - * @param int $w width - * @param boolean $checked define the initial state (default = false). - * @param string $prop properties. - * @access public - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function CheckBox($name, $w, $checked=false, $prop=array()) { - $prop['value'] = ($checked ? 'Yes' : 'Off'); - if(!isset($prop['BorderColor'])) { - $prop['BorderColor']='black'; - } - $this->_addfield('checkbox',$name,$this->x,$this->y,$w,$w,$prop); - } - - /* - * Creates a button field - * @param string $name field name - * @param int $w width - * @param int $h height - * @param string $caption caption. - * @param string $action action triggered by the button (JavaScript code). - * @param string $prop properties. - * @access public - * @author Denis Van Nuffelen, Nicola Asuni - * @since 2.1.002 (2008-02-12) - */ - public function Button($name, $w, $h, $caption, $action, $prop=array()) { - if(!isset($prop['BorderColor'])) { - $prop['BorderColor']='black'; - } - $prop['BorderStyle']='beveled'; - $this->_addfield('button',$name,$this->x,$this->y,$w,$h,$prop); - $this->javascript .= "f.buttonSetCaption('".addslashes($caption)."');"; - $this->javascript .= "f.setAction('MouseUp','".addslashes($action)."');"; - $this->javascript .= "f.highlight='push';"; - $this->javascript .= 'f.print=false;'; - } - - // END JAVASCRIPT - FORMS ------------------------------ - - } // END OF TCPDF CLASS - - //Handle special IE contype request - if(isset($_SERVER['HTTP_USER_AGENT']) AND ($_SERVER['HTTP_USER_AGENT']=='contype')) { - header('Content-Type: application/pdf'); - exit; - } - + //------------------------------------------------------------ + // Public methods + //------------------------------------------------------------ + + /** + * This is the class constructor. + * It allows to set up the page format, the orientation and + * the measure unit used in all the methods (except for the font sizes). + * @since 1.0 + * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul> + * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit. + * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul> + * @param boolean $unicode TRUE means that the input text is unicode (default = true) + * @param String $encoding charset encoding; default is UTF-8 + */ + public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding="UTF-8") { + + /* Set internal character encoding to ASCII */ + if (function_exists("mb_internal_encoding") AND mb_internal_encoding()) { + $this->internal_encoding = mb_internal_encoding(); + mb_internal_encoding("ASCII"); + } + + // set language direction + $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; + $this->tmprtl = false; + + //Some checks + $this->_dochecks(); + + //Initialization of properties + $this->isunicode=$unicode; + $this->page=0; + $this->n=2; + $this->buffer=''; + $this->pages=array(); + $this->OrientationChanges=array(); + $this->state=0; + $this->fonts=array(); + $this->FontFiles=array(); + $this->diffs=array(); + $this->images=array(); + $this->links=array(); + $this->InFooter=false; + $this->lasth=0; + $this->FontFamily=''; + $this->FontStyle=''; + $this->FontSizePt=12; + $this->underline=false; + $this->DrawColor='0 G'; + $this->FillColor='0 g'; + $this->TextColor='0 g'; + $this->ColorFlag=false; + $this->ws=0; + // encryption values + $this->encrypted=false; + $this->last_rc4_key=''; + $this->padding="\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A"; + + //Standard Unicode fonts + $this->CoreFonts=array( + 'courier'=>'Courier', + 'courierB'=>'Courier-Bold', + 'courierI'=>'Courier-Oblique', + 'courierBI'=>'Courier-BoldOblique', + 'helvetica'=>'Helvetica', + 'helveticaB'=>'Helvetica-Bold', + 'helveticaI'=>'Helvetica-Oblique', + 'helveticaBI'=>'Helvetica-BoldOblique', + 'times'=>'Times-Roman', + 'timesB'=>'Times-Bold', + 'timesI'=>'Times-Italic', + 'timesBI'=>'Times-BoldItalic', + 'symbol'=>'Symbol', + 'zapfdingbats'=>'ZapfDingbats' + ); + + //Scale factor + switch (strtolower($unit)){ + case 'pt': {$this->k=1; break;} + case 'mm': {$this->k=72/25.4; break;} + case 'cm': {$this->k=72/2.54; break;} + case 'in': {$this->k=72; break;} + default : {$this->Error('Incorrect unit: '.$unit); break;} + } + + //Page format + if(is_string($format)) { + // Page formats (45 standard ISO paper formats and 4 american common formats). + // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm) + switch (strtoupper($format)){ + case '4A0': {$format = array(4767.87,6740.79); break;} + case '2A0': {$format = array(3370.39,4767.87); break;} + case 'A0': {$format = array(2383.94,3370.39); break;} + case 'A1': {$format = array(1683.78,2383.94); break;} + case 'A2': {$format = array(1190.55,1683.78); break;} + case 'A3': {$format = array(841.89,1190.55); break;} + case 'A4': default: {$format = array(595.28,841.89); break;} + case 'A5': {$format = array(419.53,595.28); break;} + case 'A6': {$format = array(297.64,419.53); break;} + case 'A7': {$format = array(209.76,297.64); break;} + case 'A8': {$format = array(147.40,209.76); break;} + case 'A9': {$format = array(104.88,147.40); break;} + case 'A10': {$format = array(73.70,104.88); break;} + case 'B0': {$format = array(2834.65,4008.19); break;} + case 'B1': {$format = array(2004.09,2834.65); break;} + case 'B2': {$format = array(1417.32,2004.09); break;} + case 'B3': {$format = array(1000.63,1417.32); break;} + case 'B4': {$format = array(708.66,1000.63); break;} + case 'B5': {$format = array(498.90,708.66); break;} + case 'B6': {$format = array(354.33,498.90); break;} + case 'B7': {$format = array(249.45,354.33); break;} + case 'B8': {$format = array(175.75,249.45); break;} + case 'B9': {$format = array(124.72,175.75); break;} + case 'B10': {$format = array(87.87,124.72); break;} + case 'C0': {$format = array(2599.37,3676.54); break;} + case 'C1': {$format = array(1836.85,2599.37); break;} + case 'C2': {$format = array(1298.27,1836.85); break;} + case 'C3': {$format = array(918.43,1298.27); break;} + case 'C4': {$format = array(649.13,918.43); break;} + case 'C5': {$format = array(459.21,649.13); break;} + case 'C6': {$format = array(323.15,459.21); break;} + case 'C7': {$format = array(229.61,323.15); break;} + case 'C8': {$format = array(161.57,229.61); break;} + case 'C9': {$format = array(113.39,161.57); break;} + case 'C10': {$format = array(79.37,113.39); break;} + case 'RA0': {$format = array(2437.80,3458.27); break;} + case 'RA1': {$format = array(1729.13,2437.80); break;} + case 'RA2': {$format = array(1218.90,1729.13); break;} + case 'RA3': {$format = array(864.57,1218.90); break;} + case 'RA4': {$format = array(609.45,864.57); break;} + case 'SRA0': {$format = array(2551.18,3628.35); break;} + case 'SRA1': {$format = array(1814.17,2551.18); break;} + case 'SRA2': {$format = array(1275.59,1814.17); break;} + case 'SRA3': {$format = array(907.09,1275.59); break;} + case 'SRA4': {$format = array(637.80,907.09); break;} + case 'LETTER': {$format = array(612.00,792.00); break;} + case 'LEGAL': {$format = array(612.00,1008.00); break;} + case 'EXECUTIVE': {$format = array(521.86,756.00); break;} + case 'FOLIO': {$format = array(612.00,936.00); break;} + // default: {$this->Error('Unknown page format: '.$format); break;} + } + $this->fwPt=$format[0]; + $this->fhPt=$format[1]; + } + else { + $this->fwPt=$format[0]*$this->k; + $this->fhPt=$format[1]*$this->k; + } + + $this->fw=$this->fwPt/$this->k; + $this->fh=$this->fhPt/$this->k; + + //Page orientation + $orientation=strtolower($orientation); + if($orientation=='p' or $orientation=='portrait') { + $this->DefOrientation='P'; + $this->wPt=$this->fwPt; + $this->hPt=$this->fhPt; + } + elseif($orientation=='l' or $orientation=='landscape') { + $this->DefOrientation='L'; + $this->wPt=$this->fhPt; + $this->hPt=$this->fwPt; + } + else { + $this->Error('Incorrect orientation: '.$orientation); + } + + $this->CurOrientation=$this->DefOrientation; + $this->w=$this->wPt/$this->k; + $this->h=$this->hPt/$this->k; + //Page margins (1 cm) + $margin=28.35/$this->k; + $this->SetMargins($margin,$margin); + //Interior cell margin (1 mm) + $this->cMargin=$margin/10; + //Line width (0.2 mm) + $this->LineWidth=.567/$this->k; + //Automatic page break + $this->SetAutoPageBreak(true,2*$margin); + //Full width display mode + $this->SetDisplayMode('fullwidth'); + //Compression + $this->SetCompression(true); + //Set default PDF version number + $this->PDFVersion = "1.5"; + + $this->encoding = $encoding; + $this->b = 0; + $this->i = 0; + $this->u = 0; + $this->HREF = ''; + $this->fontlist = array("arial", "times", "courier", "helvetica", "symbol"); + $this->issetfont = false; + $this->issetcolor = false; + $this->tableborder = 0; + $this->tdbegin = false; + $this->tdwidth= 0; + $this->tdheight = 0; + if($this->rtl) { + $this->tdalign = "R"; + } else { + $this->tdalign = "L"; + } + $this->tdbgcolor = false; + + $this->SetFillColor(200, 200, 200, true); + $this->SetTextColor(0, 0, 0, true); + } + + /** + * Default destructor. + * @since 1.53.0.TC016 + */ + public function __destruct() { + // restore internal encoding + if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) { + mb_internal_encoding($this->internal_encoding); + } + } + + /** + * Enable or disable Right-To-Left language mode + * @param Boolean $enable if true enable Right-To-Left language mode. + * @since 2.0.000 (2008-01-03) + */ + public function setRTL($enable) { + $this->rtl = $enable ? true : false; + $this->tmprtl = false; + } + + /** + * Force temporary RTL language direction + * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL + * @since 2.1.000 (2008-01-09) + */ + public function setTempRTL($mode) { + switch ($mode) { + case false: + case 'L': + case 'R': { + $this->tmprtl = $mode; + } + } + } + + /** + * Set the last cell height. + * @param float $h cell height. + * @author Nicola Asuni + * @since 1.53.0.TC034 + */ + public function setLastH($h) { + $this->lasth=$h; + } + + /** + * Set the image scale. + * @param float $scale image scale. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function setImageScale($scale) { + $this->imgscale=$scale; + } + + /** + * Returns the image scale. + * @return float image scale. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function getImageScale() { + return $this->imgscale; + } + + /** + * Returns the page width in units. + * @return int page width. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function getPageWidth() { + return $this->w; + } + + /** + * Returns the page height in units. + * @return int page height. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function getPageHeight() { + return $this->h; + } + + /** + * Returns the page break margin. + * @return int page break margin. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function getBreakMargin() { + return $this->bMargin; + } + + /** + * Returns the scale factor (number of points in user unit). + * @return int scale factor. + * @author Nicola Asuni + * @since 1.5.2 + */ + public function getScaleFactor() { + return $this->k; + } + + /** + * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them. + * @param float $left Left margin. + * @param float $top Top margin. + * @param float $right Right margin. Default value is the left one. + * @since 1.0 + * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak() + */ + public function SetMargins($left, $top, $right=-1) { + //Set left, top and right margins + $this->lMargin=$left; + $this->tMargin=$top; + if($right==-1) { + $right=$left; + } + $this->rMargin=$right; + } + + /** + * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin. + * @param float $margin The margin. + * @since 1.4 + * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() + */ + public function SetLeftMargin($margin) { + //Set left margin + $this->lMargin=$margin; + if(($this->page > 0) AND ($this->x < $margin)) { + $this->x = $margin; + } + } + + /** + * Defines the top margin. The method can be called before creating the first page. + * @param float $margin The margin. + * @since 1.5 + * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins() + */ + public function SetTopMargin($margin) { + //Set top margin + $this->tMargin=$margin; + if(($this->page > 0) AND ($this->y < $margin)) { + $this->y = $margin; + } + } + + /** + * Defines the right margin. The method can be called before creating the first page. + * @param float $margin The margin. + * @since 1.5 + * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() + */ + public function SetRightMargin($margin) { + $this->rMargin=$margin; + if(($this->page > 0) AND ($this->x > ($this->w - $margin))) { + $this->x = $this->w - $margin; + } + } + + /** + * Set the internal Cell padding. + * @param float $pad internal padding. + * @since 2.1.000 (2008-01-09) + * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins() + */ + public function SetCellPadding($pad) { + $this->cMargin=$pad; + } + + /** + * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm. + * @param boolean $auto Boolean indicating if mode should be on or off. + * @param float $margin Distance from the bottom of the page. + * @since 1.0 + * @see Cell(), MultiCell(), AcceptPageBreak() + */ + public function SetAutoPageBreak($auto, $margin=0) { + //Set auto page break mode and triggering margin + $this->AutoPageBreak = $auto; + $this->bMargin = $margin; + $this->PageBreakTrigger = $this->h - $margin; + } + + /** + * Defines the way the document is to be displayed by the viewer. The zoom level can be set: pages can be displayed entirely on screen, occupy the full width of the window, use real size, be scaled by a specific zooming factor or use viewer default (configured in the Preferences menu of Acrobat). The page layout can be specified too: single at once, continuous display, two columns or viewer default. By default, documents use the full width mode with continuous display. + * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul> + * @param string $layout The page layout. Possible values are:<ul><li>single: displays one page at once</li><li>continuous: displays pages continuously (default)</li><li>two: displays two pages on two columns</li><li>default: uses viewer default mode</li></ul> + * @since 1.2 + */ + public function SetDisplayMode($zoom, $layout='continuous') { + //Set display mode in viewer + if($zoom=='fullpage' or $zoom=='fullwidth' or $zoom=='real' or $zoom=='default' or !is_string($zoom)) { + $this->ZoomMode=$zoom; + } + else { + $this->Error('Incorrect zoom display mode: '.$zoom); + } + if($layout=='single' or $layout=='continuous' or $layout=='two' or $layout=='default') { + $this->LayoutMode=$layout; + } + else { + $this->Error('Incorrect layout display mode: '.$layout); + } + } + + /** + * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default. + * Note: the Zlib extension is required for this feature. If not present, compression will be turned off. + * @param boolean $compress Boolean indicating if compression must be enabled. + * @since 1.4 + */ + public function SetCompression($compress) { + //Set page compression + if(function_exists('gzcompress')) { + $this->compress=$compress; + } + else { + $this->compress=false; + } + } + + /** + * Defines the title of the document. + * @param string $title The title. + * @since 1.2 + * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject() + */ + public function SetTitle($title) { + //Title of document + $this->title=$title; + } + + /** + * Defines the subject of the document. + * @param string $subject The subject. + * @since 1.2 + * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle() + */ + public function SetSubject($subject) { + //Subject of document + $this->subject=$subject; + } + + /** + * Defines the author of the document. + * @param string $author The name of the author. + * @since 1.2 + * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle() + */ + public function SetAuthor($author) { + //Author of document + $this->author=$author; + } + + /** + * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'. + * @param string $keywords The list of keywords. + * @since 1.2 + * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle() + */ + public function SetKeywords($keywords) { + //Keywords of document + $this->keywords=$keywords; + } + + /** + * Defines the creator of the document. This is typically the name of the application that generates the PDF. + * @param string $creator The name of the creator. + * @since 1.2 + * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle() + */ + public function SetCreator($creator) { + //Creator of document + $this->creator=$creator; + } + + /** + * Defines an alias for the total number of pages. It will be substituted as the document is closed.<br /> + * <b>Example:</b><br /> + * <pre> + * class PDF extends TCPDF { + * public function Footer() { + * //Go to 1.5 cm from bottom + * $this->SetY(-15); + * //Select Arial italic 8 + * $this->SetFont('Arial','I',8); + * //Print current and total page numbers + * $this->Cell(0,10,'Page '.$this->PageNo().'/{nb}',0,0,'C'); + * } + * } + * $pdf=new PDF(); + * $pdf->AliasNbPages(); + * </pre> + * @param string $alias The alias. Default value: {nb}. + * @since 1.4 + * @see PageNo(), Footer() + */ + public function AliasNbPages($alias='{nb}') { + //Define an alias for total number of pages + $this->AliasNbPages = $this->_escapetext($alias); + } + + /** + * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid. + * 2004-06-11 :: Nicola Asuni : changed bold tag with strong + * @param string $msg The error message + * @since 1.0 + */ + public function Error($msg) { + //Fatal error + die('<strong>TCPDF error: </strong>'.$msg); + } + + /** + * This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically. + * Note: no page is created by this method + * @since 1.0 + * @see AddPage(), Close() + */ + public function Open() { + //Begin document + $this->state=1; + } + + /** + * Terminates the PDF document. It is not necessary to call this method explicitly because Output() does it automatically. If the document contains no page, AddPage() is called to prevent from getting an invalid document. + * @since 1.0 + * @see Open(), Output() + */ + public function Close() { + //Terminate document + if($this->state==3) { + return; + } + if($this->page==0) { + $this->AddPage(); + } + //Page footer + $this->InFooter=true; + $this->Footer(); + $this->InFooter=false; + //Close page + $this->_endpage(); + //Close document + $this->_enddoc(); + } + + /** + * Reset pointer to the last document page. + * @since 2.0.000 (2008-01-04) + * @see setPage(), getPage(), getNumPages() + */ + public function lastPage() { + $this->page = count($this->pages); + } + + /** + * Move pointer to the apecified document page. + * @param int $pnum page number + * @since 2.1.000 (2008-01-07) + * @see getPage(), lastpage(), getNumPages() + */ + public function setPage($pnum) { + if(($pnum > 0) AND ($pnum <= count($this->pages))) { + $this->page = $pnum; + } + } + + /** + * Get current document page number. + * @return int page number + * @since 2.1.000 (2008-01-07) + * @see setPage(), lastpage(), getNumPages() + */ + public function getPage() { + return $this->page; + } + + + /** + * Get the total number of insered pages. + * @return int number of pages + * @since 2.1.000 (2008-01-07) + * @see setPage(), getPage(), lastpage() + */ + public function getNumPages() { + return count($this->pages); + } + + /** + * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer. Then the page is added, the current position set to the top-left corner according to the left and top margins, and Header() is called to display the header. + * The font which was set before calling is automatically restored. There is no need to call SetFont() again if you want to continue with the same font. The same is true for colors and line width. + * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards. + * @param string $orientation Page orientation. Possible values are (case insensitive):<ul><li>P or Portrait</li><li>L or Landscape</li></ul> The default value is the one passed to the constructor. + * @since 1.0 + * @see TCPDF(), Header(), Footer(), SetMargins() + */ + public function AddPage($orientation='') { + if (count($this->pages) > ($this->page + 1)) { + // this page has been already added + $this->page++; + return; + } + //Start a new page + if($this->state==0) { + $this->Open(); + } + $family=$this->FontFamily; + $style=$this->FontStyle.($this->underline ? 'U' : ''); + $size=$this->FontSizePt; + $lw=$this->LineWidth; + $dc=$this->DrawColor; + $fc=$this->FillColor; + $tc=$this->TextColor; + $cf=$this->ColorFlag; + if($this->page>0) { + //Page footer + $this->InFooter=true; + $this->Footer(); + $this->InFooter=false; + //Close page + $this->_endpage(); + } + //Start new page + $this->_beginpage($orientation); + //Set line cap style to square + $this->_out('2 J'); + //Set line width + $this->LineWidth=$lw; + $this->_out(sprintf('%.2f w',$lw*$this->k)); + //Set font + if($family) { + $this->SetFont($family,$style,$size); + } + //Set colors + $this->DrawColor=$dc; + if($dc!='0 G') { + $this->_out($dc); + } + $this->FillColor=$fc; + if($fc!='0 g') { + $this->_out($fc); + } + $this->TextColor=$tc; + $this->ColorFlag=$cf; + //Page header + $this->Header(); + //Restore line width + if($this->LineWidth!=$lw) { + $this->LineWidth=$lw; + $this->_out(sprintf('%.2f w',$lw*$this->k)); + } + //Restore font + if($family) { + $this->SetFont($family,$style,$size); + } + //Restore colors + if($this->DrawColor!=$dc) { + $this->DrawColor=$dc; + $this->_out($dc); + } + if($this->FillColor!=$fc) { + $this->FillColor=$fc; + $this->_out($fc); + } + $this->TextColor=$tc; + $this->ColorFlag=$cf; + } + + /** + * Set header data. + * @param string $ln header image logo + * @param string $lw header image logo width in mm + * @param string $ht string to print as title on document header + * @param string $hs string to print on document header + */ + public function setHeaderData($ln="", $lw=0, $ht="", $hs="") { + $this->header_logo = $ln; + $this->header_logo_width = $lw; + $this->header_title = $ht; + $this->header_string = $hs; + } + + /** + * Set header margin. + * (minimum distance between header and top page margin) + * @param int $hm distance in millimeters + */ + public function setHeaderMargin($hm=10) { + $this->header_margin = $hm; + } + + /** + * Set footer margin. + * (minimum distance between footer and bottom page margin) + * @param int $fm distance in millimeters + */ + public function setFooterMargin($fm=10) { + $this->footer_margin = $fm; + } + + /** + * Set a flag to print page header. + * @param boolean $val set to true to print the page header (default), false otherwise. + */ + public function setPrintHeader($val=true) { + $this->print_header = $val; + } + + /** + * Set a flag to print page footer. + * @param boolean $value set to true to print the page footer (default), false otherwise. + */ + public function setPrintFooter($val=true) { + $this->print_footer = $val; + } + + /** + * This method is used to render the page header. + * It is automatically called by AddPage() and could be overwritten in your own inherited class. + */ + public function Header() { + if ($this->print_header) { + + if (!isset($this->original_lMargin)) { + $this->original_lMargin = $this->lMargin; + } + if (!isset($this->original_rMargin)) { + $this->original_rMargin = $this->rMargin; + } + + //set current position + if ($this->rtl) { + $this->SetXY($this->original_rMargin, $this->header_margin); + } else { + $this->SetXY($this->original_lMargin, $this->header_margin); + } + + if (($this->header_logo) AND ($this->header_logo != K_BLANK_IMAGE)) { + $this->Image(K_PATH_IMAGES.$this->header_logo, $this->GetX(), $this->header_margin, $this->header_logo_width); + } else { + $this->img_rb_x = $this->GetX(); + $this->img_rb_y = $this->GetY(); + } + + $cell_height = round((K_CELL_HEIGHT_RATIO * $this->header_font[2]) / $this->k, 2); + // set starting margin for text data cell + if ($this->rtl) { + $header_x = $this->original_rMargin + ($this->header_logo_width * 1.1); + } else { + $header_x = $this->original_lMargin + ($this->header_logo_width * 1.1); + } + + // header title + $this->SetFont($this->header_font[0], 'B', $this->header_font[2] + 1); + $this->SetX($header_x); + $this->Cell($this->header_width, $cell_height, $this->header_title, 0, 1, ''); + + // header string + $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]); + $this->SetX($header_x); + $this->MultiCell($this->header_width, $cell_height, $this->header_string, 0, '', 0); + + // print an ending header line + if (empty($this->header_width)) { + //set style for cell border + $prevlinewidth = $this->GetLineWidth(); + $line_width = 0.3; + $this->SetLineWidth($line_width); + $this->SetDrawColor(0, 0, 0); + $this->SetY(1 + max($this->img_rb_y, $this->GetY())); + if ($this->rtl) { + $this->SetX($this->original_rMargin); + } else { + $this->SetX($this->original_lMargin); + } + $this->Cell(0, 0, '', 'T', 0, 'C'); + $this->SetLineWidth($prevlinewidth); + } + + //restore position + if ($this->rtl) { + $this->SetXY($this->original_rMargin, $this->tMargin); + } else { + $this->SetXY($this->original_lMargin, $this->tMargin); + } + } + } + + /** + * This method is used to render the page footer. + * It is automatically called by AddPage() and could be overwritten in your own inherited class. + */ + public function Footer() { + if ($this->print_footer) { + + if (!isset($this->original_lMargin)) { + $this->original_lMargin = $this->lMargin; + } + if (!isset($this->original_rMargin)) { + $this->original_rMargin = $this->rMargin; + } + + //set font + $this->SetFont($this->footer_font[0], $this->footer_font[1] , $this->footer_font[2]); + //set style for cell border + $prevlinewidth = $this->GetLineWidth(); + $line_width = 0.3; + $this->SetLineWidth($line_width); + $this->SetDrawColor(0, 0, 0); + + $footer_height = round((K_CELL_HEIGHT_RATIO * $this->footer_font[2]) / $this->k, 2); //footer height + //get footer y position + $footer_y = $this->h - $this->footer_margin - $footer_height; + //set current position + if ($this->rtl) { + $this->SetXY($this->original_rMargin, $footer_y); + } else { + $this->SetXY($this->original_lMargin, $footer_y); + } + + //print document barcode + if ($this->barcode) { + $this->Ln(); + $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin)/3); //max width + $this->writeBarcode($this->GetX(), $footer_y + $line_width, $barcode_width, $footer_height - $line_width, "C128B", false, false, 2, $this->barcode); + } + + $pagenumtxt = $this->l['w_page']." ".$this->PageNo().' / {nb}'; + + $this->SetY($footer_y); + + //Print page number + if ($this->rtl) { + $this->SetX($this->original_rMargin); + $this->Cell(0, $footer_height, $pagenumtxt, 'T', 0, 'L'); + } else { + $this->SetX($this->original_lMargin); + $this->Cell(0, $footer_height, $pagenumtxt, 'T', 0, 'R'); + } + // restore line width + $this->SetLineWidth($prevlinewidth); + } + } + + /** + * Returns the current page number. + * @return int page number + * @since 1.0 + * @see AliasNbPages() + */ + public function PageNo() { + //Get current page number + return $this->page; + } + + /** + * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. + * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 + * @param int $g Green component (between 0 and 255) + * @param int $b Blue component (between 0 and 255) + * @since 1.3 + * @see SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell() + */ + public function SetDrawColor($r, $g=-1, $b=-1) { + //Set color for all stroking operations + if(($r==0 and $g==0 and $b==0) or $g==-1) { + $this->DrawColor=sprintf('%.3f G',$r/255); + } + else { + $this->DrawColor=sprintf('%.3f %.3f %.3f RG',$r/255,$g/255,$b/255); + } + if($this->page>0) { + $this->_out($this->DrawColor); + } + } + + /** + * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. + * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 + * @param int $g Green component (between 0 and 255) + * @param int $b Blue component (between 0 and 255) + * @param boolean $storeprev if true stores the RGB array on $prevFillColor variable. + * @since 1.3 + * @see SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell() + */ + public function SetFillColor($r, $g=-1, $b=-1, $storeprev=false) { + //Set color for all filling operations + if(($r==0 and $g==0 and $b==0) or $g==-1) { + $this->FillColor=sprintf('%.3f g',$r/255); + } + else { + $this->FillColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); + } + $this->ColorFlag=($this->FillColor!=$this->TextColor); + if($this->page>0) { + $this->_out($this->FillColor); + } + if ($storeprev) { + // store color as previous value + $this->prevFillColor = array($r, $g, $b); + } + } + + /** + * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page. + * @param int $r If g et b are given, red component; if not, indicates the gray level. Value between 0 and 255 + * @param int $g Green component (between 0 and 255) + * @param int $b Blue component (between 0 and 255) + * @param boolean $storeprev if true stores the RGB array on $prevTextColor variable. + * @since 1.3 + * @see SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell() + */ + public function SetTextColor($r, $g=-1, $b=-1, $storeprev=false) { + //Set color for text + if(($r==0 and $g==0 and $b==0) or $g==-1) { + $this->TextColor=sprintf('%.3f g',$r/255); + } + else { + $this->TextColor=sprintf('%.3f %.3f %.3f rg',$r/255,$g/255,$b/255); + } + $this->ColorFlag=($this->FillColor!=$this->TextColor); + if ($storeprev) { + // store color as previous value + $this->prevTextColor = array($r, $g, $b); + } + } + + /** + * Returns the length of a string in user unit. A font must be selected.<br> + * Support UTF-8 Unicode [Nicola Asuni, 2005-01-02] + * @param string $s The string whose length is to be computed + * @return int + * @since 1.2 + */ + public function GetStringWidth($s) { + //Get width of a string in the current font + $s = (string)$s; + $cw = &$this->CurrentFont['cw']; + $w = 0; + if($this->isunicode) { + $unicode = $this->UTF8StringToArray($s); + foreach($unicode as $char) { + if (isset($cw[$char])) { + $w+=$cw[$char]; + } elseif(isset($cw[ord($char)])) { + $w+=$cw[ord($char)]; + } elseif(isset($cw[chr($char)])) { + $w+=$cw[chr($char)]; + } elseif(isset($this->CurrentFont['desc']['MissingWidth'])) { + $w += $this->CurrentFont['desc']['MissingWidth']; // set default size + } else { + $w += 500; + } + } + } else { + $l = strlen($s); + for($i=0; $i < $l; $i++) { + if (isset($cw[$s{$i}])) { + $w += $cw[$s{$i}]; + } else if (isset($cw[ord($s{$i})])) { + $w += $cw[ord($s{$i})]; + } + } + } + return ($w * $this->FontSize / 1000); + } + + /** + * Returns the numbero of characters in a string. + * @param string $s The input string. + * @return int number of characters + * @since 2.0.0001 (2008-01-07) + */ + public function GetNumChars($s) { + if($this->isunicode) { + return count($this->UTF8StringToArray($s)); + } + return strlen($s); + } + + /** + * Imports a TrueType or Type1 font and makes it available. It is necessary to generate a font definition file first with the makefont.php utility. The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated. + * Support UTF-8 Unicode [Nicola Asuni, 2005-01-02]. + * <b>Example</b>:<br /> + * <pre> + * $pdf->AddFont('Comic','I'); + * // is equivalent to: + * $pdf->AddFont('Comic','I','comici.php'); + * </pre> + * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font. + * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul> + * @param string $file The font definition file. By default, the name is built from the family and style, in lower case with no space. + * @since 1.5 + * @see SetFont() + */ + public function AddFont($family, $style='', $file='') { + if(empty($family)) { + return; + } + + //Add a TrueType or Type1 font + $family = strtolower($family); + if((!$this->isunicode) AND ($family == 'arial')) { + $family = 'helvetica'; + } + + $style=strtoupper($style); + $style=str_replace('U','',$style); + if($style == 'IB') { + $style = 'BI'; + } + + $fontkey = $family.$style; + // check if the font has been already added + if(isset($this->fonts[$fontkey])) { + return; + } + + if($file=='') { + $file = str_replace(' ', '', $family).strtolower($style).'.php'; + } + if(!file_exists($this->_getfontpath().$file)) { + // try to load the basic file without styles + $file = str_replace(' ', '', $family).'.php'; + } + + include($this->_getfontpath().$file); + + if(!isset($name) AND !isset($fpdf_charwidths)) { + $this->Error('Could not include font definition file'); + } + + $i = count($this->fonts)+1; + + if($this->isunicode) { + $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file, 'ctg'=>$ctg); + $fpdf_charwidths[$fontkey] = $cw; + } else { + $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$this->CoreFonts[$fontkey], 'up'=>-100, 'ut'=>50, 'cw'=>$fpdf_charwidths[$fontkey]); + } + + if(isset($diff) AND (!empty($diff))) { + //Search existing encodings + $d=0; + $nb=count($this->diffs); + for($i=1;$i<=$nb;$i++) { + if($this->diffs[$i]==$diff) { + $d=$i; + break; + } + } + if($d==0) { + $d=$nb+1; + $this->diffs[$d]=$diff; + } + $this->fonts[$fontkey]['diff']=$d; + } + if(!empty($file)) { + if((strcasecmp($type,"TrueType") == 0) OR (strcasecmp($type,"TrueTypeUnicode") == 0)) { + $this->FontFiles[$file]=array('length1'=>$originalsize); + } + else { + $this->FontFiles[$file]=array('length1'=>$size1,'length2'=>$size2); + } + } + } + + /** + * Sets the font used to print character strings. It is mandatory to call this method at least once before printing text or the resulting document would not be valid. + * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe). + * The method can be called before the first page is created and the font is retained from page to page. + If you just wish to change the current font size, it is simpler to call SetFontSize(). + * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br /> + * Example for the last case (note the trailing slash):<br /> + * <pre> + * define('K_PATH_FONTS','/home/www/font/'); + * require('tcpdf.php'); + * + * //Times regular 12 + * $pdf->SetFont('Times'); + * //Arial bold 14 + * $pdf->SetFont('Arial','B',14); + * //Removes bold + * $pdf->SetFont(''); + * //Times bold, italic and underlined 14 + * $pdf->SetFont('Times','BIU'); + * </pre><br /> + * If the file corresponding to the requested font is not found, the error "Could not include font metric file" is generated. + * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard families (case insensitive):<ul><li>Courier (fixed-width)</li><li>Helvetica or Arial (synonymous; sans serif)</li><li>Times (serif)</li><li>Symbol (symbolic)</li><li>ZapfDingbats (symbolic)</li></ul>It is also possible to pass an empty string. In that case, the current family is retained. + * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li></ul>or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats + * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12 + * @since 1.0 + * @see AddFont(), SetFontSize(), Cell(), MultiCell(), Write() + */ + public function SetFont($family, $style='', $size=0) { + // save previous values + $this->prevFontFamily = $this->FontFamily; + $this->prevFontStyle = $this->FontStyle; + + //Select a font; size given in points + global $fpdf_charwidths; + + $family=strtolower($family); + if($family=='') { + $family=$this->FontFamily; + } + if((!$this->isunicode) AND ($family == 'arial')) { + $family = 'helvetica'; + } + elseif(($family=="symbol") OR ($family=="zapfdingbats")) { + $style=''; + } + $style=strtoupper($style); + + if(strpos($style,'U')!==false) { + $this->underline=true; + $style=str_replace('U','',$style); + } + else { + $this->underline=false; + } + if($style=='IB') { + $style='BI'; + } + if($size==0) { + $size=$this->FontSizePt; + } + + // try to add font (if not already added) + if($this->isunicode) { + $this->AddFont($family, $style); + } + + //Test if font is already selected + if(($this->FontFamily == $family) AND ($this->FontStyle == $style) AND ($this->FontSizePt == $size)) { + return; + } + + $fontkey = $family.$style; + //if(!isset($this->fonts[$fontkey]) AND isset($this->fonts[$family])) { + // $style=''; + //} + + //Test if used for the first time + if(!isset($this->fonts[$fontkey])) { + //Check if one of the standard fonts + if(isset($this->CoreFonts[$fontkey])) { + if(!isset($fpdf_charwidths[$fontkey])) { + //Load metric file + $file = $family; + if(($family!='symbol') AND ($family!='zapfdingbats')) { + $file .= strtolower($style); + } + if(!file_exists($this->_getfontpath().$file.'.php')) { + // try to load the basic file without styles + $file = $family; + $fontkey = $family; + } + include($this->_getfontpath().$file.'.php'); + if (($this->isunicode AND !isset($ctg)) OR ((!$this->isunicode) AND (!isset($fpdf_charwidths[$fontkey]))) ) { + $this->Error("Could not include font metric file [".$fontkey."]: ".$this->_getfontpath().$file.".php"); + } + } + $i = count($this->fonts) + 1; + + if($this->isunicode) { + $this->fonts[$fontkey] = array('i'=>$i, 'type'=>$type, 'name'=>$name, 'desc'=>$desc, 'up'=>$up, 'ut'=>$ut, 'cw'=>$cw, 'enc'=>$enc, 'file'=>$file, 'ctg'=>$ctg); + $fpdf_charwidths[$fontkey] = $cw; + } else { + $this->fonts[$fontkey]=array('i'=>$i, 'type'=>'core', 'name'=>$this->CoreFonts[$fontkey], 'up'=>-100, 'ut'=>50, 'cw'=>$fpdf_charwidths[$fontkey]); + } + } + else { + $this->Error('Undefined font: '.$family.' '.$style); + } + } + //Select it + $this->FontFamily = $family; + $this->FontStyle = $style; + $this->FontSizePt = $size; + $this->FontSize = $size / $this->k; + $this->CurrentFont = &$this->fonts[$fontkey]; + if($this->page>0) { + $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); + } + } + + /** + * Defines the size of the current font. + * @param float $size The size (in points) + * @since 1.0 + * @see SetFont() + */ + public function SetFontSize($size) { + //Set font size in points + if($this->FontSizePt==$size) { + return; + } + $this->FontSizePt = $size; + $this->FontSize = $size / $this->k; + if($this->page > 0) { + $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); + } + } + + /** + * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br /> + * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink(). + * @since 1.5 + * @see Cell(), Write(), Image(), Link(), SetLink() + */ + public function AddLink() { + //Create a new internal link + $n=count($this->links)+1; + $this->links[$n]=array(0,0); + return $n; + } + + /** + * Defines the page and position a link points to + * @param int $link The link identifier returned by AddLink() + * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page) + * @param int $page Number of target page; -1 indicates the current page. This is the default value + * @since 1.5 + * @see AddLink() + */ + public function SetLink($link, $y=0, $page=-1) { + //Set destination of internal link + if($y==-1) { + $y=$this->y; + } + if($page==-1) { + $page=$this->page; + } + $this->links[$link]=array($page,$y); + } + + /** + * Puts a link on a rectangular area of the page. Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image. + * @param float $x Abscissa of the upper-left corner of the rectangle (or upper-right for RTL languages) + * @param float $y Ordinate of the upper-left corner of the rectangle (or upper-right for RTL languages) + * @param float $w Width of the rectangle + * @param float $h Height of the rectangle + * @param mixed $link URL or identifier returned by AddLink() + * @since 1.5 + * @see AddLink(), Cell(), Write(), Image() + */ + public function Link($x, $y, $w, $h, $link) { + $this->PageLinks[$this->page][] = array($x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h*$this->k, $link); + } + + /** + * Prints a character string. The origin is on the left of the first charcter, on the baseline. This method allows to place a string precisely on the page, but it is usually easier to use Cell(), MultiCell() or Write() which are the standard methods to print text. + * @param float $x Abscissa of the origin + * @param float $y Ordinate of the origin + * @param string $txt String to print + * @since 1.0 + * @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write() + */ + public function Text($x, $y, $txt) { + //Output a string + if($this->rtl) { + $xr = $this->w - $x - $this->GetStringWidth($txt); + } else { + $xr = $x; + } + $s = sprintf('BT %.2f %.2f Td (%s) Tj ET', $xr * $this->k, ($this->h-$y) * $this->k, $this->_escapetext($txt)); + if($this->underline AND ($txt!='')) { + $s .= ' '.$this->_dounderline($xr, $y, $txt); + } + if($this->ColorFlag) { + $s='q '.$this->TextColor.' '.$s.' Q'; + } + $this->_out($s); + } + + /** + * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br /> + * This method is called automatically and should not be called directly by the application.<br /> + * <b>Example:</b><br /> + * The method is overriden in an inherited class in order to obtain a 3 column layout:<br /> + * <pre> + * class PDF extends TCPDF { + * var $col=0; + * + * public function SetCol($col) { + * //Move position to a column + * $this->col=$col; + * $x=10+$col*65; + * $this->SetLeftMargin($x); + * $this->SetX($x); + * } + * + * public function AcceptPageBreak() { + * if($this->col<2) { + * //Go to next column + * $this->SetCol($this->col+1); + * $this->SetY(10); + * return false; + * } + * else { + * //Go back to first column and issue page break + * $this->SetCol(0); + * return true; + * } + * } + * } + * + * $pdf=new PDF(); + * $pdf->Open(); + * $pdf->AddPage(); + * $pdf->SetFont('Arial','',12); + * for($i=1;$i<=300;$i++) { + * $pdf->Cell(0,5,"Line $i",0,1); + * } + * $pdf->Output(); + * </pre> + * @return boolean + * @since 1.4 + * @see SetAutoPageBreak() + */ + public function AcceptPageBreak() { + //Accept automatic page break or not + return $this->AutoPageBreak; + } + + /** + * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br /> + * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. + * @param float $w Cell width. If 0, the cell extends up to the right margin. + * @param float $h Cell height. Default value: 0. + * @param string $txt String to print. Default value: empty string. + * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> + * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> + Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. + * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul> + * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * @param mixed $link URL or identifier returned by AddLink(). + * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul> + * @since 1.0 + * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak() + */ + public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) { + //Output a cell + $k=$this->k; + if(($this->y + $h) > $this->PageBreakTrigger AND empty($this->InFooter) AND $this->AcceptPageBreak()) { + //Automatic page break + $x = $this->x; + $ws = $this->ws; + if($ws > 0) { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->AddPage($this->CurOrientation); + $this->x = $x; + if($ws > 0) { + $this->ws = $ws; + $this->_out(sprintf('%.3f Tw',$ws * $k)); + } + } + if($w == 0) { + if ($this->rtl) { + $w = $this->x - $this->lMargin; + } else { + $w = $this->w - $this->rMargin - $this->x; + } + } + $s = ''; + if(($fill == 1) OR ($border == 1)) { + if($fill == 1) { + $op = ($border == 1) ? 'B' : 'f'; + } else { + $op = 'S'; + } + if ($this->rtl) { + $xk = ($this->x - $w) * $k; + } else { + $xk = $this->x * $k; + } + $s .= sprintf('%.2f %.2f %.2f %.2f re %s ', $xk, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op); + } + if(is_string($border)) { + $x=$this->x; + $y=$this->y; + if(strpos($border,'L')!==false) { + if ($this->rtl) { + $xk = ($x - $w) * $k; + } else { + $xk = $x * $k; + } + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xk,($this->h-($y+$h))*$k); + } + if(strpos($border,'T')!==false) { + if ($this->rtl) { + $xk = ($x - $w) * $k; + $xwk = $x * $k; + } else { + $xk = $x * $k; + $xwk = ($x + $w) * $k; + } + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xwk,($this->h-$y)*$k); + } + if(strpos($border,'R')!==false) { + if ($this->rtl) { + $xk = $x * $k; + } else { + $xk = ($x + $w) * $k; + } + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-$y)*$k,$xk,($this->h-($y+$h))*$k); + } + if(strpos($border,'B')!==false) { + if ($this->rtl) { + $xk = ($x - $w) * $k; + $xwk = $x * $k; + } else { + $xk = $x * $k; + $xwk = ($x + $w) * $k; + } + $s.=sprintf('%.2f %.2f m %.2f %.2f l S ',$xk,($this->h-($y+$h))*$k,$xwk,($this->h-($y+$h))*$k); + } + } + if($txt != '') { + // text lenght + $width = $this->GetStringWidth($txt); + // ratio between cell lenght and text lenght + $ratio = ($w - (2 * $this->cMargin)) / $width; + + // stretch text if requested + if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) { + if ($stretch > 2) { + // spacing + //Calculate character spacing in points + $char_space = ($w - (2 * $this->cMargin) - $width) / max($this->GetNumChars($s)-1,1) * $this->k; + //Set character spacing + $this->_out(sprintf('BT %.2f Tc ET', $char_space)); + } else { + // scaling + //Calculate horizontal scaling + $horiz_scale = $ratio*100.0; + //Set horizontal scaling + $this->_out(sprintf('BT %.2f Tz ET', $horiz_scale)); + } + $align = ''; + $width = $w - (2 * $this->cMargin); + } else { + $stretch == 0; + } + + if($align == 'L') { + if ($this->rtl) { + $dx = $w - $this->cMargin - $width; + } else { + $dx = $this->cMargin; + } + } elseif($align == 'R') { + if ($this->rtl) { + $dx = $this->cMargin; + } else { + $dx = $w - $this->cMargin - $width; + } + } elseif($align=='C') { + $dx = ($w - $width)/2; + } elseif($align=='J') { + if ($this->rtl) { + $dx = $w - $this->cMargin - $width; + } else { + $dx = $this->cMargin; + } + } else { + $dx = $this->cMargin; + } + if($this->ColorFlag) { + $s .= 'q '.$this->TextColor.' '; + } + $txt2 = $this->_escapetext($txt); + if ($this->rtl) { + $xdk = ($this->x - $dx - $width) * $k; + } else { + $xdk = ($this->x + $dx) * $k; + } + // 2008-02-16 Jacek Czekaj - multibyte justification + if ($align == 'J') { + // count number of spaces + $ns = substr_count($txt, ' '); + // get string width without spaces + $width = $this->GetStringWidth(str_replace(' ', '', $txt)); + // set word position to be used with TJ operator + $txt2 = str_replace(chr(0).' ', ') '. -2830*($w-$width-2)/($ns?$ns:1)/$this->FontSize/$this->k . ' (', $txt2); + } + $s.=sprintf('BT %.2f %.2f Td [(%s)] TJ ET', $xdk, ($this->h - ($this->y + 0.5 * $h + 0.3 * $this->FontSize)) * $k, $txt2); + //$s.=sprintf('BT %.2f %.2f Td (%s) Tj ET', $xdk, ($this->h - ($this->y + 0.5 * $h + 0.3 * $this->FontSize)) * $k, $txt2); + if($this->underline) { + if ($this->rtl) { + $xdx = $this->x - $dx - $width; + } else { + $xdx = $this->x + $dx; + } + $s.=' '.$this->_dounderline($xdx, $this->y + 0.5 * $h + 0.3 * $this->FontSize, $txt); + } + if($this->ColorFlag) { + $s.=' Q'; + } + if($link) { + if ($this->rtl) { + $xdx = $this->x - $dx - $width; + } else { + $xdx = $this->x + $dx; + } + $this->Link($xdx, $this->y + 0.5 * $h - 0.5 * $this->FontSize, $width, $this->FontSize, $link); + } + } + + // output cell + if($s) { + // output cell + $this->_out($s); + // reset text stretching + if($stretch > 2) { + //Reset character horizontal spacing + $this->_out('BT 0 Tc ET'); + } elseif($stretch > 0) { + //Reset character horizontal scaling + $this->_out('BT 100 Tz ET'); + } + } + + $this->lasth = $h; + if($ln>0) { + //Go to the beginning of the next line + $this->y += $h; + if($ln == 1) { + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + } else { + $this->x = $this->lMargin; + } + } + } else { + // go left or right by case + if ($this->rtl) { + $this->x -= $w; + } else { + $this->x += $w; + } + } + } + + /** + * This method allows printing text with line breaks. They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br /> + * Text can be aligned, centered or justified. The cell block can be framed and the background painted. + * @param float $w Width of cells. If 0, they extend up to the right margin of the page. + * @param float $h Height of cells. + * @param string $txt String to print + * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> + * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value)</li></ul> + * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul> + * @return int number of cells (number of lines) + * @since 1.3 + * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak() + */ + public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1) { + + // save current position + $prevx = $this->x; + $prevy = $this->y; + + // get current page number + $startpage = $this->page; + + // calculate remaining vertical space on first page ($startpage) + $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin(); + + //Output text with automatic or explicit line breaks + $cw = &$this->CurrentFont['cw']; + + if($w == 0) { + if ($this->rtl) { + $w = $this->x - $this->lMargin; + } else { + $w = $this->w - $this->rMargin - $this->x; + } + } + + $wmax = ($w - 2 * $this->cMargin); + + $s = str_replace("\r", '', $txt); // remove carriage returns + $nb = strlen($s); + + $sep=-1; + $i=0; + $j=0; + $l=0; + $ns=0; + $nl=1; + while($i < $nb) { + //Get next character + $c = $s{$i}; + if(preg_match("/[\n]/u", $c)) { + //Explicit line break + if($this->ws > 0) { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); + $i++; + $sep=-1; + $j=$i; + $l=0; + $ns=0; + $nl++; + continue; + } + if(preg_match("/[ ]/u", $c)) { + $sep = $i; + $ls = $l; + $ns++; + } + + $l = $this->GetStringWidth(substr($s, $j, $i-$j)); + + if($l > $wmax) { + //Automatic line break + if($sep == -1) { + if($i == $j) { + $i++; + } + if($this->ws > 0) { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); + } + else { + if($align=='J') { + $this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0; + $this->_out(sprintf('%.3f Tw', $this->ws * $this->k)); + } + $this->Cell($w, $h, substr($s, $j, $sep-$j), 0, 2, $align, $fill, ''); + $i = $sep + 1; + } + $sep=-1; + $j=$i; + $l=0; + $ns=0; + $nl++; + } + else { + $i++; + } + } + //Last chunk + if($this->ws>0) { + $this->ws=0; + $this->_out('0 Tw'); + } + if($align == "J") { + $align = "L"; + } + $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, $align, $fill, ''); + + // Get end-of-text Y position + $currentY = $this->GetY(); + // get latest page number + $endpage = $this->page; + + if (!empty($border)) { + // check if a new page has been created + if ($endpage > $startpage) { + // design borders around HTML cells. + for ($page=$startpage; $page<=$endpage; $page++) { + $this->page = $page; + if ($page==$startpage) { + $this->y = $this->getPageHeight() - $restspace - $this->getBreakMargin(); + $h = $restspace - 1; + } elseif ($page==$endpage) { + $this->y = $this->tMargin; // put cursor at the beginning of text + $h = $currentY - $this->tMargin; + } else { + $this->y = $this->tMargin; // put cursor at the beginning of text + $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); + } + $this->Cell($w, $h, "", $border, 1, '', 0); + } + } else { + $h = max($h, ($currentY - $prevy)); + $this->y = $prevy; // put cursor at the beginning of text + // design a cell around the text + $this->Cell($w, $h, "", $border, 1, '', 0); + } + } + + // move cursor to specified position + if($ln>0) { + //Go to the beginning of the next line + $this->SetY($currentY); + if($ln == 2) { + $this->SetX($prevx + $w); + } + } else { + // go left or right by case + $this->page = $startpage; + $this->y = $prevy; + $this->SetX($prevx + $w); + } + + return $nl; + } + + /** + * This method prints text from the current position. When the right margin is reached (or the \n character is met) a line break occurs and text continues from the left margin. Upon method exit, the current position is left just at the end of the text. It is possible to put a link on the text.<br /> + * <b>Example:</b><br /> + * <pre> + * //Begin with regular font + * $pdf->SetFont('Arial','',14); + * $pdf->Write(5,'Visit '); + * //Then put a blue underlined link + * $pdf->SetTextColor(0,0,255); + * $pdf->SetFont('','U'); + * $pdf->Write(5,'www.tecnick.com','http://www.tecnick.com'); + * </pre> + * @param float $h Line height + * @param string $txt String to print + * @param mixed $link URL or identifier returned by AddLink() + * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. + * @since 1.5 + * @see SetFont(), SetTextColor(), AddLink(), MultiCell(), SetAutoPageBreak() + */ + public function Write($h, $txt, $link='', $fill=0) { + //Output text in flowing mode + $cw = &$this->CurrentFont['cw']; + // calculating remaining line width ($w) + if ($this->rtl) { + $w = $this->x - $this->lMargin; + } else { + $w = $this->w - $this->rMargin - $this->x; + } + $wmax = $w - 2 * $this->cMargin; + $s = str_replace("\r", '', $txt); + $nb = strlen($s); + // handle single space character + if(($nb==1) AND preg_match("/[ ]/u", $s)) { + if ($this->rtl) { + $this->x -= $this->GetStringWidth($s); + } else { + $this->x += $this->GetStringWidth($s); + } + return; + } + $sep=-1; + $i=0; + $j=0; + $l=0; + $nl=1; + while($i<$nb) { + //Get next character + $c=$s{$i}; + if(preg_match("/[\n]/u", $c)) { + //Explicit line break + $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, '', $fill, $link); + $i++; + $sep = -1; + $j = $i; + $l = 0; + if($nl == 1) { + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + $w = $this->x - $this->lMargin; + } + else { + $this->x = $this->lMargin; + $w = $this->w - $this->rMargin - $this->x; + } + $wmax = ($w - 2 * $this->cMargin); + } + $nl++; + continue; + } + if(preg_match("/[ ]/u", $c)) { + $sep= $i; + } + $l = $this->GetStringWidth(substr($s, $j, $i-$j)); + if($l > $wmax) { + //Automatic line break (word wrapping) + if($sep == -1) { + if((!$this->rtl) AND ($this->x > $this->lMargin)) { + //Move to next line + $this->x = $this->lMargin; + $this->y += $h; + $w=$this->w - $this->rMargin - $this->x; + $wmax=($w - 2 * $this->cMargin); + $i++; + $nl++; + continue; + } + if($this->rtl AND ($this->x < $this->rMargin)) { + //Move to next line + $this->x = $this->w - $this->rMargin; + $this->y += $h; + $w=$this->x - $this->lMargin; + $wmax=($w - 2 * $this->cMargin); + $i++; + $nl++; + continue; + } + if($i==$j) { + $i++; + } + $this->Cell($w, $h, substr($s, $j, $i-$j), 0, 2, '', $fill, $link); + } + else { + $this->Cell($w, $h, substr($s, $j, $sep-$j), 0, 2, '', $fill, $link); + $i=$sep+1; + } + $sep = -1; + $j = $i; + $l = 0; + if($nl==1) { + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + $w = $this->x - $this->lMargin; + } else { + $this->x = $this->lMargin; + $w = $this->w - $this->rMargin - $this->x; + } + $wmax = ($w - 2 * $this->cMargin); + } + $nl++; + } + else { + $i++; + } + } + + //Last chunk + if($i!=$j) { + $this->Cell($this->GetStringWidth(substr($s, $j)) + 2 * $this->cMargin, $h, substr($s, $j), 0, 0, '', $fill, $link); + } + } + + /** + * Puts an image in the page. The upper-left corner must be given. The dimensions can be specified in different ways:<ul><li>explicit width and height (expressed in user unit)</li><li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li><li>no explicit dimension, in which case the image is put at 72 dpi</li></ul> + * Supported formats are JPEG and PNG. + * For JPEG, all flavors are allowed:<ul><li>gray scales</li><li>true colors (24 bits)</li><li>CMYK (32 bits)</li></ul> + * For PNG, are allowed:<ul><li>gray scales on at most 8 bits (256 levels)</li><li>indexed colors</li><li>true colors (24 bits)</li></ul> + * If a transparent color is defined, it will be taken into account (but will be only interpreted by Acrobat 4 and above).<br /> + * The format can be specified explicitly or inferred from the file extension.<br /> + * It is possible to put a link on the image.<br /> + * Remark: if an image is used several times, only one copy will be embedded in the file.<br /> + * @param string $file Name of the file containing the image. + * @param float $x Abscissa of the upper-left corner. + * @param float $y Ordinate of the upper-left corner. + * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated. + * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated. + * @param string $type Image format. Possible values are (case insensitive): JPG, JPEG, PNG. If not specified, the type is inferred from the file extension. + * @param mixed $link URL or identifier returned by AddLink(). + * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> + * @since 1.1 + * @see AddLink() + */ + public function Image($file, $x, $y, $w=0, $h=0, $type='', $link='', $align='') { + //Put an image on the page + if(!isset($this->images[$file])) { + //First use of image, get info + if($type == '') { + $pos = strrpos($file,'.'); + if(empty($pos)) { + $this->Error('Image file has no extension and no type was specified: '.$file); + } + $type = substr($file, $pos+1); + } + $type = strtolower($type); + $mqr = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + if($type == 'jpg' or $type == 'jpeg') { + $info=$this->_parsejpg($file); + } elseif($type == 'gif') { + $info=$this->_parsegif($file); + } elseif($type == 'png') { + $info=$this->_parsepng($file); + }else { + //Allow for additional formats + $mtd='_parse'.$type; + if(!method_exists($this,$mtd)) { + $this->Error('Unsupported image type: '.$type); + } + $info=$this->$mtd($file); + } + if($info === false) { + //If false, we cannot process image + return; + } + set_magic_quotes_runtime($mqr); + $info['i']=count($this->images)+1; + $this->images[$file]=$info; + } + else { + $info=$this->images[$file]; + } + //Automatic width and height calculation if needed + if(($w == 0) and ($h == 0)) { + //Put image at 72 dpi + // 2004-06-14 :: Nicola Asuni, scale factor where added + $w = $info['w'] / ($this->imgscale * $this->k); + $h = $info['h'] / ($this->imgscale * $this->k); + } + if($w == 0) { + $w = $h * $info['w'] / $info['h']; + } + if($h == 0) { + $h = $w * $info['h'] / $info['w']; + } + + // 2007-10-19 Warren Sherliker + // Check whether we need a new page first as this does not fit + // Copied from Cell() + if((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) { + // Automatic page break + $this->AddPage($this->CurOrientation); + // Reset coordinates to top fo next page + $x = $this->GetX(); + $y = $this->GetY(); + } + // 2007-10-19 Warren Sherliker: End Edit + + // set bottomcoordinates + $this->img_rb_y = $y + $h; + if ($this->rtl) { + $ximg = ($this->w - $x -$w); + // set left side coordinate + $this->img_rb_x = $ximg; + } else { + $ximg = $x; + // set right side coordinate + $this->img_rb_x = $ximg + $w; + } + $xkimg = $ximg * $this->k; + $this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', $w*$this->k, $h*$this->k, $xkimg, ($this->h-($y+$h))*$this->k, $info['i'])); + + if($link) { + $this->Link($ximg, $y, $w, $h, $link); + } + + // set pointer to align the successive text/objects + switch($align) { + case 'T':{ + $this->y = $y; + $this->x = $this->img_rb_x; + break; + } + case 'M':{ + $this->y = $y + round($h/2); + $this->x = $this->img_rb_x; + break; + } + case 'B':{ + $this->y = $this->img_rb_y; + $this->x = $this->img_rb_x; + break; + } + case 'N':{ + $this->SetY($this->img_rb_y); + break; + } + default:{ + break; + } + } + } + + + /** + * Performs a line break. The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter. + * @param float $h The height of the break. By default, the value equals the height of the last printed cell. + * @since 1.0 + * @see Cell() + */ + public function Ln($h='') { + //Line feed; default value is last cell height + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + } else { + $this->x = $this->lMargin; + } + if(is_string($h)) { + $this->y += $this->lasth; + } else { + $this->y += $h; + } + } + + /** + * Returns the relative X value of current position. + * The value is relative to the left border for LTR languages and to the right border for RTL languages. + * @return float + * @since 1.2 + * @see SetX(), GetY(), SetY() + */ + public function GetX() { + //Get x position + if ($this->rtl) { + return ($this->w - $this->x); + } else { + return $this->x; + } + } + + /** + * Returns the absolute X value of current position. + * @return float + * @since 1.2 + * @see SetX(), GetY(), SetY() + */ + public function GetAbsX() { + return $this->x; + } + + /** + * Returns the ordinate of the current position. + * @return float + * @since 1.0 + * @see SetY(), GetX(), SetX() + */ + public function GetY() { + //Get y position + return $this->y; + } + + /** + * Defines the abscissa of the current position. + * If the passed value is negative, it is relative to the right of the page (or left if language is RTL). + * @param float $x The value of the abscissa. + * @since 1.2 + * @see GetX(), GetY(), SetY(), SetXY() + */ + public function SetX($x) { + //Set x position + if ($this->rtl) { + if($x >= 0) { + $this->x = $this->w - $x; + } else { + $this->x = abs($x); + } + } else { + if($x >= 0) { + $this->x = $x; + } else { + $this->x = $this->w + $x; + } + } + } + + /** + * Moves the current abscissa back to the left margin and sets the ordinate. + * If the passed value is negative, it is relative to the bottom of the page. + * @param float $y The value of the ordinate. + * @since 1.0 + * @see GetX(), GetY(), SetY(), SetXY() + */ + public function SetY($y) { + //Set y position and reset x + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + } else { + $this->x = $this->lMargin; + } + if($y >= 0) { + $this->y = $y; + } else { + $this->y = $this->h + $y; + } + } + + + /** + * Defines the abscissa and ordinate of the current position. If the passed values are negative, they are relative respectively to the right and bottom of the page. + * @param float $x The value of the abscissa + * @param float $y The value of the ordinate + * @since 1.2 + * @see SetX(), SetY() + */ + public function SetXY($x, $y) { + //Set x and y positions + $this->SetY($y); + $this->SetX($x); + } + + /** + * Send the document to a given destination: string, local file or browser. In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br /> + * The method first calls Close() if necessary to terminate the document. + * @param string $name The name of the file. If not given, the document will be sent to the browser (destination I) with the name doc.pdf. + * @param string $dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser. The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li></ul>If the parameter is not specified but a name is given, destination is F. If no parameter is specified at all, destination is I.<br />Note: for compatibility with previous versions, a boolean value is also accepted (false for F and true for D). + * @since 1.0 + * @see Close() + */ + public function Output($name='',$dest='') { + //Output PDF to some destination + //Finish document if necessary + if($this->state < 3) { + $this->Close(); + } + //Normalize parameters + if(is_bool($dest)) { + $dest=$dest ? 'D' : 'F'; + } + $dest=strtoupper($dest); + if($dest=='') { + if($name=='') { + $name='doc.pdf'; + $dest='I'; + } else { + $dest='F'; + } + } + switch($dest) { + case 'I': { + //Send to standard output + //if(ob_get_contents()) { + // $this->Error('Some data has already been output, can\'t send PDF file'); + //} + ob_end_clean(); + ob_start(); + if(php_sapi_name()!='cli') { + //We send to a browser + header('Content-Type: application/pdf'); + if(headers_sent()) { + $this->Error('Some data has already been output to browser, can\'t send PDF file'); + } + header('Content-Length: '.strlen($this->buffer)); + header('Content-disposition: inline; filename="'.$name.'"'); + } + echo $this->buffer; + break; + } + case 'D': { + //Download file + // if(ob_get_contents()) { + // $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); + //} + + if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { + header('Content-Type: application/force-download'); + } else { + header('Content-Type: application/octet-stream'); + } + if(headers_sent()) { + $this->Error('Some data has already been output to browser, can\'t send PDF file'); + } + header('Content-Length: '.strlen($this->buffer)); + header('Content-disposition: attachment; filename="'.$name.'"'); + echo $this->buffer; + break; + } + case 'F': { + //Save to local file + $f=fopen($name,'wb'); + if(!$f) { + $this->Error('Unable to create output file: '.$name); + } + fwrite($f,$this->buffer,strlen($this->buffer)); + fclose($f); + break; + } + case 'S': { + //Return as a string + return $this->buffer; + } + default: { + $this->Error('Incorrect output destination: '.$dest); + } + } + return ''; + } + + // Protected methods + + /** + * Check for locale-related bug + * @access protected + */ + protected function _dochecks() { + //Check for locale-related bug + if(1.1==1) { + $this->Error('Don\'t alter the locale before including class file'); + } + //Check for decimal separator + if(sprintf('%.1f',1.0)!='1.0') { + setlocale(LC_NUMERIC,'C'); + } + } + + /** + * Return fonts path + * @access protected + */ + protected function _getfontpath() { + if(!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/font')) { + define('K_PATH_FONTS', dirname(__FILE__).'/font/'); + } + return defined('K_PATH_FONTS') ? K_PATH_FONTS : ''; + } + + /** + * Start document + * @access protected + */ + protected function _begindoc() { + //Start document + $this->state=1; + $this->_out('%PDF-'.$this->PDFVersion); + } + + /** + * _putpages + * @access protected + */ + protected function _putpages() { + $nb = $this->page; + if(!empty($this->AliasNbPages)) { + $nbstr = $this->UTF8ToUTF16BE($nb, false); + //Replace number of pages + for($n=1;$n<=$nb;$n++) { + $this->pages[$n]=str_replace($this->AliasNbPages, $nbstr, $this->pages[$n]); + } + } + if($this->DefOrientation=='P') { + $wPt=$this->fwPt; + $hPt=$this->fhPt; + } + else { + $wPt=$this->fhPt; + $hPt=$this->fwPt; + } + $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + for($n=1;$n<=$nb;$n++) { + //Page + $this->_newobj(); + $this->_out('<</Type /Page'); + $this->_out('/Parent 1 0 R'); + if(isset($this->OrientationChanges[$n])) { + $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$hPt,$wPt)); + } + $this->_out('/Resources 2 0 R'); + if(isset($this->PageLinks[$n])) { + //Links + $annots='/Annots ['; + foreach($this->PageLinks[$n] as $pl) { + $rect=sprintf('%.2f %.2f %.2f %.2f',$pl[0],$pl[1],$pl[0]+$pl[2],$pl[1]-$pl[3]); + $annots.='<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] '; + if(is_string($pl[4])) { + $annots.='/A <</S /URI /URI '.$this->_uristring($pl[4]).'>>>>'; + } + else { + $l=$this->links[$pl[4]]; + $h=isset($this->OrientationChanges[$l[0]]) ? $wPt : $hPt; + $annots.=sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>',1+2*$l[0],$h-$l[1]*$this->k); + } + } + $this->_out($annots.']'); + } + $this->_out('/Contents '.($this->n+1).' 0 R>>'); + $this->_out('endobj'); + //Page content + $p=($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n]; + $this->_newobj(); + $this->_out('<<'.$filter.'/Length '.strlen($p).'>>'); + $this->_putstream($p); + $this->_out('endobj'); + } + //Pages root + $this->offsets[1]=strlen($this->buffer); + $this->_out('1 0 obj'); + $this->_out('<</Type /Pages'); + $kids='/Kids ['; + for($i=0;$i<$nb;$i++) { + $kids.=(3+2*$i).' 0 R '; + } + $this->_out($kids.']'); + $this->_out('/Count '.$nb); + $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$wPt,$hPt)); + $this->_out('>>'); + $this->_out('endobj'); + } + + /** + * Adds fonts + * _putfonts + * @access protected + */ + protected function _putfonts() { + $nf=$this->n; + foreach($this->diffs as $diff) { + //Encodings + $this->_newobj(); + $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>'); + $this->_out('endobj'); + } + $mqr=get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + foreach($this->FontFiles as $file=>$info) { + //Font file embedding + $this->_newobj(); + $this->FontFiles[$file]['n']=$this->n; + $font=''; + $f=fopen($this->_getfontpath().strtolower($file),'rb',1); + if(!$f) { + $this->Error('Font file not found: '.$file); + } + while(!feof($f)) { + $font .= fread($f, 8192); + } + fclose($f); + $compressed=(substr($file,-2)=='.z'); + if(!$compressed && isset($info['length2'])) { + $header=(ord($font{0})==128); + if($header) { + //Strip first binary header + $font=substr($font,6); + } + if($header && ord($font{$info['length1']})==128) { + //Strip second binary header + $font=substr($font,0,$info['length1']).substr($font,$info['length1']+6); + } + } + $this->_out('<</Length '.strlen($font)); + if($compressed) { + $this->_out('/Filter /FlateDecode'); + } + $this->_out('/Length1 '.$info['length1']); + if(isset($info['length2'])) { + $this->_out('/Length2 '.$info['length2'].' /Length3 0'); + } + $this->_out('>>'); + $this->_putstream($font); + $this->_out('endobj'); + } + set_magic_quotes_runtime($mqr); + foreach($this->fonts as $k=>$font) { + //Font objects + $this->fonts[$k]['n']=$this->n+1; + $type=$font['type']; + $name=$font['name']; + if($type=='core') { + //Standard font + $this->_newobj(); + $this->_out('<</Type /Font'); + $this->_out('/BaseFont /'.$name); + $this->_out('/Subtype /Type1'); + if($name!='Symbol' && $name!='ZapfDingbats') { + $this->_out('/Encoding /WinAnsiEncoding'); + } + $this->_out('>>'); + $this->_out('endobj'); + } elseif($type=='Type1' OR $type=='TrueType') { + //Additional Type1 or TrueType font + $this->_newobj(); + $this->_out('<</Type /Font'); + $this->_out('/BaseFont /'.$name); + $this->_out('/Subtype /'.$type); + $this->_out('/FirstChar 32 /LastChar 255'); + $this->_out('/Widths '.($this->n+1).' 0 R'); + $this->_out('/FontDescriptor '.($this->n+2).' 0 R'); + if($font['enc']) { + if(isset($font['diff'])) { + $this->_out('/Encoding '.($nf+$font['diff']).' 0 R'); + } else { + $this->_out('/Encoding /WinAnsiEncoding'); + } + } + $this->_out('>>'); + $this->_out('endobj'); + //Widths + $this->_newobj(); + $cw=&$font['cw']; + $s='['; + for($i=32;$i<=255;$i++) { + $s.=$cw[chr($i)].' '; + } + $this->_out($s.']'); + $this->_out('endobj'); + //Descriptor + $this->_newobj(); + $s='<</Type /FontDescriptor /FontName /'.$name; + foreach($font['desc'] as $k=>$v) { + $s.=' /'.$k.' '.$v; + } + $file = $font['file']; + if($file) { + $s.=' /FontFile'.($type=='Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R'; + } + $this->_out($s.'>>'); + $this->_out('endobj'); + } else { + //Allow for additional types + $mtd='_put'.strtolower($type); + if(!method_exists($this, $mtd)) { + $this->Error('Unsupported font type: '.$type); + } + $this->$mtd($font); + } + } + } + + /** + * _putimages + * @access protected + */ + protected function _putimages() { + $filter=($this->compress) ? '/Filter /FlateDecode ' : ''; + reset($this->images); + while(list($file,$info)=each($this->images)) { + $this->_newobj(); + $this->images[$file]['n']=$this->n; + $this->_out('<</Type /XObject'); + $this->_out('/Subtype /Image'); + $this->_out('/Width '.$info['w']); + $this->_out('/Height '.$info['h']); + + if (isset($info["masked"])) { + $this->_out('/SMask '.($this->n-1).' 0 R'); + } + + if($info['cs']=='Indexed') { + $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal'])/3-1).' '.($this->n+1).' 0 R]'); + } + else { + $this->_out('/ColorSpace /'.$info['cs']); + if($info['cs']=='DeviceCMYK') { + $this->_out('/Decode [1 0 1 0 1 0 1 0]'); + } + } + $this->_out('/BitsPerComponent '.$info['bpc']); + if(isset($info['f'])) { + $this->_out('/Filter /'.$info['f']); + } + if(isset($info['parms'])) { + $this->_out($info['parms']); + } + if(isset($info['trns']) and is_array($info['trns'])) { + $trns=''; + for($i=0;$i<count($info['trns']);$i++) { + $trns.=$info['trns'][$i].' '.$info['trns'][$i].' '; + } + $this->_out('/Mask ['.$trns.']'); + } + $this->_out('/Length '.strlen($info['data']).'>>'); + $this->_putstream($info['data']); + unset($this->images[$file]['data']); + $this->_out('endobj'); + //Palette + if($info['cs']=='Indexed') { + $this->_newobj(); + $pal=($this->compress) ? gzcompress($info['pal']) : $info['pal']; + $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>'); + $this->_putstream($pal); + $this->_out('endobj'); + } + } + } + + /** + * _putxobjectdict + * @access protected + */ + function _putxobjectdict() { + foreach($this->images as $image) { + $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R'); + } + } + + /** + * _putresourcedict + * @access protected + */ + function _putresourcedict(){ + $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); + $this->_out('/Font <<'); + foreach($this->fonts as $font) { + $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R'); + } + $this->_out('>>'); + $this->_out('/XObject <<'); + $this->_putxobjectdict(); + $this->_out('>>'); + } + + /** + * _putresources + * @access protected + */ + function _putresources() { + $this->_putfonts(); + $this->_putimages(); + //Resource dictionary + $this->offsets[2]=strlen($this->buffer); + $this->_out('2 0 obj'); + $this->_out('<<'); + $this->_putresourcedict(); + $this->_out('>>'); + $this->_out('endobj'); + $this->_putjavascript(); + $this->_putbookmarks(); + // encryption + if ($this->encrypted) { + $this->_newobj(); + $this->enc_obj_id = $this->n; + $this->_out('<<'); + $this->_putencryption(); + $this->_out('>>'); + $this->_out('endobj'); + } + } + + /** + * _putinfo + * Adds some meta information + * @access protected + */ + protected function _putinfo() { + $this->_out('/CreationDate ('.$this->_escape('D:'.date('YmdHis')).')'); + $this->_out('/ModDate ('.$this->_escape('D:'.date('YmdHis')).')'); + $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER)); + if(!empty($this->title)) { + $this->_out('/Title '.$this->_textstring($this->title)); + } + if(!empty($this->subject)) { + $this->_out('/Subject '.$this->_textstring($this->subject)); + } + if(!empty($this->author)) { + $this->_out('/Author '.$this->_textstring($this->author)); + } + if(!empty($this->keywords)) { + $this->_out('/Keywords '.$this->_textstring($this->keywords)); + } + if(!empty($this->creator)) { + $this->_out('/Creator '.$this->_textstring($this->creator)); + } + } + + /** + * _putcatalog + * @access protected + */ + protected function _putcatalog() { + $this->_out('/Type /Catalog'); + $this->_out('/Pages 1 0 R'); + if($this->ZoomMode=='fullpage') { + $this->_out('/OpenAction [3 0 R /Fit]'); + } + elseif($this->ZoomMode=='fullwidth') { + $this->_out('/OpenAction [3 0 R /FitH null]'); + } + elseif($this->ZoomMode=='real') { + $this->_out('/OpenAction [3 0 R /XYZ null null 1]'); + } + elseif(!is_string($this->ZoomMode)) { + $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode/100).']'); + } + if($this->LayoutMode=='single') { + $this->_out('/PageLayout /SinglePage'); + } + elseif($this->LayoutMode=='continuous') { + $this->_out('/PageLayout /OneColumn'); + } + elseif($this->LayoutMode=='two') { + $this->_out('/PageLayout /TwoColumnLeft'); + } + if (!empty($this->javascript)) { + $this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>'); + } + if(count($this->outlines)>0) { + $this->_out('/Outlines '.$this->OutlineRoot.' 0 R'); + $this->_out('/PageMode /UseOutlines'); + } + if($this->rtl) { + $this->_out('/ViewerPreferences << /Direction /R2L >>'); + } + } + + /** + * _puttrailer + * @access protected + */ + protected function _puttrailer() { + $this->_out('/Size '.($this->n+1)); + $this->_out('/Root '.$this->n.' 0 R'); + $this->_out('/Info '.($this->n-1).' 0 R'); + if ($this->encrypted) { + $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R'); + $this->_out('/ID [()()]'); + } + } + + /** + * _putheader + * @access protected + */ + function _putheader() { + $this->_out('%PDF-'.$this->PDFVersion); + } + + /** + * _enddoc + * @access protected + */ + protected function _enddoc() { + $this->_putheader(); + $this->_putpages(); + $this->_putresources(); + //Info + $this->_newobj(); + $this->_out('<<'); + $this->_putinfo(); + $this->_out('>>'); + $this->_out('endobj'); + //Catalog + $this->_newobj(); + $this->_out('<<'); + $this->_putcatalog(); + $this->_out('>>'); + $this->_out('endobj'); + //Cross-ref + $o=strlen($this->buffer); + $this->_out('xref'); + $this->_out('0 '.($this->n+1)); + $this->_out('0000000000 65535 f '); + for($i=1;$i<=$this->n;$i++) { + $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i])); + } + //Trailer + $this->_out('trailer'); + $this->_out('<<'); + $this->_puttrailer(); + $this->_out('>>'); + $this->_out('startxref'); + $this->_out($o); + $this->_out('%%EOF'); + $this->state=3; + } + + /** + * _beginpage + * @access protected + */ + protected function _beginpage($orientation) { + $this->page++; + $this->pages[$this->page]=''; + $this->state=2; + if ($this->rtl) { + $this->x = $this->w - $this->rMargin; + } else { + $this->x = $this->lMargin; + } + $this->y = $this->tMargin; + $this->FontFamily=''; + //Page orientation + if(empty($orientation)) { + $orientation=$this->DefOrientation; + } + else { + $orientation=strtoupper($orientation{0}); + if($orientation!=$this->DefOrientation) { + $this->OrientationChanges[$this->page]=true; + } + } + if($orientation!=$this->CurOrientation) { + //Change orientation + if($orientation=='P') { + $this->wPt=$this->fwPt; + $this->hPt=$this->fhPt; + $this->w=$this->fw; + $this->h=$this->fh; + } + else { + $this->wPt=$this->fhPt; + $this->hPt=$this->fwPt; + $this->w=$this->fh; + $this->h=$this->fw; + } + $this->PageBreakTrigger=$this->h-$this->bMargin; + $this->CurOrientation=$orientation; + } + } + + /** + * End of page contents + * @access protected + */ + protected function _endpage() { + $this->state=1; + } + + /** + * Begin a new object + * @access protected + */ + protected function _newobj() { + $this->n++; + $this->offsets[$this->n]=strlen($this->buffer); + $this->_out($this->n.' 0 obj'); + } + + /** + * Underline text + * @param int $x X coordinate + * @param int $y Y coordinate + * @param string $txt text to underline + * @access protected + */ + protected function _dounderline($x, $y, $txt) { + $up = $this->CurrentFont['up']; + $ut = $this->CurrentFont['ut']; + $w = $this->GetStringWidth($txt) + $this->ws * substr_count($txt,' '); + return sprintf('%.2f %.2f %.2f %.2f re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt); + } + + + // REWRITTEN by Warren Sherliker wsherliker@gmail.com + // altered to allow compatibility with all sorts of image formats including gif. + // Can easily extend to work with others + // such as gd xbm etc. which are all supported by php 5+ + // (Requires GD library) + + /** + * Extract info from a JPEG file + * @param string $file image file to parse + * @return string + * @access protected + */ + protected function _parsejpg($file) { + if(!function_exists('imagecreatefromjpeg')) { + // GD is not installed, try legacy method + return $this->_legacyparsejpg($file); + } + $a=getimagesize($file); + if(empty($a)) { + $this->Error('Missing or incorrect image file: '.$file); + } + if($a[2]!=2) { + $this->Error('Not a JPEG file: '.$file); + } + $jpeg = imagecreatefromjpeg($file); + return $this->outputjpg($file, $jpeg); + } + + /** + * Extract info from a GIF file + * @param string $file image file to parse + * @return string + * @access protected + */ + protected function _parsegif($file) { + if(!function_exists('imagecreatefromgif')) { + // PDF doesn't support native GIF and GD is not installed + return false; + } + $a=getimagesize($file); + if(empty($a)) { + $this->Error('Missing or incorrect image file: '.$file); + } + if($a[2]!=1) { + $this->Error('Not a GIF file: '.$file); + } + // Temporary convert file to jpg and then delete this temp data file + $gif = imagecreatefromgif($file); + return $this->toJPEG($file, $gif); + } + + /** + * Extract info from a PNG file + * @param string $file image file to parse + * @return string + * @access protected + */ + protected function _parsepng($file) { + if(!function_exists('imagecreatefrompng')) { + // GD is not installed, try legacy method + return $this->_legacyparsepng($file); + } + $f=fopen($file,'rb'); + if(empty($f)) { + $this->Error('Can\'t open image file: '.$file); + } + //Check signature + if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { + $this->Error('Not a PNG file: '.$file); + } + //Read header chunk + fread($f,4); + if(fread($f,4)!='IHDR') { + $this->Error('Incorrect PNG file: '.$file); + } + // Temporary convert file to jpg and then delete this temp data file + $a=getimagesize($file); + $png = imagecreatefrompng($file); + return $this->toJPEG($file, $png); + } + + /** + * Extract info from a JPEG file without using GD + * @param string $file image file to parse + * @return string + * @access protected + */ + protected function _legacyparsejpg($file) { + $a=GetImageSize($file); + if(empty($a)) { + $this->Error('Missing or incorrect image file: '.$file); + } + if($a[2]!=2) { + $this->Error('Not a JPEG file: '.$file); + } + if(!isset($a['channels']) or $a['channels']==3) { + $colspace='DeviceRGB'; + } + elseif($a['channels']==4) { + $colspace='DeviceCMYK'; + } + else { + $colspace='DeviceGray'; + } + $bpc=isset($a['bits']) ? $a['bits'] : 8; + //Read whole file + $f=fopen($file,'rb'); + $data=''; + while(!feof($f)) { + $data.=fread($f,4096); + } + fclose($f); + return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data); + } + + /** + * Extract info from a PNG file without using GD + * @param string $file image file to parse + * @return string + * @access protected + */ + protected function _legacyparsepng($file) { + $f=fopen($file,'rb'); + if(empty($f)) { + $this->Error('Can\'t open image file: '.$file); + } + //Check signature + if(fread($f,8)!=chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { + $this->Error('Not a PNG file: '.$file); + } + //Read header chunk + fread($f,4); + if(fread($f,4)!='IHDR') { + $this->Error('Incorrect PNG file: '.$file); + } + $w=$this->_freadint($f); + $h=$this->_freadint($f); + $bpc=ord(fread($f,1)); + if($bpc>8) { + $this->Error('16-bit depth not supported: '.$file); + } + $ct=ord(fread($f,1)); + if($ct==0) { + $colspace='DeviceGray'; + } + elseif($ct==2) { + $colspace='DeviceRGB'; + } + elseif($ct==3) { + $colspace='Indexed'; + } + else { + $this->Error('Alpha channel not supported: '.$file); + } + if(ord(fread($f,1))!=0) { + $this->Error('Unknown compression method: '.$file); + } + if(ord(fread($f,1))!=0) { + $this->Error('Unknown filter method: '.$file); + } + if(ord(fread($f,1))!=0) { + $this->Error('Interlacing not supported: '.$file); + } + fread($f,4); + $parms='/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>'; + //Scan chunks looking for palette, transparency and image data + $pal=''; + $trns=''; + $data=''; + do { + $n=$this->_freadint($f); + $type=fread($f,4); + if($type=='PLTE') { + //Read palette + $pal=fread($f,$n); + fread($f,4); + } + elseif($type=='tRNS') { + //Read transparency info + $t=fread($f,$n); + if($ct==0) { + $trns=array(ord(substr($t,1,1))); + } + elseif($ct==2) { + $trns=array(ord(substr($t,1,1)),ord(substr($t,3,1)),ord(substr($t,5,1))); + } + else { + $pos=strpos($t,chr(0)); + if($pos!==false) { + $trns=array($pos); + } + } + fread($f,4); + } + elseif($type=='IDAT') { + //Read image data block + $data.=fread($f,$n); + fread($f,4); + } + elseif($type=='IEND') { + break; + } + else { + fread($f,$n+4); + } + } + while($n); + if($colspace=='Indexed' and empty($pal)) { + $this->Error('Missing palette in '.$file); + } + fclose($f); + return array('w'=>$w, 'h'=>$h, 'cs'=>$colspace, 'bpc'=>$bpc, 'f'=>'FlateDecode', 'parms'=>$parms, 'pal'=>$pal, 'trns'=>$trns, 'data'=>$data); + } + + /** + * Convert the loaded php image to a JPEG and then return a structure for the PDF creator. + * @param string $file Image file name. + * @param image $image Image object. + * return image JPEG image object. + * @access protected + */ + protected function toJPEG($file, $image) { + if ($image) { + // output + $tempname = tempnam(K_PATH_CACHE,'jpg'); + imagejpeg($image, $tempname, 100); + imagedestroy($image); + $retvars = $this->outputjpg($tempname); + // tidy up by removing temporary image + unlink($tempname); + return $retvars; + } else { + $this->Error('Can\'t open image file: '.$file); + } + } + + /** + * Get a JPEG filename and return a structure for the PDF creator. + * @param string $filename JPEG file name. + * @return array structure containing the image data + * @access protected + */ + protected function outputjpg($filename) { + $a=getimagesize($filename); + + if(!isset($a['channels']) or $a['channels']==3) { + $colspace='DeviceRGB'; + } + elseif($a['channels']==4) { + $colspace='DeviceCMYK'; + } + else { + $colspace='DeviceGray'; + } + $bpc=isset($a['bits']) ? $a['bits'] : 8; + //Read whole file + + $f=fopen($filename,'rb'); + $data=''; + while(!feof($f)) { + $data.=fread($f,4096); + } + fclose($f); + + return array('w'=>$a[0],'h'=>$a[1],'cs'=>$colspace,'bpc'=>$bpc,'f'=>'DCTDecode','data'=>$data); + } + + /// END OF REWRITE BY Warren Sherliker wsherliker@gmail.com + + /** + * Read a 4-byte integer from file + * @param string $f file name. + * @return 4-byte integer + * @access protected + */ + protected function _freadint($f) { + $a=unpack('Ni',fread($f,4)); + return $a['i']; + } + + /** + * Format a text string for meta information + * @param string $s string to escape. + * @return string escaped string. + * @access protected + */ + protected function _textstring($s) { + if($this->isunicode) { + //Convert string to UTF-16BE + $s = $this->UTF8ToUTF16BE($s, true); + } + if ($this->encrypted) { + $s = $this->_RC4($this->_objectkey($this->n), $s); + } + return '('. $this->_escape($s).')'; + } + + /** + * Format an URI string + * @param string $s string to escape. + * @return string escaped string. + * @access protected + */ + protected function _uristring($s) { + if ($this->encrypted) { + $s = $this->_RC4($this->_objectkey($this->n), $s); + } + return '('.$this->_escape($s).')'; + } + + /** + * Format a text string + * @param string $s string to escape. + * @return string escaped string. + * @access protected + */ + protected function _escapetext($s) { + if($this->isunicode) { + //Convert string to UTF-16BE and reverse RTL language + $s = $this->utf8StrRev($s, false, $this->tmprtl); + } + return $this->_escape($s); + } + + /** + * Add \ before \, ( and ) + * @param string $s string to escape. + * @return string escaped string. + * @access protected + */ + protected function _escape($s) { + // the chr(13) substitution fixes the Bugs item #1421290. + return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r')); + } + + /** + * Output a stream. + * @param string $s string to output. + * @access protected + */ + protected function _putstream($s) { + if ($this->encrypted) { + $s = $this->_RC4($this->_objectkey($this->n), $s); + } + $this->_out('stream'); + $this->_out($s); + $this->_out('endstream'); + } + + /** + * Output a string to the document. + * @param string $s string to output. + * @access protected + */ + protected function _out($s) { + if($this->state==2) { + $this->pages[$this->page] .= $s."\n"; + } + else { + $this->buffer .= $s."\n"; + } + } + + /** + * Adds unicode fonts.<br> + * Based on PDF Reference 1.3 (section 5) + * @access protected + * @author Nicola Asuni + * @since 1.52.0.TC005 (2005-01-05) + */ + protected function _puttruetypeunicode($font) { + // Type0 Font + // A composite font composed of other fonts, organized hierarchically + $this->_newobj(); + $this->_out('<</Type /Font'); + $this->_out('/Subtype /Type0'); + $this->_out('/BaseFont /'.$font['name'].''); + $this->_out('/Encoding /Identity-H'); //The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values. + $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]'); + $this->_out('/ToUnicode '.($this->n + 2).' 0 R'); + $this->_out('>>'); + $this->_out('endobj'); + + // CIDFontType2 + // A CIDFont whose glyph descriptions are based on TrueType font technology + $this->_newobj(); + $this->_out('<</Type /Font'); + $this->_out('/Subtype /CIDFontType2'); + $this->_out('/BaseFont /'.$font['name'].''); + $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R'); + $this->_out('/FontDescriptor '.($this->n + 3).' 0 R'); + if (isset($font['desc']['MissingWidth'])){ + $this->_out('/DW '.$font['desc']['MissingWidth'].''); // The default width for glyphs in the CIDFont MissingWidth + } + $w = ""; + foreach ($font['cw'] as $cid => $width) { + $w .= ''.$cid.' ['.$width.'] '; // define a specific width for each individual CID + } + $this->_out('/W ['.$w.']'); // A description of the widths for the glyphs in the CIDFont + $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R'); + $this->_out('>>'); + $this->_out('endobj'); + + // ToUnicode + // is a stream object that contains the definition of the CMap + // (PDF Reference 1.3 chap. 5.9) + $this->_newobj(); + $this->_out('<</Length 345>>'); + $this->_out('stream'); + $this->_out('/CIDInit /ProcSet findresource begin'); + $this->_out('12 dict begin'); + $this->_out('begincmap'); + $this->_out('/CIDSystemInfo'); + $this->_out('<</Registry (Adobe)'); + $this->_out('/Ordering (UCS)'); + $this->_out('/Supplement 0'); + $this->_out('>> def'); + $this->_out('/CMapName /Adobe-Identity-UCS def'); + $this->_out('/CMapType 2 def'); + $this->_out('1 begincodespacerange'); + $this->_out('<0000> <FFFF>'); + $this->_out('endcodespacerange'); + $this->_out('1 beginbfrange'); + $this->_out('<0000> <FFFF> <0000>'); + $this->_out('endbfrange'); + $this->_out('endcmap'); + $this->_out('CMapName currentdict /CMap defineresource pop'); + $this->_out('end'); + $this->_out('end'); + $this->_out('endstream'); + $this->_out('endobj'); + + // CIDSystemInfo dictionary + // A dictionary containing entries that define the character collection of the CIDFont. + $this->_newobj(); + $this->_out('<</Registry (Adobe)'); // A string identifying an issuer of character collections + $this->_out('/Ordering (UCS)'); // A string that uniquely names a character collection issued by a specific registry + $this->_out('/Supplement 0'); // The supplement number of the character collection. + $this->_out('>>'); + $this->_out('endobj'); + + // Font descriptor + // A font descriptor describing the CIDFont default metrics other than its glyph widths + $this->_newobj(); + $this->_out('<</Type /FontDescriptor'); + $this->_out('/FontName /'.$font['name']); + foreach ($font['desc'] as $key => $value) { + $this->_out('/'.$key.' '.$value); + } + if ($font['file']) { + // A stream containing a TrueType font program + $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R'); + } + $this->_out('>>'); + $this->_out('endobj'); + + // Embed CIDToGIDMap + // A specification of the mapping from CIDs to glyph indices + $this->_newobj(); + $ctgfile = $this->_getfontpath().strtolower($font['ctg']); + if(!file_exists($ctgfile)) { + $this->Error('Font file not found: '.$ctgfile); + } + $size = filesize($ctgfile); + $this->_out('<</Length '.$size.''); + if(substr($ctgfile, -2) == '.z') { // check file extension + /* Decompresses data encoded using the public-domain + zlib/deflate compression method, reproducing the + original text or binary data */ + $this->_out('/Filter /FlateDecode'); + } + $this->_out('>>'); + $this->_putstream(file_get_contents($ctgfile)); + $this->_out('endobj'); + } + + /** + * Converts UTF-8 strings to codepoints array.<br> + * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br> + * Based on: http://www.faqs.org/rfcs/rfc3629.html + * <pre> + * Char. number range | UTF-8 octet sequence + * (hexadecimal) | (binary) + * --------------------+----------------------------------------------- + * 0000 0000-0000 007F | 0xxxxxxx + * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * --------------------------------------------------------------------- + * + * ABFN notation: + * --------------------------------------------------------------------- + * UTF8-octets = *( UTF8-char ) + * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 + * UTF8-1 = %x00-7F + * UTF8-2 = %xC2-DF UTF8-tail + * + * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / + * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) + * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / + * %xF4 %x80-8F 2( UTF8-tail ) + * UTF8-tail = %x80-BF + * --------------------------------------------------------------------- + * </pre> + * @param string $str string to process. + * @return array containing codepoints (UTF-8 characters values) + * @access protected + * @author Nicola Asuni + * @since 1.53.0.TC005 (2005-01-05) + */ + protected function UTF8StringToArray($str) { + if(!$this->isunicode) { + return $str; // string is not in unicode + } + $unicode = array(); // array containing unicode values + $bytes = array(); // array containing single character byte sequences + $numbytes = 1; // number of octetc needed to represent the UTF-8 character + + $str .= ""; // force $str to be a string + $length = strlen($str); + + for($i = 0; $i < $length; $i++) { + $char = ord($str{$i}); // get one string character at time + if(count($bytes) == 0) { // get starting octect + if ($char <= 0x7F) { + $unicode[] = $char; // use the character "as is" because is ASCII + $numbytes = 1; + } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN) + $bytes[] = ($char - 0xC0) << 0x06; + $numbytes = 2; + } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN) + $bytes[] = ($char - 0xE0) << 0x0C; + $numbytes = 3; + } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN) + $bytes[] = ($char - 0xF0) << 0x12; + $numbytes = 4; + } else { + // use replacement character for other invalid sequences + $unicode[] = 0xFFFD; + $bytes = array(); + $numbytes = 1; + } + } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN + $bytes[] = $char - 0x80; + if (count($bytes) == $numbytes) { + // compose UTF-8 bytes to a single unicode value + $char = $bytes[0]; + for($j = 1; $j < $numbytes; $j++) { + $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06)); + } + if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) { + /* The definition of UTF-8 prohibits encoding character numbers between + U+D800 and U+DFFF, which are reserved for use with the UTF-16 + encoding form (as surrogate pairs) and do not directly represent + characters. */ + $unicode[] = 0xFFFD; // use replacement character + } + else { + $unicode[] = $char; // add char to array + } + // reset data for next char + $bytes = array(); + $numbytes = 1; + } + } else { + // use replacement character for other invalid sequences + $unicode[] = 0xFFFD; + $bytes = array(); + $numbytes = 1; + } + } + return $unicode; + } + + /** + * Converts UTF-8 strings to UTF16-BE.<br> + * @param string $str string to process. + * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) + * @return string + * @access protected + * @author Nicola Asuni + * @since 1.53.0.TC005 (2005-01-05) + * @uses UTF8StringToArray(), arrUTF8ToUTF16BE() + */ + protected function UTF8ToUTF16BE($str, $setbom=true) { + if(!$this->isunicode) { + return $str; // string is not in unicode + } + $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values + return $this->arrUTF8ToUTF16BE($unicode, $setbom); + } + + /** + * Converts array of UTF-8 characters to UTF16-BE string.<br> + * Based on: http://www.faqs.org/rfcs/rfc2781.html + * <pre> + * Encoding UTF-16: + * + * Encoding of a single character from an ISO 10646 character value to + * UTF-16 proceeds as follows. Let U be the character number, no greater + * than 0x10FFFF. + * + * 1) If U < 0x10000, encode U as a 16-bit unsigned integer and + * terminate. + * + * 2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, + * U' must be less than or equal to 0xFFFFF. That is, U' can be + * represented in 20 bits. + * + * 3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and + * 0xDC00, respectively. These integers each have 10 bits free to + * encode the character value, for a total of 20 bits. + * + * 4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order + * bits of W1 and the 10 low-order bits of U' to the 10 low-order + * bits of W2. Terminate. + * + * Graphically, steps 2 through 4 look like: + * U' = yyyyyyyyyyxxxxxxxxxx + * W1 = 110110yyyyyyyyyy + * W2 = 110111xxxxxxxxxx + * </pre> + * @param array $unicode array containing UTF-8 unicode values + * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF) + * @return string + * @access protected + * @author Nicola Asuni + * @since 2.1.000 (2008-01-08) + * @see UTF8ToUTF16BE() + */ + protected function arrUTF8ToUTF16BE($unicode, $setbom=true) { + $outstr = ""; // string to be returned + if ($setbom) { + $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM) + } + foreach($unicode as $char) { + if($char == 0xFFFD) { + $outstr .= "\xFF\xFD"; // replacement character + } elseif ($char < 0x10000) { + $outstr .= chr($char >> 0x08); + $outstr .= chr($char & 0xFF); + } else { + $char -= 0x10000; + $w1 = 0xD800 | ($char >> 0x10); + $w2 = 0xDC00 | ($char & 0x3FF); + $outstr .= chr($w1 >> 0x08); + $outstr .= chr($w1 & 0xFF); + $outstr .= chr($w2 >> 0x08); + $outstr .= chr($w2 & 0xFF); + } + } + return $outstr; + } + // ==================================================== + + /** + * Set header font. + * @param array $font font + * @since 1.1 + */ + public function setHeaderFont($font) { + $this->header_font = $font; + } + + /** + * Set footer font. + * @param array $font font + * @since 1.1 + */ + public function setFooterFont($font) { + $this->footer_font = $font; + } + + /** + * Set language array. + * @param array $language + * @since 1.1 + */ + public function setLanguageArray($language) { + $this->l = $language; + $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false; + } + + /** + * Set document barcode. + * @param string $bc barcode + */ + public function setBarcode($bc="") { + $this->barcode = $bc; + } + + /** + * Print Barcode. + * @param int $x x position in user units + * @param int $y y position in user units + * @param int $w width in user units + * @param int $h height position in user units + * @param string $type type of barcode (I25, C128A, C128B, C128C, C39) + * @param string $style barcode style + * @param string $font font for text + * @param int $xres x resolution + * @param string $code code to print + */ + public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) { + require_once(dirname(__FILE__)."/barcode/barcode.php"); + require_once(dirname(__FILE__)."/barcode/i25object.php"); + require_once(dirname(__FILE__)."/barcode/c39object.php"); + require_once(dirname(__FILE__)."/barcode/c128aobject.php"); + require_once(dirname(__FILE__)."/barcode/c128bobject.php"); + require_once(dirname(__FILE__)."/barcode/c128cobject.php"); + + if (empty($code)) { + return; + } + + if (empty($style)) { + $style = BCS_ALIGN_LEFT; + $style |= BCS_IMAGE_PNG; + $style |= BCS_TRANSPARENT; + //$style |= BCS_BORDER; + //$style |= BCS_DRAW_TEXT; + //$style |= BCS_STRETCH_TEXT; + //$style |= BCS_REVERSE_COLOR; + } + if (empty($font)) {$font = BCD_DEFAULT_FONT;} + if (empty($xres)) {$xres = BCD_DEFAULT_XRES;} + + $scale_factor = 1.5 * $xres * $this->k; + $bc_w = round($w * $scale_factor); //width in points + $bc_h = round($h * $scale_factor); //height in points + + switch (strtoupper($type)) { + case "I25": { + $obj = new I25Object($bc_w, $bc_h, $style, $code); + break; + } + case "C128A": { + $obj = new C128AObject($bc_w, $bc_h, $style, $code); + break; + } + default: + case "C128B": { + $obj = new C128BObject($bc_w, $bc_h, $style, $code); + break; + } + case "C128C": { + $obj = new C128CObject($bc_w, $bc_h, $style, $code); + break; + } + case "C39": { + $obj = new C39Object($bc_w, $bc_h, $style, $code); + break; + } + } + + $obj->SetFont($font); + $obj->DrawObject($xres); + + //use a temporary file.... + $tmpName = tempnam(K_PATH_CACHE,'img'); + imagepng($obj->getImage(), $tmpName); + $this->Image($tmpName, $x, $y, $w, $h, 'png'); + $obj->DestroyObject(); + unset($obj); + unlink($tmpName); + } + + /** + * Returns the PDF data. + */ + public function getPDFData() { + if($this->state < 3) { + $this->Close(); + } + return $this->buffer; + } + + // --- HTML PARSER FUNCTIONS --- + + /** + * Allows to preserve some HTML formatting.<br /> + * Supports: h1, h2, h3, h4, h5, h6, b, u, i, a, img, p, br, strong, em, font, blockquote, li, ul, ol, hr, td, th, tr, table, sup, sub, small + * @param string $html text to display + * @param boolean $ln if true add a new line after text (default = true) + * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0. + * @param boolean $reseth if true reset the last cell height (default false). + * @param boolean $cell if true add the default cMargin space to each Write. + */ + public function writeHTML($html, $ln=true, $fill=0, $reseth=false, $cell=false) { + + // store some variables + $html=strip_tags($html,"<h1><h2><h3><h4><h5><h6><b><u><i><a><img><p><br><br/><strong><em><font><blockquote><li><ul><ol><hr><td><th><tr><table><sup><sub><small><span><div>"); //remove all unsupported tags + //replace carriage returns, newlines and tabs + $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " "); + $html = strtr($html, $repTable); + $pattern = '/(<[^>]+>)/Uu'; + $a = preg_split($pattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); //explodes the string + + if ((empty($this->lasth))OR ($reseth)) { + //set row height + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + } + + foreach($a as $key=>$element) { + if (!preg_match($pattern, $element)) { + //Text + if($this->HREF) { + $this->addHtmlLink($this->HREF, $element, $fill); + } elseif($this->tdbegin) { + if((strlen(trim($element)) > 0) AND ($element != "&nbsp;")) { + $this->Cell($this->tdwidth, $this->tdheight, $this->unhtmlentities($element), $this->tableborder, '', $this->tdalign, $this->tdbgcolor); + } elseif($element == "&nbsp;") { + $this->Cell($this->tdwidth, $this->tdheight, '', $this->tableborder, '', $this->tdalign, $this->tdbgcolor); + } + } else { + + $ctmpmargin = $this->cMargin; + if(!$cell) { + $this->cMargin = 0; + } + $this->Write($this->lasth, stripslashes($this->unhtmlentities($element)), '', $fill, 0); + $this->cMargin = $ctmpmargin; + } + } else { + $element = substr($element, 1, -1); + //Tag + if($element{0}=='/') { + $this->closedHTMLTagHandler(strtolower(substr($element, 1))); + } + else { + //Extract attributes + // get tag name + preg_match('/([a-zA-Z0-9]*)/', $element, $tag); + $tag = strtolower($tag[0]); + // get attributes + preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER); + $attr = array(); // reset attribute array + while(list($id,$name)=each($attr_array[1])) { + $attr[strtolower($name)] = $attr_array[2][$id]; + } + $this->openHTMLTagHandler($tag, $attr, $fill); + } + } + } + if ($ln) { + $this->Ln($this->lasth); + } + } + + /** + * Prints a cell (rectangular area) with optional borders, background color and html text string. The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br /> + * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting. + * @param float $w Cell width. If 0, the cell extends up to the right margin. + * @param float $h Cell minimum height. The cell extends automatically if needed. + * @param float $x upper-left corner X coordinate + * @param float $y upper-left corner Y coordinate + * @param string $html html text to print. Default value: empty string. + * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> + * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> + Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0. + * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * @param boolean $reseth if true reset the last cell height (default false). + * @see Cell() + */ + public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=false) { + + if ((empty($this->lasth))OR ($reseth)) { + //set row height + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + } + + // get current page number + $startpage = $this->page; + + if (!empty($y)) { + $this->SetY($y); + } else { + $y = $this->GetY(); + } + if (!empty($x)) { + $this->SetX($x); + } else { + $x = $this->GetX(); + } + + if(empty($w)) { + if ($this->rtl) { + $w = $this->x - $this->lMargin; + } else { + $w = $this->w - $this->rMargin - $this->x; + } + } + + // store original margin values + $lMargin = $this->lMargin; + $rMargin = $this->rMargin; + + // set new margin values + if ($this->rtl) { + $this->SetLeftMargin($this->x - $w); + $this->SetRightMargin($this->w - $this->x); + } else { + $this->SetLeftMargin($this->x); + $this->SetRightMargin($this->w - $this->x - $w); + } + + // calculate remaining vertical space on first page ($startpage) + $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin(); + + // Write HTML text + $this->writeHTML($html, true, $fill, $reseth, true); + + // Get end-of-text Y position + $currentY = $this->GetY(); + // get latest page number + $endpage = $this->page; + + if (!empty($border)) { + // check if a new page has been created + if ($endpage > $startpage) { + // design borders around HTML cells. + for ($page=$startpage; $page<=$endpage; $page++) { + $this->page = $page; + if ($page==$startpage) { + $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin()); + $h = $restspace - 1; + } elseif ($page==$endpage) { + $this->SetY($this->tMargin); // put cursor at the beginning of text + $h = $currentY - $this->tMargin; + } else { + $this->SetY($this->tMargin); // put cursor at the beginning of text + $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin(); + } + $this->Cell($w, $h, "", $border, 1, '', 0); + } + } else { + $h = max($h, ($currentY - $y)); + $this->SetY($y); // put cursor at the beginning of text + // design a cell around the text + $this->Cell($w, $h, "", $border, 1, '', 0); + } + } + + // restore original margin values + $this->SetLeftMargin($lMargin); + $this->SetRightMargin($rMargin); + + + if($ln>0) { + //Go to the beginning of the next line + $this->SetY($currentY); + if($ln == 2) { + $this->SetX($x + $w); + } + } else { + // go left or right by case + $this->page = $startpage; + $this->y = $y; + $this->SetX($x + $w); + } + + } + + /** + * Process opening tags. + * @param string $tag tag name (in uppercase) + * @param string $attr tag attribute (in uppercase) + * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * @access private + */ + private function openHTMLTagHandler($tag, $attr, $fill=0) { + // check for text direction attribute + if (isset($attr['dir'])) { + $this->tmprtl = $attr['dir']=='rtl' ? 'R' : 'L'; + } else { + $this->tmprtl = false; + } + //Opening tag + switch($tag) { + case 'table': { + if ((isset($attr['border'])) AND ($attr['border'] != '')) { + $this->tableborder = $attr['border']; + } + else { + $this->tableborder = 0; + } + break; + } + case 'tr': { + break; + } + case 'td': + case 'th': { + if ((isset($attr['width'])) AND ($attr['width'] != '')) { + $this->tdwidth = ($attr['width']/4); + } + else { + $this->tdwidth = (($this->w - $this->lMargin - $this->rMargin) / $this->default_table_columns); + } + if ((isset($attr['height'])) AND ($attr['height'] != '')) { + $this->tdheight=($attr['height'] / $this->k); + } + else { + $this->tdheight = $this->lasth; + } + if ((isset($attr['align'])) AND ($attr['align'] != '')) { + switch ($attr['align']) { + case 'center': { + $this->tdalign = "C"; + break; + } + case 'right': { + $this->tdalign = "R"; + break; + } + default: + case 'left': { + $this->tdalign = "L"; + break; + } + } + } else { + if($this->rtl) { + $this->tdalign = "R"; + } else { + $this->tdalign = "L"; + } + } + if ((isset($attr['bgcolor'])) AND ($attr['bgcolor'] != '')) { + $coul = $this->convertColorHexToDec($attr['bgcolor']); + $this->SetFillColor($coul['R'], $coul['G'], $coul['B']); + $this->tdbgcolor=true; + } + $this->tdbegin=true; + break; + } + case 'hr': { + $this->Ln(); + if ((isset($attr['width'])) AND ($attr['width'] != '')) { + $hrWidth = $attr['width']; + } + else { + $hrWidth = $this->w - $this->lMargin - $this->rMargin; + } + $x = $this->GetX(); + $y = $this->GetY(); + $this->GetLineWidth(); + $prevlinewidth = $this->SetLineWidth(0.2); + $this->Line($x, $y, $x + $hrWidth, $y); + $this->SetLineWidth($prevlinewidth); + $this->Ln(); + break; + } + case 'strong': { + $this->setStyle('b', true); + break; + } + case 'em': { + $this->setStyle('i', true); + break; + } + case 'b': + case 'i': + case 'u': { + $this->setStyle($tag, true); + break; + } + case 'a': { + $this->HREF = $attr['href']; + break; + } + case 'img': { + if(isset($attr['src'])) { + // replace relative path with real server path + if ($attr['src'][0] == '/') { + $attr['src'] = $_SERVER['DOCUMENT_ROOT'].$attr['src']; + } + $attr['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $attr['src']); + if(!isset($attr['width'])) { + $attr['width'] = 0; + } + if(!isset($attr['height'])) { + $attr['height'] = 0; + } + if(!isset($attr['align'])) { + $align = 'N'; + } else { + switch($attr['align']) { + case 'top':{ + $align = 'T'; + break; + } + case 'middle':{ + $align = 'M'; + break; + } + case 'bottom':{ + $align = 'B'; + break; + } + default:{ + $align = 'N'; + break; + } + } + } + $this->Image($attr['src'], $this->GetX(),$this->GetY(), $this->pixelsToMillimeters($attr['width']), $this->pixelsToMillimeters($attr['height']), '', '', $align); + + } + break; + } + case 'ul': { + $this->listordered = false; + $this->listcount = 0; + break; + } + case 'ol': { + $this->listordered = true; + $this->listcount = 0; + break; + } + case 'li': { + $this->Ln(); + if ($this->listordered) { + if (isset($attr['value'])) { + $this->listcount = intval($attr['value']); + } + $this->lispacer = " ".(++$this->listcount).". "; + } else { + //unordered list simbol + $this->lispacer = " - "; + } + $rtldir = $this->tmprtl; + $this->tmprtl = false; + $this->Write($this->lasth, $this->lispacer, '', $fill); + $this->tmprtl = $rtldir; + break; + } + case 'blockquote': + case 'br': { + $this->Ln(); + if(strlen($this->lispacer) > 0) { + if ($this->rtl) { + $this->x -= $this->GetStringWidth($this->lispacer); + } else { + $this->x += $this->GetStringWidth($this->lispacer); + } + } + break; + } + case 'p': { + $this->Ln(); + $this->Ln(); + break; + } + case 'sup': { + $currentFontSize = $this->FontSize; + $this->tempfontsize = $this->FontSizePt; + $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); + $this->SetXY($this->GetX(), $this->GetY() - (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); + break; + } + case 'sub': { + $currentFontSize = $this->FontSize; + $this->tempfontsize = $this->FontSizePt; + $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); + $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); + break; + } + case 'small': { + $currentFontSize = $this->FontSize; + $this->tempfontsize = $this->FontSizePt; + $this->SetFontSize($this->FontSizePt * K_SMALL_RATIO); + $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)/3)); + break; + } + case 'font': { + if (isset($attr['color']) AND $attr['color']!='') { + $coul = $this->convertColorHexToDec($attr['color']); + $this->SetTextColor($coul['R'],$coul['G'],$coul['B']); + $this->issetcolor=true; + } + if (isset($attr['face']) and in_array(strtolower($attr['face']), $this->fontlist)) { + $this->SetFont(strtolower($attr['face'])); + $this->issetfont=true; + } + if (isset($attr['size'])) { + $headsize = intval($attr['size']); + } else { + $headsize = 0; + } + $currentFontSize = $this->FontSize; + $this->tempfontsize = $this->FontSizePt; + $this->SetFontSize($this->FontSizePt + $headsize); + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + break; + } + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': { + $headsize = (4 - substr($tag, 1)) * 2; + $currentFontSize = $this->FontSize; + $this->tempfontsize = $this->FontSizePt; + $this->SetFontSize($this->FontSizePt + $headsize); + $this->setStyle('b', true); + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + break; + } + } + } + + /** + * Process closing tags. + * @param string $tag tag name (in uppercase) + * @access private + */ + private function closedHTMLTagHandler($tag) { + //Closing tag + switch($tag) { + case 'td': + case 'th': { + $this->tdbegin = false; + $this->tdwidth = 0; + $this->tdheight = 0; + if($this->rtl) { + $this->tdalign = "R"; + } else { + $this->tdalign = "L"; + } + $this->tdbgcolor = false; + $this->SetFillColor($this->prevFillColor[0], $this->prevFillColor[1], $this->prevFillColor[2]); + break; + } + case 'tr': { + $this->Ln(); + break; + } + case 'table': { + $this->tableborder=0; + break; + } + case 'strong': { + $this->setStyle('b', false); + break; + } + case 'em': { + $this->setStyle('i', false); + break; + } + case 'b': + case 'i': + case 'u': { + $this->setStyle($tag, false); + break; + } + case 'a': { + $this->HREF = ''; + break; + } + case 'sup': { + $currentFontSize = $this->FontSize; + $this->SetFontSize($this->tempfontsize); + $this->tempfontsize = $this->FontSizePt; + $this->SetXY($this->GetX(), $this->GetY() - (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); + break; + } + case 'sub': { + $currentFontSize = $this->FontSize; + $this->SetFontSize($this->tempfontsize); + $this->tempfontsize = $this->FontSizePt; + $this->SetXY($this->GetX(), $this->GetY() + (($currentFontSize - $this->FontSize)*(K_SMALL_RATIO))); + break; + } + case 'small': { + $currentFontSize = $this->FontSize; + $this->SetFontSize($this->tempfontsize); + $this->tempfontsize = $this->FontSizePt; + $this->SetXY($this->GetX(), $this->GetY() - (($this->FontSize - $currentFontSize)/3)); + break; + } + case 'font': { + if ($this->issetcolor == true) { + $this->SetTextColor($this->prevTextColor[0], $this->prevTextColor[1], $this->prevTextColor[2]); + } + if ($this->issetfont) { + $this->FontFamily = $this->prevFontFamily; + $this->FontStyle = $this->prevFontStyle; + $this->SetFont($this->FontFamily); + $this->issetfont = false; + } + $currentFontSize = $this->FontSize; + $this->SetFontSize($this->tempfontsize); + $this->tempfontsize = $this->FontSizePt; + //$this->TextColor = $this->prevTextColor; + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + break; + } + case 'p': { + $this->Ln(); + $this->Ln(); + break; + } + case 'ul': + case 'ol': { + $this->Ln(); + $this->Ln(); + break; + } + case 'li': { + $this->lispacer = ""; + break; + } + case 'h1': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': { + $currentFontSize = $this->FontSize; + $this->SetFontSize($this->tempfontsize); + $this->tempfontsize = $this->FontSizePt; + $this->setStyle('b', false); + $this->Ln(); + $this->lasth = $this->FontSize * K_CELL_HEIGHT_RATIO; + break; + } + default : { + break; + } + } + $this->tmprtl = false; + } + + /** + * Sets font style. + * @param string $tag tag name (in lowercase) + * @param boolean $enable + * @access private + */ + private function setStyle($tag, $enable) { + //Modify style and select corresponding font + $this->$tag += ($enable ? 1 : -1); + $style=''; + foreach(array('b', 'i', 'u') as $s) { + if($this->$s > 0) { + $style .= $s; + } + } + $this->SetFont('', $style); + } + + /** + * Output anchor link. + * @param string $url link URL + * @param string $name link name + * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0. + * @access public + */ + public function addHtmlLink($url, $name, $fill=0) { + //Put a hyperlink + $this->SetTextColor(0, 0, 255); + $this->setStyle('u', true); + $this->Write($this->lasth, $name, $url, $fill); + $this->setStyle('u', false); + $this->SetTextColor(0); + } + + /** + * Returns an associative array (keys: R,G,B) from + * a hex html code (e.g. #3FE5AA). + * @param string $color hexadecimal html color [#rrggbb] + * @return array + * @access private + */ + private function convertColorHexToDec($color = "#000000"){ + $tbl_color = array(); + $tbl_color['R'] = hexdec(substr($color, 1, 2)); + $tbl_color['G'] = hexdec(substr($color, 3, 2)); + $tbl_color['B'] = hexdec(substr($color, 5, 2)); + return $tbl_color; + } + + /** + * Converts pixels to millimeters in 72 dpi. + * @param int $px pixels + * @return float millimeters + * @access private + */ + private function pixelsToMillimeters($px){ + return $px * 25.4 / 72; + } + + /** + * Reverse function for htmlentities. + * Convert entities in UTF-8. + * + * @param $text_to_convert Text to convert. + * @return string converted + */ + public function unhtmlentities($text_to_convert) { + return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding); + } + + // ENCRYPTION METHODS ---------------------------------- + // SINCE 2.0.000 (2008-01-02) + /** + * Compute encryption key depending on object number where the encrypted data is stored + * @param int $n object number + * @since 2.0.000 (2008-01-02) + */ + protected function _objectkey($n) { + return substr($this->_md5_16($this->encryption_key.pack('VXxx',$n)),0,10); + } + + /** + * Put encryption on PDF document + * @since 2.0.000 (2008-01-02) + */ + protected function _putencryption() { + $this->_out('/Filter /Standard'); + $this->_out('/V 1'); + $this->_out('/R 2'); + $this->_out('/O ('.$this->_escape($this->Ovalue).')'); + $this->_out('/U ('.$this->_escape($this->Uvalue).')'); + $this->_out('/P '.$this->Pvalue); + } + + /** + * Returns the input text exrypted using RC4 algorithm and the specified key. + * RC4 is the standard encryption algorithm used in PDF format + * @param string $key encryption key + * @param String $text input text to be encrypted + * @return String encrypted text + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + protected function _RC4($key, $text) { + if ($this->last_rc4_key != $key) { + $k = str_repeat($key, 256/strlen($key)+1); + $rc4 = range(0,255); + $j = 0; + for ($i=0; $i<256; $i++) { + $t = $rc4[$i]; + $j = ($j + $t + ord($k{$i})) % 256; + $rc4[$i] = $rc4[$j]; + $rc4[$j] = $t; + } + $this->last_rc4_key = $key; + $this->last_rc4_key_c = $rc4; + } else { + $rc4 = $this->last_rc4_key_c; + } + $len = strlen($text); + $a = 0; + $b = 0; + $out = ''; + for ($i=0; $i<$len; $i++) { + $a = ($a+1)%256; + $t= $rc4[$a]; + $b = ($b+$t)%256; + $rc4[$a] = $rc4[$b]; + $rc4[$b] = $t; + $k = $rc4[($rc4[$a]+$rc4[$b])%256]; + $out.=chr(ord($text{$i}) ^ $k); + } + return $out; + } + + /** + * Encrypts a string using MD5 and returns it's value as a binary string. + * @param string $str input string + * @return String MD5 encrypted binary string + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + protected function _md5_16($str) { + return pack('H*',md5($str)); + } + + /** + * Compute O value (used for RC4 encryption) + * @param String $user_pass user password + * @param String $owner_pass user password + * @return String O value + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + protected function _Ovalue($user_pass, $owner_pass) { + $tmp = $this->_md5_16($owner_pass); + $owner_RC4_key = substr($tmp,0,5); + return $this->_RC4($owner_RC4_key, $user_pass); + } + + /** + * Compute U value (used for RC4 encryption) + * @return String U value + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + protected function _Uvalue() { + return $this->_RC4($this->encryption_key, $this->padding); + } + + /** + * Compute encryption key + * @param String $user_pass user password + * @param String $owner_pass user password + * @param String $protection protection type + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + protected function _generateencryptionkey($user_pass, $owner_pass, $protection) { + // Pad passwords + $user_pass = substr($user_pass.$this->padding,0,32); + $owner_pass = substr($owner_pass.$this->padding,0,32); + // Compute O value + $this->Ovalue = $this->_Ovalue($user_pass,$owner_pass); + // Compute encyption key + $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF"); + $this->encryption_key = substr($tmp,0,5); + // Compute U value + $this->Uvalue = $this->_Uvalue(); + // Compute P value + $this->Pvalue = -(($protection^255)+1); + } + + /** + * Set document protection + * The permission array is composed of values taken from the following ones: + * - copy: copy text and images to the clipboard + * - print: print the document + * - modify: modify it (except for annotations and forms) + * - annot-forms: add annotations and forms + * Remark: the protection against modification is for people who have the full Acrobat product. + * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access. + * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts. + * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms) + * @param String $user_pass user password. Empty by default. + * @param String $owner_pass owner password. If not specified, a random value is used. + * @since 2.0.000 (2008-01-02) + * @author Klemen Vodopivec + */ + public function SetProtection($permissions=array(),$user_pass='',$owner_pass=null) { + $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32); + $protection = 192; + foreach($permissions as $permission) { + if (!isset($options[$permission])) { + $this->Error('Incorrect permission: '.$permission); + } + $protection += $options[$permission]; + } + if ($owner_pass === null) { + $owner_pass = uniqid(rand()); + } + $this->encrypted = true; + $this->_generateencryptionkey($user_pass, $owner_pass, $protection); + } + + // END OF ENCRYPTION FUNCTIONS ------------------------- + + // START TRANSFORMATIONS SECTION ----------------------- + // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni + + /** + * Starts a 2D tranformation saving current graphic state. + * This function must be called before scaling, mirroring, translation, rotation and skewing. + * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function StartTransform() { + $this->_out('q'); + } + + /** + * Stops a 2D tranformation restoring previous graphic state. + * This function must be called after scaling, mirroring, translation, rotation and skewing. + * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior. + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function StopTransform() { + $this->_out('Q'); + } + /** + * Horizontal Scaling. + * @param float $s_x scaling factor for width as percent. 0 is not allowed. + * @param int $x abscissa of the scaling center. Default is current x position + * @param int $y ordinate of the scaling center. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function ScaleX($s_x, $x='', $y=''){ + $this->Scale($s_x, 100, $x, $y); + } + + /** + * Vertical Scaling. + * @param float $s_y scaling factor for height as percent. 0 is not allowed. + * @param int $x abscissa of the scaling center. Default is current x position + * @param int $y ordinate of the scaling center. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function ScaleY($s_y, $x='', $y=''){ + $this->Scale(100, $s_y, $x, $y); + } + + /** + * Vertical and horizontal proportional Scaling. + * @param float $s scaling factor for width and height as percent. 0 is not allowed. + * @param int $x abscissa of the scaling center. Default is current x position + * @param int $y ordinate of the scaling center. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function ScaleXY($s, $x='', $y=''){ + $this->Scale($s, $s, $x, $y); + } + + /** + * Vertical and horizontal non-proportional Scaling. + * @param float $s_x scaling factor for width as percent. 0 is not allowed. + * @param float $s_y scaling factor for height as percent. 0 is not allowed. + * @param int $x abscissa of the scaling center. Default is current x position + * @param int $y ordinate of the scaling center. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function Scale($s_x, $s_y, $x='', $y=''){ + if($x === '') { + $x=$this->x; + } + if($y === '') { + $y=$this->y; + } + if($this->rtl) { + $x = $this->w - $x; + } + if($s_x == 0 OR $s_y == 0) + $this->Error('Please use values unequal to zero for Scaling'); + $y=($this->h-$y)*$this->k; + $x*=$this->k; + //calculate elements of transformation matrix + $s_x/=100; + $s_y/=100; + $tm[0]=$s_x; + $tm[1]=0; + $tm[2]=0; + $tm[3]=$s_y; + $tm[4]=$x*(1-$s_x); + $tm[5]=$y*(1-$s_y); + //scale the coordinate system + $this->Transform($tm); + } + + /** + * Horizontal Mirroring. + * @param int $x abscissa of the point. Default is current x position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function MirrorH($x=''){ + $this->Scale(-100, 100, $x); + } + + /** + * Verical Mirroring. + * @param int $y ordinate of the point. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function MirrorV($y=''){ + $this->Scale(100, -100, '', $y); + } + + /** + * Point reflection mirroring. + * @param int $x abscissa of the point. Default is current x position + * @param int $y ordinate of the point. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function MirrorP($x='',$y=''){ + $this->Scale(-100, -100, $x, $y); + } + + /** + * Reflection against a straight line through point (x, y) with the gradient angle (angle). + * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line). + * @param int $x abscissa of the point. Default is current x position + * @param int $y ordinate of the point. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function MirrorL($angle=0, $x='',$y=''){ + $this->Scale(-100, 100, $x, $y); + $this->Rotate(-2*($angle-90),$x,$y); + } + + /** + * Translate graphic object horizontally. + * @param int $t_x movement to the right + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function TranslateX($t_x){ + $this->Translate($t_x, 0); + } + + /** + * Translate graphic object vertically. + * @param int $t_y movement to the bottom + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function TranslateY($t_y){ + $this->Translate(0, $t_y, $x, $y); + } + + /** + * Translate graphic object horizontally and vertically. + * @param int $t_x movement to the right + * @param int $t_y movement to the bottom + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function Translate($t_x, $t_y){ + if($this->rtl) { + $t_x = -$t_x; + } + //calculate elements of transformation matrix + $tm[0]=1; + $tm[1]=0; + $tm[2]=0; + $tm[3]=1; + $tm[4]=$t_x*$this->k; + $tm[5]=-$t_y*$this->k; + //translate the coordinate system + $this->Transform($tm); + } + + /** + * Rotate object. + * @param float $angle angle in degrees for counter-clockwise rotation + * @param int $x abscissa of the rotation center. Default is current x position + * @param int $y ordinate of the rotation center. Default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function Rotate($angle, $x='', $y=''){ + if($x === '') { + $x=$this->x; + } + if($y === '') { + $y=$this->y; + } + if($this->rtl) { + $x = $this->w - $x; + $angle = -$angle; + } + $y=($this->h-$y)*$this->k; + $x*=$this->k; + //calculate elements of transformation matrix + $tm[0]=cos(deg2rad($angle)); + $tm[1]=sin(deg2rad($angle)); + $tm[2]=-$tm[1]; + $tm[3]=$tm[0]; + $tm[4]=$x+$tm[1]*$y-$tm[0]*$x; + $tm[5]=$y-$tm[0]*$y-$tm[1]*$x; + //rotate the coordinate system around ($x,$y) + $this->Transform($tm); + } + + /** + * Skew horizontally. + * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) + * @param int $x abscissa of the skewing center. default is current x position + * @param int $y ordinate of the skewing center. default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function SkewX($angle_x, $x='', $y=''){ + $this->Skew($angle_x, 0, $x, $y); + } + + /** + * Skew vertically. + * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) + * @param int $x abscissa of the skewing center. default is current x position + * @param int $y ordinate of the skewing center. default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function SkewY($angle_y, $x='', $y=''){ + $this->Skew(0, $angle_y, $x, $y); + } + + /** + * Skew. + * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right) + * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top) + * @param int $x abscissa of the skewing center. default is current x position + * @param int $y ordinate of the skewing center. default is current y position + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + public function Skew($angle_x, $angle_y, $x='', $y=''){ + if($x === '') { + $x=$this->x; + } + if($y === '') { + $y=$this->y; + } + if($this->rtl) { + $x = $this->w - $x; + $angle_x = -$angle_x; + } + if($angle_x <= -90 OR $angle_x >= 90 OR $angle_y <= -90 OR $angle_y >= 90) + $this->Error('Please use values between -90� and 90� for skewing'); + $x*=$this->k; + $y=($this->h-$y)*$this->k; + //calculate elements of transformation matrix + $tm[0]=1; + $tm[1]=tan(deg2rad($angle_y)); + $tm[2]=tan(deg2rad($angle_x)); + $tm[3]=1; + $tm[4]=-$tm[2]*$y; + $tm[5]=-$tm[1]*$x; + //skew the coordinate system + $this->Transform($tm); + } + + /** + * Apply graphic transformations. + * @since 2.1.000 (2008-01-07) + * @see StartTransform(), StopTransform() + */ + protected function Transform($tm){ + $this->_out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', $tm[0],$tm[1],$tm[2],$tm[3],$tm[4],$tm[5])); + } + + // END TRANSFORMATIONS SECTION ------------------------- + + + // START GRAPHIC FUNCTIONS SECTION --------------------- + // The following section is based on the code provided by David Hernandez Sanz + + /** + * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page. + * @param float $width The width. + * @since 1.0 + * @see Line(), Rect(), Cell(), MultiCell() + */ + public function SetLineWidth($width) { + //Set line width + $this->LineWidth=$width; + if($this->page>0) { + $this->_out(sprintf('%.2f w',$width*$this->k)); + } + } + + /** + * Returns the current the line width. + * @return int Line width + * @since 2.1.000 (2008-01-07) + * @see Line(), SetLineWidth() + */ + public function GetLineWidth() { + return $this->LineWidth; + } + + /** + * Set line style. + * + * @param array $style Line style. Array with keys among the following: + * <ul> + * <li>width (float): Width of the line in user units.</li> + * <li>cap (string): Type of cap to put on the line. Possible values are: + * butt, round, square. The difference between "square" and "butt" is that + * "square" projects a flat end past the end of the line.</li> + * <li>join (string): Type of join. Possible values are: miter, round, + * bevel.</li> + * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with + * series of length values, which are the lengths of the on and off dashes. + * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on, + * 1 off, 2 on, 1 off, ...</li> + * <li>phase (integer): Modifier on the dash pattern which is used to shift + * the point at which the pattern starts.</li> + * <li>color (array): Draw color. Format: array(red, green, blue).</li> + * </ul> + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function SetLineStyle($style) { + extract($style); + if (isset($width)) { + $width_prev = $this->LineWidth; + $this->SetLineWidth($width); + $this->LineWidth = $width_prev; + } + if (isset($cap)) { + $ca = array("butt" => 0, "round"=> 1, "square" => 2); + if (isset($ca[$cap])) { + $this->_out($ca[$cap] . " J"); + } + } + if (isset($join)) { + $ja = array("miter" => 0, "round" => 1, "bevel" => 2); + if (isset($ja[$join])) { + $this->_out($ja[$join] . " j"); + } + } + if (isset($dash)) { + $dash_string = ""; + if ($dash) { + // phpMyAdmin change + if (preg_match("/^.+,/", $dash)) { + $tab = explode(",", $dash); + } else { + $tab = array($dash); + } + $dash_string = ""; + foreach ($tab as $i => $v) { + if ($i) { + $dash_string .= " "; + } + $dash_string .= sprintf("%.2f", $v); + } + } + if (!isset($phase) OR !$dash) { + $phase = 0; + } + $this->_out(sprintf("[%s] %.2f d", $dash_string, $phase)); + } + if (isset($color)) { + list($r, $g, $b) = $color; + $this->SetDrawColor($r, $g, $b); + } + } + + /* + * Set a draw point. + * @param float $x Abscissa of point. + * @param float $y Ordinate of point. + * @access private + * @since 2.1.000 (2008-01-08) + */ + protected function _outPoint($x, $y) { + if($this->rtl) { + $x = $this->w - $x; + } + $this->_out(sprintf("%.2f %.2f m", $x * $this->k, ($this->h - $y) * $this->k)); + } + + /* + * Draws a line from last draw point. + * @param float $x Abscissa of end point. + * @param float $y Ordinate of end point. + * @access private + * @since 2.1.000 (2008-01-08) + */ + protected function _outLine($x, $y) { + if($this->rtl) { + $x = $this->w - $x; + } + $this->_out(sprintf("%.2f %.2f l", $x * $this->k, ($this->h - $y) * $this->k)); + } + + /** + * Draws a rectangle. + * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). + * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). + * @param float $w Width. + * @param float $h Height. + * @param string $op options + * @access protected + * @since 2.1.000 (2008-01-08) + */ + protected function _outRect($x, $y, $w, $h, $op) { + if($this->rtl) { + $x = $this->w - $x - $w; + } + $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s',$x*$this->k,($this->h-$y)*$this->k,$w*$this->k,-$h*$this->k,$op)); + } + + /* + * Draws a Bezier curve from last draw point. + * The Bezier curve is a tangent to the line between the control points at either end of the curve. + * @param float $x1 Abscissa of control point 1. + * @param float $y1 Ordinate of control point 1. + * @param float $x2 Abscissa of control point 2. + * @param float $y2 Ordinate of control point 2. + * @param float $x3 Abscissa of end point. + * @param float $y3 Ordinate of end point. + * @access private + * @since 2.1.000 (2008-01-08) + */ + protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) { + if($this->rtl) { + $x1 = $this->w - $x1; + $x2 = $this->w - $x2; + $x3 = $this->w - $x3; + } + $this->_out(sprintf("%.2f %.2f %.2f %.2f %.2f %.2f c", $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k)); + } + + /** + * Draws a line between two points. + * @param float $x1 Abscissa of first point. + * @param float $y1 Ordinate of first point. + * @param float $x2 Abscissa of second point. + * @param float $y2 Ordinate of second point. + * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @access public + * @since 1.0 + * @see SetLineWidth(), SetDrawColor(), SetLineStyle() + */ + public function Line($x1, $y1, $x2, $y2, $style = array()) { + if ($style) { + $this->SetLineStyle($style); + } + $this->_outPoint($x1, $y1); + $this->_outLine($x2, $y2); + $this->_out(" S"); + } + + /** + * Draws a rectangle. + * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language). + * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language). + * @param float $w Width. + * @param float $h Height. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $border_style Border style of rectangle. Array with keys among the following: + * <ul> + * <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li> + * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li> + * </ul> + * If a key is not present or is null, not draws the border. Default value: default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @since 1.0 + * @see SetLineStyle() + */ + public function Rect($x, $y, $w, $h, $style='', $border_style = array(), $fill_color = array()) { + if (!(false === strpos($style, "F")) AND $fill_color) { + list($r, $g, $b) = $fill_color; + $this->SetFillColor($r, $g, $b); + } + switch ($style) { + case "F": { + $op='f'; + $border_style = array(); + $this->_outRect($x, $y, $w, $h, $op); + break; + } + case "DF": + case "FD": { + if (!$border_style OR isset($border_style["all"])) { + $op='B'; + if (isset($border_style["all"])) { + $this->SetLineStyle($border_style["all"]); + $border_style = array(); + } + } else { + $op='f'; + } + $this->_outRect($x, $y, $w, $h, $op); + break; + } + default: { + $op='S'; + if (!$border_style OR isset($border_style["all"])) { + if (isset($border_style["all"]) && $border_style["all"]) { + $this->SetLineStyle($border_style["all"]); + $border_style = array(); + } + $this->_outRect($x, $y, $w, $h, $op); + } + break; + } + } + if ($border_style) { + $border_style2 = array(); + foreach ($border_style as $line => $value) { + $lenght = strlen($line); + for ($i = 0; $i < $lenght; $i++) { + $border_style2[$line[$i]] = $value; + } + } + $border_style = $border_style2; + if (isset($border_style["L"]) && $border_style["L"]) { + $this->Line($x, $y, $x, $y + $h, $border_style["L"]); + } + if (isset($border_style["T"]) && $border_style["T"]) { + $this->Line($x, $y, $x + $w, $y, $border_style["T"]); + } + if (isset($border_style["R"]) && $border_style["R"]) { + $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style["R"]); + } + if (isset($border_style["B"]) && $border_style["B"]) { + $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style["B"]); + } + } + } + + + /** + * Draws a Bezier curve. + * The Bezier curve is a tangent to the line between the control points at + * either end of the curve. + * @param float $x0 Abscissa of start point. + * @param float $y0 Ordinate of start point. + * @param float $x1 Abscissa of control point 1. + * @param float $y1 Ordinate of control point 1. + * @param float $x2 Abscissa of control point 2. + * @param float $y2 Ordinate of control point 2. + * @param float $x3 Abscissa of end point. + * @param float $y3 Ordinate of end point. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @see SetLineStyle() + * @since 2.1.000 (2008-01-08) + */ + public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style = "", $line_style = array(), $fill_color = array()) { + if (!(false === strpos($style, "F")) AND $fill_color) { + list($r, $g, $b) = $fill_color; + $this->SetFillColor($r, $g, $b); + } + switch ($style) { + case "F": { + $op = "f"; + $line_style = array(); + break; + } + case "FD": + case "DF": { + $op = "B"; + break; + } + default: { + $op = "S"; + break; + } + } + if ($line_style) { + $this->SetLineStyle($line_style); + } + $this->_outPoint($x0, $y0); + $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3); + $this->_out($op); + } + + /** + * Draws an ellipse. + * An ellipse is formed from n Bezier curves. + * @param float $x0 Abscissa of center point. + * @param float $y0 Ordinate of center point. + * @param float $rx Horizontal radius. + * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0. + * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. + * @param float $astart: Angle start of draw line. Default value: 0. + * @param float $afinish: Angle finish of draw line. Default value: 360. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * <li>C: Draw close.</li> + * </ul> + * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @param integer $nc Number of curves used in ellipse. Default value: 8. + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function Ellipse($x0, $y0, $rx, $ry = 0, $angle = 0, $astart = 0, $afinish = 360, $style = "", $line_style = array(), $fill_color = array(), $nc = 8) { + if ($angle) { + $this->StartTransform(); + $this->Rotate($angle, $x0, $y0); + $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc); + $this->StopTransform(); + return; + } + if ($rx) { + if (!(false === strpos($style, "F")) AND $fill_color) { + list($r, $g, $b) = $fill_color; + $this->SetFillColor($r, $g, $b); + } + switch ($style) { + case "F": { + $op = "f"; + $line_style = array(); + break; + } + case "FD": + case "DF": { + $op = "B"; + break; + } + case "C": { + $op = "s"; // Small "s" signifies closing the path as well + break; + } + default: { + $op = "S"; + break; + } + } + if ($line_style) { + $this->SetLineStyle($line_style); + } + if (!$ry) { + $ry = $rx; + } + $rx *= $this->k; + $ry *= $this->k; + if ($nc < 2){ + $nc = 2; + } + $astart = deg2rad((float) $astart); + $afinish = deg2rad((float) $afinish); + $total_angle = $afinish - $astart; + $dt = $total_angle / $nc; + $dtm = $dt/3; + $x0 *= $this->k; + $y0 = ($this->h - $y0) * $this->k; + $t1 = $astart; + $a0 = $x0 + ($rx * cos($t1)); + $b0 = $y0 + ($ry * sin($t1)); + $c0 = -$rx * sin($t1); + $d0 = $ry * cos($t1); + $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k)); + for ($i = 1; $i <= $nc; $i++) { + // Draw this bit of the total curve + $t1 = ($i * $dt) + $astart; + $a1 = $x0 + ($rx * cos($t1)); + $b1 = $y0 + ($ry * sin($t1)); + $c1 = -$rx * sin($t1); + $d1 = $ry * cos($t1); + $this->_outCurve(($a0 + ($c0 * $dtm)) / $this->k, $this->h - (($b0 + ($d0 * $dtm)) / $this->k), ($a1 - ($c1 * $dtm)) / $this->k, $this->h - (($b1 - ($d1 * $dtm)) / $this->k), $a1 / $this->k, $this->h - ($b1 / $this->k)); + $a0 = $a1; + $b0 = $b1; + $c0 = $c1; + $d0 = $d1; + } + $this->_out($op); + } + } + + /** + * Draws a circle. + * A circle is formed from n Bezier curves. + * @param float $x0 Abscissa of center point. + * @param float $y0 Ordinate of center point. + * @param float $r Radius. + * @param float $astart: Angle start of draw line. Default value: 0. + * @param float $afinish: Angle finish of draw line. Default value: 360. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * <li>C: Draw close.</li> + * </ul> + * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @param integer $nc Number of curves used in circle. Default value: 8. + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function Circle($x0, $y0, $r, $astart = 0, $afinish = 360, $style = "", $line_style = array(), $fill_color = array(), $nc = 8) { + $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc); + } + + /** + * Draws a polygon. + * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1)) + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $line_style Line style of polygon. Array with keys among the following: + * <ul> + * <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li> + * <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li> + * </ul> + * If a key is not present or is null, not draws the line. Default value is default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function Polygon($p, $style = "", $line_style = array(), $fill_color = array()) { + $np = count($p) / 2; + if (!(false === strpos($style, "F")) AND $fill_color) { + list($r, $g, $b) = $fill_color; + $this->SetFillColor($r, $g, $b); + } + switch ($style) { + case "F": { + $line_style = array(); + $op = "f"; + break; + } + case "FD": + case "DF": { + $op = "B"; + break; + } + default: { + $op = "S"; + break; + } + } + $draw = true; + if ($line_style) { + if (isset($line_style["all"])) { + $this->SetLineStyle($line_style["all"]); + } + else { // 0 .. (np - 1), op = {B, S} + $draw = false; + if ("B" == $op) { + $op = "f"; + $this->_outPoint($p[0], $p[1]); + for ($i = 2; $i < ($np * 2); $i = $i + 2) { + $this->_outLine($p[$i], $p[$i + 1]); + } + $this->_outLine($p[0], $p[1]); + $this->_out($op); + } + $p[$np * 2] = $p[0]; + $p[($np * 2) + 1] = $p[1]; + for ($i = 0; $i < $np; $i++) { + if (isset($line_style[$i])) { + $this->Line($p[$i * 2], $p[($i * 2) + 1], $p[($i * 2) + 2], $p[($i * 2) + 3], $line_style[$i]); + } + } + } + } + if ($draw) { + $this->_outPoint($p[0], $p[1]); + for ($i = 2; $i < ($np * 2); $i = $i + 2) { + $this->_outLine($p[$i], $p[$i + 1]); + } + $this->_outLine($p[0], $p[1]); + $this->_out($op); + } + } + + /** + * Draws a regular polygon. + * @param float $x0 Abscissa of center point. + * @param float $y0 Ordinate of center point. + * @param float $r: Radius of inscribed circle. + * @param integer $ns Number of sides. + * @param float $angle Angle oriented (anti-clockwise). Default value: 0. + * @param boolean $draw_circle Draw inscribed circle or not. Default value: false. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $line_style Line style of polygon sides. Array with keys among the following: + * <ul> + * <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li> + * <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> + * </ul> + * If a key is not present or is null, not draws the side. Default value is default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function RegularPolygon($x0, $y0, $r, $ns, $angle = 0, $draw_circle = false, $style = "", $line_style = array(), $fill_color = array(), $circle_style = "", $circle_outLine_style = array(), $circle_fill_color = array()) { + if (3 > $ns) { + $ns = 3; + } + if ($draw_circle) { + $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); + } + $p = array(); + for ($i = 0; $i < $ns; $i++) { + $a = $angle + ($i * 360 / $ns); + $a_rad = deg2rad((float) $a); + $p[] = $x0 + ($r * sin($a_rad)); + $p[] = $y0 + ($r * cos($a_rad)); + } + $this->Polygon($p, $style, $line_style, $fill_color); + } + + /** + * Draws a star polygon + * @param float $x0 Abscissa of center point. + * @param float $y0 Ordinate of center point. + * @param float $r Radius of inscribed circle. + * @param integer $nv Number of vertices. + * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon). + * @param float $angle: Angle oriented (anti-clockwise). Default value: 0. + * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false. + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $line_style Line style of polygon sides. Array with keys among the following: + * <ul> + * <li>all: Line style of all sides. Array like for + * {@link SetLineStyle SetLineStyle}.</li> + * <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li> + * </ul> + * If a key is not present or is null, not draws the side. Default value is default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle = 0, $draw_circle = false, $style = "", $line_style = array(), $fill_color = array(), $circle_style = "", $circle_outLine_style = array(), $circle_fill_color = array()) { + if (2 > $nv) { + $nv = 2; + } + if ($draw_circle) { + $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color); + } + $p2 = array(); + $visited = array(); + for ($i = 0; $i < $nv; $i++) { + $a = $angle + ($i * 360 / $nv); + $a_rad = deg2rad((float) $a); + $p2[] = $x0 + ($r * sin($a_rad)); + $p2[] = $y0 + ($r * cos($a_rad)); + $visited[] = false; + } + $p = array(); + $i = 0; + do { + $p[] = $p2[$i * 2]; + $p[] = $p2[($i * 2) + 1]; + $visited[$i] = true; + $i += $ng; + $i %= $nv; + } while (!$visited[$i]); + $this->Polygon($p, $style, $line_style, $fill_color); + } + + /** + * Draws a rounded rectangle. + * @param float $x Abscissa of upper-left corner. + * @param float $y Ordinate of upper-left corner. + * @param float $w Width. + * @param float $h Height. + * @param float $r Radius of the rounded corners. + * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left. Default value: all rounded corner ("1111"). + * @param string $style Style of rendering. Possible values are: + * <ul> + * <li>D or empty string: Draw (default).</li> + * <li>F: Fill.</li> + * <li>DF or FD: Draw and fill.</li> + * </ul> + * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array). + * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array). + * @access public + * @since 2.1.000 (2008-01-08) + */ + public function RoundedRect($x, $y, $w, $h, $r, $round_corner = "1111", $style = "", $border_style = array(), $fill_color = array()) { + if ("0000" == $round_corner) { // Not rounded + $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color); + } else { // Rounded + if (!(false === strpos($style, "F")) AND $fill_color) { + list($red, $g, $b) = $fill_color; + $this->SetFillColor($red, $g, $b); + } + switch ($style) { + case "F": { + $border_style = array(); + $op = "f"; + break; + } + case "FD": + case "DF": { + $op = "B"; + break; + } + default: { + $op = "S"; + break; + } + } + if ($border_style) { + $this->SetLineStyle($border_style); + } + $MyArc = 4 / 3 * (sqrt(2) - 1); + $this->_outPoint($x + $r, $y); + $xc = $x + $w - $r; + $yc = $y + $r; + $this->_outLine($xc, $y); + if ($round_corner[0]) { + $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc); + } else { + $this->_outLine($x + $w, $y); + } + $xc = $x + $w - $r; + $yc = $y + $h - $r; + $this->_outLine($x + $w, $yc); + if ($round_corner[1]) { + $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r); + } else { + $this->_outLine($x + $w, $y + $h); + } + $xc = $x + $r; + $yc = $y + $h - $r; + $this->_outLine($xc, $y + $h); + if ($round_corner[2]) { + $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc); + } else { + $this->_outLine($x, $y + $h); + } + $xc = $x + $r; + $yc = $y + $r; + $this->_outLine($x, $yc); + if ($round_corner[3]) { + $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r); + } else { + $this->_outLine($x, $y); + $this->_outLine($x + $r, $y); + } + $this->_out($op); + } + } + + // END GRAPHIC FUNCTIONS SECTION ----------------------- + + // BIDIRECTIONAL TEXT SECTION -------------------------- + + /** + * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/). + * @param string $str string to manipulate. + * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR + * @return string + * @author Nicola Asuni + * @since 2.1.000 (2008-01-08) + */ + protected function utf8StrRev($str, $setbom=false, $forcertl=false) { + global $unicode,$unicode_mirror, $unicode_arlet; + require_once(dirname(__FILE__).'/unicode_data.php'); + + // paragraph embedding level + $pel = 0; + // max level + $maxlevel = 0; + + // check if string contains arabic text + if (preg_match(K_RE_PATTERN_ARABIC, $str)) { + $arabic = true; + } else { + $arabic = false; + } + + // check if string contains RTL text + if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) { + return $this->UTF8ToUTF16BE($str, false); + } + + // convert string to array of unicode chars + $ta = $this->UTF8StringToArray($str); + // get number of chars + $numchars = count($ta); + + if ($forcertl == 'R') { + $pel = 1; + } elseif ($forcertl == 'L') { + $pel = 0; + } else { + // P2. In each paragraph, find the first character of type L, AL, or R. + // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero. + for ($i=0; $i < $numchars; $i++) { + $type = $unicode[$ta[$i]]; + if ($type == 'L') { + $pel = 0; + break; + } elseif (($type == 'AL') OR ($type == 'R')) { + $pel = 1; + break; + } + } + } + + // Current Embedding Level + $cel = $pel; + // directional override status + $dos = 'N'; + $remember = array(); + // start-of-level-run + $sor = $pel % 2 ? 'R' : 'L'; + $eor = $sor; + + //$levels = array(array('level' => $cel, 'sor' => $sor, 'eor' => '', 'chars' => array())); + //$current_level = &$levels[count( $levels )-1]; + + // Array of characters data + $chardata = Array(); + + // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase. + // In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached. + for ($i=0; $i < $numchars; $i++) { + if ($ta[$i] == K_RLE) { + // X2. With each RLE, compute the least greater odd embedding level. + // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. + // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. + $next_level = $cel + ($cel % 2) + 1; + if ($next_level < 62) { + $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos); + $cel = $next_level; + $dos = 'N'; + $sor = $eor; + $eor = $cel % 2 ? 'R' : 'L'; + } + } elseif ($ta[$i] == K_LRE) { + // X3. With each LRE, compute the least greater even embedding level. + // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral. + // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. + $next_level = $cel + 2 - ($cel % 2); + if ( $next_level < 62 ) { + $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos); + $cel = $next_level; + $dos = 'N'; + $sor = $eor; + $eor = $cel % 2 ? 'R' : 'L'; + } + } elseif ($ta[$i] == K_RLO) { + // X4. With each RLO, compute the least greater odd embedding level. + // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left. + // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. + $next_level = $cel + ($cel % 2) + 1; + if ($next_level < 62) { + $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos); + $cel = $next_level; + $dos = 'R'; + $sor = $eor; + $eor = $cel % 2 ? 'R' : 'L'; + } + } elseif ($ta[$i] == K_LRO) { + // X5. With each LRO, compute the least greater even embedding level. + // a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right. + // b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status. + $next_level = $cel + 2 - ($cel % 2); + if ( $next_level < 62 ) { + $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos); + $cel = $next_level; + $dos = 'L'; + $sor = $eor; + $eor = $cel % 2 ? 'R' : 'L'; + } + } elseif ($ta[$i] == K_PDF) { + // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override. + if (count($remember)) { + $last = count($remember ) - 1; + if (($remember[$last]['num'] == K_RLE) OR + ($remember[$last]['num'] == K_LRE) OR + ($remember[$last]['num'] == K_RLO) OR + ($remember[$last]['num'] == K_LRO)) { + $match = array_pop($remember); + $cel = $match['cel']; + $dos = $match['dos']; + $sor = $eor; + $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L'; + } + } + } elseif (($ta[$i] != K_RLE) AND + ($ta[$i] != K_LRE) AND + ($ta[$i] != K_RLO) AND + ($ta[$i] != K_LRO) AND + ($ta[$i] != K_PDF)) { + // X6. For all types besides RLE, LRE, RLO, LRO, and PDF: + // a. Set the level of the current character to the current embedding level. + // b. Whenever the directional override status is not neutral, reset the current character type to the directional override status. + if ($dos != 'N') { + $chardir = $dos; + } else { + $chardir = $unicode[$ta[$i]]; + } + // stores string characters and other information + $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor); + } + } // end for each char + + // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding. + // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes. + // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the “other” run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L. + + // 3.3.3 Resolving Weak Types + // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used. + // Nonspacing marks are now resolved based on the previous characters. + $numchars = count($chardata); + + // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor. + $prevlevel = -1; // track level changes + $levcount = 0; // counts consecutive chars at the same level + for ($i=0; $i < $numchars; $i++) { + if ($chardata[$i]['type'] == 'NSM') { + if ($levcount) { + $chardata[$i]['type'] = $chardata[$i]['sor']; + } elseif ($i > 0) { + $chardata[$i]['type'] = $chardata[($i-1)]['type']; + } + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if ($chardata[$i]['char'] == 'EN') { + for ($j=$levcount; $j >= 0; $j--) { + if ($chardata[$j]['type'] == 'AL') { + $chardata[$i]['type'] = 'AN'; + } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) { + break; + } + } + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // W3. Change all ALs to R. + for ($i=0; $i < $numchars; $i++) { + if ($chardata[$i]['type'] == 'AL') { + $chardata[$i]['type'] = 'R'; + } + } + + // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { + if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { + $chardata[$i]['type'] = 'EN'; + } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) { + $chardata[$i]['type'] = 'EN'; + } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) { + $chardata[$i]['type'] = 'AN'; + } + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if($chardata[$i]['type'] == 'ET') { + if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) { + $chardata[$i]['type'] = 'EN'; + } else { + $j = $i+1; + while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) { + if ($chardata[$j]['type'] == 'EN') { + $chardata[$i]['type'] = 'EN'; + break; + } elseif ($chardata[$j]['type'] != 'ET') { + break; + } + $j++; + } + } + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // W6. Otherwise, separators and terminators change to Other Neutral. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) { + $chardata[$i]['type'] = 'ON'; + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if ($chardata[$i]['char'] == 'EN') { + for ($j=$levcount; $j >= 0; $j--) { + if ($chardata[$j]['type'] == 'L') { + $chardata[$i]['type'] = 'L'; + } elseif ($chardata[$j]['type'] == 'R') { + break; + } + } + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries. + $prevlevel = -1; + $levcount = 0; + for ($i=0; $i < $numchars; $i++) { + if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { + if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { + $chardata[$i]['type'] = 'L'; + } elseif (($chardata[$i]['type'] == 'N') AND + (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND + (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { + $chardata[$i]['type'] = 'R'; + } elseif ($chardata[$i]['type'] == 'N') { + // N2. Any remaining neutrals take the embedding direction + $chardata[$i]['type'] = $chardata[$i]['sor']; + } + } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) { + // first char + if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) { + $chardata[$i]['type'] = 'L'; + } elseif (($chardata[$i]['type'] == 'N') AND + (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND + (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) { + $chardata[$i]['type'] = 'R'; + } elseif ($chardata[$i]['type'] == 'N') { + // N2. Any remaining neutrals take the embedding direction + $chardata[$i]['type'] = $chardata[$i]['sor']; + } + } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) { + //last char + if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) { + $chardata[$i]['type'] = 'L'; + } elseif (($chardata[$i]['type'] == 'N') AND + (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND + (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) { + $chardata[$i]['type'] = 'R'; + } elseif ($chardata[$i]['type'] == 'N') { + // N2. Any remaining neutrals take the embedding direction + $chardata[$i]['type'] = $chardata[$i]['sor']; + } + } elseif ($chardata[$i]['type'] == 'N') { + // N2. Any remaining neutrals take the embedding direction + $chardata[$i]['type'] = $chardata[$i]['sor']; + } + if ($chardata[$i]['level'] != $prevlevel) { + $levcount = 0; + } else { + $levcount++; + } + $prevlevel = $chardata[$i]['level']; + } + + // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels. + // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level. + for ($i=0; $i < $numchars; $i++) { + $odd = $chardata[$i]['level'] % 2; + if ($odd) { + if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){ + $chardata[$i]['level'] += 1; + } + } else { + if ($chardata[$i]['type'] == 'R') { + $chardata[$i]['level'] += 1; + } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){ + $chardata[$i]['level'] += 2; + } + } + $maxlevel = max($chardata[$i]['level'],$maxlevel); + } + + // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level: + // 1. Segment separators, + // 2. Paragraph separators, + // 3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and + // 4. Any sequence of white space characters at the end of the line. + for ($i=0; $i < $numchars; $i++) { + if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) { + $chardata[$i]['level'] = $pel; + } elseif ($chardata[$i]['type'] == 'WS') { + $j = $i+1; + while ($j < $numchars) { + if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR + (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) { + $chardata[$i]['level'] = $pel;; + break; + } elseif ($chardata[$j]['type'] != 'WS') { + break; + } + $j++; + } + } + } + + // Arabic Shaping + // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run. + if ($arabic) { + for ($i=0; $i < $numchars; $i++) { + if ($unicode[$chardata[$i]['char']] == 'AL') { + if (($i > 0) AND (($i+1) < $numchars) AND + ($unicode[$chardata[($i-1)]['char']] == 'AL') AND + ($unicode[$chardata[($i+1)]['char']] == 'AL') AND + ($chardata[($i-1)]['type'] == $chardata[$i]['type']) AND + ($chardata[($i+1)]['type'] == $chardata[$i]['type'])) { + // medial + if (isset($unicode_arlet[$chardata[$i]['char']][3])) { + $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][3]; + } + } elseif ((($i+1) < $numchars) AND + ($unicode[$chardata[($i+1)]['char']] == 'AL') AND + ($chardata[($i+1)]['type'] == $chardata[$i]['type'])) { + // initial + if (isset($unicode_arlet[$chardata[$i]['char']][2])) { + $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][2]; + } + } elseif (($i > 0) AND + ($unicode[$chardata[($i-1)]['char']] == 'AL') AND + ($chardata[($i-1)]['type'] == $chardata[$i]['type'])) { + // final + if (isset($unicode_arlet[$chardata[$i]['char']][1])) { + $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][1]; + } + } elseif (isset($unicode_arlet[$chardata[$i]['char']][0])) { + // isolated + $chardata[$i]['char'] = $unicode_arlet[$chardata[$i]['char']][0]; + } + } + } + } + + // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher. + for ($j=$maxlevel; $j > 0; $j--) { + $ordarray = Array(); + $revarr = Array(); + $onlevel = false; + for ($i=0; $i < $numchars; $i++) { + if ($chardata[$i]['level'] >= $j) { + $onlevel = true; + if (isset($unicode_mirror[$chardata[$i]['char']])) { + // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true. + $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']]; + } + $revarr[] = $chardata[$i]; + } else { + if($onlevel) { + $revarr = array_reverse($revarr); + $ordarray = array_merge($ordarray, $revarr); + $revarr = Array(); + $onlevel = false; + } + $ordarray[] = $chardata[$i]; + } + } + if($onlevel) { + $revarr = array_reverse($revarr); + $ordarray = array_merge($ordarray, $revarr); + } + $chardata = $ordarray; + } + + $ordarray = array(); + for ($i=0; $i < $numchars; $i++) { + $ordarray[] = $chardata[$i]['char']; + } + + return $this->arrUTF8ToUTF16BE($ordarray, $setbom); + } + + // END OF BIDIRECTIONAL TEXT SECTION ------------------- + + /* + * Adds a bookmark. + * @param string $txt bookmark description. + * @param int $level bookmark level. + * @param float $y Ordinate of the boorkmark position (default = -1 = current position). + * @access public + * @author Olivier Plathey, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function Bookmark($txt, $level=0, $y=-1) { + if($y == -1) { + $y = $this->GetY(); + } + $this->outlines[]=array('t'=>$txt,'l'=>$level,'y'=>$y,'p'=>$this->PageNo()); + } + + /* + * Create a bookmark PDF string. + * @access private + * @author Olivier Plathey, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + private function _putbookmarks() { + $nb = count($this->outlines); + if($nb == 0) { + return; + } + $lru = array(); + $level = 0; + foreach($this->outlines as $i=>$o) { + if($o['l'] > 0) { + $parent = $lru[$o['l'] - 1]; + //Set parent and last pointers + $this->outlines[$i]['parent'] = $parent; + $this->outlines[$parent]['last'] = $i; + if($o['l'] > $level) { + //Level increasing: set first pointer + $this->outlines[$parent]['first'] = $i; + } + } else { + $this->outlines[$i]['parent']=$nb; + } + if($o['l']<=$level and $i>0) { + //Set prev and next pointers + $prev = $lru[$o['l']]; + $this->outlines[$prev]['next'] = $i; + $this->outlines[$i]['prev'] = $prev; + } + $lru[$o['l']] = $i; + $level = $o['l']; + } + //Outline items + $n = $this->n+1; + foreach($this->outlines as $i=>$o) { + $this->_newobj(); + $this->_out('<</Title '.$this->_textstring($o['t'])); + $this->_out('/Parent '.($n+$o['parent']).' 0 R'); + if(isset($o['prev'])) + $this->_out('/Prev '.($n+$o['prev']).' 0 R'); + if(isset($o['next'])) + $this->_out('/Next '.($n+$o['next']).' 0 R'); + if(isset($o['first'])) + $this->_out('/First '.($n+$o['first']).' 0 R'); + if(isset($o['last'])) + $this->_out('/Last '.($n+$o['last']).' 0 R'); + $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]',1+2*$o['p'],($this->h-$o['y'])*$this->k)); + $this->_out('/Count 0>>'); + $this->_out('endobj'); + } + //Outline root + $this->_newobj(); + $this->OutlineRoot=$this->n; + $this->_out('<</Type /Outlines /First '.$n.' 0 R'); + $this->_out('/Last '.($n+$lru[0]).' 0 R>>'); + $this->_out('endobj'); + } + + + // --- JAVASCRIPT - FORMS ------------------------------ + + /* + * Adds a javascript + * @access public + * @author Johannes Güntert, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function IncludeJS($script) { + $this->javascript .= $script; + } + + /* + * Create a javascript PDF string. + * @access private + * @author Johannes Güntert, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + private function _putjavascript() { + if (empty($this->javascript)) { + return; + } + $this->_newobj(); + $this->n_js = $this->n; + $this->_out('<<'); + $this->_out('/Names [(EmbeddedJS) '.($this->n+1).' 0 R ]'); + $this->_out('>>'); + $this->_out('endobj'); + $this->_newobj(); + $this->_out('<<'); + $this->_out('/S /JavaScript'); + $this->_out('/JS '.$this->_textstring($this->javascript)); + $this->_out('>>'); + $this->_out('endobj'); + } + + /* + * Convert color to javascript color. + * @param string $color color name or #RRGGBB + * @access private + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + private function _JScolor($color) { + static $aColors = array('transparent','black','white','red','green','blue','cyan','magenta','yellow','dkGray','gray','ltGray'); + if(substr($color,0,1) == '#') { + return sprintf("['RGB',%.3f,%.3f,%.3f]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255); + } + if(!in_array($color,$aColors)) { + $this->Error('Invalid color: '.$color); + } + return 'color.'.$color; + } + + /* + * Adds a javascript form field. + * @param string $type field type + * @param string $name field name + * @param int $x horizontal position + * @param int $y vertical position + * @param int $w width + * @param int $h height + * @param string $prop properties + * @access private + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + private function _addfield($type, $name, $x, $y, $w, $h, $prop) { + $k = $this->k; + $this->javascript .= sprintf("f=addField('%s','%s',%d,[%.2f,%.2f,%.2f,%.2f]);",$name,$type,$this->PageNo()-1,$x*$k,($this->h-$y)*$k+1,($x+$w)*$k,($this->h-$y-$h)*$k+1); + $this->javascript .= 'f.textSize='.$this->FontSizePt.';'; + if(isset($prop['value'])) { + $this->javascript .= "f.value='".addslashes($prop['value'])."';"; + } + if(isset($prop['TextColor'])) { + $this->javascript .= 'f.textColor='.$this->_JScolor($prop['TextColor']).';'; + } + if(isset($prop['FillColor'])) { + $this->javascript .= 'f.fillColor='.$this->_JScolor($prop['FillColor']).';'; + } + if(isset($prop['BorderColor'])) { + $this->javascript .= 'f.strokeColor='.$this->_JScolor($prop['BorderColor']).';'; + } + if(isset($prop['BorderStyle'])) { + $this->javascript .= "f.borderStyle='".$prop['BorderStyle']."';"; + } + $this->x+=$w; + } + + /* + * Creates a text field + * @param string $name field name + * @param int $w width + * @param int $h height + * @param string $prop properties. The value property allows to set the initial value. The multiline property allows to define the field as multiline. + * @access public + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function TextField($name, $w, $h, $prop=array()) { + $this->_addfield('text',$name,$this->x,$this->y,$w,$h,$prop); + if(isset($prop['multiline']) AND $prop['multiline']) { + $this->javascript .= 'f.multiline=true;'; + } + } + + /* + * Creates a Combo-box field + * @param string $name field name + * @param int $w width + * @param int $h height + * @param array $values array containing the list of values. + * @param string $prop properties. + * @access public + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function ComboBox($name, $w, $h, $values, $prop=array()) { + $this->_addfield('combobox',$name,$this->x,$this->y,$w,$h,$prop); + $s = ''; + foreach($values as $value) { + $s .= "'".addslashes($value)."',"; + } + $this->javascript .= 'f.setItems(['.substr($s,0,-1).']);'; + } + + /* + * Creates a CheckBox field + * @param string $name field name + * @param int $w width + * @param boolean $checked define the initial state (default = false). + * @param string $prop properties. + * @access public + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function CheckBox($name, $w, $checked=false, $prop=array()) { + $prop['value'] = ($checked ? 'Yes' : 'Off'); + if(!isset($prop['BorderColor'])) { + $prop['BorderColor']='black'; + } + $this->_addfield('checkbox',$name,$this->x,$this->y,$w,$w,$prop); + } + + /* + * Creates a button field + * @param string $name field name + * @param int $w width + * @param int $h height + * @param string $caption caption. + * @param string $action action triggered by the button (JavaScript code). + * @param string $prop properties. + * @access public + * @author Denis Van Nuffelen, Nicola Asuni + * @since 2.1.002 (2008-02-12) + */ + public function Button($name, $w, $h, $caption, $action, $prop=array()) { + if(!isset($prop['BorderColor'])) { + $prop['BorderColor']='black'; + } + $prop['BorderStyle']='beveled'; + $this->_addfield('button',$name,$this->x,$this->y,$w,$h,$prop); + $this->javascript .= "f.buttonSetCaption('".addslashes($caption)."');"; + $this->javascript .= "f.setAction('MouseUp','".addslashes($action)."');"; + $this->javascript .= "f.highlight='push';"; + $this->javascript .= 'f.print=false;'; + } + + // END JAVASCRIPT - FORMS ------------------------------ + + } // END OF TCPDF CLASS + + //Handle special IE contype request + if(isset($_SERVER['HTTP_USER_AGENT']) AND ($_SERVER['HTTP_USER_AGENT']=='contype')) { + header('Content-Type: application/pdf'); + exit; + } + } //============================================================+ // END OF FILE From cc4ace1f22921439d1fd7ec6cb23316c76d53bd6 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 21 Jul 2010 16:08:48 +0500 Subject: [PATCH 28/48] Export Relation Schema: fixing notices --- export_relation_schema.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/export_relation_schema.php b/export_relation_schema.php index b21ed8030..c2608f826 100644 --- a/export_relation_schema.php +++ b/export_relation_schema.php @@ -71,7 +71,7 @@ if ($cfgRelation['pdfwork']) { * User object created for presenting the HTML options * so, user can interact with it and perform export of relations schema */ - + require_once './libraries/schema/User_Schema.class.php'; $user_schema = new PMA_User_Schema(); @@ -84,8 +84,10 @@ if ($cfgRelation['pdfwork']) { * @param string $do It tells what the Schema is supposed to do * create and select a page, generate schema etc */ - - $user_schema->processUserPreferences($_REQUEST['do']); + if(isset($_REQUEST['do'])){ + $user_schema->setAction($_REQUEST['do']); + $user_schema->processUserPreferences(); + } /** * Show some possibility to select a page for the export of relation schema From 9595dce3b824ab5c35706d470b4d34ce355bab51 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 21 Jul 2010 16:11:08 +0500 Subject: [PATCH 29/48] PDF + SVG + Dia Schema: Notices Fix + Prompt User for Download Files --- .../schema/Dia_Relation_Schema.class.php | 19 ++++++++-------- .../schema/Pdf_Relation_Schema.class.php | 16 +++++++------- .../schema/Svg_Relation_Schema.class.php | 22 +++++++++---------- libraries/schema/User_Schema.class.php | 19 ++++++++++------ 4 files changed, 39 insertions(+), 37 deletions(-) diff --git a/libraries/schema/Dia_Relation_Schema.class.php b/libraries/schema/Dia_Relation_Schema.class.php index a35a39418..af9ce2066 100644 --- a/libraries/schema/Dia_Relation_Schema.class.php +++ b/libraries/schema/Dia_Relation_Schema.class.php @@ -33,7 +33,7 @@ class PMA_DIA extends XMLWriter * Upon instantiation This starts writing the Dia XML document * * @return void - * @see PMA_DIA,Table_Stats,Relation_Stats + * @see XMLWriter::openMemory(),XMLWriter::setIndent(),XMLWriter::startDocument() */ function __construct() { @@ -58,7 +58,7 @@ class PMA_DIA extends XMLWriter * dia document starts by first initializing dia:diagram tag * then dia:diagramdata contains all the attributes that needed * to define the document, then finally a Layer starts which - * holds all the objects. + * holds all the objects. * * @param string paper The size of the paper/document * @param float topMargin top margin of the paper/document in cm @@ -165,16 +165,15 @@ class PMA_DIA extends XMLWriter /** * Output Dia Document for download * - * @param string fileName name of the dia document + * @param string fileName name of the dia document * @return void * @access public * @see XMLWriter::flush() */ function showOutput($fileName) { - if(ob_end_clean()){ + if(ob_get_clean()){ ob_end_clean(); - //ob_start(); } //header('Content-type: text/xml'); header('Content-Disposition: attachment; filename="'.$fileName.'.dia"'); @@ -352,7 +351,7 @@ class Table_Stats <dia:real val="0.10000000000000001"/> </dia:attribute> <dia:attribute name="name"> - <dia:string>#'.$this->_tableName.'#</dia:string> + <dia:string>#'.$this->tableName.'#</dia:string> </dia:attribute> <dia:attribute name="comment"> <dia:string>##</dia:string> @@ -388,7 +387,7 @@ class Table_Stats <dia:real val="0.69999999999999996"/> </dia:attribute>' ); - + $dia->startElement('dia:attribute'); $dia->writeAttribute('name', 'attributes'); @@ -673,10 +672,10 @@ class PMA_Dia_Relation_Schema extends PMA_Export_Relation_Schema global $dia,$db; $this->setPageNumber($_POST['pdf_page_number']); - $this->setShowGrid($_POST['show_grid']); + $this->setShowGrid(isset($_POST['show_grid'])); $this->setShowColor($_POST['show_color']); - $this->setShowKeys($_POST['show_keys']); - $this->setOrientation($_POST['orientation']); + $this->setShowKeys(isset($_POST['show_keys'])); + $this->setOrientation(isset($_POST['orientation'])); $this->setPaper($_POST['paper']); $this->setExportType($_POST['export_type']); diff --git a/libraries/schema/Pdf_Relation_Schema.class.php b/libraries/schema/Pdf_Relation_Schema.class.php index 9b88e382e..28424bffa 100644 --- a/libraries/schema/Pdf_Relation_Schema.class.php +++ b/libraries/schema/Pdf_Relation_Schema.class.php @@ -808,8 +808,8 @@ class Relation_Stats $pdf->PMA_PDF_setLineWidthScale(0.1); $pdf->PMA_PDF_lineScale($this->xSrc + $this->srcDir * $this->wTick, $this->ySrc, $this->xDest + $this->destDir * $this->wTick, $this->yDest); /* - * Draws arrows -> - */ + * Draws arrows -> + */ $root2 = 2 * sqrt(2); $pdf->PMA_PDF_lineScale($this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick, $this->ySrc + $this->wTick / $root2); $pdf->PMA_PDF_lineScale($this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick, $this->ySrc - $this->wTick / $root2); @@ -853,11 +853,11 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema global $pdf,$db,$cfgRelation; $this->setPageNumber($_POST['pdf_page_number']); - $this->setShowGrid($_POST['show_grid']); - $this->setShowColor($_POST['show_color']); - $this->setShowKeys($_POST['show_keys']); - $this->setTableDimension($_POST['show_table_dimension']); - $this->setAllTableSameWidth($_POST['all_table_same_wide']); + $this->setShowGrid(isset($_POST['show_grid'])); + $this->setShowColor(isset($_POST['show_color'])); + $this->setShowKeys(isset($_POST['show_keys'])); + $this->setTableDimension(isset($_POST['show_table_dimension'])); + $this->setAllTableSameWidth(isset($_POST['all_table_same_wide'])); $this->setWithDataDictionary($_POST['with_doc']); $this->setOrientation($_POST['orientation']); $this->setPaper($_POST['paper']); @@ -950,10 +950,10 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema print_r(get_object_vars($this)); print_r(get_object_vars($pdf)); print '</pre>'; - exit(); */ $this->_drawTables($this->showColor); $this->_showOutput($this->pageNumber); + exit(); } /** diff --git a/libraries/schema/Svg_Relation_Schema.class.php b/libraries/schema/Svg_Relation_Schema.class.php index 996801019..397750f62 100644 --- a/libraries/schema/Svg_Relation_Schema.class.php +++ b/libraries/schema/Svg_Relation_Schema.class.php @@ -172,12 +172,9 @@ class PMA_SVG extends XMLWriter */ function showOutput($fileName) { - if(ob_end_clean()){ - ob_end_clean(); - //ob_start(); - } + ob_get_clean(); header('Content-type: image/svg+xml'); - //header('Content-Disposition: attachment; filename="downloaded.svg"'); + header('Content-Disposition: attachment; filename="'.$fileName.'.svg"'); $output = $this->flush(); print $output; } @@ -334,6 +331,7 @@ class Table_Stats public $height; public $fields = array(); public $heightCell = 0; + public $currentCell = 0; public $x, $y; public $primary = array(); @@ -492,7 +490,7 @@ class Table_Stats 'fill:none;stroke:black;' ); foreach ($this->fields as $field) { - $currentCell += $this->heightCell; + $this->currentCell += $this->heightCell; $showColor = 'none'; if ($showColor) { if (in_array($field, $this->primary)) { @@ -502,12 +500,12 @@ class Table_Stats $showColor = 'none'; } } - $svg->printElement('rect', $this->x,$this->y + $currentCell, + $svg->printElement('rect', $this->x,$this->y + $this->currentCell, $this->width, $this->heightCell, NULL, 'fill:'.$showColor.';stroke:black;' ); - $svg->printElement('text', $this->x + 5, $this->y + 14 + $currentCell, + $svg->printElement('text', $this->x + 5, $this->y + 14 + $this->currentCell, $this->width, $this->heightCell, $field, 'fill:none;stroke:black;' @@ -717,10 +715,10 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema global $svg,$db; $this->setPageNumber($_POST['pdf_page_number']); - $this->setShowColor($_POST['show_color']); - $this->setShowKeys($_POST['show_keys']); - $this->setTableDimension($_POST['show_table_dimension']); - $this->setAllTableSameWidth($_POST['all_table_same_wide']); + $this->setShowColor(isset($_POST['show_color'])); + $this->setShowKeys(isset($_POST['show_keys'])); + $this->setTableDimension(isset($_POST['show_table_dimension'])); + $this->setAllTableSameWidth(isset($_POST['all_table_same_wide'])); $this->setExportType($_POST['export_type']); $svg = new PMA_SVG(); diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php index 1e4151f7b..1d89ff628 100644 --- a/libraries/schema/User_Schema.class.php +++ b/libraries/schema/User_Schema.class.php @@ -25,24 +25,29 @@ class PMA_User_Schema public $autoLayoutInternal; public $pageNumber; public $c_table_rows; - + public $action; + + public function setAction($value) + { + $this->action = $value; + } /** * This function will process the user defined pages * and tables which will be exported as Relational schema * you can set the table positions on the paper via scratchboard * for table positions, put the x,y co-ordinates * - * @param string $action It tells what the Schema is supposed to do + * @param string $this->action It tells what the Schema is supposed to do * create and select a page, generate schema etc * @access public */ - public function processUserPreferences($action) + public function processUserPreferences() { global $action_choose,$db,$cfgRelation,$cfg,$query_default_option; - if (isset($action)) { - switch ($action) { + if (isset($this->action)) { + switch ($this->action) { case 'selectpage': $this->choosenPage = $_REQUEST['chpage']; if ($action_choose=="1") { @@ -187,7 +192,7 @@ class PMA_User_Schema */ public function showTableDashBoard() { - global $db,$cfgRelation,$table,$cfg,$with_field_names; + global $db,$cfgRelation,$table,$cfg,$with_field_names,$query_default_option; /* * We will need an array of all tables in this db */ @@ -320,7 +325,7 @@ class PMA_User_Schema echo "\n" . '</form>' . "\n\n"; } // end if - $this->_deleteTables($db, $this->choosenPage, $tabExist); + $this->_deleteTables($db, $this->choosenPage, isset($tabExist)); } /** From 14b26e314a159c4b282e02940d3c44563f302251 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 21 Jul 2010 16:12:49 +0500 Subject: [PATCH 30/48] TCPDF Class: Clean output buffer before downloading file --- libraries/tcpdf/tcpdf.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/tcpdf/tcpdf.php b/libraries/tcpdf/tcpdf.php index 8656ebc61..98ab40445 100644 --- a/libraries/tcpdf/tcpdf.php +++ b/libraries/tcpdf/tcpdf.php @@ -2999,7 +2999,8 @@ if(!class_exists('TCPDF', false)) { // if(ob_get_contents()) { // $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); //} - + ob_end_clean(); + ob_start(); if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { header('Content-Type: application/force-download'); } else { From 2c42e1bccba5b848bca81de475343d2876d5e029 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Sun, 15 Aug 2010 15:06:26 +0500 Subject: [PATCH 31/48] EPS Relation Schema Class --- .../schema/Eps_Relation_Schema.class.php | 872 +++++++++++++++++- 1 file changed, 871 insertions(+), 1 deletion(-) diff --git a/libraries/schema/Eps_Relation_Schema.class.php b/libraries/schema/Eps_Relation_Schema.class.php index fa2bef147..e0df230fe 100644 --- a/libraries/schema/Eps_Relation_Schema.class.php +++ b/libraries/schema/Eps_Relation_Schema.class.php @@ -1,7 +1,877 @@ <?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * + * @version $Id$ + * @package phpMyAdmin + */ + include_once("Export_Relation_Schema.class.php"); + +/** + * This Class is EPS Library and + * helps in developing structure of EPS Schema Export + * + * @name PMA_EPS + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @access public + * @see http://php.net/manual/en/book.xmlwriter.php + */ + +class PMA_EPS +{ + public $font; + public $fontSize; + public $stringCommands; + + /** + * The "PMA_EPS" constructor + * + * Upon instantiation This starts writing the EPS Document. + * %!PS-Adobe-3.0 EPSF-3.0 This is the MUST first comment to include + * it shows/tells that the Post Script document is purely under + * Document Structuring Convention [DSC] and is Compliant + * Encapsulated Post Script Document + * + * @return void + * @access public + */ + function __construct() + { + $this->stringCommands = ""; + $this->stringCommands .= "%!PS-Adobe-3.0 EPSF-3.0 \n"; + } + + /** + * Set document title + * + * @param string value sets the title text + * @return void + * @access public + */ + function setTitle($value) + { + $this->stringCommands .= '%%Title: ' . $value . "\n"; + } + + /** + * Set document author + * + * @param string value sets the author + * @return void + * @access public + */ + function setAuthor($value) + { + $this->stringCommands .= '%%Creator: ' . $value . "\n"; + } + + /** + * Set document creation date + * + * @param string value sets the date + * @return void + * @access public + */ + function setDate($value) + { + $this->stringCommands .= '%%CreationDate: ' . $value . "\n"; + } + + /** + * Set document orientation + * + * @param string value sets the author + * @return void + * @access public + */ + function setOrientation($value) + { + $this->stringCommands .= "%%PageOrder: Ascend \n"; + if($value == "L"){ + $value = "Landscape"; + $this->stringCommands .= '%%Orientation: ' . $value . "\n"; + }else{ + $value = "Portrait"; + $this->stringCommands .= '%%Orientation: ' . $value . "\n"; + } + $this->stringCommands .= "%%EndComments \n"; + } + + /** + * Set the font and size + * + * font can be set whenever needed in EPS + * + * @param string value sets the font name e.g Arial + * @param integer value sets the size of the font e.g 10 + * @return void + * @access public + */ + function setFont($value,$size) + { + $this->font = $value; + $this->fontSize = $size; + $this->stringCommands .= "/".$value." findfont % Get the basic font\n"; + $this->stringCommands .= "".$size." scalefont % Scale the font to $size points\n"; + $this->stringCommands .= "setfont % Make it the current font\n"; + } + + /** + * Get the font + * + * @return string return the font name e.g Arial + * @access public + */ + function getFont() + { + return $this->font; + } + + /** + * Get the font Size + * + * @return string return the size of the font e.g 10 + * @access public + */ + function getFontSize() + { + return $this->fontSize; + } + + /** + * Draw the line + * + * drawing the lines from x,y source to x,y destination and set the + * width of the line. lines helps in showing relationships of tables + * + * @param integer x_from The x_from attribute defines the start + left position of the element + * @param integer y_from The y_from attribute defines the start + right position of the element + * @param integer x_to The x_to attribute defines the end + left position of the element + * @param integer y_to The y_to attribute defines the end + right position of the element + * @param integer lineWidth sets the width of the line e.g 2 + * @return void + * @access public + */ + function line($x_from=0, $y_from=0, $x_to=0, $y_to=0, $lineWidth=0) + { + $this->stringCommands .= $lineWidth . " setlinewidth \n"; + $this->stringCommands .= $x_from . ' ' . $y_from . " moveto \n"; + $this->stringCommands .= $x_to . ' ' . $y_to . " lineto \n"; + $this->stringCommands .= "stroke \n"; + } + + /** + * Draw the rectangle + * + * drawing the rectangle from x,y source to x,y destination and set the + * width of the line. rectangles drawn around the text shown of fields + * + * @param integer x_from The x_from attribute defines the start + left position of the element + * @param integer y_from The y_from attribute defines the start + right position of the element + * @param integer x_to The x_to attribute defines the end + left position of the element + * @param integer y_to The y_to attribute defines the end + right position of the element + * @param integer lineWidth sets the width of the line e.g 2 + * @return void + * @access public + */ + function rect($x_from, $y_from, $x_to, $y_to, $lineWidth) + { + $this->stringCommands .= $lineWidth . " setlinewidth \n"; + $this->stringCommands .= "newpath \n"; + $this->stringCommands .= $x_from . ' ' . $y_from . " moveto \n"; + $this->stringCommands .= $x_to . ' ' . $y_from . " lineto \n"; + $this->stringCommands .= $x_to . ' ' . $y_to . " lineto \n"; + $this->stringCommands .= $x_from . " " . $y_to . " lineto \n"; + $this->stringCommands .= "closepath \n"; + $this->stringCommands .= "stroke \n"; + } + + /** + * Set the current point + * + * The moveto operator takes two numbers off the stack and treats + * them as x and y coordinates to which to move. The coordinates + * specified become the current point. + * + * @param integer x The x attribute defines the + left position of the element + * @param integer y The y attribute defines the + right position of the element + * @return void + * @access public + */ + function moveTo($x, $y) + { + $this->stringCommands .= $x . ' ' . $y . " moveto \n"; + } + + /** + * Output/Display the text + * + * @param string text The string to be displayed + * @return void + * @access public + */ + function show($text) + { + $this->stringCommands .= '(' . $text . ") show \n"; + } + + /** + * Output the text at specified co-ordinates + * + * @param string text The string to be displayed + * @param integer x The x attribute defines the + left position of the element + * @param integer y The y attribute defines the + right position of the element + * @return void + * @access public + */ + function showXY($text, $x, $y) + { + $this->moveTo($x, $y); + $this->show($text); + } + + /** + * get width of string/text + * + * EPS text width is calcualted depending on font name + * and font size. It is very important to know the width of text + * because rectangle is drawn around it. + * + * This is a bit hardcore method. I didn't found any other better than this. + * if someone found better than this. would love to hear that method + * + * @param string text string that width will be calculated + * @param integer font name of the font like Arial,sans-serif etc + * @param integer fontSize size of font + * @return integer width of the text + * @access public + */ + function getStringWidth($text,$font,$fontSize) + { + /* + * Start by counting the width, giving each character a modifying value + */ + $count = 0; + $count = $count + ((strlen($text) - strlen(str_replace(array("i","j","l"),"",$text)))*0.23);//ijl + $count = $count + ((strlen($text) - strlen(str_replace(array("f"),"",$text)))*0.27);//f + $count = $count + ((strlen($text) - strlen(str_replace(array("t","I"),"",$text)))*0.28);//tI + $count = $count + ((strlen($text) - strlen(str_replace(array("r"),"",$text)))*0.34);//r + $count = $count + ((strlen($text) - strlen(str_replace(array("1"),"",$text)))*0.49);//1 + $count = $count + ((strlen($text) - strlen(str_replace(array("c","k","s","v","x","y","z","J"),"",$text)))*0.5);//cksvxyzJ + $count = $count + ((strlen($text) - strlen(str_replace(array("a","b","d","e","g","h","n","o","p","q","u","L","0","2","3","4","5","6","7","8","9"),"",$text)))*0.56);//abdeghnopquL023456789 + $count = $count + ((strlen($text) - strlen(str_replace(array("F","T","Z"),"",$text)))*0.61);//FTZ + $count = $count + ((strlen($text) - strlen(str_replace(array("A","B","E","K","P","S","V","X","Y"),"",$text)))*0.67);//ABEKPSVXY + $count = $count + ((strlen($text) - strlen(str_replace(array("w","C","D","H","N","R","U"),"",$text)))*0.73);//wCDHNRU + $count = $count + ((strlen($text) - strlen(str_replace(array("G","O","Q"),"",$text)))*0.78);//GOQ + $count = $count + ((strlen($text) - strlen(str_replace(array("m","M"),"",$text)))*0.84);//mM + $count = $count + ((strlen($text) - strlen(str_replace("W","",$text)))*.95);//W + $count = $count + ((strlen($text) - strlen(str_replace(" ","",$text)))*.28);//" " + $text = str_replace(" ","",$text);//remove the " "'s + $count = $count + (strlen(preg_replace("/[a-z0-9]/i","",$text))*0.3); //all other chrs + + $modifier = 1; + $font = strtolower($font); + switch($font){ + /* + * no modifier for arial and sans-serif + */ + case 'arial': + case 'sans-serif': + break; + /* + * .92 modifer for time, serif, brushscriptstd, and californian fb + */ + case 'times': + case 'serif': + case 'brushscriptstd': + case 'californian fb': + $modifier = .92; + break; + /* + * 1.23 modifier for broadway + */ + case 'broadway': + $modifier = 1.23; + break; + } + $textWidth = $count*$fontSize; + return ceil($textWidth*$modifier); + } + + /** + * Ends EPS Document + * + * @return void + * @access public + */ + function endEpsDoc() + { + $this->stringCommands .= "showpage \n"; + } + + /** + * Output EPS Document for download + * + * @param string fileName name of the eps document + * @return void + * @access public + */ + function showOutput($fileName) + { + if(ob_get_clean()){ + ob_end_clean(); + } + //header('Content-type: text/xml'); + header('Content-Disposition: attachment; filename="'.$fileName.'.eps"'); + $output = $this->stringCommands; + print $output; + } +} + +/** + * Table preferences/statistics + * + * This class preserves the table co-ordinates,fields + * and helps in drawing/generating the Tables in EPS. + * + * @name Table_Stats + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @see PMA_EPS + */ +class Table_Stats +{ + /** + * Defines properties + */ + + private $_tableName; + private $_showInfo = false; + + public $width = 0; + public $height; + public $fields = array(); + public $heightCell = 0; + public $currentCell = 0; + public $x, $y; + public $primary = array(); + + /** + * The "Table_Stats" constructor + * + * @param string tableName The table name + * @param string font The font name + * @param integer fontSize The font size + * @param integer same_wide_width The max width among tables + * @param boolean showKeys Whether to display keys or not + * @param boolean showInfo Whether to display table position or not + * @global object The current eps document + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @global array The relations settings + * @global string The current db name + * @access private + * @see PMA_EPS, Table_Stats::Table_Stats_setWidth, + Table_Stats::Table_Stats_setHeight + */ + function __construct($tableName, $font, $fontSize, $pageNumber, &$same_wide_width, $showKeys = false, $showInfo = false) + { + global $eps, $cfgRelation, $db; + + $this->_tableName = $tableName; + $sql = 'DESCRIBE ' . PMA_backquote($tableName); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $eps->dieSchema(sprintf(__('The %s table doesn\'t exist!'), $tableName)); + } + + /* + * load fields + * check to see if it will load all fields or only the foreign keys + */ + if ($showKeys) { + $indexes = PMA_Index::getFromTable($this->_tableName, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $this->_showInfo = $showInfo; + + // height and width + $this->_setHeightTable($fontSize); + + // setWidth must me after setHeight, because title + // can include table height which changes table width + $this->_setWidthTable($font,$fontSize); + if ($same_wide_width < $this->width) { + $same_wide_width = $this->width; + } + + // x and y + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + + if (!$result || !PMA_DBI_num_rows($result)) { + //$eps->dieSchema(sprintf(__('Please configure the coordinates for table %s'), $tableName)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + // displayfield + $this->displayfield = PMA_getDisplayField($db, $tableName); + // index + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } + } + + /** + * Returns title of the current table, + * title can have the dimensions/co-ordinates of the table + * + * @return string The relation/table name + * @access private + */ + private function _getTitle() + { + return ($this->_showInfo ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->heightCell) : '') . ' ' . $this->_tableName; + } + + /** + * Sets the width of the table + * + * @param string font The font name + * @param integer fontSize The font size + * @global object The current eps document + * @return void + * @access private + * @see PMA_EPS + */ + private function _setWidthTable($font,$fontSize) + { + global $eps; + + foreach ($this->fields as $field) { + $this->width = max($this->width, $eps->getStringWidth($field,$font,$fontSize)); + } + $this->width += $eps->getStringWidth(' ',$font,$fontSize); + /* + * it is unknown what value must be added, because + * table title is affected by the tabe width value + */ + while ($this->width < $eps->getStringWidth($this->_getTitle(),$font,$fontSize)) { + $this->width += 7; + } + } + + /** + * Sets the height of the table + * + * @param integer fontSize The font size + * @return void + * @access private + */ + private function _setHeightTable($fontSize) + { + $this->heightCell = $fontSize + 4; + $this->height = (count($this->fields) + 1) * $this->heightCell; + } + + /** + * Draw the table + * + * @param boolean showColor Whether to display color + * @global object The current eps document + * @return void + * @access public + * @see PMA_EPS,PMA_EPS::line,PMA_EPS::rect + */ + public function tableDraw($showColor) + { + global $eps; + //echo $this->_tableName.'<br />'; + //$eps->rect($this->x,$this->y, + //$this->width,$this->heightCell, + //2 + // ); + $eps->showXY($this->_getTitle(),$this->x + 5,$this->y+ 14); + foreach ($this->fields as $field) { + $this->currentCell += $this->heightCell; + $showColor = 'none'; + if ($showColor) { + if (in_array($field, $this->primary)) { + $showColor = '#0c0'; + } + if ($field == $this->displayfield) { + $showColor = 'none'; + } + } + //$eps->rect($this->x,$this->y + $this->currentCell, + //$this->width, $this->heightCell,2); + $eps->showXY($field, $this->x + 5, $this->y + 14 + $this->currentCell); + } + } +} + +/** + * Relation preferences/statistics + * + * This class fetches the table master and foreign fields positions + * and helps in generating the Table references and then connects + * master table's master field to foreign table's foreign key + * in EPS document. + * + * @name Relation_Stats + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @see PMA_EPS + */ +class Relation_Stats +{ + /** + * Defines properties + */ + public $xSrc, $ySrc; + public $srcDir ; + public $destDir; + public $xDest, $yDest; + public $wTick = 10; + + /** + * The "Relation_Stats" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @see Relation_Stats::_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->_getXy($master_table, $master_field); + $dest_pos = $this->_getXy($foreign_table, $foreign_field); + /* + * [0] is x-left + * [1] is x-right + * [2] is y + */ + $src_left = $src_pos[0] - $this->wTick; + $src_right = $src_pos[1] + $this->wTick; + $dest_left = $dest_pos[0] - $this->wTick; + $dest_right = $dest_pos[1] + $this->wTick; + + $d1 = abs($src_left - $dest_left); + $d2 = abs($src_right - $dest_left); + $d3 = abs($src_left - $dest_right); + $d4 = abs($src_right - $dest_right); + $d = min($d1, $d2, $d3, $d4); + + if ($d == $d1) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d2) { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d3) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } else { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } + $this->ySrc = $src_pos[2]; + $this->yDest = $dest_pos[2]; + } + + /** + * Gets arrows coordinates + * + * @param string table The current table name + * @param string column The relation column name + * @return array Arrows coordinates + * @access private + */ + private function _getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // x_left, x_right, y + return array($table->x, $table->x + $table->width, $table->y + ($pos + 1.5) * $table->heightCell); + } + + /** + * draws relation links and arrows + * shows foreign key relations + * + * @param boolean changeColor Whether to use one color per relation or not + * @global object The current EPS document + * @access public + * @see PMA_EPS + */ + public function relationDraw($changeColor) + { + global $eps; + + if ($changeColor) { + $listOfColors = array( + 'red', + 'grey', + 'black', + 'yellow', + 'green', + 'cyan', + ' orange' + ); + shuffle($listOfColors); + $color = $listOfColors[0]; + } else { + $color = 'black'; + } + + $eps->line($this->xSrc,$this->ySrc, + $this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, + 1 + ); + $eps->line($this->xDest + $this->destDir * $this->wTick, $this->yDest, + $this->xDest, $this->yDest, + 1 + ); + $eps->line($this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, + $this->xDest + $this->destDir * $this->wTick, $this->yDest, + 1 + ); + $root2 = 2 * sqrt(2); + $eps->line($this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, + $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick , + $this->ySrc + $this->wTick / $root2 , + 1 + ); + $eps->line($this->xSrc + $this->srcDir * $this->wTick * 0.75, $this->ySrc, + $this->xSrc + $this->srcDir * (0.75 - 1 / $root2) * $this->wTick , + $this->ySrc - $this->wTick / $root2 , + 1 + ); + $eps->line($this->xDest + $this->destDir * $this->wTick / 2 , $this->yDest , + $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick, + $this->yDest + $this->wTick / $root2 , + 1); + $eps->line($this->xDest + $this->destDir * $this->wTick / 2 , + $this->yDest , $this->xDest + $this->destDir * (0.5 + 1 / $root2) * $this->wTick , + $this->yDest - $this->wTick / $root2 , + 1 + ); + } +} +/* +* end of the "Relation_Stats" class +*/ + +/** + * EPS Relation Schema Class + * + * Purpose of this class is to generate the EPS Document + * which is used for representing the database diagrams. + * This class uses post script commands and with + * the combination of these commands actually helps in preparing EPS Document. + * + * This class inherits Export_Relation_Schema class has common functionality added + * to this class + * + * @name Eps_Relation_Schema + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + */ class PMA_Eps_Relation_Schema extends PMA_Export_Relation_Schema { - + private $tables = array(); + private $_relations = array(); + + /** + * The "PMA_EPS_Relation_Schema" constructor + * + * Upon instantiation This starts writing the EPS document + * user will be prompted for download as .eps extension + * + * @return void + * @see PMA_EPS + */ + function __construct() + { + global $eps,$db; + + $this->setPageNumber($_POST['pdf_page_number']); + $this->setShowColor(isset($_POST['show_color'])); + $this->setShowKeys(isset($_POST['show_keys'])); + $this->setTableDimension(isset($_POST['show_table_dimension'])); + $this->setAllTableSameWidth(isset($_POST['all_table_same_wide'])); + $this->setOrientation($_POST['orientation']); + $this->setExportType($_POST['export_type']); + + $eps = new PMA_EPS(); + $eps->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $this->pageNumber)); + $eps->setAuthor('phpMyAdmin ' . PMA_VERSION); + $eps->setDate(date("j F Y, g:i a")); + $eps->setOrientation($this->orientation); + $eps->setFont('Verdana','10'); + + + + $alltables = $this->getAllTables($db,$this->pageNumber); + + foreach ($alltables AS $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new Table_Stats($table,$eps->getFont(),$eps->getFontSize(), $this->pageNumber, $this->_tablewidth, $this->showKeys, $this->tableDimension); + } + + if ($this->sameWide) { + $this->tables[$table]->width = $this->_tablewidth; + } + $this->_setMinMax($this->tables[$table]); + } + + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + /* put the foreign table on the schema only if selected + * by the user + * (do not use array_search() because we would have to + * to do a === FALSE and this is not PHP3 compatible) + */ + if (in_array($rel['foreign_table'], $alltables)) { + $this->_addRelation($one_table,$eps->getFont(),$eps->getFontSize(), $master_field, $rel['foreign_table'], $rel['foreign_field'], $this->tableDimension); + } + } + } + } + if ($seen_a_relation) { + $this->_drawRelations($this->showColor); + } + + $this->_drawTables($this->showColor); + $eps->endEpsDoc(); + print '<pre>'; + print_r(get_object_vars($eps)); + print_r($alltables); + print_r(get_object_vars($this)); + print '</pre>'; + exit(); + $eps->showOutput($db.'-'.$this->pageNumber); + } + + /** + * Sets X and Y minimum and maximum for a table cell + * + * @param string table The table name + * @return void + * @access private + */ + private function _setMinMax($table) + { + $this->_xMax = max($this->_xMax, $table->x + $table->width); + $this->_yMax = max($this->_yMax, $table->y + $table->height); + $this->_xMin = min($this->_xMin, $table->x); + $this->_yMin = min($this->_yMin, $table->y); + } + + /** + * Defines relation objects + * + * @param string masterTable The master table name + * @param string masterField The relation field in the master table + * @param string foreignTable The foreign table name + * @param string foreignField The relation field in the foreign table + * @param boolean showInfo Whether to display table position or not + * @return void + * @access private + * @see _setMinMax,Table_Stats::__construct(),Relation_Stats::__construct() + */ + private function _addRelation($masterTable,$font,$fontSize, $masterField, $foreignTable, $foreignField, $showInfo) + { + if (!isset($this->tables[$masterTable])) { + $this->tables[$masterTable] = new Table_Stats($masterTable, $font, $fontSize, $this->pageNumber, $this->_tablewidth, false, $showInfo); + $this->_setMinMax($this->tables[$masterTable]); + } + if (!isset($this->tables[$foreignTable])) { + $this->tables[$foreignTable] = new Table_Stats($foreignTable,$font,$fontSize,$this->pageNumber, $this->_tablewidth, false, $showInfo); + $this->_setMinMax($this->tables[$foreignTable]); + } + $this->_relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); + } + + /** + * Draws relation arrows and lines + * connects master table's master field to + * foreign table's forein field + * + * @param boolean changeColor Whether to use one color per relation or not + * @return void + * @access private + * @see Relation_Stats::relationDraw() + */ + private function _drawRelations($changeColor) + { + foreach ($this->_relations as $relation) { + $relation->relationDraw($changeColor); + } + } + + /** + * Draws tables + * + * @param boolean changeColor Whether to show color for primary fields or not + * @return void + * @access private + * @see Table_Stats::Table_Stats_tableDraw() + */ + private function _drawTables($changeColor) + { + foreach ($this->tables as $table) { + $table->tableDraw($changeColor); + } + } } ?> \ No newline at end of file From 6f750d73797e1034804956ce90a6c00e1e17e943 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Mon, 16 Aug 2010 04:49:45 +0500 Subject: [PATCH 32/48] Visio Relation Schema Class --- .../schema/Visio_Relation_Schema.class.php | 600 +++++++++++++++++- 1 file changed, 599 insertions(+), 1 deletion(-) diff --git a/libraries/schema/Visio_Relation_Schema.class.php b/libraries/schema/Visio_Relation_Schema.class.php index e20c383b9..7ced40047 100644 --- a/libraries/schema/Visio_Relation_Schema.class.php +++ b/libraries/schema/Visio_Relation_Schema.class.php @@ -1,7 +1,605 @@ <?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * + * @version $Id$ + * @package phpMyAdmin + */ + include_once("Export_Relation_Schema.class.php"); + +/** + * This Class inherits the XMLwriter class and + * helps in developing structure of MS Visio Schema Export + * + * @name PMA_VISIO + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @access public + * @see http://php.net/manual/en/book.xmlwriter.php + */ + +class PMA_VISIO extends XMLWriter +{ + public $title; + public $author; + public $font; + public $fontSize; + + /** + * The "PMA_VISIO" constructor + * + * Upon instantiation This starts writing the Visio XML .VDX document + * + * @return void + * @see XMLWriter::openMemory(),XMLWriter::setIndent(),XMLWriter::startDocument() + */ + function __construct() + { + $this->openMemory(); + /* + * Set indenting using three spaces, + * so output is formatted + */ + + $this->setIndent(TRUE); + $this->setIndentString(' '); + /* + * Create the XML document + */ + + $this->startDocument('1.0','UTF-8'); + } + + /** + * Starts Visio XML .VDX Document + * + * Visio XML document starts by first initializing VisioDocument tag + * then DocumentProperties & DocumentSettings contains all the + * attributes that needed to define the document. Order of elements + * should be maintained while generating XML of Visio. + * + * @return void + * @access public + * @see XMLWriter::startElement(),XMLWriter::writeAttribute(),_documentProperties,_documentSettings + */ + function startVisioDoc() + { + $this->startElement('VisioDocument'); + $this->writeAttribute('xmlns', 'http://schemas.microsoft.com/visio/2003/core'); + $this->writeAttribute('xmlns:vx', 'http://schemas.microsoft.com/visio/2006/extension'); + $this->writeAttribute('xml:space', 'preserve'); + $this->_documentProperties(); + $this->_documentSettings(); + } + + /** + * Set document title + * + * @param string value sets the title text + * @return void + * @access public + */ + function setTitle($value) + { + $this->title = $value; + } + + /** + * Set document author + * + * @param string value sets the author + * @return void + * @access public + */ + function setAuthor($value) + { + $this->author = $value; + } + + /** + * Sets Visio XML .VDX Document Properties + * + * DocumentProperties tag contains document property elements such as + the document's Title,Subject,Creator and templates tags + * + * @return void + * @access private + * @see XMLWriter::startElement(),XMLWriter::endElement(),XMLWriter::writeRaw() + */ + private function _documentProperties() + { + $this->startElement('DocumentProperties'); + $this->writeRaw('<Title>'.$this->title.'</Title>'); + $this->writeRaw('<Subject>'.$this->title.'</Subject>'); + $this->writeRaw('<Creator>'.$this->author.'</Creator>'); + $this->writeRaw('<Company>phpMyAdmin</Company>'); + $this->writeRaw('<Template>c:\program files\microsoft office\office12\1033\DBMODL_U.VST</Template>'); + $this->endElement(); + } + + /** + * Sets Visio XML .VDX Document Settings + * + * DocumentSettings tag contains elements that specify document settings. + * + * @return void + * @access private + * @see XMLWriter::startElement(),XMLWriter::endElement() + */ + private function _documentSettings() + { + $this->startElement('DocumentSettings'); + $this->endElement(); + } + + /** + * Ends Visio XML Document + * + * @return void + * @access public + * @see XMLWriter::endElement(),XMLWriter::endDocument() + */ + function endVisioDoc() + { + $this->endElement(); + $this->endDocument(); + } + + /** + * Output Visio XML .VDX Document for download + * + * @param string fileName name of the Visio XML document + * @return void + * @access public + * @see XMLWriter::flush() + */ + function showOutput($fileName) + { + if(ob_get_clean()){ + ob_end_clean(); + } + //header('Content-type: text/xml'); + header('Content-Disposition: attachment; filename="'.$fileName.'.vdx"'); + $output = $this->flush(); + print $output; + } +} + + +/** + * Draws tables schema + */ +class Table_Stats +{ + /** + * Defines properties + */ + + private $_tableName; + private $_showInfo = false; + + public $width = 0; + public $height; + public $fields = array(); + public $heightCell = 0; + public $currentCell = 0; + public $x, $y; + public $primary = array(); + + /** + * The "Table_Stats" constructor + * + * @param string tableName The table name + * @param integer same_wide_width The max. with among tables + * @param boolean showKeys Whether to display keys or not + * @param boolean showInfo Whether to display table position or not + * @global object The current Visio XML document + * @global integer The current page number (from the + * $cfg['Servers'][$i]['table_coords'] table) + * @global array The relations settings + * @global string The current db name + * @access private + * @see PMA_VISIO, Table_Stats::Table_Stats_setWidth, + Table_Stats::Table_Stats_setHeight + */ + function __construct($tableName, $pageNumber, &$same_wide_width, $showKeys = false, $showInfo = false) + { + global $visio, $cfgRelation, $db; + + $this->_tableName = $tableName; + $sql = 'DESCRIBE ' . PMA_backquote($tableName); + $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); + if (!$result || !PMA_DBI_num_rows($result)) { + $visio->dieSchema(sprintf(__('The %s table doesn\'t exist!'), $tableName)); + } + + /* + * load fields + * check to see if it will load all fields or only the foreign keys + */ + + if ($showKeys) { + $indexes = PMA_Index::getFromTable($this->_tableName, $db); + $all_columns = array(); + foreach ($indexes as $index) { + $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); + } + $this->fields = array_keys($all_columns); + } else { + while ($row = PMA_DBI_fetch_row($result)) { + $this->fields[] = $row[0]; + } + } + + $this->_showInfo = $showInfo; + + // height and width + $this->_setHeightTable($fontSize); + + // setWidth must me after setHeight, because title + // can include table height which changes table width + $this->_setWidthTable($font,$fontSize); + if ($same_wide_width < $this->width) { + $same_wide_width = $this->width; + } + + // x and y + $sql = 'SELECT x, y FROM ' + . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) + . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' + . ' AND table_name = \'' . PMA_sqlAddslashes($tableName) . '\'' + . ' AND pdf_page_number = ' . $pageNumber; + $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); + + if (!$result || !PMA_DBI_num_rows($result)) { + //$visio->dieSchema(sprintf(__('Please configure the coordinates for table %s'), $tableName)); + } + list($this->x, $this->y) = PMA_DBI_fetch_row($result); + $this->x = (double) $this->x; + $this->y = (double) $this->y; + // displayfield + $this->displayfield = PMA_getDisplayField($db, $tableName); + // index + $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($tableName) . ';', null, PMA_DBI_QUERY_STORE); + if (PMA_DBI_num_rows($result) > 0) { + while ($row = PMA_DBI_fetch_assoc($result)) { + if ($row['Key_name'] == 'PRIMARY') { + $this->primary[] = $row['Column_name']; + } + } + } + } + + /** + * Returns title of the current table, + * title can have the dimensions/co-ordinates of the table + * + * @access private + */ + private function _getTitle() + { + return ($this->_showInfo ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->heightCell) : '') . ' ' . $this->_tableName; + } + + /** + * Sets the width of the table + * + * @param string font The font name + * @param integer fontSize The font size + * @global object The current Visio XML document + * @access private + * @see PMA_VISIO + */ + function _setWidthTable($font,$fontSize) + { + global $visio; + + } + + /** + * Sets the height of the table + * + * @access private + */ + function _setHeightTable($fontSize) + { + $this->heightCell = $fontSize + 4; + $this->height = (count($this->fields) + 1) * $this->heightCell; + } + + /** + * draw the table + * + * @param boolean showColor Whether to display color + * @global object The current Visio XML document + * @access public + * @see PMA_VISIO + */ + public function tableDraw($showColor) + { + global $visio; + //echo $this->_tableName.'<br />'; + + foreach ($this->fields as $field) { + $this->currentCell += $this->heightCell; + $showColor = 'none'; + if ($showColor) { + if (in_array($field, $this->primary)) { + $showColor = '#0c0'; + } + if ($field == $this->displayfield) { + $showColor = 'none'; + } + } + // code here for drawing table diagrams + } + } +} + +/** + * Draws relation links + * + * @access public + * @see PMA_VISIO + */ +class Relation_Stats +{ + /** + * Defines properties + */ + public $xSrc, $ySrc; + public $srcDir ; + public $destDir; + public $xDest, $yDest; + public $wTick = 10; + + /** + * The "Relation_Stats" constructor + * + * @param string master_table The master table name + * @param string master_field The relation field in the master table + * @param string foreign_table The foreign table name + * @param string foreigh_field The relation field in the foreign table + * @see Relation_Stats::_getXy + */ + function __construct($master_table, $master_field, $foreign_table, $foreign_field) + { + $src_pos = $this->_getXy($master_table, $master_field); + $dest_pos = $this->_getXy($foreign_table, $foreign_field); + /* + * [0] is x-left + * [1] is x-right + * [2] is y + */ + $src_left = $src_pos[0] - $this->wTick; + $src_right = $src_pos[1] + $this->wTick; + $dest_left = $dest_pos[0] - $this->wTick; + $dest_right = $dest_pos[1] + $this->wTick; + + $d1 = abs($src_left - $dest_left); + $d2 = abs($src_right - $dest_left); + $d3 = abs($src_left - $dest_right); + $d4 = abs($src_right - $dest_right); + $d = min($d1, $d2, $d3, $d4); + + if ($d == $d1) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d2) { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[0]; + $this->destDir = -1; + } elseif ($d == $d3) { + $this->xSrc = $src_pos[0]; + $this->srcDir = -1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } else { + $this->xSrc = $src_pos[1]; + $this->srcDir = 1; + $this->xDest = $dest_pos[1]; + $this->destDir = 1; + } + $this->ySrc = $src_pos[2]; + $this->yDest = $dest_pos[2]; + } + + /** + * Gets arrows coordinates + * + * @param string table The current table name + * @param string column The relation column name + * @return array Arrows coordinates + * @access private + */ + function _getXy($table, $column) + { + $pos = array_search($column, $table->fields); + // x_left, x_right, y + return array($table->x, $table->x + $table->width, $table->y + ($pos + 1.5) * $table->heightCell); + } + + /** + * draws relation links and arrows + * shows foreign key relations + * + * @param boolean changeColor Whether to use one color per relation or not + * @global object The current Visio XML document + * @access public + * @see PMA_VISIO + */ + public function relationDraw($changeColor) + { + global $visio; + + if ($changeColor) { + $listOfColors = array( + 'red', + 'grey', + 'black', + 'yellow', + 'green', + 'cyan', + ' orange' + ); + shuffle($listOfColors); + $color = $listOfColors[0]; + } else { + $color = 'black'; + } + + // code here for making connections b/w relation objects + + } +} +/* +* end of the "Relation_Stats" class +*/ + +/** + * Visio Relation Schema Class + * + * Purpose of this class is to generate the Visio XML .VDX Document + * which is used for representing the database diagrams in any version of MS Visio IDE. + * This class uses Software and Database Template and Database model diagram of Visio + * and with the combination of these objects actually helps in preparing Visio XML .VDX document. + * + * Visio XML is generated by using XMLWriter php extension and this class + * inherits Export_Relation_Schema class has common functionality added + * to this class + * + * @name Visio_Relation_Schema + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + */ class PMA_Visio_Relation_Schema extends PMA_Export_Relation_Schema { - + /** + * The "PMA_Visio_Relation_Schema" constructor + * + * Upon instantiation This outputs the Visio XML document + * that user can download + * + * @return void + * @see PMA_VISIO,Table_Stats,Relation_Stats + */ + function __construct() + { + global $visio,$db; + + $this->setPageNumber($_POST['pdf_page_number']); + $this->setShowGrid(isset($_POST['show_grid'])); + $this->setShowColor($_POST['show_color']); + $this->setShowKeys(isset($_POST['show_keys'])); + $this->setOrientation(isset($_POST['orientation'])); + $this->setPaper($_POST['paper']); + $this->setExportType($_POST['export_type']); + + $visio = new PMA_VISIO(); + $visio->setTitle(sprintf(__('Schema of the %s database - Page %s'), $db, $this->pageNumber)); + $visio->SetAuthor('phpMyAdmin ' . PMA_VERSION); + $visio->startVisioDoc(); + $alltables = $this->getAllTables($db,$this->pageNumber); + + foreach ($alltables as $table) { + if (!isset($this->tables[$table])) { + $this->tables[$table] = new Table_Stats($table, $this->pageNumber, $this->showKeys); + } + } + + $seen_a_relation = false; + foreach ($alltables as $one_table) { + $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); + if ($exist_rel) { + $seen_a_relation = true; + foreach ($exist_rel as $master_field => $rel) { + /* put the foreign table on the schema only if selected + * by the user + * (do not use array_search() because we would have to + * to do a === FALSE and this is not PHP3 compatible) + */ + if (in_array($rel['foreign_table'], $alltables)) { + $this->_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'],$this->showKeys); + } + } + } + } + $this->_drawTables($this->showColor); + + if ($seen_a_relation) { + $this->_drawRelations($this->showColor); + } + + $visio->endVisioDoc(); + $visio->showOutput($db.'-'.$this->pageNumber); + exit(); + print '<pre>'; + print_r(get_object_vars($visio)); + print_r(get_object_vars($this)); + print '</pre>'; + + } + + /** + * Defines relation objects + * + * @param string masterTable The master table name + * @param string masterField The relation field in the master table + * @param string foreignTable The foreign table name + * @param string foreignField The relation field in the foreign table + * @return void + * @access private + * @see Table_Stats::__construct(),Relation_Stats::__construct() + */ + private function _addRelation($masterTable, $masterField, $foreignTable, $foreignField, $showKeys) + { + if (!isset($this->tables[$masterTable])) { + $this->tables[$masterTable] = new Table_Stats($masterTable, $this->pageNumber, $showKeys); + } + if (!isset($this->tables[$foreignTable])) { + $this->tables[$foreignTable] = new Table_Stats($foreignTable, $this->pageNumber, $showKeys); + } + $this->_relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); + } + + /** + * Draws relation references + * + * connects master table's master field to + * foreign table's forein field. + * + * @param boolean changeColor Whether to use one color per relation or not + * @return void + * @access private + * @see Relation_Stats::relationDraw() + */ + private function _drawRelations($changeColor) + { + foreach ($this->_relations as $relation) { + $relation->relationDraw($changeColor); + } + } + + /** + * Draws tables + * + * + * @param boolean changeColor Whether to show color for tables text or not + * @return void + * @access private + * @see Table_Stats::tableDraw() + */ + private function _drawTables($changeColor) + { + foreach ($this->tables as $table) { + $table->tableDraw($changeColor); + } + } } ?> \ No newline at end of file From 2a5a6e39b1eb888bd01d3c00af69b2b4a77dccbf Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Mon, 16 Aug 2010 17:53:30 +0500 Subject: [PATCH 33/48] EPS Relation Schema Class: adding boundingbox and pages commands --- libraries/schema/Eps_Relation_Schema.class.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/schema/Eps_Relation_Schema.class.php b/libraries/schema/Eps_Relation_Schema.class.php index e0df230fe..674b89e25 100644 --- a/libraries/schema/Eps_Relation_Schema.class.php +++ b/libraries/schema/Eps_Relation_Schema.class.php @@ -98,6 +98,8 @@ class PMA_EPS $this->stringCommands .= '%%Orientation: ' . $value . "\n"; } $this->stringCommands .= "%%EndComments \n"; + $this->stringCommands .= "%%Pages 1 \n"; + $this->stringCommands .= "%%BoundingBox: 72 150 144 170 \n"; } /** @@ -793,13 +795,14 @@ class PMA_Eps_Relation_Schema extends PMA_Export_Relation_Schema $this->_drawTables($this->showColor); $eps->endEpsDoc(); + $eps->showOutput($db.'-'.$this->pageNumber); + exit(); print '<pre>'; print_r(get_object_vars($eps)); print_r($alltables); print_r(get_object_vars($this)); print '</pre>'; - exit(); - $eps->showOutput($db.'-'.$this->pageNumber); + } /** From 03d37b45f1ec22cfc9a7c46ee9d9f549980d10bb Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Tue, 17 Aug 2010 15:23:06 +0500 Subject: [PATCH 34/48] Removing previous schema generation code which was duplicating --- db_operations.php | 15 +- libraries/display_pdf_schema.lib.php | 71 -- pdf_pages.php | 559 ---------- pdf_schema.php | 1404 -------------------------- 4 files changed, 4 insertions(+), 2045 deletions(-) delete mode 100644 libraries/display_pdf_schema.lib.php delete mode 100644 pdf_pages.php delete mode 100644 pdf_schema.php diff --git a/db_operations.php b/db_operations.php index f2ab263ab..9bba859e2 100644 --- a/db_operations.php +++ b/db_operations.php @@ -520,17 +520,10 @@ if ($cfgRelation['pdfwork'] && $num_tables > 0) { ?> WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\''; $test_rs = PMA_query_as_controluser($test_query, null, PMA_DBI_QUERY_STORE); - if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { - include('./libraries/display_pdf_schema.lib.php'); - } // end if - echo '<fieldset><a href="pdf_pages.php?' . $url_query . '">'; - if ($cfg['PropertiesIconic']) { - echo '<img class="icon" src="' . $pmaThemeImage . 'b_edit.png"' - .' alt="" width="16" height="16" />'; - } - echo __('Edit PDF Pages') . '</a></fieldset>'; - // Export Relational Schema View - echo '<fieldset><a href="export_relation_schema.php?' . $url_query . '">'; + /* + * Export Relational Schema View + */ + echo '<fieldset><a href="export_relation_schema.php?' . $url_query . '">'; if ($cfg['PropertiesIconic']) { echo '<img class="icon" src="' . $pmaThemeImage . 'b_edit.png"' .' alt="" width="16" height="16" />'; diff --git a/libraries/display_pdf_schema.lib.php b/libraries/display_pdf_schema.lib.php deleted file mode 100644 index ad12fc3d6..000000000 --- a/libraries/display_pdf_schema.lib.php +++ /dev/null @@ -1,71 +0,0 @@ -<!-- PDF schema --> -<form method="post" action="pdf_schema.php"> -<fieldset> - <legend> - <?php - echo PMA_generate_common_hidden_inputs($db); - if ($cfg['PropertiesIconic']) { - echo '<img class="icon" src="' . $pmaThemeImage . 'b_view.png"' - .' alt="" width="16" height="16" />'; - } - echo __('Display PDF schema'); - ?>: - </legend> - <?php - if (isset($test_rs)) { - ?> - <label for="pdf_page_number_opt"><?php echo __('Page number:'); ?></label> - <select name="pdf_page_number" id="pdf_page_number_opt"> - <?php - while ($pages = @PMA_DBI_fetch_assoc($test_rs)) { - echo ' <option value="' . $pages['page_nr'] . '">' - . $pages['page_nr'] . ': ' . htmlspecialchars($pages['page_descr']) . '</option>' . "\n"; - } // end while - PMA_DBI_free_result($test_rs); - unset($test_rs); - ?> - </select><br /> - <?php } else { ?> - <input type="hidden" name="pdf_page_number" value="<?php echo htmlspecialchars($chpage); ?>" /> - <?php } ?> - - <input type="checkbox" name="show_grid" id="show_grid_opt" /> - <label for="show_grid_opt"><?php echo __('Show grid'); ?></label><br /> - <input type="checkbox" name="show_color" id="show_color_opt" - checked="checked" /> - <label for="show_color_opt"><?php echo __('Show color'); ?></label><br /> - <input type="checkbox" name="show_table_dimension" id="show_table_dim_opt" /> - <label for="show_table_dim_opt"><?php echo __('Show dimension of tables'); ?> - </label><br /> - <input type="checkbox" name="all_tab_same_wide" id="all_tab_same_wide" /> - <label for="all_tab_same_wide"><?php echo __('Display all tables with the same width'); ?> - </label><br /> - <input type="checkbox" name="with_doc" id="with_doc" checked="checked" /> - <label for="with_doc"><?php echo __('Data Dictionary'); ?></label><br /> - <input type="checkbox" name="show_keys" id="show_keys" /> - <label for="show_keys"><?php echo __('Only show keys'); ?></label><br /> - - <label for="orientation_opt"><?php echo __('Data Dictionary Format'); ?></label> - <select name="orientation" id="orientation_opt"> - <option value="L"><?php echo __('Landscape');?></option> - <option value="P"><?php echo __('Portrait');?></option> - </select><br /> - - <label for="paper_opt"><?php echo __('Paper size'); ?></label> - <select name="paper" id="paper_opt"> - <?php - foreach ($cfg['PDFPageSizes'] AS $key => $val) { - echo '<option value="' . $val . '"'; - if ($val == $cfg['PDFDefaultPageSize']) { - echo ' selected="selected"'; - } - echo ' >' . $val . '</option>' . "\n"; - } - ?> - </select> -</fieldset> -<fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /> -</fieldset> -</form> - diff --git a/pdf_pages.php b/pdf_pages.php deleted file mode 100644 index 75d4622fd..000000000 --- a/pdf_pages.php +++ /dev/null @@ -1,559 +0,0 @@ -<?php -/* vim: set expandtab sw=4 ts=4 sts=4: */ -/** - * - * @package phpMyAdmin - */ - -/** - * Gets some core libraries - */ -require_once './libraries/common.inc.php'; -require_once './libraries/db_common.inc.php'; -require './libraries/StorageEngine.class.php'; - -$active_page = 'db_operations.php'; -require_once './libraries/db_common.inc.php'; -$url_query .= '&amp;goto=pdf_pages.php'; -require_once './libraries/db_info.inc.php'; - -/** - * Settings for relation stuff - */ -$cfgRelation = PMA_getRelationsParam(); - -// This is to avoid "Command out of sync" errors. Before switching this to -// a value of 0 (for MYSQLI_USE_RESULT), please check the logic -// to free results wherever needed. -$query_default_option = PMA_DBI_QUERY_STORE; - -/** - * Now in ./libraries/relation.lib.php we check for all tables - * that we need, but if we don't find them we are quiet about it - * so people can work without. - * This page is absolutely useless if you didn't set up your tables - * correctly, so it is a good place to see which tables we can and - * complain ;-) - */ -if (!$cfgRelation['relwork']) { - echo sprintf(__('<b>%s</b> table not found or not set in %s'), 'relation', 'config.inc.php') . '<br />' . "\n" - . PMA_showDocu('relation') . "\n"; - require './libraries/footer.inc.php'; -} - -if (!$cfgRelation['displaywork']) { - echo sprintf(__('<b>%s</b> table not found or not set in %s'), 'table_info', 'config.inc.php') . '<br />' . "\n" - . PMA_showDocu('table_info') . "\n"; - require './libraries/footer.inc.php'; -} - -if (!isset($cfgRelation['table_coords'])){ - echo sprintf(__('<b>%s</b> table not found or not set in %s'), 'table_coords', 'config.inc.php') . '<br />' . "\n" - . PMA_showDocu('table_coords') . "\n"; - require './libraries/footer.inc.php'; -} -if (!isset($cfgRelation['pdf_pages'])) { - echo sprintf(__('<b>%s</b> table not found or not set in %s'), 'pdf_page', 'config.inc.php') . '<br />' . "\n" - . PMA_showDocu('pdf_pages') . "\n"; - require './libraries/footer.inc.php'; -} - -if ($cfgRelation['pdfwork']) { - // Now is the time to work on all changes - if (isset($do)) { - switch ($do) { - case 'choosepage': - if ($action_choose=="1") { - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND page_nr = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - - unset($chpage); - } - break; - case 'createpage': - $pdf_page_number = PMA_REL_create_page($newpage, $cfgRelation, $db, $query_default_option); - - // A u t o m a t i c l a y o u t - // ================================ - if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { - $all_tables = array(); - } - - if (isset($auto_layout_foreign)) { - // get the tables list - $tables = PMA_DBI_get_tables_full($db); - // find the ones who support FOREIGN KEY; it's not - // important that we group together InnoDB tables - // and PBXT tables, as this logic is just to put - // the tables on the layout, not to determine relations - $foreignkey_tables = array(); - foreach($tables as $table_name => $table_properties) { - if (PMA_foreignkey_supported($table_properties['ENGINE'])) { - $foreignkey_tables[] = $table_name; - } - } - $all_tables = $foreignkey_tables; - // could be improved by finding the tables which have the - // most references keys and placing them at the beginning - // of the array (so that they are all center of schema) - unset($tables, $foreignkey_tables); - } // endif auto_layout_foreign - - if (isset($auto_layout_internal)) { - // get the tables that have relations, by descending - // number of links - $master_tables = 'SELECT COUNT(master_table), master_table' - . ' FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) - . ' WHERE master_db = \'' . $db . '\'' - . ' GROUP BY master_table' - . ' ORDER BY ' . PMA_backquote('COUNT(master_table)') . ' DESC '; - $master_tables_rs = PMA_query_as_controluser($master_tables, FALSE, $query_default_option); - if ($master_tables_rs && PMA_DBI_num_rows($master_tables_rs) > 0) { - // first put all the master tables at beginning - // of the list, so they are near the center of - // the schema - while (list(, $master_table) = PMA_DBI_fetch_row($master_tables_rs)) { - $all_tables[] = $master_table; - } - - // then for each master, add its foreigns into an array - // of foreign tables, if not already there - // (a foreign might be foreign for more than - // one table, and might be a master itself) - - $foreign_tables = array(); - foreach ($all_tables AS $master_table) { - $foreigners = PMA_getForeigners($db, $master_table); - foreach ($foreigners AS $foreigner) { - if (!in_array($foreigner['foreign_table'], $foreign_tables)) { - $foreign_tables[] = $foreigner['foreign_table']; - } - } - } - - // then merge the arrays - foreach ($foreign_tables AS $foreign_table) { - if (!in_array($foreign_table, $all_tables)) { - $all_tables[] = $foreign_table; - } - } - } // endif there are master tables - } // endif auto_layout_internal - - if (isset($auto_layout_internal) || isset($auto_layout_foreign)) { - // now generate the coordinates for the schema, - // in a clockwise spiral - - $pos_x = 300; - $pos_y = 300; - $delta = 110; - $delta_mult = 1.10; - $direction = "right"; - foreach ($all_tables AS $current_table) { - - // save current table's coordinates - $insert_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . '(db_name, table_name, pdf_page_number, x, y) ' - . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($current_table) . '\',' . $pdf_page_number . ',' . $pos_x . ',' . $pos_y . ')'; - PMA_query_as_controluser($insert_query, FALSE, $query_default_option); - - // compute for the next table - switch ($direction) { - case 'right': - $pos_x += $delta; - $direction = "down"; - $delta *= $delta_mult; - break; - case 'down': - $pos_y += $delta; - $direction = "left"; - $delta *= $delta_mult; - break; - case 'left': - $pos_x -= $delta; - $direction = "up"; - $delta *= $delta_mult; - break; - case 'up': - $pos_y -= $delta; - $direction = "right"; - $delta *= $delta_mult; - break; - } // end switch - } // end foreach - } // end if some auto-layout to do - - $chpage = $pdf_page_number; - - break; - - case 'edcoord': - for ($i = 0; $i < $c_table_rows; $i++) { - $arrvalue = 'c_table_' . $i; - $arrvalue = $$arrvalue; - if (!isset($arrvalue['x']) || $arrvalue['x'] == '') { - $arrvalue['x'] = 0; - } - if (!isset($arrvalue['y']) || $arrvalue['y'] == '') { - $arrvalue['y'] = 0; - } - if (isset($arrvalue['name']) && $arrvalue['name'] != '--') { - $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - $test_rs = PMA_query_as_controluser($test_query, FALSE, $query_default_option); - if ($test_rs && PMA_DBI_num_rows($test_rs) > 0) { - if (isset($arrvalue['delete']) && $arrvalue['delete'] == 'y') { - $ch_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - } else { - $ch_query = 'UPDATE ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . 'SET x = ' . $arrvalue['x'] . ', y= ' . $arrvalue['y'] - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($arrvalue['name']) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - } - } else { - $ch_query = 'INSERT INTO ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' - . '(db_name, table_name, pdf_page_number, x, y) ' - . 'VALUES (\'' . PMA_sqlAddslashes($db) . '\', \'' . PMA_sqlAddslashes($arrvalue['name']) . '\', \'' . PMA_sqlAddslashes($chpage) . '\',' . $arrvalue['x'] . ',' . $arrvalue['y'] . ')'; - } - PMA_query_as_controluser($ch_query, FALSE, $query_default_option); - } // end if - } // end for - break; - case 'deleteCrap': - foreach ($delrow AS $current_row) { - $d_query = 'DELETE FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) . ' ' . "\n" - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' . "\n" - . ' AND table_name = \'' . PMA_sqlAddslashes($current_row) . '\'' . "\n" - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; - PMA_query_as_controluser($d_query, FALSE, $query_default_option); - } - break; - } // end switch - } // end if (isset($do)) - - // We will need an array of all tables in this db - $selectboxall = array('--'); - $alltab_rs = PMA_DBI_query('SHOW TABLES FROM ' . PMA_backquote($db) . ';', null, PMA_DBI_QUERY_STORE); - while ($val = @PMA_DBI_fetch_row($alltab_rs)) { - $selectboxall[] = $val[0]; - } - - // Now first show some possibility to choose a page for the pdf - $page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\''; - $page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); - - if ($page_rs && PMA_DBI_num_rows($page_rs) > 0) { - ?> -<form method="get" action="pdf_pages.php" name="selpage"> - <fieldset> - <legend> - <?php echo __('Please choose a page to edit') . "\n"; ?> - </legend> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="do" value="choosepage" /> - <select name="chpage" onchange="this.form.submit()"> - <?php - while ($curr_page = PMA_DBI_fetch_assoc($page_rs)) { - echo "\n" . ' ' - . '<option value="' . $curr_page['page_nr'] . '"'; - if (isset($chpage) && $chpage == $curr_page['page_nr']) { - echo ' selected="selected"'; - } - echo '>' . $curr_page['page_nr'] . ': ' . htmlspecialchars($curr_page['page_descr']) . '</option>'; - } // end while - echo "\n"; - ?> - </select> -<?php - $choices = array( - '0' => __('Edit'), - '1' => __('Delete')); - PMA_display_html_radio('action_choose', $choices, '0', false); - unset($choices); -?> - </fieldset> - <fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /><br /> - </fieldset> -</form> - <?php - } - echo "\n"; - - // Possibility to create a new page: - ?> -<form method="post" action="pdf_pages.php" name="crpage"> - <fieldset> - <legend> - <?php echo __('Create a page') . "\n"; ?> - </legend> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="do" value="createpage" /> - <table> - <tr> - <td><label for="id_newpage"><?php echo __('Page name'); ?></label></td> - <td><input type="text" name="newpage" id="id_newpage" size="20" maxlength="50" /></td> - </tr> - <tr> - <td><?php echo __('Automatic layout based on'); ?></td> - <td> - <input type="checkbox" name="auto_layout_internal" id="id_auto_layout_internal" /><label for="id_auto_layout_internal"><?php echo __('Internal relations'); ?></label><br /> -<?php - if (PMA_StorageEngine::isValid('InnoDB') || PMA_StorageEngine::isValid('PBXT')) { -?> - <input type="checkbox" name="auto_layout_foreign" id="id_auto_layout_foreign" /><label for="id_auto_layout_foreign">FOREIGN KEY</label><br /> -<?php - } -?> - </td></tr> - </table> - </fieldset> - <fieldset class="tblFooters"> - <input type="submit" value="<?php echo __('Go'); ?>" /> - </fieldset> -</form> - <?php - // Now if we already have chosen a page number then we should show the - // tables involved - if (isset($chpage) && $chpage > 0) { - echo "\n"; - ?> -<hr /> - -<h2><?php echo __('Select Tables') ;?></h2> - -<?php -$page_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = \'' . PMA_sqlAddslashes($chpage) . '\''; -$page_rs = PMA_query_as_controluser($page_query, FALSE, $query_default_option); -$array_sh_page = array(); -$draginit = ''; -$reset_draginit = ''; -$i = 0; -while ($temp_sh_page = @PMA_DBI_fetch_assoc($page_rs)) { - $array_sh_page[] = $temp_sh_page; -} - -// Display WYSIWYG-PDF parts? -if ($cfg['WYSIWYG-PDF']) { - if (!isset($_POST['with_field_names']) && !isset($_POST['showwysiwyg'])) { - $with_field_names = TRUE; - } -?> -<script type="text/javascript" src="./js/dom-drag.js"></script> -<form method="post" action="pdf_pages.php" name="dragdrop"> -<input type="button" name="dragdrop" value="<?php echo __('Toggle scratchboard'); ?>" onclick="ToggleDragDrop('pdflayout');" /> - <input type="button" name="dragdropreset" value="<?php echo __('Reset'); ?>" onclick="resetDrag();" /> -</form> -<div id="pdflayout" class="pdflayout" style="visibility: hidden;"> -<?php -foreach ($array_sh_page AS $key => $temp_sh_page) { - $drag_x = $temp_sh_page['x']; - $drag_y = $temp_sh_page['y']; - - $draginit .= ' Drag.init(getElement("table_' . $i . '"), null, 0, parseInt(myid.style.width)-2, 0, parseInt(myid.style.height)-5);' . "\n"; - $draginit .= ' getElement("table_' . $i . '").onDrag = function (x, y) { document.edcoord.elements["c_table_' . $i . '[x]"].value = parseInt(x); document.edcoord.elements["c_table_' . $i . '[y]"].value = parseInt(y) }' . "\n"; - $draginit .= ' getElement("table_' . $i . '").style.left = "' . $drag_x . 'px";' . "\n"; - $draginit .= ' getElement("table_' . $i . '").style.top = "' . $drag_y . 'px";' . "\n"; - $reset_draginit .= ' getElement("table_' . $i . '").style.left = "2px";' . "\n"; - $reset_draginit .= ' getElement("table_' . $i . '").style.top = "' . (15 * $i) . 'px";' . "\n"; - $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[x]"].value = "2"' . "\n"; - $reset_draginit .= ' document.edcoord.elements["c_table_' . $i . '[y]"].value = "' . (15 * $i) . '"' . "\n"; - - $local_query = 'SHOW FIELDS FROM ' - . PMA_backquote($temp_sh_page['table_name']) - . ' FROM ' . PMA_backquote($db); - $fields_rs = PMA_DBI_query($local_query); - unset($local_query); - $fields_cnt = PMA_DBI_num_rows($fields_rs); - - echo '<div id="table_' . $i . '" class="pdflayout_table"><u>' . $temp_sh_page['table_name'] . '</u>'; - if (isset($with_field_names)) { - while ($row = PMA_DBI_fetch_assoc($fields_rs)) { - echo '<br />' . htmlspecialchars($row['Field']) . "\n"; - } - } - echo '</div>' . "\n"; - PMA_DBI_free_result($fields_rs); - unset($fields_rs); - - $i++; -} -?> -</div> -<script type="text/javascript"> -//<![CDATA[ -function PDFinit() { - refreshLayout(); - myid = getElement('pdflayout'); - <?php echo $draginit; ?> -} - -function resetDrag() { - <?php echo $reset_draginit; ?> -} -//]]> -</script> -<?php -} // end if WYSIWYG-PDF -?> - -<form method="post" action="pdf_pages.php" name="edcoord"> - <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> - <input type="hidden" name="chpage" value="<?php echo htmlspecialchars($chpage); ?>" /> - <input type="hidden" name="do" value="edcoord" /> - <table border="0"> - <tr> - <th><?php echo __('Table'); ?></th> - <th><?php echo __('Delete'); ?></th> - <th>X</th> - <th>Y</th> - </tr> - <?php - if (isset($ctable)) { - unset($ctable); - } - - - $i = 0; - $odd_row = true; - foreach ($array_sh_page AS $dummy_sh_page => $sh_page) { - $_mtab = $sh_page['table_name']; - $tabExist[$_mtab] = FALSE; - echo "\n" . ' <tr class="'; - if ($odd_row) { - echo 'odd'; - } else { - echo 'even'; - } - echo '">'; - $odd_row != $odd_row; - echo "\n" . ' <td>' - . "\n" . ' <select name="c_table_' . $i . '[name]">'; - foreach ($selectboxall AS $key => $value) { - echo "\n" . ' <option value="' . htmlspecialchars($value) . '"'; - if ($value == $sh_page['table_name']) { - echo ' selected="selected"'; - $tabExist[$_mtab] = TRUE; - } - echo '>' . htmlspecialchars($value) . '</option>'; - } // end while - echo "\n" . ' </select>' - . "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'x\', this.value)"' : '') . ' name="c_table_' . $i . '[x]" value="' . $sh_page['x'] . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" ' . ($cfg['WYSIWYG-PDF'] ? 'onchange="dragPlace(' . $i . ', \'y\', this.value)"' : '') . ' name="c_table_' . $i . '[y]" value="' . $sh_page['y'] . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' </tr>'; - $i++; - } // end while - // Do one more empty row - echo "\n" . ' <tr class="'; - if ($odd_row) { - echo 'odd'; - } else { - echo 'even'; - } - $odd_row != $odd_row; - echo '">'; - echo "\n" . ' <td>' - . "\n" . ' <select name="c_table_' . $i . '[name]">'; - foreach ($selectboxall AS $key => $value) { - echo "\n" . ' <option value="' . htmlspecialchars($value) . '">' . htmlspecialchars($value) . '</option>'; - } - echo "\n" . ' </select>' - . "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="checkbox" name="c_table_' . $i . '[delete]" value="y" />' . __('Delete'); - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" name="c_table_' . $i . '[x]" value="' . (isset($sh_page['x'])?$sh_page['x']:'') . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' <td>' - . "\n" . ' <input type="text" name="c_table_' . $i . '[y]" value="' . (isset($sh_page['y'])?$sh_page['y']:'') . '" />'; - echo "\n" . ' </td>'; - echo "\n" . ' </tr>'; - echo "\n" . ' </table>' . "\n"; - - echo "\n" . ' <input type="hidden" name="c_table_rows" value="' . ($i + 1) . '" />'; - echo ($cfg['WYSIWYG-PDF'] ? "\n" . ' <input type="hidden" id="showwysiwyg" name="showwysiwyg" value="' . ((isset($showwysiwyg) && $showwysiwyg == '1') ? '1' : '0') . '" />' : ''); - echo "\n" . ' <input type="checkbox" name="with_field_names" ' . (isset($with_field_names) ? 'checked="checked"' : ''). ' />' . __('Column names') . '<br />'; - echo "\n" . ' <input type="submit" value="' . __('Save') . '" />'; - echo "\n" . '</form>' . "\n\n"; - } // end if - - // Check if there are tables that need to be deleted, - // if there are, ask the user for allowance - $_strtrans = ''; - $_strname = ''; - $shoot = FALSE; - if (!empty($tabExist) && is_array($tabExist)) { - foreach ($tabExist AS $key => $value) { - if (!$value) { - $_strtrans .= '<input type="hidden" name="delrow[]" value="' . htmlspecialchars($key) . '" />' . "\n"; - $_strname .= '<li>' . htmlspecialchars($key) . '</li>' . "\n"; - $shoot = TRUE; - } - } - if ($shoot) { - echo '<form action="pdf_pages.php" method="post">' . "\n" - . PMA_generate_common_hidden_inputs($db, $table) - . '<input type="hidden" name="do" value="deleteCrap" />' . "\n" - . '<input type="hidden" name="chpage" value="' . htmlspecialchars($chpage) . '" />' . "\n" - . __('The current page has references to tables that no longer exist. Would you like to delete those references?') - . '<ul>' . "\n" - . $_strname - . '</ul>' . "\n" - . $_strtrans - . '<input type="submit" value="' . __('Go') . '" />' . "\n" - . '</form>'; - } - } - // ------------------------------------ - // d i s p l a y p d f s c h e m a - // ------------------------------------ - - if (isset($do) - && ($do == 'edcoord' - || ($do == 'choosepage' && isset($chpage)) - || ($do == 'createpage' && isset($chpage)))) { - include('./libraries/display_pdf_schema.lib.php'); - if ((isset($showwysiwyg) && $showwysiwyg == '1')) { -?> -<script type="text/javascript"> -//<![CDATA[ -ToggleDragDrop('pdflayout'); -//]]> -</script> -<?php - } - } // end if -} // end if ($cfgRelation['pdfwork']) - - -/** - * Displays the footer - */ -echo "\n"; -require './libraries/footer.inc.php'; -?> diff --git a/pdf_schema.php b/pdf_schema.php deleted file mode 100644 index dd7426f1b..000000000 --- a/pdf_schema.php +++ /dev/null @@ -1,1404 +0,0 @@ -<?php -/* vim: set expandtab sw=4 ts=4 sts=4: */ -/** - * - * @package phpMyAdmin - */ - -/** - * Gets some core scripts - */ -require_once './libraries/common.inc.php'; - -/** - * Settings for relation stuff - */ -require_once './libraries/transformations.lib.php'; -require_once './libraries/Index.class.php'; - -$cfgRelation = PMA_getRelationsParam(); - -/** - * Now in ./libraries/relation.lib.php we check for all tables - * that we need, but if we don't find them we are quiet about it - * so people can work without. - * This page is absolutely useless if you didn't set up your tables - * correctly, so it is a good place to see which tables we can and - * complain ;-) - */ -if (!$cfgRelation['pdfwork']) { - echo '<font color="red">' . __('Error') . '</font><br />' . "\n"; - $url_to_goto = '<a href="' . $cfg['PmaAbsoluteUri'] . 'chk_rel.php?' . $url_query . '">'; - echo sprintf(__('The phpMyAdmin configuration storage has been deactivated. To find out why click %shere%s.'), $url_to_goto, '</a>') . "\n"; -} - -/** - * Font used in PDF. - * - * @todo Make this configuratble (at least Sans/Serif). - */ -define('PMA_PDF_FONT', 'DejaVuSans'); -require_once './libraries/tcpdf/tcpdf.php'; - -/** - * Extends the "FPDF" class and prepares the work - * - * @access public - * @see FPDF - * @package phpMyAdmin - */ -class PMA_PDF extends TCPDF { - /** - * Defines private properties - */ - var $x_min; - var $y_min; - var $l_marg = 10; - var $t_marg = 10; - var $scale; - var $PMA_links; - var $Outlines = array(); - var $def_outlines; - var $Alias = array(); - var $widths; - - public function getFh() - { - return $this->fh; - } - - public function getFw() - { - return $this->fw; - } - - public function setCMargin($c_margin) - { - $this->cMargin = $c_margin; - } - - function SetAlias($name, $value) - { - $this->Alias[$name] = $value ; - } - - function _putpages() - { - if (count($this->Alias) > 0) { - $nb = $this->page; - foreach ($this->Alias AS $alias => $value) { - for ($n = 1;$n <= $nb;$n++) - $this->pages[$n]=str_replace($alias, $value, $this->pages[$n]); - } - } - parent::_putpages(); - } - - /** - * Sets the scaling factor, defines minimum coordinates and margins - * - * @param float scale The scaling factor - * @param float x_min The minimum X coordinate - * @param float y_min The minimum Y coordinate - * @param float l_marg The left margin - * @param float t_marg The top margin - * @access public - */ - function PMA_PDF_setScale($scale = 1, $x_min = 0, $y_min = 0, $l_marg = -1, $t_marg = -1) - { - $this->scale = $scale; - $this->x_min = $x_min; - $this->y_min = $y_min; - if ($this->l_marg != -1) { - $this->l_marg = $l_marg; - } - if ($this->t_marg != -1) { - $this->t_marg = $t_marg; - } - } // end of the "PMA_PDF_setScale" function - /** - * Outputs a scaled cell - * - * @param float w The cell width - * @param float h The cell height - * @param string txt The text to output - * @param mixed border Whether to add borders or not - * @param integer ln Where to put the cursor once the output is done - * @param string align Align mode - * @param integer fill Whether to fill the cell with a color or not - * @access public - * @see FPDF::Cell() - */ - function PMA_PDF_cellScale($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = 0, $link = '') - { - $h = $h / $this->scale; - $w = $w / $this->scale; - $this->Cell($w, $h, $txt, $border, $ln, $align, $fill, $link); - } // end of the "PMA_PDF_cellScale" function - /** - * Draws a scaled line - * - * @param float x1 The horizontal position of the starting point - * @param float y1 The vertical position of the starting point - * @param float x2 The horizontal position of the ending point - * @param float y2 The vertical position of the ending point - * @access public - * @see FPDF::Line() - */ - function PMA_PDF_lineScale($x1, $y1, $x2, $y2) - { - $x1 = ($x1 - $this->x_min) / $this->scale + $this->l_marg; - $y1 = ($y1 - $this->y_min) / $this->scale + $this->t_marg; - $x2 = ($x2 - $this->x_min) / $this->scale + $this->l_marg; - $y2 = ($y2 - $this->y_min) / $this->scale + $this->t_marg; - $this->Line($x1, $y1, $x2, $y2); - } // end of the "PMA_PDF_lineScale" function - /** - * Sets x and y scaled positions - * - * @param float x The x position - * @param float y The y position - * @access public - * @see FPDF::SetXY() - */ - function PMA_PDF_setXyScale($x, $y) - { - $x = ($x - $this->x_min) / $this->scale + $this->l_marg; - $y = ($y - $this->y_min) / $this->scale + $this->t_marg; - $this->SetXY($x, $y); - } // end of the "PMA_PDF_setXyScale" function - /** - * Sets the X scaled positions - * - * @param float x The x position - * @access public - * @see FPDF::SetX() - */ - function PMA_PDF_setXScale($x) - { - $x = ($x - $this->x_min) / $this->scale + $this->l_marg; - $this->SetX($x); - } // end of the "PMA_PDF_setXScale" function - /** - * Sets the scaled font size - * - * @param float size The font size (in points) - * @access public - * @see FPDF::SetFontSize() - */ - function PMA_PDF_setFontSizeScale($size) - { - // Set font size in points - $size = $size / $this->scale; - $this->SetFontSize($size); - } // end of the "PMA_PDF_setFontSizeScale" function - /** - * Sets the scaled line width - * - * @param float width The line width - * @access public - * @see FPDF::SetLineWidth() - */ - function PMA_PDF_setLineWidthScale($width) - { - $width = $width / $this->scale; - $this->SetLineWidth($width); - } // end of the "PMA_PDF_setLineWidthScale" function - /** - * Displays an error message - * - * @param string error_message the error mesage - * @global array the PMA configuration array - * @global integer the current server id - * @global string the current language - * @global string the charset to convert to - * @global string the current database name - * @global string the current charset - * @global string the current text direction - * @global string a localized string - * @global string an other localized string - * @access public - */ - function PMA_PDF_die($error_message = '') - { - global $cfg; - global $server, $lang, $db; - global $charset, $text_dir; - - require_once './libraries/header.inc.php'; - - echo '<p><strong>PDF - ' . __('Error') . '</strong></p>' . "\n"; - if (!empty($error_message)) { - $error_message = htmlspecialchars($error_message); - } - echo '<p>' . "\n"; - echo ' ' . $error_message . "\n"; - echo '</p>' . "\n"; - - echo '<a href="db_structure.php?' . PMA_generate_common_url($db) - . '">' . __('Back') . '</a>'; - echo "\n"; - - require './libraries/footer.inc.php'; - } // end of the "PMA_PDF_die()" function - /** - * Aliases the "Error()" function from the FPDF class to the - * "PMA_PDF_die()" one - * - * @param string error_message the error mesage - * @access public - * @see PMA_PDF_die - */ - function Error($error_message = '') - { - $this->PMA_PDF_die($error_message); - } // end of the "Error()" method - function Header() - { - // We only show this if we find something in the new pdf_pages table - - // This function must be named "Header" to work with the FPDF library - global $cfgRelation, $db, $pdf_page_number, $with_doc; - if ($with_doc) { - $test_query = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND page_nr = \'' . $pdf_page_number . '\''; - $test_rs = PMA_query_as_controluser($test_query); - $pages = @PMA_DBI_fetch_assoc($test_rs); - $this->SetFont('', 'B', 14); - $this->Cell(0, 6, ucfirst($pages['page_descr']), 'B', 1, 'C'); - $this->SetFont('', ''); - $this->Ln(); - } - } - function Footer() - { - // This function must be named "Footer" to work with the FPDF library - global $with_doc; - if ($with_doc) { - $this->SetY(-15); - $this->SetFont('', '', 14); - $this->Cell(0, 6, __('Page number:') . ' ' . $this->PageNo() . '/{nb}', 'T', 0, 'C'); - $this->Cell(0, 6, PMA_localisedDate(), 0, 1, 'R'); - $this->SetY(20); - } - } - function Bookmark($txt, $level = 0, $y = 0) - { - // Add a bookmark - $this->Outlines[0][] = $level; - $this->Outlines[1][] = $txt; - $this->Outlines[2][] = $this->page; - if ($y == -1) { - $y = $this->GetY(); - } - $this->Outlines[3][] = round($this->hPt - $y * $this->k, 2); - } - - function _putbookmarks() - { - if (count($this->Outlines) > 0) { - // Save object number - $memo_n = $this->n; - // Take the number of sub elements for an outline - $nb_outlines = sizeof($this->Outlines[0]); - $first_level = array(); - $parent = array(); - $parent[0] = 1; - for ($i = 0; $i < $nb_outlines; $i++) { - $level = $this->Outlines[0][$i]; - $kids = 0; - $last = -1; - $prev = -1; - $next = -1; - if ($i > 0) { - $cursor = $i-1; - // Take the previous outline in the same level - while ($this->Outlines[0][$cursor] > $level && $cursor > 0) - $cursor--; - if ($this->Outlines[0][$cursor] == $level) { - $prev = $cursor; - } - } - if ($i < $nb_outlines-1) { - $cursor = $i + 1; - while (isset($this->Outlines[0][$cursor]) && $this->Outlines[0][$cursor] > $level) { - // Take the immediate kid in level + 1 - if ($this->Outlines[0][$cursor] == $level + 1) { - $kids++; - $last = $cursor; - } - $cursor++; - } - $cursor = $i + 1; - // Take the next outline in the same level - while ($this->Outlines[0][$cursor] > $level && ($cursor + 1 < sizeof($this->Outlines[0]))) - $cursor++; - if ($this->Outlines[0][$cursor] == $level) { - $next = $cursor; - } - } - $this->_newobj(); - $parent[$level + 1] = $this->n; - if ($level == 0) { - $first_level[] = $this->n; - } - $this->_out('<<'); - $this->_out('/Title (' . $this->Outlines[1][$i] . ')'); - $this->_out('/Parent ' . $parent[$level] . ' 0 R'); - if ($prev != -1) { - $this->_out('/Prev ' . ($memo_n + $prev + 1) . ' 0 R'); - } - if ($next != -1) { - $this->_out('/Next ' . ($this->n + $next - $i) . ' 0 R'); - } - $this->_out('/Dest [' . (1 + (2 * $this->Outlines[2][$i])) . ' 0 R /XYZ null ' . $this->Outlines[3][$i] . ' null]'); - if ($kids > 0) { - $this->_out('/First ' . ($this->n + 1) . ' 0 R'); - $this->_out('/Last ' . ($this->n + $last - $i) . ' 0 R'); - $this->_out('/Count -' . $kids); - } - $this->_out('>>'); - $this->_out('endobj'); - } - // First page of outlines - $this->_newobj(); - $this->def_outlines = $this->n; - $this->_out('<<'); - $this->_out('/Type'); - $this->_out('/Outlines'); - $this->_out('/First ' . $first_level[0] . ' 0 R'); - $this->_out('/Last ' . $first_level[sizeof($first_level)-1] . ' 0 R'); - $this->_out('/Count ' . sizeof($first_level)); - $this->_out('>>'); - $this->_out('endobj'); - } - } - - function _putresources() - { - parent::_putresources(); - $this->_putbookmarks(); - } - - function _putcatalog() - { - parent::_putcatalog(); - if (count($this->Outlines) > 0) { - $this->_out('/Outlines ' . $this->def_outlines . ' 0 R'); - $this->_out('/PageMode /UseOutlines'); - } - } - function SetWidths($w) - { - // column widths - $this->widths = $w; - } - - function Row($data, $links) - { - // line height - $nb = 0; - $data_cnt = count($data); - for ($i = 0;$i < $data_cnt;$i++) - $nb = max($nb, $this->NbLines($this->widths[$i], $data[$i])); - $il = $this->FontSize; - $h = ($il + 1) * $nb; - // page break if necessary - $this->CheckPageBreak($h); - // draw the cells - $data_cnt = count($data); - for ($i = 0;$i < $data_cnt;$i++) { - $w = $this->widths[$i]; - // save current position - $x = $this->GetX(); - $y = $this->GetY(); - // draw the border - $this->Rect($x, $y, $w, $h); - if (isset($links[$i])) { - $this->Link($x, $y, $w, $h, $links[$i]); - } - // print text - $this->MultiCell($w, $il + 1, $data[$i], 0, 'L'); - // go to right side - $this->SetXY($x + $w, $y); - } - // go to line - $this->Ln($h); - } - - function CheckPageBreak($h) - { - // if height h overflows, manual page break - if ($this->GetY() + $h > $this->PageBreakTrigger) { - $this->AddPage($this->CurOrientation); - } - } - - function NbLines($w, $txt) - { - // compute number of lines used by a multicell of width w - $cw = &$this->CurrentFont['cw']; - if ($w == 0) { - $w = $this->w - $this->rMargin - $this->x; - } - $wmax = ($w-2 * $this->cMargin) * 1000 / $this->FontSize; - $s = str_replace("\r", '', $txt); - $nb = strlen($s); - if ($nb > 0 and $s[$nb-1] == "\n") { - $nb--; - } - $sep = -1; - $i = 0; - $j = 0; - $l = 0; - $nl = 1; - while ($i < $nb) { - $c = $s[$i]; - if ($c == "\n") { - $i++; - $sep = -1; - $j = $i; - $l = 0; - $nl++; - continue; - } - if ($c == ' ') { - $sep = $i; - } - $l += isset($cw[ord($c)])?$cw[ord($c)]:0 ; - if ($l > $wmax) { - if ($sep == -1) { - if ($i == $j) { - $i++; - } - } else { - $i = $sep + 1; - } - $sep = -1; - $j = $i; - $l = 0; - $nl++; - } else { - $i++; - } - } - return $nl; - } -} // end of the "PMA_PDF" class - - -/** - * Draws tables schema - * - * @access private - * @see PMA_RT - * @package phpMyAdmin - */ -class PMA_RT_Table { - /** - * Defines private properties - */ - var $nb_fiels; - var $table_name; - var $width = 0; - var $height; - var $fields = array(); - var $height_cell = 6; - var $x, $y; - var $primary = array(); - var $show_info = false; - - /** - * Returns title of the current table, - * title can have the dimensions of the table - * - * @access private - */ - function getTitle() - { - return ($this->show_info ? sprintf('%.0f', $this->width) . 'x' . sprintf('%.0f', $this->height) : '') . ' ' . $this->table_name; - } // end of the "getTitle" function - /** - * Sets the width of the table - * - * @param integer ff The font size - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Table_setWidth($ff) - { - global $pdf; - - foreach ($this->fields AS $field) { - $this->width = max($this->width, $pdf->GetStringWidth($field)); - } - $this->width += $pdf->GetStringWidth(' '); - $pdf->SetFont($ff, 'B'); - // it is unknown what value must be added, because - // table title is affected by the tabe width value - while ($this->width < $pdf->GetStringWidth($this->getTitle())) { - $this->width += 5; - } - $pdf->SetFont($ff, ''); - } // end of the "PMA_RT_Table_setWidth()" method - /** - * Sets the height of the table - * - * @access private - */ - function PMA_RT_Table_setHeight() - { - $this->height = (count($this->fields) + 1) * $this->height_cell; - } // end of the "PMA_RT_Table_setHeight()" method - /** - * Do draw the table - * - * @param integer ff The font size - * @param boolean setcolortWhether to display color - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Table_draw($ff, $setcolor = 0) - { - global $pdf, $with_doc; - - $pdf->PMA_PDF_setXyScale($this->x, $this->y); - $pdf->SetFont($ff, 'B'); - if ($setcolor) { - $pdf->SetTextColor(200); - $pdf->SetFillColor(0, 0, 128); - } - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name]['-'], -1); - } else { - $pdf->PMA_links['doc'][$this->table_name]['-'] = ''; - } - - $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, $this->getTitle(), 1, 1, 'C', $setcolor, $pdf->PMA_links['doc'][$this->table_name]['-']); - $pdf->PMA_PDF_setXScale($this->x); - $pdf->SetFont($ff, ''); - $pdf->SetTextColor(0); - $pdf->SetFillColor(255); - - foreach ($this->fields AS $field) { - if ($setcolor) { - if (in_array($field, $this->primary)) { - $pdf->SetFillColor(215, 121, 123); - } - if ($field == $this->displayfield) { - $pdf->SetFillColor(142, 159, 224); - } - } - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT'][$this->table_name][$field], -1); - } else { - $pdf->PMA_links['doc'][$this->table_name][$field] = ''; - } - - $pdf->PMA_PDF_cellScale($this->width, $this->height_cell, ' ' . $field, 1, 1, 'L', $setcolor, $pdf->PMA_links['doc'][$this->table_name][$field]); - $pdf->PMA_PDF_setXScale($this->x); - $pdf->SetFillColor(255); - } // end while - /*if ($pdf->PageNo() > 1) { - $pdf->PMA_PDF_die(__('The scale factor is too small to fit the schema on one page')); - } */ - } // end of the "PMA_RT_Table_draw()" method - /** - * The "PMA_RT_Table" constructor - * - * @param string table_name The table name - * @param integer ff The font size - * @param integer same_width The max. with among tables - * @param boolean show_keys Whether to display keys or not - * @param boolean show_info Whether to display table position or not - * @global object The current PDF document - * @global integer The current page number (from the - * $cfg['Servers'][$i]['table_coords'] table) - * @global array The relations settings - * @global string The current db name - * @access private - * @see PMA_PDF, PMA_RT_Table::PMA_RT_Table_setWidth, - PMA_RT_Table::PMA_RT_Table_setHeight - */ - function __construct($table_name, $ff, &$same_wide_width, $show_keys = false, $show_info = false) - { - global $pdf, $pdf_page_number, $cfgRelation, $db; - - $this->table_name = $table_name; - $sql = 'DESCRIBE ' . PMA_backquote($table_name); - $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); - if (!$result || !PMA_DBI_num_rows($result)) { - $pdf->PMA_PDF_die(sprintf(__('The %s table doesn\'t exist!'), $table_name)); - } - // load fields - //check to see if it will load all fields or only the foreign keys - if ($show_keys) { - $indexes = PMA_Index::getFromTable($this->table_name, $db); - $all_columns = array(); - foreach ($indexes as $index) { - $all_columns = array_merge($all_columns, array_flip(array_keys($index->getColumns()))); - } - $this->fields = array_keys($all_columns); - } else { - while ($row = PMA_DBI_fetch_row($result)) { - $this->fields[] = $row[0]; - } - } - - $this->show_info = $show_info; - - // height and width - $this->PMA_RT_Table_setHeight(); - // setWidth must me after setHeight, because title - // can include table height which changes table width - $this->PMA_RT_Table_setWidth($ff); - if ($same_wide_width < $this->width) { - $same_wide_width = $this->width; - } - // x and y - $sql = 'SELECT x, y FROM ' - . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND table_name = \'' . PMA_sqlAddslashes($table_name) . '\'' - . ' AND pdf_page_number = ' . $pdf_page_number; - $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); - - if (!$result || !PMA_DBI_num_rows($result)) { - $pdf->PMA_PDF_die(sprintf(__('Please configure the coordinates for table %s'), $table_name)); - } - list($this->x, $this->y) = PMA_DBI_fetch_row($result); - $this->x = (double) $this->x; - $this->y = (double) $this->y; - // displayfield - $this->displayfield = PMA_getDisplayField($db, $table_name); - // index - $result = PMA_DBI_query('SHOW INDEX FROM ' . PMA_backquote($table_name) . ';', null, PMA_DBI_QUERY_STORE); - if (PMA_DBI_num_rows($result) > 0) { - while ($row = PMA_DBI_fetch_assoc($result)) { - if ($row['Key_name'] == 'PRIMARY') { - $this->primary[] = $row['Column_name']; - } - } - } // end if - } // end of the "PMA_RT_Table()" method -} // end class "PMA_RT_Table" -/** - * Draws relation links - * - * @access private - * @see PMA_RT - * @package phpMyAdmin - */ -class PMA_RT_Relation { - /** - * Defines private properties - */ - var $x_src, $y_src; - var $src_dir ; - var $dest_dir; - var $x_dest, $y_dest; - var $w_tick = 5; - - /** - * Gets arrows coordinates - * - * @param string table The current table name - * @param string column The relation column name - * @return array Arrows coordinates - * @access private - */ - function PMA_RT_Relation_getXy($table, $column) - { - $pos = array_search($column, $table->fields); - // x_left, x_right, y - return array($table->x, $table->x + + $table->width, $table->y + ($pos + 1.5) * $table->height_cell); - } // end of the "PMA_RT_Relation_getXy()" method - /** - * Do draws relation links - * - * @param boolean change_color Whether to use one color per relation or not - * @param integer i The id of the link to draw - * @global object The current PDF document - * @access private - * @see PMA_PDF - */ - function PMA_RT_Relation_draw($change_color, $i) - { - global $pdf; - - if ($change_color) { - $d = $i % 6; - $j = ($i - $d) / 6; - $j = $j % 4; - $j++; - $case = array( - array(1, 0, 0), - array(0, 1, 0), - array(0, 0, 1), - array(1, 1, 0), - array(1, 0, 1), - array(0, 1, 1) - ); - list ($a, $b, $c) = $case[$d]; - $e = (1 - ($j - 1) / 6); - $pdf->SetDrawColor($a * 255 * $e, $b * 255 * $e, $c * 255 * $e); - } else { - $pdf->SetDrawColor(0); - } // end if... else... - $pdf->PMA_PDF_setLineWidthScale(0.2); - $pdf->PMA_PDF_lineScale($this->x_src, $this->y_src, $this->x_src + $this->src_dir * $this->w_tick, $this->y_src); - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest, $this->x_dest, $this->y_dest); - $pdf->PMA_PDF_setLineWidthScale(0.1); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick, $this->y_src, $this->x_dest + $this->dest_dir * $this->w_tick, $this->y_dest); - // arrow - $root2 = 2 * sqrt(2); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src + $this->w_tick / $root2); - $pdf->PMA_PDF_lineScale($this->x_src + $this->src_dir * $this->w_tick * 0.75, $this->y_src, $this->x_src + $this->src_dir * (0.75 - 1 / $root2) * $this->w_tick, $this->y_src - $this->w_tick / $root2); - - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest + $this->w_tick / $root2); - $pdf->PMA_PDF_lineScale($this->x_dest + $this->dest_dir * $this->w_tick / 2, $this->y_dest, $this->x_dest + $this->dest_dir * (0.5 + 1 / $root2) * $this->w_tick, $this->y_dest - $this->w_tick / $root2); - $pdf->SetDrawColor(0); - } // end of the "PMA_RT_Relation_draw()" method - /** - * The "PMA_RT_Relation" constructor - * - * @param string master_table The master table name - * @param string master_field The relation field in the master table - * @param string foreign_table The foreign table name - * @param string foreigh_field The relation field in the foreign table - * @access private - * @see PMA_RT_Relation::PMA_RT_Relation_getXy - */ - function __construct($master_table, $master_field, $foreign_table, $foreign_field) - { - $src_pos = $this->PMA_RT_Relation_getXy($master_table, $master_field); - $dest_pos = $this->PMA_RT_Relation_getXy($foreign_table, $foreign_field); - $src_left = $src_pos[0] - $this->w_tick; - $src_right = $src_pos[1] + $this->w_tick; - $dest_left = $dest_pos[0] - $this->w_tick; - $dest_right = $dest_pos[1] + $this->w_tick; - - $d1 = abs($src_left - $dest_left); - $d2 = abs($src_right - $dest_left); - $d3 = abs($src_left - $dest_right); - $d4 = abs($src_right - $dest_right); - $d = min($d1, $d2, $d3, $d4); - - if ($d == $d1) { - $this->x_src = $src_pos[0]; - $this->src_dir = -1; - $this->x_dest = $dest_pos[0]; - $this->dest_dir = -1; - } elseif ($d == $d2) { - $this->x_src = $src_pos[1]; - $this->src_dir = 1; - $this->x_dest = $dest_pos[0]; - $this->dest_dir = -1; - } elseif ($d == $d3) { - $this->x_src = $src_pos[0]; - $this->src_dir = -1; - $this->x_dest = $dest_pos[1]; - $this->dest_dir = 1; - } else { - $this->x_src = $src_pos[1]; - $this->src_dir = 1; - $this->x_dest = $dest_pos[1]; - $this->dest_dir = 1; - } - $this->y_src = $src_pos[2]; - $this->y_dest = $dest_pos[2]; - } // end of the "PMA_RT_Relation()" method -} // end of the "PMA_RT_Relation" class -/** - * Draws and send the database schema - * - * @access public - * @see PMA_PDF - * @package phpMyAdmin - */ -class PMA_RT { - /** - * Defines private properties - */ - var $tables = array(); - var $relations = array(); - var $ff = PMA_PDF_FONT; - var $x_max = 0; - var $y_max = 0; - var $scale; - var $x_min = 100000; - var $y_min = 100000; - var $t_marg = 10; - var $b_marg = 10; - var $l_marg = 10; - var $r_marg = 10; - var $tablewidth; - var $same_wide = 0; - - /** - * Sets X and Y minimum and maximum for a table cell - * - * @param string table The table name - * @access private - */ - function PMA_RT_setMinMax($table) - { - $this->x_max = max($this->x_max, $table->x + $table->width); - $this->y_max = max($this->y_max, $table->y + $table->height); - $this->x_min = min($this->x_min, $table->x); - $this->y_min = min($this->y_min, $table->y); - } // end of the "PMA_RT_setMinMax()" method - /** - * Defines relation objects - * - * @param string master_table The master table name - * @param string master_field The relation field in the master table - * @param string foreign_table The foreign table name - * @param string foreign_field The relation field in the foreign table - * @param boolean show_info Whether to display table position or not - * @access private - * @see PMA_RT_setMinMax - */ - function PMA_RT_addRelation($master_table, $master_field, $foreign_table, $foreign_field, $show_info) - { - if (!isset($this->tables[$master_table])) { - $this->tables[$master_table] = new PMA_RT_Table($master_table, $this->ff, $this->tablewidth, false, $show_info); - $this->PMA_RT_setMinMax($this->tables[$master_table]); - } - if (!isset($this->tables[$foreign_table])) { - $this->tables[$foreign_table] = new PMA_RT_Table($foreign_table, $this->ff, $this->tablewidth, false, $show_info); - $this->PMA_RT_setMinMax($this->tables[$foreign_table]); - } - $this->relations[] = new PMA_RT_Relation($this->tables[$master_table], $master_field, $this->tables[$foreign_table], $foreign_field); - } // end of the "PMA_RT_addRelation()" method - /** - * Draws the grid - * - * @global object the current PMA_PDF instance - * @access private - * @see PMA_PDF - */ - function PMA_RT_strokeGrid() - { - global $pdf; - - $pdf->SetMargins(0, 0); - $pdf->SetDrawColor(200, 200, 200); - // Draws horizontal lines - for ($l = 0; $l < 21; $l++) { - $pdf->line(0, $l * 10, $pdf->getFh(), $l * 10); - // Avoid duplicates - if ($l > 0) { - $pdf->SetXY(0, $l * 10); - $label = (string) sprintf('%.0f', ($l * 10 - $this->t_marg) * $this->scale + $this->y_min); - $pdf->Cell(5, 5, ' ' . $label); - } // end if - } // end for - // Draws vertical lines - for ($j = 0; $j < 30 ;$j++) { - $pdf->line($j * 10, 0, $j * 10, $pdf->getFw()); - $pdf->SetXY($j * 10, 0); - $label = (string) sprintf('%.0f', ($j * 10 - $this->l_marg) * $this->scale + $this->x_min); - $pdf->Cell(5, 7, $label); - } // end for - } // end of the "PMA_RT_strokeGrid()" method - /** - * Draws relation arrows - * - * @param boolean change_color Whether to use one color per relation or not - * @access private - * @see PMA_RT_Relation::PMA_RT_Relation_draw() - */ - function PMA_RT_drawRelations($change_color) - { - $i = 0; - foreach ($this->relations AS $relation) { - $relation->PMA_RT_Relation_draw($change_color, $i); - $i++; - } // end while - } // end of the "PMA_RT_drawRelations()" method - /** - * Draws tables - * - * @param boolean draw_color Whether to display table position or not - * @access private - * @see PMA_RT_Table::PMA_RT_Table_draw() - */ - function PMA_RT_drawTables($draw_color = 0) - { - foreach ($this->tables AS $table) { - $table->PMA_RT_Table_draw($this->ff, $draw_color); - } - } // end of the "PMA_RT_drawTables()" method - /** - * Ouputs the PDF document to a file - * - * @global object The current PDF document - * @global string The current database name - * @global integer The current page number (from the - * $cfg['Servers'][$i]['table_coords'] table) - * @access private - * @see PMA_PDF - */ - function PMA_RT_showRt() - { - global $pdf, $db, $pdf_page_number, $cfgRelation; - - $pdf->SetFontSize(14); - $pdf->SetLineWidth(0.2); - $pdf->SetDisplayMode('fullpage'); - // Get the name of this pdfpage to use as filename (Mike Beck) - $_name_sql = 'SELECT page_descr FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['pdf_pages']) - . ' WHERE page_nr = ' . $pdf_page_number; - $_name_rs = PMA_query_as_controluser($_name_sql); - if ($_name_rs) { - $_name_row = PMA_DBI_fetch_row($_name_rs); - $filename = $_name_row[0] . '.pdf'; - } - // i don't know if there is a chance for this to happen, but rather be on the safe side: - if (empty($filename)) { - $filename = $pdf_page_number . '.pdf'; - } - // $pdf->Output($db . '_' . $filename, TRUE); - $pdf->Output($db . '_' . $filename, 'I'); // destination: Inline - } // end of the "PMA_RT_showRt()" method - /** - * The "PMA_RT" constructor - * - * @param integer which_rel The page number to draw (from the - * $cfg['Servers'][$i]['table_coords'] table) - * @param boolean show_info Whether to display table position or not - * @param boolean change_color Was originally whether to use one color per - * relation or not, now enables/disables color - * everywhere, due to some problems printing with color - * @param boolean show_grid Whether to draw grids or not - * @param boolean all_tab_same_wide Whether all tables should have the same width or not - * @param boolean show_keys Wheter to show all field or only the keys - * @global object The current PDF document - * @global string The current db name - * @global array The relations settings - * @access private - * @see PMA_PDF - */ - function __construct($which_rel, $show_info = 0, $change_color = 0, $show_grid = 0, $all_tab_same_wide = 0, $orientation = 'L', $paper = 'A4', $show_keys = 0) - { - global $pdf, $db, $cfgRelation, $with_doc; - - $this->same_wide = $all_tab_same_wide; - // Initializes a new document - $pdf = new PMA_PDF('L', 'mm', $paper); - $pdf->SetTitle(sprintf(__('Schema of the %s database - Page %s'), $GLOBALS['db'], $which_rel)); - $pdf->setCMargin(0); - $pdf->Open(); - $pdf->SetAuthor('phpMyAdmin ' . PMA_VERSION); - $pdf->AliasNbPages(); - $pdf->AddFont('DejaVuSans', '', 'dejavusans.php'); - $pdf->AddFont('DejaVuSans', 'B', 'dejavusansb.php'); - $pdf->AddFont('DejaVuSerif', '', 'dejavuserif.php'); - $pdf->AddFont('DejaVuSerif', 'B', 'dejavuserifb.php'); - $this->ff = PMA_PDF_FONT; - $pdf->SetFont($this->ff, '', 14); - $pdf->SetAutoPageBreak('auto'); - // Gets tables on this page - $tab_sql = 'SELECT table_name FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['table_coords']) - . ' WHERE db_name = \'' . PMA_sqlAddslashes($db) . '\'' - . ' AND pdf_page_number = ' . $which_rel; - $tab_rs = PMA_query_as_controluser($tab_sql, null, PMA_DBI_QUERY_STORE); - if (!$tab_rs || !PMA_DBI_num_rows($tab_rs) > 0) { - $pdf->PMA_PDF_die(__('No tables')); - // die('No tables'); - } while ($curr_table = @PMA_DBI_fetch_assoc($tab_rs)) { - $alltables[] = PMA_sqlAddslashes($curr_table['table_name']); - // $intable = '\'' . implode('\', \'', $alltables) . '\''; - } - // make doc // - if ($with_doc) { - $pdf->SetAutoPageBreak('auto', 15); - $pdf->setCMargin(1); - PMA_RT_DOC($alltables); - $pdf->SetAutoPageBreak('auto'); - $pdf->setCMargin(0); - } - - $pdf->Addpage(); - - if ($with_doc) { - $pdf->SetLink($pdf->PMA_links['RT']['-'], -1); - $pdf->Bookmark(__('Relational schema')); - $pdf->SetAlias('{00}', $pdf->PageNo()) ; - $this->t_marg = 18; - $this->b_marg = 18; - } - - /* snip */ - - foreach ($alltables AS $table) { - if (!isset($this->tables[$table])) { - $this->tables[$table] = new PMA_RT_Table($table, $this->ff, $this->tablewidth, $show_keys, $show_info); - } - - if ($this->same_wide) { - $this->tables[$table]->width = $this->tablewidth; - } - $this->PMA_RT_setMinMax($this->tables[$table]); - } - // Defines the scale factor - $this->scale = ceil( - max( - ($this->x_max - $this->x_min) / ($pdf->getFh() - $this->r_marg - $this->l_marg), - ($this->y_max - $this->y_min) / ($pdf->getFw() - $this->t_marg - $this->b_marg)) - * 100) / 100; - - $pdf->PMA_PDF_setScale($this->scale, $this->x_min, $this->y_min, $this->l_marg, $this->t_marg); - // Builds and save the PDF document - $pdf->PMA_PDF_setLineWidthScale(0.1); - - if ($show_grid) { - $pdf->SetFontSize(10); - $this->PMA_RT_strokeGrid(); - } - $pdf->PMA_PDF_setFontSizeScale(14); - // $sql = 'SELECT * FROM ' . PMA_backquote($GLOBALS['cfgRelation']['db']) . '.' . PMA_backquote($cfgRelation['relation']) - // . ' WHERE master_db = \'' . PMA_sqlAddslashes($db) . '\' ' - // . ' AND foreign_db = \'' . PMA_sqlAddslashes($db) . '\' ' - // . ' AND master_table IN (' . $intable . ')' - // . ' AND foreign_table IN (' . $intable . ')'; - // $result = PMA_query_as_controluser($sql); - - // previous logic was checking master tables and foreign tables - // but I think that looping on every table of the pdf page as a master - // and finding its foreigns is OK (then we can support innodb) - $seen_a_relation = false; - foreach ($alltables AS $one_table) { - $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); - if ($exist_rel) { - $seen_a_relation = true; - foreach ($exist_rel AS $master_field => $rel) { - // put the foreign table on the schema only if selected - // by the user - // (do not use array_search() because we would have to - // to do a === FALSE and this is not PHP3 compatible) - if (in_array($rel['foreign_table'], $alltables)) { - $this->PMA_RT_addRelation($one_table, $master_field, $rel['foreign_table'], $rel['foreign_field'], $show_info); - } - } // end while - } // end if - } // end while - // also show tables without relations - // $norelations = TRUE; - // if ($result && PMA_DBI_num_rows($result) > 0) { - // $norelations = FALSE; - // while ($row = PMA_DBI_fetch_assoc($result)) { - // $this->PMA_RT_addRelation($row['master_table'], $row['master_field'], $row['foreign_table'], $row['foreign_field']); - // } - // } - // if ($norelations == FALSE) { - if ($seen_a_relation) { - $this->PMA_RT_drawRelations($change_color); - } - - $this->PMA_RT_drawTables($change_color); - - $this->PMA_RT_showRt(); - } // end of the "PMA_RT()" method -} // end of the "PMA_RT" class - -function PMA_RT_DOC($alltables) -{ - global $db, $pdf, $orientation, $paper; - // TOC - $pdf->addpage($GLOBALS['orientation']); - $pdf->Cell(0, 9, __('Table of contents'), 1, 0, 'C'); - $pdf->Ln(15); - $i = 1; - foreach ($alltables AS $table) { - $pdf->PMA_links['doc'][$table]['-'] = $pdf->AddLink(); - $pdf->SetX(10); - // $pdf->Ln(1); - $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$table]['-']); - $pdf->SetX(10); - $pdf->Cell(0, 6, $i . ' ' . $table, 0, 1, 'L', 0, $pdf->PMA_links['doc'][$table]['-']); - // $pdf->Ln(1); - $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';'); - while ($row = PMA_DBI_fetch_assoc($result)) { - $pdf->SetX(20); - $field_name = $row['Field']; - $pdf->PMA_links['doc'][$table][$field_name] = $pdf->AddLink(); - // $pdf->Cell(0, 6, $field_name,0,1,'L',0, $pdf->PMA_links['doc'][$table][$field_name]); - } - $lasttable = $table; - $i++; - } - $pdf->PMA_links['RT']['-'] = $pdf->AddLink(); - $pdf->SetX(10); - $pdf->Cell(0, 6, __('Page number:') . ' {' . sprintf("%02d", $i + 1) . '}', 0, 0, 'R', 0, $pdf->PMA_links['doc'][$lasttable]['-']); - $pdf->SetX(10); - $pdf->Cell(0, 6, $i + 1 . ' ' . __('Relational schema'), 0, 1, 'L', 0, $pdf->PMA_links['RT']['-']); - $z = 0; - foreach ($alltables AS $table) { - $z++; - $pdf->addpage($GLOBALS['orientation']); - $pdf->Bookmark($table); - $pdf->SetAlias('{' . sprintf("%02d", $z) . '}', $pdf->PageNo()) ; - $pdf->PMA_links['RT'][$table]['-'] = $pdf->AddLink(); - $pdf->SetLink($pdf->PMA_links['doc'][$table]['-'], -1); - $pdf->SetFont('', 'B', 18); - $pdf->Cell(0, 8, $z . ' ' . $table, 1, 1, 'C', 0, $pdf->PMA_links['RT'][$table]['-']); - $pdf->SetFont('', '', 8); - $pdf->ln(); - - $cfgRelation = PMA_getRelationsParam(); - $comments = PMA_getComments($db, $table); - if ($cfgRelation['mimework']) { - $mime_map = PMA_getMIME($db, $table, true); - } - - /** - * Gets table informations - */ - $showtable = PMA_Table::sGetStatusInfo($db, $table); - $num_rows = (isset($showtable['Rows']) ? $showtable['Rows'] : 0); - $show_comment = (isset($showtable['Comment']) ? $showtable['Comment'] : ''); - $create_time = (isset($showtable['Create_time']) ? PMA_localisedDate(strtotime($showtable['Create_time'])) : ''); - $update_time = (isset($showtable['Update_time']) ? PMA_localisedDate(strtotime($showtable['Update_time'])) : ''); - $check_time = (isset($showtable['Check_time']) ? PMA_localisedDate(strtotime($showtable['Check_time'])) : ''); - - /** - * Gets table keys and retains them - */ - $result = PMA_DBI_query('SHOW KEYS FROM ' . PMA_backquote($table) . ';'); - $primary = ''; - $indexes = array(); - $lastIndex = ''; - $indexes_info = array(); - $indexes_data = array(); - $pk_array = array(); // will be use to emphasis prim. keys in the table - // view - while ($row = PMA_DBI_fetch_assoc($result)) { - // Backups the list of primary keys - if ($row['Key_name'] == 'PRIMARY') { - $primary .= $row['Column_name'] . ', '; - $pk_array[$row['Column_name']] = 1; - } - // Retains keys informations - if ($row['Key_name'] != $lastIndex) { - $indexes[] = $row['Key_name']; - $lastIndex = $row['Key_name']; - } - $indexes_info[$row['Key_name']]['Sequences'][] = $row['Seq_in_index']; - $indexes_info[$row['Key_name']]['Non_unique'] = $row['Non_unique']; - if (isset($row['Cardinality'])) { - $indexes_info[$row['Key_name']]['Cardinality'] = $row['Cardinality']; - } - // I don't know what does following column mean.... - // $indexes_info[$row['Key_name']]['Packed'] = $row['Packed']; - $indexes_info[$row['Key_name']]['Comment'] = $row['Comment']; - - $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Column_name'] = $row['Column_name']; - if (isset($row['Sub_part'])) { - $indexes_data[$row['Key_name']][$row['Seq_in_index']]['Sub_part'] = $row['Sub_part']; - } - } // end while - if ($result) { - PMA_DBI_free_result($result); - } - - /** - * Gets fields properties - */ - $result = PMA_DBI_query('SHOW FIELDS FROM ' . PMA_backquote($table) . ';', null, PMA_DBI_QUERY_STORE); - $fields_cnt = PMA_DBI_num_rows($result); - // Check if we can use Relations (Mike Beck) - if (!empty($cfgRelation['relation'])) { - // Find which tables are related with the current one and write it in - // an array - $res_rel = PMA_getForeigners($db, $table); - - if (count($res_rel) > 0) { - $have_rel = true; - } else { - $have_rel = false; - } - } else { - $have_rel = false; - } // end if - /** - * Displays the comments of the table if MySQL >= 3.23 - */ - - $break = false; - if (!empty($show_comment)) { - $pdf->Cell(0, 3, __('Table comments') . ' : ' . $show_comment, 0, 1); - $break = true; - } - - if (!empty($create_time)) { - $pdf->Cell(0, 3, __('Creation') . ': ' . $create_time, 0, 1); - $break = true; - } - - if (!empty($update_time)) { - $pdf->Cell(0, 3, __('Last update') . ': ' . $update_time, 0, 1); - $break = true; - } - - if (!empty($check_time)) { - $pdf->Cell(0, 3, __('Last check') . ': ' . $check_time, 0, 1); - $break = true; - } - - if ($break == true) { - $pdf->Cell(0, 3, '', 0, 1); - $pdf->Ln(); - } - - $pdf->SetFont('', 'B'); - if (isset($orientation) && $orientation == 'L') { - $pdf->Cell(25, 8, ucfirst(__('Column')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); - $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Default')), 1, 0, 'C'); - $pdf->Cell(25, 8, ucfirst(__('Extra')), 1, 0, 'C'); - $pdf->Cell(45, 8, ucfirst(__('Links to')), 1, 0, 'C'); - - if ($paper == 'A4') { - $comments_width = 67; - } else { - // this is really intended for 'letter' - /** - * @todo find optimal width for all formats - */ - $comments_width = 50; - } - $pdf->Cell($comments_width, 8, ucfirst(__('Comments')), 1, 0, 'C'); - $pdf->Cell(45, 8, 'MIME', 1, 1, 'C'); - $pdf->SetWidths(array(25, 20, 20, 10, 20, 25, 45, $comments_width, 45)); - } else { - $pdf->Cell(20, 8, ucfirst(__('Column')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Type')), 1, 0, 'C'); - $pdf->Cell(20, 8, ucfirst(__('Attributes')), 1, 0, 'C'); - $pdf->Cell(10, 8, ucfirst(__('Null')), 1, 0, 'C'); - $pdf->Cell(15, 8, ucfirst(__('Default')), 1, 0, 'C'); - $pdf->Cell(15, 8, ucfirst(__('Extra')), 1, 0, 'C'); - $pdf->Cell(30, 8, ucfirst(__('Links to')), 1, 0, 'C'); - $pdf->Cell(30, 8, ucfirst(__('Comments')), 1, 0, 'C'); - $pdf->Cell(30, 8, 'MIME', 1, 1, 'C'); - $pdf->SetWidths(array(20, 20, 20, 10, 15, 15, 30, 30, 30)); - } - $pdf->SetFont('', ''); - - while ($row = PMA_DBI_fetch_assoc($result)) { - $type = $row['Type']; - // reformat mysql query output - // set or enum types: slashes single quotes inside options - if (preg_match('@^(set|enum)\((.+)\)$@i', $type, $tmp)) { - $tmp[2] = substr(preg_replace("@([^,])''@", "\\1\\'", ',' . $tmp[2]), 1); - $type = $tmp[1] . '(' . str_replace(',', ', ', $tmp[2]) . ')'; - $type_nowrap = ''; - - $binary = 0; - $unsigned = 0; - $zerofill = 0; - } else { - $type_nowrap = ' nowrap="nowrap"'; - $type = preg_replace('@BINARY@i', '', $type); - $type = preg_replace('@ZEROFILL@i', '', $type); - $type = preg_replace('@UNSIGNED@i', '', $type); - if (empty($type)) { - $type = '&nbsp;'; - } - - $binary = stristr($row['Type'], 'BINARY'); - $unsigned = stristr($row['Type'], 'UNSIGNED'); - $zerofill = stristr($row['Type'], 'ZEROFILL'); - } - $attribute = ' '; - if ($binary) { - $attribute = 'BINARY'; - } - if ($unsigned) { - $attribute = 'UNSIGNED'; - } - if ($zerofill) { - $attribute = 'UNSIGNED ZEROFILL'; - } - if (!isset($row['Default'])) { - if ($row['Null'] != '' && $row['Null'] != 'NO') { - $row['Default'] = 'NULL'; - } - } - $field_name = $row['Field']; - // $pdf->Ln(); - $pdf->PMA_links['RT'][$table][$field_name] = $pdf->AddLink(); - $pdf->Bookmark($field_name, 1, -1); - $pdf->SetLink($pdf->PMA_links['doc'][$table][$field_name], -1); - $pdf_row = array($field_name, - $type, - $attribute, - ($row['Null'] == '' || $row['Null'] == 'NO') ? __('No') : __('Yes'), - ((isset($row['Default'])) ? $row['Default'] : ''), - $row['Extra'], - ((isset($res_rel[$field_name])) ? $res_rel[$field_name]['foreign_table'] . ' -> ' . $res_rel[$field_name]['foreign_field'] : ''), - ((isset($comments[$field_name])) ? $comments[$field_name] : ''), - ((isset($mime_map) && isset($mime_map[$field_name])) ? str_replace('_', '/', $mime_map[$field_name]['mimetype']) : '') - ); - $links[0] = $pdf->PMA_links['RT'][$table][$field_name]; - if (isset($res_rel[$field_name]['foreign_table']) AND - isset($res_rel[$field_name]['foreign_field']) AND - isset($pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]) - ) - { - $links[6] = $pdf->PMA_links['doc'][$res_rel[$field_name]['foreign_table']][$res_rel[$field_name]['foreign_field']]; - } else { - unset($links[6]); - } - $pdf->Row($pdf_row, $links); - - /*$pdf->Cell(20, 8, $field_name, 1, 0, 'L', 0, $pdf->PMA_links['RT'][$table][$field_name]); - //echo ' ' . $field_name . '&nbsp;' . "\n"; - } - $pdf->Cell(20, 8, $type, 1, 0, 'L'); - $pdf->Cell(20, 8, $attribute, 1, 0, 'L'); - $pdf->Cell(15, 8, , 1, 0, 'L'); - $pdf->Cell(15, 8, ((isset($row['Default'])) ? $row['Default'] : ''),1,0,'L'); - $pdf->Cell(15, 8, $row['Extra'], 1, 0, 'L'); - if ($have_rel) { - if (isset($res_rel[$field_name])) { - $pdf->Cell(30, 8, $res_rel[$field_name]['foreign_table'] . ' -> ' . $res_rel[$field_name]['foreign_field'],1,0,'L'); - } - } - if ($cfgRelation['commwork']) { - if (isset($comments[$field_name])) { - $pdf->Cell(0, 8, $comments[$field_name], 1, 0, 'L'); - } - } */ - } // end while - $pdf->SetFont('', '', 14); - PMA_DBI_free_result($result); - } //end each -} // end function PMA_RT_DOC - -/** - * Main logic - */ -if (!isset($pdf_page_number)) { - $pdf_page_number = 1; -} - -$show_grid = (isset($show_grid) && $show_grid == 'on') ? 1 : 0; -$show_color = (isset($show_color) && $show_color == 'on') ? 1 : 0; -$show_table_dimension = (isset($show_table_dimension) && $show_table_dimension == 'on') ? 1 : 0; -$all_tab_same_wide = (isset($all_tab_same_wide) && $all_tab_same_wide == 'on') ? 1 : 0; -$with_doc = (isset($with_doc) && $with_doc == 'on') ? 1 : 0; -$orientation = (isset($orientation) && $orientation == 'P') ? 'P' : 'L'; -$paper = isset($paper) ? $paper : 'A4'; -$show_keys = (isset($show_keys) && $show_keys == 'on') ? 1 : 0; -PMA_DBI_select_db($db); - -$rt = new PMA_RT($pdf_page_number, $show_table_dimension, $show_color, $show_grid, $all_tab_same_wide, $orientation, $paper, $show_keys); - -?> From 1cdf881f42a49988300b898caf7e16f2b6c5fdc3 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Tue, 17 Aug 2010 20:41:15 +0500 Subject: [PATCH 35/48] handling headers issue in schema export --- handle_relation_schema.php | 48 +++++++++++++++++++ .../schema/Eps_Relation_Schema.class.php | 6 +-- .../schema/Svg_Relation_Schema.class.php | 4 +- libraries/schema/User_Schema.class.php | 6 +-- .../schema/Visio_Relation_Schema.class.php | 8 ++-- libraries/tcpdf/tcpdf.php | 20 ++++---- 6 files changed, 69 insertions(+), 23 deletions(-) create mode 100644 handle_relation_schema.php diff --git a/handle_relation_schema.php b/handle_relation_schema.php new file mode 100644 index 000000000..043bef28c --- /dev/null +++ b/handle_relation_schema.php @@ -0,0 +1,48 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * + * @version $Id$ + * @package phpMyAdmin + */ + +/** + * Gets some core libraries + */ +require_once './libraries/common.inc.php'; +require './libraries/StorageEngine.class.php'; + +/** + * Includ settings for relation stuff + * get all variables needed for exporting relational schema + * in $cfgRelation + */ +require_once './libraries/relation.lib.php'; +$cfgRelation = PMA_getRelationsParam(); + +/** + * Settings for relation stuff + */ +require_once './libraries/transformations.lib.php'; +require_once './libraries/Index.class.php'; + +/** + * This is to avoid "Command out of sync" errors. Before switching this to + * a value of 0 (for MYSQLI_USE_RESULT), please check the logic + * to free results wherever needed. + */ +$query_default_option = PMA_DBI_QUERY_STORE; + + /** + * get all the export options and verify + * call and include the appropriate Schema Class depending on $export_type + * + /** + * default is PDF + */ + global $db,$export_type; + $export_type = isset($export_type) ? $export_type : 'pdf'; + PMA_DBI_select_db($db); + + include("./libraries/schema/".ucfirst($export_type)."_Relation_Schema.class.php"); + $obj_schema = eval("new PMA_".ucfirst($export_type)."_Relation_Schema();"); diff --git a/libraries/schema/Eps_Relation_Schema.class.php b/libraries/schema/Eps_Relation_Schema.class.php index 674b89e25..d22e43ce1 100644 --- a/libraries/schema/Eps_Relation_Schema.class.php +++ b/libraries/schema/Eps_Relation_Schema.class.php @@ -335,9 +335,9 @@ class PMA_EPS */ function showOutput($fileName) { - if(ob_get_clean()){ - ob_end_clean(); - } + // if(ob_get_clean()){ + //ob_end_clean(); + //} //header('Content-type: text/xml'); header('Content-Disposition: attachment; filename="'.$fileName.'.eps"'); $output = $this->stringCommands; diff --git a/libraries/schema/Svg_Relation_Schema.class.php b/libraries/schema/Svg_Relation_Schema.class.php index 397750f62..a6b809531 100644 --- a/libraries/schema/Svg_Relation_Schema.class.php +++ b/libraries/schema/Svg_Relation_Schema.class.php @@ -172,7 +172,7 @@ class PMA_SVG extends XMLWriter */ function showOutput($fileName) { - ob_get_clean(); + //ob_get_clean(); header('Content-type: image/svg+xml'); header('Content-Disposition: attachment; filename="'.$fileName.'.svg"'); $output = $this->flush(); @@ -349,7 +349,7 @@ class Table_Stats * @global array The relations settings * @global string The current db name * @access private - * @see PMA_PDF, Table_Stats::Table_Stats_setWidth, + * @see PMA_SVG, Table_Stats::Table_Stats_setWidth, Table_Stats::Table_Stats_setHeight */ function __construct($tableName, $font, $fontSize, $pageNumber, &$same_wide_width, $showKeys = false, $showInfo = false) diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php index 1d89ff628..6e41e782d 100644 --- a/libraries/schema/User_Schema.class.php +++ b/libraries/schema/User_Schema.class.php @@ -65,7 +65,7 @@ class PMA_User_Schema case 'edcoord': $this->choosenPage = $_POST['chpage']; $this->c_table_rows = $_POST['c_table_rows']; - $this->_editCoordinates($db, $cfgRelation); + $this->_editCoordinates($db, $cfgRelation,$query_default_option); break; case 'deleteCrap': $this->_deleteTableRows($delrow,$cfgRelation,$db,$this->choosenPage); @@ -341,7 +341,7 @@ class PMA_User_Schema { global $cfg,$pmaThemeImage,$db,$test_rs,$chpage; ?> - <form method="post" action="export_relation_schema.php"> + <form method="post" action="handle_relation_schema.php"> <fieldset> <legend> <?php @@ -775,7 +775,7 @@ class PMA_User_Schema * @return void * @access private */ - private function _editCoordinates($db, $cfgRelation) + private function _editCoordinates($db, $cfgRelation,$query_default_option) { for ($i = 0; $i < $this->c_table_rows; $i++) { $arrvalue = 'c_table_' . $i; diff --git a/libraries/schema/Visio_Relation_Schema.class.php b/libraries/schema/Visio_Relation_Schema.class.php index 7ced40047..e41cdcc4c 100644 --- a/libraries/schema/Visio_Relation_Schema.class.php +++ b/libraries/schema/Visio_Relation_Schema.class.php @@ -157,9 +157,9 @@ class PMA_VISIO extends XMLWriter */ function showOutput($fileName) { - if(ob_get_clean()){ - ob_end_clean(); - } + //if(ob_get_clean()){ + //ob_end_clean(); + //} //header('Content-type: text/xml'); header('Content-Disposition: attachment; filename="'.$fileName.'.vdx"'); $output = $this->flush(); @@ -535,7 +535,6 @@ class PMA_Visio_Relation_Schema extends PMA_Export_Relation_Schema if ($seen_a_relation) { $this->_drawRelations($this->showColor); } - $visio->endVisioDoc(); $visio->showOutput($db.'-'.$this->pageNumber); exit(); @@ -543,7 +542,6 @@ class PMA_Visio_Relation_Schema extends PMA_Export_Relation_Schema print_r(get_object_vars($visio)); print_r(get_object_vars($this)); print '</pre>'; - } /** diff --git a/libraries/tcpdf/tcpdf.php b/libraries/tcpdf/tcpdf.php index 98ab40445..b3881b31e 100644 --- a/libraries/tcpdf/tcpdf.php +++ b/libraries/tcpdf/tcpdf.php @@ -2977,11 +2977,11 @@ if(!class_exists('TCPDF', false)) { switch($dest) { case 'I': { //Send to standard output - //if(ob_get_contents()) { - // $this->Error('Some data has already been output, can\'t send PDF file'); - //} - ob_end_clean(); - ob_start(); + if(ob_get_contents()) { + $this->Error('Some data has already been output, can\'t send PDF file'); + } + //ob_end_clean(); + //ob_start(); if(php_sapi_name()!='cli') { //We send to a browser header('Content-Type: application/pdf'); @@ -2996,11 +2996,11 @@ if(!class_exists('TCPDF', false)) { } case 'D': { //Download file - // if(ob_get_contents()) { - // $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); - //} - ob_end_clean(); - ob_start(); + if(ob_get_contents()) { + $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); + } + //ob_end_clean(); + //ob_start(); if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { header('Content-Type: application/force-download'); } else { From 224abbaf5efd89c7fecf4b2c01ef45297a4a444b Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Tue, 17 Aug 2010 21:02:54 +0500 Subject: [PATCH 36/48] TCPDF: reverted back from 14b26e3 --- libraries/tcpdf/tcpdf.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/tcpdf/tcpdf.php b/libraries/tcpdf/tcpdf.php index b3881b31e..ad433cec8 100644 --- a/libraries/tcpdf/tcpdf.php +++ b/libraries/tcpdf/tcpdf.php @@ -2996,11 +2996,18 @@ if(!class_exists('TCPDF', false)) { } case 'D': { //Download file +<<<<<<< HEAD if(ob_get_contents()) { $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); } //ob_end_clean(); //ob_start(); +======= + // if(ob_get_contents()) { + // $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); + //} + +>>>>>>> 14b26e3... TCPDF Class: Clean output buffer before downloading file if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { header('Content-Type: application/force-download'); } else { From b95a14c0de93965cd5eb29edcedae60d7cf97e8b Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Tue, 17 Aug 2010 22:26:15 +0500 Subject: [PATCH 37/48] TCPDF: merge conflict fixed --- libraries/tcpdf/tcpdf.php | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/libraries/tcpdf/tcpdf.php b/libraries/tcpdf/tcpdf.php index ad433cec8..3d134b61d 100644 --- a/libraries/tcpdf/tcpdf.php +++ b/libraries/tcpdf/tcpdf.php @@ -2980,8 +2980,7 @@ if(!class_exists('TCPDF', false)) { if(ob_get_contents()) { $this->Error('Some data has already been output, can\'t send PDF file'); } - //ob_end_clean(); - //ob_start(); + if(php_sapi_name()!='cli') { //We send to a browser header('Content-Type: application/pdf'); @@ -2996,18 +2995,10 @@ if(!class_exists('TCPDF', false)) { } case 'D': { //Download file -<<<<<<< HEAD if(ob_get_contents()) { $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); } - //ob_end_clean(); - //ob_start(); -======= - // if(ob_get_contents()) { - // $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); - //} - ->>>>>>> 14b26e3... TCPDF Class: Clean output buffer before downloading file + if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { header('Content-Type: application/force-download'); } else { From 03b1bbf9b3910fe3ffe9539d639bf300ff4c51da Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 18 Aug 2010 12:22:56 +0500 Subject: [PATCH 38/48] User Interface change in Schema Generation Options Box --- libraries/schema/User_Schema.class.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php index 6e41e782d..63020a206 100644 --- a/libraries/schema/User_Schema.class.php +++ b/libraries/schema/User_Schema.class.php @@ -393,14 +393,12 @@ class PMA_User_Schema <label for="with_doc"><?php echo __('Data Dictionary'); ?></label><br /> <input type="checkbox" name="show_keys" id="show_keys" /> <label for="show_keys"><?php echo __('Only show keys'); ?></label><br /> - <label for="orientation_opt"><?php echo __('Data Dictionary Format'); ?></label> <select name="orientation" id="orientation_opt"> <option value="L"><?php echo __('Landscape');?></option> <option value="P"><?php echo __('Portrait');?></option> </select> + <label for="orientation_opt"><?php echo __('Orientation'); ?></label> <br /> - - <label for="paper_opt"><?php echo __('Paper size'); ?></label> <select name="paper" id="paper_opt"> <?php foreach ($cfg['PDFPageSizes'] as $key => $val) { @@ -412,6 +410,7 @@ class PMA_User_Schema } ?> </select> + <label for="paper_opt"><?php echo __('Paper size'); ?></label> </fieldset> <fieldset class="tblFooters"> <input type="submit" value="<?php echo __('Go'); ?>" /> From ae877a1de6a6cfe36beeb139456ea8c45277822a Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 18 Aug 2010 12:27:02 +0500 Subject: [PATCH 39/48] UserInterface show/hide Schema Generation Options Available --- js/functions.js | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/js/functions.js b/js/functions.js index 2d5830393..33c8e960a 100644 --- a/js/functions.js +++ b/js/functions.js @@ -1695,16 +1695,42 @@ $(document).ready(function(){ }); $("#export_type").change(function(){ - if($("#export_type").val()!='pdf'){ + if($("#export_type").val()=='svg'){ $("#show_grid_opt").attr("disabled","disabled"); $("#orientation_opt").attr("disabled","disabled"); $("#with_doc").attr("disabled","disabled"); - $(this).css("background-color","yellow"); - } - if($("#export_type").val()=='pdf'){ + $("#show_table_dim_opt").removeAttr("disabled"); + $("#all_table_same_wide").removeAttr("disabled"); + $("#paper_opt").removeAttr("disabled","disabled"); + $("#show_color_opt").removeAttr("disabled","disabled"); + //$(this).css("background-color","yellow"); + }else if($("#export_type").val()=='dia'){ + $("#show_grid_opt").attr("disabled","disabled"); + $("#with_doc").attr("disabled","disabled"); + $("#show_table_dim_opt").attr("disabled","disabled"); + $("#all_table_same_wide").attr("disabled","disabled"); + $("#paper_opt").removeAttr("disabled","disabled"); + $("#show_color_opt").removeAttr("disabled","disabled"); + $("#orientation_opt").removeAttr("disabled","disabled"); + }else if($("#export_type").val()=='eps'){ + $("#show_grid_opt").attr("disabled","disabled"); + $("#orientation_opt").removeAttr("disabled"); + $("#with_doc").attr("disabled","disabled"); + $("#show_table_dim_opt").attr("disabled","disabled"); + $("#all_table_same_wide").attr("disabled","disabled"); + $("#paper_opt").attr("disabled","disabled"); + $("#show_color_opt").attr("disabled","disabled"); + + }else if($("#export_type").val()=='pdf'){ $("#show_grid_opt").removeAttr("disabled"); $("#orientation_opt").removeAttr("disabled"); $("#with_doc").removeAttr("disabled","disabled"); + $("#show_table_dim_opt").removeAttr("disabled","disabled"); + $("#all_table_same_wide").removeAttr("disabled","disabled"); + $("#paper_opt").removeAttr("disabled","disabled"); + $("#show_color_opt").removeAttr("disabled","disabled"); + }else{ + // nothing } }); From 36ba514c5109c679fcee0e4139b2411a4c5803ce Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 18 Aug 2010 15:55:06 +0500 Subject: [PATCH 40/48] Eval security issue & Indentation fixed --- handle_relation_schema.php | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/handle_relation_schema.php b/handle_relation_schema.php index 043bef28c..9f74feaaa 100644 --- a/handle_relation_schema.php +++ b/handle_relation_schema.php @@ -13,16 +13,13 @@ require_once './libraries/common.inc.php'; require './libraries/StorageEngine.class.php'; /** - * Includ settings for relation stuff + * Include settings for relation stuff * get all variables needed for exporting relational schema * in $cfgRelation */ require_once './libraries/relation.lib.php'; $cfgRelation = PMA_getRelationsParam(); -/** - * Settings for relation stuff - */ require_once './libraries/transformations.lib.php'; require_once './libraries/Index.class.php'; @@ -33,16 +30,20 @@ require_once './libraries/Index.class.php'; */ $query_default_option = PMA_DBI_QUERY_STORE; - /** - * get all the export options and verify - * call and include the appropriate Schema Class depending on $export_type - * - /** - * default is PDF - */ - global $db,$export_type; - $export_type = isset($export_type) ? $export_type : 'pdf'; - PMA_DBI_select_db($db); +include_once("./libraries/schema/Export_Relation_Schema.class.php"); - include("./libraries/schema/".ucfirst($export_type)."_Relation_Schema.class.php"); - $obj_schema = eval("new PMA_".ucfirst($export_type)."_Relation_Schema();"); +/** + * get all the export options and verify + * call and include the appropriate Schema Class depending on $export_type + * default is PDF + */ +global $db,$export_type; +$export_type = isset($export_type) ? $export_type : 'pdf'; +PMA_DBI_select_db($db); + +$path = PMA_securePath(ucfirst($export_type)); +if (!file_exists('./libraries/schema/' . $path . '_Relation_Schema.class.php')) { + PMA_Export_Relation_Schema::dieSchema($_POST['chpage'],$export_type,__('File doesn\'t exist')); +} +include("./libraries/schema/".$path."_Relation_Schema.class.php"); +$obj_schema = eval("new PMA_".$path."_Relation_Schema();"); \ No newline at end of file From d6c630c64e279e85cc9b2062e3b499c1567ded7e Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 18 Aug 2010 15:58:24 +0500 Subject: [PATCH 41/48] Proper Error handling of Schema --- .../schema/Export_Relation_Schema.class.php | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/libraries/schema/Export_Relation_Schema.class.php b/libraries/schema/Export_Relation_Schema.class.php index d88d33a50..b9115015a 100644 --- a/libraries/schema/Export_Relation_Schema.class.php +++ b/libraries/schema/Export_Relation_Schema.class.php @@ -183,9 +183,29 @@ class PMA_Export_Relation_Schema /** * Displays an error message + * + * @param integer pageNumber ID of the page choosen + * @param string type Schema Type + * @param string error_message the error mesage + * @global array the PMA configuration array + * @global integer the current server id + * @global string the current language + * @global string the charset to convert to + * @global string the current database name + * @global string the current charset + * @global string the current text direction + * @global string a localized string + * @global string an other localized string + * @access public + * @return void */ - function dieSchema($type = '',$error_message = '') + function dieSchema($pageNumber, $type = '', $error_message = '') { + global $cfg; + global $server, $lang, $convcharset, $db; + global $charset, $text_dir; + + require_once './libraries/header.inc.php'; echo "<p><strong>" . __("SCHEMA ERROR: ") . $type ."</strong></p>" . "\n"; if (!empty($error_message)) { $error_message = htmlspecialchars($error_message); @@ -193,6 +213,11 @@ class PMA_Export_Relation_Schema echo '<p>' . "\n"; echo ' ' . $error_message . "\n"; echo '</p>' . "\n"; + echo '<a href="export_relation_schema.php?' . PMA_generate_common_url($db).'&do=selectpage&chpage='.$pageNumber.'&action_choose=0' + . '">' . __('Back') . '</a>'; + echo "\n"; + require_once './libraries/footer.inc.php'; + exit(); } } ?> \ No newline at end of file From 4a4ddeadcf85f11edf580894d754ade80cb975be Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 18 Aug 2010 18:05:01 +0500 Subject: [PATCH 42/48] Fixed mime types and improve comments --- .../schema/Dia_Relation_Schema.class.php | 14 +-- .../schema/Eps_Relation_Schema.class.php | 16 ++-- .../schema/Pdf_Relation_Schema.class.php | 94 ++++++++++--------- .../schema/Svg_Relation_Schema.class.php | 51 ++++++---- libraries/schema/User_Schema.class.php | 5 +- .../schema/Visio_Relation_Schema.class.php | 6 +- 6 files changed, 106 insertions(+), 80 deletions(-) diff --git a/libraries/schema/Dia_Relation_Schema.class.php b/libraries/schema/Dia_Relation_Schema.class.php index af9ce2066..97ac28a19 100644 --- a/libraries/schema/Dia_Relation_Schema.class.php +++ b/libraries/schema/Dia_Relation_Schema.class.php @@ -175,7 +175,7 @@ class PMA_DIA extends XMLWriter if(ob_get_clean()){ ob_end_clean(); } - //header('Content-type: text/xml'); + header('Content-type: application/x-dia-diagram'); header('Content-Disposition: attachment; filename="'.$fileName.'.dia"'); $output = $this->flush(); print $output; @@ -227,7 +227,7 @@ class Table_Stats $sql = 'DESCRIBE ' . PMA_backquote($tableName); $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { - //$dia->Error(sprintf(__('The %s table doesn\'t exist!'), $tableName)); + $dia->dieSchema($pageNumber,"DIA",sprintf(__('The %s table doesn\'t exist!'), $tableName)); } /* * load fields @@ -253,7 +253,7 @@ class Table_Stats . ' AND pdf_page_number = ' . $pageNumber; $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { - // $dia->Error(sprintf(__('Please configure the coordinates for table %s'), $tableName)); + $dia->dieSchema($pageNumber,"DIA",sprintf(__('Please configure the coordinates for table %s'), $tableName)); } list($this->x, $this->y) = PMA_DBI_fetch_row($result); $this->x = (double) $this->x; @@ -713,10 +713,10 @@ class PMA_Dia_Relation_Schema extends PMA_Export_Relation_Schema $dia->endDiaDoc(); $dia->showOutput($db.'-'.$this->pageNumber); exit(); - print '<pre>'; - print_r(get_object_vars($dia)); - print_r(get_object_vars($this)); - print '</pre>'; + // print '<pre>'; + //print_r(get_object_vars($dia)); + //print_r(get_object_vars($this)); + //print '</pre>'; } /** diff --git a/libraries/schema/Eps_Relation_Schema.class.php b/libraries/schema/Eps_Relation_Schema.class.php index d22e43ce1..07c3417d2 100644 --- a/libraries/schema/Eps_Relation_Schema.class.php +++ b/libraries/schema/Eps_Relation_Schema.class.php @@ -338,7 +338,7 @@ class PMA_EPS // if(ob_get_clean()){ //ob_end_clean(); //} - //header('Content-type: text/xml'); + header('Content-type: image/x-eps'); header('Content-Disposition: attachment; filename="'.$fileName.'.eps"'); $output = $this->stringCommands; print $output; @@ -400,7 +400,7 @@ class Table_Stats $sql = 'DESCRIBE ' . PMA_backquote($tableName); $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { - $eps->dieSchema(sprintf(__('The %s table doesn\'t exist!'), $tableName)); + $eps->dieSchema($pageNumber,"EPS",sprintf(__('The %s table doesn\'t exist!'), $tableName)); } /* @@ -441,7 +441,7 @@ class Table_Stats $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { - //$eps->dieSchema(sprintf(__('Please configure the coordinates for table %s'), $tableName)); + $eps->dieSchema($pageNumber,"EPS",sprintf(__('Please configure the coordinates for table %s'), $tableName)); } list($this->x, $this->y) = PMA_DBI_fetch_row($result); $this->x = (double) $this->x; @@ -797,11 +797,11 @@ class PMA_Eps_Relation_Schema extends PMA_Export_Relation_Schema $eps->endEpsDoc(); $eps->showOutput($db.'-'.$this->pageNumber); exit(); - print '<pre>'; - print_r(get_object_vars($eps)); - print_r($alltables); - print_r(get_object_vars($this)); - print '</pre>'; + //print '<pre>'; + //print_r(get_object_vars($eps)); + //print_r($alltables); + //print_r(get_object_vars($this)); + //print '</pre>'; } diff --git a/libraries/schema/Pdf_Relation_Schema.class.php b/libraries/schema/Pdf_Relation_Schema.class.php index 28424bffa..829798137 100644 --- a/libraries/schema/Pdf_Relation_Schema.class.php +++ b/libraries/schema/Pdf_Relation_Schema.class.php @@ -75,6 +75,17 @@ class PMA_PDF extends TCPDF parent::_putpages(); } + // added because tcpdf for PHP 5 has a protected $buffer + public function getBuffer() + { + return $this->buffer; + } + + public function getState() + { + return $this->state; + } + /** * Sets the scaling factor, defines minimum coordinates and margins * @@ -196,44 +207,6 @@ class PMA_PDF extends TCPDF * Displays an error message * * @param string error_message the error mesage - * @global array the PMA configuration array - * @global integer the current server id - * @global string the current language - * @global string the charset to convert to - * @global string the current database name - * @global string the current charset - * @global string the current text direction - * @global string a localized string - * @global string an other localized string - * @access public - */ -/* function PMA_PDF_die($error_message = '') - { - global $cfg; - global $server, $lang, $convcharset, $db; - global $charset, $text_dir; - - require_once './libraries/header.inc.php'; - - echo '<p><strong>PDF - ' . __('Error') . '</strong></p>' . "\n"; - if (!empty($error_message)) { - $error_message = htmlspecialchars($error_message); - } - echo '<p>' . "\n"; - echo ' ' . $error_message . "\n"; - echo '</p>' . "\n"; - - echo '<a href="db_structure.php?' . PMA_generate_common_url($db) - . '">' . __('Back') . '</a>'; - echo "\n"; - - require_once './libraries/footer.inc.php'; - }*/ // end of the "PMA_PDF_die()" function - /** - * Aliases the "Error()" function from the TCPDF class to the - * "PMA_PDF_die()" one - * - * @param string error_message the error mesage * @access public * @see PMA_Export_Relation_Schema::dieSchema */ @@ -480,8 +453,16 @@ class PMA_PDF extends TCPDF } /** - * Draws tables schema + * Table preferences/statistics + * + * This class preserves the table co-ordinates,fields + * and helps in drawing/generating the Tables in PDF document. * + * @name Table_Stats + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @see PMA_PDF */ class Table_Stats { @@ -684,10 +665,18 @@ class Table_Stats } /** - * Draws relation links + * Relation preferences/statistics + * + * This class fetches the table master and foreign fields positions + * and helps in generating the Table references and then connects + * master table's master field to foreign table's foreign key + * in PDF document. * - * @access public - * @see PMA_PDF + * @name Relation_Stats + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @see PMA_PDF::SetDrawColor,PMA_PDF::PMA_PDF_setLineWidthScale,PMA_PDF::PMA_PDF_lineScale */ class Relation_Stats { @@ -820,6 +809,20 @@ class Relation_Stats } } +/** + * Pdf Relation Schema Class + * + * Purpose of this class is to generate the PDF Document. PDF is widely + * used format for documenting text,fonts,images and 3d vector graphics. + * + * This class inherits Export_Relation_Schema class has common functionality added + * to this class + * + * @name Pdf_Relation_Schema + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + */ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema { /** @@ -1085,7 +1088,12 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema if (empty($filename)) { $filename = $pageNumber . '.pdf'; } - $pdf->Output($db . '_' . $filename, 'D'); // destination: download + // instead of $pdf->Output(): + $pdfData = $pdf->getPDFData(); + header('Content-Type: application/pdf'); + header('Content-Length: '.strlen($pdfData).''); + header('Content-disposition: attachment; filename="'.$filename.'"'); + echo $pdfData; } public function dataDictionaryDoc($alltables) diff --git a/libraries/schema/Svg_Relation_Schema.class.php b/libraries/schema/Svg_Relation_Schema.class.php index a6b809531..8714abcaf 100644 --- a/libraries/schema/Svg_Relation_Schema.class.php +++ b/libraries/schema/Svg_Relation_Schema.class.php @@ -316,7 +316,16 @@ class PMA_SVG extends XMLWriter } /** - * Draws tables schema + * Table preferences/statistics + * + * This class preserves the table co-ordinates,fields + * and helps in drawing/generating the Tables in SVG XML document. + * + * @name Table_Stats + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @see PMA_SVG */ class Table_Stats { @@ -360,7 +369,7 @@ class Table_Stats $sql = 'DESCRIBE ' . PMA_backquote($tableName); $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { - $svg->dieSchema(sprintf(__('The %s table doesn\'t exist!'), $tableName)); + $svg->dieSchema($pageNumber,"SVG",sprintf(__('The %s table doesn\'t exist!'), $tableName)); } /* @@ -402,7 +411,7 @@ class Table_Stats $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { - //$svg->dieSchema(sprintf(__('Please configure the coordinates for table %s'), $tableName)); + $svg->dieSchema($pageNumber,"SVG",sprintf(__('Please configure the coordinates for table %s'), $tableName)); } list($this->x, $this->y) = PMA_DBI_fetch_row($result); $this->x = (double) $this->x; @@ -516,10 +525,18 @@ class Table_Stats /** - * Draws relation links + * Relation preferences/statistics + * + * This class fetches the table master and foreign fields positions + * and helps in generating the Table references and then connects + * master table's master field to foreign table's foreign key + * in SVG XML document. * - * @access public - * @see PMA_SVG,PMA_SVG::printElementLine + * @name Relation_Stats + * @author Muhammad Adnan <hiddenpearls@gmail.com> + * @copyright + * @license + * @see PMA_SVG::printElementLine */ class Relation_Stats { @@ -671,16 +688,16 @@ class Relation_Stats /** * Svg Relation Schema Class * - * Purpose of this class is to generate the SVG XML Document - * which is used for representing the database diagrams as image in Dia IDE - * This class uses Database Table and Reference Objects of Dia and with - * the combination of these objects actually helps in preparing Dia XML. + * Purpose of this class is to generate the SVG XML Document because + * SVG defines the graphics in XML format which is used for representing + * the database diagrams as vector image. This class actually helps + * in preparing SVG XML format. * - * Dia XML is generated by using XMLWriter php extension and this class + * SVG XML is generated by using XMLWriter php extension and this class * inherits Export_Relation_Schema class has common functionality added * to this class * - * @name Dia_Relation_Schema + * @name Svg_Relation_Schema * @author Muhammad Adnan <hiddenpearls@gmail.com> * @copyright * @license @@ -726,7 +743,7 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema $svg->SetAuthor('phpMyAdmin ' . PMA_VERSION); $svg->setFont('Arial'); $svg->setFontSize('16px'); - $svg->startSvgDoc('1000px','600px'); + $svg->startSvgDoc('1000px','1000px'); $alltables = $this->getAllTables($db,$this->pageNumber); foreach ($alltables AS $table) { @@ -764,11 +781,11 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema $svg->endSvgDoc(); $svg->showOutput($db.'-'.$this->pageNumber); exit(); - print '<pre>'; - print_r(get_object_vars($svg)); + //print '<pre>'; + // print_r(get_object_vars($svg)); //print_r($alltables); - print_r($this); - print '</pre>'; + // print_r($this); + // print '</pre>'; } diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php index 63020a206..93b5c9dad 100644 --- a/libraries/schema/User_Schema.class.php +++ b/libraries/schema/User_Schema.class.php @@ -92,7 +92,7 @@ class PMA_User_Schema <form method="post" action="export_relation_schema.php" name="frm_create_page"> <fieldset> <legend> - <?php echo __('Create a page !') . "\n"; ?> + <?php echo __('Create a page') . "\n"; ?> </legend> <?php echo PMA_generate_common_hidden_inputs($db); ?> <input type="hidden" name="do" value="createpage" /> @@ -147,7 +147,7 @@ class PMA_User_Schema <form method="get" action="export_relation_schema.php" name="frm_select_page"> <fieldset> <legend> - <?php echo __('Please select a page to edit') . "\n"; ?> + <?php echo __('Please choose a page to edit') . "\n"; ?> </legend> <?php echo PMA_generate_common_hidden_inputs($db, $table); ?> <input type="hidden" name="do" value="selectpage" /> @@ -379,6 +379,7 @@ class PMA_User_Schema <input type="hidden" name="pdf_page_number" value="<?php echo htmlspecialchars($this->choosenPage); ?>" /> <?php } ?> <input type="hidden" name="do" value="process_export" /> + <input type="hidden" name="chpage" value="<?php echo $chpage; ?>" /> <input type="checkbox" name="show_grid" id="show_grid_opt" /> <label for="show_grid_opt"><?php echo __('Show grid'); ?></label><br /> <input type="checkbox" name="show_color" id="show_color_opt" checked="checked" /> diff --git a/libraries/schema/Visio_Relation_Schema.class.php b/libraries/schema/Visio_Relation_Schema.class.php index e41cdcc4c..472785c71 100644 --- a/libraries/schema/Visio_Relation_Schema.class.php +++ b/libraries/schema/Visio_Relation_Schema.class.php @@ -160,7 +160,7 @@ class PMA_VISIO extends XMLWriter //if(ob_get_clean()){ //ob_end_clean(); //} - //header('Content-type: text/xml'); + header('Content-type: application/visio'); header('Content-Disposition: attachment; filename="'.$fileName.'.vdx"'); $output = $this->flush(); print $output; @@ -212,7 +212,7 @@ class Table_Stats $sql = 'DESCRIBE ' . PMA_backquote($tableName); $result = PMA_DBI_try_query($sql, null, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { - $visio->dieSchema(sprintf(__('The %s table doesn\'t exist!'), $tableName)); + $visio->dieSchema($pageNumber,"VISIO",sprintf(__('The %s table doesn\'t exist!'), $tableName)); } /* @@ -254,7 +254,7 @@ class Table_Stats $result = PMA_query_as_controluser($sql, false, PMA_DBI_QUERY_STORE); if (!$result || !PMA_DBI_num_rows($result)) { - //$visio->dieSchema(sprintf(__('Please configure the coordinates for table %s'), $tableName)); + $visio->dieSchema($pageNumber,"VISIO",sprintf(__('Please configure the coordinates for table %s'), $tableName)); } list($this->x, $this->y) = PMA_DBI_fetch_row($result); $this->x = (double) $this->x; From d579653dc5fd6af2dac343b8b750806354dc405a Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Wed, 18 Aug 2010 19:31:29 +0500 Subject: [PATCH 43/48] Removed debug commented code --- libraries/schema/Dia_Relation_Schema.class.php | 4 ---- libraries/schema/Eps_Relation_Schema.class.php | 6 ------ libraries/schema/Pdf_Relation_Schema.class.php | 5 ----- libraries/schema/Svg_Relation_Schema.class.php | 6 ------ libraries/schema/Visio_Relation_Schema.class.php | 4 ---- 5 files changed, 25 deletions(-) diff --git a/libraries/schema/Dia_Relation_Schema.class.php b/libraries/schema/Dia_Relation_Schema.class.php index 97ac28a19..72eea5ebc 100644 --- a/libraries/schema/Dia_Relation_Schema.class.php +++ b/libraries/schema/Dia_Relation_Schema.class.php @@ -713,10 +713,6 @@ class PMA_Dia_Relation_Schema extends PMA_Export_Relation_Schema $dia->endDiaDoc(); $dia->showOutput($db.'-'.$this->pageNumber); exit(); - // print '<pre>'; - //print_r(get_object_vars($dia)); - //print_r(get_object_vars($this)); - //print '</pre>'; } /** diff --git a/libraries/schema/Eps_Relation_Schema.class.php b/libraries/schema/Eps_Relation_Schema.class.php index 07c3417d2..e0dbe09b3 100644 --- a/libraries/schema/Eps_Relation_Schema.class.php +++ b/libraries/schema/Eps_Relation_Schema.class.php @@ -797,12 +797,6 @@ class PMA_Eps_Relation_Schema extends PMA_Export_Relation_Schema $eps->endEpsDoc(); $eps->showOutput($db.'-'.$this->pageNumber); exit(); - //print '<pre>'; - //print_r(get_object_vars($eps)); - //print_r($alltables); - //print_r(get_object_vars($this)); - //print '</pre>'; - } /** diff --git a/libraries/schema/Pdf_Relation_Schema.class.php b/libraries/schema/Pdf_Relation_Schema.class.php index 829798137..78e6387b5 100644 --- a/libraries/schema/Pdf_Relation_Schema.class.php +++ b/libraries/schema/Pdf_Relation_Schema.class.php @@ -949,11 +949,6 @@ class PMA_Pdf_Relation_Schema extends PMA_Export_Relation_Schema if ($seen_a_relation) { $this->_drawRelations($this->showColor); } - /* print '<pre>'; - print_r(get_object_vars($this)); - print_r(get_object_vars($pdf)); - print '</pre>'; - */ $this->_drawTables($this->showColor); $this->_showOutput($this->pageNumber); exit(); diff --git a/libraries/schema/Svg_Relation_Schema.class.php b/libraries/schema/Svg_Relation_Schema.class.php index 8714abcaf..03422cb8f 100644 --- a/libraries/schema/Svg_Relation_Schema.class.php +++ b/libraries/schema/Svg_Relation_Schema.class.php @@ -781,12 +781,6 @@ class PMA_Svg_Relation_Schema extends PMA_Export_Relation_Schema $svg->endSvgDoc(); $svg->showOutput($db.'-'.$this->pageNumber); exit(); - //print '<pre>'; - // print_r(get_object_vars($svg)); - //print_r($alltables); - // print_r($this); - // print '</pre>'; - } /** diff --git a/libraries/schema/Visio_Relation_Schema.class.php b/libraries/schema/Visio_Relation_Schema.class.php index 472785c71..e3aaba12f 100644 --- a/libraries/schema/Visio_Relation_Schema.class.php +++ b/libraries/schema/Visio_Relation_Schema.class.php @@ -538,10 +538,6 @@ class PMA_Visio_Relation_Schema extends PMA_Export_Relation_Schema $visio->endVisioDoc(); $visio->showOutput($db.'-'.$this->pageNumber); exit(); - print '<pre>'; - print_r(get_object_vars($visio)); - print_r(get_object_vars($this)); - print '</pre>'; } /** From afcf1e6bbee7c7a1720df0e61b2ef64a925b8f0b Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Thu, 19 Aug 2010 17:31:01 +0500 Subject: [PATCH 44/48] Automatic Foreign/Internal Layout fix --- libraries/schema/User_Schema.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/schema/User_Schema.class.php b/libraries/schema/User_Schema.class.php index 93b5c9dad..7739ce340 100644 --- a/libraries/schema/User_Schema.class.php +++ b/libraries/schema/User_Schema.class.php @@ -58,8 +58,8 @@ class PMA_User_Schema break; case 'createpage': $this->pageNumber = PMA_REL_create_page($_POST['newpage'], $cfgRelation, $db, $query_default_option); - $this->autoLayoutForeign = $_POST['auto_layout_foreign']; - $this->autoLayoutInternal = $_POST['auto_layout_internal']; + $this->autoLayoutForeign = isset($_POST['auto_layout_foreign']) ? "1":NULL; + $this->autoLayoutInternal = isset($_POST['auto_layout_internal']) ? "1":NULL; $this->processRelations($db, $this->pageNumber,$cfgRelation,$query_default_option); break; case 'edcoord': @@ -702,7 +702,7 @@ class PMA_User_Schema } } - if (isset($this->autoLayoutInternal) || isset($this->auto_layout_foreign)) { + if (isset($this->autoLayoutInternal) || isset($this->autoLayoutForeign)) { $this->addRelationCoordinates($all_tables,$pageNumber,$db, $cfgRelation,$query_default_option); } From 27fe8e4658cc8a79cfce17ac359dfdf7b41ba98b Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Thu, 19 Aug 2010 17:32:43 +0500 Subject: [PATCH 45/48] Indenation fix in functions.js --- js/functions.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/js/functions.js b/js/functions.js index 33c8e960a..61a58f861 100644 --- a/js/functions.js +++ b/js/functions.js @@ -1436,14 +1436,11 @@ function refreshDragOption(e) { function refreshLayout() { var elm = $('#pdflayout') var orientation = $('#orientation_opt').val(); - if($('#paper_opt').length==1) - { - var paper = $('#paper_opt').val(); - } - else - { + if($('#paper_opt').length==1){ + var paper = $('#paper_opt').val(); + }else{ var paper = 'A4'; - } + } if (orientation == 'P') { posa = 'x'; posb = 'y'; From 75623e5066071c91b12e5b2e565dd9907e790180 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Fri, 20 Aug 2010 11:09:37 +0500 Subject: [PATCH 46/48] EPS Relation Schema: missing lines fix --- .../schema/Eps_Relation_Schema.class.php | 52 +++++++------------ 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/libraries/schema/Eps_Relation_Schema.class.php b/libraries/schema/Eps_Relation_Schema.class.php index e0dbe09b3..e783cbd33 100644 --- a/libraries/schema/Eps_Relation_Schema.class.php +++ b/libraries/schema/Eps_Relation_Schema.class.php @@ -191,10 +191,10 @@ class PMA_EPS { $this->stringCommands .= $lineWidth . " setlinewidth \n"; $this->stringCommands .= "newpath \n"; - $this->stringCommands .= $x_from . ' ' . $y_from . " moveto \n"; - $this->stringCommands .= $x_to . ' ' . $y_from . " lineto \n"; - $this->stringCommands .= $x_to . ' ' . $y_to . " lineto \n"; - $this->stringCommands .= $x_from . " " . $y_to . " lineto \n"; + $this->stringCommands .= $x_from . " " . $y_from . " moveto \n"; + $this->stringCommands .= "0 " . $y_to . " rlineto \n"; + $this->stringCommands .= $x_to . " 0 rlineto \n"; + $this->stringCommands .= "0 -" . $y_to . " rlineto \n"; $this->stringCommands .= "closepath \n"; $this->stringCommands .= "stroke \n"; } @@ -488,7 +488,7 @@ class Table_Stats foreach ($this->fields as $field) { $this->width = max($this->width, $eps->getStringWidth($field,$font,$fontSize)); } - $this->width += $eps->getStringWidth(' ',$font,$fontSize); + $this->width += $eps->getStringWidth(' ',$font,$fontSize); /* * it is unknown what value must be added, because * table title is affected by the tabe width value @@ -524,11 +524,11 @@ class Table_Stats { global $eps; //echo $this->_tableName.'<br />'; - //$eps->rect($this->x,$this->y, - //$this->width,$this->heightCell, - //2 - // ); - $eps->showXY($this->_getTitle(),$this->x + 5,$this->y+ 14); + $eps->rect($this->x,$this->y + 12, + $this->width,$this->heightCell, + 1 + ); + $eps->showXY($this->_getTitle(),$this->x + 5,$this->y + 14); foreach ($this->fields as $field) { $this->currentCell += $this->heightCell; $showColor = 'none'; @@ -540,8 +540,8 @@ class Table_Stats $showColor = 'none'; } } - //$eps->rect($this->x,$this->y + $this->currentCell, - //$this->width, $this->heightCell,2); + $eps->rect($this->x,$this->y + 12 + $this->currentCell, + $this->width, $this->heightCell,1); $eps->showXY($field, $this->x + 5, $this->y + 14 + $this->currentCell); } } @@ -622,8 +622,8 @@ class Relation_Stats $this->xDest = $dest_pos[1]; $this->destDir = 1; } - $this->ySrc = $src_pos[2]; - $this->yDest = $dest_pos[2]; + $this->ySrc = $src_pos[2] + 10; + $this->yDest = $dest_pos[2] + 10; } /** @@ -669,15 +669,17 @@ class Relation_Stats } else { $color = 'black'; } - + // draw a line like -- to foreign field $eps->line($this->xSrc,$this->ySrc, $this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, 1 ); + // draw a line like -- to master field $eps->line($this->xDest + $this->destDir * $this->wTick, $this->yDest, $this->xDest, $this->yDest, 1 ); + // draw a line that connects to master field line and foreign field line $eps->line($this->xSrc + $this->srcDir * $this->wTick,$this->ySrc, $this->xDest + $this->destDir * $this->wTick, $this->yDest, 1 @@ -769,9 +771,8 @@ class PMA_Eps_Relation_Schema extends PMA_Export_Relation_Schema if ($this->sameWide) { $this->tables[$table]->width = $this->_tablewidth; } - $this->_setMinMax($this->tables[$table]); } - + $seen_a_relation = false; foreach ($alltables as $one_table) { $exist_rel = PMA_getForeigners($db, $one_table, '', 'both'); @@ -799,21 +800,6 @@ class PMA_Eps_Relation_Schema extends PMA_Export_Relation_Schema exit(); } - /** - * Sets X and Y minimum and maximum for a table cell - * - * @param string table The table name - * @return void - * @access private - */ - private function _setMinMax($table) - { - $this->_xMax = max($this->_xMax, $table->x + $table->width); - $this->_yMax = max($this->_yMax, $table->y + $table->height); - $this->_xMin = min($this->_xMin, $table->x); - $this->_yMin = min($this->_yMin, $table->y); - } - /** * Defines relation objects * @@ -830,11 +816,9 @@ class PMA_Eps_Relation_Schema extends PMA_Export_Relation_Schema { if (!isset($this->tables[$masterTable])) { $this->tables[$masterTable] = new Table_Stats($masterTable, $font, $fontSize, $this->pageNumber, $this->_tablewidth, false, $showInfo); - $this->_setMinMax($this->tables[$masterTable]); } if (!isset($this->tables[$foreignTable])) { $this->tables[$foreignTable] = new Table_Stats($foreignTable,$font,$fontSize,$this->pageNumber, $this->_tablewidth, false, $showInfo); - $this->_setMinMax($this->tables[$foreignTable]); } $this->_relations[] = new Relation_Stats($this->tables[$masterTable], $masterField, $this->tables[$foreignTable], $foreignField); } From 5fb5678a50f155041edd5a3fad6c93ff6e1df3ee Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Fri, 20 Aug 2010 11:11:51 +0500 Subject: [PATCH 47/48] DIA Relation Schema: scale diagram fix --- libraries/schema/Dia_Relation_Schema.class.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/schema/Dia_Relation_Schema.class.php b/libraries/schema/Dia_Relation_Schema.class.php index 72eea5ebc..26e7b926d 100644 --- a/libraries/schema/Dia_Relation_Schema.class.php +++ b/libraries/schema/Dia_Relation_Schema.class.php @@ -321,16 +321,16 @@ class Table_Stats $dia->writeAttribute('id', ''.$this->tableId.''); $dia->writeRaw( '<dia:attribute name="obj_pos"> - <dia:point val="'.($this->x * 0.026458333).','.($this->y * 0.026458333).'"/> + <dia:point val="'.($this->x * .05).','.($this->y * .05).'"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="'.($this->x * 0.026458333).','.($this->y * 0.026458333).';9.97,9.2"/> + <dia:rectangle val="'.($this->x * .05).','.($this->y * .05).';9.97,9.2"/> </dia:attribute> <dia:attribute name="meta"> <dia:composite type="dict"/> </dia:attribute> <dia:attribute name="elem_corner"> - <dia:point val="'.($this->x * 0.026458333).','.($this->y * 0.026458333).'"/> + <dia:point val="'.($this->x * .05).','.($this->y * .05).'"/> </dia:attribute> <dia:attribute name="elem_width"> <dia:real val="5.9199999999999999"/> From 8a827e52614960f90c4d741ee6f89123424363c4 Mon Sep 17 00:00:00 2001 From: Adnan <hiddenpearls@gmail.com> Date: Fri, 20 Aug 2010 12:13:30 +0500 Subject: [PATCH 48/48] TCPDF: removed ob_get_contents() [was added for debugging] --- libraries/tcpdf/tcpdf.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/tcpdf/tcpdf.php b/libraries/tcpdf/tcpdf.php index 3d134b61d..a6b707ac2 100644 --- a/libraries/tcpdf/tcpdf.php +++ b/libraries/tcpdf/tcpdf.php @@ -2996,7 +2996,7 @@ if(!class_exists('TCPDF', false)) { case 'D': { //Download file if(ob_get_contents()) { - $this->Error(ob_get_contents().'Some data has already been output, can\'t send PDF file'); + $this->Error('Some data has already been output, can\'t send PDF file'); } if(isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) { @@ -3007,6 +3007,7 @@ if(!class_exists('TCPDF', false)) { if(headers_sent()) { $this->Error('Some data has already been output to browser, can\'t send PDF file'); } + header('Content-Type: application/pdf'); header('Content-Length: '.strlen($this->buffer)); header('Content-disposition: attachment; filename="'.$name.'"'); echo $this->buffer;