├── .gitignore ├── examples ├── pictures │ ├── img │ │ ├── ktip.png │ │ ├── acroread.png │ │ ├── kmplayer.png │ │ ├── video-display.png │ │ ├── view-pim-notes.png │ │ ├── view-pim-tasks.png │ │ ├── application-x-zerosize.png │ │ ├── im-status-message-edit.png │ │ ├── application-x-smb-server.png │ │ ├── utilities-file-archiver.png │ │ ├── application-x-smb-workgroup.png │ │ └── preferences-desktop-cryptography.png │ ├── pictures.ods │ ├── pictures.pdf │ ├── pictures-out.ods │ ├── pictures-out.pdf │ ├── test_pictures.php │ ├── pictures.json │ ├── manifest.xml │ └── pictures-out.ods-content.xml ├── continents │ ├── continents.ods │ ├── continents.pdf │ ├── continents-out.ods │ ├── continents-out.pdf │ ├── test_OOT.php │ └── continents.json ├── deepdata │ ├── deepdata-out.ods │ ├── deepdata-out.pdf │ ├── deepdata.json.zip │ ├── deepdata-template.ods │ ├── deepdata-template.pdf │ └── deepdata.php ├── documents │ ├── document_bill.ods │ ├── document_bill.pdf │ ├── img │ │ ├── logo_empty.jpg │ │ ├── logo_ubuntu.jpg │ │ ├── sign_empty.jpg │ │ ├── sign_empty.png │ │ ├── sign_ubuntu.png │ │ ├── stamp_empty.jpg │ │ ├── stamp_empty.png │ │ ├── stamp_ubuntu.png │ │ ├── logo_libre_office.jpg │ │ ├── logo_open_doc_template.jpg │ │ ├── sign_libre_office_calc.png │ │ ├── sign_open_doc_template.png │ │ ├── stamp_libre_office_calc.png │ │ └── stamp_open_doc_template.png │ ├── document_bill-out.ods │ ├── document_bill-out_sample2.pdf │ ├── document_bill-out_ubuntu_stamp.pdf │ ├── test_document.php │ ├── documents.json │ ├── README.md │ ├── content.xml │ └── document_bill-out.ods-content.xml └── text_document │ ├── text_doc-out.odt │ ├── text_doc-template.odt │ ├── data.php │ └── text_doc.php ├── docs └── img │ ├── continents_template_out.jpg │ ├── continents_template_src.jpg │ ├── deepdata_template_out.jpg │ ├── deepdata_template_src.jpg │ ├── document_template_out.jpg │ ├── document_template_src.jpg │ ├── pictures_template_out.jpg │ ├── pictures_template_src.jpg │ ├── document_template_out_libre.png │ ├── document_template_out_ubuntu.png │ ├── document_template_src_named.jpg │ ├── document_template_src_img_anchor.png │ ├── document_template_src_properties.jpg │ └── document_template_src_virtual_fields.png ├── composer.json ├── README.md └── OpenDocumentTemplate.php /.gitignore: -------------------------------------------------------------------------------- 1 | .directory 2 | .~* 3 | .swp 4 | -------------------------------------------------------------------------------- /examples/pictures/img/ktip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/ktip.png -------------------------------------------------------------------------------- /examples/pictures/pictures.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/pictures.ods -------------------------------------------------------------------------------- /examples/pictures/pictures.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/pictures.pdf -------------------------------------------------------------------------------- /docs/img/continents_template_out.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/continents_template_out.jpg -------------------------------------------------------------------------------- /docs/img/continents_template_src.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/continents_template_src.jpg -------------------------------------------------------------------------------- /docs/img/deepdata_template_out.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/deepdata_template_out.jpg -------------------------------------------------------------------------------- /docs/img/deepdata_template_src.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/deepdata_template_src.jpg -------------------------------------------------------------------------------- /docs/img/document_template_out.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/document_template_out.jpg -------------------------------------------------------------------------------- /docs/img/document_template_src.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/document_template_src.jpg -------------------------------------------------------------------------------- /docs/img/pictures_template_out.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/pictures_template_out.jpg -------------------------------------------------------------------------------- /docs/img/pictures_template_src.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/pictures_template_src.jpg -------------------------------------------------------------------------------- /examples/continents/continents.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/continents/continents.ods -------------------------------------------------------------------------------- /examples/continents/continents.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/continents/continents.pdf -------------------------------------------------------------------------------- /examples/deepdata/deepdata-out.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/deepdata/deepdata-out.ods -------------------------------------------------------------------------------- /examples/deepdata/deepdata-out.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/deepdata/deepdata-out.pdf -------------------------------------------------------------------------------- /examples/deepdata/deepdata.json.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/deepdata/deepdata.json.zip -------------------------------------------------------------------------------- /examples/documents/document_bill.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/document_bill.ods -------------------------------------------------------------------------------- /examples/documents/document_bill.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/document_bill.pdf -------------------------------------------------------------------------------- /examples/pictures/img/acroread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/acroread.png -------------------------------------------------------------------------------- /examples/pictures/img/kmplayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/kmplayer.png -------------------------------------------------------------------------------- /examples/pictures/pictures-out.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/pictures-out.ods -------------------------------------------------------------------------------- /examples/pictures/pictures-out.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/pictures-out.pdf -------------------------------------------------------------------------------- /examples/continents/continents-out.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/continents/continents-out.ods -------------------------------------------------------------------------------- /examples/continents/continents-out.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/continents/continents-out.pdf -------------------------------------------------------------------------------- /examples/documents/img/logo_empty.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/logo_empty.jpg -------------------------------------------------------------------------------- /examples/documents/img/logo_ubuntu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/logo_ubuntu.jpg -------------------------------------------------------------------------------- /examples/documents/img/sign_empty.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/sign_empty.jpg -------------------------------------------------------------------------------- /examples/documents/img/sign_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/sign_empty.png -------------------------------------------------------------------------------- /examples/documents/img/sign_ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/sign_ubuntu.png -------------------------------------------------------------------------------- /examples/documents/img/stamp_empty.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/stamp_empty.jpg -------------------------------------------------------------------------------- /examples/documents/img/stamp_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/stamp_empty.png -------------------------------------------------------------------------------- /docs/img/document_template_out_libre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/document_template_out_libre.png -------------------------------------------------------------------------------- /docs/img/document_template_out_ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/document_template_out_ubuntu.png -------------------------------------------------------------------------------- /docs/img/document_template_src_named.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/document_template_src_named.jpg -------------------------------------------------------------------------------- /examples/deepdata/deepdata-template.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/deepdata/deepdata-template.ods -------------------------------------------------------------------------------- /examples/deepdata/deepdata-template.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/deepdata/deepdata-template.pdf -------------------------------------------------------------------------------- /examples/documents/document_bill-out.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/document_bill-out.ods -------------------------------------------------------------------------------- /examples/documents/img/stamp_ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/stamp_ubuntu.png -------------------------------------------------------------------------------- /examples/pictures/img/video-display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/video-display.png -------------------------------------------------------------------------------- /examples/pictures/img/view-pim-notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/view-pim-notes.png -------------------------------------------------------------------------------- /examples/pictures/img/view-pim-tasks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/view-pim-tasks.png -------------------------------------------------------------------------------- /examples/text_document/text_doc-out.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/text_document/text_doc-out.odt -------------------------------------------------------------------------------- /docs/img/document_template_src_img_anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/document_template_src_img_anchor.png -------------------------------------------------------------------------------- /docs/img/document_template_src_properties.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/document_template_src_properties.jpg -------------------------------------------------------------------------------- /examples/documents/img/logo_libre_office.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/logo_libre_office.jpg -------------------------------------------------------------------------------- /examples/text_document/text_doc-template.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/text_document/text_doc-template.odt -------------------------------------------------------------------------------- /examples/documents/document_bill-out_sample2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/document_bill-out_sample2.pdf -------------------------------------------------------------------------------- /examples/pictures/img/application-x-zerosize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/application-x-zerosize.png -------------------------------------------------------------------------------- /examples/pictures/img/im-status-message-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/im-status-message-edit.png -------------------------------------------------------------------------------- /docs/img/document_template_src_virtual_fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/docs/img/document_template_src_virtual_fields.png -------------------------------------------------------------------------------- /examples/documents/img/logo_open_doc_template.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/logo_open_doc_template.jpg -------------------------------------------------------------------------------- /examples/documents/img/sign_libre_office_calc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/sign_libre_office_calc.png -------------------------------------------------------------------------------- /examples/documents/img/sign_open_doc_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/sign_open_doc_template.png -------------------------------------------------------------------------------- /examples/documents/img/stamp_libre_office_calc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/stamp_libre_office_calc.png -------------------------------------------------------------------------------- /examples/documents/img/stamp_open_doc_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/img/stamp_open_doc_template.png -------------------------------------------------------------------------------- /examples/pictures/img/application-x-smb-server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/application-x-smb-server.png -------------------------------------------------------------------------------- /examples/pictures/img/utilities-file-archiver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/utilities-file-archiver.png -------------------------------------------------------------------------------- /examples/documents/document_bill-out_ubuntu_stamp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/documents/document_bill-out_ubuntu_stamp.pdf -------------------------------------------------------------------------------- /examples/pictures/img/application-x-smb-workgroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/application-x-smb-workgroup.png -------------------------------------------------------------------------------- /examples/pictures/img/preferences-desktop-cryptography.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xv1t/OpenDocumentTemplate/HEAD/examples/pictures/img/preferences-desktop-cryptography.png -------------------------------------------------------------------------------- /examples/continents/test_OOT.php: -------------------------------------------------------------------------------- 1 | open('continents.ods', 'continents-out.ods', 'continents.json', array( 8 | 'hide_draws' => array( 9 | 'ImageNULL' 10 | ) 11 | )); 12 | -------------------------------------------------------------------------------- /examples/pictures/test_pictures.php: -------------------------------------------------------------------------------- 1 | open('pictures.ods', 'pictures-out.ods', 'pictures.json', array( 8 | 'with_image_dir' => 'img', 9 | 'extract_content' => true 10 | )); 11 | 12 | //print_r($od->meta); 13 | //print_r($od->schema); 14 | 15 | print_r($od->used_images); 16 | -------------------------------------------------------------------------------- /examples/text_document/data.php: -------------------------------------------------------------------------------- 1 | array( 5 | 'name' => 'The report main name!', 6 | 'date' => date('c'), 7 | 'title' => 'Contract 34', 8 | 'author' => 'user@localhost', 9 | 'picture' => 'acroread.png' 10 | ), 11 | 'Document' => array( 12 | 'address' => 'Mella st. 123-2 JK fede' 13 | ), 14 | 'Author' => array( 15 | 'name' => 'R. Sabbatinique' 16 | ) 17 | ); 18 | -------------------------------------------------------------------------------- /examples/deepdata/deepdata.php: -------------------------------------------------------------------------------- 1 | open('deepdata.json.zip'); 9 | echo "unzip deepdata.json\n"; 10 | $data = json_decode( $zip->getFromName('deepdata.json'), true ); 11 | 12 | $zip->close(); 13 | 14 | echo "Build report...\n"; 15 | $od->open('deepdata-template.ods', 'deepdata-out.ods', $data, array( 16 | // 'with_image_dir' => 'img', 17 | // 'extract_content' => true, 18 | // 'dom_stay' => true 19 | )); 20 | 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xv1t/opendocument-template", 3 | "description": "Rendering templates with multidimensional data into a reports", 4 | "keywords": ["template", "render", "ods", "libreoffice", "report"], 5 | "license": "MIT", 6 | "homepage": "https://github.com/xv1t/OpenDocumentTemplate", 7 | "authors": [ 8 | { 9 | "name": "Victor Fedotov", 10 | "email": "xv1t@yandex.ru" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.3.0", 15 | "ext-zip": "*", 16 | "ext-xml": "*" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/documents/test_document.php: -------------------------------------------------------------------------------- 1 | open('document_bill.ods', 'document_bill-out.ods', 'documents.json', array( 8 | 'with_image_dir' => 'img', 9 | 'extract_content' => true, 10 | 'dom_stay' => true 11 | )); 12 | 13 | //print_r($od->meta); 14 | $sheet = $od->dom->getElementsByTagName('table')->item(0); 15 | $xpath = new DOMXPath($od->dom); 16 | 17 | $items = $xpath->query("table:table-row/table:table-cell[@table:number-rows-spanned]", $sheet); 18 | 19 | //print_r($items[0]); 20 | //print_r($od->data); 21 | //print_r($od->schema['named-range']); 22 | 23 | print_r($od->used_images); 24 | echo(1234); 25 | $data = json_decode( file_get_contents( 'documents.json' ), true); 26 | var_dump($data); 27 | print_r( $data ); 28 | -------------------------------------------------------------------------------- /examples/text_document/text_doc.php: -------------------------------------------------------------------------------- 1 | open('text_doc-template.odt', 'text_doc-out.odt', $data, array( 10 | // 'with_image_dir' => 'img', 11 | // 'extract_content' => true, 12 | 'with_image_dir' => '../pictures/img', 13 | 'dom_stay' => true 14 | )); 15 | 16 | $text = $od->dom->getElementsByTagName('text')->item(0); 17 | 18 | $para = $text->getElementsByTagName('p'); 19 | print_r($para); 20 | 21 | foreach ($para as $p){ 22 | $textContent = $p->textContent; 23 | $childNodes = $p->childNodes->length; 24 | $firstChildTextContent = null; 25 | 26 | $childnodes = array(); 27 | 28 | if (!empty($p->childNodes)){ 29 | foreach ($p->childNodes as $childNode){ 30 | $childnodes[] = $childNode->textContent; 31 | } 32 | } 33 | 34 | //print_r(compact('textContent', 'childNodes', 'childnodes', 'p')); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /examples/pictures/pictures.json: -------------------------------------------------------------------------------- 1 | { 2 | "Report": { 3 | "title": "Data with pictures example" 4 | 5 | }, 6 | "People": [ 7 | { 8 | "Person": { 9 | "name": "Took Adalva", 10 | "title": "Izvr Magistr", 11 | "image": "acroread.png", 12 | "money": 12.34 13 | } 14 | }, 15 | { 16 | "Person": { 17 | "name": "Googve", 18 | "title": "Devon Otver", 19 | "image": "im-status-message-edit.png", 20 | "money": 6.7 21 | } 22 | }, 23 | { 24 | "Person": { 25 | "name": "Boorva", 26 | "title": "Grevuan Octanr", 27 | "image": "kmplayer.png" , 28 | "money": 0 29 | } 30 | }, 31 | { 32 | "Person": { 33 | "name": "Grigge", 34 | "title": "Fuurm Horsd", 35 | "image": false, 36 | "money": 15.76 37 | } 38 | }, 39 | { 40 | "Person": { 41 | "name": "muarge", 42 | "title": "Thimd", 43 | "image": "ktip.png", 44 | "money": 15.76 45 | } 46 | }, 47 | { 48 | "Person": { 49 | "name": "Jant", 50 | "title": "Qwerbg", 51 | "image": "utilities-file-archiver.png", 52 | "money": 10.54 53 | } 54 | }, 55 | { 56 | "Person": { 57 | "name": "Tika", 58 | "title": "Qwerbg", 59 | "image": "acroread.png", 60 | "money": 3.45 61 | } 62 | }, 63 | { 64 | "Person": { 65 | "name": "Atlam", 66 | "title": "--", 67 | "image": "video-display.png", 68 | "money": 0.6 69 | } 70 | } 71 | ] 72 | } -------------------------------------------------------------------------------- /examples/documents/documents.json: -------------------------------------------------------------------------------- 1 | { 2 | "Document": { 3 | "name": "Bill", 4 | "number": "123/A9", 5 | "date": "2016-09-23", 6 | "manager": "Gretto Uzz", 7 | "stamp": "stamp_libre_office_calc.png", 8 | "sign": "sign_open_doc_template.png" 9 | }, 10 | "Supplier": { 11 | "logo" : "logo_open_doc_template.jpg", 12 | "name": "Open source shot test", 13 | "address": "The city of the country, 123-23FZ", 14 | "email": "mail@example.free" 15 | }, 16 | "Buyer": { 17 | "name": "Typical customer", 18 | "address": "My country", 19 | "email": "mail@sample.com" 20 | }, 21 | "Goods": [ 22 | { 23 | "Good": { 24 | "name": "Cofee", 25 | "cost": 6.45, 26 | "count": 4 27 | } 28 | }, 29 | { 30 | "Good": { 31 | "name": "Disk", 32 | "cost": 0.17, 33 | "count": 3 34 | } 35 | }, 36 | { 37 | "Good": { 38 | "name": "Book", 39 | "cost": 2.30, 40 | "count": 3 41 | } 42 | }, 43 | { 44 | "Good": { 45 | "name": "USB Flash", 46 | "cost": 19, 47 | "count": 2 48 | } 49 | }, 50 | { 51 | "Good": { 52 | "name": "Floppy disk", 53 | "cost": 1.01, 54 | "count": 10 55 | } 56 | }, 57 | { 58 | "Good": { 59 | "name": "Manual PDF", 60 | "cost": 0.0, 61 | "count": 1 62 | } 63 | }, 64 | { 65 | "Good": { 66 | "name": "Hat", 67 | "cost": 17.34, 68 | "count": 7 69 | } 70 | }, 71 | { 72 | "Good": { 73 | "name": "Pen", 74 | "cost": 0.87, 75 | "count": 26 76 | } 77 | }, 78 | { 79 | "Good": { 80 | "name": "Keyboard AB", 81 | "cost": 16.04, 82 | "count": 8 83 | } 84 | } 85 | ] 86 | } 87 | -------------------------------------------------------------------------------- /examples/pictures/manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/continents/continents.json: -------------------------------------------------------------------------------- 1 | { 2 | "Report": { 3 | "title": "Report about continents", 4 | "created": "2016-09-17 14:14", 5 | "author": "xv1t@yandex.ru", 6 | "rating": 1254.5, 7 | "show_sign_image": false 8 | }, 9 | "Continents": [ 10 | { 11 | "Continent": { 12 | "name": "Africa", 13 | "area": 6309.01 14 | }, 15 | "Countries": [ 16 | { 17 | "Country": { 18 | "name": "Egypt", 19 | "area": 231 20 | }, 21 | "Cities": [ 22 | { 23 | "City": { 24 | "name": "Kair" 25 | } 26 | } 27 | ] 28 | }, 29 | { 30 | "Country": { 31 | "name": "Madagaskar", 32 | "area": 111 33 | } 34 | } 35 | ], 36 | "Seas": [ 37 | { 38 | "Sea": { 39 | "name": "Red Sea", 40 | "area": 233.2 41 | }, 42 | "Islands": [ 43 | { 44 | "Island": { 45 | "name": "Monkey" 46 | }, 47 | "Island": { 48 | "name": "Palm", 49 | "area": 23.5 50 | } 51 | } 52 | ] 53 | } 54 | ] 55 | }, 56 | { 57 | "Continent": { 58 | "name": "Euroasia", 59 | "area": 8726.34 60 | }, 61 | "Seas": [ 62 | { 63 | "Sea": { 64 | "name": "Black sea", 65 | "area": 4452.16 66 | }, 67 | "Islands": [ 68 | { 69 | "Island": { 70 | "name": "Buyan", 71 | "area": 45, 72 | "animals": "Belki, lebedy", 73 | "volcano": "no" 74 | } 75 | } 76 | ] 77 | }, 78 | { 79 | "Sea": { 80 | "name": "Azovsloe", 81 | "area": 342 82 | } 83 | } 84 | ], 85 | "Countries": [ 86 | { 87 | "Country": { 88 | "name": "USSR", 89 | "capital": "Moscow" 90 | }, 91 | "Cities": [ 92 | { 93 | "City": { 94 | "name": "Samara" 95 | } 96 | }, 97 | { 98 | "City": { 99 | "name": "Moscow" 100 | } 101 | } 102 | ] 103 | }, 104 | { 105 | "Country": { 106 | "name": "Belorussia", 107 | "capital": "Minsk" 108 | }, 109 | "Cities": [ 110 | { 111 | "City": { 112 | "name": "Minsk" 113 | } 114 | }, 115 | { 116 | "City": { 117 | "name": "Grodno" 118 | } 119 | } 120 | ] 121 | } 122 | ] 123 | }, 124 | { 125 | "Continent": { 126 | "name": "North America", 127 | "area": 12343 128 | }, 129 | "Countries": [ 130 | { 131 | "Country": { 132 | "name": "Canada", 133 | "capital": "Ottawa" 134 | }, 135 | "Cities": [] 136 | }, 137 | { 138 | "Country": { 139 | "name": "USA", 140 | "capital": "Washington" 141 | }, 142 | "Cities": [] 143 | } 144 | ] 145 | } 146 | ], 147 | "Oceans": [ 148 | { 149 | "Ocean": { 150 | "name": "Pacific", 151 | "area": 2381.45 152 | } 153 | 154 | }, 155 | { 156 | "Ocean": { 157 | "name": "Atlantic", 158 | "area": 23422 159 | } 160 | }, 161 | { 162 | "Ocean": { 163 | "name": "Indian", 164 | "area": 9031.09 165 | } 166 | }, 167 | { 168 | "Ocean": { 169 | "name": "Arctic", 170 | "area": 73076.4 171 | } 172 | } 173 | ] 174 | } 175 | -------------------------------------------------------------------------------- /examples/documents/README.md: -------------------------------------------------------------------------------- 1 | # Documents example 2 | 3 | # 1. Prepare image files 4 | All files put in one directory, in out example in `img` 5 | 6 | Image | size | dummy image | image name 7 | ------|----------|----------------|-------------------- 8 | Logo | 1024×100 | | [Supplier.logo] 9 | Stamp | 400×400 | | [Document.stamp] 10 | Sign | 685×350 | | [Document.sign] 11 | 12 | If you planing use different images, firstly you make a dummy version of images, and working 13 | image sizes must be identically! 14 | 15 | ## Stamps 16 | stamp_libre_office_calc.png|stamp_open_doc_template.png|stamp_ubuntu.png 17 | :------:|:-----:|:-----: 18 | | | 19 | 20 | ## Logos 21 | Picture | File 22 | ------|----- 23 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/examples/documents/img/logo_libre_office.jpg) |logo_libre_office.jpg 24 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/examples/documents/img/logo_open_doc_template.jpg) |logo_open_doc_template.jpg 25 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/examples/documents/img/logo_ubuntu.jpg) | logo_ubuntu.jpg 26 | 27 | ## Signs 28 | sign_libre_office_calc.png | sign_open_doc_template.png | sign_ubuntu.png 29 | --------|------|-----| 30 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/examples/documents/img/sign_libre_office_calc.png) |![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/examples/documents/img/sign_open_doc_template.png) |![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/examples/documents/img/sign_ubuntu.png) 31 | 32 | ## Image folder 33 | All image put in the once folder `img` 34 | 35 | # 2. Prepare data array 36 | Our `$data` in this example is a array and it have following keys: 37 | ```php 38 | $data = array( 39 | 'Document' => /*...*/, 40 | 'Supplier' => /*...*/, 41 | 'Buyer' => /*...*/ 42 | ); 43 | ``` 44 | ## Linear object notation. 45 | Keys `Document`, `Supplier`, `Buyer` - is a linear object keys 46 | ```php 47 | $data = array( 48 | 'Document' => array( 49 | 'name' => 'Bill', 50 | 'number' => '123/A9', 51 | 'date' => '2016-09-23', 52 | 'manager' => '', 53 | 'stamp' => 'stamp_open_doc_template.png', 54 | 'sign' => 'sign_open_doc_template.png' 55 | ), 56 | 'Supplier' => array( 57 | 'logo' => 'logo_libre_office.jpg', 58 | 'name' => "Open source shot test", 59 | 'address' => "The city of the country, 123-23FZ", 60 | 'email' => 'mail@example.free' 61 | ), 62 | 'Buyer' => array( 63 | 'name' => 'Typical customer', 64 | 'address' => 'My country', 65 | 'email' => 'mail@sample.com' 66 | ), 67 | ); 68 | ``` 69 | 70 | ## Next deep level data 71 | Add a key `Goods` is contain a list of objects. 72 | ```php 73 | $data = array( 74 | 'Document' => /*...*/, 75 | 'Supplier' => /*...*/, 76 | 'Buyer' => /*...*/ 77 | 'Goods' => array( //key for list of goods objects 78 | array(/*...*/), 79 | array(/*...*/), 80 | array(/*...*/), 81 | array(/*...*/), 82 | array(/*...*/), 83 | array(/*...*/), 84 | array(/*...*/), 85 | ) 86 | ); 87 | ``` 88 | And each `Good` object fill as 89 | ```php 90 | array( 91 | 'Goods' => array( 92 | array('Good' => array(/*...*/)), 93 | array('Good' => array(/*...*/)), 94 | array('Good' => array(/*...*/)), 95 | array('Good' => array(/*...*/)), 96 | array('Good' => array(/*...*/)), 97 | array('Good' => array(/*...*/)), 98 | array('Good' => array(/*...*/)), 99 | ) 100 | ) 101 | ``` 102 | Add all field for each `Good` contain a kes: `name`, `cost`, `count` 103 | ```php 104 | $data = array( 105 | 'Document' => array( 106 | 'name' => 'Bill', 107 | 'number' => '123/A9', 108 | 'date' => '2016-09-23', 109 | 'manager' => '', 110 | 'stamp' => 'stamp_open_doc_template.png', 111 | 'sign' => 'sign_open_doc_template.png' 112 | ), 113 | 'Supplier' => array( 114 | 'logo' => 'logo_libre_office.jpg', 115 | 'name' => "Open source shot test", 116 | 'address' => "The city of the country, 123-23FZ", 117 | 'email' => 'mail@example.free' 118 | ), 119 | 'Buyer' => array( 120 | 'name' => 'Typical customer', 121 | 'address' => 'My country', 122 | 'email' => 'mail@sample.com' 123 | ), 124 | 'Goods' => array( //This list of data next dimension 125 | array( 126 | 'Good' => array( 127 | 'name' => 'Cofee', 128 | 'cost' => 6.45, 129 | 'count' => 4, 130 | ) 131 | ), 132 | array( 133 | 'Good' => array( 134 | 'name' => 'Disk', 135 | 'cost' => 0.17, 136 | 'count' => 3, 137 | ) 138 | ), 139 | array( 140 | 'Good' => array( 141 | 'name' => 'Book', 142 | 'cost' => 2.30, 143 | 'count' => 3, 144 | ) 145 | ), 146 | array( 147 | 'Good' => array( 148 | 'name' => 'USB Flash', 149 | 'cost' => 19, 150 | 'count' => 2, 151 | ) 152 | ), 153 | array( 154 | 'Good' => array( 155 | 'name' => 'Floppy disk', 156 | 'cost' => 1.01, 157 | 'count' => 10, 158 | ) 159 | ), 160 | array( 161 | 'Good' => array( 162 | 'name' => 'Manual PDF', 163 | 'cost' => 9, 164 | 'count' => 1, 165 | ) 166 | ), 167 | array( 168 | 'Good' => array( 169 | 'name' => 'Hat', 170 | 'cost' => 17.34, 171 | 'count' => 7 172 | ) 173 | ), 174 | array( 175 | 'Good' => array( 176 | 'name' => 'Pen', 177 | 'cost' => 0.87, 178 | 'count' => 26 179 | ) 180 | ), 181 | array( 182 | 'Good' => array( 183 | 'name' => 'Keyboard AB', 184 | 'cost' => 16.04, 185 | 'count' => 8 186 | ) 187 | ), 188 | ) 189 | ); 190 | ``` 191 | 192 | # 3. Design template 193 | Design template file `document_bill.ods` 194 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/document_template_src.jpg) 195 | 196 | ## Data field notation 197 | All data fields writes on cells: 198 | 199 | Example | Value in the report | 200 | --------|------------ 201 | [Good.name] | Book 202 | [Document.name] [Document.number] as [Document.date] | Bill 123/A9 at 2016-09-23 203 | 204 | ## Named range of items 205 | Row `10` is a named range by name `Goods`. 206 | 207 | Range option 208 | 209 | - [x] Repeat row 210 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/document_template_src_named.jpg) 211 | 212 | 213 | ## Images 214 | All images need `Anchor` to `cell`. If select image, then be visible a anchor icon on the cell 215 | 216 | 217 | 218 | ## Image names 219 | If you want dinamic change image source, then set name to field name of current object, 220 | 221 | Logo | Stamp | Sign 222 | -----------------|--------|--------- 223 | [Supplier.logo] | [Document.stamp] | [Document.sign] 224 | 225 | ```php 226 | $data = array( 227 | 'Document' => array( 228 | /*...*/ 229 | 'stamp' => 'stamp_open_doc_template.png', //stamp image, image name: [Document.stamp] 230 | 'sign' => 'sign_open_doc_template.png' //sign image, image name : [Document.sign] 231 | ), 232 | 'Supplier' => array( 233 | 'logo' => 'logo_libre_office.jpg', //logo image name, image name: [Supplier.logo] 234 | /*...*/ 235 | ), 236 | 'Buyer' => array(/*..*/), 237 | 'Goods' => array(/*..*/), 238 | ``` 239 | ); 240 | 241 | ## Virtual fields 242 | In the good we have a two numeric fields: `cost`, `count`. 243 | But what about a value of `cost * count`? 244 | 245 | Virtual field | Formula 246 | --------------|----------- 247 | [Good.total] | [Good.cost]*[Good.count] 248 | [Good.tax] | [Good.total] * 0.18 249 | [Good.with_tax] | [Good.with_tax] 250 | 251 | On the spreedsheet in the cell with virtual field add a `Comment` and write formula, such as 252 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/document_template_src_virtual_fields.png) 253 | 254 | ## Aggregate function `COUNT()` 255 | All values `COUNT()` for defined named ranges automaticaly calculated 256 | Examples 257 | 258 | Named range | cell template value | Report value 259 | ------------|-----------------------|---------- 260 | Goods | [COUNT(Goods)] | 8 261 | Countries | [COUNT(Countries)] | 3 262 | 263 | ## SUM() 264 | 265 | Set properties for define aggregate `SUM()` functions: 266 | 267 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/document_template_src_properties.jpg) 268 | 269 | In the `LibreOffice Calc` click `File`/`Properties` - tab `Custom properties` 270 | And cells with values: `[SUM(Good.tax)]`, `[SUM(Good.total)]` and other are correctly to sum 271 | 272 | # And render our template 273 | ```php 274 | open('document_bill.ods', 'document_bill-out.ods', $data, array( 281 | 'with_image_dir' => 'img/', //this path of your images folder 282 | )); 283 | ``` 284 | 285 | And open new file `document_bill-out.ods` in the libre office and show print preview 286 | 287 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/document_template_out.jpg) 288 | 289 | Change the image names and your reports be a differents! 290 | 291 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/document_template_out_ubuntu.png) 292 | 293 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/document_template_out_libre.png) 294 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Project name | OpenDocumentTemplate 2 | -------------|--------------------- 3 | Language | PHP 4 | Source files | ODS, ODT 5 | 6 | 7 | #Fast generation OpenDocument reports 8 | Support files: `ODS`, `ODT` 9 | 10 | Template | Report 11 | ----------|------- 12 | [ ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/document_template_src.jpg) ](https://github.com/xv1t/OpenDocumentTemplate/tree/master/examples/documents) Bill document template (ODS) | [ ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/document_template_out.jpg) ](https://github.com/xv1t/OpenDocumentTemplate/tree/master/examples/documents) See the great manual [/examples/documents](https://github.com/xv1t/OpenDocumentTemplate/tree/master/examples/documents) 13 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/continents_template_src.jpg) Simple cards with data (ODS) | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/continents_template_out.jpg) [/examples/continents](https://github.com/xv1t/OpenDocumentTemplate/tree/master/examples/continents) 14 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/pictures_template_src.jpg) Dynamic pictures (ODS) | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/pictures_template_out.jpg) [/examples/pictures](https://github.com/xv1t/OpenDocumentTemplate/tree/master/examples/pictures) 15 | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/deepdata_template_src.jpg) 15 level deep dimensions (ODS) | ![](https://github.com/xv1t/OpenDocumentTemplate/blob/master/docs/img/deepdata_template_out.jpg) [/examples/deepdata](https://github.com/xv1t/OpenDocumentTemplate/tree/master/examples/deepdata) 16 | 17 | Recommended software for create template 18 | * LibreOffice 19 | * OpenOffice 20 | 21 | Your done report files was correct opened in 22 | * LibreOffice 23 | * OpenOffice 24 | * MS Office >=2010 25 | 26 | ## Fast manual (ods) 27 | 1. Create template ods file 28 | 2. Put elements 29 | 3. Mark ranges 30 | 4. Load data from database 31 | 5. Render data through template file info your new ods file 32 | 6. Open in the LibreOffice Calc or other and enjoy 33 | 34 | 35 | ## Requirements 36 | Php extensions 37 | * zip 38 | * xml 39 | 40 | Php version >=5.3 41 | 42 | Recommended sowfware for templating: LibreOffice 5 43 | 44 | ## Install 45 | Put file `OpenDocumentTemplate.php` into your project, and use it 46 | ```php 47 | array( 66 | 'name' => 'Test Report', 67 | 'date' => '2016-09-25', 68 | 'author' => 'Me' 69 | ) 70 | ); 71 | ``` 72 | All fields grouping in the `Report` parent array. 73 | This method of naming provides a powerful technique for multidimensional data. 74 | 75 | ## Design template file 76 | Open the LibreOffice Calc. Create new spreedsheet; 77 | 78 | add a 3 cells for next contents: 79 | 80 | [] |A | B | C | 81 | ---|----|---|------| 82 | 1 | [Report.name] | [Report.date] | [Report.author] 83 | 2 | | | 84 | 3 | | | 85 | 86 | Save it with name `sample_report.ods`. 87 | 88 | ## Render template with data 89 | ```php 90 | open('sample_report.ods', 'sample_report-out.ods', $data) 92 | ``` 93 | And open new file `sample_report-out.ods` and you see in sheet: 94 | 95 | [] |A | B | C 96 | ---|---|---|---- 97 | 1 |Test Report | 2016-09-25 | Me 98 | 2 | | | 99 | 3 | | | 100 | 101 | ## Add second dimension 102 | Add a key `Cities` for the list of objects 103 | ```php 104 | array(/* main Report data */), 107 | 'Cities' => array( 108 | array(/* city data */), 109 | array(/* city data */), 110 | array(/* city data */), 111 | array(/* city data */), 112 | ) 113 | ); 114 | ``` 115 | 116 | All `Cities` object must be have an identical list of the fields. In our example: `name`, `streets`, `population` 117 | ```php 118 | array( 123 | 'name' => 'Albatros', 124 | 'streets' => 165, 125 | 'population' => 1300000 126 | ) 127 | ); 128 | ``` 129 | 130 | And all data 131 | ```php 132 | $data = array( 133 | 'Report' => array( 134 | 'name' => 'Test Report', 135 | 'date' => '2016-09-25', 136 | 'author' => 'Me' 137 | ), 138 | 'Cities' => array( 139 | array( //first object 140 | 'City' => array( 141 | 'name' => 'Albatros', 142 | 'streets' => 165, 143 | 'population' => 1300000 144 | ) 145 | ), 146 | array( //next object 147 | 'City' => array( 148 | 'name' => 'Turtuga', 149 | 'streets' => 132, 150 | 'population' => 750000 151 | ) 152 | ), 153 | array( //next object 154 | 'City' => array( 155 | 'name' => 'Palmtown', 156 | 'streets' => 18, 157 | 'population' => 10000 158 | ) 159 | ), 160 | ) 161 | ); 162 | ``` 163 | ## Add a other object as linear dimesion 164 | Add inforamation about the mayor of the each city, in the sibling key `Mayor` 165 | ```php 166 | array(/*...*/), 170 | 'Cities' => array( 171 | array( 172 | 'City' => array(/*...*/), 173 | 'Mayor' => array( 174 | 'name' => 'John Do', 175 | 'old' => 47 176 | ), 177 | ), 178 | array( 179 | 'City' => array(/*...*/), 180 | 'Mayor' => array( 181 | 'name' => 'Mary Ann', 182 | 'old' => 32 183 | ), 184 | ), 185 | array( 186 | 'City' => array(/*...*/), 187 | 'Mayor' => array( 188 | 'name' => 'Mike Tee', 189 | 'old' => 29 190 | ), 191 | ), 192 | ) 193 | ); 194 | ``` 195 | 196 | ## Add third dimesions 197 | Add a key `Squares` for the list of the squares in the each city 198 | 199 | ```php 200 | array(/*...*/), 203 | 'Cities' => array( 204 | array( 205 | 'City' => array(/*...*/), 206 | 'Mayor' => array(/*...*/), 207 | 'Squares' => array( 208 | array(/*...*/), 209 | array(/*...*/), 210 | array(/*...*/), 211 | ) 212 | ), 213 | array( 214 | 'City' => array(/*...*/), 215 | 'Mayor' => array(/*...*/), 216 | 'Squares' => array( 217 | array(/*...*/), 218 | array(/*...*/), 219 | array(/*...*/), 220 | array(/*...*/), 221 | array(/*...*/), 222 | ) 223 | ), 224 | array( 225 | 'City' => array(/*...*/), 226 | 'Mayor' => array(/*...*/), 227 | 'Squares' => array( 228 | array(/*...*/), 229 | array(/*...*/), 230 | ) 231 | ), 232 | 233 | ) 234 | ); 235 | ``` 236 | Example Square object: 237 | ```php 238 | array( 241 | 'name' => 'Trafalgaar', 242 | 'length' => 23, 243 | 'width' => 45 244 | ) 245 | ) 246 | ``` 247 | 248 | And out `$data` finish version: 249 | ```php 250 | array( 255 | 'name' => 'Test Report', 256 | 'date' => '2016-09-25', 257 | 'author' => 'Me' 258 | ), 259 | 'Cities' => array( 260 | array( 261 | //level 2 262 | 'City' => array( 263 | 'name' => 'Albatros', 264 | 'streets' => 165, 265 | 'population' => 1300000 266 | ) 267 | 'Mayor' => array( 268 | 'name' => 'John Do', 269 | 'old' => 47 270 | ), 271 | 'Squares' => array( 272 | array( 273 | //level 3 274 | 'Sqaure' => array( 275 | 'name' => 'Trafalgaar', 276 | 'length' => 23, 277 | 'width' => 45 278 | ) 279 | ), 280 | array( 281 | 'Sqaure' => array( 282 | 'name' => 'Square #2', 283 | 'length' => 23, 284 | 'width' => 45 285 | ) 286 | ), 287 | array( 288 | 'Sqaure' => array( 289 | 'name' => 'Square #3', 290 | 'length' => 23, 291 | 'width' => 45 292 | ) 293 | ), 294 | ) 295 | ), 296 | array( 297 | 'City' => array( 298 | 'name' => 'Turtuga', 299 | 'streets' => 132, 300 | 'population' => 750000 301 | ) 302 | 'Mayor' => array( 303 | 'name' => 'Mary Ann', 304 | 'old' => 32 305 | ), 306 | 'Squares' => array( 307 | array( 308 | 'Sqaure' => array( 309 | 'name' => 'Square #4', 310 | 'length' => 23, 311 | 'width' => 45 312 | ) 313 | ), 314 | array( 315 | 'Sqaure' => array( 316 | 'name' => 'Square #5', 317 | 'length' => 23, 318 | 'width' => 45 319 | ) 320 | ), 321 | ) 322 | ), 323 | array( 324 | 'City' => array( 325 | 'name' => 'Palmtown', 326 | 'streets' => 18, 327 | 'population' => 10000 328 | ), 329 | 'Mayor' => array( 330 | 'name' => 'Mike Tee', 331 | 'old' => 29 332 | ), 333 | 'Squares' => array( 334 | array( 335 | 'Sqaure' => array( 336 | 'name' => 'Square #6', 337 | 'length' => 23, 338 | 'width' => 45 339 | ) 340 | ), 341 | array( 342 | 'Sqaure' => array( 343 | 'name' => 'Square #7', 344 | 'length' => 23, 345 | 'width' => 45 346 | ) 347 | ), 348 | array( 349 | 'Sqaure' => array( 350 | 'name' => 'Square #8', 351 | 'length' => 23, 352 | 'width' => 45 353 | ) 354 | ), 355 | ) 356 | ), 357 | ) 358 | ); 359 | ``` 360 | ## Design a spreedsheet 361 | 362 | [] | A | B | C | D | E 363 | ---|---|---|---|---|---- 364 | 1 | [Report.name] | | Author | [Report.author] 365 | 2 | **Cities** 366 | 3 | | City | [City.name] 367 | 4 | | Streets | [City.streets] 368 | 5 | | Population | [City.population] 369 | 6 | | Mayor | [Mayor.name] 370 | 7 | | **Squares** 371 | 8 | | | name | length | width 372 | 9 | | | [Square.name] | [Square.length] | [Square.width] 373 | 11 | | Squares count | [COUNT(Squares)] 374 | 12 | Cities count | [COUNT(Cities)] 375 | 13 | Report date | [Report.date] 376 | 377 | 378 | 379 | Well, we have a 3 level dimension array of objects: 380 | 381 | In LibreOffice Calc they are called `Range names`. 382 | 383 | `Insert` -> `Names` => `Manage` 384 | 385 | 386 | # Examples 387 | 388 | 389 | -------------------------------------------------------------------------------- /examples/documents/content.xml: -------------------------------------------------------------------------------- 1 | 2 | [Document.name] [Document.number] at [Document.date]Supplier[Supplier.name]Buyer[Buyer.name]Address[Supplier.address]Addres[Buyer.address]E-mail[Supplier.email]E-mail[Buyer.email]PosArticleCostCountTotal[position[[Good.name][Good.cost][Good.count]2016-09-23T00:00:00[Good.cost]*[Good.count][Good.tota]TotalSUM(Good.count)SUM(Good.total)]Total goods[SUM(Good.count)]Total moneySUM(Good.total)]Seller name[Supplier.manager]Seller sign -------------------------------------------------------------------------------- /examples/pictures/pictures-out.ods-content.xml: -------------------------------------------------------------------------------- 1 | 2 | total rows8Total money65,15Data with pictures examplePos.NameTitlePictureMoney1Took AdalvaIzvr Magistracroread.pngsettingsHREF=[Person.image_href] 3 | HIDE=!empty($data['Person']['hide'])12,342GoogveDevon Otverim-status-message-edit.pngsettingsHREF=[Person.image_href] 4 | HIDE=!empty($data['Person']['hide'])6,73BoorvaGrevuan Octanrkmplayer.pngsettingsHREF=[Person.image_href] 5 | HIDE=!empty($data['Person']['hide'])04GriggeFuurm Horsd15,765muargeThimdktip.pngsettingsHREF=[Person.image_href] 6 | HIDE=!empty($data['Person']['hide'])15,766JantQwerbgutilities-file-archiver.pngsettingsHREF=[Person.image_href] 7 | HIDE=!empty($data['Person']['hide'])10,547TikaQwerbgacroread.pngsettingsHREF=[Person.image_href] 8 | HIDE=!empty($data['Person']['hide'])3,458Atlam--video-display.pngsettingsHREF=[Person.image_href] 9 | HIDE=!empty($data['Person']['hide'])0,665,15 10 | -------------------------------------------------------------------------------- /examples/documents/document_bill-out.ods-content.xml: -------------------------------------------------------------------------------- 1 | 2 | Bill 123/A9 at 2016-09-23SupplierOpen source shot testBuyerTypical customerAddressThe city of the country, 123-23FZAddresMy countryE-mailmail@example.freeE-mailmail@sample.comPosArticleCostCountTotaltaxTotal with tax1Cofee6,45425,84,64430,4442Disk0,1730,510,09180,60183Book2,336,91,2428,1424USB Flash192386,8444,845Floppy disk1,011010,11,81811,9186Manual PDF010007Hat17,347121,3821,8484143,22848Pen0,872622,624,071626,69169Keyboard AB16,048128,3223,0976151,4176Total64353,6363,6534417,2834Total goods64Money353,63Tax63,6534With tax417,2834Seller nameGretto UzzSeller sign 3 | -------------------------------------------------------------------------------- /OpenDocumentTemplate.php: -------------------------------------------------------------------------------- 1 | '[', 19 | 'after' => ']', 20 | 'separator' => '.' 21 | ); 22 | var $used_images = array(); //list of 23 | var $mimetype; //of current open file 24 | var $virtualFields = array(); 25 | var $hasFormula = false; 26 | 27 | /** 28 | * Open a template file, read him, and write result to a out_file 29 | * @param string $template_file

