356 lines
9.4 KiB
Python
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
|
|
|