hydrus/include/ClientSerialisable.py

356 lines
9.4 KiB
Python

from . import ClientConstants as CC
from . import ClientImageHandling
from . import ClientImporting
from . import ClientParsing
from . import ClientPaths
import collections
import cv2
from . import HydrusConstants as HC
from . import HydrusData
from . import HydrusGlobals as HG
from . import HydrusPaths
from . import HydrusSerialisable
import numpy
import os
import shutil
import struct
import wx
if cv2.__version__.startswith( '2' ):
IMREAD_UNCHANGED = cv2.CV_LOAD_IMAGE_UNCHANGED
else:
IMREAD_UNCHANGED = cv2.IMREAD_UNCHANGED
def CreateTopImage( width, title, payload_description, text ):
text_extent_bmp = HG.client_controller.bitmap_manager.GetBitmap( 20, 20, 24 )
dc = wx.MemoryDC( text_extent_bmp )
text_font = wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT )
basic_font_size = text_font.GetPointSize()
payload_description_font = wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT )
payload_description_font.SetPointSize( int( basic_font_size * 1.4 ) )
title_font = wx.SystemSettings.GetFont( wx.SYS_DEFAULT_GUI_FONT )
title_font.SetPointSize( int( basic_font_size * 2.0 ) )
texts_to_draw = []
current_y = 6
for ( t, f ) in ( ( title, title_font ), ( payload_description, payload_description_font ), ( text, text_font ) ):
dc.SetFont( f )
wrapped_texts = WrapText( dc, t, width - 20 )
( gumpf, line_height ) = dc.GetTextExtent( 'abcdefghijklmnopqrstuvwxyz' )
wrapped_texts_with_ys = []
if len( wrapped_texts ) > 0:
current_y += 10
for wrapped_text in wrapped_texts:
wrapped_texts_with_ys.append( ( wrapped_text, current_y ) )
current_y += line_height + 4
texts_to_draw.append( ( wrapped_texts_with_ys, f ) )
current_y += 6
top_height = current_y
del dc
del text_extent_bmp
#
top_bmp = HG.client_controller.bitmap_manager.GetBitmap( width, top_height, 24 )
dc = wx.MemoryDC( top_bmp )
dc.SetBackground( wx.Brush( wx.WHITE ) )
dc.Clear()
#
dc.DrawBitmap( CC.GlobalBMPs.file_repository, width - 16 - 5, 5 )
#
for ( wrapped_texts_with_ys, f ) in texts_to_draw:
dc.SetFont( f )
for ( wrapped_text, y ) in wrapped_texts_with_ys:
( t_width, t_height ) = dc.GetTextExtent( wrapped_text )
dc.DrawText( wrapped_text, ( width - t_width ) // 2, y )
del dc
data_bytearray = top_bmp.ConvertToImage().GetData()
data_bytes = bytes( data_bytearray )
top_image_rgb = numpy.fromstring( data_bytes, dtype = 'uint8' ).reshape( ( top_height, width, 3 ) )
HG.client_controller.bitmap_manager.ReleaseBitmap( top_bmp )
top_image = cv2.cvtColor( top_image_rgb, cv2.COLOR_RGB2GRAY )
top_height_header = struct.pack( '!H', top_height )
byte0 = top_height_header[0:1]
byte1 = top_height_header[1:2]
top_image[0][0] = ord( byte0 )
top_image[0][1] = ord( byte1 )
return top_image
def DumpToPng( width, payload_bytes, title, payload_description, text, path ):
payload_bytes_length = len( payload_bytes )
header_and_payload_bytes_length = payload_bytes_length + 4
payload_height = int( header_and_payload_bytes_length / width )
if ( header_and_payload_bytes_length / width ) % 1.0 > 0:
payload_height += 1
top_image = CreateTopImage( width, title, payload_description, text )
payload_length_header = struct.pack( '!I', payload_bytes_length )
num_empty_bytes = payload_height * width - header_and_payload_bytes_length
header_and_payload_bytes = payload_length_header + payload_bytes + b'\x00' * num_empty_bytes
payload_image = numpy.fromstring( header_and_payload_bytes, dtype = 'uint8' ).reshape( ( payload_height, width ) )
finished_image = numpy.concatenate( ( top_image, payload_image ) )
# this is to deal with unicode paths, which cv2 can't handle
( os_file_handle, temp_path ) = HydrusPaths.GetTempPath( suffix = '.png' )
try:
cv2.imwrite( temp_path, finished_image, [ cv2.IMWRITE_PNG_COMPRESSION, 9 ] )
shutil.copy2( temp_path, path )
except Exception as e:
HydrusData.ShowException( e )
raise Exception( 'Could not save the png!' )
finally:
HydrusPaths.CleanUpTempPath( os_file_handle, temp_path )
def GetPayloadBytes( payload_obj ):
if isinstance( payload_obj, bytes ):
return payload_obj
elif isinstance( payload_obj, str ):
return bytes( payload_obj, 'utf-8' )
else:
return payload_obj.DumpToNetworkBytes()
def GetPayloadTypeString( payload_obj ):
if isinstance( payload_obj, bytes ):
return 'Bytes'
elif isinstance( payload_obj, str ):
return 'String'
elif isinstance( payload_obj, HydrusSerialisable.SerialisableList ):
type_string_counts = collections.Counter()
for o in payload_obj:
type_string_counts[ GetPayloadTypeString( o ) ] += 1
type_string = ', '.join( ( HydrusData.ToHumanInt( count ) + ' ' + s for ( s, count ) in list(type_string_counts.items()) ) )
return 'A list of ' + type_string
elif isinstance( payload_obj, HydrusSerialisable.SerialisableBase ):
return payload_obj.SERIALISABLE_NAME
else:
return repr( type( payload_obj ) )
def GetPayloadDescriptionAndBytes( payload_obj ):
payload_bytes = GetPayloadBytes( payload_obj )
payload_description = GetPayloadTypeString( payload_obj ) + ' - ' + HydrusData.ToHumanBytes( len( payload_bytes ) )
return ( payload_description, payload_bytes )
def LoadFromPng( path ):
# this is to deal with unicode paths, which cv2 can't handle
( os_file_handle, temp_path ) = HydrusPaths.GetTempPath()
try:
shutil.copy2( path, temp_path )
numpy_image = cv2.imread( temp_path, flags = IMREAD_UNCHANGED )
except Exception as e:
HydrusData.ShowException( e )
raise Exception( 'That did not appear to be a valid image!' )
finally:
HydrusPaths.CleanUpTempPath( os_file_handle, temp_path )
try:
try:
( height, width ) = numpy_image.shape
except:
raise Exception( 'The file did not appear to be monochrome!' )
try:
complete_data = numpy_image.tostring()
top_height_header = complete_data[:2]
( top_height, ) = struct.unpack( '!H', top_height_header )
payload_and_header_bytes = complete_data[ width * top_height : ]
except:
raise Exception( 'Header bytes were invalid!' )
try:
payload_length_header = payload_and_header_bytes[:4]
( payload_bytes_length, ) = struct.unpack( '!I', payload_length_header )
payload_bytes = payload_and_header_bytes[ 4 : 4 + payload_bytes_length ]
except:
raise Exception( 'Payload bytes were invalid!' )
except Exception as e:
message = 'The image loaded, but it did not seem to be a hydrus serialised png! The error was: {}'.format( str( e ) )
message += os.linesep * 2
message += 'If you believe this is a legit non-resized, non-converted hydrus serialised png, please send it to hydrus_dev.'
raise Exception( message )
return payload_bytes
def TextExceedsWidth( dc, text, width ):
( t_width, t_height ) = dc.GetTextExtent( text )
return t_width > width
def WrapText( dc, text, width ):
words = text.split( ' ' )
lines = []
next_line = []
for word in words:
if word == '':
continue
potential_next_line = list( next_line )
potential_next_line.append( word )
if TextExceedsWidth( dc, ' '.join( potential_next_line ), width ):
if len( potential_next_line ) == 1: # one very long word
lines.append( ' '.join( potential_next_line ) )
next_line = []
else:
lines.append( ' '.join( next_line ) )
next_line = [ word ]
else:
next_line = potential_next_line
if len( next_line ) > 0:
lines.append( ' '.join( next_line ) )
return lines