30 | * Path to source ods or odt file 31 | *

32 | * @param string $out_file

33 | * Path to result file 34 | *

35 | * @param mixed $data

36 | * Structured array, or path to json file with data 37 | *

38 | * @param array $options

39 | * Options 40 | *

41 | */ 42 | public function open($template_file, $out_file, $data, $options = array()) { 43 | if (empty($this->dom)) { 44 | $this->dom = new DOMDocument; 45 | } 46 | $zip = new ZipArchive; 47 | $zip->open($template_file); 48 | $this->mimetype = $zip->getFromName('mimetype'); 49 | 50 | $this->dom->loadXML($zip->getFromName('meta.xml')); 51 | $this->read_meta(); 52 | 53 | $this->dom->loadXML($zip->getFromName('content.xml')); 54 | $zip->close(); 55 | 56 | if (is_string($data) && file_exists($data)){ 57 | $data = json_decode(file_get_contents($data), true ); 58 | } 59 | 60 | if (empty($data)){ 61 | $data = array(); 62 | } 63 | 64 | //prepare data 65 | $data += array( 66 | 'now' => date('Y-m-d'), 67 | 'now_datetime' => date('Y-m-d H:i:i'), 68 | 'datetime' => date('Y-m-d H:i:i'), 69 | 'now_time' => date('H:i:s'), 70 | 'time' => date('H:i:s'), 71 | 'template_file' => $template_file, 72 | 'out_file' => $out_file 73 | ); 74 | 75 | //populate data to object property 76 | $this->data = $data; 77 | switch ($this->mimetype) { 78 | case 'application/vnd.oasis.opendocument.spreadsheet': 79 | $this->ods_analyze(); 80 | $this->ods_analyze_data(); 81 | 82 | /* 83 | * Manipulate images, hide or visible 84 | */ 85 | 86 | if (!empty($options['hide_draws'])) { 87 | $this->ods_hide_draws($options['hide_draws']); 88 | } 89 | $this->ods_hide_draw_conditions(); 90 | 91 | break; 92 | case 'application/vnd.oasis.opendocument.text': 93 | echo "ODT!!!!!!!!!!!"; 94 | $this->odt_analyze(); 95 | break; 96 | } 97 | 98 | if (file_exists($out_file)) { 99 | //delete destination file if exists 100 | unlink($out_file); 101 | } 102 | 103 | //copy template to destinatoin 104 | copy($template_file, $out_file); 105 | $zip->open($out_file); 106 | 107 | $content = $this->dom->saveXml(); 108 | 109 | if (!empty($options['extract_content'])){ 110 | file_put_contents($out_file . '-content.xml', $content); 111 | } 112 | 113 | // 114 | 115 | $zip->addFromString('content.xml', $content); 116 | 117 | //styles.xml 118 | $this->dom->loadXML($zip->getFromName('styles.xml')); 119 | $zip->addFromString('styles.xml', $this->render_styles()); 120 | 121 | //add images 122 | if ( !empty($options['with_image_dir']) ){ 123 | if (is_dir($options['with_image_dir'])){ 124 | //var_dump($zip->getFromName('META-INF/manifest.xml')); 125 | $this->dom->loadXML($zip->getFromName('META-INF/manifest.xml')); 126 | 127 | $zip->addFromString('META-INF/manifest.xml', 128 | $this->write_manifest( 129 | $this->dir_to_zip( 130 | $zip, 131 | $options['with_image_dir'], 132 | 'Pictures') 133 | ) 134 | ); 135 | } 136 | } 137 | 138 | if (!empty($options['dom_stay'])){ 139 | $this->dom->loadXML($content); 140 | } 141 | 142 | //find unused images and and delete from zip 143 | 144 | $zip->close(); 145 | } 146 | 147 | function odt_analyze(){ 148 | $text = $this->dom->getElementsByTagName('text')->item(0); 149 | 150 | $para = $text->getElementsByTagName('p'); 151 | $this->odt_parse_elements($para); 152 | 153 | $para = $text->getElementsByTagName('h'); 154 | $this->odt_parse_elements($para); 155 | 156 | //images 157 | $this->ods_render_cell_images($text, $this->data); 158 | } 159 | 160 | function odt_parse_elements($elements){ 161 | foreach ($elements as $p){ 162 | foreach ($p->childNodes as $item){ 163 | $textContent = $item->textContent; 164 | if ($this->string_has_params($textContent)){ 165 | //echo "PARSE: \"$textContent\" -> "; 166 | $item->nodeValue = $this->parse_string($textContent, $this->data); 167 | //echo "\"" . $item->nodeValue . "\"\n"; 168 | } 169 | } 170 | } 171 | } 172 | 173 | function delete_from_manifest($filename){ 174 | $file_entries = $this->dom_elements2array( $this->dom->getElementsByTagName('file-entry') ); 175 | foreach ($file_entries as $file_entry){ 176 | if ( $file_entry->getAttribute('manifest:full-path') == $filename ){ 177 | $file_entry->parentNode->removeChildren( $file_entry ); 178 | } 179 | } 180 | } 181 | 182 | function write_manifest($files = array()){ 183 | $manifest = $this->dom->getElementsByTagName('manifest')->item(0); 184 | 185 | foreach ($files as $file){ 186 | $file_entry = $this->dom->createElement('manifest:file-entry'); 187 | 188 | $file_entry->setAttribute('manifest:full-path', $file['path']); 189 | $file_entry->setAttribute('manifest:media-type', $file['mime']); 190 | 191 | $manifest->appendChild($file_entry); 192 | } 193 | 194 | return $this->dom->saveXML(); 195 | } 196 | 197 | function read_meta() { 198 | $this->meta = array(); 199 | foreach ($this->dom->getElementsByTagName('user-defined') as $ud) { 200 | $pair = array( 201 | 'name' => $ud->getAttribute('meta:name'), 202 | 'value' => $ud->nodeValue, 203 | 'func' => 'unknown' 204 | ) 205 | ; 206 | 207 | if (strpos($pair['name'], 'SUM(') === 0) { 208 | $pair['func'] = 'sum'; 209 | list($one, $two) = explode('SUM(', $pair['name']); 210 | $prm_str = explode(')', $two); 211 | $params1 = explode(',', $prm_str[0]); 212 | $pair['params'] = array(); 213 | foreach ($params1 as $prm) { 214 | $param = ltrim(rtrim($prm)); 215 | if ($param) { 216 | $pair['params'][] = $param; 217 | } 218 | } 219 | } 220 | 221 | if (strpos($pair['name'], 'MAX(') === 0) { 222 | $pair['func'] = 'max'; 223 | list($one, $two) = explode('MAX(', $pair['name']); 224 | $prm_str = explode(')', $two); 225 | $params1 = explode(',', $prm_str[0]); 226 | $pair['params'] = array(); 227 | foreach ($params1 as $prm) { 228 | $param = ltrim(rtrim($prm)); 229 | if ($param) { 230 | $pair['params'][] = $param; 231 | } 232 | } 233 | } 234 | 235 | if (strpos($pair['name'], 'MIN(') === 0) { 236 | $pair['func'] = 'min'; 237 | list($one, $two) = explode('MIN(', $pair['name']); 238 | $prm_str = explode(')', $two); 239 | $params1 = explode(',', $prm_str[0]); 240 | $pair['params'] = array(); 241 | foreach ($params1 as $prm) { 242 | $param = ltrim(rtrim($prm)); 243 | if ($param) { 244 | $pair['params'][] = $param; 245 | } 246 | } 247 | } 248 | 249 | $this->meta[$ud->getAttribute('meta:name')] = $pair; 250 | } 251 | } 252 | 253 | function render_styles() { 254 | foreach ( 255 | $this->dom 256 | ->getElementsByTagName('master-styles')->item(0) 257 | ->getElementsByTagName('p') as $p) { 258 | $text = $p->nodeValue; 259 | 260 | if ($this->string_has_params($text)) { 261 | $p->nodeValue = $this->parse_string($text, $this->data); 262 | } 263 | } 264 | 265 | return $this->dom->saveXml(); 266 | } 267 | 268 | function ods_hide_draws($hide_draws) { 269 | $draws = $this->dom->getElementsByTagName('frame'); 270 | foreach ($draws as $draw) { 271 | if (in_array($draw->getAttribute('draw:name'), $hide_draws)) { 272 | $draw->parentNode->removeChild($draw); 273 | } 274 | } 275 | } 276 | 277 | /* 278 | analyze all draws and read descr conditions by data 279 | */ 280 | function ods_hide_draw_conditions() { 281 | $draws = $this->dom->getElementsByTagName('frame'); 282 | foreach ($draws as $draw) { 283 | $hide_image = false; 284 | 285 | $title = $draw->getElementsByTagName('title'); 286 | 287 | if ($title->length > 0 && $title->item(0)->nodeValue == 'conditions') { 288 | 289 | $desc = $draw->getElementsByTagName('desc'); 290 | if ($desc->length > 0 && $desc->item(0)->nodeValue) { 291 | $conditions = $desc->item(0)->nodeValue; 292 | 293 | $data = $this->data; 294 | $hide_image = eval("return " . $conditions . ";"); 295 | } 296 | } 297 | 298 | if ($hide_image) { 299 | $draw->parentNode->removeChild($draw); 300 | } 301 | } 302 | } 303 | 304 | function shell($commands = array()) { 305 | exec(join('; ', $commands), $out); 306 | return $out; 307 | } 308 | 309 | function ods_sheet($sheet_name) { 310 | 311 | $list = $this->dom->getElementsByTagName('table:table'); 312 | 313 | foreach ($list as $sheet) { 314 | 315 | if ( 316 | $sheet->getAttribute('table:name') == $sheet_name || 317 | $sheet->getAttribute('name') == $sheet_name || 318 | $sheet->getAttributeNS('table', 'name') == $sheet_name 319 | ) { 320 | return $sheet; 321 | } 322 | } 323 | return false; 324 | } 325 | 326 | function ods_sheet_add($sheet_name) { 327 | 328 | //create temporary sheet 329 | $sheet = $this->dom->createElement('table:table'); 330 | $sheet->setAttribute('table:name', $sheet_name); 331 | $sheet->setAttribute('name', $sheet_name); 332 | 333 | $this->dom 334 | ->getElementsByTagName('spreadsheet') 335 | ->item(0) 336 | ->appendChild($sheet); 337 | 338 | return $sheet; 339 | } 340 | 341 | function ods_debug($title, $value = '') { 342 | 343 | $row = $this->dom->createElement('table:table-row'); 344 | 345 | $p1 = $this->dom->createElement('text:p'); 346 | $p2 = $this->dom->createElement('text:p'); 347 | $p1->nodeValue = $title; 348 | $p2->nodeValue = $value; 349 | 350 | $cell1 = $this->dom->createElement('table:table-cell'); 351 | $cell2 = $this->dom->createElement('table:table-cell'); 352 | 353 | $cell1->appendChild($p1); 354 | $cell2->appendChild($p2); 355 | 356 | $row->appendChild($cell1); 357 | $row->appendChild($cell2); 358 | 359 | $this->ods_sheet('DEBUG')->appendChild($row); 360 | } 361 | 362 | /* 363 | * return ranges with level=1 364 | */ 365 | function ods_level1_ranges() { 366 | $level0ranges = array(); 367 | 368 | foreach ($this->schema['named-range'] as $range_name => $range){ 369 | if ($range['level'] == 1){ 370 | $level0ranges[] = $range_name; 371 | } 372 | } 373 | 374 | return $level0ranges; 375 | } 376 | 377 | function ods_analyze_ranges() { 378 | $nlist = array(); 379 | 380 | foreach ($this->dom->getElementsByTagName('named-range') as $named) { 381 | $range_id = 'range' . $this->last_id++; 382 | 383 | $named->setAttribute('id', $range_id); 384 | 385 | $range = compact('range_id') + array( 386 | 'name' => 387 | $named->getAttribute('table:name'), 388 | 'id' => $range_id, 389 | 'cell-range-address' => 390 | $named->getAttribute('table:cell-range-address'), 391 | 'range-usable-as' => 392 | explode(' ', $named->getAttribute('table:range-usable-as')), 393 | ); 394 | 395 | list($sh, $start, $end) = explode( 396 | '.$', str_replace( 397 | ':', '', $range['cell-range-address']) 398 | ); 399 | 400 | list($tmp, $range['start']) = explode('$', $start); 401 | list($tmp, $range['end']) = explode('$', $end); 402 | list($tmp, $range['sheet']) = explode('$', $sh); 403 | 404 | $range['length'] = $range['end'] - $range['start'] + 1; 405 | 406 | $range['children'] = array(); 407 | $range['parent'] = null; 408 | $range['template_rows'] = array(); //Array of row elements 409 | 410 | //repeat-row only 411 | if (in_array('repeat-row', $range['range-usable-as'])){ 412 | $nlist[$range['start']] = $range; 413 | } 414 | } 415 | 416 | //Sort ranges 417 | ksort($nlist); 418 | 419 | foreach ($nlist as $nkey => $n) { 420 | $this->schema['named-range'][$n['name']] = $n; 421 | } 422 | 423 | 424 | /* 425 | * counting level1 ranges 426 | */ 427 | 428 | foreach ($this->schema['named-range'] as $range_name => $range){ 429 | if (array_key_exists($range_name, $this->data) ){ 430 | $this->data[ "COUNT($range_name)" ] = count( $this->data[ $range_name ] ); 431 | 432 | 433 | } 434 | 435 | $this->data += $this->ods_aggregate_data($range_name, $this->data); 436 | } 437 | } 438 | 439 | function ods_analyze_images() { 440 | $this->dom->getElementsByTagName('table')->item(0) 441 | ->getElementsByTagName(''); 442 | } 443 | 444 | function ods_analyze_row_ranges($row) { 445 | 446 | $row_ranges = $this->row_ranges($row->getAttribute('number')); 447 | 448 | if ($row_ranges) { 449 | 450 | $from = $row->getAttribute('from'); 451 | 452 | //analyze ranges with parent<>child 453 | $level = count($row_ranges); 454 | 455 | $last_row_range = end($row_ranges); 456 | 457 | $row->setAttribute('range_name', $last_row_range); 458 | $row->setAttribute('range_level', $level); 459 | 460 | if ($from == $this->schema['named-range'][$last_row_range]['start']) { 461 | $row->setAttribute('range_start', $last_row_range); 462 | } 463 | 464 | $end_ranges = array(); 465 | 466 | foreach ($this->schema['named-range'] as $tmprange) { 467 | if ($from == $tmprange['end']) { 468 | $end_ranges[] = $tmprange['name']; 469 | } 470 | } 471 | 472 | if ($end_ranges) { 473 | $row->setAttribute('range_end', join(',', $end_ranges)); 474 | } 475 | 476 | //Insert all used ranges 477 | $row->setAttribute('ranges', join(',', $row_ranges)); 478 | 479 | $this->schema['named-range'][$last_row_range]['level'] = $level; 480 | if ($level > 1) { 481 | $range_parent = $row_ranges[$level - 2]; 482 | $this->schema['named-range'] 483 | [$last_row_range] 484 | ['parent'] = $range_parent; 485 | 486 | $row->setAttribute('range_parent', $range_parent); 487 | $this->schema['named-range'] 488 | [$range_parent] 489 | ['children'] 490 | [$last_row_range] = $last_row_range; 491 | 492 | } else { 493 | 494 | $this->schema['named-range'][$last_row_range]['parent'] = null; 495 | } 496 | 497 | 498 | foreach ($this->schema['named-range'] as $tmprange) { 499 | if (in_array($tmprange['name'], $row_ranges)) { 500 | $this->schema['named-range'] 501 | [$tmprange['name']] 502 | ['template_rows'] 503 | [] = $row; 504 | //$this->dom->saveXml($row); 505 | } 506 | } 507 | 508 | //analyze virtualFields, in office:annotations 509 | $office_annotationa = $row->getElementsByTagName('annotation'); 510 | if ($office_annotationa->length > 0) { 511 | $office_annotationa = $this->dom_elements2array($office_annotationa); 512 | foreach ($office_annotationa as $item){ 513 | $p = $item->getElementsByTagName('p')->item(0); 514 | $expression = $p->nodeValue; 515 | 516 | $fieldName = $item->parentNode->lastChild->nodeValue; //->firstChild->nodeValue; 517 | $expr = $this->ods_is_string_expression($expression) 518 | ? 'true' : 'false'; 519 | //print_r(compact('expression', 'fieldName', 'expr')); 520 | 521 | if ($this->ods_is_string_expression($expression)){ 522 | $this->schema['named-range'] 523 | [ $last_row_range ] 524 | ['virtualFields'] 525 | [ $fieldName ] = 526 | $expression; 527 | 528 | $item->parentNode->removeChild($item); 529 | } 530 | 531 | } 532 | } 533 | } //if $row_nages 534 | 535 | return $row_ranges; 536 | } 537 | 538 | //http://ru.stackoverflow.com/questions/454598/%D0%92%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B5 539 | //http://stackoverflow.com/questions/18880772/calculate-math-expression-from-a-string-using-eval 540 | private function parse_string_expression($string, $data = array()){ 541 | 542 | $string1 = $this->parse_string($string, $data); 543 | 544 | /* 545 | * check is string is contains ONLY DIGITS and 546 | * operations!, and not contain not parsed [param] strings parts 547 | */ 548 | 549 | try { 550 | $newfunc = create_function('', "return $string1;"); 551 | $val = $newfunc(); 552 | unset($newfunc); 553 | return $val; 554 | 555 | } catch (Exception $exc) { 556 | //echo $exc->getTraceAsString(); 557 | //function error 558 | return "#ERROR"; 559 | } 560 | } 561 | 562 | /* 563 | * "[Good.price] * [Good.count]" is ok 564 | */ 565 | private function ods_is_string_expression($string){ 566 | $string = str_replace(array( 567 | ' ', 568 | '+', 569 | '-', 570 | '*', 571 | '/', 572 | '(', 573 | ')' 574 | ), array( 575 | '', 576 | '
', 577 | '
', 578 | '
', 579 | '
', 580 | '
', 581 | '
', 582 | ), $string); 583 | $parts = explode('
', $string); 584 | return count($parts) > 1; 585 | 586 | } 587 | 588 | function ods_analyze() { 589 | //Read named ranges 590 | $this->schema = array(); 591 | 592 | $this->ods_analyze_ranges(); 593 | 594 | //Enumerate rows given row-repeated$this->schema['named-range'] 595 | $rows = $this->dom 596 | ->getElementsByTagName('table')->item(0) 597 | ->getElementsByTagName('table-row'); 598 | $number = 1; 599 | $this->schema['rows'] = array(); 600 | 601 | 602 | foreach ($rows as $row) { 603 | 604 | $repeated = $row->getAttribute('table:number-rows-repeated') 605 | ? : 1; 606 | 607 | $from = $number; 608 | //$row_id = "row" . $number; 609 | $to = $number + $repeated - 1; 610 | 611 | //mark row elements 612 | $row->setAttribute('from', $from); 613 | $row->setAttribute('to', $to); 614 | $row->setAttribute('number', $number); 615 | //$row->setAttribute('id', $row_id); 616 | 617 | $row_ranges = $this->ods_analyze_row_ranges($row); 618 | 619 | //cells 620 | $cells = $row->getElementsByTagName('table-cell'); 621 | $this->schema['rows'][] = compact('from', 'repeated', 'to', 'row_id') + 622 | array( 623 | 'ranges' => $row_ranges, 624 | 'cells_count' => $cells->length, 625 | ); 626 | 627 | //read cells 628 | 629 | 630 | 631 | $number += $repeated; 632 | } 633 | 634 | //virtual fields 635 | //$this->ods_populate_virtual_fields($range_name, $data) 636 | 637 | // 638 | 639 | foreach ($this->ods_level1_ranges() as $range_name){ 640 | $this->ods_populate_virtual_fields($range_name, $this->data); 641 | 642 | $aggregates = $this->ods_aggregate_data($range_name, $this->data); 643 | 644 | if ($aggregates){ 645 | foreach ($aggregates as $key => $val){ 646 | $this->data[$key] = $val; 647 | } 648 | } 649 | 650 | 651 | 652 | } 653 | 654 | //print_r($this->data); 655 | //render rows - separate!! cycle 656 | foreach ($rows as $row) { 657 | $this->ods_render_row($row, $this->data); 658 | } 659 | } 660 | 661 | function ods_clean_data(&$data){ 662 | foreach ($data as $key => $datum){ 663 | if (is_array($data[$key])){ 664 | if (array_key_exists('__rows__', $datum[$key]) ){ 665 | unset($data[$key]['__rows__']); 666 | } 667 | } 668 | } 669 | } 670 | 671 | /* 672 | * Render all cell by cell 673 | */ 674 | function ods_render_row($row, $data = array()) { 675 | $cells = $row->getElementsByTagName('table-cell'); 676 | 677 | foreach ($cells as $cell) { 678 | $value_type = $cell 679 | ->getAttribute('office:value-type'); 680 | 681 | //get text data 682 | $p1 = $cell 683 | ->getElementsByTagName('p'); 684 | 685 | foreach ($p1 as $p) { 686 | 687 | $orig_cell_text = $p->nodeValue; 688 | 689 | $data_val = false; 690 | 691 | if (!empty($orig_cell_text)) { 692 | if ($this->string_has_params($orig_cell_text)) { 693 | 694 | if ($this->parse_string_is_once_param($orig_cell_text)) { 695 | $param_key = $this->parse_string_extract_param($orig_cell_text); 696 | 697 | if ($this->parse_param_exists($param_key, $data)) { 698 | 699 | $data_val = $this->parse_param_value( 700 | $param_key, $data 701 | ); 702 | $this->ods_cell_set_val($cell, $p, $data_val, array()); 703 | } 704 | } else { 705 | $p->nodeValue = $this->parse_string($orig_cell_text, $data); 706 | } 707 | } 708 | } 709 | } 710 | 711 | $this->ods_render_cell_images($cell, $data); 712 | 713 | } 714 | return $row; 715 | } 716 | 717 | private function ods_render_cell_images($cell, $data){ 718 | //analyze images 719 | $frames = $this->dom_elements2array( $cell->getElementsByTagname('frame') ); 720 | if ($frames){ 721 | foreach ($frames as $frame){ 722 | $frame_name = $frame->getAttribute('draw:name'); 723 | $image_name = false; 724 | //print_r(compact('frame_name')); 725 | if ($this->parse_string_is_once_param($frame_name)){ 726 | $param_key = $this->parse_string_extract_param($frame_name); 727 | 728 | if ($this->parse_param_exists($param_key, $data)){ 729 | $image_name = $this->parse_param_value($param_key, $data); 730 | //var_dump($val); 731 | if (is_string($image_name)){ 732 | $image_name = 'Pictures/' . $image_name; 733 | $draw = $frame->getElementsByTagName('image')->item(0); 734 | $draw->setAttribute('xlink:href', $image_name); 735 | 736 | 737 | } else { 738 | //remove draw element 739 | $frame->parentNode->removeChild($frame); 740 | } 741 | } 742 | 743 | } else { 744 | $draw = $frame->getElementsByTagName('image')->item(0); 745 | //print_r(compact('draw')); 746 | if ($draw){ 747 | 748 | $image_name = $draw->getAttribute('xlink:href'); 749 | } 750 | } 751 | 752 | 753 | if ($image_name){ 754 | //print_r(compact('image_name')); 755 | $this->used_images[$image_name] = $image_name; 756 | } 757 | 758 | /* 759 | * path frame style to size:protect 760 | */ 761 | 762 | $style_name = $frame->getAttribute('draw:style-name'); 763 | 764 | if (!array_key_exists('patched_styles', $this->schema)){ 765 | $this->schema['patched_styles'] = array(); 766 | } 767 | 768 | if (!in_array($style_name, $this->schema['patched_styles']) ){ 769 | 770 | foreach($this->dom->getElementsByTagName('style') as $style){ 771 | if ($style->getAttribute('style:name') == $style_name){ 772 | 773 | $gr = $style->getElementsByTagName('graphic-properties') 774 | ->item(0); 775 | //print_r($gr); 776 | $gr->setAttribute('style:protect', 'size'); 777 | 778 | $this->schema['patched_styles'][ $style_name ] = 779 | $style_name; 780 | } 781 | } 782 | } 783 | } 784 | } 785 | } 786 | 787 | function dom_elements2array($elements){ 788 | $result = array(); 789 | foreach ($elements as $el){ 790 | $result[] = $el; 791 | } 792 | 793 | return $result; 794 | } 795 | 796 | function ods_cell_set_val($cell, $p, $val, $options = array()) { 797 | //check a data type and change a cell type with from 798 | 799 | if (is_numeric($val)) { 800 | $string_val = (string) $val; 801 | $p->nodeValue = str_replace('.', ',', $string_val); 802 | $cell->setAttribute('office:value-type', 'float'); 803 | $cell->setAttribute('calcext:value-type', 'float'); 804 | $cell->setAttribute('office:value', $val); 805 | } else { 806 | $p->nodeValue = $val; 807 | } 808 | } 809 | 810 | function ods_tmp_sheet_empty() { 811 | foreach ($this->tmp_sheet->getElementsByTagName('table-row') as $row) { 812 | $row->parentNode->removeChild($row); 813 | } 814 | } 815 | 816 | function ods_aggregate_data($range_name, $data) { 817 | $result = array(); 818 | 819 | foreach ($this->meta as $name => $options) { 820 | //sums 821 | if ($options['func'] == 'sum' && $options['value'] == $range_name) { 822 | 823 | if (array_key_exists($range_name, $data)) { 824 | 825 | $result[$name] = 0; 826 | foreach ($data[$range_name] as $datum) { 827 | 828 | $data_key = $options['params'][0]; 829 | 830 | if ($this->parse_param_exists($options['params'][0], $datum)) { 831 | $val = $this->parse_param_value($options['params'][0], $datum); 832 | if (is_numeric($val)) { 833 | $result[$name] += $val; 834 | } 835 | } 836 | } 837 | } 838 | } 839 | } 840 | if ($result) { 841 | // print_r(compact('result', 'range_name', 'data')); 842 | } 843 | return $result; 844 | } 845 | 846 | //Recursive function!!!! 847 | function ods_render_range($range_name, &$data) { 848 | 849 | $range = $this->schema['named-range'][$range_name]; 850 | $result_render_rows = array(); 851 | 852 | if (array_key_exists($range_name, $data) && is_array($data[$range_name])) { 853 | //render objects, cycling data 854 | foreach ($data[$range_name] as $i => $datum) { 855 | 856 | 857 | 858 | //Aggregate values from children 859 | if (!empty($range['children'])) { 860 | 861 | foreach ($range['children'] as $children_range) { 862 | 863 | $children_range_object = $this->schema['named-range'][$children_range]; 864 | 865 | $data[$range_name][$i]["COUNT($children_range)"] = 0; 866 | if (array_key_exists($children_range, $data[$range_name][$i])) { 867 | $data[$range_name][$i]["COUNT($children_range)"] = 868 | count($data[$range_name][$i] 869 | [$children_range]); 870 | } 871 | 872 | // 873 | $this->ods_populate_virtual_fields($children_range, $data[$range_name][$i]); 874 | 875 | //stat from document_options, functions 876 | $data[$range_name][$i] += $this->ods_aggregate_data( 877 | $children_range, 878 | $data[$range_name][$i] 879 | ); 880 | } 881 | } 882 | 883 | //initialize rendered rows of each datum object 884 | $data[$range_name][$i]['__rows__'] = array(); 885 | 886 | //local [position], for each data object only in the range 887 | $data[$range_name][$i]['position'] = $i + 1; 888 | 889 | foreach ($this->schema['named-range'][$range_name]['template_rows'] as $static_row) { 890 | $row = $static_row->cloneNode(true); 891 | /* 892 | * Check if range is other 893 | */ 894 | if ($row->getAttribute('range_name') == $range_name && 895 | !$row->hasAttribute('done') 896 | ) { 897 | //Mark own rows as done 898 | $row->setAttribute('done', 'true'); 899 | 900 | //Replace rendered row object in the source rows 901 | $this->ods_render_row( 902 | $row, 903 | $data[$range_name][$i] 904 | ); 905 | } 906 | 907 | $data[$range_name][$i]['__rows__'][] = $row; 908 | 909 | }//foreach rows 910 | //render children 911 | 912 | /*********** Populate render children arrays to local array */ 913 | if (!empty($range['children'])) { 914 | 915 | $data[$range_name][$i]['__children_rows__'] = array(); 916 | 917 | $children_results = array(); 918 | $children_area = array(); 919 | foreach ($range['children'] as $children_range) { 920 | $children_area[$children_range] = array( 921 | 'start' => null, 922 | 'end' => null 923 | ); 924 | $data[$range_name][$i]['__children_rows__'][ $children_range ] = array(); 925 | if (array_key_exists($children_range, $datum)) { 926 | 927 | $data[$range_name][$i]['__children_rows__'][ $children_range ] = 928 | $this->ods_render_range( 929 | $children_range, $data[$range_name][$i] 930 | ); 931 | } 932 | 933 | //empty children template area 934 | //mark children area 935 | foreach ($data[$range_name][$i]['__rows__'] as $j => $row){ 936 | if ( 937 | $row->getAttribute('range_name') == $children_range && 938 | $row->hasAttribute('range_start') 939 | 940 | ){ 941 | $children_area[$children_range]['start'] = $j ; 942 | } 943 | if ( 944 | $row->hasAttribute('range_end') 945 | && in_array($children_range, explode(',',$row->getAttribute('range_end') ) ) 946 | ){ 947 | $children_area[$children_range]['end'] = $j ; 948 | $children_area[$children_range]['length'] = 949 | $children_area[$children_range]['end'] - 950 | $children_area[$children_range]['start'] +1; 951 | 952 | } 953 | } 954 | 955 | if ( isset($children_area[$children_range]['start']) && 956 | isset($children_area[$children_range]['end']) 957 | 958 | ){ 959 | array_splice( 960 | $data[$range_name][$i]['__rows__'], 961 | $children_area[$children_range]['start'], 962 | $children_area[$children_range]['length'], 963 | $data[$range_name][$i]['__children_rows__'][ $children_range ] 964 | ); 965 | 966 | } 967 | } 968 | 969 | //insert children render rows in the source array $rows 970 | //merge 971 | //mark children area 972 | 973 | }//childrens 974 | $result_render_rows = array_merge($result_render_rows, $data[$range_name][$i]['__rows__']); 975 | } //foreach data 976 | /* 977 | * render 978 | */ 979 | } //if 980 | return $result_render_rows; 981 | } 982 | 983 | function ods_range($range_name){ 984 | return $this->schema['named-range'][$range_name]; 985 | } 986 | 987 | function ods_populate_virtual_fields($range_name, &$data){ 988 | 989 | //virtual fields 990 | $range = $this->ods_range($range_name); 991 | 992 | 993 | if (!empty($range['virtualFields'])){ 994 | foreach ($range['virtualFields'] as $virtualField => $xpression){ 995 | if ( $this->parse_string_is_once_param($virtualField) ){ 996 | $param_key = 997 | $this->parse_string_extract_param($virtualField); 998 | 999 | //parse expression 1000 | 1001 | //populate exprassion virtual fields on on children data 1002 | if (array_key_exists($range_name, $data)){ 1003 | foreach ($data[$range_name] as $k => $datum){ 1004 | 1005 | 1006 | $val = $this->parse_string_expression( 1007 | $xpression, 1008 | $data[$range_name][$k]); 1009 | 1010 | $this->parse_create_param( 1011 | $param_key, 1012 | $data[$range_name][$k], 1013 | $val 1014 | ); 1015 | } 1016 | } 1017 | } 1018 | } 1019 | } 1020 | 1021 | } 1022 | 1023 | function ods_analyze_data() { 1024 | //Start for top level ranges 1025 | $results_level1 = array(); 1026 | foreach ($this->schema['named-range'] as $range) { 1027 | $range_name = $range['name']; 1028 | switch ($range['level']) { 1029 | case 1: 1030 | if (!empty($this->data[$range_name])) { 1031 | // get all rows elements on this range 1032 | //foreach($this->data[ $range_name ] as $data_level1){ 1033 | //row cycle 1034 | $results_level1[$range_name]['rows'] = $this->ods_render_range($range_name, $this->data); 1035 | } 1036 | break; 1037 | } 1038 | } 1039 | if ($results_level1){ 1040 | $sheet = $this->dom 1041 | ->getElementsByTagName('table') 1042 | ->item(0); 1043 | 1044 | foreach ($results_level1 as $range_name => $result){ 1045 | 1046 | //delete template on the sheet 1047 | $in_range = false; 1048 | foreach ($sheet->getElementsByTagName('table-row') as $row){ 1049 | if ($row->hasAttribute('range_name') 1050 | && $row->getAttribute('range_name') == $range_name 1051 | && $row->hasAttribute('range_start') 1052 | 1053 | ){ 1054 | $results_level1[ $range_name ]['start'] = $row; 1055 | $in_range = true; 1056 | } 1057 | 1058 | if ($in_range){ 1059 | $row->setAttribute('remove_me_please', 'yes'); 1060 | } 1061 | 1062 | if ($row->hasAttribute('range_name') 1063 | 1064 | && $row->hasAttribute('range_end') 1065 | && in_array($range_name, explode(',', $row->getAttribute('range_end')) ) 1066 | ){ 1067 | $results_level1[ $range_name ]['end'] = $row; 1068 | $in_range = false; 1069 | } 1070 | 1071 | } 1072 | 1073 | //insert data after end 1074 | foreach ( $results_level1[$range_name]['rows'] as $row ){ 1075 | $results_level1[ $range_name ]['start'] 1076 | ->parentNode 1077 | ->insertBefore($row, $results_level1[ $range_name ]['start']); 1078 | } 1079 | 1080 | } 1081 | 1082 | //clear to_empty rows 1083 | $remove_rows = array(); 1084 | 1085 | foreach ($sheet->getElementsByTagName('table-row') as $row){ 1086 | if ($row->hasAttribute('remove_me_please')){ 1087 | $remove_rows[] = $row; 1088 | } 1089 | } 1090 | 1091 | foreach ($remove_rows as $row){ 1092 | $row->parentNode->removeChild($row); 1093 | } 1094 | 1095 | //after this - rows count is CONSTANT 1096 | if (!empty( $this->hasFormula )){ 1097 | $this->ods_recover_formula(); 1098 | } 1099 | 1100 | } 1101 | } 1102 | 1103 | /* 1104 | * Recover cells cover 1105 | */ 1106 | function ods_recover_formula(){ 1107 | /* 1108 | * mark numbers of row 1109 | */ 1110 | 1111 | /* 1112 | * get cells with formula 1113 | */ 1114 | 1115 | /* 1116 | * check attribute 1117 | * table:formula="of:=[.E10]*[.D10]" 1118 | * ^^ ^^ 1119 | * This row number need to check 1120 | */ 1121 | } 1122 | 1123 | function add_file($filename, $zip, $zip_dest_dir = 'Pictures'){ 1124 | $added_files = array(); 1125 | if (file_exists($filename)){ 1126 | $added_files[] = array( 1127 | 'path' => $zip_dest_dir . '/' . basename($filename), 1128 | 'mime' => mime_content_type($filename) 1129 | ); 1130 | 1131 | $zip_path = $zip_dest_dir . '/' . basename($filename); 1132 | 1133 | if (in_array($zip_path, $this->used_images)){ 1134 | $zip->addFile($filename, $zip_path); 1135 | } 1136 | } 1137 | 1138 | return $added_files; 1139 | } 1140 | 1141 | //add custom dir to zip 1142 | function dir_to_zip($zip, $source_dir, $zip_dest_dir){ 1143 | $added_files = array(); 1144 | $files = new RecursiveIteratorIterator( 1145 | new RecursiveDirectoryIterator($source_dir), 1146 | RecursiveIteratorIterator::LEAVES_ONLY 1147 | ); 1148 | 1149 | foreach ($files as $name => $file) 1150 | { 1151 | 1152 | // Skip directories (they would be added automatically) 1153 | if (!$file->isDir()) 1154 | { 1155 | 1156 | 1157 | // Get real and relative path for current file 1158 | $filePath = $file->getRealPath(); 1159 | $relativePath = substr($filePath, strlen($source_dir) + 1); 1160 | 1161 | $added_files[] = array( 1162 | 'path' => $zip_dest_dir . '/' . basename($name), 1163 | 'mime' => mime_content_type($filePath) 1164 | ); 1165 | //echo print_r( compact('name', 'file', 'filePath', 'relativePath') ); 1166 | // Add current file to archive 1167 | $file_name = $zip_dest_dir . '/' . basename($name); 1168 | 1169 | //add files only was used images in sheet 1170 | if (in_array($file_name, $this->used_images)){ 1171 | $zip->addFile($filePath, $file_name); 1172 | } 1173 | 1174 | } 1175 | } 1176 | // print_r(compact('added_files')); 1177 | return $added_files; 1178 | } 1179 | 1180 | //return true if $this->params[before] and [after] exists 1181 | function string_has_params($string) { 1182 | if (strpos($string, $this->parse['before']) !== false) { 1183 | if (strpos($string, $this->parse['after']) !== false) { 1184 | return strpos($string, $this->parse['after']) > 1185 | strpos($string, $this->parse['before']); 1186 | } 1187 | } 1188 | 1189 | return false; 1190 | } 1191 | 1192 | 1193 | function parse_create_param($param_key, &$data, $value = null){ 1194 | $chains = $this->parse_param_chain($param_key); 1195 | switch (count($chains)) { 1196 | case 1: 1197 | $data[$param_key] = $value; 1198 | 1199 | break; 1200 | case 2: 1201 | $data 1202 | [$chains[0]] 1203 | [$chains[1]] 1204 | = $value; 1205 | break; 1206 | case 3: 1207 | $data 1208 | [$chains[0]] 1209 | [$chains[1]] 1210 | [$chains[2]] 1211 | = $value; 1212 | break; 1213 | case 4: 1214 | $data 1215 | [$chains[0]] 1216 | [$chains[1]] 1217 | [$chains[2]] 1218 | [$chains[3]] 1219 | = $value; 1220 | break; 1221 | 1222 | default: 1223 | break; 1224 | } 1225 | } 1226 | 1227 | /* 1228 | parse_string_extract_param('[Model.name]'); // 'Model.name' 1229 | */ 1230 | function parse_string_extract_param($string) { 1231 | if ($this->parse_string_is_once_param($string)) { 1232 | return 1233 | substr( 1234 | $string, +strlen($this->parse['before']), -strlen($this->parse['after']) 1235 | ); 1236 | } 1237 | return false; 1238 | } 1239 | 1240 | /* 1241 | if string is '[Model.name]' then true 1242 | if string = 'Welcome to [City.name]' then false 1243 | */ 1244 | function parse_string_is_once_param($string) { 1245 | if ( 1246 | substr($string, 0, strlen($this->parse['before'])) == $this->parse['before'] && 1247 | substr($string, -strlen($this->parse['after']), strlen($this->parse['after'])) == $this->parse['after']) { 1248 | return 1249 | substr_count($string, $this->parse['before']) == 1 && 1250 | substr_count($string, $this->parse['after']) == 1; 1251 | } 1252 | 1253 | return false; 1254 | } 1255 | 1256 | /* 1257 | $param_key = 'Model.name'; 1258 | $data = array( 1259 | 'Model' => array( 1260 | 'name' => 'Sok', 1261 | 'disabled' => false, 1262 | 'stored' => null 1263 | ) 1264 | ); 1265 | parse_param_value('Model.name', $data); // 'Sok' 1266 | parse_param_value('Model.too_key', $data); //null 1267 | */ 1268 | 1269 | function parse_param_value($param_key, $data) { 1270 | if ($this->parse_param_exists($param_key, $data)) { 1271 | $chains = $this->parse_param_chain($param_key); 1272 | 1273 | switch (count($chains)) { 1274 | case 1: 1275 | return $data[$param_key]; 1276 | break; 1277 | case 2: 1278 | return 1279 | $data 1280 | [$chains[0]] 1281 | [$chains[1]]; 1282 | break; 1283 | case 3: 1284 | return 1285 | $data 1286 | [$chains[0]] 1287 | [$chains[1]] 1288 | [$chains[2]]; 1289 | break; 1290 | case 4: 1291 | return 1292 | $data 1293 | [$chains[0]] 1294 | [$chains[1]] 1295 | [$chains[2]] 1296 | [$chains[3]]; 1297 | break; 1298 | } 1299 | } else { 1300 | 1301 | } 1302 | 1303 | return null; 1304 | } 1305 | 1306 | /* 1307 | Check a key exists 1308 | parse_param_exists('Model.Model2.not_key', $data); //return false 1309 | parse_param_exists('Model.Model2.valid_key', $data); //return true 1310 | */ 1311 | function parse_param_exists($param_key, $data, $debug = false) { 1312 | 1313 | 1314 | if ($debug) { 1315 | echo __FUNCTION__ . " "; 1316 | print_r(compact('param_key', 'data')); 1317 | } 1318 | $chain = $this->parse_param_chain($param_key); 1319 | switch (count($chain)) { 1320 | case 1: 1321 | return array_key_exists($param_key, $data); 1322 | break; 1323 | case 2: 1324 | return 1325 | array_key_exists($chain[0], $data) && 1326 | array_key_exists($chain[1], $data[$chain[0]]); 1327 | break; 1328 | case 3: 1329 | return 1330 | array_key_exists($chain[0], $data) && 1331 | array_key_exists($chain[1], $data[$chain[0]]) && 1332 | array_key_exists( 1333 | $chain[2], $data[$chain[0]][$chain[1]]); 1334 | break; 1335 | case 4: 1336 | return 1337 | array_key_exists($chain[0], $data) && 1338 | array_key_exists($chain[1], $data[$chain[0]]) && 1339 | array_key_exists( 1340 | $chain[2], $data[$chain[0]][$chain[1]]) && 1341 | array_key_exists( 1342 | $chain[3], $data[$chain[0]][$chain[1]][$chain[2]]); 1343 | break; 1344 | } 1345 | return false; 1346 | } 1347 | 1348 | function parse_param_chain($param_key) { 1349 | 1350 | //check reserved words-functions 1351 | if (strpos($param_key, '(') !== false) { 1352 | return array($param_key); 1353 | } 1354 | 1355 | return explode($this->parse['separator'], $param_key); 1356 | } 1357 | 1358 | //Get string into params array 1359 | function parse_string($string, $data = array(), $options = array()) { 1360 | 1361 | if (!$this->string_has_params($string)) { 1362 | return $string; 1363 | } 1364 | 1365 | //check if string is once param 1366 | if ($this->parse_string_is_once_param($string)) { 1367 | $param_key = $this->parse_string_extract_param($string); 1368 | //print_r(compact('param_key')); 1369 | if ($this->parse_param_exists($param_key, $data)) { 1370 | return $this->parse_param_value($param_key, $data); 1371 | } 1372 | return $string; //param_key not exists, return string as is 1373 | } 1374 | 1375 | $string1 = str_replace( 1376 | array( 1377 | $this->parse['before'], 1378 | $this->parse['after']), array( 1379 | '!-=0=-!' . $this->parse['before'], 1380 | $this->parse['after'] . '!-=0=-!'), $string); 1381 | 1382 | $parsed = explode('!-=0=-!', $string1); 1383 | $finded = array(); 1384 | foreach ($parsed as $ix => $item) { 1385 | if ($item) { 1386 | if ($this->parse_string_is_once_param($item)) { 1387 | $param_key = $this->parse_string_extract_param($item); 1388 | 1389 | if ($this->parse_param_exists($param_key, $data)) { 1390 | $parsed[$ix] = $this->parse_param_value($param_key, $data); 1391 | } 1392 | } 1393 | } 1394 | } 1395 | return join($parsed); 1396 | } 1397 | 1398 | private function row_ranges($row_number) { 1399 | $ranges = array(); 1400 | foreach ($this->schema['named-range'] as $range) { 1401 | if ($this->in_range($row_number, $range['name'])) { 1402 | $ranges[] = $range['name']; 1403 | } 1404 | } 1405 | return $ranges? : false; 1406 | } 1407 | 1408 | private function in_range($row_number, $range_name) { 1409 | 1410 | $range = $this->schema['named-range'][$range_name]; 1411 | if ($row_number >= $range['start'] && 1412 | $row_number <= $range['end'] 1413 | ) { 1414 | return true; 1415 | } 1416 | return false; 1417 | } 1418 | 1419 | } 1420 | --------------------------------------------------------------------------------