Render Excel Spreadsheet in IE
A while back we had a requirement to generate a lot of heavily formatted Excel type reports out of our application. The users also wanted this integrated seamlessly into the application so that when the report was run, it displayed in the same Internet Explorer window. After a bit of research and plenty of goggling, we came up with this solution (this demo uses a copy of the emp table from the Scott schema):
Appologies in advance if the SQL / PL/SQL or HTML format gets a bit messed up but it should compile ok.
N.b. I must first point out that this only works in Office 95/2003 and IE. Firefox will generate the report but opens Excel to do so. Please take any of this code and extend it to support Office 2007 and Firefox. It is only meant as a guide / proof of concept.
Credit for this one goes to my colleague David Blake as he did the lifting on this one.
2. Create your CSS to format the report
3. Create your Report. Note that you build up your reports using an HTML Table row / cell layout format
4. Run your Report
Issue a public grant on the report package and then enter the following url (note that any requests going via mod_plsql will be fine here. I used the apex dad as it is the simplest way to demo)
http://localhost:7777/pls/apex/duncan.report1
Hope that all makes sense
Appologies in advance if the SQL / PL/SQL or HTML format gets a bit messed up but it should compile ok.
N.b. I must first point out that this only works in Office 95/2003 and IE. Firefox will generate the report but opens Excel to do so. Please take any of this code and extend it to support Office 2007 and Firefox. It is only meant as a guide / proof of concept.
Credit for this one goes to my colleague David Blake as he did the lifting on this one.
- Create this package (I create this in its own schema and have a public execute but creating it in the schema you are working is fine)
CREATE OR REPLACE PACKAGE excel_lib IS
PROCEDURE create_column (
pv_span_i IN VARCHAR2 DEFAULT '1', pv_style_i IN VARCHAR2);
PROCEDURE create_cell ( pv_class_i IN VARCHAR2 DEFAULT NULL, pv_align_i IN VARCHAR2 DEFAULT NULL, pv_colspan_i IN VARCHAR2 DEFAULT NULL, pv_rowspan_i IN VARCHAR2 DEFAULT NULL, pv_value_i IN VARCHAR2 DEFAULT NULL, pv_value_number_i IN NUMBER DEFAULT NULL, pv_valign_i IN VARCHAR2 DEFAULT NULL );
PROCEDURE create_file_header ( pv_sheetname_i IN VARCHAR2, pv_send_to_excel_i IN BOOLEAN DEFAULT TRUE );
PROCEDURE new_row ( pv_height_i IN VARCHAR2 DEFAULT NULL, pv_style_i IN VARCHAR2 DEFAULT NULL );
PROCEDURE end_row; PROCEDURE end_file; END excel_lib; / CREATE OR REPLACE PACKAGE BODY excel_lib IS PROCEDURE create_column (pv_span_i IN VARCHAR2 DEFAULT '1', pv_style_i IN VARCHAR2) IS BEGIN HTP.p ('<col style="' || pv_style_i || '" span="' || pv_span_i || '">'); END create_column; PROCEDURE create_cell ( pv_class_i IN VARCHAR2 DEFAULT NULL, pv_align_i IN VARCHAR2 DEFAULT NULL, pv_colspan_i IN VARCHAR2 DEFAULT NULL, pv_rowspan_i IN VARCHAR2 DEFAULT NULL, pv_value_i IN VARCHAR2 DEFAULT NULL, pv_value_number_i IN NUMBER DEFAULT NULL, pv_valign_i IN VARCHAR2 DEFAULT NULL ) IS lv_html_string VARCHAR2 (500) DEFAULT '<td'; BEGIN -- Add class attribute IF pv_class_i IS NOT NULL THEN lv_html_string := lv_html_string || ' class="' || pv_class_i || '"'; END IF; -- Add align attribute IF pv_align_i IS NOT NULL THEN lv_html_string := lv_html_string || ' align="' || pv_align_i || '"'; END IF; -- Add x:num attribute IF pv_value_number_i IS NOT NULL THEN lv_html_string := lv_html_string || ' x:num="' || TO_CHAR (pv_value_number_i) || '"'; END IF; -- Add colspan attribute IF pv_colspan_i IS NOT NULL THEN lv_html_string := lv_html_string || ' colspan="' || TO_CHAR (pv_colspan_i) || '"'; END IF; IF pv_rowspan_i IS NOT NULL THEN lv_html_string := lv_html_string || ' rowspan="' || TO_CHAR (pv_rowspan_i) || '"'; END IF; IF pv_valign_i IS NOT NULL THEN lv_html_string := lv_html_string || ' valign="' || TO_CHAR (pv_valign_i) || '"'; END IF; -- Finish <td> tag lv_html_string := lv_html_string || '>' || REPLACE (SUBSTR (pv_value_i, 1, LENGTH (pv_value_i) - LENGTH (LTRIM (pv_value_i)) ), ' ', ' ' ) || LTRIM (pv_value_i) || '</td>'; -- write out the cell details to the page HTP.p (lv_html_string); END create_cell; PROCEDURE new_row ( pv_height_i IN VARCHAR2 DEFAULT NULL, pv_style_i IN VARCHAR2 DEFAULT NULL ) IS BEGIN IF pv_height_i IS NULL AND pv_style_i IS NULL THEN HTP.p ('<tr>'); ELSE HTP.p ('<tr height="' || pv_height_i || '" style="' || pv_style_i || '">'); END IF; END new_row; PROCEDURE end_row IS BEGIN HTP.p ('</tr>'); END end_row; PROCEDURE create_file_header ( pv_sheetname_i IN VARCHAR2, pv_send_to_excel_i IN BOOLEAN DEFAULT TRUE ) IS BEGIN IF pv_send_to_excel_i THEN OWA_UTIL.mime_header ('application/vnd.ms-excel'); END IF; HTP.p ( ' <html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"> <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> <meta NAME=ProgId content=Excel.Sheet> <meta NAME=Generator content="Microsoft Excel 9"> <style> <!--table {mso-displayed-decimal-separator:"\."; mso-displayed-thousand-separator:"\,"; font-family:Arial, sans-serif; font-size:9pt; color:black;}'); htp.p ('@page {margin:1.0in .75in 1.0in .75in; mso-header-margin:.5in; mso-footer-margin:.5in;} --> </style> <!--[if gte mso 9]><xml> <x:ExcelWorkbook> <x:ExcelWorksheets> <x:ExcelWorksheet> <x:Name>' || pv_sheetname_i || TO_CHAR (SYSDATE, 'DD-MM-YYYY HH24.MI') || '</x:Name> <x:WorksheetOptions> <x:Zoom>75</x:Zoom> <x:Selected/> <x:ProtectContents>False</x:ProtectContents> <x:ProtectObjects>False</x:ProtectObjects> <x:ProtectScenarios>False</x:ProtectScenarios> </x:WorksheetOptions> </x:ExcelWorksheet> </x:ExcelWorksheets> <x:WindowHeight>15210</x:WindowHeight> <x:WindowWidth>21900</x:WindowWidth> <x:WindowTopX>480</x:WindowTopX> <x:WindowTopY>120</x:WindowTopY> <x:ProtectStructure>False</x:ProtectStructure> <x:ProtectWindows>False</x:ProtectWindows> </x:ExcelWorkbook> </xml><![endif]--> <title>' || pv_sheetname_i || TO_CHAR (SYSDATE, 'DD-MM-YYYY HH24.MI') || '</title> </head> <BODY LINK=blue vlink=purple> <TABLE x:str border=0 cellpadding=0 cellspacing=0 style="border-collapse:collapse;table-layout:fixed">' ); END create_file_header; PROCEDURE end_file IS BEGIN HTP.p ('</table></body></html>'); END end_file; END Excel_Lib; /
2. Create your CSS to format the report
CREATE OR REPLACE PROCEDURE report_css AS BEGIN htp.p ('.xlBlue {vertical-align:middle; border:.5pt solid black; background:aqua; font-weight:700;} .xlSilver {vertical-align:middle; border:.5pt solid black; background:silver; font-weight:700;} .xlPlain {mso-number-format:"\#\,\#\#0\.00\;\\\(\#\,\#\#0\.00\\\)"; vertical-align:top;} .xlText {mso-number-format:"\@"; vertical-align:top;} .xlYellow {mso-number-format:"\#\,\#\#0\.00\;\\\(\#\,\#\#0\.00\\\)"; background:yellow; font-weight:700;} .xlHighlightedNumber {mso-number-format:"\#\,\#\#0\.00\;\\\(\#\,\#\#0\.00\\\)"; background:#FF8080;} .xlBold {mso-number-format:"\#\,\#\#0\.00\;\\\(\#\,\#\#0\.00\\\)"; font-weight:700;} .xlUnderline {mso-number-format:"\#\,\#\#0\.00\;\\\(\#\,\#\#0\.00\\\)"; font-weight:700; text-decoration:underline; text-underline-style:single; }'); END report_css; /
3. Create your Report. Note that you build up your reports using an HTML Table row / cell layout format
CREATE OR REPLACE PROCEDURE report1 IS CURSOR c_emp IS SELECT * FROM emp; BEGIN -- Writes the MIME type etc to allow excel to run in IE excel_lib.create_file_header ('My Report '); report_css; -- Writes your custom CSS to the page excel_lib.new_row (); excel_lib.create_cell (pv_value_i => 'Emp No', pv_class_i => 'xlYellow', pv_align_i => 'center' ); excel_lib.create_cell (pv_value_i => 'Emp Name', pv_class_i => 'xlYellow', pv_align_i => 'center' ); excel_lib.create_cell (pv_value_i => 'Job', pv_class_i => 'xlYellow', pv_align_i => 'center' ); excel_lib.create_cell (pv_value_i => 'Manager', pv_class_i => 'xlYellow', pv_align_i => 'center' ); excel_lib.create_cell (pv_value_i => 'Hire Date', pv_class_i => 'xlYellow', pv_align_i => 'center' ); excel_lib.create_cell (pv_value_i => 'Sal', pv_class_i => 'xlYellow', pv_align_i => 'center' ); excel_lib.create_cell (pv_value_i => 'Commission', pv_class_i => 'xlYellow', pv_align_i => 'center' ); excel_lib.create_cell (pv_value_i => 'Dept No', pv_class_i => 'xlYellow', pv_align_i => 'center' ); excel_lib.end_row; FOR x IN c_emp LOOP excel_lib.new_row (); excel_lib.create_cell (pv_value_i => TO_NUMBER (x.empno), pv_align_i => 'right' ); excel_lib.create_cell (pv_value_i => x.ename); excel_lib.create_cell (pv_value_i => x.job); excel_lib.create_cell (pv_value_i => x.mgr); excel_lib.create_cell (pv_value_i => TO_CHAR (x.hiredate, 'DD-Mon-RRRR' ), pv_align_i => 'right' ); IF x.sal >= 3000 THEN excel_lib.create_cell (pv_value_i => TO_NUMBER (x.sal), pv_align_i => 'right', pv_class_i => 'xlBlue' ); ELSE excel_lib.create_cell (pv_value_i => TO_NUMBER (x.sal), pv_align_i => 'right' ); END IF; IF x.comm IS NULL THEN excel_lib.create_cell (pv_value_i => ' '); ELSE excel_lib.create_cell (pv_value_i => TO_NUMBER (x.comm), pv_align_i => 'right', pv_class_i => 'xlHighlightedNumber' ); END IF; excel_lib.create_cell (pv_value_i => TO_NUMBER (x.deptno), pv_align_i => 'right' ); excel_lib.end_row; END LOOP; END report1; /
4. Run your Report
Issue a public grant on the report package and then enter the following url (note that any requests going via mod_plsql will be fine here. I used the apex dad as it is the simplest way to demo)
http://localhost:7777/pls/apex/duncan.report1
Hope that all makes sense
Comments