CREATE OR REPLACE PACKAGE CREER_BMP_PKG AS TYPE LIGNE_DATA IS TABLE OF VARCHAR2(4000) INDEX BY BINARY_INTEGER; TYPE LIGNE_RAW IS TABLE OF RAW(4000) INDEX BY BINARY_INTEGER; FUNCTION GENERER_BMP(p_data IN LIGNE_DATA, p_largeur IN NUMBER, p_hauteur IN NUMBER, p_coul IN NUMBER) RETURN BLOB; PROCEDURE P_GET_DATA (p_img IN OUT NOCOPY BLOB, p_largeur OUT NUMBER, p_hauteur OUT NUMBER, p_data OUT LIGNE_RAW); PROCEDURE CREER_FIC_IMG_IMP_ESCPOS(p_nomficBMP IN VARCHAR2, p_nomficTXT IN VARCHAR2); END; / CREATE OR REPLACE PACKAGE BODY CREER_BMP_PKG AS FUNCTION BIN_TO_DEC (b IN VARCHAR2) RETURN NUMBER IS len NUMBER := LENGTH(b); n NUMBER := 0; BEGIN FOR i IN 1..len LOOP IF SUBSTR(b, len - i +1, 1) = '1' THEN n := n + POWER(2,i-1); END IF; END LOOP; RETURN n; END; FUNCTION FILE2BLOB( p_dir varchar2, p_file_name varchar2) RETURN blob IS file_lob bfile; file_blob blob; BEGIN file_lob := BFILENAME( p_dir, p_file_name ); dbms_lob.open( file_lob, dbms_lob.file_readonly ); dbms_lob.createtemporary( file_blob, TRUE ); IF DBMS_LOB.getlength(file_lob) > 0 THEN dbms_lob.loadfromfile( file_blob, file_lob, dbms_lob.lobmaxsize ); END IF; dbms_lob.close( file_lob ); RETURN file_blob; EXCEPTION WHEN others THEN IF dbms_lob.isopen( file_lob ) = 1 THEN dbms_lob.close( file_lob ); END IF; IF dbms_lob.istemporary( file_blob ) = 1 THEN dbms_lob.freetemporary( file_blob ); END IF; RAISE; END; --- +++++++++++++++++++++++++++++ FUNCTION GENERER_BMP(p_data IN LIGNE_DATA, p_largeur IN NUMBER, p_hauteur IN NUMBER, p_coul IN NUMBER) RETURN BLOB IS v_raw RAW(4000); v_lob BLOB; v_datalen NUMBER; -- Taille des données de l'image v_pallen NUMBER; -- taille de la palette v_palette RAW(1024); v_nbpal RAW(4); v_dec NUMBER; v_pad NUMBER; BEGIN -- http://www.commentcamarche.net/contents/1200-bmp-format-bmp -- p_data.count doit être égal à p_hauteur. IF p_data.COUNT <> p_hauteur THEN RAISE_APPLICATION_ERROR(-20001, 'La hauteur ('||p_hauteur||') est différente du nombre de ligne du tableau de données : '|| p_data.COUNT); END IF; IF p_coul = 2 THEN -- 1 bit par pixel => 1 octet pour 8 pixel --Nb_Octets à rajouter à chaque ligne (Il faut un multiple de 4o par ligne) v_pad := 2 * MOD(4 - MOD(CEIL(p_largeur / 8), 4), 4); v_palette := HEXTORAW('FFFFFF0000000000'); -- 2x4 octets (le 4ème est réservé) v_pallen := 8; /* ELSIF p_coul = 16 THEN v_pad := 0; -- A faire v_palette := HEXTORAW('00000000C0C0C00080808000FFFFFF0080000000FF00000080008000FF00FF000080000000FF000080800000FFFF0000000080000000FF000080800000FFFF00'); */ ELSIF p_coul = 256 THEN v_pad := 0; /* table[0] = white; -- Windows reserves first color for white table[255] = black; -- Windows reserves last color as black printf("Here are \"Safety Palette\" color table hexadecimal rgb entries\n"); printf("for table values 20 through 235.\n"); printf("(The first 20 and last 20 are reserved.)\n\n"); printf("Reference:\n"); printf(" http://msdn.microsoft.com/en-us/library/bb250466(VS.85).aspx\n\n"); for (i = 20; i < 236; i++) { if ((i % 6) == 2) { printf("\n%3d: ", i); } printf(" %02x%02x%02x", table[i].r,table[i].g,table[i].b); } */ FOR v IN 0..5 LOOP FOR r IN 0..5 LOOP FOR b IN 0..5 LOOP v_palette := v_palette || UTL_RAW.SUBSTR(UTL_RAW.cast_from_binary_integer(r*51,2), 1, 1) ||UTL_RAW.SUBSTR(UTL_RAW.cast_from_binary_integer(v*51,2), 1, 1) ||UTL_RAW.SUBSTR(UTL_RAW.cast_from_binary_integer(b*51,2), 1, 1)||HEXTORAW('00'); END LOOP; END LOOP; END LOOP; -- les 40 derniers sont mis à NOIR v_palette := v_palette || HEXTORAW(LPAD('0', 40*8, '0')); v_pallen := 1024; END IF; /* Les images en 2 couleurs utilisent 1 bit par pixel, ce qui signifie qu'un octet permet de coder 8 pixels Les images en 16 couleurs utilisent 4 BITS par pixel, ce qui signifie qu'un octet permet de coder 2 pixels Les images en 256 couleurs utilisent 8 BITS par pixel, ce qui signifie qu'un octet code chaque pixel Les images en couleurs réelles utilisent 24 BITS par pixel, ce qui signifie qu'il faut 3 octets pour coder chaque pixel, en prenant soin de respecter l'ordre de l'alternance bleu, vert et rouge. Chaque ligne de l'image doit comporter un nombre total d'octets qui soit un multiple de 4; si ce n'est pas le cas, la ligne doit être complétée par des 0 de telle manière à respecter ce critère. */ v_nbpal := UTL_RAW.cast_from_binary_integer(p_coul, 2); -- Taille Datas v_datalen := (CEIL(p_largeur/8) + v_pad) * p_hauteur; dbms_lob.createtemporary(v_lob, TRUE ); /* + 14 -- Header + 40 -- Entete + 4 *2 ; -- Palette 2 couleurs. => v_pallen*/ -- header dbms_lob.append( v_lob, UTL_RAW.CONCAT( HEXTORAW('424D') -- BMP WIndows 2o -- La taille totale du fichier en octets (codée sur 4 octets) ex 42 00 00 00 , UTL_RAW.SUBSTR( UTL_RAW.cast_from_binary_integer( v_datalen+14+40+v_pallen, UTL_RAW.little_endian ), 1, 4 ) , HEXTORAW('00000000') -- Un champ réservé (sur 4 octets) -- L'offset de l'image (sur 4 octets), en français décalage, ex 3E 00 00 00 , UTL_RAW.SUBSTR( UTL_RAW.cast_from_binary_integer( 14+40+v_pallen, UTL_RAW.little_endian ), 1, 4 ))); --Entête de l'image /* La taille de l'entête de l'image en octets (codée sur 4 octets). Les valeurs hexadécimales suivantes sont possibles suivant le type de format BMP : 28 pour Windows 3.1x, 95, NT, ... 0C pour OS/2 1.x F0 pour OS/2 2.x La largeur de l'image (sur 4 octets), c'est-à-dire le nombre de pixels horizontalement (width) La hauteur de l'image (sur 4 octets), c'est-à-dire le nombre de pixels verticalement (height) Le nombre de plans (sur 2 octets). Cette valeur vaut toujours 1 La profondeur de codage de la couleur(sur 2 octets), c'est-à-dire le nombre de bits utilisés pour coder la couleur. Cette valeur peut-être égale à 1, 4, 8, 16, 24 ou 32 La méthode de compression (sur 4 octets). 0 lorsque l'image n'est pas compressée, 1 pour un codage RLE de 8 bits par pixel 2 pour un codage RLE de 4 bits par pixel 3 pour un codage bitfields, signifiant que la couleur est codé par un triple masque représenté par la palette La taille totale de l'image en octets (sur 4 octets). La résolution horizontale (sur 4 octets), c'est-à-dire le nombre de pixels par mètre horizontalement La résolution verticale (sur 4 octets), c'est-à-dire le nombre de pixels par mètre verticalement Le nombre de couleurs de la palette (sur 4 octets) Le nombre de couleurs importantes de la palette (sur 4 octets). Ce champ peut être égal à 0 lorsque chaque couleur a son importance. */ dbms_lob.append( v_lob, UTL_RAW.CONCAT( HEXTORAW('28000000') -- Entête 4o , UTL_RAW.SUBSTR( UTL_RAW.cast_from_binary_integer( p_largeur, UTL_RAW.little_endian ), 1, 4 ) -- largeur , UTL_RAW.SUBSTR( UTL_RAW.cast_from_binary_integer( p_hauteur, UTL_RAW.little_endian ), 1, 4 ) -- hauteur , HEXTORAW('0100') -- nombre de plans 2o [Toujours = 1] , HEXTORAW('0100') -- Nb bits de couleur 2o , HEXTORAW('00000000') -- Compression 4o -- Taille totale Image en octec 4o (chaque ligne doit être un multiple de 4 , UTL_RAW.SUBSTR( UTL_RAW.cast_from_binary_integer( v_datalen, UTL_RAW.little_endian ), 1, 4 ) , HEXTORAW('130B0000') -- résolution horizontale 4o (Nb pixel/mètre) = #0B13 = 2835 px/m => 2835 / 39.3701 => 72dpi (pixel par inch) , HEXTORAW('130B0000') -- résolution verticale 4o (Nb pixel/mètre) , v_nbpal --Le nombre de couleurs de la palette (sur 4 octets) ex HEXTORAW('02000000') , v_nbpal -- Le nombre de couleurs importantes de la palette (sur 4 octets). Ce champ peut être égal à 0 lorsque chaque couleur a son importance. HEXTORAW('02000000') , v_palette)); -- Palettes de l'image (sur 2x4 octets NOIR,BLANC) /*Codage de l'image Le codage de l'image se fait en écrivant successivement les bits correspondant à chaque pixel, ligne par ligne en commençant par le pixel en bas à gauche. Les images en 2 couleurs utilisent 1 bit par pixel, ce qui signifie qu'un octet permet de coder 8 pixels Les images en 16 couleurs utilisent 4 bits par pixel, ce qui signifie qu'un octet permet de coder 2 pixels Les images en 256 couleurs utilisent 8 bits par pixel, ce qui signifie qu'un octet code chaque pixel Les images en couleurs réelles utilisent 24 bits par pixel, ce qui signifie qu'il faut 3 octets pour coder chaque pixel, en prenant soin de respecter l'ordre de l'alternance bleu, vert et rouge. Chaque ligne de l'image doit comporter un nombre total d'octets qui soit un multiple de 4; si ce n'est pas le cas, la ligne doit être complétée par des 0 de telle manière à respecter ce critère. */ FOR l IN p_data.FIRST .. p_data.LAST LOOP v_raw := NULL; FOR k IN 1 .. CEIL(p_largeur/8) LOOP v_dec := BIN_TO_DEC(RPAD(SUBSTR(p_data(l), 8*(k-1)+1, 8), 8, '0')); v_raw := v_raw || UTL_RAW.SUBSTR( UTL_RAW.cast_from_binary_integer( v_dec, UTL_RAW.little_endian ), 1, 1); END LOOP; IF v_pad > 0 THEN v_raw := v_raw || HEXTORAW(LPAD('0', v_pad, '0')); END IF; -- Multiple de 4 dbms_lob.append( v_lob, v_raw); END LOOP; RETURN v_lob; END GENERER_BMP; --- +++++++++++++++++++++++++++++ FUNCTION BLOB2NUM( p_blob BLOB, p_len INTEGER, p_pos INTEGER ) RETURN NUMBER IS rv NUMBER; BEGIN rv := UTL_RAW.cast_to_binary_integer( dbms_lob.substr( p_blob, p_len, p_pos ), UTL_RAW.little_endian ); IF rv < 0 THEN rv := rv + 4294967296; END IF; RETURN rv; END; --------------- PROCEDURE P_GET_DATA (p_img IN OUT NOCOPY BLOB, p_largeur OUT NUMBER, p_hauteur OUT NUMBER, p_data OUT LIGNE_RAW) IS c_debutBMP CONSTANT raw(2) := HEXTORAW('424D'); t_ind integer; v_linelength INTEGER; BEGIN IF NVL(dbms_lob.substr(p_img, 2, 1), HEXTORAW('11')) <> c_debutBMP THEN RAISE_APPLICATION_ERROR(-20001, 'Pas un fichier BMP'); END IF; -- Vérifier Nb couleur IF NVL(dbms_lob.substr(p_img, 2, 29), HEXTORAW('11')) <> HEXTORAW('0100') THEN RAISE_APPLICATION_ERROR(-20002, 'Pas un fichier BMP 2 couleurs. Compté '|| dbms_lob.substr(p_img, 2, 29)); END IF; -- On récupère l'offset de l'image (11-14) t_ind := blob2num(p_img, 4, 11) + 1; p_largeur := blob2num(p_img, 4, 19); p_hauteur := blob2num(p_img, 4, 23); -- Les images BMP sont codés sur 8 bits, et par groupe de 4 octet v_linelength := CEIL(p_largeur/8) + MOD(4 - MOD(CEIL(p_largeur / 8), 4), 4); FOR i IN 1 .. p_hauteur LOOP p_data(i) := dbms_lob.substr(p_img, v_linelength, t_ind); t_ind := t_ind + v_linelength; END LOOP; END P_GET_DATA; PROCEDURE CREER_FIC_IMG_IMP_ESCPOS(p_nomficBMP IN VARCHAR2, p_nomficTXT IN VARCHAR2) IS -- Création d'un fichier txt (p_nomficTXT) à partir d'un fichier BMP Noir&Blanc (p_nomficBMP) à envoyer sur une imprimante Ticket ESC/POS v_fileLob BLOB; p_largeur NUMBER; p_hauteur NUMBER; v_raw RAW(32); p_rawdata CREER_BMP_PKG.LIGNE_RAW; v_nbparam INTEGER; t_fh UTL_FILE.file_type; t_len PLS_INTEGER := 32767; BEGIN v_fileLob := FILE2BLOB('MYDIR', p_nomficBMP); P_GET_DATA (v_fileLob, p_largeur, p_hauteur, p_rawdata); dbms_lob.freetemporary(v_fileLob); --dbms_output.put_line(p_largeur ||'x'|| p_hauteur); DBMS_LOB.CREATETEMPORARY(v_fileLob, TRUE ); v_nbparam := 11 + p_hauteur * CEIL(p_largeur/8); v_raw := UTL_RAW.CAST_TO_RAW(CHR(29)||'(L' || CHR(MOD(v_nbparam, 256)) || CHR(TRUNC(v_nbparam/256)) -- pL pH || CHR(48) || CHR(67) || CHR(48) -- Fonction (en dur) || CHR(32)|| CHR(32) -- kc1 kc2 || CHR(1) -- Nb de couleurs || CHR(MOD(p_largeur,256)) || CHR(TRUNC(p_largeur/256)) -- xL xH || CHR(MOD(p_hauteur,256)) || CHR(TRUNC(p_hauteur/256)) -- yL yH || CHR(49)); -- dur coul noir DBMS_LOB.APPEND (v_fileLob, v_raw); FOR i IN 1.. p_hauteur LOOP DBMS_LOB.APPEND (v_fileLob, dbms_lob.SUBSTR(p_rawdata(p_hauteur + 1 - i), CEIL(p_largeur/8))); END LOOP; t_fh := UTL_FILE.fopen( 'MYDIR', p_nomficTXT, 'wb' ); FOR i IN 0 .. TRUNC( ( dbms_lob.getlength( v_fileLob ) - 1 ) / t_len ) LOOP UTL_FILE.put_raw( t_fh, dbms_lob.substr( v_fileLob, t_len, i * t_len + 1 ) ); END LOOP; UTL_FILE.fclose( t_fh ); dbms_lob.freetemporary(v_fileLob); END; END CREER_BMP_PKG;