Wenn man sich die Spezifikationen der gängigen Graphikformate wie JPG,
GIF und PNG anschaut, sieht man, dass Breite und Höhe relativ leicht
aus den Rohdaten der Graphik zu ermitteln sind. Bei PNG und GIF gibt
es einen festen Offset ab Beginn der Datei, in welchem Breite und Höhe
in Form von Zwei-Byte-Integern immer stehen müssen. Bei JPG ist es
etwas anders. Es gibt sogenannte Chunks (Blöcke) von verschiedener
Art, die durch ein führendes Byte gekennzeichnet sind, den Chunktyp.
Jeder Chunk enthält am Anfang, nach dem Chunktyp, den Abstand zum
nächsten Chunk. Unter Verwendung dieser Abstände kann man der Reihe
nach alle Chunks identifizieren, bis man den gewünschten gefunden hat
- der sich irgendwo befinden kann, es gibt keine feste Reihenfolge.
Das Byte 'C0' steht für den Chunktyp, der die Breite und Höhe
enthält.
Mit ABAP ist es ein Leichtes, Binärdaten zu lesen, zu verarbeiten und
zu durchsuchen. Vielleicht ist unser Algorithmus auch anderen einmal
von Nutzen. Der folgende Test-Report ist self-contained und enthält
eine Routine GET_IMAGE_SIZE, die aus Graphiken des Formats JPG, GIF
oder PNG deren Breite und Höhe extrahiert. Wir benutzen in einer
demnächst live gehenden BSP-Applikation eine ähnlich gebaute Methode.
Wir wissen, dass wir es aktuell nur mit diesen drei Graphikformaten zu
tun haben, können jedoch - bei späterem Bedarf - die Methode leicht um
weitere Formate ergänzen.
* -----------------------------------------------------------
* Dimensionen eines Bildes ermitteln
* -----------------------------------------------------------
report zz_test_dimensions.
* Unbekanntes oder noch nicht implementiertes Bildformat
class lcx_unknown_type definition inheriting from cx_static_check.
endclass. "lcx_unknown_type DEFINITION
parameters: p_file type text128, " Filename
p_type type text30. " MIME-Type, z.B. image/png
start-of-selection.
perform test_get_image_size.
* --- Das angegebene File vom Frontend einlesen
* und der Routine get_image_size vorlegen
form test_get_image_size.
data:
* Nötig, weil SAP-Methode GUI_UPLOAD
* nicht den generischen Typ CSEQUENCE verwendet:
lv_filename type string,
* Binärdatei als XSTRING:
lv_content type xstring,
* Irgendeine Tabelle mit RAW-Zeilentyp:
* Nötig, weil SAP-Methode GUI_UPLOAD
* keinen XSTRING zurückliefert
lt_data type swxmlcont,
lv_width type i,
lv_height type i.
field-symbols: <lv_data> type x255. " Zeile der RAW-Tabelle lt_data
lv_filename = p_file.
call method cl_gui_frontend_services=>gui_upload
exporting
filename = lv_filename
filetype = 'BIN'
changing
* Schade, dass wir keinen XSTRING zurückbekommen!
data_tab = lt_data
exceptions
others = 1.
if sy-subrc <> 0.
message 'Fehler beim Lesen der Datei' type 'I'.
else.
* Den XSTRING müssen wir leider selbst zusammenbauen
loop at lt_data assigning <lv_data>.
concatenate lv_content <lv_data> into lv_content in byte mode.
endloop.
try.
perform get_image_size using lv_content p_type
changing lv_width lv_height.
* Ergebnis ausgeben
write: / 'Width: ', lv_width,
/ 'Height:', lv_height.
catch lcx_unknown_type.
message 'Unknown type' type 'I'.
endtry.
endif.
endform. "test_get_image_size
* --- Breite und Höhe eines Bildes ermitteln
* Code kann als Vorlage für eine passende Klasse verwendet werden
form get_image_size using iv_content type xstring
iv_mime_type type csequence
changing ev_width type i
ev_height type i
raising lcx_unknown_type.
*---------------------------------------------------------------------
* Die Masse eines Bildes können aus dem Bild-Content ausgelesen
werden.
* GIF: http://en.wikipedia.org/wiki/Graphics_Interchange_Format
* PNG: http://www.libpng.org/pub/png/spec/1.1/PNG-Structure.html
* JPG: http://www.64lines.com/jpeg-width-height
*---------------------------------------------------------------------
data: lv_content_length type i, " Gesamtlänge des XSTRING
lv_offset type i, " aktuell untersuchte Position
lv_chunk_length type i, " für JPG
lv_chunk_offset type i, " für JPG
lv_chunk_type type x. " für JPG
clear: ev_width, ev_height.
case iv_mime_type.
when 'image/gif'.
if iv_content(6) eq '474946383961'. " Magic number für GIF89a
* Fester Offset 6-7 fpr Breite und 8-9 für Höhe
ev_width = iv_content+7(1) * 256 + iv_content+6(1).
ev_height = iv_content+9(1) * 256 + iv_content+8(1).
else.
raise exception type lcx_unknown_type.
endif.
when 'image/png'.
if iv_content(8) eq '89504E470D0A1A0A'. " Magic number für PNG
* Fester Offset 16-19 für Breite und 20-23 für Höhe
* Wir verwenden die implizite Move-Konvertierung X -> I (Byte Order!)
ev_width = iv_content+16(4).
ev_height = iv_content+20(4).
else.
raise exception type lcx_unknown_type.
endif.
when 'image/jpeg' or 'image/jpg'.
lv_content_length = xstrlen( iv_content ).
if iv_content(4) eq 'FFD8FFE0'. " Magic number für JPG
lv_chunk_offset = 2.
do.
* Ein Chunk beginnt fix mit dem Byte FF,
* danach folgt der Chunk Type
if lv_chunk_offset ge lv_content_length
or iv_content+lv_chunk_offset(1) ne 'FF'.
exit.
endif.
lv_offset = lv_chunk_offset + 1.
lv_chunk_type = iv_content+lv_offset.
if lv_chunk_type eq 'C0'.
* Chunk vom Typ C0 erreicht
* Höhe bei Offset 5-6
lv_offset = lv_chunk_offset + 5.
ev_height = iv_content+lv_offset(1) * 256.
add 1 to lv_offset.
ev_height = ev_height + iv_content+lv_offset(1).
* ... und Breite bei Offset 7-8
add 1 to lv_offset.
ev_width = iv_content+lv_offset(1) * 256.
add 1 to lv_offset.
ev_width = ev_width + iv_content+lv_offset(1).
exit.
endif.
* Nächster Chunk
add 1 to lv_offset.
lv_chunk_length = 256 * iv_content+lv_offset(1).
add 1 to lv_offset.
lv_chunk_length = lv_chunk_length + iv_content+lv_offset(1).
lv_chunk_offset = lv_chunk_offset + lv_chunk_length + 2.
enddo.
else.
raise exception type lcx_unknown_type.
endif.
when others.
raise exception type lcx_unknown_type.
endcase.
endform. "get_image_size