Quantcast
Channel: 12c – EasyOraDBA
Viewing all 60 articles
Browse latest View live

Check ASM Diskgroup Space and Directory Size

$
0
0

The script below can be used to check the Disk Group Space Free and also Check Directory sizes for each Disk Group

The script is written by somebody at Pythian but i cannot re-collect the original link to the blog.

eg:
./asmcmd_du.sh

DiskGroup Total_MB Free_MB % Free
——— ——– ——- ——
DATAC1 15962112 11215880 70
DBFS_DG 415296 403740 97
RECOC1 3996000 3460272 86

./asmcmd_du.sh DATAC1

DiskGroup Total_MB Free_MB % Free
——— ——– ——- ——
DATAC1 15962112 10155732 63

DATAC1 subdirectories size

Subdir Used MB Mirror MB
—— ——- ———
CARATST/ 55646 111356
ECCTST/ 174912 349856
—— ——- ———
Total 2799978 5600788

Script Below :

#!/bin/bash
# Shadab Mohammad -- 2016
#
# - If no parameter specified, show a du of each DiskGroup
# - If a parameter, print a du of each subdirectory
#

D=$1

#
# Colored thresholds (Red, Yellow, Green)
#
CRITICAL=90
WARNING=75

#
# Set the ASM env
#
ORACLE_SID=`ps -ef | grep pmon | grep asm | awk '{print $NF}' | sed s'/asm_pmon_//' | egrep "^[+]"`
export ORAENV_ASK=NO
. oraenv > /dev/null 2>&1

#
# A quick list of what is running on the server
#
ps -ef | grep pmon | grep -v grep | awk '{print $NF}' | sed s'/.*_pmon_//' | egrep "^([+]|[Aa-Zz])" | sort | awk -v H="`hostname -s`" '' | sed s'/, $//'

#
# Manage parameters
#
if [[ -z $D ]]
then # No directory provided, will check all the DG
DG=`asmcmd lsdg | grep -v State | awk '{print $NF}' | sed s'/\///'`
SUBDIR="No" # Do not show the subdirectories details if no directory is specified
else
DG=`echo $D | sed s'/\/.*$//g'`
fi

#
# A header
#
printf "\n%25s%16s%16s%14s" "DiskGroup" "Total_MB" "Free_MB" "% Free"
printf "\n%25s%16s%16s%14s\n" "---------" "--------" "-------" "------"

#
# Show DG info
#
for X in ${DG}
do
asmcmd lsdg ${X} | tail -1 |\
awk -v DG="$X" -v W="$WARNING" -v C="$CRITICAL" '\
BEGIN \
{COLOR_BEGIN = "\033[1;" ;
COLOR_END = "\033[m" ;
RED = COLOR_BEGIN"31m" ;
GREEN = COLOR_BEGIN"32m" ;
YELLOW = COLOR_BEGIN"33m" ;
COLOR = GREEN ;
}
{ FREE = sprintf("%12d", $8/$7*100) ;
if ((100-FREE) > W) {COLOR=YELLOW ;}
if ((100-FREE) > C) {COLOR=RED ;}
printf("%25s%16s%16s%s\n", DG, $7, $8, COLOR FREE COLOR_END) ; }'
done
printf "\n"

#
# Subdirs info
#
if [ -z ${SUBDIR} ]
then
(for DIR in `asmcmd ls ${D}`
do
echo ${DIR} `asmcmd du ${D}/${DIR} | tail -1`
done) | awk -v D="$D" ' BEGIN { printf("\n\t\t%40s\n\n", D " subdirectories size") ;
printf("%25s%16s%16s\n", "Subdir", "Used MB", "Mirror MB") ;
printf("%25s%16s%16s\n", "------", "-------", "---------") ;}
{
printf("%25s%16s%16s\n", $1, $2, $3) ;
use += $2 ;
mir += $3 ;
}
END { printf("\n\n%25s%16s%16s\n", "------", "-------", "---------") ;
printf("%25s%16s%16s\n\n", "Total", use, mir) ;} '
fi


#************************************************************************#
#* E N D O F S O U R C E *#
#************************************************************************#


Generate Excel and PDF in Oracle and Send Report by Email

$
0
0

First Install UTL_MAIL ###

To install UTL_MAIL:

sqlplus sys/<pwd>
SQL> @$ORACLE_HOME/rdbms/admin/utlmail.sql
SQL> @$ORACLE_HOME/rdbms/admin/prvtmail.plb

### Create a Reporting User ###

</pre>
create user tctreport identified by tctreport;

grant connect to tctreport;

grant dba to tctreport;

sqlplus tctreport/tctreport
<pre>

## Grant TCTREPORT User permission to execute UTL_MAIL ##

</pre>
sqlplus "/as sysdba"

grant execute on sys.utl_mail to tctreport;
<pre>

 

## Create Directory MY_DIR as location to generate the files ###

</pre>
create or replace directory MY_DIR as '/ACFS/report';

grant read, write on directory MY_DIR to tctreport;
<pre>

 

##################################################################################################################

1. Generate Excel by first creating a procedure as_xlsx (code is in file a3_excel.sql )

sqlplus tctreport/tctreport

</pre>
CREATE OR REPLACE package as_xlsx
 is
 /**********************************************
 **
 ** Author: Anton Scheffer
 ** Date: 19-02-2011
 ** Website: http://technology.amis.nl/blog
 ** See also: http://technology.amis.nl/blog/?p=10995
 **
 ** Changelog:
 ** Date: 21-02-2011
 ** Added Aligment, horizontal, vertical, wrapText
 ** Date: 06-03-2011
 ** Added Comments, MergeCells, fixed bug for dependency on NLS-settings
 ** Date: 16-03-2011
 ** Added bold and italic fonts
 ** Date: 22-03-2011
 ** Fixed issue with timezone's set to a region(name) instead of a offset
 ** Date: 08-04-2011
 ** Fixed issue with XML-escaping from text
 ** Date: 27-05-2011
 ** Added MIT-license
 ** Date: 11-08-2011
 ** Fixed NLS-issue with column width
 ** Date: 29-09-2011
 ** Added font color
 ** Date: 16-10-2011
 ** fixed bug in add_string
 ** Date: 26-04-2012
 ** Fixed set_autofilter (only one autofilter per sheet, added _xlnm._FilterDatabase)
 ** Added list_validation = drop-down
 ** Date: 27-08-2013
 ** Added freeze_pane
 **
 ******************************************************************************
 ******************************************************************************
 Copyright (C) 2011, 2012 by Anton Scheffer

Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.

******************************************************************************
 ******************************************** */
 --
 type tp_alignment is record
 ( vertical varchar2(11)
 , horizontal varchar2(16)
 , wrapText boolean
 );
 --
 procedure clear_workbook;
 --
 procedure new_sheet( p_sheetname varchar2 := null );
 --
 function OraFmt2Excel( p_format varchar2 := null )
 return varchar2;
 --
 function get_numFmt( p_format varchar2 := null )
 return pls_integer;
 --
 function get_font
 ( p_name varchar2
 , p_family pls_integer := 2
 , p_fontsize number := 11
 , p_theme pls_integer := 1
 , p_underline boolean := false
 , p_italic boolean := false
 , p_bold boolean := false
 , p_rgb varchar2 := null -- this is a hex ALPHA Red Green Blue value
 )
 return pls_integer;
 --
 function get_fill
 ( p_patternType varchar2
 , p_fgRGB varchar2 := null -- this is a hex ALPHA Red Green Blue value
 )
 return pls_integer;
 --
 function get_border
 ( p_top varchar2 := 'thin'
 , p_bottom varchar2 := 'thin'
 , p_left varchar2 := 'thin'
 , p_right varchar2 := 'thin'
 )
 /*
 none
 thin
 medium
 dashed
 dotted
 thick
 double
 hair
 mediumDashed
 dashDot
 mediumDashDot
 dashDotDot
 mediumDashDotDot
 slantDashDot
 */
 return pls_integer;
 --
 function get_alignment
 ( p_vertical varchar2 := null
 , p_horizontal varchar2 := null
 , p_wrapText boolean := null
 )
 /* horizontal
 center
 centerContinuous
 distributed
 fill
 general
 justify
 left
 right
 */
 /* vertical
 bottom
 center
 distributed
 justify
 top
 */
 return tp_alignment;
 --
 procedure cell
 ( p_col pls_integer
 , p_row pls_integer
 , p_value number
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 , p_sheet pls_integer := null
 );
 --
 procedure cell
 ( p_col pls_integer
 , p_row pls_integer
 , p_value varchar2
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 , p_sheet pls_integer := null
 );
 --
 procedure cell
 ( p_col pls_integer
 , p_row pls_integer
 , p_value date
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 , p_sheet pls_integer := null
 );
 --
 procedure hyperlink
 ( p_col pls_integer
 , p_row pls_integer
 , p_url varchar2
 , p_value varchar2 := null
 , p_sheet pls_integer := null
 );
 --
 procedure comment
 ( p_col pls_integer
 , p_row pls_integer
 , p_text varchar2
 , p_author varchar2 := null
 , p_width pls_integer := 150 -- pixels
 , p_height pls_integer := 100 -- pixels
 , p_sheet pls_integer := null
 );
 --
 procedure mergecells
 ( p_tl_col pls_integer -- top left
 , p_tl_row pls_integer
 , p_br_col pls_integer -- bottom right
 , p_br_row pls_integer
 , p_sheet pls_integer := null
 );
 --
 procedure list_validation
 ( p_sqref_col pls_integer
 , p_sqref_row pls_integer
 , p_tl_col pls_integer -- top left
 , p_tl_row pls_integer
 , p_br_col pls_integer -- bottom right
 , p_br_row pls_integer
 , p_style varchar2 := 'stop' -- stop, warning, information
 , p_title varchar2 := null
 , p_prompt varchar := null
 , p_show_error boolean := false
 , p_error_title varchar2 := null
 , p_error_txt varchar2 := null
 , p_sheet pls_integer := null
 );
 --
 procedure list_validation
 ( p_sqref_col pls_integer
 , p_sqref_row pls_integer
 , p_defined_name varchar2
 , p_style varchar2 := 'stop' -- stop, warning, information
 , p_title varchar2 := null
 , p_prompt varchar := null
 , p_show_error boolean := false
 , p_error_title varchar2 := null
 , p_error_txt varchar2 := null
 , p_sheet pls_integer := null
 );
 --
 procedure defined_name
 ( p_tl_col pls_integer -- top left
 , p_tl_row pls_integer
 , p_br_col pls_integer -- bottom right
 , p_br_row pls_integer
 , p_name varchar2
 , p_sheet pls_integer := null
 , p_localsheet pls_integer := null
 );
 --
 procedure set_column_width
 ( p_col pls_integer
 , p_width number
 , p_sheet pls_integer := null
 );
 --
 procedure set_column
 ( p_col pls_integer
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 , p_sheet pls_integer := null
 );
 --
 procedure set_row
 ( p_row pls_integer
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 , p_sheet pls_integer := null
 );
 --
 procedure freeze_rows
 ( p_nr_rows pls_integer := 1
 , p_sheet pls_integer := null
 );
 --
 procedure freeze_cols
 ( p_nr_cols pls_integer := 1
 , p_sheet pls_integer := null
 );
 --
 procedure freeze_pane
 ( p_col pls_integer
 , p_row pls_integer
 , p_sheet pls_integer := null
 );
 --
 procedure set_autofilter
 ( p_column_start pls_integer := null
 , p_column_end pls_integer := null
 , p_row_start pls_integer := null
 , p_row_end pls_integer := null
 , p_sheet pls_integer := null
 );
 --
 function finish
 return blob;
 --
 procedure save
 ( p_directory varchar2
 , p_filename varchar2
 );
 --
 procedure query2sheet
 ( p_sql varchar2
 , p_column_headers boolean := true
 , p_directory varchar2 := null
 , p_filename varchar2 := null
 , p_sheet pls_integer := null
 );
 --
 /* Example
 begin
 as_xlsx.clear_workbook;
 as_xlsx.new_sheet;
 as_xlsx.cell( 5, 1, 5 );
 as_xlsx.cell( 3, 1, 3 );
 as_xlsx.cell( 2, 2, 45 );
 as_xlsx.cell( 3, 2, 'Anton Scheffer', p_alignment =&gt; as_xlsx.get_alignment( p_wraptext =&gt; true ) );
 as_xlsx.cell( 1, 4, sysdate, p_fontId =&gt; as_xlsx.get_font( 'Calibri', p_rgb =&gt; 'FFFF0000' ) );
 as_xlsx.cell( 2, 4, sysdate, p_numFmtId =&gt; as_xlsx.get_numFmt( 'dd/mm/yyyy h:mm' ) );
 as_xlsx.cell( 3, 4, sysdate, p_numFmtId =&gt; as_xlsx.get_numFmt( as_xlsx.orafmt2excel( 'dd/mon/yyyy' ) ) );
 as_xlsx.cell( 5, 5, 75, p_borderId =&gt; as_xlsx.get_border( 'double', 'double', 'double', 'double' ) );
 as_xlsx.cell( 2, 3, 33 );
 as_xlsx.hyperlink( 1, 6, 'http://www.amis.nl', 'Amis site' );
 as_xlsx.cell( 1, 7, 'Some merged cells', p_alignment =&gt; as_xlsx.get_alignment( p_horizontal =&gt; 'center' ) );
 as_xlsx.mergecells( 1, 7, 3, 7 );
 for i in 1 .. 5
 loop
 as_xlsx.comment( 3, i + 3, 'Row ' || (i+3), 'Anton' );
 end loop;
 as_xlsx.new_sheet;
 as_xlsx.set_row( 1, p_fillId =&gt; as_xlsx.get_fill( 'solid', 'FFFF0000' ) ) ;
 for i in 1 .. 5
 loop
 as_xlsx.cell( 1, i, i );
 as_xlsx.cell( 2, i, i * 3 );
 as_xlsx.cell( 3, i, 'x ' || i * 3 );
 end loop;
 as_xlsx.query2sheet( 'select rownum, x.*
 , case when mod( rownum, 2 ) = 0 then rownum * 3 end demo
 , case when mod( rownum, 2 ) = 1 then ''demo '' || rownum end demo2 from dual x connect by rownum &lt;= 5' );
 as_xlsx.save( 'MY_DIR', 'my.xlsx' );
 end;
 --
 begin
 as_xlsx.clear_workbook;
 as_xlsx.new_sheet;
 as_xlsx.cell( 1, 6, 5 );
 as_xlsx.cell( 1, 7, 3 );
 as_xlsx.cell( 1, 8, 7 );
 as_xlsx.new_sheet;
 as_xlsx.cell( 2, 6, 15, p_sheet =&gt; 2 );
 as_xlsx.cell( 2, 7, 13, p_sheet =&gt; 2 );
 as_xlsx.cell( 2, 8, 17, p_sheet =&gt; 2 );
 as_xlsx.list_validation( 6, 3, 1, 6, 1, 8, p_show_error =&gt; true, p_sheet =&gt; 1 );
 as_xlsx.defined_name( 2, 6, 2, 8, 'Anton', 2 );
 as_xlsx.list_validation
 ( 6, 1, 'Anton'
 , p_style =&gt; 'information'
 , p_title =&gt; 'valid values are'
 , p_prompt =&gt; '13, 15 and 17'
 , p_show_error =&gt; true
 , p_error_title =&gt; 'Are you sure?'
 , p_error_txt =&gt; 'Valid values are: 13, 15 and 17'
 , p_sheet =&gt; 1 );
 as_xlsx.save( 'MY_DIR', 'my.xlsx' );
 end;
 --
 begin
 as_xlsx.clear_workbook;
 as_xlsx.new_sheet;
 as_xlsx.cell( 1, 6, 5 );
 as_xlsx.cell( 1, 7, 3 );
 as_xlsx.cell( 1, 8, 7 );
 as_xlsx.set_autofilter( 1,1, p_row_start =&gt; 5, p_row_end =&gt; 8 );
 as_xlsx.new_sheet;
 as_xlsx.cell( 2, 6, 5 );
 as_xlsx.cell( 2, 7, 3 );
 as_xlsx.cell( 2, 8, 7 );
 as_xlsx.set_autofilter( 2,2, p_row_start =&gt; 5, p_row_end =&gt; 8 );
 as_xlsx.save( 'MY_DIR', 'my.xlsx' );
 end;
 --
 begin
 as_xlsx.clear_workbook;
 as_xlsx.new_sheet;
 for c in 1 .. 10
 loop
 as_xlsx.cell( c, 1, 'COL' || c );
 as_xlsx.cell( c, 2, 'val' || c );
 as_xlsx.cell( c, 3, c );
 end loop;
 as_xlsx.freeze_rows( 1 );
 as_xlsx.new_sheet;
 for r in 1 .. 10
 loop
 as_xlsx.cell( 1, r, 'ROW' || r );
 as_xlsx.cell( 2, r, 'val' || r );
 as_xlsx.cell( 3, r, r );
 end loop;
 as_xlsx.freeze_cols( 3 );
 as_xlsx.new_sheet;
 as_xlsx.cell( 3, 3, 'Start freeze' );
 as_xlsx.freeze_pane( 3,3 );
 as_xlsx.save( 'MY_DIR', 'my.xlsx' );
 end;
 */
 end;
 /

CREATE OR REPLACE package body as_xlsx
 is
 --
 c_LOCAL_FILE_HEADER constant raw(4) := hextoraw( '504B0304' ); -- Local file header signature
 c_END_OF_CENTRAL_DIRECTORY constant raw(4) := hextoraw( '504B0506' ); -- End of central directory signature
 --
 type tp_XF_fmt is record
 ( numFmtId pls_integer
 , fontId pls_integer
 , fillId pls_integer
 , borderId pls_integer
 , alignment tp_alignment
 );
 type tp_col_fmts is table of tp_XF_fmt index by pls_integer;
 type tp_row_fmts is table of tp_XF_fmt index by pls_integer;
 type tp_widths is table of number index by pls_integer;
 type tp_cell is record
 ( value number
 , style varchar2(50)
 );
 type tp_cells is table of tp_cell index by pls_integer;
 type tp_rows is table of tp_cells index by pls_integer;
 type tp_autofilter is record
 ( column_start pls_integer
 , column_end pls_integer
 , row_start pls_integer
 , row_end pls_integer
 );
 type tp_autofilters is table of tp_autofilter index by pls_integer;
 type tp_hyperlink is record
 ( cell varchar2(10)
 , url varchar2(1000)
 );
 type tp_hyperlinks is table of tp_hyperlink index by pls_integer;
 subtype tp_author is varchar2(32767 char);
 type tp_authors is table of pls_integer index by tp_author;
 authors tp_authors;
 type tp_comment is record
 ( text varchar2(32767 char)
 , author tp_author
 , row pls_integer
 , column pls_integer
 , width pls_integer
 , height pls_integer
 );
 type tp_comments is table of tp_comment index by pls_integer;
 type tp_mergecells is table of varchar2(21) index by pls_integer;
 type tp_validation is record
 ( type varchar2(10)
 , errorstyle varchar2(32)
 , showinputmessage boolean
 , prompt varchar2(32767 char)
 , title varchar2(32767 char)
 , error_title varchar2(32767 char)
 , error_txt varchar2(32767 char)
 , showerrormessage boolean
 , formula1 varchar2(32767 char)
 , formula2 varchar2(32767 char)
 , allowBlank boolean
 , sqref varchar2(32767 char)
 );
 type tp_validations is table of tp_validation index by pls_integer;
 type tp_sheet is record
 ( rows tp_rows
 , widths tp_widths
 , name varchar2(100)
 , freeze_rows pls_integer
 , freeze_cols pls_integer
 , autofilters tp_autofilters
 , hyperlinks tp_hyperlinks
 , col_fmts tp_col_fmts
 , row_fmts tp_row_fmts
 , comments tp_comments
 , mergecells tp_mergecells
 , validations tp_validations
 );
 type tp_sheets is table of tp_sheet index by pls_integer;
 type tp_numFmt is record
 ( numFmtId pls_integer
 , formatCode varchar2(100)
 );
 type tp_numFmts is table of tp_numFmt index by pls_integer;
 type tp_fill is record
 ( patternType varchar2(30)
 , fgRGB varchar2(8)
 );
 type tp_fills is table of tp_fill index by pls_integer;
 type tp_cellXfs is table of tp_xf_fmt index by pls_integer;
 type tp_font is record
 ( name varchar2(100)
 , family pls_integer
 , fontsize number
 , theme pls_integer
 , RGB varchar2(8)
 , underline boolean
 , italic boolean
 , bold boolean
 );
 type tp_fonts is table of tp_font index by pls_integer;
 type tp_border is record
 ( top varchar2(17)
 , bottom varchar2(17)
 , left varchar2(17)
 , right varchar2(17)
 );
 type tp_borders is table of tp_border index by pls_integer;
 type tp_numFmtIndexes is table of pls_integer index by pls_integer;
 type tp_strings is table of pls_integer index by varchar2(32767 char);
 type tp_str_ind is table of varchar2(32767 char) index by pls_integer;
 type tp_defined_name is record
 ( name varchar2(32767 char)
 , ref varchar2(32767 char)
 , sheet pls_integer
 );
 type tp_defined_names is table of tp_defined_name index by pls_integer;
 type tp_book is record
 ( sheets tp_sheets
 , strings tp_strings
 , str_ind tp_str_ind
 , str_cnt pls_integer := 0
 , fonts tp_fonts
 , fills tp_fills
 , borders tp_borders
 , numFmts tp_numFmts
 , cellXfs tp_cellXfs
 , numFmtIndexes tp_numFmtIndexes
 , defined_names tp_defined_names
 );
 workbook tp_book;
 --
 procedure blob2file
 ( p_blob blob
 , p_directory varchar2 := 'MY_DIR'
 , p_filename varchar2 := 'my.xlsx'
 )
 is
 t_fh utl_file.file_type;
 t_len pls_integer := 32767;
 begin
 t_fh := utl_file.fopen( p_directory
 , p_filename
 , 'wb'
 );
 for i in 0 .. trunc( ( dbms_lob.getlength( p_blob ) - 1 ) / t_len )
 loop
 utl_file.put_raw( t_fh
 , dbms_lob.substr( p_blob
 , t_len
 , i * t_len + 1
 )
 );
 end loop;
 utl_file.fclose( t_fh );
 end;
 --
 function raw2num( p_raw raw, p_len integer, p_pos integer )
 return number
 is
 begin
 return utl_raw.cast_to_binary_integer( utl_raw.substr( p_raw, p_pos, p_len ), utl_raw.little_endian );
 end;
 --
 function little_endian( p_big number, p_bytes pls_integer := 4 )
 return raw
 is
 begin
 return utl_raw.substr( utl_raw.cast_from_binary_integer( p_big, utl_raw.little_endian ), 1, p_bytes );
 end;
 --
 function blob2num( p_blob blob, p_len integer, p_pos integer )
 return number
 is
 begin
 return utl_raw.cast_to_binary_integer( dbms_lob.substr( p_blob, p_len, p_pos ), utl_raw.little_endian );
 end;
 --
 procedure add1file
 ( p_zipped_blob in out blob
 , p_name varchar2
 , p_content blob
 )
 is
 t_now date;
 t_blob blob;
 t_len integer;
 t_clen integer;
 t_crc32 raw(4) := hextoraw( '00000000' );
 t_compressed boolean := false;
 t_name raw(32767);
 begin
 t_now := sysdate;
 t_len := nvl( dbms_lob.getlength( p_content ), 0 );
 if t_len &gt; 0
 then
 t_blob := utl_compress.lz_compress( p_content );
 t_clen := dbms_lob.getlength( t_blob ) - 18;
 t_compressed := t_clen &lt; t_len;
 t_crc32 := dbms_lob.substr( t_blob, 4, t_clen + 11 );
 end if;
 if not t_compressed
 then
 t_clen := t_len;
 t_blob := p_content;
 end if;
 if p_zipped_blob is null
 then
 dbms_lob.createtemporary( p_zipped_blob, true );
 end if;
 t_name := utl_i18n.string_to_raw( p_name, 'AL32UTF8' );
 dbms_lob.append( p_zipped_blob
 , utl_raw.concat( c_LOCAL_FILE_HEADER -- Local file header signature
 , hextoraw( '1400' ) -- version 2.0
 , case when t_name = utl_i18n.string_to_raw( p_name, 'US8PC437' )
 then hextoraw( '0000' ) -- no General purpose bits
 else hextoraw( '0008' ) -- set Language encoding flag (EFS)
 end
 , case when t_compressed
 then hextoraw( '0800' ) -- deflate
 else hextoraw( '0000' ) -- stored
 end
 , little_endian( to_number( to_char( t_now, 'ss' ) ) / 2
 + to_number( to_char( t_now, 'mi' ) ) * 32
 + to_number( to_char( t_now, 'hh24' ) ) * 2048
 , 2
 ) -- File last modification time
 , little_endian( to_number( to_char( t_now, 'dd' ) )
 + to_number( to_char( t_now, 'mm' ) ) * 32
 + ( to_number( to_char( t_now, 'yyyy' ) ) - 1980 ) * 512
 , 2
 ) -- File last modification date
 , t_crc32 -- CRC-32
 , little_endian( t_clen ) -- compressed size
 , little_endian( t_len ) -- uncompressed size
 , little_endian( utl_raw.length( t_name ), 2 ) -- File name length
 , hextoraw( '0000' ) -- Extra field length
 , t_name -- File name
 )
 );
 if t_compressed
 then
 dbms_lob.copy( p_zipped_blob, t_blob, t_clen, dbms_lob.getlength( p_zipped_blob ) + 1, 11 ); -- compressed content
 elsif t_clen &gt; 0
 then
 dbms_lob.copy( p_zipped_blob, t_blob, t_clen, dbms_lob.getlength( p_zipped_blob ) + 1, 1 ); -- content
 end if;
 if dbms_lob.istemporary( t_blob ) = 1
 then
 dbms_lob.freetemporary( t_blob );
 end if;
 end;
 --
 procedure finish_zip( p_zipped_blob in out blob )
 is
 t_cnt pls_integer := 0;
 t_offs integer;
 t_offs_dir_header integer;
 t_offs_end_header integer;
 t_comment raw(32767) := utl_raw.cast_to_raw( 'Implementation by Anton Scheffer' );
 begin
 t_offs_dir_header := dbms_lob.getlength( p_zipped_blob );
 t_offs := 1;
 while dbms_lob.substr( p_zipped_blob, utl_raw.length( c_LOCAL_FILE_HEADER ), t_offs ) = c_LOCAL_FILE_HEADER
 loop
 t_cnt := t_cnt + 1;
 dbms_lob.append( p_zipped_blob
 , utl_raw.concat( hextoraw( '504B0102' ) -- Central directory file header signature
 , hextoraw( '1400' ) -- version 2.0
 , dbms_lob.substr( p_zipped_blob, 26, t_offs + 4 )
 , hextoraw( '0000' ) -- File comment length
 , hextoraw( '0000' ) -- Disk number where file starts
 , hextoraw( '0000' ) -- Internal file attributes =&gt;
 -- 0000 binary file
 -- 0100 (ascii)text file
 , case
 when dbms_lob.substr( p_zipped_blob
 , 1
 , t_offs + 30 + blob2num( p_zipped_blob, 2, t_offs + 26 ) - 1
 ) in ( hextoraw( '2F' ) -- /
 , hextoraw( '5C' ) -- \
 )
 then hextoraw( '10000000' ) -- a directory/folder
 else hextoraw( '2000B681' ) -- a file
 end -- External file attributes
 , little_endian( t_offs - 1 ) -- Relative offset of local file header
 , dbms_lob.substr( p_zipped_blob
 , blob2num( p_zipped_blob, 2, t_offs + 26 )
 , t_offs + 30
 ) -- File name
 )
 );
 t_offs := t_offs + 30 + blob2num( p_zipped_blob, 4, t_offs + 18 ) -- compressed size
 + blob2num( p_zipped_blob, 2, t_offs + 26 ) -- File name length
 + blob2num( p_zipped_blob, 2, t_offs + 28 ); -- Extra field length
 end loop;
 t_offs_end_header := dbms_lob.getlength( p_zipped_blob );
 dbms_lob.append( p_zipped_blob
 , utl_raw.concat( c_END_OF_CENTRAL_DIRECTORY -- End of central directory signature
 , hextoraw( '0000' ) -- Number of this disk
 , hextoraw( '0000' ) -- Disk where central directory starts
 , little_endian( t_cnt, 2 ) -- Number of central directory records on this disk
 , little_endian( t_cnt, 2 ) -- Total number of central directory records
 , little_endian( t_offs_end_header - t_offs_dir_header ) -- Size of central directory
 , little_endian( t_offs_dir_header ) -- Offset of start of central directory, relative to start of archive
 , little_endian( nvl( utl_raw.length( t_comment ), 0 ), 2 ) -- ZIP file comment length
 , t_comment
 )
 );
 end;
 --
 function alfan_col( p_col pls_integer )
 return varchar2
 is
 begin
 return case
 when p_col &gt; 702 then chr( 64 + trunc( ( p_col - 27 ) / 676 ) ) || chr( 65 + mod( trunc( ( p_col - 1 ) / 26 ) - 1, 26 ) ) || chr( 65 + mod( p_col - 1, 26 ) )
 when p_col &gt; 26 then chr( 64 + trunc( ( p_col - 1 ) / 26 ) ) || chr( 65 + mod( p_col - 1, 26 ) )
 else chr( 64 + p_col )
 end;
 end;
 --
 function col_alfan( p_col varchar2 )
 return pls_integer
 is
 begin
 return ascii( substr( p_col, -1 ) ) - 64
 + nvl( ( ascii( substr( p_col, -2, 1 ) ) - 64 ) * 26, 0 )
 + nvl( ( ascii( substr( p_col, -3, 1 ) ) - 64 ) * 676, 0 );
 end;
 --
 procedure clear_workbook
 is
 t_row_ind pls_integer;
 begin
 for s in 1 .. workbook.sheets.count()
 loop
 t_row_ind := workbook.sheets( s ).rows.first();
 while t_row_ind is not null
 loop
 workbook.sheets( s ).rows( t_row_ind ).delete();
 t_row_ind := workbook.sheets( s ).rows.next( t_row_ind );
 end loop;
 workbook.sheets( s ).rows.delete();
 workbook.sheets( s ).widths.delete();
 workbook.sheets( s ).autofilters.delete();
 workbook.sheets( s ).hyperlinks.delete();
 workbook.sheets( s ).col_fmts.delete();
 workbook.sheets( s ).row_fmts.delete();
 workbook.sheets( s ).comments.delete();
 workbook.sheets( s ).mergecells.delete();
 workbook.sheets( s ).validations.delete();
 end loop;
 workbook.strings.delete();
 workbook.str_ind.delete();
 workbook.fonts.delete();
 workbook.fills.delete();
 workbook.borders.delete();
 workbook.numFmts.delete();
 workbook.cellXfs.delete();
 workbook.defined_names.delete();
 workbook := null;
 end;
 --
 procedure new_sheet( p_sheetname varchar2 := null )
 is
 t_nr pls_integer := workbook.sheets.count() + 1;
 t_ind pls_integer;
 begin
 workbook.sheets( t_nr ).name := nvl( dbms_xmlgen.convert( translate( p_sheetname, 'a/\[]*:?', 'a' ) ), 'Sheet' || t_nr );
 if workbook.strings.count() = 0
 then
 workbook.str_cnt := 0;
 end if;
 if workbook.fonts.count() = 0
 then
 t_ind := get_font( 'Calibri' );
 end if;
 if workbook.fills.count() = 0
 then
 t_ind := get_fill( 'none' );
 t_ind := get_fill( 'gray125' );
 end if;
 if workbook.borders.count() = 0
 then
 t_ind := get_border( '', '', '', '' );
 end if;
 end;
 --
 procedure set_col_width
 ( p_sheet pls_integer
 , p_col pls_integer
 , p_format varchar2
 )
 is
 t_width number;
 t_nr_chr pls_integer;
 begin
 if p_format is null
 then
 return;
 end if;
 if instr( p_format, ';' ) &gt; 0
 then
 t_nr_chr := length( translate( substr( p_format, 1, instr( p_format, ';' ) - 1 ), 'a\"', 'a' ) );
 else
 t_nr_chr := length( translate( p_format, 'a\"', 'a' ) );
 end if;
 t_width := trunc( ( t_nr_chr * 7 + 5 ) / 7 * 256 ) / 256; -- assume default 11 point Calibri
 if workbook.sheets( p_sheet ).widths.exists( p_col )
 then
 workbook.sheets( p_sheet ).widths( p_col ) :=
 greatest( workbook.sheets( p_sheet ).widths( p_col )
 , t_width
 );
 else
 workbook.sheets( p_sheet ).widths( p_col ) := greatest( t_width, 8.43 );
 end if;
 end;
 --
 function OraFmt2Excel( p_format varchar2 := null )
 return varchar2
 is
 t_format varchar2(1000) := substr( p_format, 1, 1000 );
 begin
 t_format := replace( replace( t_format, 'hh24', 'hh' ), 'hh12', 'hh' );
 t_format := replace( t_format, 'mi', 'mm' );
 t_format := replace( replace( replace( t_format, 'AM', '~~' ), 'PM', '~~' ), '~~', 'AM/PM' );
 t_format := replace( replace( replace( t_format, 'am', '~~' ), 'pm', '~~' ), '~~', 'AM/PM' );
 t_format := replace( replace( t_format, 'day', 'DAY' ), 'DAY', 'dddd' );
 t_format := replace( replace( t_format, 'dy', 'DY' ), 'DAY', 'ddd' );
 t_format := replace( replace( t_format, 'RR', 'RR' ), 'RR', 'YY' );
 t_format := replace( replace( t_format, 'month', 'MONTH' ), 'MONTH', 'mmmm' );
 t_format := replace( replace( t_format, 'mon', 'MON' ), 'MON', 'mmm' );
 return t_format;
 end;
 --
 function get_numFmt( p_format varchar2 := null )
 return pls_integer
 is
 t_cnt pls_integer;
 t_numFmtId pls_integer;
 begin
 if p_format is null
 then
 return 0;
 end if;
 t_cnt := workbook.numFmts.count();
 for i in 1 .. t_cnt
 loop
 if workbook.numFmts( i ).formatCode = p_format
 then
 t_numFmtId := workbook.numFmts( i ).numFmtId;
 exit;
 end if;
 end loop;
 if t_numFmtId is null
 then
 t_numFmtId := case when t_cnt = 0 then 164 else workbook.numFmts( t_cnt ).numFmtId + 1 end;
 t_cnt := t_cnt + 1;
 workbook.numFmts( t_cnt ).numFmtId := t_numFmtId;
 workbook.numFmts( t_cnt ).formatCode := p_format;
 workbook.numFmtIndexes( t_numFmtId ) := t_cnt;
 end if;
 return t_numFmtId;
 end;
 --
 function get_font
 ( p_name varchar2
 , p_family pls_integer := 2
 , p_fontsize number := 11
 , p_theme pls_integer := 1
 , p_underline boolean := false
 , p_italic boolean := false
 , p_bold boolean := false
 , p_rgb varchar2 := null -- this is a hex ALPHA Red Green Blue value
 )
 return pls_integer
 is
 t_ind pls_integer;
 begin
 if workbook.fonts.count() &gt; 0
 then
 for f in 0 .. workbook.fonts.count() - 1
 loop
 if ( workbook.fonts( f ).name = p_name
 and workbook.fonts( f ).family = p_family
 and workbook.fonts( f ).fontsize = p_fontsize
 and workbook.fonts( f ).theme = p_theme
 and workbook.fonts( f ).underline = p_underline
 and workbook.fonts( f ).italic = p_italic
 and workbook.fonts( f ).bold = p_bold
 and ( workbook.fonts( f ).rgb = p_rgb
 or ( workbook.fonts( f ).rgb is null and p_rgb is null )
 )
 )
 then
 return f;
 end if;
 end loop;
 end if;
 t_ind := workbook.fonts.count();
 workbook.fonts( t_ind ).name := p_name;
 workbook.fonts( t_ind ).family := p_family;
 workbook.fonts( t_ind ).fontsize := p_fontsize;
 workbook.fonts( t_ind ).theme := p_theme;
 workbook.fonts( t_ind ).underline := p_underline;
 workbook.fonts( t_ind ).italic := p_italic;
 workbook.fonts( t_ind ).bold := p_bold;
 workbook.fonts( t_ind ).rgb := p_rgb;
 return t_ind;
 end;
 --
 function get_fill
 ( p_patternType varchar2
 , p_fgRGB varchar2 := null
 )
 return pls_integer
 is
 t_ind pls_integer;
 begin
 if workbook.fills.count() &gt; 0
 then
 for f in 0 .. workbook.fills.count() - 1
 loop
 if ( workbook.fills( f ).patternType = p_patternType
 and nvl( workbook.fills( f ).fgRGB, 'x' ) = nvl( upper( p_fgRGB ), 'x' )
 )
 then
 return f;
 end if;
 end loop;
 end if;
 t_ind := workbook.fills.count();
 workbook.fills( t_ind ).patternType := p_patternType;
 workbook.fills( t_ind ).fgRGB := upper( p_fgRGB );
 return t_ind;
 end;
 --
 function get_border
 ( p_top varchar2 := 'thin'
 , p_bottom varchar2 := 'thin'
 , p_left varchar2 := 'thin'
 , p_right varchar2 := 'thin'
 )
 return pls_integer
 is
 t_ind pls_integer;
 begin
 if workbook.borders.count() &gt; 0
 then
 for b in 0 .. workbook.borders.count() - 1
 loop
 if ( nvl( workbook.borders( b ).top, 'x' ) = nvl( p_top, 'x' )
 and nvl( workbook.borders( b ).bottom, 'x' ) = nvl( p_bottom, 'x' )
 and nvl( workbook.borders( b ).left, 'x' ) = nvl( p_left, 'x' )
 and nvl( workbook.borders( b ).right, 'x' ) = nvl( p_right, 'x' )
 )
 then
 return b;
 end if;
 end loop;
 end if;
 t_ind := workbook.borders.count();
 workbook.borders( t_ind ).top := p_top;
 workbook.borders( t_ind ).bottom := p_bottom;
 workbook.borders( t_ind ).left := p_left;
 workbook.borders( t_ind ).right := p_right;
 return t_ind;
 end;
 --
 function get_alignment
 ( p_vertical varchar2 := null
 , p_horizontal varchar2 := null
 , p_wrapText boolean := null
 )
 return tp_alignment
 is
 t_rv tp_alignment;
 begin
 t_rv.vertical := p_vertical;
 t_rv.horizontal := p_horizontal;
 t_rv.wrapText := p_wrapText;
 return t_rv;
 end;
 --
 function get_XfId
 ( p_sheet pls_integer
 , p_col pls_integer
 , p_row pls_integer
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 )
 return varchar2
 is
 t_cnt pls_integer;
 t_XfId pls_integer;
 t_XF tp_XF_fmt;
 t_col_XF tp_XF_fmt;
 t_row_XF tp_XF_fmt;
 begin
 if workbook.sheets( p_sheet ).col_fmts.exists( p_col )
 then
 t_col_XF := workbook.sheets( p_sheet ).col_fmts( p_col );
 end if;
 if workbook.sheets( p_sheet ).row_fmts.exists( p_row )
 then
 t_row_XF := workbook.sheets( p_sheet ).row_fmts( p_row );
 end if;
 t_XF.numFmtId := coalesce( p_numFmtId, t_col_XF.numFmtId, t_row_XF.numFmtId, 0 );
 t_XF.fontId := coalesce( p_fontId, t_col_XF.fontId, t_row_XF.fontId, 0 );
 t_XF.fillId := coalesce( p_fillId, t_col_XF.fillId, t_row_XF.fillId, 0 );
 t_XF.borderId := coalesce( p_borderId, t_col_XF.borderId, t_row_XF.borderId, 0 );
 t_XF.alignment := coalesce( p_alignment, t_col_XF.alignment, t_row_XF.alignment );
 if ( t_XF.numFmtId + t_XF.fontId + t_XF.fillId + t_XF.borderId = 0
 and t_XF.alignment.vertical is null
 and t_XF.alignment.horizontal is null
 and not nvl( t_XF.alignment.wrapText, false )
 )
 then
 return '';
 end if;
 if t_XF.numFmtId &gt; 0
 then
 set_col_width( p_sheet, p_col, workbook.numFmts( workbook.numFmtIndexes( t_XF.numFmtId ) ).formatCode );
 end if;
 t_cnt := workbook.cellXfs.count();
 for i in 1 .. t_cnt
 loop
 if ( workbook.cellXfs( i ).numFmtId = t_XF.numFmtId
 and workbook.cellXfs( i ).fontId = t_XF.fontId
 and workbook.cellXfs( i ).fillId = t_XF.fillId
 and workbook.cellXfs( i ).borderId = t_XF.borderId
 and nvl( workbook.cellXfs( i ).alignment.vertical, 'x' ) = nvl( t_XF.alignment.vertical, 'x' )
 and nvl( workbook.cellXfs( i ).alignment.horizontal, 'x' ) = nvl( t_XF.alignment.horizontal, 'x' )
 and nvl( workbook.cellXfs( i ).alignment.wrapText, false ) = nvl( t_XF.alignment.wrapText, false )
 )
 then
 t_XfId := i;
 exit;
 end if;
 end loop;
 if t_XfId is null
 then
 t_cnt := t_cnt + 1;
 t_XfId := t_cnt;
 workbook.cellXfs( t_cnt ) := t_XF;
 end if;
 return 's="' || t_XfId || '"';
 end;
 --
 procedure cell
 ( p_col pls_integer
 , p_row pls_integer
 , p_value number
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 , p_sheet pls_integer := null
 )
 is
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 workbook.sheets( t_sheet ).rows( p_row )( p_col ).value := p_value;
 workbook.sheets( t_sheet ).rows( p_row )( p_col ).style := null;
 workbook.sheets( t_sheet ).rows( p_row )( p_col ).style := get_XfId( t_sheet, p_col, p_row, p_numFmtId, p_fontId, p_fillId, p_borderId, p_alignment );
 end;
 --
 function add_string( p_string varchar2 )
 return pls_integer
 is
 t_cnt pls_integer;
 begin
 if workbook.strings.exists( p_string )
 then
 t_cnt := workbook.strings( p_string );
 else
 t_cnt := workbook.strings.count();
 workbook.str_ind( t_cnt ) := p_string;
 workbook.strings( nvl( p_string, '' ) ) := t_cnt;
 end if;
 workbook.str_cnt := workbook.str_cnt + 1;
 return t_cnt;
 end;
 --
 procedure cell
 ( p_col pls_integer
 , p_row pls_integer
 , p_value varchar2
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 , p_sheet pls_integer := null
 )
 is
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 t_alignment tp_alignment := p_alignment;
 begin
 workbook.sheets( t_sheet ).rows( p_row )( p_col ).value := add_string( p_value );
 if t_alignment.wrapText is null and instr( p_value, chr(13) ) &gt; 0
 then
 t_alignment.wrapText := true;
 end if;
 workbook.sheets( t_sheet ).rows( p_row )( p_col ).style := 't="s" ' || get_XfId( t_sheet, p_col, p_row, p_numFmtId, p_fontId, p_fillId, p_borderId, t_alignment );
 end;
 --
 procedure cell
 ( p_col pls_integer
 , p_row pls_integer
 , p_value date
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 , p_sheet pls_integer := null
 )
 is
 t_numFmtId pls_integer := p_numFmtId;
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 workbook.sheets( t_sheet ).rows( p_row )( p_col ).value := p_value - to_date('01-01-1904','DD-MM-YYYY');
 if t_numFmtId is null
 and not ( workbook.sheets( t_sheet ).col_fmts.exists( p_col )
 and workbook.sheets( t_sheet ).col_fmts( p_col ).numFmtId is not null
 )
 and not ( workbook.sheets( t_sheet ).row_fmts.exists( p_row )
 and workbook.sheets( t_sheet ).row_fmts( p_row ).numFmtId is not null
 )
 then
 t_numFmtId := get_numFmt( 'dd/mm/yyyy' );
 end if;
 workbook.sheets( t_sheet ).rows( p_row )( p_col ).style := get_XfId( t_sheet, p_col, p_row, t_numFmtId, p_fontId, p_fillId, p_borderId, p_alignment );
 end;
 --
 procedure hyperlink
 ( p_col pls_integer
 , p_row pls_integer
 , p_url varchar2
 , p_value varchar2 := null
 , p_sheet pls_integer := null
 )
 is
 t_ind pls_integer;
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 workbook.sheets( t_sheet ).rows( p_row )( p_col ).value := add_string( nvl( p_value, p_url ) );
 workbook.sheets( t_sheet ).rows( p_row )( p_col ).style := 't="s" ' || get_XfId( t_sheet, p_col, p_row, '', get_font( 'Calibri', p_theme =&gt; 10, p_underline =&gt; true ) );
 t_ind := workbook.sheets( t_sheet ).hyperlinks.count() + 1;
 workbook.sheets( t_sheet ).hyperlinks( t_ind ).cell := alfan_col( p_col ) || p_row;
 workbook.sheets( t_sheet ).hyperlinks( t_ind ).url := p_url;
 end;
 --
 procedure comment
 ( p_col pls_integer
 , p_row pls_integer
 , p_text varchar2
 , p_author varchar2 := null
 , p_width pls_integer := 150
 , p_height pls_integer := 100
 , p_sheet pls_integer := null
 )
 is
 t_ind pls_integer;
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 t_ind := workbook.sheets( t_sheet ).comments.count() + 1;
 workbook.sheets( t_sheet ).comments( t_ind ).row := p_row;
 workbook.sheets( t_sheet ).comments( t_ind ).column := p_col;
 workbook.sheets( t_sheet ).comments( t_ind ).text := dbms_xmlgen.convert( p_text );
 workbook.sheets( t_sheet ).comments( t_ind ).author := dbms_xmlgen.convert( p_author );
 workbook.sheets( t_sheet ).comments( t_ind ).width := p_width;
 workbook.sheets( t_sheet ).comments( t_ind ).height := p_height;
 end;
 --
 procedure mergecells
 ( p_tl_col pls_integer -- top left
 , p_tl_row pls_integer
 , p_br_col pls_integer -- bottom right
 , p_br_row pls_integer
 , p_sheet pls_integer := null
 )
 is
 t_ind pls_integer;
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 t_ind := workbook.sheets( t_sheet ).mergecells.count() + 1;
 workbook.sheets( t_sheet ).mergecells( t_ind ) := alfan_col( p_tl_col ) || p_tl_row || ':' || alfan_col( p_br_col ) || p_br_row;
 end;
 --
 procedure add_validation
 ( p_type varchar2
 , p_sqref varchar2
 , p_style varchar2 := 'stop' -- stop, warning, information
 , p_formula1 varchar2 := null
 , p_formula2 varchar2 := null
 , p_title varchar2 := null
 , p_prompt varchar := null
 , p_show_error boolean := false
 , p_error_title varchar2 := null
 , p_error_txt varchar2 := null
 , p_sheet pls_integer := null
 )
 is
 t_ind pls_integer;
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 t_ind := workbook.sheets( t_sheet ).validations.count() + 1;
 workbook.sheets( t_sheet ).validations( t_ind ).type := p_type;
 workbook.sheets( t_sheet ).validations( t_ind ).errorstyle := p_style;
 workbook.sheets( t_sheet ).validations( t_ind ).sqref := p_sqref;
 workbook.sheets( t_sheet ).validations( t_ind ).formula1 := p_formula1;
 workbook.sheets( t_sheet ).validations( t_ind ).error_title := p_error_title;
 workbook.sheets( t_sheet ).validations( t_ind ).error_txt := p_error_txt;
 workbook.sheets( t_sheet ).validations( t_ind ).title := p_title;
 workbook.sheets( t_sheet ).validations( t_ind ).prompt := p_prompt;
 workbook.sheets( t_sheet ).validations( t_ind ).showerrormessage := p_show_error;
 end;
 --
 procedure list_validation
 ( p_sqref_col pls_integer
 , p_sqref_row pls_integer
 , p_tl_col pls_integer -- top left
 , p_tl_row pls_integer
 , p_br_col pls_integer -- bottom right
 , p_br_row pls_integer
 , p_style varchar2 := 'stop' -- stop, warning, information
 , p_title varchar2 := null
 , p_prompt varchar := null
 , p_show_error boolean := false
 , p_error_title varchar2 := null
 , p_error_txt varchar2 := null
 , p_sheet pls_integer := null
 )
 is
 begin
 add_validation( 'list'
 , alfan_col( p_sqref_col ) || p_sqref_row
 , p_style =&gt; lower( p_style )
 , p_formula1 =&gt; '$' || alfan_col( p_tl_col ) || '$' || p_tl_row || ':$' || alfan_col( p_br_col ) || '$' || p_br_row
 , p_title =&gt; p_title
 , p_prompt =&gt; p_prompt
 , p_show_error =&gt; p_show_error
 , p_error_title =&gt; p_error_title
 , p_error_txt =&gt; p_error_txt
 , p_sheet =&gt; p_sheet
 );
 end;
 --
 procedure list_validation
 ( p_sqref_col pls_integer
 , p_sqref_row pls_integer
 , p_defined_name varchar2
 , p_style varchar2 := 'stop' -- stop, warning, information
 , p_title varchar2 := null
 , p_prompt varchar := null
 , p_show_error boolean := false
 , p_error_title varchar2 := null
 , p_error_txt varchar2 := null
 , p_sheet pls_integer := null
 )
 is
 begin
 add_validation( 'list'
 , alfan_col( p_sqref_col ) || p_sqref_row
 , p_style =&gt; lower( p_style )
 , p_formula1 =&gt; p_defined_name
 , p_title =&gt; p_title
 , p_prompt =&gt; p_prompt
 , p_show_error =&gt; p_show_error
 , p_error_title =&gt; p_error_title
 , p_error_txt =&gt; p_error_txt
 , p_sheet =&gt; p_sheet
 );
 end;
 --
 procedure defined_name
 ( p_tl_col pls_integer -- top left
 , p_tl_row pls_integer
 , p_br_col pls_integer -- bottom right
 , p_br_row pls_integer
 , p_name varchar2
 , p_sheet pls_integer := null
 , p_localsheet pls_integer := null
 )
 is
 t_ind pls_integer;
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 t_ind := workbook.defined_names.count() + 1;
 workbook.defined_names( t_ind ).name := p_name;
 workbook.defined_names( t_ind ).ref := 'Sheet' || t_sheet || '!$' || alfan_col( p_tl_col ) || '$' || p_tl_row || ':$' || alfan_col( p_br_col ) || '$' || p_br_row;
 workbook.defined_names( t_ind ).sheet := p_localsheet;
 end;
 --
 procedure set_column_width
 ( p_col pls_integer
 , p_width number
 , p_sheet pls_integer := null
 )
 is
 begin
 workbook.sheets( nvl( p_sheet, workbook.sheets.count() ) ).widths( p_col ) := p_width;
 end;
 --
 procedure set_column
 ( p_col pls_integer
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 , p_sheet pls_integer := null
 )
 is
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 workbook.sheets( t_sheet ).col_fmts( p_col ).numFmtId := p_numFmtId;
 workbook.sheets( t_sheet ).col_fmts( p_col ).fontId := p_fontId;
 workbook.sheets( t_sheet ).col_fmts( p_col ).fillId := p_fillId;
 workbook.sheets( t_sheet ).col_fmts( p_col ).borderId := p_borderId;
 workbook.sheets( t_sheet ).col_fmts( p_col ).alignment := p_alignment;
 end;
 --
 procedure set_row
 ( p_row pls_integer
 , p_numFmtId pls_integer := null
 , p_fontId pls_integer := null
 , p_fillId pls_integer := null
 , p_borderId pls_integer := null
 , p_alignment tp_alignment := null
 , p_sheet pls_integer := null
 )
 is
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 workbook.sheets( t_sheet ).row_fmts( p_row ).numFmtId := p_numFmtId;
 workbook.sheets( t_sheet ).row_fmts( p_row ).fontId := p_fontId;
 workbook.sheets( t_sheet ).row_fmts( p_row ).fillId := p_fillId;
 workbook.sheets( t_sheet ).row_fmts( p_row ).borderId := p_borderId;
 workbook.sheets( t_sheet ).row_fmts( p_row ).alignment := p_alignment;
 end;
 --
 procedure freeze_rows
 ( p_nr_rows pls_integer := 1
 , p_sheet pls_integer := null
 )
 is
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 workbook.sheets( t_sheet ).freeze_cols := null;
 workbook.sheets( t_sheet ).freeze_rows := p_nr_rows;
 end;
 --
 procedure freeze_cols
 ( p_nr_cols pls_integer := 1
 , p_sheet pls_integer := null
 )
 is
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 workbook.sheets( t_sheet ).freeze_rows := null;
 workbook.sheets( t_sheet ).freeze_cols := p_nr_cols;
 end;
 --
 procedure freeze_pane
 ( p_col pls_integer
 , p_row pls_integer
 , p_sheet pls_integer := null
 )
 is
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 workbook.sheets( t_sheet ).freeze_rows := p_row;
 workbook.sheets( t_sheet ).freeze_cols := p_col;
 end;
 --
 procedure set_autofilter
 ( p_column_start pls_integer := null
 , p_column_end pls_integer := null
 , p_row_start pls_integer := null
 , p_row_end pls_integer := null
 , p_sheet pls_integer := null
 )
 is
 t_ind pls_integer;
 t_sheet pls_integer := nvl( p_sheet, workbook.sheets.count() );
 begin
 t_ind := 1;
 workbook.sheets( t_sheet ).autofilters( t_ind ).column_start := p_column_start;
 workbook.sheets( t_sheet ).autofilters( t_ind ).column_end := p_column_end;
 workbook.sheets( t_sheet ).autofilters( t_ind ).row_start := p_row_start;
 workbook.sheets( t_sheet ).autofilters( t_ind ).row_end := p_row_end;
 defined_name
 ( p_column_start
 , p_row_start
 , p_column_end
 , p_row_end
 , '_xlnm._FilterDatabase'
 , t_sheet
 , t_sheet - 1
 );
 end;
 --
 /*
 procedure add1xml
 ( p_excel in out nocopy blob
 , p_filename varchar2
 , p_xml clob
 )
 is
 t_tmp blob;
 c_step constant number := 24396;
 begin
 dbms_lob.createtemporary( t_tmp, true );
 for i in 0 .. trunc( length( p_xml ) / c_step )
 loop
 dbms_lob.append( t_tmp, utl_i18n.string_to_raw( substr( p_xml, i * c_step + 1, c_step ), 'AL32UTF8' ) );
 end loop;
 add1file( p_excel, p_filename, t_tmp );
 dbms_lob.freetemporary( t_tmp );
 end;
 */
 --
 procedure add1xml
 ( p_excel in out nocopy blob
 , p_filename varchar2
 , p_xml clob
 )
 is
 t_tmp blob;
 dest_offset integer := 1;
 src_offset integer := 1;
 lang_context integer;
 warning integer;
 begin
 lang_context := dbms_lob.DEFAULT_LANG_CTX;
 dbms_lob.createtemporary( t_tmp, true );
 dbms_lob.converttoblob
 ( t_tmp
 , p_xml
 , dbms_lob.lobmaxsize
 , dest_offset
 , src_offset
 , nls_charset_id( 'AL32UTF8' )
 , lang_context
 , warning
 );
 add1file( p_excel, p_filename, t_tmp );
 dbms_lob.freetemporary( t_tmp );
 end;
 --
 function finish
 return blob
 is
 t_excel blob;
 t_xxx clob;
 t_tmp varchar2(32767 char);
 t_str varchar2(32767 char);
 t_c number;
 t_h number;
 t_w number;
 t_cw number;
 t_cell varchar2(1000 char);
 t_row_ind pls_integer;
 t_col_min pls_integer;
 t_col_max pls_integer;
 t_col_ind pls_integer;
 t_len pls_integer;
 ts timestamp := systimestamp;
 begin
 dbms_lob.createtemporary( t_excel, true );
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"&gt;
 &lt;Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/&gt;
 &lt;Default Extension="xml" ContentType="application/xml"/&gt;
 &lt;Default Extension="vml" ContentType="application/vnd.openxmlformats-officedocument.vmlDrawing"/&gt;
 &lt;Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/&gt;';
 for s in 1 .. workbook.sheets.count()
 loop
 t_xxx := t_xxx || '
 &lt;Override PartName="/xl/worksheets/sheet' || s || '.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/&gt;';
 end loop;
 t_xxx := t_xxx || '
 &lt;Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/&gt;
 &lt;Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/&gt;
 &lt;Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/&gt;
 &lt;Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/&gt;
 &lt;Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/&gt;';
 for s in 1 .. workbook.sheets.count()
 loop
 if workbook.sheets( s ).comments.count() &gt; 0
 then
 t_xxx := t_xxx || '
 &lt;Override PartName="/xl/comments' || s || '.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml"/&gt;';
 end if;
 end loop;
 t_xxx := t_xxx || '
 &lt;/Types&gt;';
 add1xml( t_excel, '[Content_Types].xml', t_xxx );
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
 &lt;dc:creator&gt;' || sys_context( 'userenv', 'os_user' ) || '&lt;/dc:creator&gt;
 &lt;cp:lastModifiedBy&gt;' || sys_context( 'userenv', 'os_user' ) || '&lt;/cp:lastModifiedBy&gt;
 &lt;dcterms:created xsi:type="dcterms:W3CDTF"&gt;' || to_char( current_timestamp, 'yyyy-mm-dd"T"hh24:mi:ssTZH:TZM' ) || '&lt;/dcterms:created&gt;
 &lt;dcterms:modified xsi:type="dcterms:W3CDTF"&gt;' || to_char( current_timestamp, 'yyyy-mm-dd"T"hh24:mi:ssTZH:TZM' ) || '&lt;/dcterms:modified&gt;
 &lt;/cp:coreProperties&gt;';
 add1xml( t_excel, 'docProps/core.xml', t_xxx );
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"&gt;
 &lt;Application&gt;Microsoft Excel&lt;/Application&gt;
 &lt;DocSecurity&gt;0&lt;/DocSecurity&gt;
 &lt;ScaleCrop&gt;false&lt;/ScaleCrop&gt;
 &lt;HeadingPairs&gt;
 &lt;vt:vector size="2" baseType="variant"&gt;
 &lt;vt:variant&gt;
 &lt;vt:lpstr&gt;Worksheets&lt;/vt:lpstr&gt;
 &lt;/vt:variant&gt;
 &lt;vt:variant&gt;
 &lt;vt:i4&gt;' || workbook.sheets.count() || '&lt;/vt:i4&gt;
 &lt;/vt:variant&gt;
 &lt;/vt:vector&gt;
 &lt;/HeadingPairs&gt;
 &lt;TitlesOfParts&gt;
 &lt;vt:vector size="' || workbook.sheets.count() || '" baseType="lpstr"&gt;';
 for s in 1 .. workbook.sheets.count()
 loop
 t_xxx := t_xxx || '
 &lt;vt:lpstr&gt;' || workbook.sheets( s ).name || '&lt;/vt:lpstr&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/vt:vector&gt;
 &lt;/TitlesOfParts&gt;
 &lt;LinksUpToDate&gt;false&lt;/LinksUpToDate&gt;
 &lt;SharedDoc&gt;false&lt;/SharedDoc&gt;
 &lt;HyperlinksChanged&gt;false&lt;/HyperlinksChanged&gt;
 &lt;AppVersion&gt;14.0300&lt;/AppVersion&gt;
 &lt;/Properties&gt;';
 add1xml( t_excel, 'docProps/app.xml', t_xxx );
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"&gt;
 &lt;Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/&gt;
 &lt;Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/&gt;
 &lt;Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/&gt;
 &lt;/Relationships&gt;';
 add1xml( t_excel, '_rels/.rels', t_xxx );
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"&gt;';
 if workbook.numFmts.count() &gt; 0
 then
 t_xxx := t_xxx || '&lt;numFmts count="' || workbook.numFmts.count() || '"&gt;';
 for n in 1 .. workbook.numFmts.count()
 loop
 t_xxx := t_xxx || '&lt;numFmt numFmtId="' || workbook.numFmts( n ).numFmtId || '" formatCode="' || workbook.numFmts( n ).formatCode || '"/&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/numFmts&gt;';
 end if;
 t_xxx := t_xxx || '&lt;fonts count="' || workbook.fonts.count() || '" x14ac:knownFonts="1"&gt;';
 for f in 0 .. workbook.fonts.count() - 1
 loop
 t_xxx := t_xxx || '&lt;font&gt;' ||
 case when workbook.fonts( f ).bold then '&lt;b/&gt;' end ||
 case when workbook.fonts( f ).italic then '&lt;i/&gt;' end ||
 case when workbook.fonts( f ).underline then '&lt;u/&gt;' end ||
 '&lt;sz val="' || to_char( workbook.fonts( f ).fontsize, 'TM9', 'NLS_NUMERIC_CHARACTERS=.,' ) || '"/&gt;
 &lt;color ' || case when workbook.fonts( f ).rgb is not null
 then 'rgb="' || workbook.fonts( f ).rgb
 else 'theme="' || workbook.fonts( f ).theme
 end || '"/&gt;
 &lt;name val="' || workbook.fonts( f ).name || '"/&gt;
 &lt;family val="' || workbook.fonts( f ).family || '"/&gt;
 &lt;scheme val="none"/&gt;
 &lt;/font&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/fonts&gt;
 &lt;fills count="' || workbook.fills.count() || '"&gt;';
 for f in 0 .. workbook.fills.count() - 1
 loop
 t_xxx := t_xxx || '&lt;fill&gt;&lt;patternFill patternType="' || workbook.fills( f ).patternType || '"&gt;' ||
 case when workbook.fills( f ).fgRGB is not null then '&lt;fgColor rgb="' || workbook.fills( f ).fgRGB || '"/&gt;' end ||
 '&lt;/patternFill&gt;&lt;/fill&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/fills&gt;
 &lt;borders count="' || workbook.borders.count() || '"&gt;';
 for b in 0 .. workbook.borders.count() - 1
 loop
 t_xxx := t_xxx || '&lt;border&gt;' ||
 case when workbook.borders( b ).left is null then '&lt;left/&gt;' else '&lt;left style="' || workbook.borders( b ).left || '"/&gt;' end ||
 case when workbook.borders( b ).right is null then '&lt;right/&gt;' else '&lt;right style="' || workbook.borders( b ).right || '"/&gt;' end ||
 case when workbook.borders( b ).top is null then '&lt;top/&gt;' else '&lt;top style="' || workbook.borders( b ).top || '"/&gt;' end ||
 case when workbook.borders( b ).bottom is null then '&lt;bottom/&gt;' else '&lt;bottom style="' || workbook.borders( b ).bottom || '"/&gt;' end ||
 '&lt;/border&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/borders&gt;
 &lt;cellStyleXfs count="1"&gt;
 &lt;xf numFmtId="0" fontId="0" fillId="0" borderId="0"/&gt;
 &lt;/cellStyleXfs&gt;
 &lt;cellXfs count="' || ( workbook.cellXfs.count() + 1 ) || '"&gt;
 &lt;xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/&gt;';
 for x in 1 .. workbook.cellXfs.count()
 loop
 t_xxx := t_xxx || '&lt;xf numFmtId="' || workbook.cellXfs( x ).numFmtId || '" fontId="' || workbook.cellXfs( x ).fontId || '" fillId="' || workbook.cellXfs( x ).fillId || '" borderId="' || workbook.cellXfs( x ).borderId || '"&gt;';
 if ( workbook.cellXfs( x ).alignment.horizontal is not null
 or workbook.cellXfs( x ).alignment.vertical is not null
 or workbook.cellXfs( x ).alignment.wrapText
 )
 then
 t_xxx := t_xxx || '&lt;alignment' ||
 case when workbook.cellXfs( x ).alignment.horizontal is not null then ' horizontal="' || workbook.cellXfs( x ).alignment.horizontal || '"' end ||
 case when workbook.cellXfs( x ).alignment.vertical is not null then ' vertical="' || workbook.cellXfs( x ).alignment.vertical || '"' end ||
 case when workbook.cellXfs( x ).alignment.wrapText then ' wrapText="true"' end || '/&gt;';
 end if;
 t_xxx := t_xxx || '&lt;/xf&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/cellXfs&gt;
 &lt;cellStyles count="1"&gt;
 &lt;cellStyle name="Normal" xfId="0" builtinId="0"/&gt;
 &lt;/cellStyles&gt;
 &lt;dxfs count="0"/&gt;
 &lt;tableStyles count="0" defaultTableStyle="TableStyleMedium2" defaultPivotStyle="PivotStyleLight16"/&gt;
 &lt;extLst&gt;
 &lt;ext uri="{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"&gt;
 &lt;x14:slicerStyles defaultSlicerStyle="SlicerStyleLight1"/&gt;
 &lt;/ext&gt;
 &lt;/extLst&gt;
 &lt;/styleSheet&gt;';
 add1xml( t_excel, 'xl/styles.xml', t_xxx );
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"&gt;
 &lt;fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="9302"/&gt;
 &lt;workbookPr date1904="true" defaultThemeVersion="124226"/&gt;
 &lt;bookViews&gt;
 &lt;workbookView xWindow="120" yWindow="45" windowWidth="19155" windowHeight="4935"/&gt;
 &lt;/bookViews&gt;
 &lt;sheets&gt;';
 for s in 1 .. workbook.sheets.count()
 loop
 t_xxx := t_xxx || '
 &lt;sheet name="' || workbook.sheets( s ).name || '" sheetId="' || s || '" r:id="rId' || ( 9 + s ) || '"/&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/sheets&gt;';
 if workbook.defined_names.count() &gt; 0
 then
 t_xxx := t_xxx || '&lt;definedNames&gt;';
 for s in 1 .. workbook.defined_names.count()
 loop
 t_xxx := t_xxx || '
 &lt;definedName name="' || workbook.defined_names( s ).name || '"' ||
 case when workbook.defined_names( s ).sheet is not null then ' localSheetId="' || to_char( workbook.defined_names( s ).sheet ) || '"' end ||
 '&gt;' || workbook.defined_names( s ).ref || '&lt;/definedName&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/definedNames&gt;';
 end if;
 t_xxx := t_xxx || '&lt;calcPr calcId="144525"/&gt;&lt;/workbook&gt;';
 add1xml( t_excel, 'xl/workbook.xml', t_xxx );
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"&gt;
 &lt;a:themeElements&gt;
 &lt;a:clrScheme name="Office"&gt;
 &lt;a:dk1&gt;
 &lt;a:sysClr val="windowText" lastClr="000000"/&gt;
 &lt;/a:dk1&gt;
 &lt;a:lt1&gt;
 &lt;a:sysClr val="window" lastClr="FFFFFF"/&gt;
 &lt;/a:lt1&gt;
 &lt;a:dk2&gt;
 &lt;a:srgbClr val="1F497D"/&gt;
 &lt;/a:dk2&gt;
 &lt;a:lt2&gt;
 &lt;a:srgbClr val="EEECE1"/&gt;
 &lt;/a:lt2&gt;
 &lt;a:accent1&gt;
 &lt;a:srgbClr val="4F81BD"/&gt;
 &lt;/a:accent1&gt;
 &lt;a:accent2&gt;
 &lt;a:srgbClr val="C0504D"/&gt;
 &lt;/a:accent2&gt;
 &lt;a:accent3&gt;
 &lt;a:srgbClr val="9BBB59"/&gt;
 &lt;/a:accent3&gt;
 &lt;a:accent4&gt;
 &lt;a:srgbClr val="8064A2"/&gt;
 &lt;/a:accent4&gt;
 &lt;a:accent5&gt;
 &lt;a:srgbClr val="4BACC6"/&gt;
 &lt;/a:accent5&gt;
 &lt;a:accent6&gt;
 &lt;a:srgbClr val="F79646"/&gt;
 &lt;/a:accent6&gt;
 &lt;a:hlink&gt;
 &lt;a:srgbClr val="0000FF"/&gt;
 &lt;/a:hlink&gt;
 &lt;a:folHlink&gt;
 &lt;a:srgbClr val="800080"/&gt;
 &lt;/a:folHlink&gt;
 &lt;/a:clrScheme&gt;
 &lt;a:fontScheme name="Office"&gt;
 &lt;a:majorFont&gt;
 &lt;a:latin typeface="Cambria"/&gt;
 &lt;a:ea typeface=""/&gt;
 &lt;a:cs typeface=""/&gt;
 &lt;a:font script="Jpan" typeface="MS P????"/&gt;
 &lt;a:font script="Hang" typeface="?? ??"/&gt;
 &lt;a:font script="Hans" typeface="??"/&gt;
 &lt;a:font script="Hant" typeface="????"/&gt;
 &lt;a:font script="Arab" typeface="Times New Roman"/&gt;
 &lt;a:font script="Hebr" typeface="Times New Roman"/&gt;
 &lt;a:font script="Thai" typeface="Tahoma"/&gt;
 &lt;a:font script="Ethi" typeface="Nyala"/&gt;
 &lt;a:font script="Beng" typeface="Vrinda"/&gt;
 &lt;a:font script="Gujr" typeface="Shruti"/&gt;
 &lt;a:font script="Khmr" typeface="MoolBoran"/&gt;
 &lt;a:font script="Knda" typeface="Tunga"/&gt;
 &lt;a:font script="Guru" typeface="Raavi"/&gt;
 &lt;a:font script="Cans" typeface="Euphemia"/&gt;
 &lt;a:font script="Cher" typeface="Plantagenet Cherokee"/&gt;
 &lt;a:font script="Yiii" typeface="Microsoft Yi Baiti"/&gt;
 &lt;a:font script="Tibt" typeface="Microsoft Himalaya"/&gt;
 &lt;a:font script="Thaa" typeface="MV Boli"/&gt;
 &lt;a:font script="Deva" typeface="Mangal"/&gt;
 &lt;a:font script="Telu" typeface="Gautami"/&gt;
 &lt;a:font script="Taml" typeface="Latha"/&gt;
 &lt;a:font script="Syrc" typeface="Estrangelo Edessa"/&gt;
 &lt;a:font script="Orya" typeface="Kalinga"/&gt;
 &lt;a:font script="Mlym" typeface="Kartika"/&gt;
 &lt;a:font script="Laoo" typeface="DokChampa"/&gt;
 &lt;a:font script="Sinh" typeface="Iskoola Pota"/&gt;
 &lt;a:font script="Mong" typeface="Mongolian Baiti"/&gt;
 &lt;a:font script="Viet" typeface="Times New Roman"/&gt;
 &lt;a:font script="Uigh" typeface="Microsoft Uighur"/&gt;
 &lt;a:font script="Geor" typeface="Sylfaen"/&gt;
 &lt;/a:majorFont&gt;
 &lt;a:minorFont&gt;
 &lt;a:latin typeface="Calibri"/&gt;
 &lt;a:ea typeface=""/&gt;
 &lt;a:cs typeface=""/&gt;
 &lt;a:font script="Jpan" typeface="MS P????"/&gt;
 &lt;a:font script="Hang" typeface="?? ??"/&gt;
 &lt;a:font script="Hans" typeface="??"/&gt;
 &lt;a:font script="Hant" typeface="????"/&gt;
 &lt;a:font script="Arab" typeface="Arial"/&gt;
 &lt;a:font script="Hebr" typeface="Arial"/&gt;
 &lt;a:font script="Thai" typeface="Tahoma"/&gt;
 &lt;a:font script="Ethi" typeface="Nyala"/&gt;
 &lt;a:font script="Beng" typeface="Vrinda"/&gt;
 &lt;a:font script="Gujr" typeface="Shruti"/&gt;
 &lt;a:font script="Khmr" typeface="DaunPenh"/&gt;
 &lt;a:font script="Knda" typeface="Tunga"/&gt;
 &lt;a:font script="Guru" typeface="Raavi"/&gt;
 &lt;a:font script="Cans" typeface="Euphemia"/&gt;
 &lt;a:font script="Cher" typeface="Plantagenet Cherokee"/&gt;
 &lt;a:font script="Yiii" typeface="Microsoft Yi Baiti"/&gt;
 &lt;a:font script="Tibt" typeface="Microsoft Himalaya"/&gt;
 &lt;a:font script="Thaa" typeface="MV Boli"/&gt;
 &lt;a:font script="Deva" typeface="Mangal"/&gt;
 &lt;a:font script="Telu" typeface="Gautami"/&gt;
 &lt;a:font script="Taml" typeface="Latha"/&gt;
 &lt;a:font script="Syrc" typeface="Estrangelo Edessa"/&gt;
 &lt;a:font script="Orya" typeface="Kalinga"/&gt;
 &lt;a:font script="Mlym" typeface="Kartika"/&gt;
 &lt;a:font script="Laoo" typeface="DokChampa"/&gt;
 &lt;a:font script="Sinh" typeface="Iskoola Pota"/&gt;
 &lt;a:font script="Mong" typeface="Mongolian Baiti"/&gt;
 &lt;a:font script="Viet" typeface="Arial"/&gt;
 &lt;a:font script="Uigh" typeface="Microsoft Uighur"/&gt;
 &lt;a:font script="Geor" typeface="Sylfaen"/&gt;
 &lt;/a:minorFont&gt;
 &lt;/a:fontScheme&gt;
 &lt;a:fmtScheme name="Office"&gt;
 &lt;a:fillStyleLst&gt;
 &lt;a:solidFill&gt;
 &lt;a:schemeClr val="phClr"/&gt;
 &lt;/a:solidFill&gt;
 &lt;a:gradFill rotWithShape="1"&gt;
 &lt;a:gsLst&gt;
 &lt;a:gs pos="0"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:tint val="50000"/&gt;
 &lt;a:satMod val="300000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;a:gs pos="35000"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:tint val="37000"/&gt;
 &lt;a:satMod val="300000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;a:gs pos="100000"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:tint val="15000"/&gt;
 &lt;a:satMod val="350000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;/a:gsLst&gt;
 &lt;a:lin ang="16200000" scaled="1"/&gt;
 &lt;/a:gradFill&gt;
 &lt;a:gradFill rotWithShape="1"&gt;
 &lt;a:gsLst&gt;
 &lt;a:gs pos="0"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:shade val="51000"/&gt;
 &lt;a:satMod val="130000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;a:gs pos="80000"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:shade val="93000"/&gt;
 &lt;a:satMod val="130000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;a:gs pos="100000"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:shade val="94000"/&gt;
 &lt;a:satMod val="135000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;/a:gsLst&gt;
 &lt;a:lin ang="16200000" scaled="0"/&gt;
 &lt;/a:gradFill&gt;
 &lt;/a:fillStyleLst&gt;
 &lt;a:lnStyleLst&gt;
 &lt;a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"&gt;
 &lt;a:solidFill&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:shade val="95000"/&gt;
 &lt;a:satMod val="105000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:solidFill&gt;
 &lt;a:prstDash val="solid"/&gt;
 &lt;/a:ln&gt;
 &lt;a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"&gt;
 &lt;a:solidFill&gt;
 &lt;a:schemeClr val="phClr"/&gt;
 &lt;/a:solidFill&gt;
 &lt;a:prstDash val="solid"/&gt;
 &lt;/a:ln&gt;
 &lt;a:ln w="38100" cap="flat" cmpd="sng" algn="ctr"&gt;
 &lt;a:solidFill&gt;
 &lt;a:schemeClr val="phClr"/&gt;
 &lt;/a:solidFill&gt;
 &lt;a:prstDash val="solid"/&gt;
 &lt;/a:ln&gt;
 &lt;/a:lnStyleLst&gt;
 &lt;a:effectStyleLst&gt;
 &lt;a:effectStyle&gt;
 &lt;a:effectLst&gt;
 &lt;a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0"&gt;
 &lt;a:srgbClr val="000000"&gt;
 &lt;a:alpha val="38000"/&gt;
 &lt;/a:srgbClr&gt;
 &lt;/a:outerShdw&gt;
 &lt;/a:effectLst&gt;
 &lt;/a:effectStyle&gt;
 &lt;a:effectStyle&gt;
 &lt;a:effectLst&gt;
 &lt;a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"&gt;
 &lt;a:srgbClr val="000000"&gt;
 &lt;a:alpha val="35000"/&gt;
 &lt;/a:srgbClr&gt;
 &lt;/a:outerShdw&gt;
 &lt;/a:effectLst&gt;
 &lt;/a:effectStyle&gt;
 &lt;a:effectStyle&gt;
 &lt;a:effectLst&gt;
 &lt;a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"&gt;
 &lt;a:srgbClr val="000000"&gt;
 &lt;a:alpha val="35000"/&gt;
 &lt;/a:srgbClr&gt;
 &lt;/a:outerShdw&gt;
 &lt;/a:effectLst&gt;
 &lt;a:scene3d&gt;
 &lt;a:camera prst="orthographicFront"&gt;
 &lt;a:rot lat="0" lon="0" rev="0"/&gt;
 &lt;/a:camera&gt;
 &lt;a:lightRig rig="threePt" dir="t"&gt;
 &lt;a:rot lat="0" lon="0" rev="1200000"/&gt;
 &lt;/a:lightRig&gt;
 &lt;/a:scene3d&gt;
 &lt;a:sp3d&gt;
 &lt;a:bevelT w="63500" h="25400"/&gt;
 &lt;/a:sp3d&gt;
 &lt;/a:effectStyle&gt;
 &lt;/a:effectStyleLst&gt;
 &lt;a:bgFillStyleLst&gt;
 &lt;a:solidFill&gt;
 &lt;a:schemeClr val="phClr"/&gt;
 &lt;/a:solidFill&gt;
 &lt;a:gradFill rotWithShape="1"&gt;
 &lt;a:gsLst&gt;
 &lt;a:gs pos="0"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:tint val="40000"/&gt;
 &lt;a:satMod val="350000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;a:gs pos="40000"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:tint val="45000"/&gt;
 &lt;a:shade val="99000"/&gt;
 &lt;a:satMod val="350000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;a:gs pos="100000"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:shade val="20000"/&gt;
 &lt;a:satMod val="255000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;/a:gsLst&gt;
 &lt;a:path path="circle"&gt;
 &lt;a:fillToRect l="50000" t="-80000" r="50000" b="180000"/&gt;
 &lt;/a:path&gt;
 &lt;/a:gradFill&gt;
 &lt;a:gradFill rotWithShape="1"&gt;
 &lt;a:gsLst&gt;
 &lt;a:gs pos="0"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:tint val="80000"/&gt;
 &lt;a:satMod val="300000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;a:gs pos="100000"&gt;
 &lt;a:schemeClr val="phClr"&gt;
 &lt;a:shade val="30000"/&gt;
 &lt;a:satMod val="200000"/&gt;
 &lt;/a:schemeClr&gt;
 &lt;/a:gs&gt;
 &lt;/a:gsLst&gt;
 &lt;a:path path="circle"&gt;
 &lt;a:fillToRect l="50000" t="50000" r="50000" b="50000"/&gt;
 &lt;/a:path&gt;
 &lt;/a:gradFill&gt;
 &lt;/a:bgFillStyleLst&gt;
 &lt;/a:fmtScheme&gt;
 &lt;/a:themeElements&gt;
 &lt;a:objectDefaults/&gt;
 &lt;a:extraClrSchemeLst/&gt;
 &lt;/a:theme&gt;';
 add1xml( t_excel, 'xl/theme/theme1.xml', t_xxx );
 for s in 1 .. workbook.sheets.count()
 loop
 t_col_min := 16384;
 t_col_max := 1;
 t_row_ind := workbook.sheets( s ).rows.first();
 while t_row_ind is not null
 loop
 t_col_min := least( t_col_min, workbook.sheets( s ).rows( t_row_ind ).first() );
 t_col_max := greatest( t_col_max, workbook.sheets( s ).rows( t_row_ind ).last() );
 t_row_ind := workbook.sheets( s ).rows.next( t_row_ind );
 end loop;
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"&gt;
 &lt;dimension ref="' || alfan_col( t_col_min ) || workbook.sheets( s ).rows.first() || ':' || alfan_col( t_col_max ) || workbook.sheets( s ).rows.last() || '"/&gt;
 &lt;sheetViews&gt;
 &lt;sheetView' || case when s = 1 then ' tabSelected="1"' end || ' workbookViewId="0"&gt;';
 if workbook.sheets( s ).freeze_rows &gt; 0 and workbook.sheets( s ).freeze_cols &gt; 0
 then
 t_xxx := t_xxx || ( '&lt;pane xSplit="' || workbook.sheets( s ).freeze_cols || '" '
 || 'ySplit="' || workbook.sheets( s ).freeze_rows || '" '
 || 'topLeftCell="' || alfan_col( workbook.sheets( s ).freeze_cols + 1 ) || ( workbook.sheets( s ).freeze_rows + 1 ) || '" '
 || 'activePane="bottomLeft" state="frozen"/&gt;'
 );
 else
 if workbook.sheets( s ).freeze_rows &gt; 0
 then
 t_xxx := t_xxx || '&lt;pane ySplit="' || workbook.sheets( s ).freeze_rows || '" topLeftCell="A' || ( workbook.sheets( s ).freeze_rows + 1 ) || '" activePane="bottomLeft" state="frozen"/&gt;';
 end if;
 if workbook.sheets( s ).freeze_cols &gt; 0
 then
 t_xxx := t_xxx || '&lt;pane xSplit="' || workbook.sheets( s ).freeze_cols || '" topLeftCell="' || alfan_col( workbook.sheets( s ).freeze_cols + 1 ) || '1" activePane="bottomLeft" state="frozen"/&gt;';
 end if;
 end if;
 t_xxx := t_xxx || '&lt;/sheetView&gt;
 &lt;/sheetViews&gt;
 &lt;sheetFormatPr defaultRowHeight="15" x14ac:dyDescent="0.25"/&gt;';
 if workbook.sheets( s ).widths.count() &gt; 0
 then
 t_xxx := t_xxx || '&lt;cols&gt;';
 t_col_ind := workbook.sheets( s ).widths.first();
 while t_col_ind is not null
 loop
 t_xxx := t_xxx ||
 '&lt;col min="' || t_col_ind || '" max="' || t_col_ind || '" width="' || to_char( workbook.sheets( s ).widths( t_col_ind ), 'TM9', 'NLS_NUMERIC_CHARACTERS=.,' ) || '" customWidth="1"/&gt;';
 t_col_ind := workbook.sheets( s ).widths.next( t_col_ind );
 end loop;
 t_xxx := t_xxx || '&lt;/cols&gt;';
 end if;
 t_xxx := t_xxx || '&lt;sheetData&gt;';
 t_row_ind := workbook.sheets( s ).rows.first();
 t_tmp := null;
 while t_row_ind is not null
 loop
 t_tmp := t_tmp || '&lt;row r="' || t_row_ind || '" spans="' || t_col_min || ':' || t_col_max || '"&gt;';
 t_len := length( t_tmp );
 t_col_ind := workbook.sheets( s ).rows( t_row_ind ).first();
 while t_col_ind is not null
 loop
 t_cell := '&lt;c r="' || alfan_col( t_col_ind ) || t_row_ind || '"'
 || ' ' || workbook.sheets( s ).rows( t_row_ind )( t_col_ind ).style
 || '&gt;&lt;v&gt;'
 || to_char( workbook.sheets( s ).rows( t_row_ind )( t_col_ind ).value, 'TM9', 'NLS_NUMERIC_CHARACTERS=.,' )
 || '&lt;/v&gt;&lt;/c&gt;';
 if t_len &gt; 32000
 then
 dbms_lob.writeappend( t_xxx, t_len, t_tmp );
 t_tmp := null;
 t_len := 0;
 end if;
 t_tmp := t_tmp || t_cell;
 t_len := t_len + length( t_cell );
 t_col_ind := workbook.sheets( s ).rows( t_row_ind ).next( t_col_ind );
 end loop;
 t_tmp := t_tmp || '&lt;/row&gt;';
 t_row_ind := workbook.sheets( s ).rows.next( t_row_ind );
 end loop;
 t_tmp := t_tmp || '&lt;/sheetData&gt;';
 t_len := length( t_tmp );
 dbms_lob.writeappend( t_xxx, t_len, t_tmp );
 for a in 1 .. workbook.sheets( s ).autofilters.count()
 loop
 t_xxx := t_xxx || '&lt;autoFilter ref="' ||
 alfan_col( nvl( workbook.sheets( s ).autofilters( a ).column_start, t_col_min ) ) ||
 nvl( workbook.sheets( s ).autofilters( a ).row_start, workbook.sheets( s ).rows.first() ) || ':' ||
 alfan_col( coalesce( workbook.sheets( s ).autofilters( a ).column_end, workbook.sheets( s ).autofilters( a ).column_start, t_col_max ) ) ||
 nvl( workbook.sheets( s ).autofilters( a ).row_end, workbook.sheets( s ).rows.last() ) || '"/&gt;';
 end loop;
 if workbook.sheets( s ).mergecells.count() &gt; 0
 then
 t_xxx := t_xxx || '&lt;mergeCells count="' || to_char( workbook.sheets( s ).mergecells.count() ) || '"&gt;';
 for m in 1 .. workbook.sheets( s ).mergecells.count()
 loop
 t_xxx := t_xxx || '&lt;mergeCell ref="' || workbook.sheets( s ).mergecells( m ) || '"/&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/mergeCells&gt;';
 end if;
 --
 if workbook.sheets( s ).validations.count() &gt; 0
 then
 t_xxx := t_xxx || '&lt;dataValidations count="' || to_char( workbook.sheets( s ).validations.count() ) || '"&gt;';
 for m in 1 .. workbook.sheets( s ).validations.count()
 loop
 t_xxx := t_xxx || '&lt;dataValidation' ||
 ' type="' || workbook.sheets( s ).validations( m ).type || '"' ||
 ' errorStyle="' || workbook.sheets( s ).validations( m ).errorstyle || '"' ||
 ' allowBlank="' || case when nvl( workbook.sheets( s ).validations( m ).allowBlank, true ) then '1' else '0' end || '"' ||
 ' sqref="' || workbook.sheets( s ).validations( m ).sqref || '"';
 if workbook.sheets( s ).validations( m ).prompt is not null
 then
 t_xxx := t_xxx || ' showInputMessage="1" prompt="' || workbook.sheets( s ).validations( m ).prompt || '"';
 if workbook.sheets( s ).validations( m ).title is not null
 then
 t_xxx := t_xxx || ' promptTitle="' || workbook.sheets( s ).validations( m ).title || '"';
 end if;
 end if;
 if workbook.sheets( s ).validations( m ).showerrormessage
 then
 t_xxx := t_xxx || ' showErrorMessage="1"';
 if workbook.sheets( s ).validations( m ).error_title is not null
 then
 t_xxx := t_xxx || ' errorTitle="' || workbook.sheets( s ).validations( m ).error_title || '"';
 end if;
 if workbook.sheets( s ).validations( m ).error_txt is not null
 then
 t_xxx := t_xxx || ' error="' || workbook.sheets( s ).validations( m ).error_txt || '"';
 end if;
 end if;
 t_xxx := t_xxx || '&gt;';
 if workbook.sheets( s ).validations( m ).formula1 is not null
 then
 t_xxx := t_xxx || '&lt;formula1&gt;' || workbook.sheets( s ).validations( m ).formula1 || '&lt;/formula1&gt;';
 end if;
 if workbook.sheets( s ).validations( m ).formula2 is not null
 then
 t_xxx := t_xxx || '&lt;formula2&gt;' || workbook.sheets( s ).validations( m ).formula2 || '&lt;/formula2&gt;';
 end if;
 t_xxx := t_xxx || '&lt;/dataValidation&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/dataValidations&gt;';
 end if;
 --
 if workbook.sheets( s ).hyperlinks.count() &gt; 0
 then
 t_xxx := t_xxx || '&lt;hyperlinks&gt;';
 for h in 1 .. workbook.sheets( s ).hyperlinks.count()
 loop
 t_xxx := t_xxx || '&lt;hyperlink ref="' || workbook.sheets( s ).hyperlinks( h ).cell || '" r:id="rId' || h || '"/&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/hyperlinks&gt;';
 end if;
 t_xxx := t_xxx || '&lt;pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/&gt;';
 if workbook.sheets( s ).comments.count() &gt; 0
 then
 t_xxx := t_xxx || '&lt;legacyDrawing r:id="rId' || ( workbook.sheets( s ).hyperlinks.count() + 1 ) || '"/&gt;';
 end if;
 --
 t_xxx := t_xxx || '&lt;/worksheet&gt;';
 add1xml( t_excel, 'xl/worksheets/sheet' || s || '.xml', t_xxx );
 if workbook.sheets( s ).hyperlinks.count() &gt; 0 or workbook.sheets( s ).comments.count() &gt; 0
 then
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"&gt;';
 if workbook.sheets( s ).comments.count() &gt; 0
 then
 t_xxx := t_xxx || '&lt;Relationship Id="rId' || ( workbook.sheets( s ).hyperlinks.count() + 2 ) || '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments" Target="../comments' || s || '.xml"/&gt;';
 t_xxx := t_xxx || '&lt;Relationship Id="rId' || ( workbook.sheets( s ).hyperlinks.count() + 1 ) || '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing" Target="../drawings/vmlDrawing' || s || '.vml"/&gt;';
 end if;
 for h in 1 .. workbook.sheets( s ).hyperlinks.count()
 loop
 t_xxx := t_xxx || '&lt;Relationship Id="rId' || h || '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="' || workbook.sheets( s ).hyperlinks( h ).url || '" TargetMode="External"/&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/Relationships&gt;';
 add1xml( t_excel, 'xl/worksheets/_rels/sheet' || s || '.xml.rels', t_xxx );
 end if;
 --
 if workbook.sheets( s ).comments.count() &gt; 0
 then
 declare
 cnt pls_integer;
 author_ind tp_author;
 -- t_col_ind := workbook.sheets( s ).widths.next( t_col_ind );
 begin
 authors.delete();
 for c in 1 .. workbook.sheets( s ).comments.count()
 loop
 authors( workbook.sheets( s ).comments( c ).author ) := 0;
 end loop;
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;comments xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"&gt;
 &lt;authors&gt;';
 cnt := 0;
 author_ind := authors.first();
 while author_ind is not null or authors.next( author_ind ) is not null
 loop
 authors( author_ind ) := cnt;
 t_xxx := t_xxx || '&lt;author&gt;' || author_ind || '&lt;/author&gt;';
 cnt := cnt + 1;
 author_ind := authors.next( author_ind );
 end loop;
 end;
 t_xxx := t_xxx || '&lt;/authors&gt;&lt;commentList&gt;';
 for c in 1 .. workbook.sheets( s ).comments.count()
 loop
 t_xxx := t_xxx || '&lt;comment ref="' || alfan_col( workbook.sheets( s ).comments( c ).column ) ||
 to_char( workbook.sheets( s ).comments( c ).row || '" authorId="' || authors( workbook.sheets( s ).comments( c ).author ) ) || '"&gt;
 &lt;text&gt;';
 if workbook.sheets( s ).comments( c ).author is not null
 then
 t_xxx := t_xxx || '&lt;r&gt;&lt;rPr&gt;&lt;b/&gt;&lt;sz val="9"/&gt;&lt;color indexed="81"/&gt;&lt;rFont val="Tahoma"/&gt;&lt;charset val="1"/&gt;&lt;/rPr&gt;&lt;t xml:space="preserve"&gt;' ||
 workbook.sheets( s ).comments( c ).author || ':&lt;/t&gt;&lt;/r&gt;';
 end if;
 t_xxx := t_xxx || '&lt;r&gt;&lt;rPr&gt;&lt;sz val="9"/&gt;&lt;color indexed="81"/&gt;&lt;rFont val="Tahoma"/&gt;&lt;charset val="1"/&gt;&lt;/rPr&gt;&lt;t xml:space="preserve"&gt;' ||
 case when workbook.sheets( s ).comments( c ).author is not null then '
 ' end || workbook.sheets( s ).comments( c ).text || '&lt;/t&gt;&lt;/r&gt;&lt;/text&gt;&lt;/comment&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/commentList&gt;&lt;/comments&gt;';
 add1xml( t_excel, 'xl/comments' || s || '.xml', t_xxx );
 t_xxx := '&lt;xml xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel"&gt;
 &lt;o:shapelayout v:ext="edit"&gt;&lt;o:idmap v:ext="edit" data="2"/&gt;&lt;/o:shapelayout&gt;
 &lt;v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" path="m,l,21600r21600,l21600,xe"&gt;&lt;v:stroke joinstyle="miter"/&gt;&lt;v:path gradientshapeok="t" o:connecttype="rect"/&gt;&lt;/v:shapetype&gt;';
 for c in 1 .. workbook.sheets( s ).comments.count()
 loop
 t_xxx := t_xxx || '&lt;v:shape id="_x0000_s' || to_char( c ) || '" type="#_x0000_t202"
 style="position:absolute;margin-left:35.25pt;margin-top:3pt;z-index:' || to_char( c ) || ';visibility:hidden;" fillcolor="#ffffe1" o:insetmode="auto"&gt;
 &lt;v:fill color2="#ffffe1"/&gt;&lt;v:shadow on="t" color="black" obscured="t"/&gt;&lt;v:path o:connecttype="none"/&gt;
 &lt;v:textbox style="mso-direction-alt:auto"&gt;&lt;div style="text-align:left"&gt;&lt;/div&gt;&lt;/v:textbox&gt;
 &lt;x:ClientData ObjectType="Note"&gt;&lt;x:MoveWithCells/&gt;&lt;x:SizeWithCells/&gt;';
 t_w := workbook.sheets( s ).comments( c ).width;
 t_c := 1;
 loop
 if workbook.sheets( s ).widths.exists( workbook.sheets( s ).comments( c ).column + t_c )
 then
 t_cw := 256 * workbook.sheets( s ).widths( workbook.sheets( s ).comments( c ).column + t_c );
 t_cw := trunc( ( t_cw + 18 ) / 256 * 7); -- assume default 11 point Calibri
 else
 t_cw := 64;
 end if;
 exit when t_w &lt; t_cw;
 t_c := t_c + 1;
 t_w := t_w - t_cw;
 end loop;
 t_h := workbook.sheets( s ).comments( c ).height;
 t_xxx := t_xxx || to_char( '&lt;x:Anchor&gt;' || workbook.sheets( s ).comments( c ).column || ',15,' ||
 workbook.sheets( s ).comments( c ).row || ',30,' ||
 ( workbook.sheets( s ).comments( c ).column + t_c - 1 ) || ',' || round( t_w ) || ',' ||
 ( workbook.sheets( s ).comments( c ).row + 1 + trunc( t_h / 20 ) ) || ',' || mod( t_h, 20 ) || '&lt;/x:Anchor&gt;' );
 t_xxx := t_xxx || to_char( '&lt;x:AutoFill&gt;False&lt;/x:AutoFill&gt;&lt;x:Row&gt;' ||
 ( workbook.sheets( s ).comments( c ).row - 1 ) || '&lt;/x:Row&gt;&lt;x:Column&gt;' ||
 ( workbook.sheets( s ).comments( c ).column - 1 ) || '&lt;/x:Column&gt;&lt;/x:ClientData&gt;&lt;/v:shape&gt;' );
 end loop;
 t_xxx := t_xxx || '&lt;/xml&gt;';
 add1xml( t_excel, 'xl/drawings/vmlDrawing' || s || '.vml', t_xxx );
 end if;
 --
 end loop;
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"&gt;
 &lt;Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/&gt;
 &lt;Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/&gt;
 &lt;Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/&gt;';
 for s in 1 .. workbook.sheets.count()
 loop
 t_xxx := t_xxx || '
 &lt;Relationship Id="rId' || ( 9 + s ) || '" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet' || s || '.xml"/&gt;';
 end loop;
 t_xxx := t_xxx || '&lt;/Relationships&gt;';
 add1xml( t_excel, 'xl/_rels/workbook.xml.rels', t_xxx );
 t_xxx := '&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;
 &lt;sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="' || workbook.str_cnt || '" uniqueCount="' || workbook.strings.count() || '"&gt;';
 t_tmp := null;
 for i in 0 .. workbook.str_ind.count() - 1
 loop
 t_str := '&lt;si&gt;&lt;t&gt;' || dbms_xmlgen.convert( substr( workbook.str_ind( i ), 1, 32000 ) ) || '&lt;/t&gt;&lt;/si&gt;';
 if length( t_tmp ) + length( t_str ) &gt; 32000
 then
 t_xxx := t_xxx || t_tmp;
 t_tmp := null;
 end if;
 t_tmp := t_tmp || t_str;
 end loop;
 t_xxx := t_xxx || t_tmp || '&lt;/sst&gt;';
 add1xml( t_excel, 'xl/sharedStrings.xml', t_xxx );
 finish_zip( t_excel );
 clear_workbook;
 return t_excel;
 end;
 --
 procedure save
 ( p_directory varchar2
 , p_filename varchar2
 )
 is
 begin
 blob2file( finish, p_directory, p_filename );
 end;
 --
 procedure query2sheet
 ( p_sql varchar2
 , p_column_headers boolean := true
 , p_directory varchar2 := null
 , p_filename varchar2 := null
 , p_sheet pls_integer := null
 )
 is
 t_sheet pls_integer;
 t_c integer;
 t_col_cnt integer;
 t_desc_tab dbms_sql.desc_tab2;
 d_tab dbms_sql.date_table;
 n_tab dbms_sql.number_table;
 v_tab dbms_sql.varchar2_table;
 t_bulk_size pls_integer := 200;
 t_r integer;
 t_cur_row pls_integer;
 begin
 if p_sheet is null
 then
 new_sheet;
 end if;
 t_c := dbms_sql.open_cursor;
 dbms_sql.parse( t_c, p_sql, dbms_sql.native );
 dbms_sql.describe_columns2( t_c, t_col_cnt, t_desc_tab );
 for c in 1 .. t_col_cnt
 loop
 if p_column_headers
 then
 cell( c, 1, t_desc_tab( c ).col_name, p_sheet =&gt; t_sheet );
 end if;
 -- dbms_output.put_line( t_desc_tab( c ).col_name || ' ' || t_desc_tab( c ).col_type );
 case
 when t_desc_tab( c ).col_type in ( 2, 100, 101 )
 then
 dbms_sql.define_array( t_c, c, n_tab, t_bulk_size, 1 );
 when t_desc_tab( c ).col_type in ( 12, 178, 179, 180, 181 , 231 )
 then
 dbms_sql.define_array( t_c, c, d_tab, t_bulk_size, 1 );
 when t_desc_tab( c ).col_type in ( 1, 8, 9, 96, 112 )
 then
 dbms_sql.define_array( t_c, c, v_tab, t_bulk_size, 1 );
 else
 null;
 end case;
 end loop;
 --
 t_cur_row := case when p_column_headers then 2 else 1 end;
 t_sheet := nvl( p_sheet, workbook.sheets.count() );
 --
 t_r := dbms_sql.execute( t_c );
 loop
 t_r := dbms_sql.fetch_rows( t_c );
 if t_r &gt; 0
 then
 for c in 1 .. t_col_cnt
 loop
 case
 when t_desc_tab( c ).col_type in ( 2, 100, 101 )
 then
 dbms_sql.column_value( t_c, c, n_tab );
 for i in 0 .. t_r - 1
 loop
 if n_tab( i + n_tab.first() ) is not null
 then
 cell( c, t_cur_row + i, n_tab( i + n_tab.first() ), p_sheet =&gt; t_sheet );
 end if;
 end loop;
 n_tab.delete;
 when t_desc_tab( c ).col_type in ( 12, 178, 179, 180, 181 , 231 )
 then
 dbms_sql.column_value( t_c, c, d_tab );
 for i in 0 .. t_r - 1
 loop
 if d_tab( i + d_tab.first() ) is not null
 then
 cell( c, t_cur_row + i, d_tab( i + d_tab.first() ), p_sheet =&gt; t_sheet );
 end if;
 end loop;
 d_tab.delete;
 when t_desc_tab( c ).col_type in ( 1, 8, 9, 96, 112 )
 then
 dbms_sql.column_value( t_c, c, v_tab );
 for i in 0 .. t_r - 1
 loop
 if v_tab( i + v_tab.first() ) is not null
 then
 cell( c, t_cur_row + i, v_tab( i + v_tab.first() ), p_sheet =&gt; t_sheet );
 end if;
 end loop;
 v_tab.delete;
 else
 null;
 end case;
 end loop;
 end if;
 exit when t_r != t_bulk_size;
 t_cur_row := t_cur_row + t_r;
 end loop;
 dbms_sql.close_cursor( t_c );
 if ( p_directory is not null and p_filename is not null )
 then
 save( p_directory, p_filename );
 end if;
 exception
 when others
 then
 if dbms_sql.is_open( t_c )
 then
 dbms_sql.close_cursor( t_c );
 end if;
 end;
 end;
 /
 -------------- Generate Excel Files (Run generate_excel.sql) -------------------

-- Directory name is case sensitive in below code --

begin
 as_xlsx.query2sheet( 'select * from dual' );
 as_xlsx.save( 'MY_DIR', 'my.xlsx' );
 end;
 /
 begin
 as_xlsx.query2sheet( 'select * from swx.log_record where tran_datetime &gt; sysdate - 14' );
 as_xlsx.save( 'MY_DIR', 'test_output.xlsx' );
 end;
 /

2. Generate PDF by first creating a procedure as_pdf3 (code is in a3_pdf.sql)

————– Generate PDF Files (Run generate_pdf.sql) ——————-

— Directory name is case sensitive ib below code–

 declare
 t_rc sys_refcursor;
 t_query varchar2(1000);
 begin
 as_pdf3.init;
 as_pdf3.set_page_proc( q'~
 begin
 as_pdf3.set_font( 'helvetica', 8 );
 as_pdf3.put_txt( 10, 15, 'Page #PAGE_NR# of "PAGE_COUNT#' );
 as_pdf3.set_font( 'helvetica', 12 );
 as_pdf3.put_txt( 350, 15, 'This is a footer text' );
 as_pdf3.set_font( 'helvetica', 'B', 15 );
 as_pdf3.put_txt( 200, 780, 'This is a header text' );
 end;~' );
 as_pdf3.set_page_proc( q'~
 begin
 as_pdf3.set_font( 'Colonna MT', 'N', 50 );
 as_pdf3.put_txt( 150, 200, 'Watermark Watermark Watermark', 60 );
 end;~' );
 t_query := 'select rownum, sysdate + level, ''example'' || level from dual connect by level &lt;= 50';
 as_pdf3.query2table( t_query );
 open t_rc for t_query;
 as_pdf3.refcursor2table( t_rc );
 as_pdf3.save_pdf('MY_DIR','my.pdf');
 end;
 /
 declare
 t_rc sys_refcursor;
 t_query varchar2(1000);
 begin
 as_pdf3.init;
 as_pdf3.set_page_proc( q'~
 begin
 as_pdf3.set_font( 'helvetica', 8 );
 as_pdf3.put_txt( 10, 15, 'Page #PAGE_NR# of "PAGE_COUNT#' );
 as_pdf3.set_font( 'helvetica', 12 );
 as_pdf3.put_txt( 350, 15, 'This is a footer text' );
 as_pdf3.set_font( 'helvetica', 'B', 15 );
 as_pdf3.put_txt( 200, 780, 'This is a header text' );
 end;~' );
 as_pdf3.set_page_proc( q'~
 begin
 as_pdf3.set_font( 'Colonna MT', 'N', 50 );
 as_pdf3.put_txt( 150, 200, 'Watermark Watermark Watermark', 60 );
 end;~' );
 t_query := 'select cardholder,card_status from swx.cardholder';
 as_pdf3.query2table( t_query );
 open t_rc for t_query;
 as_pdf3.refcursor2table( t_rc );
 as_pdf3.save_pdf('MY_DIR','my.pdf');
 end;
 /

— Above is working Sample, below is Template —


declare
 t_rc sys_refcursor;
 t_query varchar2(1000);
 begin
 as_pdf3.init;
 as_pdf3.load_ttf_font( 'MY_FONTS', 'COLONNA.TTF', 'CID' );
 as_pdf3.set_page_proc( q'~
 begin
 as_pdf3.set_font( 'helvetica', 8 );
 as_pdf3.put_txt( 10, 15, 'Page #PAGE_NR# of "PAGE_COUNT#' );
 as_pdf3.set_font( 'helvetica', 12 );
 as_pdf3.put_txt( 350, 15, 'This is a footer text' );
 as_pdf3.set_font( 'helvetica', 'B', 15 );
 as_pdf3.put_txt( 200, 780, 'This is a header text' );
 as_pdf3.put_image( 'MY_DIR', 'logo.jpg', 500, 15 );
 end;~' );
 as_pdf3.set_page_proc( q'~
 begin
 as_pdf3.set_font( 'Colonna MT', 'N', 50 );
 as_pdf3.put_txt( 150, 200, 'Watermark Watermark Watermark', 60 );
 end;~' );
 t_query := 'select rownum, sysdate + level, ''example'' || level from dual connect by level &lt;= 50';
 as_pdf3.query2table( t_query );
 open t_rc for t_query;
 as_pdf3.refcursor2table( t_rc );
 as_pdf3.save_pdf('MY_DIR','my.pdf');
 end;
 /

— This is the PL SQL Package as_pdf3 to generate the pdf’s —


CREATE OR REPLACE package as_pdf3
 is
 /**********************************************
 **
 ** Author: Anton Scheffer
 ** Date: 11-04-2012
 ** Website: http://technology.amis.nl
 ** See also: http://technology.amis.nl/?p=17718
 **
 ** Changelog:
 ** Date: 13-08-2012
 ** added two procedure for Andreas Weiden
 ** see https://sourceforge.net/projects/pljrxml2pdf/
 ** Date: 16-04-2012
 ** changed code for parse_png
 ** Date: 15-04-2012
 ** only dbms_lob.freetemporary for temporary blobs
 ** Date: 11-04-2012
 ** Initial release of as_pdf3
 **
 ******************************************************************************
 ******************************************************************************
 Copyright (C) 2012 by Anton Scheffer

Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.

******************************************************************************
 ******************************************** */
 --
 c_get_page_width constant pls_integer := 0;
 c_get_page_height constant pls_integer := 1;
 c_get_margin_top constant pls_integer := 2;
 c_get_margin_right constant pls_integer := 3;
 c_get_margin_bottom constant pls_integer := 4;
 c_get_margin_left constant pls_integer := 5;
 c_get_x constant pls_integer := 6;
 c_get_y constant pls_integer := 7;
 c_get_fontsize constant pls_integer := 8;
 c_get_current_font constant pls_integer := 9;
 --
 function file2blob( p_dir varchar2, p_file_name varchar2 )
 return blob;
 --
 function conv2uu( p_value number, p_unit varchar2 )
 return number;
 --
 procedure set_page_size
 ( p_width number
 , p_height number
 , p_unit varchar2 := 'cm'
 );
 --
 procedure set_page_format( p_format varchar2 := 'A4' );
 --
 procedure set_page_orientation( p_orientation varchar2 := 'PORTRAIT' );
 --
 procedure set_margins
 ( p_top number := null
 , p_left number := null
 , p_bottom number := null
 , p_right number := null
 , p_unit varchar2 := 'cm'
 );
 --
 procedure set_info
 ( p_title varchar2 := null
 , p_author varchar2 := null
 , p_subject varchar2 := null
 , p_keywords varchar2 := null
 );
 --
 procedure init;
 --
 function get_pdf
 return blob;
 --
 procedure save_pdf
 ( p_dir varchar2 := 'MY_DIR'
 , p_filename varchar2 := 'my.pdf'
 , p_freeblob boolean := true
 );
 --
 procedure txt2page( p_txt varchar2 );
 --
 procedure put_txt( p_x number, p_y number, p_txt varchar2, p_degrees_rotation number := null );
 --
 function str_len( p_txt varchar2 )
 return number;
 --
 procedure write
 ( p_txt in varchar2
 , p_x in number := null
 , p_y in number := null
 , p_line_height in number := null
 , p_start in number := null -- left side of the available text box
 , p_width in number := null -- width of the available text box
 , p_alignment in varchar2 := null
 );
 --
 procedure set_font
 ( p_index pls_integer
 , p_fontsize_pt number
 , p_output_to_doc boolean := true
 );
 --
 function set_font
 ( p_fontname varchar2
 , p_fontsize_pt number
 , p_output_to_doc boolean := true
 )
 return pls_integer;
 --
 procedure set_font
 ( p_fontname varchar2
 , p_fontsize_pt number
 , p_output_to_doc boolean := true
 );
 --
 function set_font
 ( p_family varchar2
 , p_style varchar2 := 'N'
 , p_fontsize_pt number := null
 , p_output_to_doc boolean := true
 )
 return pls_integer;
 --
 procedure set_font
 ( p_family varchar2
 , p_style varchar2 := 'N'
 , p_fontsize_pt number := null
 , p_output_to_doc boolean := true
 );
 --
 procedure new_page;
 --
 function load_ttf_font
 ( p_font blob
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 , p_offset number := 1
 )
 return pls_integer;
 --
 procedure load_ttf_font
 ( p_font blob
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 , p_offset number := 1
 );
 --
 function load_ttf_font
 ( p_dir varchar2 := 'MY_FONTS'
 , p_filename varchar2 := 'BAUHS93.TTF'
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 )
 return pls_integer;
 --
 procedure load_ttf_font
 ( p_dir varchar2 := 'MY_FONTS'
 , p_filename varchar2 := 'BAUHS93.TTF'
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 );
 --
 procedure load_ttc_fonts
 ( p_ttc blob
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 );
 --
 procedure load_ttc_fonts
 ( p_dir varchar2 := 'MY_FONTS'
 , p_filename varchar2 := 'CAMBRIA.TTC'
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 );
 --
 procedure set_color( p_rgb varchar2 := '000000' );
 --
 procedure set_color
 ( p_red number := 0
 , p_green number := 0
 , p_blue number := 0
 );
 --
 procedure set_bk_color( p_rgb varchar2 := 'ffffff' );
 --
 procedure set_bk_color
 ( p_red number := 0
 , p_green number := 0
 , p_blue number := 0
 );
 --
 procedure horizontal_line
 ( p_x in number
 , p_y in number
 , p_width in number
 , p_line_width in number := 0.5
 , p_line_color in varchar2 := '000000'
 );
 --
 procedure vertical_line
 ( p_x in number
 , p_y in number
 , p_height in number
 , p_line_width in number := 0.5
 , p_line_color in varchar2 := '000000'
 );
 --
 procedure rect
 ( p_x in number
 , p_y in number
 , p_width in number
 , p_height in number
 , p_line_color in varchar2 := null
 , p_fill_color in varchar2 := null
 , p_line_width in number := 0.5
 );
 --
 function get( p_what in pls_integer )
 return number;
 --
 procedure put_image
 ( p_img blob
 , p_x number
 , p_y number
 , p_width number := null
 , p_height number := null
 , p_align varchar2 := 'center'
 , p_valign varchar2 := 'top'
 );
 --
 procedure put_image
 ( p_dir varchar2
 , p_file_name varchar2
 , p_x number
 , p_y number
 , p_width number := null
 , p_height number := null
 , p_align varchar2 := 'center'
 , p_valign varchar2 := 'top'
 );
 --
 procedure put_image
 ( p_url varchar2
 , p_x number
 , p_y number
 , p_width number := null
 , p_height number := null
 , p_align varchar2 := 'center'
 , p_valign varchar2 := 'top'
 );
 --
 procedure set_page_proc( p_src clob );
 --
 type tp_col_widths is table of number;
 type tp_headers is table of varchar2(32767);
 --
 procedure query2table
 ( p_query varchar2
 , p_widths tp_col_widths := null
 , p_headers tp_headers := null
 );
 --
 $IF not DBMS_DB_VERSION.VER_LE_10 $THEN
 procedure refcursor2table
 ( p_rc sys_refcursor
 , p_widths tp_col_widths := null
 , p_headers tp_headers := null
 );
 --
 $END
 --
 procedure pr_goto_page( i_npage number );
 --
 procedure pr_goto_current_page;
 /*
 begin
 as_pdf3.init;
 as_pdf3.write( 'Minimal usage' );
 as_pdf3.save_pdf;
 end;
 --
 begin
 as_pdf3.init;
 as_pdf3.write( 'Some text with a newline-character included at this "
 " place.' );
 as_pdf3.write( 'Normally text written with as_pdf3.write() is appended after the previous text. But the text wraps automaticly to a new line.' );
 as_pdf3.write( 'But you can place your text at any place', -1, 700 );
 as_pdf3.write( 'you want', 100, 650 );
 as_pdf3.write( 'You can even align it, left, right, or centered', p_y =&gt; 600, p_alignment =&gt; 'right' );
 as_pdf3.save_pdf;
 end;
 --
 begin
 as_pdf3.init;
 as_pdf3.write( 'The 14 standard PDF-fonts and the WINDOWS-1252 encoding.' );
 as_pdf3.set_font( 'helvetica' );
 as_pdf3.write( 'helvetica, normal: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, 700 );
 as_pdf3.set_font( 'helvetica', 'I' );
 as_pdf3.write( 'helvetica, italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 as_pdf3.set_font( 'helvetica', 'b' );
 as_pdf3.write( 'helvetica, bold: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 as_pdf3.set_font( 'helvetica', 'BI' );
 as_pdf3.write( 'helvetica, bold italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 as_pdf3.set_font( 'times' );
 as_pdf3.write( 'times, normal: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, 625 );
 as_pdf3.set_font( 'times', 'I' );
 as_pdf3.write( 'times, italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 as_pdf3.set_font( 'times', 'b' );
 as_pdf3.write( 'times, bold: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 as_pdf3.set_font( 'times', 'BI' );
 as_pdf3.write( 'times, bold italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 as_pdf3.set_font( 'courier' );
 as_pdf3.write( 'courier, normal: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, 550 );
 as_pdf3.set_font( 'courier', 'I' );
 as_pdf3.write( 'courier, italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 as_pdf3.set_font( 'courier', 'b' );
 as_pdf3.write( 'courier, bold: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 as_pdf3.set_font( 'courier', 'BI' );
 as_pdf3.write( 'courier, bold italic: ' || 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 --
 as_pdf3.set_font( 'courier' );
 as_pdf3.write( 'symbol:', -1, 475 );
 as_pdf3.set_font( 'symbol' );
 as_pdf3.write( 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 as_pdf3.set_font( 'courier' );
 as_pdf3.write( 'zapfdingbats:', -1, -1 );
 as_pdf3.set_font( 'zapfdingbats' );
 as_pdf3.write( 'The quick brown fox jumps over the lazy dog. 1234567890', -1, -1 );
 --
 as_pdf3.set_font( 'times', 'N', 20 );
 as_pdf3.write( 'times, normal with fontsize 20pt', -1, 400 );
 as_pdf3.set_font( 'times', 'N', 6 );
 as_pdf3.write( 'times, normal with fontsize 5pt', -1, -1 );
 as_pdf3.save_pdf;
 end;
 --
 declare
 x pls_integer;
 begin
 as_pdf3.init;
 as_pdf3.write( 'But others fonts and encodings are possible using TrueType fontfiles.' );
 x := as_pdf3.load_ttf_font( 'MY_FONTS', 'refsan.ttf', 'CID', p_compress =&gt; false );
 as_pdf3.set_font( x, 12 );
 as_pdf3.write( 'The Windows MSReference SansSerif font contains a lot of encodings, for instance', -1, 700 );
 as_pdf3.set_font( x, 15 );
 as_pdf3.write( 'Albanian: Kush mund të lexoni këtë diçka si kjo', -1, -1 );
 as_pdf3.write( 'Croatic: Tko može citati to nešto poput ovoga', -1, -1 );
 as_pdf3.write( 'Russian: ??? ????? ????????? ??? ???-?? ????? ?????', -1, -1);
 as_pdf3.write( 'Greek: ????? µp??e? ?a d?aß?se? a?t? t? ??t? sa? a?t?', -1, -1 );
 --
 as_pdf3.set_font( 'helvetica', 12 );
 as_pdf3.write( 'Or by using a TrueType collection file (ttc).', -1, 600 );
 as_pdf3.load_ttc_fonts( 'MY_FONTS', 'cambria.ttc', p_embed =&gt; true, p_compress =&gt; false );
 as_pdf3.set_font( 'cambria', 15 ); -- font family
 as_pdf3.write( 'Anton, testing 1,2,3 with Cambria', -1, -1 );
 as_pdf3.set_font( 'CambriaMT', 15 ); -- fontname
 as_pdf3.write( 'Anton, testing 1,2,3 with CambriaMath', -1, -1 );
 as_pdf3.save_pdf;
 end;
 --
 begin
 as_pdf3.init;
 for i in 1 .. 10
 loop
 as_pdf3.horizontal_line( 30, 700 - i * 15, 100, i );
 end loop;
 for i in 1 .. 10
 loop
 as_pdf3.vertical_line( 150 + i * 15, 700, 100, i );
 end loop;
 for i in 0 .. 255
 loop
 as_pdf3.horizontal_line( 330, 700 - i, 100, 2, p_line_color =&gt; to_char( i, 'fm0x' ) || to_char( i, 'fm0x' ) || to_char( i, 'fm0x' ) );
 end loop;
 as_pdf3.save_pdf;
 end;
 --
 declare
 t_logo varchar2(32767) :=
 '/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkS' ||
 'Ew8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJ' ||
 'CQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIy' ||
 'MjIyMjIyMjIyMjIyMjL/wAARCABqAJYDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEA' ||
 'AAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIh' ||
 'MUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6' ||
 'Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZ' ||
 'mqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx' ||
 '8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREA' ||
 'AgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV' ||
 'YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hp' ||
 'anN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPE' ||
 'xcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3' ||
 '+iiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAoorifiX4pk' ||
 '8PaCILR9t9eExxsOqL/E315AHuaUmkrs1oUZVqipw3ZU8X/FCz0KeSw02Jb2+Thy' ||
 'WxHGfQkdT7D8686ufih4suGJW/jgXssUC8fnk1ydvbz3lzHb28bzTyttRF5LMa7H' ||
 'Uvh+3hvRI9T1+7kUPIsf2ezUMykgnlmIHbtXI5znqtj66ng8DhFGFRJyffVv5Fnw' ||
 'r8QfEEvinTodR1N5rSaYRyIyIAd3A5A9SK7X4qeINV0Gz019LvGtmlkcOVVTkADH' ||
 'UGvNdDsPDepa7ZWdtPrMU8syiN3EWFbqCcfSu3+NXGnaOM5/ev8A+giqi5ezepy1' ||
 '6NF4+koxsne6scronxO1+01i2l1K/e6st2Joyij5T1IwByOv4V75BPHc28c8Lh45' ||
 'FDKynIIPINfJleheGPiPJong+802Ul7uEYsCRkYbsfZev04pUqttJF5rlSqKM6Eb' ||
 'PZpGv8RfiFf2etDTNDu/I+zf8fEqqG3Of4eQen8z7VB8O/GGv6x4vhs9Q1J57don' ||
 'YoUUZIHHQV5fI7yyNJIxd3JZmY5JJ6k12nwo/wCR8t/+uEn8qUajlM6K+Ao0MFJc' ||
 'qbS363O1+KviTWNBuNMXS71rYTLIZAqqd2NuOoPqayvht4u17WvFf2TUdRe4g+zu' ||
 '+woo5BXB4HuaX42f8fOj/wC7L/Naw/hH/wAjv/26yfzWqcn7W1zjpUKTytzcVez1' ||
 'sdt8QviJN4euhpelJG16VDSyuMiIHoMdz3rzZviN4tZif7YkHsIkx/6DTPiAkqeO' ||
 '9WE2dxlBXP8Ad2jH6VJ4H8LWfizUp7S51FrV40DoiKC0nPOM+nH51MpTlOyOvDYX' ||
 'C4fCKrUinpdu1zovAfjvXL7xfZ2ep6i89tOGTayKPmxkHgD0/WvbK83074RWWman' ||
 'a30Wr3Zkt5VlUFVwSDnHSvQZ7u2tU3XE8cSju7gD9a6Kakl7x89mVTD1aqlh1pbt' ||
 'YnorDfxj4eWTy11W3lfpthbzD+S5q7ZavBfy7IIrrGM75Ld41/NgKu6OB05pXaL9' ||
 'FFFMgK8A+K+ote+NZYM5jtIliA9yNx/mPyr37tXzP42cv421gseftLD8sCsK7909' ||
 'zIIKWJcn0Rf8Aa5o3h3WJtR1VZmdY9kAjj3YJ+8fbjj8TW/8QPHuj+J/D6WNgLjz' ||
 'lnWQ+ZHtGAD3z71wNno2qahEZbLTrq5jB2l4oiwB9Mii80XVNPhE17p11bxE7d8s' ||
 'RUZ9MmsFOSjZLQ9+phMNUxKqyl7y6XNHwR/yO+j/APXyP5GvQ/jX/wAg/SP+ur/+' ||
 'givPPBH/ACO+j/8AXyP5GvQ/jX/yD9I/66v/AOgirh/CZyYv/kZ0vT/M8y8PaM/i' ||
 'DV106J9kskcjRk9NyqSAfY4xWbLFJBM8UqFJI2KurDBUjgg11nww/wCR/sP92T/0' ||
 'A16B4p+Gq614xtNQg2pZznN+AcH5e4/3uh/OojT5o3R0V8xjh8S6dT4bX+ev5nk1' ||
 '7oU+n+HtP1W4yv26RxEhH8CgfN+JP5V0Hwo/5Hy3/wCuEn8q6b4zxJBY6JFEgSNG' ||
 'kVVUYAAC4Fcn8MbqG08bQyzyBEEMnJ78dB6mq5VGokZ+3licunUe7TOn+Nn/AB86' ||
 'P/uy/wA1rD+EZA8bEk4AtJMn8Vru/GHhW58c3lhKrmws7ZX3yzp875x91e3Tvj6V' ||
 'zduPDPh6/GneGtOl8Qa2wKmRnzGvrk/dx9B+NXKL9pzHDQxEHgPq8dZWd/L1exf+' ||
 'JHhuPxFdw6hozLPeIPLnCnCbBkhi5+UEfXofauEtLWy8OX0N7L4hQ3sDBli01POI' ||
 'PoXOF9j1r1O18E6nrhSfxbqJkjHK6baHy4E9jjlq84+IXg4+GNWE1qh/sy5JMX/T' ||
 'Nu6f1Ht9KVSL+OxeXYiMrYSU/wCu13/l8zudCn1jx3avcxaybO1Vijorbph9Qu1V' ||
 'z/wKt+y+HHh63fzrq3k1CfqZbyQyc/Tp+leL+CvE0vhjxDDc7z9klIjuU7FSev1H' ||
 'X8/WvpNWDqGUggjIIrSk1NXe5wZpTq4Spywdova2hFbWVrZxiO2t4oUH8MaBR+lT' ||
 '0UVseM23uFFFFAgr5y+I9obPx5qQIwsrLKvuCo/qDX0bXkPxn0YiSw1mNflINvKf' ||
 'Tuv/ALNWNdXiexklZU8Uk/tKxb+C16j6bqVgSN8cyygezDH81rR+MQ/4o6L/AK+0' ||
 '/k1cV8JrXVv+Em+2WkJNgEaO5kY4XHUAerZxxXpHxB0b/hIdBSxjv7W1kWdZC1w2' ||
 'BgA/40oXdOxti1CjmanfS6b8jxbwR/yO+j/9fI/ka9D+Nf8AyD9I/wCur/8AoIrG' ||
 '8PeCJtJ8RWOoHVLa7S2lDslpFJIT7AgY/Ouu8a+HNT8bx2EVvB9hit3ZmkuiMkEY' ||
 '4VST+eKiMGqbR1YnFUZY+nWT91L/ADPN/hh/yP8AYf7sn/oBr3y51O1tHEbybpj0' ||
 'ijBZz/wEc1xXh34WafoVyl7PqNzNcoD8yN5SgEYPTn9auar438K+FI3hhkjluB1h' ||
 'tQGYn/abp+ZzWlNckfeOHMakcbiL0E5aW2F8SeFJPG01kb7fYWlqWYKCDLJnHXsv' ||
 'T3/Cqdzqngz4cwGC0hje+xjyofnmY/7THp+P5VjHUvHfjxWXToBoult/y1clWcfX' ||
 'GT+AH1qx4Q+GN/oXiSLUtQurO5iRW+UKxbceh5HX3ovd3ivmChGnT5MRU0X2U/zZ' ||
 'yfjXxR4p1K2ga/gfTNOu9xhtlOGdRjl+56j0HtS/CL/kd/8At1k/mteg/EHwRfeL' ||
 'ZbB7O5t4RbhwwlB53Y6Y+lZ/gf4c6l4Y8Q/2jdXlrLH5LR7Yw2ckj1+lRyS9pc7F' ||
 'jsM8BKmrRk09EQeNviHrnhnxLLp8FtZvBsWSNpFbcQRznB9Qa4bxF8Q9Y8S6abC8' ||
 'htI4CwY+Wh3ZByOSTivS/H/gC78V6haXllcwQPHGY5PNB+YZyMY+prkP+FMa3/0E' ||
 'rH8n/wAKKiqNtLYeBrZdCnCc7Ka9TzcKzkKoJZuAB3NfVWjwS22i2UE3MscCI/1C' ||
 'gGuE8LfCe20e/i1DU7sXk8Lbo40TbGrdic8nFekVVGm46s485x9PEyjGlql1Ciii' ||
 'tzxAooooAKo6vpFnrmmS6ffRl7eXG4A4PByCD26VeooHGTi7rcxL3w9btpEen2Nr' ||
 'aRxRDEcciHaP++SDXG3fhzxxZzCTSpNICDpGqE5/77BP616bRUuKZ0UsVOn5+up5' ||
 'd/wkfxI0vi98Nw3ajq0A5/8AHWP8qgfxz461aQwaX4Za2boWljY7T9W2ivWKTA9K' ||
 'nkfc3WNpbujG/wA/yPKl8DeM/EZ3eI/EDW8DdYITn8MDC/zrqtC+HXh3QiskdmLi' ||
 '4XkTXHzkH2HQfgK6yimoJamdTH1prlTsuy0QgAHAGKWsvWHvVNsLcS+QXIuGhAMg' ||
 'G04wD74z3rHmfxAxkEJuFk3SL8yIUEe07GHq+duR67uMYqm7GEaXNrdHWUVx7z+K' ||
 'y+/yiCixnylC4coX389t+Fx6ZHvTbj/hKHjufmmV1ineLywmN+UMa89cAsPfFLmL' ||
 '+r/3l952VFcpqdvrcEt0bO4vJI1SAx/dOSZCJO2eFxSwPrZ1IBTc+WJ4wBIoEZh2' ||
 'DeScZ3bt2O+cdqLi9j7t+ZHVUVzFzHrUN/dNFLdPaiaMADaSIyMuUGOSDgfTOKWV' ||
 '/ES6XCbcF7j7S4XzAoJi2vs39hzt6e3vTuL2O2qOmormjHqU32F4ptRUGbFysgQE' ||
 'LsY+n97aOK6KJzJEjlGTcoO1uo9j70XIlDl6j6KKKZAUUUUAFFFFABRRRQAUUUUA' ||
 'Y3iDV59JjgNvCkrylwA5IAKxsw6e6gVnnxTchjmwZMSm2MbZ3LMUDKvoVJyN3Toa' ||
 '6ggHqAaMD0FKzNYzglZxuci3i26jghmeCAiXG9Fc7rf94qEP/wB9H05HfrUl74ou' ||
 '4PtKxW0TG3lQM+4lTG7KI2HrkMe/8JrqTGhzlF568daPLTbt2Lt6YxxSs+5ftKd/' ||
 'hOah8SXL6iLcxwSL9ojgKITvIaMMXHJGBn8h1qO48V3Vs1y5sA8EJmVnQklSrbUJ' ||
 'Hoe5HTjtXUrGinKooOMcCl2r6D8qLMXtKd/hOX1fxFqNjd3qW1ik0VpAszkkjgq5' ||
 'zn2Kjjqc0j+JrmNeIoGZIkk25wZ9zEbY8E8jHqeSOldTtU5yBz1poiRcAIox0wOl' ||
 'Fn3D2lOyXKcvZeJ72W5tPtVpFDaXErxiZmK4KiTjnr9wc+9aHh/W21W0WW4MMckh' ||
 'OyNTzx178/pWyY0ZdrIpHoRQsaISVRQT6ChJinUhJO0bDqKKKoxCiiigAooooAKK' ||
 'KKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD//Z';
 begin
 as_pdf3.init;
 as_pdf3.put_image( to_blob( utl_encode.base64_decode( utl_raw.cast_to_raw( t_logo ) ) )
 , 0
 , as_pdf3.get( as_pdf3.C_GET_PAGE_HEIGHT ) - 260
 , as_pdf3.get( as_pdf3.C_GET_PAGE_WIDTH )
 );
 as_pdf3.write( 'jpg, gif and png images are supported.' );
 as_pdf3.write( 'And because PDF 1.3 (thats the format I use) doesn''t support alpha channels, neither does AS_PDF.', -1, -1 );
 as_pdf3.save_pdf;
 end;
 --
 declare
 t_rc sys_refcursor;
 t_query varchar2(1000);
 begin
 as_pdf3.init;
 as_pdf3.load_ttf_font( 'MY_FONTS', 'COLONNA.TTF', 'CID' );
 as_pdf3.set_page_proc( q'~
 begin
 as_pdf3.set_font( 'helvetica', 8 );
 as_pdf3.put_txt( 10, 15, 'Page #PAGE_NR# of "PAGE_COUNT#' );
 as_pdf3.set_font( 'helvetica', 12 );
 as_pdf3.put_txt( 350, 15, 'This is a footer text' );
 as_pdf3.set_font( 'helvetica', 'B', 15 );
 as_pdf3.put_txt( 200, 780, 'This is a header text' );
 as_pdf3.put_image( 'MY_DIR', 'amis.jpg', 500, 15 );
 end;~' );
 as_pdf3.set_page_proc( q'~
 begin
 as_pdf3.set_font( 'Colonna MT', 'N', 50 );
 as_pdf3.put_txt( 150, 200, 'Watermark Watermark Watermark', 60 );
 end;~' );
 t_query := 'select rownum, sysdate + level, ''example'' || level from dual connect by level &lt;= 50';
 as_pdf3.query2table( t_query );
 open t_rc for t_query;
 as_pdf3.refcursor2table( t_rc );
 as_pdf3.save_pdf;
 end;
 */
 end;
 /

CREATE OR REPLACE package body as_pdf3
 is
 --
 type tp_pls_tab is table of pls_integer index by pls_integer;
 type tp_objects_tab is table of number(10) index by pls_integer;
 type tp_pages_tab is table of blob index by pls_integer;
 type tp_settings is record
 ( page_width number
 , page_height number
 , margin_left number
 , margin_right number
 , margin_top number
 , margin_bottom number
 );
 type tp_font is record
 ( standard boolean
 , family varchar2(100)
 , style varchar2(2) -- N Normal
 -- I Italic
 -- B Bold
 -- BI Bold Italic
 , subtype varchar2(15)
 , name varchar2(100)
 , fontname varchar2(100)
 , char_width_tab tp_pls_tab
 , encoding varchar2(100) , charset varchar2(1000)
 , compress_font boolean := true
 , fontsize number
 , unit_norm number
 , bb_xmin pls_integer
 , bb_ymin pls_integer
 , bb_xmax pls_integer
 , bb_ymax pls_integer
 , flags pls_integer
 , first_char pls_integer
 , last_char pls_integer
 , italic_angle number
 , ascent pls_integer
 , descent pls_integer
 , capheight pls_integer
 , stemv pls_integer
 , diff varchar2(32767)
 , cid boolean := false
 , fontfile2 blob
 , ttf_offset pls_integer
 , used_chars tp_pls_tab
 , numGlyphs pls_integer
 , indexToLocFormat pls_integer
 , loca tp_pls_tab
 , code2glyph tp_pls_tab
 , hmetrics tp_pls_tab
 );
 type tp_font_tab is table of tp_font index by pls_integer;
 type tp_img is record
 ( adler32 varchar2(8)
 , width pls_integer
 , height pls_integer
 , color_res pls_integer
 , color_tab raw(768)
 , greyscale boolean
 , pixels blob
 , type varchar2(5)
 , nr_colors pls_integer
 , transparancy_index pls_integer
 );
 type tp_img_tab is table of tp_img index by pls_integer;
 type tp_info is record
 ( title varchar2(1024)
 , author varchar2(1024)
 , subject varchar2(1024)
 , keywords varchar2(32767)
 );
 type tp_page_prcs is table of clob index by pls_integer;
 --
 -- globals
 g_pdf_doc blob; -- the PDF-document being constructed
 g_objects tp_objects_tab;
 g_pages tp_pages_tab;
 g_settings tp_settings;
 g_fonts tp_font_tab;
 g_used_fonts tp_pls_tab;
 g_current_font pls_integer;
 g_images tp_img_tab;
 g_x number; -- current x-location of the "cursor"
 g_y number; -- current y-location of the "cursor"
 g_info tp_info;
 g_page_nr pls_integer;
 g_page_prcs tp_page_prcs;
 --
 -- constants
 c_nl constant varchar2(2) := chr(13) || chr(10);
 --
 function num2raw( p_value number )
 return raw
 is
 begin
 return hextoraw( to_char( p_value, 'FM0XXXXXXX' ) );
 end;
 --
 function raw2num( p_value raw )
 return number
 is
 begin
 return to_number( rawtohex( p_value ), 'XXXXXXXX' );
 end;
 --
 function raw2num( p_value raw, p_pos pls_integer, p_len pls_integer )
 return pls_integer
 is
 begin
 return to_number( rawtohex( utl_raw.substr( p_value, p_pos, p_len ) ), 'XXXXXXXX' );
 end;
 --
 function to_short( p_val raw, p_factor number := 1 )
 return number
 is
 t_rv number;
 begin
 t_rv := to_number( rawtohex( p_val ), 'XXXXXXXXXX' );
 if t_rv &gt; 32767
 then
 t_rv := t_rv - 65536;
 end if;
 return t_rv * p_factor;
 end;
 --
 function blob2num( p_blob blob, p_len integer, p_pos integer )
 return number
 is
 begin
 return to_number( rawtohex( dbms_lob.substr( p_blob, p_len, p_pos ) ), 'xxxxxxxx' );
 end;
 --
 function file2blob( p_dir varchar2, p_file_name varchar2 )
 return blob
 is
 t_raw raw(32767);
 t_blob blob;
 fh utl_file.file_type;
 begin
 fh := utl_file.fopen( p_dir, p_file_name, 'rb' );
 dbms_lob.createtemporary( t_blob, true );
 loop
 begin
 utl_file.get_raw( fh, t_raw );
 dbms_lob.append( t_blob, t_raw );
 exception
 when no_data_found
 then
 exit;
 end;
 end loop;
 utl_file.fclose( fh );
 return t_blob;
 exception
 when others
 then
 if utl_file.is_open( fh )
 then
 utl_file.fclose( fh );
 end if;
 raise;
 end;
 --
 procedure init_core_fonts
 is
 function uncompress_withs( p_compressed_tab varchar2 )
 return tp_pls_tab
 is
 t_rv tp_pls_tab;
 t_tmp raw(32767);
 begin
 if p_compressed_tab is not null
 then
 t_tmp := utl_compress.lz_uncompress

( utl_encode.base64_decode( utl_raw.cast_to_raw( p_compressed_tab ) ) );
 for i in 0 .. 255
 loop
 t_rv( i ) := to_number( utl_raw.substr( t_tmp, i * 4 + 1, 4 ), '0xxxxxxx' );
 end loop;
 end if;
 return t_rv;
 end;
 --
 procedure init_core_font
 ( p_ind pls_integer
 , p_family varchar2
 , p_style varchar2
 , p_name varchar2
 , p_compressed_tab varchar2
 )
 is
 begin
 g_fonts( p_ind ).family := p_family;
 g_fonts( p_ind ).style := p_style;
 g_fonts( p_ind ).name := p_name;
 g_fonts( p_ind ).fontname := p_name;
 g_fonts( p_ind ).standard := true;
 g_fonts( p_ind ).encoding := 'WE8MSWIN1252';
 g_fonts( p_ind ).charset := sys_context( 'userenv', 'LANGUAGE' );
 g_fonts( p_ind ).charset := substr( g_fonts( p_ind ).charset
 , 1
 , instr( g_fonts( p_ind ).charset, '.' )
 ) || g_fonts( p_ind ).encoding;
 g_fonts( p_ind ).char_width_tab := uncompress_withs( p_compressed_tab );
 end;
 begin
 init_core_font( 1, 'helvetica', 'N', 'Helvetica'
 , 'H4sIAAAAAAAAC81Tuw3CMBC94FQMgMQOLAGVGzNCGtc0dAxAT+8lsgE7RKJFomOA'
 || 'SLT4frHjBEFJ8XSX87372C8A1Qr+Ax5gsWGYU7QBAK4x7gTnGLOS6xJPOd8w5NsM'
 || '2OvFvQidAP04j1nyN3F7iSNny3E6DylPeeqbNqvti31vMpfLZuzH86oPdwaeo6X+'
 || '5X6Oz5VHtTqJKfYRNVu6y0ZyG66rdcxzXJe+Q/KJ59kql+bTt5K6lKucXvxWeHKf'
 || '+p6Tfersfh7RHuXMZjHsdUkxBeWtM60gDjLTLoHeKsyDdu6m8VK3qhnUQAmca9BG'
 || 'Dq3nP+sV/4FcD6WOf9K/ne+hdav+DTuNLeYABAAA' );
 --
 init_core_font( 2, 'helvetica', 'I', 'Helvetica-Oblique'
 , 'H4sIAAAAAAAAC81Tuw3CMBC94FQMgMQOLAGVGzNCGtc0dAxAT+8lsgE7RKJFomOA'
 || 'SLT4frHjBEFJ8XSX87372C8A1Qr+Ax5gsWGYU7QBAK4x7gTnGLOS6xJPOd8w5NsM'
 || '2OvFvQidAP04j1nyN3F7iSNny3E6DylPeeqbNqvti31vMpfLZuzH86oPdwaeo6X+'
 || '5X6Oz5VHtTqJKfYRNVu6y0ZyG66rdcxzXJe+Q/KJ59kql+bTt5K6lKucXvxWeHKf'
 || '+p6Tfersfh7RHuXMZjHsdUkxBeWtM60gDjLTLoHeKsyDdu6m8VK3qhnUQAmca9BG'
 || 'Dq3nP+sV/4FcD6WOf9K/ne+hdav+DTuNLeYABAAA' );
 --
 init_core_font( 3, 'helvetica', 'B', 'Helvetica-Bold'
 , 'H4sIAAAAAAAAC8VSsRHCMAx0SJcBcgyRJaBKkxXSqKahYwB6+iyRTbhLSUdHRZUB'
 || 'sOWXLF8SKCn+ZL/0kizZuaJ2/0fn8XBu10SUF28n59wbvoCr51oTD61ofkHyhBwK'
 || '8rXusVaGAb4q3rXOBP4Qz+wfUpzo5FyO4MBr39IH+uLclFvmCTrz1mB5PpSD52N1'
 || 'DfqS988xptibWfbw9Sa/jytf+dz4PqQz6wi63uxxBpCXY7uUj88jNDNy1mYGdl97'
 || '856nt2f4WsOFed4SpzumNCvlT+jpmKC7WgH3PJn9DaZfA42vlgh96d+wkHy0/V95'
 || 'xyv8oj59QbvBN2I/iAuqEAAEAAA=' );
 --
 init_core_font( 4, 'helvetica', 'BI', 'Helvetica-BoldOblique'
 , 'H4sIAAAAAAAAC8VSsRHCMAx0SJcBcgyRJaBKkxXSqKahYwB6+iyRTbhLSUdHRZUB'
 || 'sOWXLF8SKCn+ZL/0kizZuaJ2/0fn8XBu10SUF28n59wbvoCr51oTD61ofkHyhBwK'
 || '8rXusVaGAb4q3rXOBP4Qz+wfUpzo5FyO4MBr39IH+uLclFvmCTrz1mB5PpSD52N1'
 || 'DfqS988xptibWfbw9Sa/jytf+dz4PqQz6wi63uxxBpCXY7uUj88jNDNy1mYGdl97'
 || '856nt2f4WsOFed4SpzumNCvlT+jpmKC7WgH3PJn9DaZfA42vlgh96d+wkHy0/V95'
 || 'xyv8oj59QbvBN2I/iAuqEAAEAAA=' );
 --
 init_core_font( 5, 'times', 'N', 'Times-Roman'
 , 'H4sIAAAAAAAAC8WSKxLCQAyG+3Bopo4bVHbwHGCvUNNT9AB4JEwvgUBimUF3wCNR'
 || 'qAoGRZL9twlQikR8kzTvZBtF0SP6O7Ej1kTnSRfEhHw7+Jy3J4XGi8w05yeZh2sE'
 || '4j312ZDeEg1gvSJy6C36L9WX1urr4xrolfrSrYmrUCeDPGMu5+cQ3Ur3OXvQ+TYf'
 || '+2FGexOZvTM1L3S3o5fJjGQJX2n68U2ur3X5m3cTvfbxsk9pcsMee60rdTjnhNkc'
 || 'Zip9HOv9+7/tI3Oif3InOdV/oLdx3gq2HIRaB1Ob7XPk35QwwxDyxg3e09Dv6nSf'
 || 'rxQjvty8ywDce9CXvdF9R+4y4o+7J1P/I9sABAAA' );
 --
 init_core_font( 6, 'times', 'I', 'Times-Italic'
 , 'H4sIAAAAAAAAC8WSPQ6CQBCFF+i01NB5g63tPcBegYZTeAB6SxNLjLUH4BTEeAYr'
 || 'Kwpj5ezsW2YgoKXFl2Hnb9+wY4x5m7+TOOJMdIFsRywodkfMBX9aSz7bXGp+gj6+'
 || 'R4TvOtJ3CU5Eq85tgGsbxG3QN8iFZY1WzpxXwkckFTR7e1G6osZGWT1bDuBnTeP5'
 || 'KtW/E71c0yB2IFbBphuyBXIL9Y/9fPvhf8se6vsa8nmeQtU6NSf6ch9fc8P9DpqK'
 || 'cPa5/I7VxDwruTN9kV3LDvQ+h1m8z4I4x9LIbnn/Fv6nwOdyGq+d33jk7/cxztyq'
 || 'XRhTz/it7Mscg7fT5CO+9ahnYk20Hww5IrwABAAA' );
 --
 init_core_font( 7, 'times', 'B', 'Times-Bold'
 , 'H4sIAAAAAAAAC8VSuw3CQAy9XBqUAVKxAZkgHQUNEiukySxpqOjTMQEDZIrUDICE'
 || 'RHUVVfy9c0IQJcWTfbafv+ece7u/Izs553cgAyN/APagl+wjgN3XKZ5kmTg/IXkw'
 || 'h4JqXUEfAb1I1VvwFYysk9iCffmN4+gtccSr5nlwDpuTepCZ/MH0FZibDUnO7MoR'
 || 'HXdDuvgjpzNxgevG+dF/hr3dWfoNyEZ8Taqn+7d7ozmqpGM8zdMYruFrXopVjvY2'
 || 'in9gXe+5vBf1KfX9E6TOVBsb8i5iqwQyv9+a3Gg/Cv+VoDtaQ7xdPwfNYRDji09g'
 || 'X/FvLNGmO62B9jSsoFwgfM+jf1z/SPwrkTMBOkCTBQAEAAA=' );
 --
 init_core_font( 8, 'times', 'BI', 'Times-BoldItalic'
 , 'H4sIAAAAAAAAC8WSuw2DMBCGHegYwEuECajIAGwQ0TBFBnCfPktkAKagzgCRIqWi'
 || 'oso9fr+Qo5RB+nT2ve+wMWYzf+fgjKmOJFelPhENnS0xANJXHfwHSBtjfoI8nMMj'
 || 'tXo63xKW/Cx9ONRn3US6C/wWvYeYNr+LH2IY6cHGPkJfvsc5kX7mFjF+Vqs9iT6d'
 || 'zwEL26y1Qz62nWlvD5VSf4R9zPuon/ne+C45+XxXf5lnTGLTOZCXPx8v9Qfdjdid'
 || '5vD/f/+/pE/Ur14kG+xjTHRc84pZWsC2Hjk2+Hgbx78j4Z8W4DlL+rBnEN5Bie6L'
 || 'fsL+1u/InuYCdsdaeAs+RxftKfGdfQDlDF/kAAQAAA==' );
 --
 init_core_font( 9, 'courier', 'N', 'Courier', null );
 for i in 0 .. 255
 loop
 g_fonts( 9 ).char_width_tab( i ) := 600;
 end loop;
 --
 init_core_font( 10, 'courier', 'I', 'Courier-Oblique', null );
 g_fonts( 10 ).char_width_tab := g_fonts( 9 ).char_width_tab;
 --
 init_core_font( 11, 'courier', 'B', 'Courier-Bold', null );
 g_fonts( 11 ).char_width_tab := g_fonts( 9 ).char_width_tab;
 --
 init_core_font( 12, 'courier', 'BI', 'Courier-BoldOblique', null );
 g_fonts( 12 ).char_width_tab := g_fonts( 9 ).char_width_tab;
 --
 init_core_font( 13, 'symbol', 'N', 'Symbol'
 , 'H4sIAAAAAAAAC82SIU8DQRCFZ28xIE+cqcbha4tENKk/gQCJJ6AweIK9H1CHqKnp'
 || 'D2gTFBaDIcFwCQkJSTG83fem7SU0qYNLvry5nZ25t7NnZkv7c8LQrFhAP6GHZvEY'
 || 'HOB9ylxGubTfNVRc34mKpFonzBQ/gUZ6Ds7AN6i5lv1dKv8Ab1eKQYSV4hUcgZFq'
 || 'J/Sec7fQHtdTn3iqfvdrb7m3e2pZW+xDG3oIJ/Li3gfMr949rlU74DyT1/AuTX1f'
 || 'YGhOzTP8B0/RggsEX/I03vgXPrrslZjfM8/pGu40t2ZjHgud97F7337mXP/GO4h9'
 || '3WmPPaOJ/jrOs9yC52MlrtUzfWupfTX51X/L+13Vl/J/s4W2S3pSfSh5DmeXerMf'
 || '+LXhWQAEAAA=' );
 --
 init_core_font( 14, 'zapfdingbats', 'N', 'ZapfDingbats'
 , 'H4sIAAAAAAAAC83ROy9EQRjG8TkzjdJl163SSHR0EpdsVkSi2UahFhUljUKUIgoq'
 || 'CrvJCtFQyG6EbSSERGxhC0ofQAQFxbIi8T/7PoUPIOEkvzxzzsycdy7O/fUTtToX'
 || 'bnCuvHPOV8gk4r423ovkGQ5od5OTWMeesmBz/RuZIWv4wCAY4z/xjipeqflC9qAD'
 || 'aRwxrxkJievSFzrRh36tZ1zttL6nkGX+A27xrLnttE/IBji9x7UvcIl9nPJ9AL36'
 || 'd1L9hyihoDW10L62cwhNyhntryZVExYl3kMj+zym+CrJv6M8VozPmfr5L8uwJORL'
 || 'tox7NFHG/Obj79FlwhqZ1X292xn6CbAXP/fjjv6rJYyBtUdl1vxEO6fcRB7bMmJ3'
 || 'GYZsTN0GdrDL/Ao5j1GZNr5kwqydX5z1syoiYEq5gCtlSrXi+mVbi3PfVAuhoQAE'
 || 'AAA=' );
 --
 end;
 --
 function to_char_round
 ( p_value number
 , p_precision pls_integer := 2
 )
 return varchar2
 is
 begin
 return to_char( round( p_value, p_precision ), 'TM9', 'NLS_NUMERIC_CHARACTERS=.,' );
 end;
 --
 procedure raw2pdfdoc( p_raw blob )
 is
 begin
 dbms_lob.append( g_pdf_doc, p_raw );
 end;
 --
 procedure txt2pdfdoc( p_txt varchar2 )
 is
 begin
 raw2pdfdoc( utl_raw.cast_to_raw( p_txt || c_nl ) );
 end;
 --
 function add_object( p_txt varchar2 := null )
 return number
 is
 t_self number(10);
 begin
 t_self := g_objects.count( );
 g_objects( t_self ) := dbms_lob.getlength( g_pdf_doc );
 --
 if p_txt is null
 then
 txt2pdfdoc( t_self || ' 0 obj' );
 else
 txt2pdfdoc( t_self || ' 0 obj' || c_nl || '&lt;&lt;' || p_txt || '&gt;&gt;' || c_nl || 'endobj' );
 end if;
 --
 return t_self;
 end;
 --
 procedure add_object( p_txt varchar2 := null )
 is
 t_dummy number(10) := add_object( p_txt );
 begin
 null;
 end;
 --
 function adler32( p_src in blob )
 return varchar2
 is
 s1 pls_integer := 1;
 s2 pls_integer := 0;
 n pls_integer;
 step_size number;
 tmp varchar2(32766);
 c65521 constant pls_integer := 65521;
 begin
 step_size := trunc( 16383 / dbms_lob.getchunksize( p_src ) ) * dbms_lob.getchunksize( p_src );
 for j in 0 .. trunc( ( dbms_lob.getlength( p_src ) - 1 ) / step_size )
 loop
 tmp := rawtohex( dbms_lob.substr( p_src, step_size, j * step_size + 1 ) );
 for i in 1 .. length( tmp ) / 2
 loop
 n := to_number( substr( tmp, i * 2 - 1, 2 ), 'xx' );
 s1 := s1 + n;
 if s1 &gt;= c65521
 then
 s1 := s1 - c65521;
 end if;
 s2 := s2 + s1;
 if s2 &gt;= c65521
 then
 s2 := s2 - c65521;
 end if;
 end loop;
 end loop;
 return to_char( s2, 'fm0XXX' ) || to_char( s1, 'fm0XXX' );
 end;
 --
 function flate_encode( p_val blob )
 return blob
 is
 t_blob blob;
 begin
 t_blob := hextoraw( '789C' );
 dbms_lob.copy( t_blob
 , utl_compress.lz_compress( p_val )
 , dbms_lob.lobmaxsize
 , 3
 , 11
 );
 dbms_lob.trim( t_blob, dbms_lob.getlength( t_blob ) - 8 );
 dbms_lob.append( t_blob, hextoraw( adler32( p_val ) ) );
 return t_blob;
 end;
 --
 procedure put_stream
 ( p_stream blob
 , p_compress boolean := true
 , p_extra varchar2 := ''
 , p_tag boolean := true
 )
 is
 t_blob blob;
 t_compress boolean := false;
 begin
 if p_compress and nvl( dbms_lob.getlength( p_stream ), 0 ) &gt; 0
 then
 t_compress := true;
 t_blob := flate_encode( p_stream );
 else
 t_blob := p_stream;
 end if;
 txt2pdfdoc( case when p_tag then '&lt;&lt;' end
 || case when t_compress then '/Filter /FlateDecode ' end
 || '/Length ' || nvl( length( t_blob ), 0 )
 || p_extra
 || '&gt;&gt;' );
 txt2pdfdoc( 'stream' );
 raw2pdfdoc( t_blob );
 txt2pdfdoc( 'endstream' );
 if dbms_lob.istemporary( t_blob ) = 1
 then
 dbms_lob.freetemporary( t_blob );
 end if;
 end;
 --
 function add_stream
 ( p_stream blob
 , p_extra varchar2 := ''
 , p_compress boolean := true
 )
 return number
 is
 t_self number(10);
 begin
 t_self := add_object;
 put_stream( p_stream
 , p_compress
 , p_extra
 );
 txt2pdfdoc( 'endobj' );
 return t_self;
 end;
 --
 function subset_font( p_index pls_integer )
 return blob
 is
 t_tmp blob;
 t_header blob;
 t_tables blob;
 t_len pls_integer;
 t_code pls_integer;
 t_glyph pls_integer;
 t_offset pls_integer;
 t_factor pls_integer;
 t_unicode pls_integer;
 t_used_glyphs tp_pls_tab;
 t_fmt varchar2(10);
 t_utf16_charset varchar2(1000);
 t_raw raw(32767);
 t_v varchar2(32767);
 t_table_records raw(32767);
 begin
 if g_fonts( p_index ).cid
 then
 t_used_glyphs := g_fonts( p_index ).used_chars;
 t_used_glyphs( 0 ) := 0;
 else
 t_utf16_charset := substr( g_fonts( p_index ).charset, 1, instr( g_fonts( p_index ).charset, '.' ) ) || 'AL16UTF16';
 t_used_glyphs( 0 ) := 0;
 t_code := g_fonts( p_index ).used_chars.first;
 while t_code is not null
 loop
 t_unicode := to_number( rawtohex( utl_raw.convert( hextoraw( to_char( t_code, 'fm0x' ) )
 , t_utf16_charset
 , g_fonts( p_index ).charset -- ???? database characterset ?????
 )
 ), 'XXXXXXXX' );
 if g_fonts( p_index ).flags = 4 -- a symbolic font
 then
 -- assume code 32, space maps to the first code from the font
 t_used_glyphs( g_fonts( p_index ).code2glyph( g_fonts( p_index ).code2glyph.first + t_unicode - 32 ) ) := 0;
 else
 t_used_glyphs( g_fonts( p_index ).code2glyph( t_unicode ) ) := 0;
 end if;
 t_code := g_fonts( p_index ).used_chars.next( t_code );
 end loop;
 end if;
 --
 dbms_lob.createtemporary( t_tables, true );
 t_header := utl_raw.concat( hextoraw( '00010000' )
 , dbms_lob.substr( g_fonts( p_index ).fontfile2, 8, g_fonts( p_index ).ttf_offset + 4 )
 );
 t_offset := 12 + blob2num( g_fonts( p_index ).fontfile2, 2, g_fonts( p_index ).ttf_offset + 4 ) * 16;
 t_table_records := dbms_lob.substr( g_fonts( p_index ).fontfile2
 , blob2num( g_fonts( p_index ).fontfile2, 2, g_fonts( p_index ).ttf_offset + 4 ) * 16
 , g_fonts( p_index ).ttf_offset + 12
 );
 for i in 1 .. blob2num( g_fonts( p_index ).fontfile2, 2, g_fonts( p_index ).ttf_offset + 4 )
 loop
 case utl_raw.cast_to_varchar2( utl_raw.substr( t_table_records, i * 16 - 15, 4 ) )
 when 'post'
 then
 dbms_lob.append( t_header
 , utl_raw.concat( utl_raw.substr( t_table_records, i * 16 - 15, 4 ) -- tag
 , hextoraw( '00000000' ) -- checksum
 , num2raw( t_offset + dbms_lob.getlength( t_tables ) ) -- offset
 , num2raw( 32 ) -- length
 )
 );
 dbms_lob.append( t_tables
 , utl_raw.concat( hextoraw( '00030000' )
 , dbms_lob.substr( g_fonts( p_index ).fontfile2
 , 28
 , raw2num( t_table_records, i * 16 - 7, 4 ) + 5
 )
 )
 );
 when 'loca'
 then
 if g_fonts( p_index ).indexToLocFormat = 0
 then
 t_fmt := 'fm0XXX';
 else
 t_fmt := 'fm0XXXXXXX';
 end if;
 t_raw := null;
 dbms_lob.createtemporary( t_tmp, true );
 t_len := 0;
 for g in 0 .. g_fonts( p_index ).numGlyphs - 1
 loop
 t_raw := utl_raw.concat( t_raw, hextoraw( to_char( t_len, t_fmt ) ) );
 if utl_raw.length( t_raw ) &gt; 32770
 then
 dbms_lob.append( t_tmp, t_raw );
 t_raw := null;
 end if;
 if t_used_glyphs.exists( g )
 then
 t_len := t_len + g_fonts( p_index ).loca( g + 1 ) - g_fonts( p_index ).loca( g );
 end if;
 end loop;
 t_raw := utl_raw.concat( t_raw, hextoraw( to_char( t_len, t_fmt ) ) );
 dbms_lob.append( t_tmp, t_raw );
 dbms_lob.append( t_header
 , utl_raw.concat( utl_raw.substr( t_table_records, i * 16 - 15, 4 ) -- tag
 , hextoraw( '00000000' ) -- checksum
 , num2raw( t_offset + dbms_lob.getlength( t_tables ) ) -- offset
 , num2raw( dbms_lob.getlength( t_tmp ) ) -- length
 )
 );
 dbms_lob.append( t_tables, t_tmp );
 dbms_lob.freetemporary( t_tmp );
 when 'glyf'
 then
 if g_fonts( p_index ).indexToLocFormat = 0
 then
 t_factor := 2;
 else
 t_factor := 1;
 end if;
 t_raw := null;
 dbms_lob.createtemporary( t_tmp, true );
 for g in 0 .. g_fonts( p_index ).numGlyphs - 1
 loop
 if ( t_used_glyphs.exists( g )
 and g_fonts( p_index ).loca( g + 1 ) &gt; g_fonts( p_index ).loca( g )
 )
 then
 t_raw := utl_raw.concat( t_raw
 , dbms_lob.substr( g_fonts( p_index ).fontfile2
 , ( g_fonts( p_index ).loca( g + 1 ) - g_fonts( p_index ).loca( g ) ) * t_factor
 , g_fonts( p_index ).loca( g ) * t_factor + raw2num( t_table_records, i * 16 - 7, 4 ) + 1
 )
 );
 if utl_raw.length( t_raw ) &gt; 32778
 then
 dbms_lob.append( t_tmp, t_raw );
 t_raw := null;
 end if;
 end if;
 end loop;
 if utl_raw.length( t_raw ) &gt; 0
 then
 dbms_lob.append( t_tmp, t_raw );
 end if;
 dbms_lob.append( t_header
 , utl_raw.concat( utl_raw.substr( t_table_records, i * 16 - 15, 4 ) -- tag
 , hextoraw( '00000000' ) -- checksum
 , num2raw( t_offset + dbms_lob.getlength( t_tables ) ) -- offset
 , num2raw( dbms_lob.getlength( t_tmp ) ) -- length
 )
 );
 dbms_lob.append( t_tables, t_tmp );
 dbms_lob.freetemporary( t_tmp );
 else
 dbms_lob.append( t_header
 , utl_raw.concat( utl_raw.substr( t_table_records, i * 16 - 15, 4 ) -- tag
 , utl_raw.substr( t_table_records, i * 16 - 11, 4 ) -- checksum
 , num2raw( t_offset + dbms_lob.getlength( t_tables ) ) -- offset
 , utl_raw.substr( t_table_records, i * 16 - 3, 4 ) -- length
 )
 );
 dbms_lob.copy( t_tables
 , g_fonts( p_index ).fontfile2
 , raw2num( t_table_records, i * 16 - 3, 4 )
 , dbms_lob.getlength( t_tables ) + 1
 , raw2num( t_table_records, i * 16 - 7, 4 ) + 1
 );
 end case;
 end loop;
 dbms_lob.append( t_header, t_tables );
 dbms_lob.freetemporary( t_tables );
 return t_header;
 end;
 --
 function add_font( p_index pls_integer )
 return number
 is
 t_self number(10);
 t_fontfile number(10);
 t_font_subset blob;
 t_used pls_integer;
 t_used_glyphs tp_pls_tab;
 t_w varchar2(32767);
 t_unicode pls_integer;
 t_utf16_charset varchar2(1000);
 t_width number;
 begin
 if g_fonts( p_index ).standard
 then
 return add_object( '/Type/Font'
 || '/Subtype/Type1'
 || '/BaseFont/' || g_fonts( p_index ).name
 || '/Encoding/WinAnsiEncoding' -- code page 1252
 );
 end if;
 --
 if g_fonts( p_index ).cid
 then
 t_self := add_object;
 txt2pdfdoc( '&lt;&lt;/Type/Font/Subtype/Type0/Encoding/Identity-H'
 || '/BaseFont/' || g_fonts( p_index ).name
 || '/DescendantFonts ' || to_char( t_self + 1 ) || ' 0 R'
 || '/ToUnicode ' || to_char( t_self + 8 ) || ' 0 R'
 || '&gt;&gt;' );
 txt2pdfdoc( 'endobj' );
 add_object;
 txt2pdfdoc( '[' || to_char( t_self + 2 ) || ' 0 R]' );
 txt2pdfdoc( 'endobj' );
 add_object( '/Type/Font/Subtype/CIDFontType2/CIDToGIDMap/Identity/DW 1000'
 || '/BaseFont/' || g_fonts( p_index ).name
 || '/CIDSystemInfo ' || to_char( t_self + 3 ) || ' 0 R'
 || '/W ' || to_char( t_self + 4 ) || ' 0 R'
 || '/FontDescriptor ' || to_char( t_self + 5 ) || ' 0 R' );
 add_object( '/Ordering(Identity) /Registry(Adobe) /Supplement 0' );
 --
 t_utf16_charset := substr( g_fonts( p_index ).charset, 1, instr( g_fonts( p_index ).charset, '.' ) ) || 'AL16UTF16';
 t_used_glyphs := g_fonts( p_index ).used_chars;
 t_used_glyphs( 0 ) := 0;
 t_used := t_used_glyphs.first();
 while t_used is not null
 loop
 if g_fonts( p_index ).hmetrics.exists( t_used )
 then
 t_width := g_fonts( p_index ).hmetrics( t_used );
 else
 t_width := g_fonts( p_index ).hmetrics( g_fonts( p_index ).hmetrics.last() );
 end if;
 t_width := trunc( t_width * g_fonts( p_index ).unit_norm );
 if t_used_glyphs.prior( t_used ) = t_used - 1
 then
 t_w := t_w || ' ' || t_width;
 else
 t_w := t_w || '] ' || t_used || ' [' || t_width;
 end if;
 t_used := t_used_glyphs.next( t_used );
 end loop;
 t_w := '[' || ltrim( t_w, '] ' ) || ']]';
 add_object;
 txt2pdfdoc( t_w );
 txt2pdfdoc( 'endobj' );
 add_object
 ( '/Type/FontDescriptor'
 || '/FontName/' || g_fonts( p_index ).name
 || '/Flags ' || g_fonts( p_index ).flags
 || '/FontBBox [' || g_fonts( p_index ).bb_xmin
 || ' ' || g_fonts( p_index ).bb_ymin
 || ' ' || g_fonts( p_index ).bb_xmax
 || ' ' || g_fonts( p_index ).bb_ymax
 || ']'
 || '/ItalicAngle ' || to_char_round( g_fonts( p_index ).italic_angle )
 || '/Ascent ' || g_fonts( p_index ).ascent
 || '/Descent ' || g_fonts( p_index ).descent
 || '/CapHeight ' || g_fonts( p_index ).capheight
 || '/StemV ' || g_fonts( p_index ).stemv
 || '/FontFile2 ' || to_char( t_self + 6 ) || ' 0 R' );
 t_fontfile := add_stream( g_fonts( p_index ).fontfile2
 , '/Length1 ' || dbms_lob.getlength( g_fonts( p_index ).fontfile2 )
 , g_fonts( p_index ).compress_font
 );
 t_font_subset := subset_font( p_index );
 t_fontfile := add_stream( t_font_subset
 , '/Length1 ' || dbms_lob.getlength( t_font_subset )
 , g_fonts( p_index ).compress_font
 );
 declare
 t_g2c tp_pls_tab;
 t_code pls_integer;
 t_c_start pls_integer;
 t_map varchar2(32767);
 t_cmap varchar2(32767);
 t_cor pls_integer;
 t_cnt pls_integer;
 begin
 t_code := g_fonts( p_index ).code2glyph.first;
 if g_fonts( p_index ).flags = 4 -- a symbolic font
 then
 -- assume code 32, space maps to the first code from the font
 t_cor := t_code - 32;
 else
 t_cor := 0;
 end if;
 while t_code is not null
 loop
 t_g2c( g_fonts( p_index ).code2glyph( t_code ) ) := t_code - t_cor;
 t_code := g_fonts( p_index ).code2glyph.next( t_code );
 end loop;
 t_cnt := 0;
 t_used_glyphs := g_fonts( p_index ).used_chars;
 t_used := t_used_glyphs.first();
 while t_used is not null
 loop
 t_map := t_map || '&lt;' || to_char( t_used, 'FM0XXX' )
 || '&gt; &lt;' || to_char( t_g2c( t_used ), 'FM0XXX' )
 || '&gt;' || chr( 10 );
 if t_cnt = 99
 then
 t_cnt := 0;
 t_cmap := t_cmap || chr( 10 ) || '100 beginbfchar' || chr( 10 ) || t_map || 'endbfchar';
 t_map := '';
 else
 t_cnt := t_cnt + 1;
 end if;
 t_used := t_used_glyphs.next( t_used );
 end loop;
 if t_cnt &gt; 0
 then
 t_cmap := t_cnt || ' beginbfchar' || chr( 10 ) || t_map || 'endbfchar';
 end if;
 t_fontfile := add_stream( utl_raw.cast_to_raw(
 '/CIDInit /ProcSet findresource begin 12 dict begin
 begincmap
 /CIDSystemInfo
 &lt;&lt; /Registry (Adobe) /Ordering (UCS) /Supplement 0 &gt;&gt; def
 /CMapName /Adobe-Identity-UCS def /CMapType 2 def
 1 begincodespacerange
 &lt;0000&gt; &lt;FFFF&gt;
 endcodespacerange
 ' || t_cmap || '
 endcmap
 CMapName currentdict /CMap defineresource pop
 end
 end' ) );
 end;
 return t_self;
 end if;
 --
 g_fonts( p_index ).first_char := g_fonts( p_index ).used_chars.first();
 g_fonts( p_index ).last_char := g_fonts( p_index ).used_chars.last();
 t_self := add_object;
 txt2pdfdoc( '&lt;&lt;/Type /Font '
 || '/Subtype /' || g_fonts( p_index ).subtype
 || ' /BaseFont /' || g_fonts( p_index ).name
 || ' /FirstChar ' || g_fonts( p_index ).first_char
 || ' /LastChar ' || g_fonts( p_index ).last_char
 || ' /Widths ' || to_char( t_self + 1 ) || ' 0 R'
 || ' /FontDescriptor ' || to_char( t_self + 2 ) || ' 0 R'
 || ' /Encoding ' || to_char( t_self + 3 ) || ' 0 R'
 || ' &gt;&gt;' );
 txt2pdfdoc( 'endobj' );
 add_object;
 txt2pdfdoc( '[' );
 begin
 for i in g_fonts( p_index ).first_char .. g_fonts( p_index ).last_char
 loop
 txt2pdfdoc( g_fonts( p_index ).char_width_tab( i ) );
 end loop;
 exception
 when others
 then
 dbms_output.put_line( '**** ' || g_fonts( p_index ).name );
 end;
 txt2pdfdoc( ']' );
 txt2pdfdoc( 'endobj' );
 add_object
 ( '/Type /FontDescriptor'
 || ' /FontName /' || g_fonts( p_index ).name
 || ' /Flags ' || g_fonts( p_index ).flags
 || ' /FontBBox [' || g_fonts( p_index ).bb_xmin
 || ' ' || g_fonts( p_index ).bb_ymin
 || ' ' || g_fonts( p_index ).bb_xmax
 || ' ' || g_fonts( p_index ).bb_ymax
 || ']'
 || ' /ItalicAngle ' || to_char_round( g_fonts( p_index ).italic_angle )
 || ' /Ascent ' || g_fonts( p_index ).ascent
 || ' /Descent ' || g_fonts( p_index ).descent
 || ' /CapHeight ' || g_fonts( p_index ).capheight
 || ' /StemV ' || g_fonts( p_index ).stemv
 || case
 when g_fonts( p_index ).fontfile2 is not null
 then ' /FontFile2 ' || to_char( t_self + 4 ) || ' 0 R'
 end );
 add_object( '/Type /Encoding /BaseEncoding /WinAnsiEncoding '
 || g_fonts( p_index ).diff
 || ' ' );
 if g_fonts( p_index ).fontfile2 is not null
 then
 t_font_subset := subset_font( p_index );
 t_fontfile :=
 add_stream( t_font_subset
 , '/Length1 ' || dbms_lob.getlength( t_font_subset )
 , g_fonts( p_index ).compress_font
 );
 end if;
 return t_self;
 end;
 --
 procedure add_image( p_img tp_img )
 is
 t_pallet number(10);
 begin
 if p_img.color_tab is not null
 then
 t_pallet := add_stream( p_img.color_tab );
 else
 t_pallet := add_object; -- add an empty object
 txt2pdfdoc( 'endobj' );
 end if;
 add_object;
 txt2pdfdoc( '&lt;&lt;/Type /XObject /Subtype /Image'
 || ' /Width ' || to_char( p_img.width )
 || ' /Height ' || to_char( p_img.height )
 || ' /BitsPerComponent ' || to_char( p_img.color_res )
 );
 --
 if p_img.transparancy_index is not null
 then
 txt2pdfdoc( '/Mask [' || p_img.transparancy_index || ' ' || p_img.transparancy_index || ']' );
 end if;
 if p_img.color_tab is null
 then
 if p_img.greyscale
 then
 txt2pdfdoc( '/ColorSpace /DeviceGray' );
 else
 txt2pdfdoc( '/ColorSpace /DeviceRGB' );
 end if;
 else
 txt2pdfdoc( '/ColorSpace [/Indexed /DeviceRGB '
 || to_char( utl_raw.length( p_img.color_tab ) / 3 - 1 )
 || ' ' || to_char( t_pallet ) || ' 0 R]'
 );
 end if;
 --
 if p_img.type = 'jpg'
 then
 put_stream( p_img.pixels, false, '/Filter /DCTDecode', false );
 elsif p_img.type = 'png'
 then
 put_stream( p_img.pixels, false
 , ' /Filter /FlateDecode /DecodeParms &lt;&lt;/Predictor 15 '
 || '/Colors ' || p_img.nr_colors
 || '/BitsPerComponent ' || p_img.color_res
 || ' /Columns ' || p_img.width
 || ' &gt;&gt; '
 , false );
 else
 put_stream( p_img.pixels, p_tag =&gt; false );
 end if;
 txt2pdfdoc( 'endobj' );
 end;
 --
 function add_resources
 return number
 is
 t_ind pls_integer;
 t_self number(10);
 t_fonts tp_objects_tab;
 begin
 --
 t_ind := g_used_fonts.first;
 while t_ind is not null
 loop
 t_fonts( t_ind ) := add_font( t_ind );
 t_ind := g_used_fonts.next( t_ind );
 end loop;
 --
 t_self := add_object;
 txt2pdfdoc( '&lt;&lt;/ProcSet [/PDF /Text]' );
 --
 if g_used_fonts.count() &gt; 0
 then
 txt2pdfdoc( '/Font &lt;&lt;' );
 t_ind := g_used_fonts.first;
 while t_ind is not null
 loop
 txt2pdfdoc( '/F'|| to_char( t_ind ) || ' '
 || to_char( t_fonts( t_ind ) ) || ' 0 R'
 );
 t_ind := g_used_fonts.next( t_ind );
 end loop;
 txt2pdfdoc( '&gt;&gt;' );
 end if;
 --
 if g_images.count( ) &gt; 0
 then
 txt2pdfdoc( '/XObject &lt;&lt;' );
 for i in g_images.first .. g_images.last
 loop
 txt2pdfdoc( '/I' || to_char( i ) || ' ' || to_char( t_self + 2 * i ) || ' 0 R' );
 end loop;
 txt2pdfdoc( '&gt;&gt;' );
 end if;
 --
 txt2pdfdoc( '&gt;&gt;' );
 txt2pdfdoc( 'endobj' );
 --
 if g_images.count( ) &gt; 0
 then
 for i in g_images.first .. g_images.last
 loop
 add_image( g_images( i ) );
 end loop;
 end if;
 return t_self;
 end;
 --
 procedure add_page
 ( p_page_ind pls_integer
 , p_parent number
 , p_resources number
 )
 is
 t_content number(10);
 begin
 t_content := add_stream( g_pages( p_page_ind ) );
 add_object;
 txt2pdfdoc( '&lt;&lt; /Type /Page' );
 txt2pdfdoc( '/Parent ' || to_char( p_parent ) || ' 0 R' );
 txt2pdfdoc( '/Contents ' || to_char( t_content ) || ' 0 R' );
 txt2pdfdoc( '/Resources ' || to_char( p_resources ) || ' 0 R' );
 txt2pdfdoc( '&gt;&gt;' );
 txt2pdfdoc( 'endobj' );
 end;
 --
 function add_pages
 return number
 is
 t_self number(10);
 t_resources number(10);
 begin
 t_resources := add_resources;
 t_self := add_object;
 txt2pdfdoc( '&lt;&lt;/Type/Pages/Kids [' );
 --
 for i in g_pages.first .. g_pages.last
 loop
 txt2pdfdoc( to_char( t_self + i * 2 + 2 ) || ' 0 R' );
 end loop;
 --
 txt2pdfdoc( ']' );
 txt2pdfdoc( '/Count ' || g_pages.count() );
 txt2pdfdoc( '/MediaBox [0 0 '
 || to_char_round( g_settings.page_width
 , 0
 )
 || ' '
 || to_char_round( g_settings.page_height
 , 0
 )
 || ']' );
 txt2pdfdoc( '&gt;&gt;' );
 txt2pdfdoc( 'endobj' );
 --
 if g_pages.count() &gt; 0
 then
 for i in g_pages.first .. g_pages.last
 loop
 add_page( i, t_self, t_resources );
 end loop;
 end if;
 --
 return t_self;
 end;
 --
 function add_catalogue
 return number
 is
 begin
 return add_object( '/Type/Catalog'
 || '/Pages ' || to_char( add_pages ) || ' 0 R'
 || '/OpenAction [0 /XYZ null null 0.77]'
 );
 end;
 --
 function add_info
 return number
 is
 t_banner varchar2( 1000 );
 begin
 begin
 select 'running on '
 || replace( replace( replace( substr( banner
 , 1
 , 950
 )
 , '\'
 , '\\'
 )
 , '('
 , '\('
 )
 , ')'
 , '\)'
 )
 into t_banner
 from v$version
 where instr( upper( banner )
 , 'DATABASE'
 ) &gt; 0;
 t_banner := '/Producer (' || t_banner || ')';
 exception
 when others
 then
 null;
 end;
 --
 return add_object( to_char( sysdate, '"/CreationDate (D:"YYYYMMDDhh24miss")"' )
 || '/Creator (AS-PDF 0.3.0 by Anton Scheffer)'
 || t_banner
 || '/Title &lt;FEFF' || utl_i18n.string_to_raw( g_info.title, 'AL16UTF16' ) || '&gt;'
 || '/Author &lt;FEFF' || utl_i18n.string_to_raw( g_info.author, 'AL16UTF16' ) || '&gt;'
 || '/Subject &lt;FEFF' || utl_i18n.string_to_raw( g_info.subject, 'AL16UTF16' ) || '&gt;'
 || '/Keywords &lt;FEFF' || utl_i18n.string_to_raw( g_info.keywords, 'AL16UTF16' ) || '&gt;'
 );
 end;
 --
 procedure finish_pdf
 is
 t_xref number;
 t_info number(10);
 t_catalogue number(10);
 begin
 if g_pages.count = 0
 then
 new_page;
 end if;
 if g_page_prcs.count &gt; 0
 then
 for i in g_pages.first .. g_pages.last
 loop
 g_page_nr := i;
 for p in g_page_prcs.first .. g_page_prcs.last
 loop
 begin
 execute immediate replace( replace( g_page_prcs( p ), '#PAGE_NR#', i + 1 ), '"PAGE_COUNT#', g_pages.count );
 exception
 when others then null;
 end;
 end loop;
 end loop;
 end if;
 dbms_lob.createtemporary( g_pdf_doc, true );
 txt2pdfdoc( '%PDF-1.3' );
 raw2pdfdoc( hextoraw( '25E2E3CFD30D0A' ) ); -- add a hex comment
 t_info := add_info;
 t_catalogue := add_catalogue;
 t_xref := dbms_lob.getlength( g_pdf_doc );
 txt2pdfdoc( 'xref' );
 txt2pdfdoc( '0 ' || to_char( g_objects.count() ) );
 txt2pdfdoc( '0000000000 65535 f ' );
 for i in 1 .. g_objects.count( ) - 1
 loop
 txt2pdfdoc( to_char( g_objects( i ), 'fm0000000000' ) || ' 00000 n' );
 -- this line should be exactly 20 bytes, including EOL
 end loop;
 txt2pdfdoc( 'trailer' );
 txt2pdfdoc( '&lt;&lt; /Root ' || to_char( t_catalogue ) || ' 0 R' );
 txt2pdfdoc( '/Info ' || to_char( t_info ) || ' 0 R' );
 txt2pdfdoc( '/Size ' || to_char( g_objects.count() ) );
 txt2pdfdoc( '&gt;&gt;' );
 txt2pdfdoc( 'startxref' );
 txt2pdfdoc( to_char( t_xref ) );
 txt2pdfdoc( '%%EOF' );
 --
 g_objects.delete;
 for i in g_pages.first .. g_pages.last
 loop
 dbms_lob.freetemporary( g_pages( i ) );
 end loop;
 g_objects.delete;
 g_pages.delete;
 g_fonts.delete;
 g_used_fonts.delete;
 g_page_prcs.delete;
 if g_images.count() &gt; 0
 then
 for i in g_images.first .. g_images.last
 loop
 if dbms_lob.istemporary( g_images( i ).pixels ) = 1
 then
 dbms_lob.freetemporary( g_images( i ).pixels );
 end if;
 end loop;
 g_images.delete;
 end if;
 end;
 --
 function conv2uu( p_value number, p_unit varchar2 )
 return number
 is
 c_inch constant number := 25.40025;
 begin
 return round( case lower( p_unit )
 when 'mm' then p_value * 72 / c_inch
 when 'cm' then p_value * 720 / c_inch
 when 'pt' then p_value -- also point
 when 'point' then p_value
 when 'inch' then p_value * 72
 when 'in' then p_value * 72 -- also inch
 when 'pica' then p_value * 12
 when 'p' then p_value * 12 -- also pica
 when 'pc' then p_value * 12 -- also pica
 when 'em' then p_value * 12 -- also pica
 when 'px' then p_value -- pixel voorlopig op point zetten
 when 'px' then p_value * 0.8 -- pixel
 else null
 end
 , 3
 );
 end;
 --
 procedure set_page_size
 ( p_width number
 , p_height number
 , p_unit varchar2 := 'cm'
 )
 is
 begin
 g_settings.page_width := conv2uu( p_width, p_unit );
 g_settings.page_height := conv2uu( p_height, p_unit );
 end;
 --
 procedure set_page_format( p_format varchar2 := 'A4' )
 is
 begin
 case upper( p_format )
 when 'A3'
 then
 set_page_size( 420, 297, 'mm' );
 when 'A4'
 then
 set_page_size( 297, 210, 'mm' );
 when 'A5'
 then
 set_page_size( 210, 148, 'mm' );
 when 'A6'
 then
 set_page_size( 148, 105, 'mm' );
 when 'LEGAL'
 then
 set_page_size( 14, 8.5, 'in' );
 when 'LETTER'
 then
 set_page_size( 11, 8.5, 'in' );
 when 'QUARTO'
 then
 set_page_size( 11, 9, 'in' );
 when 'EXECUTIVE'
 then
 set_page_size( 10.5, 7.25, 'in' );
 else
 null;
 end case;
 end;
 --
 procedure set_page_orientation( p_orientation varchar2 := 'PORTRAIT' )
 is
 t_tmp number;
 begin
 if ( ( upper( p_orientation ) in ( 'L', 'LANDSCAPE' )
 and g_settings.page_height &gt; g_settings.page_width
 )
 or ( upper( p_orientation ) in( 'P', 'PORTRAIT' )
 and g_settings.page_height &lt; g_settings.page_width
 )
 )
 then
 t_tmp := g_settings.page_width;
 g_settings.page_width := g_settings.page_height;
 g_settings.page_height := t_tmp;
 end if;
 end;
 --
 procedure set_margins
 ( p_top number := null
 , p_left number := null
 , p_bottom number := null
 , p_right number := null
 , p_unit varchar2 := 'cm'
 )
 is
 t_tmp number;
 begin
 t_tmp := nvl( conv2uu( p_top, p_unit ), -1 );
 if t_tmp &lt; 0 or t_tmp &gt; g_settings.page_height
 then
 t_tmp := conv2uu( 3, 'cm' );
 end if;
 g_settings.margin_top := t_tmp;
 t_tmp := nvl( conv2uu( p_bottom, p_unit ), -1 );
 if t_tmp &lt; 0 or t_tmp &gt; g_settings.page_height
 then
 t_tmp := conv2uu( 4, 'cm' );
 end if;
 g_settings.margin_bottom := t_tmp;
 t_tmp := nvl( conv2uu( p_left, p_unit ), -1 );
 if t_tmp &lt; 0 or t_tmp &gt; g_settings.page_width
 then
 t_tmp := conv2uu( 1, 'cm' );
 end if;
 g_settings.margin_left := t_tmp;
 t_tmp := nvl( conv2uu( p_right, p_unit ), -1 );
 if t_tmp &lt; 0 or t_tmp &gt; g_settings.page_width
 then
 t_tmp := conv2uu( 1, 'cm' );
 end if;
 g_settings.margin_right := t_tmp;
 --
 if g_settings.margin_top + g_settings.margin_bottom + conv2uu( 1, 'cm' )&gt; g_settings.page_height
 then
 g_settings.margin_top := 0;
 g_settings.margin_bottom := 0;
 end if;
 if g_settings.margin_left + g_settings.margin_right + conv2uu( 1, 'cm' )&gt; g_settings.page_width
 then
 g_settings.margin_left := 0;
 g_settings.margin_right := 0;
 end if;
 end;
 --
 procedure set_info
 ( p_title varchar2 := null
 , p_author varchar2 := null
 , p_subject varchar2 := null
 , p_keywords varchar2 := null
 )
 is
 begin
 g_info.title := substr( p_title, 1, 1024 );
 g_info.author := substr( p_author, 1, 1024 );
 g_info.subject := substr( p_subject, 1, 1024 );
 g_info.keywords := substr( p_keywords, 1, 16383 );
 end;
 --
 procedure init
 is
 begin
 g_objects.delete;
 g_pages.delete;
 g_fonts.delete;
 g_used_fonts.delete;
 g_page_prcs.delete;
 g_images.delete;
 g_settings := null;
 g_current_font := null;
 g_x := null;
 g_y := null;
 g_info := null;
 g_page_nr := null;
 g_objects( 0 ) := 0;
 init_core_fonts;
 set_page_format;
 set_page_orientation;
 set_margins;
 end;
 --
 function get_pdf
 return blob
 is
 begin
 finish_pdf;
 return g_pdf_doc;
 end;
 --
 procedure save_pdf
 ( p_dir varchar2 := 'MY_DIR'
 , p_filename varchar2 := 'my.pdf'
 , p_freeblob boolean := true
 )
 is
 t_fh utl_file.file_type;
 t_len pls_integer := 32767;
 begin
 finish_pdf;
 t_fh := utl_file.fopen( p_dir, p_filename, 'wb' );
 for i in 0 .. trunc( ( dbms_lob.getlength( g_pdf_doc ) - 1 ) / t_len )
 loop
 utl_file.put_raw( t_fh
 , dbms_lob.substr( g_pdf_doc
 , t_len
 , i * t_len + 1
 )
 );
 end loop;
 utl_file.fclose( t_fh );
 if p_freeblob
 then
 dbms_lob.freetemporary( g_pdf_doc );
 end if;
 end;
 --
 procedure raw2page( p_txt blob )
 is
 begin
 if g_pages.count() = 0
 then
 new_page;
 end if;
 dbms_lob.append( g_pages( coalesce( g_page_nr, g_pages.count( ) - 1 ) )
 , utl_raw.concat( p_txt, hextoraw( '0D0A' ) )
 );
 end;
 --
 procedure txt2page( p_txt varchar2 )
 is
 begin
 raw2page( utl_raw.cast_to_raw( p_txt ) );
 end;
 --
 procedure output_font_to_doc( p_output_to_doc boolean )
 is
 begin
 if p_output_to_doc
 then
 txt2page( 'BT /F' || g_current_font || ' '
 || to_char_round( g_fonts( g_current_font ).fontsize ) || ' Tf ET'
 );
 end if;
 end;
 --
 procedure set_font
 ( p_index pls_integer
 , p_fontsize_pt number
 , p_output_to_doc boolean := true
 )
 is
 begin
 if p_index is not null
 then
 g_used_fonts( p_index ) := 0;
 g_current_font := p_index;
 g_fonts( p_index ).fontsize := p_fontsize_pt;
 output_font_to_doc( p_output_to_doc );
 end if;
 end;
 --
 function set_font
 ( p_fontname varchar2
 , p_fontsize_pt number
 , p_output_to_doc boolean := true
 )
 return pls_integer
 is
 t_fontname varchar2(100);
 begin
 if p_fontname is null
 then
 if ( g_current_font is not null
 and p_fontsize_pt != g_fonts( g_current_font ).fontsize
 )
 then
 g_fonts( g_current_font ).fontsize := p_fontsize_pt;
 output_font_to_doc( p_output_to_doc );
 end if;
 return g_current_font;
 end if;
 --
 t_fontname := lower( p_fontname );
 for i in g_fonts.first .. g_fonts.last
 loop
 if lower( g_fonts( i ).fontname ) = t_fontname
 then
 exit when g_current_font = i and g_fonts( i ).fontsize = p_fontsize_pt and g_page_nr is null;
 g_fonts( i ).fontsize := coalesce( p_fontsize_pt
 , g_fonts( nvl( g_current_font, i ) ).fontsize
 , 12
 );
 g_current_font := i;
 g_used_fonts( i ) := 0;
 output_font_to_doc( p_output_to_doc );
 return g_current_font;
 end if;
 end loop;
 return null;
 end;
 --
 procedure set_font
 ( p_fontname varchar2
 , p_fontsize_pt number
 , p_output_to_doc boolean := true
 )
 is
 t_dummy pls_integer;
 begin
 t_dummy := set_font( p_fontname, p_fontsize_pt, p_output_to_doc );
 end;
 --
 function set_font
 ( p_family varchar2
 , p_style varchar2 := 'N'
 , p_fontsize_pt number := null
 , p_output_to_doc boolean := true
 )
 return pls_integer
 is
 t_family varchar2(100);
 t_style varchar2(100);
 begin
 if p_family is null and g_current_font is null
 then
 return null;
 end if;
 if p_family is null and p_style is null and p_fontsize_pt is null
 then
 return null;
 end if;
 t_family := coalesce( lower( p_family )
 , g_fonts( g_current_font ).family
 );
 t_style := upper( p_style );
 t_style := case t_style
 when 'NORMAL' then 'N'
 when 'REGULAR' then 'N'
 when 'BOLD' then 'B'
 when 'ITALIC' then 'I'
 when 'OBLIQUE' then 'I'
 else t_style
 end;
 t_style := coalesce( t_style
 , case when g_current_font is null then 'N' else g_fonts( g_current_font ).style end
 );
 --
 for i in g_fonts.first .. g_fonts.last
 loop
 if ( g_fonts( i ).family = t_family
 and g_fonts( i ).style = t_style
 )
 then
 return set_font( g_fonts( i ).fontname, p_fontsize_pt, p_output_to_doc );
 end if;
 end loop;
 return null;
 end;
 --
 procedure set_font
 ( p_family varchar2
 , p_style varchar2 := 'N'
 , p_fontsize_pt number := null
 , p_output_to_doc boolean := true
 )
 is
 t_dummy pls_integer;
 begin
 t_dummy := set_font( p_family, p_style, p_fontsize_pt, p_output_to_doc );
 end;
 --
 procedure new_page
 is
 begin
 g_pages( g_pages.count() ) := null;
 dbms_lob.createtemporary( g_pages( g_pages.count() - 1 ), true );
 if g_current_font is not null and g_pages.count() &gt; 0
 then
 txt2page( 'BT /F' || g_current_font || ' '
 || to_char_round( g_fonts( g_current_font ).fontsize )
 || ' Tf ET'
 );
 end if;
 g_x := null;
 g_y := null;
 end;
 --
 function pdf_string( p_txt in blob )
 return blob
 is
 t_rv blob;
 t_ind integer;
 type tp_tab_raw is table of raw(1);
 tab_raw tp_tab_raw
 := tp_tab_raw( utl_raw.cast_to_raw( '\' )
 , utl_raw.cast_to_raw( '(' )
 , utl_raw.cast_to_raw( ')' )
 );
 begin
 t_rv := p_txt;
 for i in tab_raw.first .. tab_raw.last
 loop
 t_ind := -1;
 loop
 t_ind := dbms_lob.instr( t_rv
 , tab_raw( i )
 , t_ind + 2
 );
 exit when t_ind &lt;= 0;
 dbms_lob.copy( t_rv
 , t_rv
 , dbms_lob.lobmaxsize
 , t_ind + 1
 , t_ind
 );
 dbms_lob.copy( t_rv
 , utl_raw.cast_to_raw( '\' )
 , 1
 , t_ind
 , 1
 );
 end loop;
 end loop;
 return t_rv;
 end;
 --
 function txt2raw( p_txt varchar2 )
 return raw
 is
 t_rv raw(32767);
 t_unicode pls_integer;
 begin
 if g_current_font is null
 then
 set_font( 'helvetica' );
 end if;
 if g_fonts( g_current_font ).cid
 then
 for i in 1 .. length( p_txt )
 loop
 t_unicode := utl_raw.cast_to_binary_integer( utl_raw.convert( utl_raw.cast_to_raw( substr( p_txt, i, 1 ) )
 , 'AMERICAN_AMERICA.AL16UTF16'
 , sys_context( 'userenv', 'LANGUAGE' ) -- ???? font characterset ?????
 )
 );
 if g_fonts( g_current_font ).flags = 4 -- a symbolic font
 then
 -- assume code 32, space maps to the first code from the font
 t_unicode := g_fonts( g_current_font ).code2glyph.first + t_unicode - 32;
 end if;
 if g_fonts( g_current_font ).code2glyph.exists( t_unicode )
 then
 g_fonts( g_current_font ).used_chars( g_fonts( g_current_font ).code2glyph( t_unicode ) ) := 0;
 t_rv := utl_raw.concat( t_rv
 , utl_raw.cast_to_raw( to_char( g_fonts( g_current_font ).code2glyph( t_unicode ), 'FM0XXX' ) )
 );
 else
 t_rv := utl_raw.concat( t_rv, utl_raw.cast_to_raw( '0000' ) );
 end if;
 end loop;
 t_rv := utl_raw.concat( utl_raw.cast_to_raw( '&lt;' )
 , t_rv
 , utl_raw.cast_to_raw( '&gt;' )
 );
 else
 t_rv := utl_raw.convert( utl_raw.cast_to_raw( p_txt )
 , g_fonts( g_current_font ).charset
 , sys_context( 'userenv', 'LANGUAGE' )
 );
 for i in 1 .. utl_raw.length( t_rv )
 loop
 g_fonts( g_current_font ).used_chars( raw2num( t_rv, i, 1 ) ) := 0;
 end loop;
 t_rv := utl_raw.concat( utl_raw.cast_to_raw( '(' )
 , pdf_string( t_rv )
 , utl_raw.cast_to_raw( ')' )
 );
 end if;
 return t_rv;
 end;
 --
 procedure put_raw( p_x number, p_y number, p_txt raw, p_degrees_rotation number := null )
 is
 c_pi constant number := 3.14159265358979323846264338327950288419716939937510;
 t_tmp varchar2(32767);
 t_sin number;
 t_cos number;
 begin
 t_tmp := to_char_round( p_x ) || ' ' || to_char_round( p_y );
 if p_degrees_rotation is null
 then
 t_tmp := t_tmp || ' Td ';
 else
 t_sin := sin( p_degrees_rotation / 180 * c_pi );
 t_cos := cos( p_degrees_rotation / 180 * c_pi );
 t_tmp := to_char_round( t_cos, 5 ) || ' ' || t_tmp;
 t_tmp := to_char_round( - t_sin, 5 ) || ' ' || t_tmp;
 t_tmp := to_char_round( t_sin, 5 ) || ' ' || t_tmp;
 t_tmp := to_char_round( t_cos, 5 ) || ' ' || t_tmp;
 t_tmp := t_tmp || ' Tm ';
 end if;
 raw2page( utl_raw.concat( utl_raw.cast_to_raw( 'BT ' || t_tmp )
 , p_txt
 , utl_raw.cast_to_raw( ' Tj ET' )
 )
 );
 end;
 --
 procedure put_txt( p_x number, p_y number, p_txt varchar2, p_degrees_rotation number := null )
 is
 begin
 if p_txt is not null
 then
 put_raw( p_x, p_y, txt2raw( p_txt ), p_degrees_rotation );
 end if;
 end;
 --
 function str_len( p_txt in varchar2 )
 return number
 is
 t_width number;
 t_char pls_integer;
 t_rtxt raw(32767);
 t_tmp number;
 t_font tp_font;
 begin
 if p_txt is null
 then
 return 0;
 end if;
 --
 t_width := 0;
 t_font := g_fonts( g_current_font );
 if t_font.cid
 then
 t_rtxt := utl_raw.convert( utl_raw.cast_to_raw( p_txt )
 , 'AMERICAN_AMERICA.AL16UTF16' -- 16 bit font =&gt; 2 bytes per char
 , sys_context( 'userenv', 'LANGUAGE' ) -- ???? font characterset ?????
 );
 for i in 1 .. utl_raw.length( t_rtxt ) / 2
 loop
 t_char := to_number( utl_raw.substr( t_rtxt, i * 2 - 1, 2 ), 'xxxx' );
 if t_font.flags = 4 -- a symbolic font
 then
 -- assume code 32, space maps to the first code from the font
 t_char := t_font.code2glyph.first + t_char - 32;
 end if;
 if ( t_font.code2glyph.exists( t_char )
 and t_font.hmetrics.exists( t_font.code2glyph( t_char ) )
 )
 then
 t_tmp := t_font.hmetrics( t_font.code2glyph( t_char ) );
 else
 t_tmp := t_font.hmetrics( t_font.hmetrics.last() );
 end if;
 t_width := t_width + t_tmp;
 end loop;
 t_width := t_width * t_font.unit_norm;
 t_width := t_width * t_font.fontsize / 1000;
 else
 t_rtxt := utl_raw.convert( utl_raw.cast_to_raw( p_txt )
 , t_font.charset -- should be an 8 bit font
 , sys_context( 'userenv', 'LANGUAGE' )
 );
 for i in 1 .. utl_raw.length( t_rtxt )
 loop
 t_char := to_number( utl_raw.substr( t_rtxt, i, 1 ), 'xx' );
 t_width := t_width + t_font.char_width_tab( t_char );
 end loop;
 t_width := t_width * t_font.fontsize / 1000;
 end if;
 return t_width;
 end;
 --
 procedure write
 ( p_txt in varchar2
 , p_x in number := null
 , p_y in number := null
 , p_line_height in number := null
 , p_start in number := null -- left side of the available text box
 , p_width in number := null -- width of the available text box
 , p_alignment in varchar2 := null
 )
 is
 t_line_height number;
 t_x number;
 t_y number;
 t_start number;
 t_width number;
 t_len number;
 t_cnt pls_integer;
 t_ind pls_integer;
 t_alignment varchar2(100);
 begin
 if p_txt is null
 then
 return;
 end if;
 --
 if g_current_font is null
 then
 set_font( 'helvetica' );
 end if;
 --
 t_line_height := nvl( p_line_height, g_fonts( g_current_font ).fontsize );
 if ( t_line_height &lt; g_fonts( g_current_font ).fontsize
 or t_line_height &gt; ( g_settings.page_height - g_settings.margin_top - t_line_height ) / 4
 )
 then
 t_line_height := g_fonts( g_current_font ).fontsize;
 end if;
 t_start := nvl( p_start, g_settings.margin_left );
 if ( t_start &lt; g_settings.margin_left
 or t_start &gt; g_settings.page_width - g_settings.margin_right - g_settings.margin_left
 )
 then
 t_start := g_settings.margin_left;
 end if;
 t_width := nvl( p_width
 , g_settings.page_width - g_settings.margin_right - g_settings.margin_left
 );
 if ( t_width &lt; str_len( ' ' )
 or t_width &gt; g_settings.page_width - g_settings.margin_right - g_settings.margin_left
 )
 then
 t_width := g_settings.page_width - g_settings.margin_right - g_settings.margin_left;
 end if;
 t_x := coalesce( p_x, g_x, g_settings.margin_left );
 t_y := coalesce( p_y
 , g_y
 , g_settings.page_height - g_settings.margin_top - t_line_height
 );
 if t_y &lt; 0
 then
 t_y := coalesce( g_y
 , g_settings.page_height - g_settings.margin_top - t_line_height
 ) - t_line_height;
 end if;
 if t_x &gt; t_start + t_width
 then
 t_x := t_start;
 t_y := t_y - t_line_height;
 elsif t_x &lt; t_start
 then
 t_x := t_start;
 end if;
 if t_y &lt; g_settings.margin_bottom
 then
 new_page;
 t_x := t_start;
 t_y := g_settings.page_height - g_settings.margin_top - t_line_height;
 end if;
 --
 t_ind := instr( p_txt, chr(10) );
 if t_ind &gt; 0
 then
 g_x := t_x;
 g_y := t_y;
 write( rtrim( substr( p_txt, 1, t_ind - 1 ), chr(13) ), t_x, t_y, t_line_height, t_start, t_width, p_alignment );
 t_y := g_y - t_line_height;
 if t_y &lt; g_settings.margin_bottom
 then
 new_page;
 t_y := g_settings.page_height - g_settings.margin_top - t_line_height;
 end if;
 g_x := t_start;
 g_y := t_y;
 write( substr( p_txt, t_ind + 1 ), t_start, t_y, t_line_height, t_start, t_width, p_alignment );
 return;
 end if;
 --
 t_len := str_len( p_txt );
 if t_len &lt;= t_width - t_x + t_start
 then
 t_alignment := lower( substr( p_alignment, 1, 100 ) );
 if instr( t_alignment, 'right' ) &gt; 0 or instr( t_alignment, 'end' ) &gt; 0
 then
 t_x := t_start + t_width - t_len;
 elsif instr( t_alignment, 'center' ) &gt; 0
 then
 t_x := ( t_width + t_x + t_start - t_len ) / 2;
 end if;
 put_txt( t_x, t_y, p_txt );
 g_x := t_x + t_len + str_len( ' ' );
 g_y := t_y;
 return;
 end if;
 --
 t_cnt := 0;
 while ( instr( p_txt, ' ', 1, t_cnt + 1 ) &gt; 0
 and str_len( substr( p_txt, 1, instr( p_txt, ' ', 1, t_cnt + 1 ) - 1 ) ) &lt;= t_width - t_x + t_start
 )
 loop
 t_cnt := t_cnt + 1;
 end loop;
 if t_cnt &gt; 0
 then
 t_ind := instr( p_txt, ' ', 1, t_cnt );
 write( substr( p_txt, 1, t_ind - 1 ), t_x, t_y, t_line_height, t_start, t_width, p_alignment );
 t_y := t_y - t_line_height;
 if t_y &lt; g_settings.margin_bottom
 then
 new_page;
 t_y := g_settings.page_height - g_settings.margin_top - t_line_height;
 end if;
 write( substr( p_txt, t_ind + 1 ), t_start, t_y, t_line_height, t_start, t_width, p_alignment );
 return;
 end if;
 --
 if t_x &gt; t_start and t_len &lt; t_width
 then
 t_y := t_y - t_line_height;
 if t_y &lt; g_settings.margin_bottom
 then
 new_page;
 t_y := g_settings.page_height - g_settings.margin_top - t_line_height;
 end if;
 write( p_txt, t_start, t_y, t_line_height, t_start, t_width, p_alignment );
 else
 if length( p_txt ) = 1
 then
 if t_x &gt; t_start
 then
 t_y := t_y - t_line_height;
 if t_y &lt; g_settings.margin_bottom
 then
 new_page;
 t_y := g_settings.page_height - g_settings.margin_top - t_line_height;
 end if;
 end if;
 write( p_txt, t_x, t_y, t_line_height, t_start, t_len );
 else
 t_ind := 2; -- start with 2 to make sure we get amaller string!
 while str_len( substr( p_txt, 1, t_ind ) ) &lt;= t_width - t_x + t_start
 loop
 t_ind := t_ind + 1;
 end loop;
 write( substr( p_txt, 1, t_ind - 1 ), t_x, t_y, t_line_height, t_start, t_width, p_alignment );
 t_y := t_y - t_line_height;
 if t_y &lt; g_settings.margin_bottom
 then
 new_page;
 t_y := g_settings.page_height - g_settings.margin_top - t_line_height;
 end if;
 write( substr( p_txt, t_ind ), t_start, t_y, t_line_height, t_start, t_width, p_alignment );
 end if;
 end if;
 end;
 --
 function load_ttf_font
 ( p_font blob
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 , p_offset number := 1
 )
 return pls_integer
 is
 this_font tp_font;
 type tp_font_table is record
 ( offset pls_integer
 , length pls_integer
 );
 type tp_tables is table of tp_font_table index by varchar2(4);
 t_tables tp_tables;
 t_tag varchar2(4);
 t_blob blob;
 t_offset pls_integer;
 nr_hmetrics pls_integer;
 subtype tp_glyphname is varchar2(250);
 type tp_glyphnames is table of tp_glyphname index by pls_integer;
 t_glyphnames tp_glyphnames;
 t_glyph2name tp_pls_tab;
 t_font_ind pls_integer;
 begin
 if dbms_lob.substr( p_font, 4, p_offset ) != hextoraw( '00010000' ) -- OpenType Font
 then
 return null;
 end if;
 for i in 1 .. blob2num( p_font, 2, p_offset + 4 )
 loop
 t_tag :=
 utl_raw.cast_to_varchar2( dbms_lob.substr( p_font, 4, p_offset - 4 + i * 16 ) );
 t_tables( t_tag ).offset := blob2num( p_font, 4, p_offset + 4 + i * 16 ) + 1;
 t_tables( t_tag ).length := blob2num( p_font, 4, p_offset + 8 + i * 16 );
 end loop;
 --
 if ( not t_tables.exists( 'cmap' )
 or not t_tables.exists( 'glyf' )
 or not t_tables.exists( 'head' )
 or not t_tables.exists( 'hhea' )
 or not t_tables.exists( 'hmtx' )
 or not t_tables.exists( 'loca' )
 or not t_tables.exists( 'maxp' )
 or not t_tables.exists( 'name' )
 or not t_tables.exists( 'post' )
 )
 then
 return null;
 end if;
 --
 dbms_lob.createtemporary( t_blob, true );
 dbms_lob.copy( t_blob, p_font, t_tables( 'maxp' ).length, 1, t_tables( 'maxp' ).offset );
 this_font.numGlyphs := blob2num( t_blob, 2, 5 );
 --
 dbms_lob.copy( t_blob, p_font, t_tables( 'cmap' ).length, 1, t_tables( 'cmap' ).offset );
 for i in 0 .. blob2num( t_blob, 2, 3 ) - 1
 loop
 if ( dbms_lob.substr( t_blob, 2, 5 + i * 8 ) = hextoraw( '0003' ) -- Windows
 and dbms_lob.substr( t_blob, 2, 5 + i * 8 + 2 )
 in ( hextoraw( '0000' ) -- Symbol
 , hextoraw( '0001' ) -- Unicode BMP (UCS-2)
 )
 )
 then
 if dbms_lob.substr( t_blob, 2, 5 + i * 8 + 2 ) = hextoraw( '0000' ) -- Symbol
 then
 this_font.flags := 4; -- symbolic
 else
 this_font.flags := 32; -- non-symbolic
 end if;
 t_offset := blob2num( t_blob, 4, 5 + i * 8 + 4 ) + 1;
 if dbms_lob.substr( t_blob, 2, t_offset ) != hextoraw( '0004' )
 then
 return null;
 end if;
 declare
 t_seg_cnt pls_integer;
 t_end_offs pls_integer;
 t_start_offs pls_integer;
 t_idDelta_offs pls_integer;
 t_idRangeOffset_offs pls_integer;
 t_tmp pls_integer;
 t_start pls_integer;
 begin
 t_seg_cnt := blob2num( t_blob, 2, t_offset + 6 ) / 2;
 t_end_offs := t_offset + 14;
 t_start_offs := t_end_offs + t_seg_cnt * 2 + 2;
 t_idDelta_offs := t_start_offs + t_seg_cnt * 2;
 t_idRangeOffset_offs := t_idDelta_offs + t_seg_cnt * 2;
 for seg in 0 .. t_seg_cnt - 1
 loop
 t_tmp := blob2num( t_blob, 2, t_idRangeOffset_offs + seg * 2 );
 if t_tmp = 0
 then
 t_tmp := blob2num( t_blob, 2, t_idDelta_offs + seg * 2 );
 for c in blob2num( t_blob, 2, t_start_offs + seg * 2 )
 .. blob2num( t_blob, 2, t_end_offs + seg * 2 )
 loop
 this_font.code2glyph( c ) := mod( c + t_tmp, 65536 );
 end loop;
 else
 t_start := blob2num( t_blob, 2, t_start_offs + seg * 2 );
 for c in t_start .. blob2num( t_blob, 2, t_end_offs + seg * 2 )
 loop
 this_font.code2glyph( c ) := blob2num( t_blob, 2, t_idRangeOffset_offs + t_tmp + ( seg + c - t_start ) * 2 );
 end loop;
 end if;
 end loop;
 end;
 exit;
 end if;
 end loop;
 --
 t_glyphnames( 0 ) := '.notdef';
 t_glyphnames( 1 ) := '.null';
 t_glyphnames( 2 ) := 'nonmarkingreturn';
 t_glyphnames( 3 ) := 'space';
 t_glyphnames( 4 ) := 'exclam';
 t_glyphnames( 5 ) := 'quotedbl';
 t_glyphnames( 6 ) := 'numbersign';
 t_glyphnames( 7 ) := 'dollar';
 t_glyphnames( 8 ) := 'percent';
 t_glyphnames( 9 ) := 'ampersand';
 t_glyphnames( 10 ) := 'quotesingle';
 t_glyphnames( 11 ) := 'parenleft';
 t_glyphnames( 12 ) := 'parenright';
 t_glyphnames( 13 ) := 'asterisk';
 t_glyphnames( 14 ) := 'plus';
 t_glyphnames( 15 ) := 'comma';
 t_glyphnames( 16 ) := 'hyphen';
 t_glyphnames( 17 ) := 'period';
 t_glyphnames( 18 ) := 'slash';
 t_glyphnames( 19 ) := 'zero';
 t_glyphnames( 20 ) := 'one';
 t_glyphnames( 21 ) := 'two';
 t_glyphnames( 22 ) := 'three';
 t_glyphnames( 23 ) := 'four';
 t_glyphnames( 24 ) := 'five';
 t_glyphnames( 25 ) := 'six';
 t_glyphnames( 26 ) := 'seven';
 t_glyphnames( 27 ) := 'eight';
 t_glyphnames( 28 ) := 'nine';
 t_glyphnames( 29 ) := 'colon';
 t_glyphnames( 30 ) := 'semicolon';
 t_glyphnames( 31 ) := 'less';
 t_glyphnames( 32 ) := 'equal';
 t_glyphnames( 33 ) := 'greater';
 t_glyphnames( 34 ) := 'question';
 t_glyphnames( 35 ) := 'at';
 t_glyphnames( 36 ) := 'A';
 t_glyphnames( 37 ) := 'B';
 t_glyphnames( 38 ) := 'C';
 t_glyphnames( 39 ) := 'D';
 t_glyphnames( 40 ) := 'E';
 t_glyphnames( 41 ) := 'F';
 t_glyphnames( 42 ) := 'G';
 t_glyphnames( 43 ) := 'H';
 t_glyphnames( 44 ) := 'I';
 t_glyphnames( 45 ) := 'J';
 t_glyphnames( 46 ) := 'K';
 t_glyphnames( 47 ) := 'L';
 t_glyphnames( 48 ) := 'M';
 t_glyphnames( 49 ) := 'N';
 t_glyphnames( 50 ) := 'O';
 t_glyphnames( 51 ) := 'P';
 t_glyphnames( 52 ) := 'Q';
 t_glyphnames( 53 ) := 'R';
 t_glyphnames( 54 ) := 'S';
 t_glyphnames( 55 ) := 'T';
 t_glyphnames( 56 ) := 'U';
 t_glyphnames( 57 ) := 'V';
 t_glyphnames( 58 ) := 'W';
 t_glyphnames( 59 ) := 'X';
 t_glyphnames( 60 ) := 'Y';
 t_glyphnames( 61 ) := 'Z';
 t_glyphnames( 62 ) := 'bracketleft';
 t_glyphnames( 63 ) := 'backslash';
 t_glyphnames( 64 ) := 'bracketright';
 t_glyphnames( 65 ) := 'asciicircum';
 t_glyphnames( 66 ) := 'underscore';
 t_glyphnames( 67 ) := 'grave';
 t_glyphnames( 68 ) := 'a';
 t_glyphnames( 69 ) := 'b';
 t_glyphnames( 70 ) := 'c';
 t_glyphnames( 71 ) := 'd';
 t_glyphnames( 72 ) := 'e';
 t_glyphnames( 73 ) := 'f';
 t_glyphnames( 74 ) := 'g';
 t_glyphnames( 75 ) := 'h';
 t_glyphnames( 76 ) := 'i';
 t_glyphnames( 77 ) := 'j';
 t_glyphnames( 78 ) := 'k';
 t_glyphnames( 79 ) := 'l';
 t_glyphnames( 80 ) := 'm';
 t_glyphnames( 81 ) := 'n';
 t_glyphnames( 82 ) := 'o';
 t_glyphnames( 83 ) := 'p';
 t_glyphnames( 84 ) := 'q';
 t_glyphnames( 85 ) := 'r';
 t_glyphnames( 86 ) := 's';
 t_glyphnames( 87 ) := 't';
 t_glyphnames( 88 ) := 'u';
 t_glyphnames( 89 ) := 'v';
 t_glyphnames( 90 ) := 'w';
 t_glyphnames( 91 ) := 'x';
 t_glyphnames( 92 ) := 'y';
 t_glyphnames( 93 ) := 'z';
 t_glyphnames( 94 ) := 'braceleft';
 t_glyphnames( 95 ) := 'bar';
 t_glyphnames( 96 ) := 'braceright';
 t_glyphnames( 97 ) := 'asciitilde';
 t_glyphnames( 98 ) := 'Adieresis';
 t_glyphnames( 99 ) := 'Aring';
 t_glyphnames( 100 ) := 'Ccedilla';
 t_glyphnames( 101 ) := 'Eacute';
 t_glyphnames( 102 ) := 'Ntilde';
 t_glyphnames( 103 ) := 'Odieresis';
 t_glyphnames( 104 ) := 'Udieresis';
 t_glyphnames( 105 ) := 'aacute';
 t_glyphnames( 106 ) := 'agrave';
 t_glyphnames( 107 ) := 'acircumflex';
 t_glyphnames( 108 ) := 'adieresis';
 t_glyphnames( 109 ) := 'atilde';
 t_glyphnames( 110 ) := 'aring';
 t_glyphnames( 111 ) := 'ccedilla';
 t_glyphnames( 112 ) := 'eacute';
 t_glyphnames( 113 ) := 'egrave';
 t_glyphnames( 114 ) := 'ecircumflex';
 t_glyphnames( 115 ) := 'edieresis';
 t_glyphnames( 116 ) := 'iacute';
 t_glyphnames( 117 ) := 'igrave';
 t_glyphnames( 118 ) := 'icircumflex';
 t_glyphnames( 119 ) := 'idieresis';
 t_glyphnames( 120 ) := 'ntilde';
 t_glyphnames( 121 ) := 'oacute';
 t_glyphnames( 122 ) := 'ograve';
 t_glyphnames( 123 ) := 'ocircumflex';
 t_glyphnames( 124 ) := 'odieresis';
 t_glyphnames( 125 ) := 'otilde';
 t_glyphnames( 126 ) := 'uacute';
 t_glyphnames( 127 ) := 'ugrave';
 t_glyphnames( 128 ) := 'ucircumflex';
 t_glyphnames( 129 ) := 'udieresis';
 t_glyphnames( 130 ) := 'dagger';
 t_glyphnames( 131 ) := 'degree';
 t_glyphnames( 132 ) := 'cent';
 t_glyphnames( 133 ) := 'sterling';
 t_glyphnames( 134 ) := 'section';
 t_glyphnames( 135 ) := 'bullet';
 t_glyphnames( 136 ) := 'paragraph';
 t_glyphnames( 137 ) := 'germandbls';
 t_glyphnames( 138 ) := 'registered';
 t_glyphnames( 139 ) := 'copyright';
 t_glyphnames( 140 ) := 'trademark';
 t_glyphnames( 141 ) := 'acute';
 t_glyphnames( 142 ) := 'dieresis';
 t_glyphnames( 143 ) := 'notequal';
 t_glyphnames( 144 ) := 'AE';
 t_glyphnames( 145 ) := 'Oslash';
 t_glyphnames( 146 ) := 'infinity';
 t_glyphnames( 147 ) := 'plusminus';
 t_glyphnames( 148 ) := 'lessequal';
 t_glyphnames( 149 ) := 'greaterequal';
 t_glyphnames( 150 ) := 'yen';
 t_glyphnames( 151 ) := 'mu';
 t_glyphnames( 152 ) := 'partialdiff';
 t_glyphnames( 153 ) := 'summation';
 t_glyphnames( 154 ) := 'product';
 t_glyphnames( 155 ) := 'pi';
 t_glyphnames( 156 ) := 'integral';
 t_glyphnames( 157 ) := 'ordfeminine';
 t_glyphnames( 158 ) := 'ordmasculine';
 t_glyphnames( 159 ) := 'Omega';
 t_glyphnames( 160 ) := 'ae';
 t_glyphnames( 161 ) := 'oslash';
 t_glyphnames( 162 ) := 'questiondown';
 t_glyphnames( 163 ) := 'exclamdown';
 t_glyphnames( 164 ) := 'logicalnot';
 t_glyphnames( 165 ) := 'radical';
 t_glyphnames( 166 ) := 'florin';
 t_glyphnames( 167 ) := 'approxequal';
 t_glyphnames( 168 ) := 'Delta';
 t_glyphnames( 169 ) := 'guillemotleft';
 t_glyphnames( 170 ) := 'guillemotright';
 t_glyphnames( 171 ) := 'ellipsis';
 t_glyphnames( 172 ) := 'nonbreakingspace';
 t_glyphnames( 173 ) := 'Agrave';
 t_glyphnames( 174 ) := 'Atilde';
 t_glyphnames( 175 ) := 'Otilde';
 t_glyphnames( 176 ) := 'OE';
 t_glyphnames( 177 ) := 'oe';
 t_glyphnames( 178 ) := 'endash';
 t_glyphnames( 179 ) := 'emdash';
 t_glyphnames( 180 ) := 'quotedblleft';
 t_glyphnames( 181 ) := 'quotedblright';
 t_glyphnames( 182 ) := 'quoteleft';
 t_glyphnames( 183 ) := 'quoteright';
 t_glyphnames( 184 ) := 'divide';
 t_glyphnames( 185 ) := 'lozenge';
 t_glyphnames( 186 ) := 'ydieresis';
 t_glyphnames( 187 ) := 'Ydieresis';
 t_glyphnames( 188 ) := 'fraction';
 t_glyphnames( 189 ) := 'currency';
 t_glyphnames( 190 ) := 'guilsinglleft';
 t_glyphnames( 191 ) := 'guilsinglright';
 t_glyphnames( 192 ) := 'fi';
 t_glyphnames( 193 ) := 'fl';
 t_glyphnames( 194 ) := 'daggerdbl';
 t_glyphnames( 195 ) := 'periodcentered';
 t_glyphnames( 196 ) := 'quotesinglbase';
 t_glyphnames( 197 ) := 'quotedblbase';
 t_glyphnames( 198 ) := 'perthousand';
 t_glyphnames( 199 ) := 'Acircumflex';
 t_glyphnames( 200 ) := 'Ecircumflex';
 t_glyphnames( 201 ) := 'Aacute';
 t_glyphnames( 202 ) := 'Edieresis';
 t_glyphnames( 203 ) := 'Egrave';
 t_glyphnames( 204 ) := 'Iacute';
 t_glyphnames( 205 ) := 'Icircumflex';
 t_glyphnames( 206 ) := 'Idieresis';
 t_glyphnames( 207 ) := 'Igrave';
 t_glyphnames( 208 ) := 'Oacute';
 t_glyphnames( 209 ) := 'Ocircumflex';
 t_glyphnames( 210 ) := 'apple';
 t_glyphnames( 211 ) := 'Ograve';
 t_glyphnames( 212 ) := 'Uacute';
 t_glyphnames( 213 ) := 'Ucircumflex';
 t_glyphnames( 214 ) := 'Ugrave';
 t_glyphnames( 215 ) := 'dotlessi';
 t_glyphnames( 216 ) := 'circumflex';
 t_glyphnames( 217 ) := 'tilde';
 t_glyphnames( 218 ) := 'macron';
 t_glyphnames( 219 ) := 'breve';
 t_glyphnames( 220 ) := 'dotaccent';
 t_glyphnames( 221 ) := 'ring';
 t_glyphnames( 222 ) := 'cedilla';
 t_glyphnames( 223 ) := 'hungarumlaut';
 t_glyphnames( 224 ) := 'ogonek';
 t_glyphnames( 225 ) := 'caron';
 t_glyphnames( 226 ) := 'Lslash';
 t_glyphnames( 227 ) := 'lslash';
 t_glyphnames( 228 ) := 'Scaron';
 t_glyphnames( 229 ) := 'scaron';
 t_glyphnames( 230 ) := 'Zcaron';
 t_glyphnames( 231 ) := 'zcaron';
 t_glyphnames( 232 ) := 'brokenbar';
 t_glyphnames( 233 ) := 'Eth';
 t_glyphnames( 234 ) := 'eth';
 t_glyphnames( 235 ) := 'Yacute';
 t_glyphnames( 236 ) := 'yacute';
 t_glyphnames( 237 ) := 'Thorn';
 t_glyphnames( 238 ) := 'thorn';
 t_glyphnames( 239 ) := 'minus';
 t_glyphnames( 240 ) := 'multiply';
 t_glyphnames( 241 ) := 'onesuperior';
 t_glyphnames( 242 ) := 'twosuperior';
 t_glyphnames( 243 ) := 'threesuperior';
 t_glyphnames( 244 ) := 'onehalf';
 t_glyphnames( 245 ) := 'onequarter';
 t_glyphnames( 246 ) := 'threequarters';
 t_glyphnames( 247 ) := 'franc';
 t_glyphnames( 248 ) := 'Gbreve';
 t_glyphnames( 249 ) := 'gbreve';
 t_glyphnames( 250 ) := 'Idotaccent';
 t_glyphnames( 251 ) := 'Scedilla';
 t_glyphnames( 252 ) := 'scedilla';
 t_glyphnames( 253 ) := 'Cacute';
 t_glyphnames( 254 ) := 'cacute';
 t_glyphnames( 255 ) := 'Ccaron';
 t_glyphnames( 256 ) := 'ccaron';
 t_glyphnames( 257 ) := 'dcroat';
 --
 dbms_lob.copy( t_blob, p_font, t_tables( 'post' ).length, 1, t_tables( 'post' ).offset );
 this_font.italic_angle := to_short( dbms_lob.substr( t_blob, 2, 5 ) )
 + to_short( dbms_lob.substr( t_blob, 2, 7 ) ) / 65536;
 case rawtohex( dbms_lob.substr( t_blob, 4, 1 ) )
 when '00010000'
 then
 for g in 0 .. 257
 loop
 t_glyph2name( g ) := g;
 end loop;
 when '00020000'
 then
 t_offset := blob2num( t_blob, 2, 33 ) * 2 + 35;
 while nvl( blob2num( t_blob, 1, t_offset ), 0 ) &gt; 0
 loop
 t_glyphnames( t_glyphnames.count ) := utl_raw.cast_to_varchar2( dbms_lob.substr( t_blob, blob2num( t_blob, 1, t_offset ), t_offset + 1 ) );
 t_offset := t_offset + blob2num( t_blob, 1, t_offset ) + 1;
 end loop;
 for g in 0 .. blob2num( t_blob, 2, 33 ) - 1
 loop
 t_glyph2name( g ) := blob2num( t_blob, 2, 35 + 2 * g );
 end loop;
 when '00025000'
 then
 for g in 0 .. blob2num( t_blob, 2, 33 ) - 1
 loop
 t_offset := blob2num( t_blob, 1, 35 + g );
 if t_offset &gt; 127
 then
 t_glyph2name( g ) := g - t_offset;
 else
 t_glyph2name( g ) := g + t_offset;
 end if;
 end loop;
 when '00030000'
 then
 t_glyphnames.delete;
 else
 dbms_output.put_line( 'no post ' || dbms_lob.substr( t_blob, 4, 1 ) );
 end case;
 --
 dbms_lob.copy( t_blob, p_font, t_tables( 'head' ).length, 1, t_tables( 'head' ).offset );
 if dbms_lob.substr( t_blob, 4, 13 ) = hextoraw( '5F0F3CF5' ) -- magic
 then
 declare
 t_tmp pls_integer := blob2num( t_blob, 2, 45 );
 begin
 if bitand( t_tmp, 1 ) = 1
 then
 this_font.style := 'B';
 end if;
 if bitand( t_tmp, 2 ) = 2
 then
 this_font.style := this_font.style || 'I';
 this_font.flags := this_font.flags + 64;
 end if;
 this_font.style := nvl( this_font.style, 'N' );
 this_font.unit_norm := 1000 / blob2num( t_blob, 2, 19 );
 this_font.bb_xmin := to_short( dbms_lob.substr( t_blob, 2, 37 ), this_font.unit_norm );
 this_font.bb_ymin := to_short( dbms_lob.substr( t_blob, 2, 39 ), this_font.unit_norm );
 this_font.bb_xmax := to_short( dbms_lob.substr( t_blob, 2, 41 ), this_font.unit_norm );
 this_font.bb_ymax := to_short( dbms_lob.substr( t_blob, 2, 43 ), this_font.unit_norm );
 this_font.indexToLocFormat := blob2num( t_blob, 2, 51 ); -- 0 for short offsets, 1 for long
 end;
 end if;
 --
 dbms_lob.copy( t_blob, p_font, t_tables( 'hhea' ).length, 1, t_tables( 'hhea' ).offset );
 if dbms_lob.substr( t_blob, 4, 1 ) = hextoraw( '00010000' ) -- version 1.0
 then
 this_font.ascent := to_short( dbms_lob.substr( t_blob, 2, 5 ), this_font.unit_norm );
 this_font.descent := to_short( dbms_lob.substr( t_blob, 2, 7 ), this_font.unit_norm );
 this_font.capheight := this_font.ascent;
 nr_hmetrics := blob2num( t_blob, 2, 35 );
 end if;
 --
 dbms_lob.copy( t_blob, p_font, t_tables( 'hmtx' ).length, 1, t_tables( 'hmtx' ).offset );
 for j in 0 .. nr_hmetrics - 1
 loop
 this_font.hmetrics( j ) := blob2num( t_blob, 2, 1 + 4 * j );
 end loop;
 --
 dbms_lob.copy( t_blob, p_font, t_tables( 'name' ).length, 1, t_tables( 'name' ).offset );
 if dbms_lob.substr( t_blob, 2, 1 ) = hextoraw( '0000' ) -- format 0
 then
 t_offset := blob2num( t_blob, 2, 5 ) + 1;
 for j in 0 .. blob2num( t_blob, 2, 3 ) - 1
 loop
 if ( dbms_lob.substr( t_blob, 2, 7 + j * 12 ) = hextoraw( '0003' ) -- Windows
 and dbms_lob.substr( t_blob, 2, 11 + j * 12 ) = hextoraw( '0409' ) -- English United States
 )
 then
 case rawtohex( dbms_lob.substr( t_blob, 2, 13 + j * 12 ) )
 when '0001'
 then
 this_font.family := utl_i18n.raw_to_char( dbms_lob.substr( t_blob, blob2num( t_blob, 2, 15 + j * 12 ), t_offset + blob2num( t_blob, 2, 17 + j * 12 ) ), 'AL16UTF16' );
 when '0006'
 then
 this_font.name := utl_i18n.raw_to_char( dbms_lob.substr( t_blob, blob2num( t_blob, 2, 15 + j * 12 ), t_offset + blob2num( t_blob, 2, 17 + j * 12 ) ), 'AL16UTF16' );
 else
 null;
 end case;
 end if;
 end loop;
 end if;
 --
 if this_font.italic_angle != 0
 then
 this_font.flags := this_font.flags + 64;
 end if;
 this_font.subtype := 'TrueType';
 this_font.stemv := 50;
 this_font.family := lower( this_font.family );
 this_font.encoding := utl_i18n.map_charset( p_encoding
 , utl_i18n.generic_context
 , utl_i18n.iana_to_oracle
 );
 this_font.encoding := nvl( this_font.encoding, upper( p_encoding ) );
 this_font.charset := sys_context( 'userenv', 'LANGUAGE' );
 this_font.charset := substr( this_font.charset
 , 1
 , instr( this_font.charset, '.' )
 ) || this_font.encoding;
 this_font.cid := upper( p_encoding ) in ( 'CID', 'AL16UTF16', 'UTF', 'UNICODE' );
 this_font.fontname := this_font.name;
 this_font.compress_font := p_compress;
 --
 if ( p_embed or this_font.cid ) and t_tables.exists( 'OS/2' )
 then
 dbms_lob.copy( t_blob, p_font, t_tables( 'OS/2' ).length, 1, t_tables( 'OS/2' ).offset );
 if blob2num( t_blob, 2, 9 ) != 2
 then
 this_font.fontfile2 := p_font;
 this_font.ttf_offset := p_offset;
 this_font.name := dbms_random.string( 'u', 6 ) || '+' || this_font.name;
 --
 t_blob := dbms_lob.substr( p_font, t_tables( 'loca' ).length, t_tables( 'loca' ).offset );
 declare
 t_size pls_integer := 2 + this_font.indexToLocFormat * 2; -- 0 for short offsets, 1 for long
 begin
 for i in 0 .. this_font.numGlyphs
 loop
 this_font.loca( i ) := blob2num( t_blob, t_size, 1 + i * t_size );
 end loop;
 end;
 end if;
 end if;
 --
 if not this_font.cid
 then
 if this_font.flags = 4 -- a symbolic font
 then
 declare
 t_real pls_integer;
 begin
 for t_code in 32 .. 255
 loop
 t_real := this_font.code2glyph.first + t_code - 32; -- assume code 32, space maps to the first code from the font
 if this_font.code2glyph.exists( t_real )
 then
 this_font.first_char := least( nvl( this_font.first_char, 255 ), t_code );
 this_font.last_char := t_code;
 if this_font.hmetrics.exists( this_font.code2glyph( t_real ) )
 then
 this_font.char_width_tab( t_code ) := trunc( this_font.hmetrics( this_font.code2glyph( t_real ) ) * this_font.unit_norm );
 else
 this_font.char_width_tab( t_code ) := trunc( this_font.hmetrics( this_font.hmetrics.last() ) * this_font.unit_norm );
 end if;
 else
 this_font.char_width_tab( t_code ) := trunc( this_font.hmetrics( 0 ) * this_font.unit_norm );
 end if;
 end loop;
 end;
 else
 declare
 t_unicode pls_integer;
 t_prv_diff pls_integer;
 t_utf16_charset varchar2(1000);
 t_winansi_charset varchar2(1000);
 t_glyphname tp_glyphname;
 begin
 t_prv_diff := -1;
 t_utf16_charset := substr( this_font.charset, 1, instr( this_font.charset, '.' ) ) || 'AL16UTF16';
 t_winansi_charset := substr( this_font.charset, 1, instr( this_font.charset, '.' ) ) || 'WE8MSWIN1252';
 for t_code in 32 .. 255
 loop
 t_unicode := utl_raw.cast_to_binary_integer( utl_raw.convert( hextoraw( to_char( t_code, 'fm0x' ) )
 , t_utf16_charset
 , this_font.charset
 )
 );
 t_glyphname := '';
 this_font.char_width_tab( t_code ) := trunc( this_font.hmetrics( this_font.hmetrics.last() ) * this_font.unit_norm );
 if this_font.code2glyph.exists( t_unicode )
 then
 this_font.first_char := least( nvl( this_font.first_char, 255 ), t_code );
 this_font.last_char := t_code;
 if this_font.hmetrics.exists( this_font.code2glyph( t_unicode ) )
 then
 this_font.char_width_tab( t_code ) := trunc( this_font.hmetrics( this_font.code2glyph( t_unicode ) ) * this_font.unit_norm );
 end if;
 if t_glyph2name.exists( this_font.code2glyph( t_unicode ) )
 then
 if t_glyphnames.exists( t_glyph2name( this_font.code2glyph( t_unicode ) ) )
 then
 t_glyphname := t_glyphnames( t_glyph2name( this_font.code2glyph( t_unicode ) ) );
 end if;
 end if;
 end if;
 --
 if ( t_glyphname is not null
 and t_unicode != utl_raw.cast_to_binary_integer( utl_raw.convert( hextoraw( to_char( t_code, 'fm0x' ) )
 , t_winansi_charset
 , this_font.charset
 )
 )
 )
 then
 this_font.diff := this_font.diff || case when t_prv_diff != t_code - 1 then ' ' || t_code end || ' /' || t_glyphname;
 t_prv_diff := t_code;
 end if;
 end loop;
 end;
 if this_font.diff is not null
 then
 this_font.diff := '/Differences [' || this_font.diff || ']';
 end if;
 end if;
 end if;
 --
 t_font_ind := g_fonts.count( ) + 1;
 g_fonts( t_font_ind ) := this_font;
 /*
 --
 dbms_output.put_line( this_font.fontname || ' ' || this_font.family || ' ' || this_font.style
 || ' ' || this_font.flags
 || ' ' || this_font.code2glyph.first
 || ' ' || this_font.code2glyph.prior( this_font.code2glyph.last )
 || ' ' || this_font.code2glyph.last
 || ' nr glyphs: ' || this_font.numGlyphs
 ); */
 --
 return t_font_ind;
 end;
 --
 procedure load_ttf_font
 ( p_font blob
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 , p_offset number := 1
 )
 is
 t_tmp pls_integer;
 begin
 t_tmp := load_ttf_font( p_font, p_encoding, p_embed, p_compress );
 end;
 --
 function load_ttf_font
 ( p_dir varchar2 := 'MY_FONTS'
 , p_filename varchar2 := 'BAUHS93.TTF'
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 )
 return pls_integer
 is
 begin
 return load_ttf_font( file2blob( p_dir, p_filename ), p_encoding, p_embed, p_compress );
 end;
 --
 procedure load_ttf_font
 ( p_dir varchar2 := 'MY_FONTS'
 , p_filename varchar2 := 'BAUHS93.TTF'
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 )
 is
 begin
 load_ttf_font( file2blob( p_dir, p_filename ), p_encoding, p_embed, p_compress );
 end;
 --
 procedure load_ttc_fonts
 ( p_ttc blob
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 )
 is
 type tp_font_table is record
 ( offset pls_integer
 , length pls_integer
 );
 type tp_tables is table of tp_font_table index by varchar2(4);
 t_tables tp_tables;
 t_tag varchar2(4);
 t_blob blob;
 t_offset pls_integer;
 t_font_ind pls_integer;
 begin
 if utl_raw.cast_to_varchar2( dbms_lob.substr( p_ttc, 4, 1 ) ) != 'ttcf'
 then
 return;
 end if;
 for f in 0 .. blob2num( p_ttc, 4, 9 ) - 1
 loop
 t_font_ind := load_ttf_font( p_ttc, p_encoding, p_embed, p_compress, blob2num( p_ttc, 4, 13 + f * 4 ) + 1 );
 dbms_output.put_line( t_font_ind || ' ' || g_fonts( t_font_ind ).fontname || ' ' || g_fonts( t_font_ind ).family || ' ' || g_fonts( t_font_ind ).style );
 end loop;
 end;
 --
 procedure load_ttc_fonts
 ( p_dir varchar2 := 'MY_FONTS'
 , p_filename varchar2 := 'CAMBRIA.TTC'
 , p_encoding varchar2 := 'WINDOWS-1252'
 , p_embed boolean := false
 , p_compress boolean := true
 )
 is
 begin
 load_ttc_fonts( file2blob( p_dir, p_filename ), p_encoding, p_embed, p_compress );
 end;
 --
 function rgb( p_hex_rgb varchar2 )
 return varchar2
 is
 begin
 return to_char_round( nvl( to_number( substr( ltrim( p_hex_rgb, '#' )
 , 1, 2 )
 , 'xx' ) / 255
 , 0 )
 , 5 ) || ' '
 || to_char_round( nvl( to_number( substr( ltrim( p_hex_rgb, '#' )
 , 3, 2 )
 , 'xx' ) / 255
 , 0 )
 , 5 ) || ' '
 || to_char_round( nvl( to_number( substr( ltrim( p_hex_rgb, '#' )
 , 5, 2 )
 , 'xx' ) / 255
 , 0 )
 , 5 ) || ' ';
 end;
 --
 procedure set_color( p_rgb varchar2 := '000000', p_backgr boolean )
 is
 begin
 txt2page( rgb( p_rgb ) || case when p_backgr then 'RG' else 'rg' end );
 end;
 --
 procedure set_color( p_rgb varchar2 := '000000' )
 is
 begin
 set_color( p_rgb, false );
 end;
 --
 procedure set_color
 ( p_red number := 0
 , p_green number := 0
 , p_blue number := 0
 )
 is
 begin
 if ( p_red between 0 and 255
 and p_blue between 0 and 255
 and p_green between 0 and 255
 )
 then
 set_color( to_char( p_red, 'fm0x' )
 || to_char( p_green, 'fm0x' )
 || to_char( p_blue, 'fm0x' )
 , false
 );
 end if;
 end;
 --
 procedure set_bk_color( p_rgb varchar2 := 'ffffff' )
 is
 begin
 set_color( p_rgb, true );
 end;
 --
 procedure set_bk_color
 ( p_red number := 0
 , p_green number := 0
 , p_blue number := 0
 )
 is
 begin
 if ( p_red between 0 and 255
 and p_blue between 0 and 255
 and p_green between 0 and 255
 )
 then
 set_color( to_char( p_red, 'fm0x' )
 || to_char( p_green, 'fm0x' )
 || to_char( p_blue, 'fm0x' )
 , true
 );
 end if;
 end;
 --
 procedure horizontal_line
 ( p_x number
 , p_y number
 , p_width number
 , p_line_width number := 0.5
 , p_line_color varchar2 := '000000'
 )
 is
 t_use_color boolean;
 begin
 txt2page( 'q ' || to_char_round( p_line_width, 5 ) || ' w' );
 t_use_color := substr( p_line_color
 , -6
 ) != '000000';
 if t_use_color
 then
 set_color( p_line_color );
 set_bk_color( p_line_color );
 else
 txt2page( '0 g' );
 end if;
 txt2page( to_char_round( p_x, 5 ) || ' '
 || to_char_round( p_y, 5 ) || ' m '
 || to_char_round( p_x + p_width, 5 ) || ' '
 || to_char_round( p_y, 5 ) || ' l b'
 );
 txt2page( 'Q' );
 end;
 --
 procedure vertical_line
 ( p_x number
 , p_y number
 , p_height number
 , p_line_width number := 0.5
 , p_line_color varchar2 := '000000'
 )
 is
 t_use_color boolean;
 begin
 txt2page( 'q ' || to_char_round( p_line_width, 5 ) || ' w' );
 t_use_color := substr( p_line_color
 , -6
 ) != '000000';
 if t_use_color
 then
 set_color( p_line_color );
 set_bk_color( p_line_color );
 else
 txt2page( '0 g' );
 end if;
 txt2page( to_char_round( p_x, 5 ) || ' '
 || to_char_round( p_y, 5 ) || ' m '
 || to_char_round( p_x, 5 ) || ' '
 || to_char_round( p_y + p_height, 5 ) || ' l b'
 );
 txt2page( 'Q' );
 end;
 --
 procedure rect
 ( p_x number
 , p_y number
 , p_width number
 , p_height number
 , p_line_color varchar2 := null
 , p_fill_color varchar2 := null
 , p_line_width number := 0.5
 )
 is
 begin
 txt2page( 'q' );
 if substr( p_line_color, -6 ) != substr( p_fill_color, -6 )
 then
 txt2page( to_char_round( p_line_width, 5 ) || ' w' );
 end if;
 if substr( p_line_color, -6 ) != '000000'
 then
 set_bk_color( p_line_color );
 else
 txt2page( '0 g' );
 end if;
 if p_fill_color is not null
 then
 set_color( p_fill_color );
 end if;
 txt2page( to_char_round( p_x, 5 ) || ' ' || to_char_round( p_y, 5 ) || ' '
 || to_char_round( p_width, 5 ) || ' ' || to_char_round( p_height, 5 ) || ' re '
 || case
 when p_fill_color is null
 then 'S'
 else case when p_line_color is null then 'f' else 'b' end
 end
 );
 txt2page( 'Q' );
 end;
 --
 function get( p_what pls_integer )
 return number
 is
 begin
 return case p_what
 when c_get_page_width then g_settings.page_width
 when c_get_page_height then g_settings.page_height
 when c_get_margin_top then g_settings.margin_top
 when c_get_margin_right then g_settings.margin_right
 when c_get_margin_bottom then g_settings.margin_bottom
 when c_get_margin_left then g_settings.margin_left
 when c_get_x then g_x
 when c_get_y then g_y
 when c_get_fontsize then g_fonts( g_current_font ).fontsize
 when c_get_current_font then g_current_font
 end;
 end;
 --
 function parse_jpg( p_img_blob blob )
 return tp_img
 is
 buf raw(4);
 t_img tp_img;
 t_ind integer;
 begin
 if ( dbms_lob.substr( p_img_blob, 2, 1 ) != hextoraw( 'FFD8' ) -- SOI Start of Image
 or dbms_lob.substr( p_img_blob, 2, dbms_lob.getlength( p_img_blob ) - 1 ) != hextoraw( 'FFD9' ) -- EOI End of Image
 )
 then -- this is not a jpg I can handle
 return null;
 end if;
 --
 t_img.pixels := p_img_blob;
 t_img.type := 'jpg';
 if dbms_lob.substr( t_img.pixels, 2, 3 ) in ( hextoraw( 'FFE0' ) -- a APP0 jpg
 , hextoraw( 'FFE1' ) -- a APP1 jpg
 )
 then
 t_img.color_res := 8;
 t_img.height := 1;
 t_img.width := 1;
 --
 t_ind := 3;
 t_ind := t_ind + 2 + blob2num( t_img.pixels, 2, t_ind + 2 );
 loop
 buf := dbms_lob.substr( t_img.pixels, 2, t_ind );
 exit when buf = hextoraw( 'FFDA' ); -- SOS Start of Scan
 exit when buf = hextoraw( 'FFD9' ); -- EOI End Of Image
 exit when substr( rawtohex( buf ), 1, 2 ) != 'FF';
 if rawtohex( buf ) in ( 'FFD0' -- RSTn
 , 'FFD1', 'FFD2', 'FFD3', 'FFD4', 'FFD5', 'FFD6', 'FFD7', 'FF01' -- TEM
 )
 then
 t_ind := t_ind + 2;
 else
 if buf = hextoraw( 'FFC0' ) -- SOF0 (Start Of Frame 0) marker
 then
 t_img.color_res := blob2num( t_img.pixels, 1, t_ind + 4 );
 t_img.height := blob2num( t_img.pixels, 2, t_ind + 5 );
 t_img.width := blob2num( t_img.pixels, 2, t_ind + 7 );
 end if;
 t_ind := t_ind + 2 + blob2num( t_img.pixels, 2, t_ind + 2 );
 end if;
 end loop;
 end if;
 --
 return t_img;
 end;
 --
 function parse_png( p_img_blob blob )
 return tp_img
 is
 t_img tp_img;
 buf raw(32767);
 len integer;
 ind integer;
 color_type pls_integer;
 begin
 if rawtohex( dbms_lob.substr( p_img_blob, 8, 1 ) ) != '89504E470D0A1A0A' -- not the right signature
 then
 return null;
 end if;
 dbms_lob.createtemporary( t_img.pixels, true );
 ind := 9;
 loop
 len := blob2num( p_img_blob, 4, ind ); -- length
 exit when len is null or ind &gt; dbms_lob.getlength( p_img_blob );
 case utl_raw.cast_to_varchar2( dbms_lob.substr( p_img_blob, 4, ind + 4 ) ) -- Chunk type
 when 'IHDR'
 then
 t_img.width := blob2num( p_img_blob, 4, ind + 8 );
 t_img.height := blob2num( p_img_blob, 4, ind + 12 );
 t_img.color_res := blob2num( p_img_blob, 1, ind + 16 );
 color_type := blob2num( p_img_blob, 1, ind + 17 );
 t_img.greyscale := color_type in ( 0, 4 );
 when 'PLTE'
 then
 t_img.color_tab := dbms_lob.substr( p_img_blob, len, ind + 8 );
 when 'IDAT'
 then
 dbms_lob.copy( t_img.pixels, p_img_blob, len, dbms_lob.getlength( t_img.pixels ) + 1, ind + 8 );
 when 'IEND'
 then
 exit;
 else
 null;
 end case;
 ind := ind + 4 + 4 + len + 4; -- Length + Chunk type + Chunk data + CRC
 end loop;
 --
 t_img.type := 'png';
 t_img.nr_colors := case color_type
 when 0 then 1
 when 2 then 3
 when 3 then 1
 when 4 then 2
 else 4
 end;
 --
 return t_img;
 end;
 --
 function lzw_decompress
 ( p_blob blob
 , p_bits pls_integer
 )
 return blob
 is
 powers tp_pls_tab;
 --
 g_lzw_ind pls_integer;
 g_lzw_bits pls_integer;
 g_lzw_buffer pls_integer;
 g_lzw_bits_used pls_integer;
 --
 type tp_lzw_dict is table of raw(1000) index by pls_integer;
 t_lzw_dict tp_lzw_dict;
 t_clr_code pls_integer;
 t_nxt_code pls_integer;
 t_new_code pls_integer;
 t_old_code pls_integer;
 t_blob blob;
 --
 function get_lzw_code
 return pls_integer
 is
 t_rv pls_integer;
 begin
 while g_lzw_bits_used &lt; g_lzw_bits
 loop
 g_lzw_ind := g_lzw_ind + 1;
 g_lzw_buffer := blob2num( p_blob, 1, g_lzw_ind ) * powers( g_lzw_bits_used ) + g_lzw_buffer;
 g_lzw_bits_used := g_lzw_bits_used + 8;
 end loop;
 t_rv := bitand( g_lzw_buffer, powers( g_lzw_bits ) - 1 );
 g_lzw_bits_used := g_lzw_bits_used - g_lzw_bits;
 g_lzw_buffer := trunc( g_lzw_buffer / powers( g_lzw_bits ) );
 return t_rv;
 end;
 --
 begin
 for i in 0 .. 30
 loop
 powers( i ) := power( 2, i );
 end loop;
 --
 t_clr_code := powers( p_bits - 1 );
 t_nxt_code := t_clr_code + 2;
 for i in 0 .. least( t_clr_code - 1, 255 )
 loop
 t_lzw_dict( i ) := hextoraw( to_char( i, 'fm0X' ) );
 end loop;
 dbms_lob.createtemporary( t_blob, true );
 g_lzw_ind := 0;
 g_lzw_bits := p_bits;
 g_lzw_buffer := 0;
 g_lzw_bits_used := 0;
 --
 t_old_code := null;
 t_new_code := get_lzw_code( );
 loop
 case nvl( t_new_code, t_clr_code + 1 )
 when t_clr_code + 1
 then
 exit;
 when t_clr_code
 then
 t_new_code := null;
 g_lzw_bits := p_bits;
 t_nxt_code := t_clr_code + 2;
 else
 if t_new_code = t_nxt_code
 then
 t_lzw_dict( t_nxt_code ) :=
 utl_raw.concat( t_lzw_dict( t_old_code )
 , utl_raw.substr( t_lzw_dict( t_old_code ), 1, 1 )
 );
 dbms_lob.append( t_blob, t_lzw_dict( t_nxt_code ) );
 t_nxt_code := t_nxt_code + 1;
 elsif t_new_code &gt; t_nxt_code
 then
 exit;
 else
 dbms_lob.append( t_blob, t_lzw_dict( t_new_code ) );
 if t_old_code is not null
 then
 t_lzw_dict( t_nxt_code ) := utl_raw.concat( t_lzw_dict( t_old_code )
 , utl_raw.substr( t_lzw_dict( t_new_code ), 1, 1 )
 );
 t_nxt_code := t_nxt_code + 1;
 end if;
 end if;
 if bitand( t_nxt_code, powers( g_lzw_bits ) - 1 ) = 0
 and g_lzw_bits &lt; 12
 then
 g_lzw_bits := g_lzw_bits + 1;
 end if;
 end case;
 t_old_code := t_new_code;
 t_new_code := get_lzw_code( );
 end loop;
 t_lzw_dict.delete;
 --
 return t_blob;
 end;
 --
 function parse_gif( p_img_blob blob )
 return tp_img
 is
 img tp_img;
 buf raw(4000);
 ind integer;
 t_len pls_integer;
 begin
 if dbms_lob.substr( p_img_blob, 3, 1 ) != utl_raw.cast_to_raw( 'GIF' )
 then
 return null;
 end if;
 ind := 7;
 buf := dbms_lob.substr( p_img_blob, 7, 7 ); -- Logical Screen Descriptor
 ind := ind + 7;
 img.color_res := raw2num( utl_raw.bit_and( utl_raw.substr( buf, 5, 1 ), hextoraw( '70' ) ) ) / 16 + 1;
 img.color_res := 8;
 if raw2num( buf, 5, 1 ) &gt; 127
 then
 t_len := 3 * power( 2, raw2num( utl_raw.bit_and( utl_raw.substr( buf, 5, 1 ), hextoraw( '07' ) ) ) + 1 );
 img.color_tab := dbms_lob.substr( p_img_blob, t_len, ind ); -- Global Color Table
 ind := ind + t_len;
 end if;
 --
 loop
 case dbms_lob.substr( p_img_blob, 1, ind )
 when hextoraw( '3B' ) -- trailer
 then
 exit;
 when hextoraw( '21' ) -- extension
 then
 if dbms_lob.substr( p_img_blob, 1, ind + 1 ) = hextoraw( 'F9' )
 then -- Graphic Control Extension
 if utl_raw.bit_and( dbms_lob.substr( p_img_blob, 1, ind + 3 ), hextoraw( '01' ) ) = hextoraw( '01' )
 then -- Transparent Color Flag set
 img.transparancy_index := blob2num( p_img_blob, 1, ind + 6 );
 end if;
 end if;
 ind := ind + 2; -- skip sentinel + label
 loop
 t_len := blob2num( p_img_blob, 1, ind ); -- Block Size
 exit when t_len = 0;
 ind := ind + 1 + t_len; -- skip Block Size + Data Sub-block
 end loop;
 ind := ind + 1; -- skip last Block Size
 when hextoraw( '2C' ) -- image
 then
 declare
 img_blob blob;
 min_code_size pls_integer;
 code_size pls_integer;
 flags raw(1);
 begin
 img.width := utl_raw.cast_to_binary_integer( dbms_lob.substr( p_img_blob, 2, ind + 5 )
 , utl_raw.little_endian
 );
 img.height := utl_raw.cast_to_binary_integer( dbms_lob.substr( p_img_blob, 2, ind + 7 )
 , utl_raw.little_endian
 );
 img.greyscale := false;
 ind := ind + 1 + 8; -- skip sentinel + img sizes
 flags := dbms_lob.substr( p_img_blob, 1, ind );
 if utl_raw.bit_and( flags, hextoraw( '80' ) ) = hextoraw( '80' )
 then
 t_len := 3 * power( 2, raw2num( utl_raw.bit_and( flags, hextoraw( '07' ) ) ) + 1 );
 img.color_tab := dbms_lob.substr( p_img_blob, t_len, ind + 1 ); -- Local Color Table
 end if;
 ind := ind + 1; -- skip image Flags
 min_code_size := blob2num( p_img_blob, 1, ind );
 ind := ind + 1; -- skip LZW Minimum Code Size
 dbms_lob.createtemporary( img_blob, true );
 loop
 t_len := blob2num( p_img_blob, 1, ind ); -- Block Size
 exit when t_len = 0;
 dbms_lob.append( img_blob, dbms_lob.substr( p_img_blob, t_len, ind + 1 ) ); -- Data Sub-block
 ind := ind + 1 + t_len; -- skip Block Size + Data Sub-block
 end loop;
 ind := ind + 1; -- skip last Block Size
 img.pixels := lzw_decompress( img_blob, min_code_size + 1 );
 --
 if utl_raw.bit_and( flags, hextoraw( '40' ) ) = hextoraw( '40' )
 then -- interlaced
 declare
 pass pls_integer;
 pass_ind tp_pls_tab;
 begin
 dbms_lob.createtemporary( img_blob, true );
 pass_ind( 1 ) := 1;
 pass_ind( 2 ) := trunc( ( img.height - 1 ) / 8 ) + 1;
 pass_ind( 3 ) := pass_ind( 2 ) + trunc( ( img.height + 3 ) / 8 );
 pass_ind( 4 ) := pass_ind( 3 ) + trunc( ( img.height + 1 ) / 4 );
 pass_ind( 2 ) := pass_ind( 2 ) * img.width + 1;
 pass_ind( 3 ) := pass_ind( 3 ) * img.width + 1;
 pass_ind( 4 ) := pass_ind( 4 ) * img.width + 1;
 for i in 0 .. img.height - 1
 loop
 pass := case mod( i, 8 )
 when 0 then 1
 when 4 then 2
 when 2 then 3
 when 6 then 3
 else 4
 end;
 dbms_lob.append( img_blob, dbms_lob.substr( img.pixels, img.width, pass_ind( pass ) ) );
 pass_ind( pass ) := pass_ind( pass ) + img.width;
 end loop;
 img.pixels := img_blob;
 end;
 end if;
 --
 dbms_lob.freetemporary( img_blob );
 end;
 else
 exit;
 end case;
 end loop;
 --
 img.type := 'gif';
 return img;
 end;
 --
 function parse_img
 ( p_blob in blob
 , p_adler32 in varchar2 := null
 , p_type in varchar2 := null
 )
 return tp_img
 is
 t_img tp_img;
 begin
 t_img.type := p_type;
 if t_img.type is null
 then
 if rawtohex( dbms_lob.substr( p_blob, 8, 1 ) ) = '89504E470D0A1A0A'
 then
 t_img.type := 'png';
 elsif dbms_lob.substr( p_blob , 3, 1 ) = utl_raw.cast_to_raw( 'GIF' )
 then
 t_img.type := 'gif';
 else
 t_img.type := 'jpg';
 end if;
 end if;
 --
 t_img := case lower( t_img.type )
 when 'gif' then parse_gif( p_blob )
 when 'png' then parse_png( p_blob )
 when 'jpg' then parse_jpg( p_blob )
 else null
 end;
 --
 if t_img.type is not null
 then
 t_img.adler32 := coalesce( p_adler32, adler32( p_blob ) );
 end if;
 return t_img;
 end;
 --
 procedure put_image
 ( p_img blob
 , p_x number
 , p_y number
 , p_width number := null
 , p_height number := null
 , p_align varchar2 := 'center'
 , p_valign varchar2 := 'top'
 )
 is
 t_x number;
 t_y number;
 t_img tp_img;
 t_ind pls_integer;
 t_adler32 varchar2(8);
 begin
 if p_img is null
 then
 return;
 end if;
 t_adler32 := adler32( p_img );
 t_ind := g_images.first;
 while t_ind is not null
 loop
 exit when g_images( t_ind ).adler32 = t_adler32;
 t_ind := g_images.next( t_ind );
 end loop;
 --
 if t_ind is null
 then
 t_img := parse_img( p_img, t_adler32 );
 if t_img.adler32 is null
 then
 return;
 end if;
 t_ind := g_images.count( ) + 1;
 g_images( t_ind ) := t_img;
 end if;
 --
 t_x := case substr( upper( p_align ), 1, 1 )
 when 'L' then p_x -- left
 when 'S' then p_x -- start
 when 'R' then p_x + nvl( p_width, 0 ) - g_images( t_ind ).width -- right
 when 'E' then p_x + nvl( p_width, 0 ) - g_images( t_ind ).width -- end
 else ( p_x + nvl( p_width, 0 ) - g_images( t_ind ).width ) / 2 -- center
 end;
 t_y := case substr( upper( p_valign ), 1, 1 )
 when 'C' then ( p_y - nvl( p_height, 0 ) + g_images( t_ind ).height ) / 2 -- center
 when 'B' then p_y - nvl( p_height, 0 ) + g_images( t_ind ).height -- bottom
 else p_y -- top
 end;
 --
 txt2page( 'q ' || to_char_round( least( nvl( p_width, g_images( t_ind ).width ), g_images( t_ind ).width ) )
 || ' 0 0 ' || to_char_round( least( nvl( p_height, g_images( t_ind ).height ), g_images( t_ind ).height ) )
 || ' ' || to_char_round( t_x ) || ' ' || to_char_round( t_y )
 || ' cm /I' || to_char( t_ind ) || ' Do Q'
 );
 end;
 --
 procedure put_image
 ( p_dir varchar2
 , p_file_name varchar2
 , p_x number
 , p_y number
 , p_width number := null
 , p_height number := null
 , p_align varchar2 := 'center'
 , p_valign varchar2 := 'top'
 )
 is
 t_blob blob;
 begin
 t_blob := file2blob( p_dir
 , p_file_name
 );
 put_image( t_blob
 , p_x
 , p_y
 , p_width
 , p_height
 , p_align
 , p_valign
 );
 dbms_lob.freetemporary( t_blob );
 end;
 --
 procedure put_image
 ( p_url varchar2
 , p_x number
 , p_y number
 , p_width number := null
 , p_height number := null
 , p_align varchar2 := 'center'
 , p_valign varchar2 := 'top'
 )
 is
 t_blob blob;
 begin
 t_blob := httpuritype( p_url ).getblob( );
 put_image( t_blob
 , p_x
 , p_y
 , p_width
 , p_height
 , p_align
 , p_valign
 );
 dbms_lob.freetemporary( t_blob );
 end;
 --
 procedure set_page_proc( p_src clob )
 is
 begin
 g_page_prcs( g_page_prcs.count ) := p_src;
 end;
 --
 procedure cursor2table
 ( p_c integer
 , p_widths tp_col_widths := null
 , p_headers tp_headers := null
 )
 is
 t_col_cnt integer;
 $IF DBMS_DB_VERSION.VER_LE_10 $THEN
 t_desc_tab dbms_sql.desc_tab2;
 $ELSE
 t_desc_tab dbms_sql.desc_tab3;
 $END
 d_tab dbms_sql.date_table;
 n_tab dbms_sql.number_table;
 v_tab dbms_sql.varchar2_table;
 t_bulk_size pls_integer := 200;
 t_r integer;
 t_cur_row pls_integer;
 type tp_integer_tab is table of integer;
 t_chars tp_integer_tab := tp_integer_tab( 1, 8, 9, 96, 112 );
 t_dates tp_integer_tab := tp_integer_tab( 12, 178, 179, 180, 181 , 231 );
 t_numerics tp_integer_tab := tp_integer_tab( 2, 100, 101 );
 t_widths tp_col_widths;
 t_tmp number;
 t_x number;
 t_y number;
 t_start_x number;
 t_lineheight number;
 t_padding number := 2;
 t_num_format varchar2(100) := 'tm9';
 t_date_format varchar2(100) := 'dd.mm.yyyy';
 t_txt varchar2(32767);
 c_rf number := 0.2; -- raise factor of text above cell bottom
 --
 procedure show_header
 is
 begin
 if p_headers is not null and p_headers.count &gt; 0
 then
 t_x := t_start_x;
 for c in 1 .. t_col_cnt
 loop
 rect( t_x, t_y, t_widths( c ), t_lineheight );
 if c &lt;= p_headers.count
 then
 put_txt( t_x + t_padding, t_y + c_rf * t_lineheight, p_headers( c ) );
 end if;
 t_x := t_x + t_widths( c );
 end loop;
 t_y := t_y - t_lineheight;
 end if;
 end;
 --
 begin
 $IF DBMS_DB_VERSION.VER_LE_10 $THEN
 dbms_sql.describe_columns2( p_c, t_col_cnt, t_desc_tab );
 $ELSE
 dbms_sql.describe_columns3( p_c, t_col_cnt, t_desc_tab );
 $END
 if p_widths is null or p_widths.count &lt; t_col_cnt
 then
 t_tmp := get( c_get_page_width ) - get( c_get_margin_left ) - get( c_get_margin_right );
 t_widths := tp_col_widths();
 t_widths.extend( t_col_cnt );
 for c in 1 .. t_col_cnt
 loop
 t_widths( c ) := round( t_tmp / t_col_cnt, 1 );
 end loop;
 else
 t_widths := p_widths;
 end if;
 --
 if get( c_get_current_font ) is null
 then
 set_font( 'helvetica', 12 );
 end if;
 --
 for c in 1 .. t_col_cnt
 loop
 case
 when t_desc_tab( c ).col_type member of t_numerics
 then
 dbms_sql.define_array( p_c, c, n_tab, t_bulk_size, 1 );
 when t_desc_tab( c ).col_type member of t_dates
 then
 dbms_sql.define_array( p_c, c, d_tab, t_bulk_size, 1 );
 when t_desc_tab( c ).col_type member of t_chars
 then
 dbms_sql.define_array( p_c, c, v_tab, t_bulk_size, 1 );
 else
 null;
 end case;
 end loop;
 --
 t_start_x := get( c_get_margin_left );
 t_lineheight := get( c_get_fontsize ) * 1.2;
 t_y := coalesce( get( c_get_y ) - t_lineheight, get( c_get_page_height ) - get( c_get_margin_top ) ) - t_lineheight;
 --
 show_header;
 --
 loop
 t_r := dbms_sql.fetch_rows( p_c );
 for i in 0 .. t_r - 1
 loop
 if t_y &lt; get( c_get_margin_bottom )
 then
 new_page;
 t_y := get( c_get_page_height ) - get( c_get_margin_top ) - t_lineheight;
 show_header;
 end if;
 t_x := t_start_x;
 for c in 1 .. t_col_cnt
 loop
 case
 when t_desc_tab( c ).col_type member of t_numerics
 then
 n_tab.delete;
 dbms_sql.column_value( p_c, c, n_tab );
 rect( t_x, t_y, t_widths( c ), t_lineheight );
 t_txt := to_char( n_tab( i + n_tab.first() ), t_num_format );
 if t_txt is not null
 then
 put_txt( t_x + t_widths( c ) - t_padding - str_len( t_txt ), t_y + c_rf * t_lineheight, t_txt );
 end if;
 t_x := t_x + t_widths( c );
 when t_desc_tab( c ).col_type member of t_dates
 then
 d_tab.delete;
 dbms_sql.column_value( p_c, c, d_tab );
 rect( t_x, t_y, t_widths( c ), t_lineheight );
 t_txt := to_char( d_tab( i + d_tab.first() ), t_date_format );
 if t_txt is not null
 then
 put_txt( t_x + t_padding, t_y + c_rf * t_lineheight, t_txt );
 end if;
 t_x := t_x + t_widths( c );
 when t_desc_tab( c ).col_type member of t_chars
 then
 v_tab.delete;
 dbms_sql.column_value( p_c, c, v_tab );
 rect( t_x, t_y, t_widths( c ), t_lineheight );
 t_txt := v_tab( i + v_tab.first() );
 if t_txt is not null
 then
 put_txt( t_x + t_padding, t_y + c_rf * t_lineheight, t_txt );
 end if;
 t_x := t_x + t_widths( c );
 else
 null;
 end case;
 end loop;
 t_y := t_y - t_lineheight;
 end loop;
 exit when t_r != t_bulk_size;
 end loop;
 g_y := t_y;
 end;
 --
 procedure query2table
 ( p_query varchar2
 , p_widths tp_col_widths := null
 , p_headers tp_headers := null
 )
 is
 t_cx integer;
 t_dummy integer;
 begin
 t_cx := dbms_sql.open_cursor;
 dbms_sql.parse( t_cx, p_query, dbms_sql.native );
 t_dummy := dbms_sql.execute( t_cx );
 cursor2table( t_cx, p_widths, p_headers );
 dbms_sql.close_cursor( t_cx );
 end;
 $IF not DBMS_DB_VERSION.VER_LE_10 $THEN
 --
 procedure refcursor2table
 ( p_rc sys_refcursor
 , p_widths tp_col_widths := null
 , p_headers tp_headers := null
 )
 is
 t_cx integer;
 t_rc sys_refcursor;
 begin
 t_rc := p_rc;
 t_cx := dbms_sql.to_cursor_number( t_rc );
 cursor2table( t_cx, p_widths, p_headers );
 dbms_sql.close_cursor( t_cx );
 end;
 $END
 --
 procedure pr_goto_page( i_npage number )
 is
 begin
 if i_npage &lt;= g_pages.count
 then
 g_page_nr := i_npage - 1;
 end if;
 end;
 --
 procedure pr_goto_current_page
 is
 begin
 g_page_nr := null;
 end;
 end;
 /

3. Install UTL_MAIL procedure and send email using PL_SQL procedure

To install UTL_MAIL:

sqlplus sys/<pwd>

 SQL&gt; @$ORACLE_HOME/rdbms/admin/utlmail.sql
 SQL&gt; @$ORACLE_HOME/rdbms/admin/prvtmail.plb

sqlplus tctreport/tctreport

grant execute on sys.utl_mail to tctreport;

— change ‘filenm’ to your filename change ‘MY_DIR’ to the directory you created xslx and PDF report —
— Send PDF Report (code is in send_mail_pdf.sql) —


DECLARE
fil BFILE;
file_len PLS_INTEGER;
MAX_LINE_WIDTH PLS_INTEGER := 54;
buf RAW(2100);
amt BINARY_INTEGER := 2000;
pos PLS_INTEGER := 1; /* pointer for each piece */
filepos PLS_INTEGER := 1; /* pointer for the file */
filenm VARCHAR2(50) := 'my.xlsx'; /* binary file attachment */
data RAW(2100);
chunks PLS_INTEGER;
len PLS_INTEGER;
modulo PLS_INTEGER;
pieces PLS_INTEGER;
err_num NUMBER;
err_msg VARCHAR2(100);
resultraw RAW(32000);

BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET smtp_out_server = ''172.21.103.8''';
/* Assign the file a handle */
fil := BFILENAME('MY_DIR', filenm);

/* Get the length of the file in bytes */
file_len := dbms_lob.getlength(fil);

/* Get the remainer when we divide by amt */
modulo := mod(file_len, amt);

/* How many pieces? */
pieces := trunc(file_len / amt);
if (modulo <> 0) then
pieces := pieces + 1;
end if;

/* Open the file */
dbms_lob.fileopen(fil, dbms_lob.file_readonly);

/* Read the first amt into the buffer */
dbms_lob.read(fil, amt, filepos, buf);

/* For each piece of the file . . . */
FOR i IN 1..pieces LOOP

/* Position file pointer for next read */
filepos := i * amt + 1;

/* Calculate remaining file length */
file_len := file_len - amt;

/* Stick the buffer contents into data */
data := utl_raw.concat(data, buf);

/* Calculate the number of chunks in this piece */
chunks := trunc(utl_raw.length(data) / MAX_LINE_WIDTH);

/* Don't want too many chunks */
IF (i <> pieces) THEN
chunks := chunks - 1;
END IF;

/* For each chunk in this piece . . . */
FOR j IN 0..chunks LOOP

/* Position ourselves in this piece */
pos := j * MAX_LINE_WIDTH + 1;

/* Is this the last chunk in this piece? */
IF (j <> chunks) THEN
len := MAX_LINE_WIDTH;
ELSE
len := utl_raw.length(data) - pos + 1;
IF (len > MAX_LINE_width) THEN
len := MAX_LINE_WIDTH;
END IF;
END IF;
/* If we got something, let's write it */
IF (len > 0 ) THEN

resultraw := resultraw || utl_raw.substr(data, pos, len);
END IF;
END LOOP;

/* Point at the rest of the data buffer */
IF (pos + len <= utl_raw.length(data)) THEN
data := utl_raw.substr(data, pos + len);
ELSE
data := NULL;
END IF;

/* We're running out of file, only get the rest of it */
if (file_len < amt and file_len > 0) then
amt := file_len;
end if;

/* Read the next amount into the buffer */
dbms_lob.read(fil, amt, filepos, buf);

END LOOP;

/* Don't forget to close the file */
dbms_lob.fileclose(fil);

UTL_MAIL.SEND_ATTACH_RAW(sender => 'sendfrom@email.com', recipients => 'sendto@email.com', subject => ' Daily  Report', message => 'Hello World', attachment => resultraw, att_filename => 'my.xlsx');

END;
/

— Send PDF Report (code is in send_mail_pdf.sql )–


DECLARE
fil BFILE;
file_len PLS_INTEGER;
MAX_LINE_WIDTH PLS_INTEGER := 54;
buf RAW(2100);
amt BINARY_INTEGER := 2000;
pos PLS_INTEGER := 1; /* pointer for each piece */
filepos PLS_INTEGER := 1; /* pointer for the file */
filenm VARCHAR2(50) := 'my.pdf'; /* binary file attachment */
data RAW(2100);
chunks PLS_INTEGER;
len PLS_INTEGER;
modulo PLS_INTEGER;
pieces PLS_INTEGER;
err_num NUMBER;
err_msg VARCHAR2(100);
resultraw RAW(32000);

BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET smtp_out_server = ''172.21.103.8''';
/* Assign the file a handle */
fil := BFILENAME('MY_DIR', filenm);

/* Get the length of the file in bytes */
file_len := dbms_lob.getlength(fil);

/* Get the remainer when we divide by amt */
modulo := mod(file_len, amt);

/* How many pieces? */
pieces := trunc(file_len / amt);
if (modulo <> 0) then
pieces := pieces + 1;
end if;

/* Open the file */
dbms_lob.fileopen(fil, dbms_lob.file_readonly);

/* Read the first amt into the buffer */
dbms_lob.read(fil, amt, filepos, buf);

/* For each piece of the file . . . */
FOR i IN 1..pieces LOOP

/* Position file pointer for next read */
filepos := i * amt + 1;

/* Calculate remaining file length */
file_len := file_len - amt;

/* Stick the buffer contents into data */
data := utl_raw.concat(data, buf);

/* Calculate the number of chunks in this piece */
chunks := trunc(utl_raw.length(data) / MAX_LINE_WIDTH);

/* Don't want too many chunks */
IF (i <> pieces) THEN
chunks := chunks - 1;
END IF;

/* For each chunk in this piece . . . */
FOR j IN 0..chunks LOOP

/* Position ourselves in this piece */
pos := j * MAX_LINE_WIDTH + 1;

/* Is this the last chunk in this piece? */
IF (j <> chunks) THEN
len := MAX_LINE_WIDTH;
ELSE
len := utl_raw.length(data) - pos + 1;
IF (len > MAX_LINE_width) THEN
len := MAX_LINE_WIDTH;
END IF;
END IF;
/* If we got something, let's write it */
IF (len > 0 ) THEN

resultraw := resultraw || utl_raw.substr(data, pos, len);
END IF;
END LOOP;

/* Point at the rest of the data buffer */
IF (pos + len <= utl_raw.length(data)) THEN
data := utl_raw.substr(data, pos + len);
ELSE
data := NULL;
END IF;

/* We're running out of file, only get the rest of it */
if (file_len < amt and file_len > 0) then
amt := file_len;
end if;

/* Read the next amount into the buffer */
dbms_lob.read(fil, amt, filepos, buf);

END LOOP;

/* Don't forget to close the file */
dbms_lob.fileclose(fil);

UTL_MAIL.SEND_ATTACH_RAW(sender => 'sendfrom@email.com', recipients => 'sendto@email.com', subject => ' Daily  Report', message => 'Hello World', attachment => resultraw, att_filename => 'my.pdf');

END;
/

4. Brief Execution of Steps

$ sqlplus tctreport/tctreport

SQL> @generate_excel.sql

PL/SQL procedure successfully completed.

SQL> @generate_pdf.sql

PL/SQL procedure successfully completed.

SQL> @send_mail_excel.sql

PL/SQL procedure successfully completed.

SQL> @send_mail_pdf.sql

PL/SQL procedure successfully completed.

SQL>


Monitor Tablespace Script Oracle 12c 11g 10g

$
0
0

The Below Script will generate output if percentage of tablespace space left free is less tan 20%. You can easily put in a shell script and schedule it on crontab to send alerts on a daily basis.

 

select df.tablespace_name tspace,
round(sum(fs.bytes)/(df.bytes) * 100) “%_free”,
round(sum(fs.bytes)/(1024*1024)) free_ts_size,
df.bytes/(1024*1024) tot_ts_size
from dba_free_space fs, (select tablespace_name, sum(bytes) bytes from dba_data_files
group by tablespace_name ) df
where fs.tablespace_name = df.tablespace_name
group by df.tablespace_name, df.bytes
having round(sum(fs.bytes)/(df.bytes) * 100) < 20;


SYSAUX tablespace growing rapidly in Oracle, 12c Unified Audit Trail AUDSYS

$
0
0

The Unified Auditing in Oracle 12c can Grow the SYSAUX table to a large size. In our case it grew to about 100GB and We had to take action to make sure it did not grow any larger. First we query to identify what is residing inside the SYSAUX tablespace. In our case it was the AUDSYS schema which was taking the most space. All arrows pointed to Unified Auditing’s way

— Query to Check the SYSAUX Tablespace Occupants —

COLUMN “Item” FORMAT A25
COLUMN “Space Used (GB)” FORMAT 999.99
COLUMN “Schema” FORMAT A25
COLUMN “Move Procedure” FORMAT A40

SELECT occupant_name “Item”,
space_usage_kbytes/1048576 “Space Used (GB)”,
schema_name “Schema”,
move_procedure “Move Procedure”
FROM v$sysaux_occupants
ORDER BY 2
/
— Query to Check the SYSAUX Tablespace Largest Segments–

col owner for a6
col segment_name for a50
select * from
(select owner,segment_name||’~’||partition_name segment_name,bytes/(1024*1024) size_m
from dba_segments
where tablespace_name = ‘SYSAUX’ ORDER BY BLOCKS desc) where rownum < 11;
— Clean Up the Unified Audit Trail —

1. First set the Date, starting from where you need to have the Unified Auditing Records

BEGIN
DBMS_AUDIT_MGMT.SET_LAST_ARCHIVE_TIMESTAMP(
audit_trail_type => DBMS_AUDIT_MGMT.AUDIT_TRAIL_UNIFIED,
last_archive_time => TO_TIMESTAMP(’15-SEP-2016 00:00:10.0′,’DD-MON-RRRR HH24:MI:SS.FF’),
rac_instance_number => 2);
END;
/
2. Since Unified Auditing caches the audit trail in memory to implement a ‘lazy write’ feature that helps performance, some of the records eligible for deletion may still linger in the cache, to also first flush this cache

BEGIN
DBMS_AUDIT_MGMT.FLUSH_UNIFIED_AUDIT_TRAIL;
END;
/

3. Execute the Purge Procedure

BEGIN
DBMS_AUDIT_MGMT.CLEAN_AUDIT_TRAIL(
audit_trail_type => DBMS_AUDIT_MGMT.AUDIT_TRAIL_UNIFIED,
use_last_arch_timestamp => TRUE);
END;
/

Useful Links:

https://docs.oracle.com/database/121/DBSEG/audit_admin.htm#DBSEG474

How To Purge The UNIFIED AUDIT TRAIL (Doc ID 1582627.1)

 


Oracle 12c Transparent Data Encryption (TDE) on RAC Database with Physical Standby Database : Multiple Databases in Single Oracle Home

$
0
0

Oracle 12c TDE on RAC Database with Physical Standby RAC. The Oracle Database Home has Multiple Instances, due to which we have to use parameter $ORACLE_UNQNAME for ENCRYPTION_WALLET_LOCATION in sqlnet.ora file

In Oracle 12c ALTER SYSTEM and orapki has been deprecated and the correct method is to use ADMINISTER KEY MANAGEMENT commands. Many of the statements from the ALTER SYSTEM SQL statement correspond to the ADMINISTER KEY MANAGEMENT statement.

1. Create folder for wallets, since this is RAC cluster database ,it is better to have a shared filesystem. In our case it is ACFS
mkdir -p /ACFS/wallets/primeprd
mkdir -p /ACFS/wallets/onlnprd
chown -R oracle:oinstall /ACFS/wallets/primeprd
chown -R oracle:oinstall /ACFS/wallets/onlnprd
2. Create sqlnet.ora file with encryption wallet location, do on both nodes with oracle user
ENCRYPTION_WALLET_LOCATION =
(SOURCE = (METHOD = FILE) (METHOD_DATA =
(DIRECTORY=/ACFS/wallets/$ORACLE_UNQNAME/)))
3. Set DB Unique Name for the databases and verfiy the encryption wallet location, do on both nodes with oracle user

Also it is better to set the ORACLE_UNQNAME in the user profile

export ORACLE_UNQNAME=`$ORACLE_HOME/bin/srvctl config database |grep -w ${ORACLE_SID%?}`
echo $ORACLE_UNQNAME
srvctl setenv database -d primeprd -T “ORACLE_UNQNAME=primeprd”
select * from v$encryption_wallet;

export ORACLE_UNQNAME=`$ORACLE_HOME/bin/srvctl config database |grep -w ${ORACLE_SID%?}`
echo $ORACLE_UNQNAME
srvctl setenv database -d onlnprd -T “ORACLE_UNQNAME=onlnprd”
select * from v$encryption_wallet;

4. At Dr side, with Oracle user in DB home directory
mkdir -p /ACFS/wallets/primedr
mkdir -p /ACFS/wallets/onlndr
chown -R oracle:oinstall /ACFS/wallets/onlndr
chown -R oracle:oinstall /ACFS/wallets/primedr

Create sqlnet.ora file in DR side with encryption wallet location, do on both nodes with oracle user

ENCRYPTION_WALLET_LOCATION =
(SOURCE = (METHOD = FILE) (METHOD_DATA =
(DIRECTORY=/ACFS/wallets/$ORACLE_UNQNAME/)))

export ORACLE_UNQNAME=`$ORACLE_HOME/bin/srvctl config database |grep -w ${ORACLE_SID%?}`
echo $ORACLE_UNQNAME
srvctl setenv database -d onlndr -T “ORACLE_UNQNAME=onlndr”

export ORACLE_UNQNAME=`$ORACLE_HOME/bin/srvctl config database |grep -w ${ORACLE_SID%?}`
echo $ORACLE_UNQNAME
srvctl setenv database -d primedr -T “ORACLE_UNQNAME=primedr”

SQL> select * from v$encryption_wallet;

WRL_TYPE
——————–
WRL_PARAMETER
——————————————————————————–
STATUS WALLET_TYPE WALLET_OR FULLY_BAC
—————————— ——————– ——— ———
CON_ID
———-
FILE
/ACFS/wallet/primeprd/
NOT_AVAILABLE UNKNOWN SINGLE UNDEFINED
0
SQL> administer key management create keystore ‘/ACFS/wallet/primeprd/’ identified by “Abc1234$#”;

keystore altered.

SQL> select * from v$encryption_wallet;

WRL_TYPE
——————–
WRL_PARAMETER
——————————————————————————–
STATUS WALLET_TYPE WALLET_OR FULLY_BAC
—————————— ——————– ——— ———
CON_ID
———-
FILE
/ACFS/wallet/primeprd/
CLOSED UNKNOWN SINGLE UNDEFINED
0
SQL> administer key management set keystore open identified by Abc1234$#;

keystore altered.

SQL> select * from v$encryption_wallet;

WRL_TYPE
——————–
WRL_PARAMETER
——————————————————————————–
STATUS WALLET_TYPE WALLET_OR FULLY_BAC
—————————— ——————– ——— ———
CON_ID
———-
FILE
/ACFS/wallet/primeprd/
OPEN_NO_MASTER_KEY PASSWORD SINGLE UNDEFINED
0
SQL> administer key management create key identified by Abc1234$# with backup;

keystore altered.

SQL> select * from v$encryption_wallet;

WRL_TYPE
——————–
WRL_PARAMETER
——————————————————————————–
STATUS WALLET_TYPE WALLET_OR FULLY_BAC
—————————— ——————– ——— ———
CON_ID
———-
FILE
/ACFS/wallet/primeprd/
OPEN_NO_MASTER_KEY PASSWORD SINGLE UNDEFINED
0
SQL> select key_id from v$encryption_keys;

KEY_ID
——————————————————————————
AcVlvDzDNE8iv/rD38jpCtYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

SQL> administer key management use key ‘AcVlvDzDNE8iv/rD38jpCtYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA’ identified by Abc1234$# with backup;

keystore altered.

SQL> select key_id,activation_time from v$encryption_keys;

KEY_ID
——————————————————————————
ACTIVATION_TIME
—————————————————————————
AcVlvDzDNE8iv/rD38jpCtYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
23-NOV-16 09.53.46.485930 AM +00:00
SQL> select * from v$encryption_wallet;

WRL_TYPE
——————–
WRL_PARAMETER
——————————————————————————–
STATUS WALLET_TYPE WALLET_OR FULLY_BAC
—————————— ——————– ——— ———
CON_ID
———-
FILE
/ACFS/wallet/primeprd/
OPEN PASSWORD SINGLE NO
0

primeprd1::12c–>srvctl stop database -d primeprd
primeprd1::12c–>srvctl start database -d primeprd
primeprd1::12c–>sqld

SQL> select * from v$encryption_wallet;

WRL_TYPE
——————–
WRL_PARAMETER
——————————————————————————–
STATUS WALLET_TYPE WALLET_OR FULLY_BAC
—————————— ——————– ——— ———
CON_ID
———-
FILE
/ACFS/wallet/primeprd/
CLOSED UNKNOWN SINGLE UNDEFINED
0
SQL> administer key management set keystore open identified by Abc1234$#;

keystore altered.

SQL> select * from v$encryption_wallet;

WRL_TYPE
——————–
WRL_PARAMETER
——————————————————————————–
STATUS WALLET_TYPE WALLET_OR FULLY_BAC
—————————— ——————– ——— ———
CON_ID
———-
FILE
/ACFS/wallet/primeprd/
OPEN PASSWORD SINGLE NO
0
— Create Auto Login for the Keystore —

SQL> ADMINISTER KEY MANAGEMENT CREATE AUTO_LOGIN KEYSTORE FROM KEYSTORE ‘/ACFS/wallet/primeprd/’ identified by Abc1234$#;

keystore altered.

primeprd1::12c–>srvctl stop database -d primeprd
primeprd1::12c–>srvctl start database -d primeprd
primeprd1::12c–>sqld

SQL*Plus: Release 12.1.0.2.0 Production on Wed Nov 23 13:13:30 2016

Copyright (c) 1982, 2014, Oracle. All rights reserved.
Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 – 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Advanced Analytics and Real Application Testing options

SQL> select * from v$encryption_wallet;

WRL_TYPE
——————–
WRL_PARAMETER
——————————————————————————–
STATUS WALLET_TYPE WALLET_OR FULLY_BAC
—————————— ——————– ——— ———
CON_ID
———-
FILE
/ACFS/wallet/primeprd/
OPEN AUTOLOGIN SINGLE NO
0
Copy the Encryption key Files to Standby Site as “Oracle” user

primeprd2::12c–>cd /ACFS/wallet/primeprd/
primeprd2::12c–>scp -r * oracle@exadrdbadm01:/ACFS/wallets/primedr

The authenticity of host ‘exadrdbadm01 (10.5.238.11)’ can’t be established.
RSA key fingerprint is c8:c0:69:80:1c:c8:ea:e3:6c:46:6b:c1:05:86:aa:c9.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added ‘exadrdbadm01,10.5.238.11’ (RSA) to the list of known hosts.
oracle@exadrdbadm01’s password:
cwallet.sso 100% 3893 3.8KB/s 00:00
ewallet_2016112309525312.p12 100% 2408 2.4KB/s 00:00
ewallet_2016112309534645.p12 100% 3656 3.6KB/s 00:00
ewallet.p12 100% 3848 3.8KB/s 00:00

— At DR Side on Both Nodes–
SQL> select * from v$encryption_wallet;
WRL_TYPE
——————–
WRL_PARAMETER
—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
STATUS WALLET_TYPE WALLET_OR FULLY_BAC CON_ID
—————————— ——————– ——— ——— ———-
FILE
/u03/app/oracle/admin/primedr/wallet
NOT_AVAILABLE UNKNOWN SINGLE UNDEFINED
/home/oracle::12c–>cd /u03/app/oracle/product/12.1.0.2/teststandby/network/admin/
/u03/app/oracle/product/12.1.0.2/teststandby/network/admin::12c–>vi sqlnet.ora
ENCRYPTION_WALLET_LOCATION = (SOURCE = (METHOD = FILE) (METHOD_DATA = (DIRECTORY = /ACFS/wallets/primedr/$ORACLE_UNQNAME/)))

/u03/app/oracle/product/12.1.0.2/teststandby/network/admin::12c–>sqld

SQL*Plus: Release 12.1.0.2.0 Production on Wed Nov 23 13:27:22 2016

Copyright (c) 1982, 2014, Oracle. All rights reserved.
Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 – 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Advanced Analytics and Real Application Testing options

SQL> select * from v$encryption_wallet;

WRL_TYPE
——————–
WRL_PARAMETER
——————————————————————————–
STATUS WALLET_TYPE WALLET_OR FULLY_BAC
—————————— ——————– ——— ———
CON_ID
———-
FILE
/ACFS/wallets/primedr/
OPEN AUTOLOGIN SINGLE NO
0
SQL> exit

 

5. Create Encrypted Tablespace
CREATE TABLESPACE encrypt_ts
DATAFILE ‘+DATAC1’ SIZE 250M
ENCRYPTION USING ‘AES256’
DEFAULT STORAGE (ENCRYPT);


EXPDP/IMPDP – ORA-04063 (view “SYS.KU$_RADM_FPTM_VIEW” Has Errors) on Full Export / Import

$
0
0

This is caused by a mismatch between the character set in which the table radm_fptm$ is created and in which the type ku$_radm_fptm$_t is created. This leads to the ORA-932 error when trying to recompile the view SYS.KU$_RADM_FPTM_VIEW, you have specified UTF8 for the national characaterset instead of the default AL16UTF16 national characaterset:

To Check Character set

col parameter for a35
col value for a40
select * from nls_database_parameters where parameter like ‘%SET’ order by 1;

 

Workaround is  to recompile the Recompile the View ku$_radm_fptm_t

alter type ku$_radm_fptm_t compile reuse settings;

Type altered.

SQL> alter view SYS.KU$_RADM_FPTM_VIEW compile;

View altered.

set lines 140
col status for a9
col object_type for a20;
col owner.object for a50
select con_id, status, object_id, object_type,
owner||’.’||object_name “OWNER.OBJECT”
3 from cdb_objects where object_name like ‘%KU$_RADM_FPTM_VIEW%’ order by 4,1;

 

CON_ID STATUS OBJECT_ID OBJECT_TYPE OWNER.OBJECT
———- ——— ———- ——————– ————————————————–
0 VALID 14188 VIEW SYS.KU$_RADM_FPTM_VIEW

 

 

 


Restore Table from RMAN Backup Oracle 12c

$
0
0

 

One of the amazing features of Oracle Database 12c is that now you can do a logical restore from a physical backup. Before 12c it was not possible to restore a table from a RMAN backup. This feature is amazing speciall when you have a large database and needs to restore a small table. One of the pre-requisites is you must have a Full Backup of the database and also archivelogs after the fully backup must be available. We can proceed to do the restore with below RMAN command

$ rman target /

RECOVER TABLE HR.EMP
UNTIL TIME ‘SYSDATE-0.22’
AUXILIARY DESTINATION ‘/backups/rmanbkp’
REMAP TABLE ‘HR’.’EMP’:’EMP_RECVR’

HR.EMP is the table to be recovered from the RMAN backup

SYSDATE-0.22 is the time till where the table will be rolled back; this is to specify the point in time for recovery: UNTIL TIME, UNTIL SCN, or UNTIL SEQUENCE can be used

Auxiliary Destination is destination where a instance is created for the recovery process

REMAP TABLE clause is used to specify to restore the table with another name

Using this method even individual table paritions can be restored.


Move Table to Another Tablespace Online Oracle Database 12c

$
0
0

The new PL/SQL procedure REDEF_TABLE in Oracle 12c  lets you redefine a table online in a single step. It can do the below changes

  • Move table,partition or index to another tablespace
  • Compression type change on a table including for table,partition and index

More information on DBMS_REDFINITION can be accessed on Oracle Support : Master Note:Overview of Online Redefinition of Tables (DBMS_REDEFINITION) (Doc ID 1357825.1)

We will now go ahead and move the table HR.EMP to another tablespace called TBS01 online.

EXEC
BEGIN
DBMS_REDEFINITION.REDEF_TABLE(
uname => ‘HR’,
tname => ‘EMP’,
table_part_tablespace => ‘TBS01’
);
END;
/

uname –> Schema Name in which Table resides
tname –> Table Name
table_part_tablespace –> New Tablespace to which table will be moved
index_tablespace => If you need to move the indexes to another table it can be done using an additional arguement



Rebuild All Indexes on a Schema in Oracle

$
0
0

spool index_rebuild.sql

select ‘alter index ‘||owner||’.’||index_name ||’ rebuild online nologging;’
from dba_indexes
where owner=upper(‘SAMPLE’);
spool off


ORA-00600: internal error code, arguments: [audins:Invalid_param_type_for_binding

$
0
0

Error in Alert Log:

Errors in file /u03/app/oracle/diag/rdbms/primeprd/primeprd1/trace/primeprd1_ora_303370.trc (incident=1070393):

ORA-00600: internal error code, arguments: [audins:Invalid_param_type_for_binding], [], [], [], [], [], [], [], [], [], [], []

 

Error in Broker :

DGMGRL> add database primedr as connect identifier is primedr maintained as physical;
Database “primedr” added

DGMGRL> enable configuration;
Enabled.
DGMGRL> show configuration

Configuration – dg_primeprd

Protection Mode: MaxPerformance
Members:
primeprd – Primary database
primedr – Physical standby database
Error: ORA-16664: unable to receive the result from a database

DGM-17016: failed to retrieve status for database “primedr”
ORA-16664: unable to receive the result from a database

Broker logfile error:

11/22/2017 16:42:26
ENABLE CONFIGURATION
11/22/2017 16:42:55
Site primedr returned ORA-16664.

 

Description:

We had this error when we were adding a Standby database to the Dataguard Broker. After enabling the configuration always it used to give error ORA-16664: unable to receive the result from a database. After literally months of digging around and opening multiple SR’s we discovered it was related to AUDIT logs which are written in AUD$. It was clear after searching the trace logfiles that an application context package has some issues with the Audit in Oracle 12c.

Error Showing Up in TraceFile

DDE: Problem Key ‘ORA 600 [audins:Invalid_param_type_for_binding]’ was flood controlled (0x2) (incident: 1070379)
ORA-00600: internal error code, arguments: [audins:Invalid_param_type_for_binding], [], [], [], [], [], [], [], [], [], [], []
*********START PLSQL RUNTIME DUMP************
***Got internal error Exception caught in pl/sql run-time while running PLSQL***
***Got ORA-2002 while running PLSQL***
PACKAGE BODY TCTDBS.SV_DB_CONTEXT:
library unit=677604e58 line=225 opcode=86 static link=7fb08c2b4888 scope=1
FP=0x7fb08c2b4d10 PC=0x9e7cdec82 Page=0 AP=0x7fb08c2b4888 ST=0x7fb08c2b5158
DL0=0x7fb0872edd50 GF=0x7fb0872edf60 DL1=0x7fb0872ede48 DPF=0x7fb0872edf48
HS=0x9e7cd8ae8 AR=0x7fb0872e9fc8 DS=0x9e7ce0148
PB_PC=(nil) SV_PC=(nil)

Solution:

The solution is simply to turn off the auditing for the objects which is giving error. Since we are already running an auditing solution;  native auditing of Oracle was not required in this scenario.

— Dynamic SQL to generate and execute the no audit statements —

DECLARE
l_sql_stmt varchar2(1000);
BEGIN
FOR t IN (SELECT owner, table_name
FROM all_tables
WHERE owner = ‘TCTDBS’
and
iot_type IS NULL
or
iot_type != ‘IOT_OVERFLOW’
)
LOOP
l_sql_stmt := ‘NOAUDIT ALL ON ‘ || t.owner || ‘.’ || t.table_name;
EXECUTE IMMEDIATE l_sql_stmt;
END LOOP;
END;

Once this was done we added the Standby Database to the Dataguard Broker and Enabled the configuration without any issues.

 

 

The post ORA-00600: internal error code, arguments: [audins:Invalid_param_type_for_binding appeared first on EasyOraDBA.

Restore and Upgrade Oracle Database from 11gR2 to 12c from Exadata X4-2 to X7-2

$
0
0

Upgrade from Exadata X4-2 11g (11.2.0.4) to Exadata X7-2 12CR1 (12.1.0.2.0) : ASM to ASM

Oracle Database 11.2.0.3and 11.2.0.4 to Oracle 12.2.0.1 Direct upgrade is supported by Oracle. If you have Database lower than those version than you have to upgrade to 11.2.0.4 first and you can upgrade to 12cR2

In our Scenario We will Migrate and Upgrade from One Exadata machine (X4-2) to another Exadata machine (X7-2) from 11.2.0.4 to 12.1.0.2.0

Source Exadata Setup

———————-

RAC 2-Nodes

Hardware : X4-2

Image version: 12.1.2.3.5.170418

DB NAME : mob

DATABASE HOME VERSION: 11.2.0.4

GRID HOME VERSION : 12.1.0.2.0

ASM Diskgroups : +DATAC1, +RECOC1

Target Exadata Setup

—————–

RAC 2-Nodes

Hardware : X7-2

Image version: 18.1.4.0.0.180125.3

DB NAME: SWX

DATABASE HOME VERSION : 12.1.0.2.0

GRID HOME VERSION : 12.2.0.1.0

ASM Diskgroups : +DATAC1, +RECOC1

 

Presumptions

————

* Oracle Home (12.1.0.2.0) is installed on the Target Server with latest patches

* Both Exadata Servers are reachable from the Network to Copy the backup files

1. Take RMAN Backup of Source Database on X4-2 hardware

rman target / nocatalog

run

{

allocate channel swx1 DEVICE TYPE DISK ;

allocate channel swx2 DEVICE TYPE DISK ;

allocate channel swx3 DEVICE TYPE DISK ;

allocate channel swx4 DEVICE TYPE DISK ;

CONFIGURE CONTROLFILE AUTOBACKUP ON;

CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO ‘/exabackups/rmanbkp/swx/autobackup_control_file_%F’;

CONFIGURE DEFAULT DEVICE TYPE TO DISK;

BACKUP DATABASE FORMAT ‘/exabackups/rmanbkp/swx/SWX_FULL_%s_%D_%M_%Y’ tag ‘SWX_DAILY_FULL_DB_BKP’;

BACKUP ARCHIVELOG ALL FORMAT ‘/exabackups/rmanbkp/swx/ARCH_%d_ALL_%s_%t’ tag ‘SWX_ARCH_LOG_BKP’;

}

SQL> create pfile=’/exabackups/rmanbkp/swx/swx_source.ora’ from spfile ;

2. Copy Backup from Source Server to Target Server

cd /exabackups/rmanbkp/swx

scp -r * oracle@qiibtstdbadm01.qiibonline.com:/u03/swx_today

3. Edit the Source pfile : swx_source.ora and change below parameters

Old pfile

———

swx1.__db_cache_size=9395240960

swx2.__db_cache_size=9395240960

swx1.__java_pool_size=33554432

swx2.__java_pool_size=33554432

swx1.__large_pool_size=67108864

swx2.__large_pool_size=67108864

swx2.__oracle_base=’/u02/app/oracle’#ORACLE_BASE set from environment

swx1.__oracle_base=’/u02/app/oracle’#ORACLE_BASE set from environment

swx1.__pga_aggregate_target=6006243328

swx2.__pga_aggregate_target=6006243328

swx1.__sga_target=11173625856

swx2.__sga_target=11173625856

swx1.__shared_io_pool_size=0

swx2.__shared_io_pool_size=0

swx1.__shared_pool_size=1577058304

swx2.__shared_pool_size=1577058304

swx1.__streams_pool_size=0

swx2.__streams_pool_size=0

*._client_enable_auto_unregister=TRUE

*.archive_lag_target=0

*.audit_file_dest=’/u02/app/oracle/admin/swx/adump’ ####### Change to Target Directory and Create teh Directory on OS ###########

*.audit_trail=’db’

*.cluster_database=TRUE ######## Set to FALSE ############

*.compatible=’11.2.0.4.0′ ######## Change to 12.1.0.2.0 ##########

*.control_files=’+datac1/swx/controlfile/current.346.853755085′,’+RECOC1/swx/controlfile/current.4208.853579009′ ########## Hash this #########

*.cpu_count=2

*.db_block_size=8192

*.db_create_file_dest=’+DATAC1′

*.db_create_online_log_dest_1=’+RECOC1′

*.db_domain=”

*.db_name=’swx’

*.db_recovery_file_dest=’+RECOC1′

*.db_recovery_file_dest_size=858993459200

*.diagnostic_dest=’/u02/app/oracle’ ########## Change to new target Oracle Base directory ############

*.dispatchers='(PROTOCOL=TCP) (SERVICE=swxXDB)’

swx2.instance_number=2

swx1.instance_number=1

*.java_jit_enabled=TRUE

*.log_archive_dest_1=’LOCATION=USE_DB_RECOVERY_FILE_DEST VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=SWX’

*.log_archive_format=’%t_%s_%r.arc’

swx1.log_archive_format=’%t_%s_%r.arc’

swx2.log_archive_format=’%t_%s_%r.arc’

*.log_archive_max_processes=8

*.log_archive_min_succeed_dest=1

swx1.log_archive_trace=0

swx2.log_archive_trace=0

*.sga_max_size=17179869184

*.sga_target=17179869184

*.open_cursors=300

*.processes=1500

*.remote_listener=’exaprod-scan:1521′ ########## Change to SCAN listener of Target DB #########

*.remote_login_passwordfile=’exclusive’

*.resource_manager_cpu_allocation=48

*.resource_manager_plan=’DEFAULT_PLAN’

*.sessions=1655

*.standby_file_management=’AUTO’

swx2.thread=2

swx1.thread=1

swx1.undo_tablespace=’UNDOTBS1′

swx2.undo_tablespace=’UNDOTBS2′

*.instance_number=2

Save the parameters and save pfile as swx_target.ora

New pfile : swx_target.ora

———

swx1.__db_cache_size=9395240960

swx2.__db_cache_size=9395240960

swx1.__java_pool_size=33554432

swx2.__java_pool_size=33554432

swx1.__large_pool_size=67108864

swx2.__large_pool_size=67108864

swx2.__oracle_base=’/u02/app/oracle’#ORACLE_BASE set from environment

swx1.__oracle_base=’/u02/app/oracle’#ORACLE_BASE set from environment

swx1.__pga_aggregate_target=6006243328

swx2.__pga_aggregate_target=6006243328

swx1.__sga_target=11173625856

swx2.__sga_target=11173625856

swx1.__shared_io_pool_size=0

swx2.__shared_io_pool_size=0

swx1.__shared_pool_size=1577058304

swx2.__shared_pool_size=1577058304

swx1.__streams_pool_size=0

swx2.__streams_pool_size=0

*._client_enable_auto_unregister=TRUE

*.archive_lag_target=0

*.audit_file_dest=’/u03/app/oracle/admin/swx/adump’ ####### Change to Target Directory and Create teh Directory on OS ###########

*.audit_trail=’db’

*.cluster_database=FALSE ######## Set to FALSE ############

*.compatible=’12.1.0.2.0′ ######## Change to 12.1.0.2.0 ##########

#*.control_files=’+datac1/swx/controlfile/current.346.853755085′,’+RECOC1/swx/controlfile/current.4208.853579009′ ########## Hash this #########

*.cpu_count=2

*.db_block_size=8192

*.db_create_file_dest=’+DATAC1′

*.db_create_online_log_dest_1=’+RECOC1′

*.db_domain=”

*.db_name=’swx’

*.db_recovery_file_dest=’+RECOC1′

*.db_recovery_file_dest_size=858993459200

*.diagnostic_dest=’/u03/app/oracle’ ########## Change to new target Oracle Base directory ############

*.dispatchers='(PROTOCOL=TCP) (SERVICE=swxXDB)’

swx2.instance_number=2

swx1.instance_number=1

*.java_jit_enabled=TRUE

*.log_archive_dest_1=’LOCATION=USE_DB_RECOVERY_FILE_DEST VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=SWX’

*.log_archive_format=’%t_%s_%r.arc’

swx1.log_archive_format=’%t_%s_%r.arc’

swx2.log_archive_format=’%t_%s_%r.arc’

*.log_archive_max_processes=8

*.log_archive_min_succeed_dest=1

swx1.log_archive_trace=0

swx2.log_archive_trace=0

*.sga_max_size=17179869184

*.sga_target=17179869184

*.open_cursors=300

*.processes=1500

*.remote_listener=’qiibtst-scan:1521′ ########## Change to SCAN listener of Target DB #########

*.remote_login_passwordfile=’exclusive’

*.resource_manager_cpu_allocation=48

*.resource_manager_plan=’DEFAULT_PLAN’

*.sessions=1655

*.standby_file_management=’AUTO’

swx2.thread=2

swx1.thread=1

swx1.undo_tablespace=’UNDOTBS1′

swx2.undo_tablespace=’UNDOTBS2′

*.instance_number=2

4. Start the Instance on Target Database using swx_target.ora pfile

Make sure you export ORACLE_HOME and ORACLE_SID environment variables properly before beginning

SQL> startup nomount pfile=’/u03/swx_today/swx_source.ora’;

rman target /

RMAN> restore controlfile from ‘/u03/swx_today/control_file_SWX_853579011_20180215_4985_1’;

Starting restore at 18-MAR-18

using target database control file instead of recovery catalog

allocated channel: ORA_DISK_1

channel ORA_DISK_1: SID=1140 instance=swx device type=DISK

channel ORA_DISK_1: restoring control file

channel ORA_DISK_1: restore complete, elapsed time: 00:00:07

output file name=+RECOC1/SWX/CONTROLFILE/current.654.971100501

Finished restore at 18-MAR-18

RMAN> alter database mount;

RMAN> catalog start with ‘/u03/swx_today’;

Starting implicit crosscheck backup at 18-MAR-18

allocated channel: ORA_DISK_1

allocated channel: ORA_DISK_2

allocated channel: ORA_DISK_3

allocated channel: ORA_DISK_4

Crosschecked 158 objects

Finished implicit crosscheck backup at 18-MAR-18

Starting implicit crosscheck copy at 18-MAR-18

using channel ORA_DISK_1

using channel ORA_DISK_2

using channel ORA_DISK_3

using channel ORA_DISK_4

Crosschecked 4 objects

Finished implicit crosscheck copy at 18-MAR-18

searching for all files in the recovery area

cataloging files…

no files cataloged

searching for all files that match the pattern /u03/swx_today

List of Files Unknown to the Database

=====================================

File Name: /u03/swx_today/ARCH_SWX_ALL_4979_968178229

File Name: /u03/swx_today/control_file_SWX_853579011_20180215_4985_1

File Name: /u03/swx_today/SWX_FULL_4975_15_02_2018

File Name: /u03/swx_today/ARCH_SWX_ALL_4981_968178230

File Name: /u03/swx_today/ARCH_SWX_ALL_4978_968178229

File Name: /u03/swx_today/ARCH_SWX_ALL_4982_968178255

File Name: /u03/swx_today/autobackup_control_file_c-133836929-20180215-02

File Name: /u03/swx_today/autobackup_control_file_c-133836929-20180215-00

File Name: /u03/swx_today/SWX_FULL_4974_15_02_2018

File Name: /u03/swx_today/autobackup_control_file_c-133836929-20180215-01

File Name: /u03/swx_today/swx_source.ora

File Name: /u03/swx_today/SWX_FULL_4976_15_02_2018

File Name: /u03/swx_today/ARCH_SWX_ALL_4980_968178230

File Name: /u03/swx_today/ARCH_SWX_ALL_4983_968178258

Do you really want to catalog the above files (enter YES or NO)? YES

cataloging files…

cataloging done

List of Cataloged Files

=======================

File Name: /u03/swx_today/ARCH_SWX_ALL_4979_968178229

File Name: /u03/swx_today/control_file_SWX_853579011_20180215_4985_1

File Name: /u03/swx_today/SWX_FULL_4975_15_02_2018

File Name: /u03/swx_today/ARCH_SWX_ALL_4981_968178230

File Name: /u03/swx_today/ARCH_SWX_ALL_4978_968178229

File Name: /u03/swx_today/ARCH_SWX_ALL_4982_968178255

File Name: /u03/swx_today/autobackup_control_file_c-133836929-20180215-02

File Name: /u03/swx_today/autobackup_control_file_c-133836929-20180215-00

File Name: /u03/swx_today/SWX_FULL_4974_15_02_2018

File Name: /u03/swx_today/autobackup_control_file_c-133836929-20180215-01

File Name: /u03/swx_today/SWX_FULL_4976_15_02_2018

File Name: /u03/swx_today/ARCH_SWX_ALL_4980_968178230

File Name: /u03/swx_today/ARCH_SWX_ALL_4983_968178258

List of Files Which Were Not Cataloged

=======================================

File Name: /u03/swx_today/swx_source.ora

RMAN-07517: Reason: The file header is corrupted

RMAN> restore database;

Starting restore at 18-MAR-18

using channel ORA_DISK_1

using channel ORA_DISK_2

using channel ORA_DISK_3

using channel ORA_DISK_4

allocated channel: ORA_SBT_TAPE_1

channel ORA_SBT_TAPE_1: SID=26 instance=swx device type=SBT_TAPE

channel ORA_SBT_TAPE_1: Data Protection for Oracle: version 7.1.0.0

channel ORA_DISK_1: starting datafile backup set restore

channel ORA_DISK_1: specifying datafile(s) to restore from backup set

channel ORA_DISK_1: restoring datafile 00003 to +DATAC1/swx/datafile/undotbs1.281.853578943

channel ORA_DISK_1: restoring datafile 00004 to +RECOC1/swx/datafile/audit_tbs.2213.871200459

channel ORA_DISK_1: reading from backup piece /u03/swx_today/SWX_FULL_4975_15_02_2018

channel ORA_DISK_2: starting datafile backup set restore

channel ORA_DISK_2: specifying datafile(s) to restore from backup set

channel ORA_DISK_2: restoring datafile 00001 to +DATAC1/swx/datafile/system.285.853578943

channel ORA_DISK_2: restoring datafile 00002 to +DATAC1/swx/datafile/sysaux.278.853578943

channel ORA_DISK_2: reading from backup piece /u03/swx_today/SWX_FULL_4976_15_02_2018

channel ORA_DISK_3: starting datafile backup set restore

channel ORA_DISK_3: specifying datafile(s) to restore from backup set

channel ORA_DISK_3: restoring datafile 00005 to +DATAC1/swx/datafile/undotbs2.351.853579027

channel ORA_DISK_3: restoring datafile 00006 to +DATAC1/swx/datafile/swx.345.853583217

channel ORA_DISK_3: reading from backup piece /u03/swx_today/SWX_FULL_4974_15_02_2018

channel ORA_DISK_1: piece handle=/u03/swx_today/SWX_FULL_4975_15_02_2018 tag=SWX_DAILY_FULL_DB_BKP

channel ORA_DISK_1: restored backup piece 1

channel ORA_DISK_1: restore complete, elapsed time: 00:00:15

channel ORA_DISK_2: piece handle=/u03/swx_today/SWX_FULL_4976_15_02_2018 tag=SWX_DAILY_FULL_DB_BKP

channel ORA_DISK_2: restored backup piece 1

channel ORA_DISK_2: restore complete, elapsed time: 00:00:15

channel ORA_DISK_3: piece handle=/u03/swx_today/SWX_FULL_4974_15_02_2018 tag=SWX_DAILY_FULL_DB_BKP

channel ORA_DISK_3: restored backup piece 1

channel ORA_DISK_3: restore complete, elapsed time: 00:04:45

Finished restore at 18-MAR-18

RMAN> recover database;

Starting recover at 18-MAR-18

using channel ORA_DISK_1

using channel ORA_DISK_2

using channel ORA_DISK_3

using channel ORA_DISK_4

using channel ORA_SBT_TAPE_1

starting media recovery

channel ORA_DISK_1: starting archived log restore to default destination

channel ORA_DISK_1: restoring archived log

archived log thread=2 sequence=48891

channel ORA_DISK_1: restoring archived log

archived log thread=1 sequence=43454

channel ORA_DISK_1: restoring archived log

archived log thread=2 sequence=48892

channel ORA_DISK_1: restoring archived log

archived log thread=1 sequence=43455

channel ORA_DISK_1: reading from backup piece /u03/swx_today/ARCH_SWX_ALL_4983_968178258

channel ORA_DISK_1: piece handle=/u03/swx_today/ARCH_SWX_ALL_4983_968178258 tag=SWX_ARCH_LOG_BKP

channel ORA_DISK_1: restored backup piece 1

channel ORA_DISK_1: restore complete, elapsed time: 00:00:01

archived log file name=+RECOC1/SWX/ARCHIVELOG/2018_03_18/thread_1_seq_43454.645.971100923 thread=1 sequence=43454

archived log file name=+RECOC1/SWX/ARCHIVELOG/2018_03_18/thread_2_seq_48891.642.971100923 thread=2 sequence=48891

channel default: deleting archived log(s)

archived log file name=+RECOC1/SWX/ARCHIVELOG/2018_03_18/thread_2_seq_48891.642.971100923 RECID=238985 STAMP=971100923

archived log file name=+RECOC1/SWX/ARCHIVELOG/2018_03_18/thread_2_seq_48892.639.971100923 thread=2 sequence=48892

channel default: deleting archived log(s)

archived log file name=+RECOC1/SWX/ARCHIVELOG/2018_03_18/thread_1_seq_43454.645.971100923 RECID=238987 STAMP=971100923

archived log file name=+RECOC1/SWX/ARCHIVELOG/2018_03_18/thread_1_seq_43455.648.971100923 thread=1 sequence=43455

channel default: deleting archived log(s)

archived log file name=+RECOC1/SWX/ARCHIVELOG/2018_03_18/thread_1_seq_43455.648.971100923 RECID=238988 STAMP=971100923

unable to find archived log

archived log thread=1 sequence=43456

RMAN-00571: ===========================================================

RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============

RMAN-00571: ===========================================================

RMAN-03002: failure of recover command at 03/18/2018 14:15:25

RMAN-06054: media recovery requesting unknown archived log for thread 1 with sequence 43456 and starting SCN of 21582106266

RMAN> alter database open resetlogs;

RMAN-00571: ===========================================================

RMAN-00569: =============== ERROR MESSAGE STACK FOLLOWS ===============

RMAN-00571: ===========================================================

RMAN-00601: fatal error in recovery manager

RMAN-03004: fatal error during execution of command

ORA-03114: not connected to ORACLE

RMAN-03002: failure of sql statement command at 03/18/2018 14:15:42

ORA-01092: ORACLE instance terminated. Disconnection forced

ORA-00704: bootstrap process failure

ORA-00604: error occurred at recursive SQL level 2

ORA-00904: “I”.”UNUSABLEBEGINNING#”: invalid identifier

Process ID: 136136

Session ID: 1137 Serial number: 14577

sqlplus “/as sysdba”

Change control file information with new location after restore in pfile and start database again +RECOC1/SWX/CONTROLFILE/current.654.971100501

Also change CLUSTER_DATABASE to false in pfile

If you get any error like below error then copy the file to the dbs folder as init.ora

ORA-01078: failure in processing system parameters

LRM-00109: could not open parameter file ‘/u03/app/oracle/product/12.1.0.2/dbhome_1/dbs/initswx.ora’

$ cp -p /u03/swx_today/swx_source.ora /u03/app/oracle/product/12.1.0.2/dbhome_1/dbs/initswx.ora

sqlplus “/as sysdba”

SQL> startup upgrade;

$ cd $ORACLE_HOME/rdbms/admin

$ORACLE_HOME/perl/bin/perl catctl.pl -n 4 catupgrd.sql

Argument list for [catctl.pl]

SQL Process Count n = 4

SQL PDB Process Count N = 0

Input Directory d = 0

Phase Logging Table t = 0

Log Dir l = 0

Script s = 0

Serial Run S = 0

Upgrade Mode active M = 0

Start Phase p = 0

End Phase P = 0

Log Id i = 0

Run in c = 0

Do not run in C = 0

Echo OFF e = 1

No Post Upgrade x = 0

Reverse Order r = 0

Open Mode Normal o = 0

Debug catcon.pm z = 0

Debug catctl.pl Z = 0

Display Phases y = 0

Child Process I = 0

catctl.pl version: 12.1.0.2.0

Oracle Base = /u03/app/oracle

Analyzing file catupgrd.sql

Log files in /u03/app/oracle/product/12.1.0.2/dbhome_1/rdbms/admin

catcon: ALL catcon-related output will be written to catupgrd_catcon_227787.lst

catcon: See catupgrd*.log files for output generated by scripts

catcon: See catupgrd_*.lst files for spool files, if any

Number of Cpus = 2

SQL Process Count = 4

——————————————————

Phases [0-73] Start Time:[2018_03_18 14:39:13]

——————————————————

Serial Phase #: 0 Files: 1 Time: 105s

Serial Phase #: 1 Files: 5 Time: 22s

Restart Phase #: 2 Files: 1 Time: 0s

Parallel Phase #: 3 Files: 18 Time: 4s

Restart Phase #: 4 Files: 1 Time: 0s

Serial Phase #: 5 Files: 5 Time: 10s

Serial Phase #: 6 Files: 1 Time: 9s

Serial Phase #: 7 Files: 4 Time: 4s

Restart Phase #: 8 Files: 1 Time: 0s

Parallel Phase #: 9 Files: 62 Time: 12s

Restart Phase #:10 Files: 1 Time: 0s

Serial Phase #:11 Files: 1 Time: 8s

Restart Phase #:12 Files: 1 Time: 0s

Parallel Phase #:13 Files: 91 Time: 4s

Restart Phase #:14 Files: 1 Time: 0s

Parallel Phase #:15 Files: 111 Time: 8s

Restart Phase #:16 Files: 1 Time: 0s

Serial Phase #:17 Files: 3 Time: 0s

Restart Phase #:18 Files: 1 Time: 0s

Parallel Phase #:19 Files: 32 Time: 8s

Restart Phase #:20 Files: 1 Time: 0s

Serial Phase #:21 Files: 3 Time: 4s

Restart Phase #:22 Files: 1 Time: 0s

Parallel Phase #:23 Files: 23 Time: 43s

Restart Phase #:24 Files: 1 Time: 0s

Parallel Phase #:25 Files: 11 Time: 17s

Restart Phase #:26 Files: 1 Time: 0s

Serial Phase #:27 Files: 1 Time: 0s

Restart Phase #:28 Files: 1 Time: 0s

Serial Phase #:30 Files: 1 Time: 0s

Serial Phase #:31 Files: 257 Time: 12s

Serial Phase #:32 Files: 1 Time: 0s

Restart Phase #:33 Files: 1 Time: 0s

Serial Phase #:34 Files: 1 Time: 3s

Restart Phase #:35 Files: 1 Time: 0s

Restart Phase #:36 Files: 1 Time: 0s

Serial Phase #:37 Files: 4 Time: 31s

Restart Phase #:38 Files: 1 Time: 0s

Parallel Phase #:39 Files: 13 Time: 27s

Restart Phase #:40 Files: 1 Time: 0s

Parallel Phase #:41 Files: 10 Time: 3s

Restart Phase #:42 Files: 1 Time: 0s

Serial Phase #:43 Files: 1 Time: 4s

Restart Phase #:44 Files: 1 Time: 0s

Serial Phase #:45 Files: 1 Time: 3s

Serial Phase #:46 Files: 1 Time: 1s

Restart Phase #:47 Files: 1 Time: 0s

Serial Phase #:48 Files: 1 Time: 82s

Restart Phase #:49 Files: 1 Time: 0s

Serial Phase #:50 Files: 1 Time: 22s

Restart Phase #:51 Files: 1 Time: 0s

Serial Phase #:52 Files: 1 Time: 16s

Restart Phase #:53 Files: 1 Time: 0s

Serial Phase #:54 Files: 1 Time: 192s

Restart Phase #:55 Files: 1 Time: 0s

Serial Phase #:56 Files: 1 Time: 37s

Restart Phase #:57 Files: 1 Time: 0s

Serial Phase #:58 Files: 1 Time: 69s

Restart Phase #:59 Files: 1 Time: 0s

Serial Phase #:60 Files: 1 Time: 295s

Restart Phase #:61 Files: 1 Time: 0s

Serial Phase #:62 Files: 1 Time: 24s

Restart Phase #:63 Files: 1 Time: 0s

Serial Phase #:64 Files: 1 Time: 0s

Serial Phase #:65 Files: 1 Time: 11s

Serial Phase #:66 Files: 1 Time: 25s

Serial Phase #:67 Files: 1 Time: 1s

Serial Phase #:68 Files: 1 Time: 0s

Serial Phase #:69 Files: 1 Time: 27s

——————————————————

Phases [0-73] End Time:[2018_03_18 14:58:16]

——————————————————

Grand Total Time: 1145s

*** WARNING: ERRORS FOUND DURING UPGRADE ***

Due to errors found during the upgrade process, the post

upgrade actions in catuppst.sql have not been automatically run.

*** THEREFORE THE DATABASE UPGRADE IS NOT YET COMPLETE ***

1. Evaluate the errors found in the upgrade logs

and determine the proper action.

2. Execute the post upgrade script as described in Chapter 3

of the Database Upgrade Guide.

REASON:

catuppst.sql unable to run in Database: swx Id: 0

ERRORS FOUND: during upgrade CATCTL ERROR COUNT=5

——————————————————

Identifier XDB 18-03-18 02:47:25 Script = /u03/app/oracle/product/12.1.0.2/dbhome_

ERROR = [ORA-01917: user or role ‘ANONYMOUS’ does not exist ORA-06512: at line 7

ORA-06512: at line 15

]

STATEMENT = [declare

already_revoked exception;

pragma exception_init(already_revoked,-01927);

procedure revoke_inherit_privileges(user in varchar2) as

begin

execute immediate ‘revoke inherit privileges on user ‘||

dbms_assert.enquote_name(user)||’ from public’;

exception

when already_revoked then null;

end;

begin

revoke_inherit_privileges(‘xdb’);

revoke_inherit_privileges(‘anonymous’);

end;]

——————————————————

——————————————————

Identifier XDB 18-03-18 02:47:25 Script = /u03/app/oracle/product/12.1.0.2/dbhome_

ERROR = [ORA-06512: at line 7 ORA-06512: at line 15

]

STATEMENT = [as above]

——————————————————

——————————————————

Identifier XDB 18-03-18 02:47:25 Script = /u03/app/oracle/product/12.1.0.2/dbhome_

ERROR = [ORA-06512: at line 15]

STATEMENT = [as above]

——————————————————

——————————————————

Identifier ORDIM 18-03-18 02:50:44 Script = /u03/app/oracle/product/12.1.0.2/dbhome_

ERROR = [ORA-20000: Oracle XML Database component not valid. Oracle XML Database must be installed and valid prior to Oracle Multimedia install, upgrade, downgrade, or patch.

ORA-06512: at line 3

]

STATEMENT = [begin

IF dbms_registry.is_valid(‘XDB’,dbms_registry.release_version) != 1 THEN

raise_application_error(-20000,

‘Oracle XML Database component not valid. ‘||

‘Oracle XML Database must be installed and valid prior to Oracle Multimedia install, upgrade, downgrade, or patch.’);

END IF;

end;]

——————————————————

——————————————————

Identifier ORDIM 18-03-18 02:50:44 Script = /u03/app/oracle/product/12.1.0.2/dbhome_

ERROR = [installed and valid prior to Oracle Multimedia install, upgrade, downgrade, or patch.

ORA-06512: at line 3

]

STATEMENT = [as above]

——————————————————

——————————————————

Identifier ORDIM 18-03-18 02:50:44 Script = /u03/app/oracle/product/12.1.0.2/dbhome_

ERROR = [patch. ORA-06512: at line 3

]

STATEMENT = [as above]

——————————————————

——————————————————

Identifier ORDIM 18-03-18 02:50:44 Script = /u03/app/oracle/product/12.1.0.2/dbhome_

ERROR = [ORA-06512: at line 3]

STATEMENT = [as above]

——————————————————

LOG FILES: (catupgrd*.log)

Upgrade Summary Report Located in:

/u03/app/oracle/product/12.1.0.2/dbhome_1/cfgtoollogs/swx/upgrade/upg_summary.log

Grand Total Upgrade Time: [0d:0h:19m:5s]

sqlplus “/as sysdba”

SQL> @utlrp.sql

SQL> select * from dba_registry;

Verify all components are valid

OWB OWB 11.2.0.4.0 VALID 18-MAR-2018 15:02:31 SERVER SYS OWBSYS DBMS_OWB.VALIDATE

EM Oracle Enterprise Manager 11.2.0.4.0 VALID 24-AUG-2013 11:58:27 SERVER SYS SYSMAN

AMD OLAP Catalog 11.2.0.4.0 OPTION OFF 18-MAR-2018 14:46:55 SERVER SYS OLAPSYS CWM2_OLAP_INSTALLER.VALIDATE_CWM2_INSTALL

SDO Spatial 12.1.0.2.0 VALID 18-MAR-2018 15:02:30 SERVER SYS MDSYS VALIDATE_SDO MDDATA,MDSYS,SPATIAL_CSW_ADMIN_USR,SPATIAL_WFS_ADMIN_USR

ORDIM Oracle Multimedia 12.1.0.2.0 VALID 18-MAR-2018 15:02:29 SERVER SYS ORDSYS VALIDATE_ORDIM ORDDATA,ORDPLUGINS,SI_INFORMTN_SCHEMA

XDB Oracle XML Database 12.1.0.2.0 VALID 18-MAR-2018 15:02:28 SERVER SYS XDB DBMS_REGXDB.VALIDATEXDB XS$NULL

CONTEXT Oracle Text 12.1.0.2.0 VALID 18-MAR-2018 15:02:27 SERVER SYS CTXSYS VALIDATE_CONTEXT

OWM Oracle Workspace Manager 12.1.0.2.0 VALID 18-MAR-2018 15:02:25 SERVER SYS WMSYS VALIDATE_OWM

CATALOG Oracle Database Catalog Views 12.1.0.2.0 VALID 18-MAR-2018 15:02:23 SERVER SYS SYS DBMS_REGISTRY_SYS.VALIDATE_CATALOG

CATPROC Oracle Database Packages and Types 12.1.0.2.0 VALID 18-MAR-2018 15:02:23 SERVER SYS SYS DBMS_REGISTRY_SYS.VALIDATE_CATPROC APPQOSSYS,DBSNMP,DIP,GSMADMIN_INTERNAL,ORACLE_OCM,OUTLN,SYSTEM

JAVAVM JServer JAVA Virtual Machine 12.1.0.2.0 VALID 18-MAR-2018 15:02:26 SERVER SYS SYS INITJVMAUX.VALIDATE_JAVAVM

COMP_ID COMP_NAME VERSION STATUS MODIFIED NAMESPACE CONTROL SCHEMA PROCEDURE STARTUP PARENT_ID OTHER_SCHEMAS

—————————— ————————————————————————————————————————————————————————————————————————————————————— —————————— ———– ——————– —————————— ——————————————————————————————————————————– ——————————————————————————————————————————– ————————————————————- ——– —————————— —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————-

XML Oracle XDK 12.1.0.2.0 VALID 18-MAR-2018 15:02:27 SERVER SYS SYS XMLVALIDATE

CATJAVA Oracle Database Java Packages 12.1.0.2.0 VALID 18-MAR-2018 15:02:27 SERVER SYS SYS DBMS_REGISTRY_SYS.VALIDATE_CATJAVA

APS OLAP Analytic Workspace 12.1.0.2.0 VALID 18-MAR-2018 15:02:29 SERVER SYS SYS APS_VALIDATE

XOQ Oracle OLAP API 12.1.0.2.0 VALID 18-MAR-2018 15:02:30 SERVER SYS SYS XOQ_VALIDATE

RAC Oracle Real Application Clusters 12.1.0.2.0 VALID 18-MAR-2018 15:02:31 SERVER SYS SYS DBMS_CLUSTDB.VALIDATE

16 rows selected.

SQL>

The post Restore and Upgrade Oracle Database from 11gR2 to 12c from Exadata X4-2 to X7-2 appeared first on EasyOraDBA.

Enable Transparent Data Encryption in Oracle 12c (12.1 12.2)

$
0
0

1. Add Encryption Wallet in sqlnet.ora Location $ORACLE_HOME/network/admin

[oracle@localhost admin]$ vi sqlnet.ora

ENCRYPTION_WALLET_LOCATION =
(SOURCE =(METHOD = FILE)(METHOD_DATA =
(DIRECTORY = /u01/wallet)))

 

2. Create encryption wallet location

mkdir -p /u01/wallet

3. Create Keystore and Key. And Open the Wallet

SQL*Plus: Release 12.2.0.1.0 Production on Thu May 24 14:36:10 2018

Copyright (c) 1982, 2016, Oracle. All rights reserved.

 

Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 – 64bit Production

SQL> ADMINISTER KEY MANAGEMENT CREATE KEYSTORE ‘/u01/wallet/’ identified by Abc1234$#;

keystore altered.

SQL> ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY Abc1234$#
2 ;

keystore altered.

SQL> ADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY Abc1234$# with backup;

keystore altered.

SQL> select * FROM v$encryption_keys;

 

——————————————————————————–
USER_ID KEY_USE KEYSTORE_TYPE
——————————————————————————————————————————– —————- —————– ——————————– ———-
AZeoliwGWU8gv2N8DKilil4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA 24-MAY-18 04.37.19.166968 AM +00:00 24-MAY-18 04.37.19.166972 AM +00:00 SYS 0 SYS 0 TDE SOFTWARE KEYSTORE LOCAL NO orcl 1502594573 orcl 1 4294967295 0 0 00000000000000000000000000000000 orcl 1502594573 orcl 1 4294967295 0 0 00000000000000000000000000000000 0

 

As we can see the encryption keystore is created and open

4. Lets Create Encrypted Tablespace and Check

 

CREATE TABLESPACE TEST_ENCRY
datafile ‘/u01/oradata/orcl/test_encry01.dbf’ size 10M ENCRYPTION USING ‘AES256’ DEFAULT STORAGE(ENCRYPT);

create user vormetric_enc identified by vormetric123 default tablespace TEST_ENCRY;

grant dba to vormetric_enc;

sqlplus vormetric_enc/vormetric123

CREATE TABLE vormetric_enc.Persons (
PersonID int ENCRYPT,
LastName varchar(255) ENCRYPT,
FirstName varchar(255) ENCRYPT,
Address varchar(255) ENCRYPT,
City varchar(255) ENCRYPT
);

 

insert into vormetric_enc.Persons values(‘898899′,’MOHAMMAD’,’SHADAB’,’SURREY HILLS’,’SYDNEY’);

commit;

insert into vormetric_enc.Persons values(‘898899′,’CHRIS’,’MARTIN’,’SURREY HILLS’,’SYDNEY’);

commit;

insert into vormetric_enc.Persons values(‘898899′,’YORKE’,’THOM’,’MANSFIELD’,’MANCHESTER’);

commit;

 

## Test if you can see the data

strings /u01/oradata/orcl/test_encry01.dbf

dp|e
aj”C
W%O*$S
DrCh
O.RY(‘
nUqn
`odj
@`j4
Wl.8)[
mkpo
HyXr
AhT{K
rGp9Ia
(6R<U
ZoQQ

You will only see garbled character, which means your data is now fully encrypted at rest.

You can drop the test tablespace after testing

drop tablespace TEST_ENCRY including contents and datafiles;

 

5. Now just to do a proof of concept to check how easy it is to read data from a datafile (tablespace) if it is not encrypted

create tablespace test datafile ‘/u01/oradata/orcl/test01.dbf’size 10M;

create user vormetric identified by vormetric default tablespace test;

alter user vormetric identified by vormetric123;

grant dba to vormetric;

sqlplus vormetric/vormetric123

CREATE TABLE vormetric.Persons (
PersonID int,
LastName varchar(255),
FirstName varchar(255),
Address varchar(255),
City varchar(255)
);

 

insert into vormetric.Persons values(‘898899′,’MOHAMMAD’,’SHADAB’,’SURREY HILLS’,’SYDNEY’);

commit;

insert into vormetric.Persons values(‘898899′,’CHRIS’,’MARTIN’,’SURREY HILLS’,’SYDNEY’);

commit;

insert into vormetric.Persons values(‘898899′,’YORKE’,’THOM’,’MANSFIELD’,’MANCHESTER’);

commit;

## Test if you can see the data

strings /u01/oradata/orcl/test01.dbf

}|{z
YORCL
|\;:
TEST
AAAAAAAA
V1!
!V1#
YORKE
THOM MANSFIELD
MANCHESTER,
MARTIN
CHRIS
SURREY HILLS
SYDNEY,
MOHAMMAD
SHADAB
SURREY HILLS
SYDNEY

## As you can see data is so clearly visible from the datafile when it is not encrypted

You can  drop the test tablespace after testing

drop tablespace test including contents and datafiles;

 

7. Enable Auto ~Open of wallet so you don’t have to specify the password every-time the database restarts

SQL> SELECT * FROM v$encryption_wallet;

STATUS WALLET_TYPE WALLET_OR FULLY_BAC CON_ID
—————————— ——————– ——— ——— ———-
FILE
/u01/wallet/
OPEN PASSWORD SINGLE NO 0

The wallet is in Password mode which means each time you restart your database you have to specify the key explicitly to open the wallet. We can change this by creating autologin for the wallet, but make you sure you have you encryption key password safely stored somewhere.

SQL> ADMINISTER KEY MANAGEMENT CREATE AUTO_LOGIN KEYSTORE FROM KEYSTORE ‘/u01/wallet/’ IDENTIFIED BY Abc1234$#;

SQL> SELECT * FROM v$encryption_wallet;

STATUS WALLET_TYPE WALLET_OR FULLY_BAC CON_ID
—————————— ——————– ——— ——— ———-
FILE
/u01/wallet/
OPEN AUTOLOGIN SINGLE NO 0

As you can see the Wallet is now AutoLogin

 

The post Enable Transparent Data Encryption in Oracle 12c (12.1 12.2) appeared first on EasyOraDBA.

Install Oracle Database 12cR2 Silent Mode on CENT OS 7 & Oracle Linux 7

$
0
0

If you are going to work on Oracle Database in cloud or in environments where X11 client or server is not available. It is always good to learn how to do a silent installation of Oracle. Specially in todays CI/CD devops model where dba's have to provision DB servers using Ansible or other such tools. … Continue reading Install Oracle Database 12cR2 Silent Mode on CENT OS 7 & Oracle Linux 7

The post Install Oracle Database 12cR2 Silent Mode on CENT OS 7 & Oracle Linux 7 appeared first on EasyOraDBA.

Oracle CRS fails with CRS-5017 ORA-01017, what to do when even in init mode you cannot bring up CRS

$
0
0

After a memory upgrade in our RAC servers, the Cluster wasn’t coming up after a reboot. We first tried to start the CRS in init mode but even that failed. Upon checking the CRS alert log we discovered that the ASM was failing to start due to below error.
Error:
CRS-5017: The resource action “ora.asm start” encountered the following error:
ORA-01017: invalid username/password; logon denied
. For details refer to “(:CLSN00107:)” in “/u01/app/grid/diag/crs/<hostname>/crs/trace/ohasd_oraagent_grid.trc”
Solution:
We discovered the above error in the CRS alert log. After knocking our heads around, trying to start crsd in init mode without any success. We eventually discovered it was related to SQLNET.AUTHENTICATION_SERVICES parameter in sqlnet.ora file in the Grid Home. We removed the entry in the sqlnet.ora file from both nodes of the cluster and restarted the cluster. Everything was back to normal after that. Another day of troubleshooting CRS without any help from Oracle Support.

The post Oracle CRS fails with CRS-5017 ORA-01017, what to do when even in init mode you cannot bring up CRS appeared first on EasyOraDBA.

Upgrade and Migrate to 12c – Oracle Official Presentation


RMAN-08132: WARNING: cannot update recovery area reclaimable file list

$
0
0

ORA-00245: control file backup failed; target is likely on a local file system
RMAN-08132: WARNING: cannot update recovery area reclaimable file list
Solution : This is due to the snapshot of the control file being on a local filesystem in a RAC database. Change the snapshot control file back to a shared disk
RMAN > CONFIGURE SNAPSHOT CONTROLFILE NAME TO ‘+DATAC1/DB004/snapcf.f’;

The post RMAN-08132: WARNING: cannot update recovery area reclaimable file list appeared first on EasyOraDBA.

Scripts to Check Library Cache Lock Contention

PRVG-1561 : Setting ORA_CRS_HOME variable is not supported

Create or Drop Public Synonyms in Oracle

$
0
0

Synonym is an alternative name for a table, view, sequence, procedure, stored function, package, materialized view, Java class schema object, user-defined object type, or another synonym.
— Create a Public Synonym for a Table —

CREATE OR REPLACE PUBLIC SYNONYM EASYNAME for EASYORADBA.TABLE00001 ;

— Drop a Public Synonym for a Table —

DROP PUBLIC SYNONYM EASYNAME ;

— View to Check Synonyms in Oracle —

SELECT * from DBA_SYNONYMS;

 

The post Create or Drop Public Synonyms in Oracle appeared first on EasyOraDBA.

Patching Oracle 12c Multi-tenant to latest PSU now known as DPBP

$
0
0

Before we begin this is a note from the Oracle Website

The name of these bundle patches was changed to “Database Proactive Bundle Patch” in April 2016.
The patches include fixes for both Engineered Systems and for DB In-Memory.
They can be used on both Exadata and non-Exadata systems, and can be used for both RAC and non-RAC configurations.
Quarterly patches are released on all platforms.
Monthly patches may only be released on a subset of platforms.

DB Version : 12.1.0.2

OS : OEL 6 Update 6, Linux x86_64

Single Instance No RAC

Multitenant Option Enabled

1 CDB, 1 PDB

CDB Name: CDBDEV

PDB Name: PDBDEV

The database bundled patches are now called “Database Proactive Bundle Patch” from April 2016.

Patch 22291127 – Database Patch Set Update 12.1.0.2.160419 (Includes CPUApr2016) – Single Instance DB

Patch 22899531: DATABASE PROACTIVE BUNDLE PATCH 12.1.0.2.160419 (APR2016)- DB Plus Grid Infrastructure

Also the naming convention has changed like below

Patch description:  “Database Patch Set Update : 12.1.0.2.160419 (22291127)”

   Created on 6 Apr 2016, 03:46:21 hrs PST8PDT

Sub-patch  21948354; “Database Patch Set Update : 12.1.0.2.160119 (21948354)”

Sub-patch  21359755; “Database Patch Set Update : 12.1.0.2.5 (21359755)”

Sub-patch  20831110; “Database Patch Set Update : 12.1.0.2.4 (20831110)”

Sub-patch  20299023; “Database Patch Set Update : 12.1.0.2.3 (20299023)”

Sub-patch  19769480; “Database Patch Set Update : 12.1.0.2.2 (19769480)”

The left most part denotes “yymmdd” as a post-fix to now which date is the PSU from

  1. Download the latest patches for your platform from Oracle Support Website.
  2. unzip the patch

unzip p22291127_12102_<platform>.zip

cd 22291127

Go to Directory of Patch and Apply the Patch

[oracle@localhost 22291127]$ /home/oracle/app/oracle/product/12.1.0/dbhome_1/OPatch/opatch prereq CheckCOnflictAgainstOHWithDetail -ph ./

Oracle Interim Patch Installer version 12.1.0.1.3

Copyright (c) 2016, Oracle Corporation.  All rights reserved.

PREREQ session

Oracle Home       : /home/oracle/app/oracle/product/12.1.0/dbhome_1

Central Inventory : /home/oracle/app/oraInventory

   from           : /home/oracle/app/oracle/product/12.1.0/dbhome_1/oraInst.loc

OPatch version    : 12.1.0.1.3

OUI version       : 12.1.0.2.0

Log file location : /home/oracle/app/oracle/product/12.1.0/dbhome_1/cfgtoollogs/opatch/opatch2016-05-13_08-38-21AM_1.log

Invoking prereq “checkconflictagainstohwithdetail”

Prereq “checkConflictAgainstOHWithDetail” passed.

OPatch succeeded.

  1. Download the latest version of Patch before applying the patches. You can search latest Patch by searching for “Patch 6880880”
  2. Shutdown the Database and Listener
  3. Go to Directory of the Patch and Apply the Patch

[oracle@localhost 22291127]$ /home/oracle/app/oracle/product/12.1.0/dbhome_1/OPatch/opatch apply

Oracle Interim Patch Installer version 12.1.0.1.10

Copyright (c) 2016, Oracle Corporation.  All rights reserved.

Oracle Home       : /home/oracle/app/oracle/product/12.1.0/dbhome_1

Central Inventory : /home/oracle/app/oraInventory

   from           : /home/oracle/app/oracle/product/12.1.0/dbhome_1/oraInst.loc

OPatch version    : 12.1.0.1.10

OUI version       : 12.1.0.2.0

Log file location : /home/oracle/app/oracle/product/12.1.0/dbhome_1/cfgtoollogs/opatch/opatch2016-05-13_08-44-41AM_1.log

Verifying environment and performing prerequisite checks…

OPatch continues with these patches:   19769480  20299023  20831110  21359755  21948354  22291127

Do you want to proceed? [y|n]

y

User Responded with: Y

All checks passed.

Provide your email address to be informed of security issues, install and

initiate Oracle Configuration Manager. Easier for you if you use your My

Oracle Support Email address/User Name.

Visit http://www.oracle.com/support/policies.html for details.

Email address/User Name:

You have not provided an email address for notification of security issues.

Do you wish to remain uninformed of security issues ([Y]es, [N]o) [N]:  y

Please shutdown Oracle instances running out of this ORACLE_HOME on the local system.

(Oracle Home = ‘/home/oracle/app/oracle/product/12.1.0/dbhome_1’)

Is the local system ready for patching? [y|n]

y

User Responded with: Y

Backing up files…

Applying sub-patch ‘19769480’ to OH ‘/home/oracle/app/oracle/product/12.1.0/dbhome_1’

Patching component oracle.rdbms.deconfig, 12.1.0.2.0…

Patching component oracle.xdk, 12.1.0.2.0…

Patching component oracle.tfa, 12.1.0.2.0…

Patching component oracle.rdbms.util, 12.1.0.2.0…

Patching component oracle.rdbms, 12.1.0.2.0…

Patching component oracle.rdbms.dbscripts, 12.1.0.2.0…

Patching component oracle.xdk.parser.java, 12.1.0.2.0…

Patching component oracle.oraolap, 12.1.0.2.0…

Patching component oracle.xdk.rsf, 12.1.0.2.0…

Patching component oracle.rdbms.rsf, 12.1.0.2.0…

Patching component oracle.rdbms.rman, 12.1.0.2.0…

Patching component oracle.ldap.rsf, 12.1.0.2.0…

Patching component oracle.ldap.rsf.ic, 12.1.0.2.0…

Applying sub-patch ‘20299023’ to OH ‘/home/oracle/app/oracle/product/12.1.0/dbhome_1’

ApplySession: Optional component(s) [ oracle.has.crs, 12.1.0.2.0 ]  not present in the Oracle Home or a higher version is found.

Patching component oracle.tfa, 12.1.0.2.0…

Patching component oracle.rdbms.deconfig, 12.1.0.2.0…

Patching component oracle.rdbms.rsf, 12.1.0.2.0…

Patching component oracle.rdbms, 12.1.0.2.0…

Patching component oracle.rdbms.dbscripts, 12.1.0.2.0…

Patching component oracle.rdbms.rsf.ic, 12.1.0.2.0…

Patching component oracle.ldap.rsf, 12.1.0.2.0…

Patching component oracle.ldap.rsf.ic, 12.1.0.2.0…

Applying sub-patch ‘20831110’ to OH ‘/home/oracle/app/oracle/product/12.1.0/dbhome_1’

Patching component oracle.rdbms, 12.1.0.2.0…

Patching component oracle.oraolap.dbscripts, 12.1.0.2.0…

Patching component oracle.ldap.rsf, 12.1.0.2.0…

Patching component oracle.tfa, 12.1.0.2.0…

Patching component oracle.rdbms.dbscripts, 12.1.0.2.0…

Patching component oracle.rdbms.rsf, 12.1.0.2.0…

Applying sub-patch ‘21359755’ to OH ‘/home/oracle/app/oracle/product/12.1.0/dbhome_1’

Patching component oracle.assistants.server, 12.1.0.2.0…

Patching component oracle.rdbms.rsf, 12.1.0.2.0…

Patching component oracle.rdbms, 12.1.0.2.0…

Patching component oracle.rdbms.dbscripts, 12.1.0.2.0…

Patching component oracle.tfa, 12.1.0.2.0…

Applying sub-patch ‘21948354’ to OH ‘/home/oracle/app/oracle/product/12.1.0/dbhome_1’

Patching component oracle.rdbms.deconfig, 12.1.0.2.0…

Patching component oracle.xdk, 12.1.0.2.0…

Patching component oracle.tfa, 12.1.0.2.0…

Patching component oracle.rdbms, 12.1.0.2.0…

Patching component oracle.rdbms.dbscripts, 12.1.0.2.0…

Patching component oracle.xdk.parser.java, 12.1.0.2.0…

Patching component oracle.rdbms.rsf, 12.1.0.2.0…

Patching component oracle.xdk.rsf, 12.1.0.2.0…

Applying sub-patch ‘22291127’ to OH ‘/home/oracle/app/oracle/product/12.1.0/dbhome_1’

ApplySession: Optional component(s) [ oracle.oid.client, 12.1.0.2.0 ]  not present in the Oracle Home or a higher version is found.

Patching component oracle.ldap.rsf, 12.1.0.2.0…

Patching component oracle.ldap.client, 12.1.0.2.0…

Patching component oracle.tfa, 12.1.0.2.0…

Patching component oracle.rdbms, 12.1.0.2.0…

Patching component oracle.rdbms.dbscripts, 12.1.0.2.0…

Patching component oracle.rdbms.rsf, 12.1.0.2.0…

Patching component oracle.oraolap, 12.1.0.2.0…

Patching component oracle.rdbms.rman, 12.1.0.2.0…

Patching component oracle.oraolap.dbscripts, 12.1.0.2.0…

Composite patch 22291127 successfully applied.

Log file location: /home/oracle/app/oracle/product/12.1.0/dbhome_1/cfgtoollogs/opatch/opatch2016-05-13_08-44-41AM_1.log

OPatch succeeded.

  1. Post- Patch Installation

The datapatch utility will then run the necessary apply scripts to load the modified SQL files into the database. An entry will be added to the dba_registry_sqlpatch view reflecting the patch application. In the dba_registry_sqlpatch view, verify the Status for the APPLY is “SUCCESS”.

Startup the Database

sqlplus “/as sysdba”

startup

alter pluggable database all open;

[oracle@localhost 22291127]$ /home/oracle/app/oracle/product/12.1.0/dbhome_1/OPatch/datapatch -verbose

SQL Patching tool version 12.1.0.2.0 on Fri May 13 08:50:32 2016

Copyright (c) 2015, Oracle.  All rights reserved.

Log file for this invocation: /home/oracle/app/oracle/cfgtoollogs/sqlpatch/sqlpatch_7427_2016_05_13_08_50_32/sqlpatch_invocation.log

Connecting to database…OK

Note:  Datapatch will only apply or rollback SQL fixes for PDBs

       that are in an open state, no patches will be applied to closed PDBs.

       Please refer to Note: Datapatch: Database 12c Post Patch SQL Automation

       (Doc ID 1585822.1)

Bootstrapping registry and package to current versions…done

Determining current state…done

Current state of SQL patches:

Bundle series PSU:

  ID 160419 in the binary registry and not installed in any PDB

Adding patches to installation queue and performing prereq checks…

Installation queue:

  For the following PDBs: CDB$ROOT PDB$SEED PDBDEV

    Nothing to roll back

    The following patches will be applied:

      22291127 (Database Patch Set Update : 12.1.0.2.160419 (22291127))

Installing patches…

Patch installation complete.  Total patches installed: 3

Validating logfiles…

Patch 22291127 apply (pdb CDB$ROOT): SUCCESS

  logfile: /home/oracle/app/oracle/cfgtoollogs/sqlpatch/22291127/19694308/22291127_apply_CDBDEV_CDBROOT_2016May13_08_52_30.log (no errors)

Patch 22291127 apply (pdb PDB$SEED): SUCCESS

  logfile: /home/oracle/app/oracle/cfgtoollogs/sqlpatch/22291127/19694308/22291127_apply_CDBDEV_PDBSEED_2016May13_08_52_51.log (no errors)

Patch 22291127 apply (pdb PDBDEV): SUCCESS

  logfile: /home/oracle/app/oracle/cfgtoollogs/sqlpatch/22291127/19694308/22291127_apply_CDBDEV_PDBDEV_2016May13_08_52_50.log (no errors)

SQL Patching tool complete on Fri May 13 08:53:04 2016

If you are using the Oracle Recovery Manager, the catalog needs to be upgraded. Enter the following command to upgrade it:

$ rman catalog username/password@alias

RMAN> UPGRADE CATALOG;

The datapatch will run the SQL load files in CDB and all PDB’s as well.

So that’s it. We have patched the Oracle 12c with CDB and PDB to the latest PSU.

The post Patching Oracle 12c Multi-tenant to latest PSU now known as DPBP appeared first on EasyOraDBA.

Viewing all 60 articles
Browse latest View live