2018-04-19 15:47:41 +02:00
'''
2018-07-11 17:38:47 +02:00
Revealer
2018-12-10 22:05:39 +01:00
Do you have something to hide ?
Secret backup plug - in for the electrum wallet .
2018-04-19 15:47:41 +02:00
Tiago Romagnani Silveira , 2017
2018-12-10 22:05:39 +01:00
2018-04-19 15:47:41 +02:00
'''
import os
import random
import qrcode
import traceback
from hashlib import sha256
2018-06-04 17:07:50 +02:00
from decimal import Decimal
2018-08-22 22:25:12 +02:00
import binascii
2018-04-19 15:47:41 +02:00
from PyQt5 . QtPrintSupport import QPrinter
2018-07-11 17:38:47 +02:00
from electrum . plugin import BasePlugin , hook
2018-04-19 15:47:41 +02:00
from electrum . i18n import _
2018-05-28 14:22:54 +02:00
from electrum . util import to_bytes , make_dir
2018-07-11 17:38:47 +02:00
from electrum . gui . qt . util import *
from electrum . gui . qt . qrtextedit import ScanQRTextEdit
2018-12-10 22:05:39 +01:00
from electrum . gui . qt . main_window import StatusBarButton
2018-07-11 17:38:47 +02:00
2018-08-22 22:25:12 +02:00
from . hmac_drbg import DRBG
2018-04-19 15:47:41 +02:00
class Plugin ( BasePlugin ) :
def __init__ ( self , parent , config , name ) :
BasePlugin . __init__ ( self , parent , config , name )
self . base_dir = config . electrum_path ( ) + ' /revealer/ '
2018-07-22 19:40:10 +02:00
if self . config . get ( ' calibration_h ' ) is None :
2018-04-19 15:47:41 +02:00
self . config . set_key ( ' calibration_h ' , 0 )
2018-07-22 19:40:10 +02:00
if self . config . get ( ' calibration_v ' ) is None :
2018-04-19 15:47:41 +02:00
self . config . set_key ( ' calibration_v ' , 0 )
self . calibration_h = self . config . get ( ' calibration_h ' )
self . calibration_v = self . config . get ( ' calibration_v ' )
2018-08-22 22:25:12 +02:00
self . version = ' 1 '
2018-04-19 15:47:41 +02:00
self . size = ( 159 , 97 )
self . f_size = QSize ( 1014 * 2 , 642 * 2 )
self . abstand_h = 21
self . abstand_v = 34
self . calibration_noise = int ( ' 10 ' * 128 )
self . rawnoise = False
2018-05-28 14:22:54 +02:00
make_dir ( self . base_dir )
2018-04-19 15:47:41 +02:00
@hook
2018-12-10 22:05:39 +01:00
def revealer_hook ( self , parent ) :
parent . addPermanentWidget ( StatusBarButton ( QIcon ( ' :icons/revealer.png ' ) , " Revealer " + _ ( " secret backup utility " ) , partial ( self . setup_dialog , parent ) ) )
2018-04-19 15:47:41 +02:00
def requires_settings ( self ) :
return True
def settings_widget ( self , window ) :
return EnterButton ( _ ( ' Printer Calibration ' ) , partial ( self . calibration_dialog , window ) )
2018-12-10 22:05:39 +01:00
def password_dialog ( self , msg = None , parent = None ) :
from electrum . gui . qt . password_dialog import PasswordDialog
parent = parent or self
d = PasswordDialog ( parent , msg )
return d . run ( )
def get_seed ( self ) :
password = None
if self . wallet . has_keystore_encryption ( ) :
password = self . password_dialog ( parent = self . d . parent ( ) )
if not password :
return
keystore = self . wallet . get_keystore ( )
try :
self . cseed = keystore . get_seed ( password )
if keystore . get_passphrase ( password ) :
self . extension = True
except Exception :
traceback . print_exc ( file = sys . stdout )
return
2018-04-19 15:47:41 +02:00
def setup_dialog ( self , window ) :
2018-12-10 22:05:39 +01:00
self . wallet = window . parent ( ) . wallet
self . update_wallet_name ( self . wallet )
2018-04-19 15:47:41 +02:00
self . user_input = False
self . noise_seed = False
2018-12-10 22:05:39 +01:00
self . d = WindowModalDialog ( window , " Setup Dialog " )
self . d . setMinimumWidth ( 500 )
self . d . setMinimumHeight ( 210 )
self . d . setMaximumHeight ( 320 )
self . d . setContentsMargins ( 11 , 11 , 1 , 1 )
self . hbox = QHBoxLayout ( self . d )
vbox = QVBoxLayout ( )
2018-04-19 15:47:41 +02:00
logo = QLabel ( )
2018-12-10 22:05:39 +01:00
self . hbox . addWidget ( logo )
2018-04-19 15:47:41 +02:00
logo . setPixmap ( QPixmap ( ' :icons/revealer.png ' ) )
2018-12-10 22:05:39 +01:00
logo . setAlignment ( Qt . AlignLeft )
self . hbox . addSpacing ( 16 )
vbox . addWidget ( WWLabel ( " <b> " + _ ( " Revealer Secret Backup Plugin " ) + " </b><br> "
+ _ ( " To encrypt your backup, first we need to load some noise. " ) + " </br> " ) )
vbox . addSpacing ( 7 )
bcreate = QPushButton ( _ ( " Create a new Revealer " ) )
bcreate . setMaximumWidth ( 181 )
bcreate . setDefault ( True )
vbox . addWidget ( bcreate , Qt . AlignCenter )
2018-04-19 15:47:41 +02:00
self . load_noise = ScanQRTextEdit ( )
self . load_noise . setTabChangesFocus ( True )
self . load_noise . textChanged . connect ( self . on_edit )
self . load_noise . setMaximumHeight ( 33 )
2018-12-10 22:05:39 +01:00
self . hbox . addLayout ( vbox )
vbox . addWidget ( WWLabel ( _ ( " or type a existing revealer code below and click ' next ' : " ) ) )
2018-04-19 15:47:41 +02:00
vbox . addWidget ( self . load_noise )
2018-12-10 22:05:39 +01:00
vbox . addSpacing ( 3 )
2018-04-19 15:47:41 +02:00
self . next_button = QPushButton ( _ ( " Next " ) , self . d )
self . next_button . setEnabled ( False )
vbox . addLayout ( Buttons ( self . next_button ) )
self . next_button . clicked . connect ( self . d . close )
self . next_button . clicked . connect ( partial ( self . cypherseed_dialog , window ) )
2018-12-10 22:05:39 +01:00
vbox . addWidget (
QLabel ( " <b> " + _ ( " Warning " ) + " </b>: " + _ ( " Each revealer should be used only once. " )
+ " <br> " + _ ( " more info at https://revealer.cc/faq " ) ) )
2018-04-19 15:47:41 +02:00
def mk_digital ( ) :
try :
self . make_digital ( self . d )
except Exception :
traceback . print_exc ( file = sys . stdout )
else :
self . cypherseed_dialog ( window )
bcreate . clicked . connect ( mk_digital )
return bool ( self . d . exec_ ( ) )
def get_noise ( self ) :
text = self . load_noise . text ( )
return ' ' . join ( text . split ( ) ) . lower ( )
def on_edit ( self ) :
s = self . get_noise ( )
b = self . is_noise ( s )
if b :
2018-08-22 22:25:12 +02:00
self . noise_seed = s [ 1 : - 3 ]
2018-04-19 15:47:41 +02:00
self . user_input = True
self . next_button . setEnabled ( b )
def code_hashid ( self , txt ) :
x = to_bytes ( txt , ' utf8 ' )
hash = sha256 ( x ) . hexdigest ( )
return hash [ - 3 : ] . upper ( )
def is_noise ( self , txt ) :
if ( len ( txt ) > = 34 ) :
try :
int ( txt , 16 )
except :
self . user_input = False
return False
else :
id = self . code_hashid ( txt [ : - 3 ] )
if ( txt [ - 3 : ] . upper ( ) == id . upper ( ) ) :
self . code_id = id
self . user_input = True
return True
else :
return False
else :
2018-08-22 22:25:12 +02:00
if ( len ( txt ) > 0 and txt [ 0 ] == ' 0 ' ) :
self . d . show_message ( ' ' . join ( [ " <b> " , _ ( " Warning: " ) , " </b> " , _ ( " Revealers starting with 0 had a vulnerability and are not supported. " ) ] ) )
2018-04-19 15:47:41 +02:00
self . user_input = False
return False
def make_digital ( self , dialog ) :
self . make_rawnoise ( True )
self . bdone ( dialog )
self . d . close ( )
def bcrypt ( self , dialog ) :
self . rawnoise = False
dialog . show_message ( ' ' . join ( [ _ ( " {} encrypted for Revealer {} _ {} saved as PNG and PDF at: " ) . format ( self . was , self . version , self . code_id ) ,
" <br/> " , " <b> " , self . base_dir + self . filename + self . version + " _ " + self . code_id , " </b> " ] ) )
dialog . close ( )
2018-07-18 13:15:31 +02:00
def ext_warning ( self , dialog ) :
2018-12-10 22:05:39 +01:00
dialog . show_message ( ' ' . join ( [ " <b> " , _ ( " Warning: " ) , " </b> " , _ ( " your seed extension will " ) + " <b> " + _ ( " not " ) + " </b> " + _ ( " be included in the encrypted backup. " ) ] ) )
2018-07-18 13:15:31 +02:00
dialog . close ( )
2018-04-19 15:47:41 +02:00
def bdone ( self , dialog ) :
dialog . show_message ( ' ' . join ( [ _ ( " Digital Revealer ( {} _ {} ) saved as PNG and PDF at: " ) . format ( self . version , self . code_id ) ,
" <br/> " , " <b> " , self . base_dir + ' revealer_ ' + self . version + ' _ ' + self . code_id , ' </b> ' ] ) )
def customtxt_limits ( self ) :
txt = self . text . text ( )
self . max_chars . setVisible ( False )
self . char_count . setText ( " ( " + str ( len ( txt ) ) + " /216) " )
if len ( txt ) > 0 :
self . ctext . setEnabled ( True )
if len ( txt ) > 216 :
self . text . setPlainText ( self . text . toPlainText ( ) [ : 216 ] )
self . max_chars . setVisible ( True )
def t ( self ) :
self . txt = self . text . text ( )
self . seed_img ( is_seed = False )
def cypherseed_dialog ( self , window ) :
2018-12-10 22:05:39 +01:00
d = WindowModalDialog ( window , " Encryption Dialog " )
d . setMinimumWidth ( 500 )
d . setMinimumHeight ( 210 )
d . setMaximumHeight ( 450 )
d . setContentsMargins ( 11 , 11 , 1 , 1 )
2018-04-19 15:47:41 +02:00
self . c_dialog = d
2018-12-10 22:05:39 +01:00
hbox = QHBoxLayout ( d )
self . vbox = QVBoxLayout ( )
2018-04-19 15:47:41 +02:00
logo = QLabel ( )
2018-12-10 22:05:39 +01:00
hbox . addWidget ( logo )
2018-04-19 15:47:41 +02:00
logo . setPixmap ( QPixmap ( ' :icons/revealer.png ' ) )
2018-12-10 22:05:39 +01:00
logo . setAlignment ( Qt . AlignLeft )
hbox . addSpacing ( 16 )
self . vbox . addWidget ( WWLabel ( " <b> " + _ ( " Revealer Secret Backup Plugin " ) + " </b><br> "
+ _ ( " Ready to encrypt for revealer " ) + self . version + ' _ ' + self . code_id ) )
self . vbox . addSpacing ( 11 )
hbox . addLayout ( self . vbox )
2018-04-19 15:47:41 +02:00
grid = QGridLayout ( )
self . vbox . addLayout ( grid )
2018-12-10 22:05:39 +01:00
cprint = QPushButton ( _ ( " Encrypt " ) + self . wallet_name + _ ( " ' s seed " ) )
cprint . setMaximumWidth ( 250 )
2018-04-19 15:47:41 +02:00
cprint . clicked . connect ( partial ( self . seed_img , True ) )
self . vbox . addWidget ( cprint )
2018-12-10 22:05:39 +01:00
self . vbox . addSpacing ( 1 )
self . vbox . addWidget ( WWLabel ( " <b> " + _ ( " OR " ) + " </b> " + _ ( " type a custom alphanumerical secret below: " ) ) )
2018-04-19 15:47:41 +02:00
self . text = ScanQRTextEdit ( )
self . text . setTabChangesFocus ( True )
self . text . setMaximumHeight ( 70 )
self . text . textChanged . connect ( self . customtxt_limits )
self . vbox . addWidget ( self . text )
self . char_count = WWLabel ( " " )
self . char_count . setAlignment ( Qt . AlignRight )
self . vbox . addWidget ( self . char_count )
self . max_chars = WWLabel ( " <font color= ' red ' > " + _ ( " This version supports a maximum of 216 characters. " ) + " </font> " )
self . vbox . addWidget ( self . max_chars )
self . max_chars . setVisible ( False )
2018-12-10 22:05:39 +01:00
self . ctext = QPushButton ( _ ( " Encrypt custom secret " ) )
2018-04-19 15:47:41 +02:00
self . ctext . clicked . connect ( self . t )
self . vbox . addWidget ( self . ctext )
self . ctext . setEnabled ( False )
2018-08-22 22:25:12 +02:00
self . vbox . addSpacing ( 11 )
2018-04-19 15:47:41 +02:00
self . vbox . addLayout ( Buttons ( CloseButton ( d ) ) )
return bool ( d . exec_ ( ) )
def update_wallet_name ( self , name ) :
self . wallet_name = str ( name )
self . base_name = self . base_dir + self . wallet_name
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
def seed_img ( self , is_seed = True ) :
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
if is_seed :
2018-12-10 22:05:39 +01:00
self . get_seed ( )
txt = self . cseed . upper ( )
2018-04-19 15:47:41 +02:00
else :
txt = self . txt . upper ( )
img = QImage ( self . size [ 0 ] , self . size [ 1 ] , QImage . Format_Mono )
bitmap = QBitmap . fromImage ( img , Qt . MonoOnly )
bitmap . fill ( Qt . white )
painter = QPainter ( )
painter . begin ( bitmap )
QFontDatabase . addApplicationFont ( os . path . join ( os . path . dirname ( __file__ ) , ' SourceSansPro-Bold.otf ' ) )
if len ( txt ) < 102 :
2018-08-07 16:33:58 +02:00
fontsize = 15
2018-04-19 15:47:41 +02:00
linespace = 15
max_letters = 17
max_lines = 6
max_words = 3
2018-07-22 19:40:10 +02:00
else :
2018-08-07 16:33:58 +02:00
fontsize = 12
2018-04-19 15:47:41 +02:00
linespace = 10
2018-08-07 16:33:58 +02:00
max_letters = 23
2018-04-19 15:47:41 +02:00
max_lines = 9
max_words = int ( max_letters / 4 )
font = QFont ( ' Source Sans Pro ' , fontsize , QFont . Bold )
font . setLetterSpacing ( QFont . PercentageSpacing , 100 )
2018-08-07 16:33:58 +02:00
font . setPixelSize ( fontsize )
2018-04-19 15:47:41 +02:00
painter . setFont ( font )
seed_array = txt . split ( ' ' )
for n in range ( max_lines ) :
nwords = max_words
temp_seed = seed_array [ : nwords ]
while len ( ' ' . join ( map ( str , temp_seed ) ) ) > max_letters :
nwords = nwords - 1
temp_seed = seed_array [ : nwords ]
painter . drawText ( QRect ( 0 , linespace * n , self . size [ 0 ] , self . size [ 1 ] ) , Qt . AlignHCenter , ' ' . join ( map ( str , temp_seed ) ) )
del seed_array [ : nwords ]
painter . end ( )
img = bitmap . toImage ( )
if ( self . rawnoise == False ) :
self . make_rawnoise ( )
self . make_cypherseed ( img , self . rawnoise , False , is_seed )
return img
def make_rawnoise ( self , create_revealer = False ) :
w = self . size [ 0 ]
h = self . size [ 1 ]
rawnoise = QImage ( w , h , QImage . Format_Mono )
if ( self . noise_seed == False ) :
self . noise_seed = random . SystemRandom ( ) . getrandbits ( 128 )
2018-08-22 22:25:12 +02:00
self . hex_noise = format ( self . noise_seed , ' 032x ' )
2018-04-19 15:47:41 +02:00
self . hex_noise = self . version + str ( self . hex_noise )
if ( self . user_input == True ) :
self . noise_seed = int ( self . noise_seed , 16 )
2018-08-22 22:25:12 +02:00
self . hex_noise = self . version + str ( format ( self . noise_seed , ' 032x ' ) )
2018-04-19 15:47:41 +02:00
self . code_id = self . code_hashid ( self . hex_noise )
self . hex_noise = ' ' . join ( self . hex_noise [ i : i + 4 ] for i in range ( 0 , len ( self . hex_noise ) , 4 ) )
2018-08-22 22:25:12 +02:00
entropy = binascii . unhexlify ( str ( format ( self . noise_seed , ' 032x ' ) ) )
code_id = binascii . unhexlify ( self . version + self . code_id )
2018-12-10 22:05:39 +01:00
print ( self . hex_noise )
2018-08-22 22:25:12 +02:00
drbg = DRBG ( entropy + code_id )
noise_array = bin ( int . from_bytes ( drbg . generate ( 1929 ) , ' big ' ) ) [ 2 : ]
i = 0
2018-04-19 15:47:41 +02:00
for x in range ( w ) :
for y in range ( h ) :
2018-08-22 22:25:12 +02:00
rawnoise . setPixel ( x , y , int ( noise_array [ i ] ) )
i + = 1
2018-04-19 15:47:41 +02:00
self . rawnoise = rawnoise
if create_revealer == True :
self . make_revealer ( )
self . noise_seed = False
def make_calnoise ( self ) :
random . seed ( self . calibration_noise )
w = self . size [ 0 ]
h = self . size [ 1 ]
rawnoise = QImage ( w , h , QImage . Format_Mono )
for x in range ( w ) :
for y in range ( h ) :
rawnoise . setPixel ( x , y , random . randint ( 0 , 1 ) )
self . calnoise = self . pixelcode_2x2 ( rawnoise )
def make_revealer ( self ) :
revealer = self . pixelcode_2x2 ( self . rawnoise )
revealer . invertPixels ( )
revealer = QBitmap . fromImage ( revealer )
2018-08-07 16:33:58 +02:00
revealer = revealer . scaled ( self . f_size , Qt . KeepAspectRatio )
2018-04-19 15:47:41 +02:00
revealer = self . overlay_marks ( revealer )
2018-08-07 16:33:58 +02:00
2018-04-19 15:47:41 +02:00
self . filename = ' Revealer - '
revealer . save ( self . base_dir + self . filename + self . version + ' _ ' + self . code_id + ' .png ' )
self . toPdf ( QImage ( revealer ) )
QDesktopServices . openUrl ( QUrl . fromLocalFile ( os . path . abspath ( self . base_dir + self . filename + self . version + ' _ ' + self . code_id + ' .pdf ' ) ) )
def make_cypherseed ( self , img , rawnoise , calibration = False , is_seed = True ) :
img = img . convertToFormat ( QImage . Format_Mono )
p = QPainter ( )
p . begin ( img )
p . setCompositionMode ( 26 ) #xor
p . drawImage ( 0 , 0 , rawnoise )
p . end ( )
cypherseed = self . pixelcode_2x2 ( img )
cypherseed = QBitmap . fromImage ( cypherseed )
cypherseed = cypherseed . scaled ( self . f_size , Qt . KeepAspectRatio )
cypherseed = self . overlay_marks ( cypherseed , True , calibration )
if not is_seed :
self . filename = _ ( ' custom_secret ' ) + ' _ '
self . was = _ ( ' Custom secret ' )
else :
self . filename = self . wallet_name + ' _ ' + _ ( ' seed ' ) + ' _ '
self . was = self . wallet_name + ' ' + _ ( ' seed ' )
2018-12-10 22:05:39 +01:00
if self . extension :
2018-07-18 13:15:31 +02:00
self . ext_warning ( self . c_dialog )
2018-04-19 15:47:41 +02:00
if not calibration :
self . toPdf ( QImage ( cypherseed ) )
QDesktopServices . openUrl ( QUrl . fromLocalFile ( os . path . abspath ( self . base_dir + self . filename + self . version + ' _ ' + self . code_id + ' .pdf ' ) ) )
cypherseed . save ( self . base_dir + self . filename + self . version + ' _ ' + self . code_id + ' .png ' )
self . bcrypt ( self . c_dialog )
return cypherseed
def calibration ( self ) :
img = QImage ( self . size [ 0 ] , self . size [ 1 ] , QImage . Format_Mono )
bitmap = QBitmap . fromImage ( img , Qt . MonoOnly )
bitmap . fill ( Qt . black )
self . make_calnoise ( )
img = self . overlay_marks ( self . calnoise . scaledToHeight ( self . f_size . height ( ) ) , False , True )
self . calibration_pdf ( img )
QDesktopServices . openUrl ( QUrl . fromLocalFile ( os . path . abspath ( self . base_dir + _ ( ' calibration ' ) + ' .pdf ' ) ) )
2018-07-11 17:38:47 +02:00
return img
2018-04-19 15:47:41 +02:00
def toPdf ( self , image ) :
printer = QPrinter ( )
printer . setPaperSize ( QSizeF ( 210 , 297 ) , QPrinter . Millimeter )
printer . setResolution ( 600 )
printer . setOutputFormat ( QPrinter . PdfFormat )
printer . setOutputFileName ( self . base_dir + self . filename + self . version + ' _ ' + self . code_id + ' .pdf ' )
printer . setPageMargins ( 0 , 0 , 0 , 0 , 6 )
painter = QPainter ( )
painter . begin ( printer )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
delta_h = round ( image . width ( ) / self . abstand_v )
2018-07-11 17:38:47 +02:00
delta_v = round ( image . height ( ) / self . abstand_h )
2018-04-19 15:47:41 +02:00
size_h = 2028 + ( ( int ( self . calibration_h ) * 2028 / ( 2028 - ( delta_h * 2 ) + int ( self . calibration_h ) ) ) / 2 )
size_v = 1284 + ( ( int ( self . calibration_v ) * 1284 / ( 1284 - ( delta_v * 2 ) + int ( self . calibration_v ) ) ) / 2 )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
image = image . scaled ( size_h , size_v )
painter . drawImage ( 553 , 533 , image )
wpath = QPainterPath ( )
wpath . addRoundedRect ( QRectF ( 553 , 533 , size_h , size_v ) , 19 , 19 )
painter . setPen ( QPen ( Qt . black , 1 ) )
painter . drawPath ( wpath )
painter . end ( )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
def calibration_pdf ( self , image ) :
printer = QPrinter ( )
printer . setPaperSize ( QSizeF ( 210 , 297 ) , QPrinter . Millimeter )
printer . setResolution ( 600 )
printer . setOutputFormat ( QPrinter . PdfFormat )
printer . setOutputFileName ( self . base_dir + _ ( ' calibration ' ) + ' .pdf ' )
printer . setPageMargins ( 0 , 0 , 0 , 0 , 6 )
painter = QPainter ( )
painter . begin ( printer )
painter . drawImage ( 553 , 533 , image )
font = QFont ( ' Source Sans Pro ' , 10 , QFont . Bold )
painter . setFont ( font )
painter . drawText ( 254 , 277 , _ ( " Calibration sheet " ) )
font = QFont ( ' Source Sans Pro ' , 7 , QFont . Bold )
painter . setFont ( font )
painter . drawText ( 600 , 2077 , _ ( " Instructions: " ) )
font = QFont ( ' Source Sans Pro ' , 7 , QFont . Normal )
painter . setFont ( font )
painter . drawText ( 700 , 2177 , _ ( " 1. Place this paper on a flat and well iluminated surface. " ) )
painter . drawText ( 700 , 2277 , _ ( " 2. Align your Revealer borderlines to the dashed lines on the top and left. " ) )
painter . drawText ( 700 , 2377 , _ ( " 3. Press slightly the Revealer against the paper and read the numbers that best "
" match on the opposite sides. " ) )
painter . drawText ( 700 , 2477 , _ ( " 4. Type the numbers in the software " ) )
painter . end ( )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
def pixelcode_2x2 ( self , img ) :
result = QImage ( img . width ( ) * 2 , img . height ( ) * 2 , QImage . Format_ARGB32 )
white = qRgba ( 255 , 255 , 255 , 0 )
black = qRgba ( 0 , 0 , 0 , 255 )
for x in range ( img . width ( ) ) :
for y in range ( img . height ( ) ) :
c = img . pixel ( QPoint ( x , y ) )
colors = QColor ( c ) . getRgbF ( )
if colors [ 0 ] :
result . setPixel ( x * 2 + 1 , y * 2 + 1 , black )
result . setPixel ( x * 2 , y * 2 + 1 , white )
result . setPixel ( x * 2 + 1 , y * 2 , white )
result . setPixel ( x * 2 , y * 2 , black )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
else :
result . setPixel ( x * 2 + 1 , y * 2 + 1 , white )
result . setPixel ( x * 2 , y * 2 + 1 , black )
result . setPixel ( x * 2 + 1 , y * 2 , black )
result . setPixel ( x * 2 , y * 2 , white )
return result
def overlay_marks ( self , img , is_cseed = False , calibration_sheet = False ) :
border_color = Qt . white
base_img = QImage ( self . f_size . width ( ) , self . f_size . height ( ) , QImage . Format_ARGB32 )
base_img . fill ( border_color )
img = QImage ( img )
painter = QPainter ( )
painter . begin ( base_img )
total_distance_h = round ( base_img . width ( ) / self . abstand_v )
dist_v = round ( total_distance_h ) / 2
dist_h = round ( total_distance_h ) / 2
img = img . scaledToWidth ( base_img . width ( ) - ( 2 * ( total_distance_h ) ) )
painter . drawImage ( total_distance_h ,
total_distance_h ,
img )
#frame around image
pen = QPen ( Qt . black , 2 )
painter . setPen ( pen )
#horz
painter . drawLine ( 0 , total_distance_h , base_img . width ( ) , total_distance_h )
painter . drawLine ( 0 , base_img . height ( ) - ( total_distance_h ) , base_img . width ( ) , base_img . height ( ) - ( total_distance_h ) )
#vert
painter . drawLine ( total_distance_h , 0 , total_distance_h , base_img . height ( ) )
painter . drawLine ( base_img . width ( ) - ( total_distance_h ) , 0 , base_img . width ( ) - ( total_distance_h ) , base_img . height ( ) )
#border around img
border_thick = 6
Rpath = QPainterPath ( )
2018-07-11 17:38:47 +02:00
Rpath . addRect ( QRectF ( ( total_distance_h ) + ( border_thick / 2 ) ,
( total_distance_h ) + ( border_thick / 2 ) ,
2018-04-19 15:47:41 +02:00
base_img . width ( ) - ( ( total_distance_h ) * 2 ) - ( ( border_thick ) - 1 ) ,
( base_img . height ( ) - ( ( total_distance_h ) ) * 2 ) - ( ( border_thick ) - 1 ) ) )
pen = QPen ( Qt . black , border_thick )
pen . setJoinStyle ( Qt . MiterJoin )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
painter . setPen ( pen )
painter . drawPath ( Rpath )
Bpath = QPainterPath ( )
Bpath . addRect ( QRectF ( ( total_distance_h ) , ( total_distance_h ) ,
base_img . width ( ) - ( ( total_distance_h ) * 2 ) , ( base_img . height ( ) - ( ( total_distance_h ) ) * 2 ) ) )
pen = QPen ( Qt . black , 1 )
painter . setPen ( pen )
painter . drawPath ( Bpath )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
pen = QPen ( Qt . black , 1 )
painter . setPen ( pen )
painter . drawLine ( 0 , base_img . height ( ) / 2 , total_distance_h , base_img . height ( ) / 2 )
painter . drawLine ( base_img . width ( ) / 2 , 0 , base_img . width ( ) / 2 , total_distance_h )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
painter . drawLine ( base_img . width ( ) - total_distance_h , base_img . height ( ) / 2 , base_img . width ( ) , base_img . height ( ) / 2 )
painter . drawLine ( base_img . width ( ) / 2 , base_img . height ( ) , base_img . width ( ) / 2 , base_img . height ( ) - total_distance_h )
#print code
f_size = 37
QFontDatabase . addApplicationFont ( os . path . join ( os . path . dirname ( __file__ ) , ' DejaVuSansMono-Bold.ttf ' ) )
font = QFont ( " DejaVu Sans Mono " , f_size - 11 , QFont . Bold )
2018-08-07 16:33:58 +02:00
font . setPixelSize ( 35 )
2018-04-19 15:47:41 +02:00
painter . setFont ( font )
if not calibration_sheet :
if is_cseed : #its a secret
painter . setPen ( QPen ( Qt . black , 1 , Qt . DashDotDotLine ) )
painter . drawLine ( 0 , dist_v , base_img . width ( ) , dist_v )
painter . drawLine ( dist_h , 0 , dist_h , base_img . height ( ) )
painter . drawLine ( 0 , base_img . height ( ) - dist_v , base_img . width ( ) , base_img . height ( ) - ( dist_v ) )
painter . drawLine ( base_img . width ( ) - ( dist_h ) , 0 , base_img . width ( ) - ( dist_h ) , base_img . height ( ) )
painter . drawImage ( ( ( total_distance_h ) ) + 11 , ( ( total_distance_h ) ) + 11 ,
QImage ( ' :icons/electrumb.png ' ) . scaledToWidth ( 2.1 * ( total_distance_h ) , Qt . SmoothTransformation ) )
painter . setPen ( QPen ( Qt . white , border_thick * 8 ) )
2018-07-11 17:38:47 +02:00
painter . drawLine ( base_img . width ( ) - ( ( total_distance_h ) ) - ( border_thick * 8 ) / 2 - ( border_thick / 2 ) - 2 ,
( base_img . height ( ) - ( ( total_distance_h ) ) ) - ( ( border_thick * 8 ) / 2 ) - ( border_thick / 2 ) - 2 ,
2018-04-19 15:47:41 +02:00
base_img . width ( ) - ( ( total_distance_h ) ) - ( border_thick * 8 ) / 2 - ( border_thick / 2 ) - 2 - 77 ,
( base_img . height ( ) - ( ( total_distance_h ) ) ) - ( ( border_thick * 8 ) / 2 ) - ( border_thick / 2 ) - 2 )
painter . setPen ( QColor ( 0 , 0 , 0 , 255 ) )
painter . drawText ( QRect ( 0 , base_img . height ( ) - 107 , base_img . width ( ) - total_distance_h - border_thick - 11 ,
base_img . height ( ) - total_distance_h - border_thick ) , Qt . AlignRight , self . version + ' _ ' + self . code_id )
painter . end ( )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
else : # revealer
painter . setPen ( QPen ( border_color , 17 ) )
painter . drawLine ( 0 , dist_v , base_img . width ( ) , dist_v )
painter . drawLine ( dist_h , 0 , dist_h , base_img . height ( ) )
painter . drawLine ( 0 , base_img . height ( ) - dist_v , base_img . width ( ) , base_img . height ( ) - ( dist_v ) )
painter . drawLine ( base_img . width ( ) - ( dist_h ) , 0 , base_img . width ( ) - ( dist_h ) , base_img . height ( ) )
painter . setPen ( QPen ( Qt . black , 2 ) )
painter . drawLine ( 0 , dist_v , base_img . width ( ) , dist_v )
painter . drawLine ( dist_h , 0 , dist_h , base_img . height ( ) )
painter . drawLine ( 0 , base_img . height ( ) - dist_v , base_img . width ( ) , base_img . height ( ) - ( dist_v ) )
painter . drawLine ( base_img . width ( ) - ( dist_h ) , 0 , base_img . width ( ) - ( dist_h ) , base_img . height ( ) )
logo = QImage ( ' :icons/revealer_c.png ' ) . scaledToWidth ( 1.3 * ( total_distance_h ) )
painter . drawImage ( ( total_distance_h ) + ( border_thick ) , ( ( total_distance_h ) ) + ( border_thick ) , logo , Qt . SmoothTransformation )
#frame around logo
2018-07-11 17:38:47 +02:00
painter . setPen ( QPen ( Qt . black , border_thick ) )
2018-04-19 15:47:41 +02:00
painter . drawLine ( total_distance_h + border_thick , total_distance_h + logo . height ( ) + 3 * ( border_thick / 2 ) ,
total_distance_h + logo . width ( ) + border_thick , total_distance_h + logo . height ( ) + 3 * ( border_thick / 2 ) )
painter . drawLine ( logo . width ( ) + total_distance_h + 3 * ( border_thick / 2 ) , total_distance_h + ( border_thick ) ,
total_distance_h + logo . width ( ) + 3 * ( border_thick / 2 ) , total_distance_h + logo . height ( ) + ( border_thick ) )
#frame around code/qr
qr_size = 179
painter . drawLine ( ( base_img . width ( ) - ( ( total_distance_h ) ) - ( border_thick / 2 ) - 2 ) - qr_size ,
( base_img . height ( ) - ( ( total_distance_h ) ) ) - ( ( border_thick * 8 ) ) - ( border_thick / 2 ) - 2 ,
( base_img . width ( ) / 2 + ( total_distance_h / 2 ) - border_thick - ( border_thick * 8 ) / 2 ) - qr_size ,
( base_img . height ( ) - ( ( total_distance_h ) ) ) - ( ( border_thick * 8 ) ) - ( border_thick / 2 ) - 2 )
painter . drawLine ( ( base_img . width ( ) / 2 + ( total_distance_h / 2 ) - border_thick - ( border_thick * 8 ) / 2 ) - qr_size ,
( base_img . height ( ) - ( ( total_distance_h ) ) ) - ( ( border_thick * 8 ) ) - ( border_thick / 2 ) - 2 ,
base_img . width ( ) / 2 + ( total_distance_h / 2 ) - border_thick - ( border_thick * 8 ) / 2 - qr_size ,
( ( base_img . height ( ) - ( ( total_distance_h ) ) ) - ( border_thick / 2 ) - 2 ) )
painter . setPen ( QPen ( Qt . white , border_thick * 8 ) )
painter . drawLine (
base_img . width ( ) - ( ( total_distance_h ) ) - ( border_thick * 8 ) / 2 - ( border_thick / 2 ) - 2 ,
( base_img . height ( ) - ( ( total_distance_h ) ) ) - ( ( border_thick * 8 ) / 2 ) - ( border_thick / 2 ) - 2 ,
base_img . width ( ) / 2 + ( total_distance_h / 2 ) - border_thick - qr_size ,
( base_img . height ( ) - ( ( total_distance_h ) ) ) - ( ( border_thick * 8 ) / 2 ) - ( border_thick / 2 ) - 2 )
painter . setPen ( QColor ( 0 , 0 , 0 , 255 ) )
painter . drawText ( QRect ( ( ( base_img . width ( ) / 2 ) + 21 ) - qr_size , base_img . height ( ) - 107 ,
base_img . width ( ) - total_distance_h - border_thick - 93 ,
base_img . height ( ) - total_distance_h - border_thick ) , Qt . AlignLeft , self . hex_noise . upper ( ) )
painter . drawText ( QRect ( 0 , base_img . height ( ) - 107 , base_img . width ( ) - total_distance_h - border_thick - 3 - qr_size ,
base_img . height ( ) - total_distance_h - border_thick ) , Qt . AlignRight , self . code_id )
# draw qr code
qr_qt = self . paintQR ( self . hex_noise . upper ( ) + self . code_id )
target = QRectF ( base_img . width ( ) - 65 - qr_size ,
base_img . height ( ) - 65 - qr_size ,
2018-07-22 19:40:10 +02:00
qr_size , qr_size )
painter . drawImage ( target , qr_qt )
2018-04-19 15:47:41 +02:00
painter . setPen ( QPen ( Qt . black , 4 ) )
painter . drawLine ( base_img . width ( ) - 65 - qr_size ,
base_img . height ( ) - 65 - qr_size ,
base_img . width ( ) - 65 - qr_size ,
( base_img . height ( ) - ( ( total_distance_h ) ) ) - ( ( border_thick * 8 ) ) - ( border_thick / 2 ) - 4
)
painter . drawLine ( base_img . width ( ) - 65 - qr_size ,
base_img . height ( ) - 65 - qr_size ,
base_img . width ( ) - 65 ,
base_img . height ( ) - 65 - qr_size
)
painter . end ( )
else : # calibration only
painter . end ( )
cal_img = QImage ( self . f_size . width ( ) + 100 , self . f_size . height ( ) + 100 ,
QImage . Format_ARGB32 )
cal_img . fill ( Qt . white )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
cal_painter = QPainter ( )
cal_painter . begin ( cal_img )
cal_painter . drawImage ( 0 , 0 , base_img )
2018-07-11 17:38:47 +02:00
#black lines in the middle of border top left only
2018-04-19 15:47:41 +02:00
cal_painter . setPen ( QPen ( Qt . black , 1 , Qt . DashDotDotLine ) )
cal_painter . drawLine ( 0 , dist_v , base_img . width ( ) , dist_v )
cal_painter . drawLine ( dist_h , 0 , dist_h , base_img . height ( ) )
pen = QPen ( Qt . black , 2 , Qt . DashDotDotLine )
cal_painter . setPen ( pen )
n = 15
cal_painter . setFont ( QFont ( " DejaVu Sans Mono " , 21 , QFont . Bold ) )
for x in range ( - n , n ) :
#lines on bottom (vertical calibration)
cal_painter . drawLine ( ( ( ( base_img . width ( ) ) / ( n * 2 ) ) * ( x ) ) + ( base_img . width ( ) / 2 ) - 13 ,
x + 2 + base_img . height ( ) - ( dist_v ) ,
( ( ( base_img . width ( ) ) / ( n * 2 ) ) * ( x ) ) + ( base_img . width ( ) / 2 ) + 13 ,
x + 2 + base_img . height ( ) - ( dist_v ) )
num_pos = 9
if x > 9 : num_pos = 17
if x < 0 : num_pos = 20
if x < - 9 : num_pos = 27
cal_painter . drawText ( ( ( ( base_img . width ( ) ) / ( n * 2 ) ) * ( x ) ) + ( base_img . width ( ) / 2 ) - num_pos ,
50 + base_img . height ( ) - ( dist_v ) ,
str ( x ) )
#lines on the right (horizontal calibrations)
cal_painter . drawLine ( x + 2 + ( base_img . width ( ) - ( dist_h ) ) ,
( ( base_img . height ( ) / ( 2 * n ) ) * ( x ) ) + ( base_img . height ( ) / n ) + ( base_img . height ( ) / 2 ) - 13 ,
x + 2 + ( base_img . width ( ) - ( dist_h ) ) ,
( ( base_img . height ( ) / ( 2 * n ) ) * ( x ) ) + ( base_img . height ( ) / n ) + ( base_img . height ( ) / 2 ) + 13 )
cal_painter . drawText ( 30 + ( base_img . width ( ) - ( dist_h ) ) ,
( ( base_img . height ( ) / ( 2 * n ) ) * ( x ) ) + ( base_img . height ( ) / 2 ) + 13 , str ( x ) )
cal_painter . end ( )
base_img = cal_img
return base_img
def paintQR ( self , data ) :
if not data :
return
qr = qrcode . QRCode ( )
qr . add_data ( data )
matrix = qr . get_matrix ( )
k = len ( matrix )
border_color = Qt . white
base_img = QImage ( k * 5 , k * 5 , QImage . Format_ARGB32 )
base_img . fill ( border_color )
qrpainter = QPainter ( )
qrpainter . begin ( base_img )
boxsize = 5
size = k * boxsize
left = ( base_img . width ( ) - size ) / 2
top = ( base_img . height ( ) - size ) / 2
qrpainter . setBrush ( Qt . black )
qrpainter . setPen ( Qt . black )
for r in range ( k ) :
for c in range ( k ) :
if matrix [ r ] [ c ] :
qrpainter . drawRect ( left + c * boxsize , top + r * boxsize , boxsize - 1 , boxsize - 1 )
qrpainter . end ( )
return base_img
def calibration_dialog ( self , window ) :
d = WindowModalDialog ( window , _ ( " Revealer - Printer calibration settings " ) )
2018-07-11 17:38:47 +02:00
2018-04-19 15:47:41 +02:00
d . setMinimumSize ( 100 , 200 )
vbox = QVBoxLayout ( d )
vbox . addWidget ( QLabel ( ' ' . join ( [ " <br/> " , _ ( " If you have an old printer, or want optimal precision " ) , " <br/> " ,
_ ( " print the calibration pdf and follow the instructions " ) , " <br/> " , " <br/> " ,
] ) ) )
self . calibration_h = self . config . get ( ' calibration_h ' )
self . calibration_v = self . config . get ( ' calibration_v ' )
cprint = QPushButton ( _ ( " Open calibration pdf " ) )
cprint . clicked . connect ( self . calibration )
vbox . addWidget ( cprint )
vbox . addWidget ( QLabel ( _ ( ' Calibration values: ' ) ) )
grid = QGridLayout ( )
vbox . addLayout ( grid )
grid . addWidget ( QLabel ( _ ( ' Right side ' ) ) , 0 , 0 )
horizontal = QLineEdit ( )
horizontal . setText ( str ( self . calibration_h ) )
grid . addWidget ( horizontal , 0 , 1 )
grid . addWidget ( QLabel ( _ ( ' Bottom ' ) ) , 1 , 0 )
vertical = QLineEdit ( )
vertical . setText ( str ( self . calibration_v ) )
grid . addWidget ( vertical , 1 , 1 )
vbox . addStretch ( )
2018-07-11 17:38:47 +02:00
vbox . addSpacing ( 13 )
2018-04-19 15:47:41 +02:00
vbox . addLayout ( Buttons ( CloseButton ( d ) , OkButton ( d ) ) )
if not d . exec_ ( ) :
return
2018-06-04 17:07:50 +02:00
self . calibration_h = int ( Decimal ( horizontal . text ( ) ) )
2018-04-19 15:47:41 +02:00
self . config . set_key ( ' calibration_h ' , self . calibration_h )
2018-06-04 17:07:50 +02:00
self . calibration_v = int ( Decimal ( vertical . text ( ) ) )
2018-04-19 15:47:41 +02:00
self . config . set_key ( ' calibration_v ' , self . calibration_v )