441 lines
19 KiB
Python
441 lines
19 KiB
Python
#!/usr/bin/env python3
|
|
'''
|
|
This script reads and writes Laser Draw (LaserDRW) LYZ files.
|
|
|
|
File history:
|
|
0.1 Initial code (2/5/2017)
|
|
|
|
Copyright (C) 2017 Scorch www.scorchworks.com
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
'''
|
|
|
|
import struct
|
|
import sys
|
|
import os
|
|
from time import time
|
|
from shutil import copyfile
|
|
|
|
##############################################################################
|
|
def show(byte_in):
|
|
print("%03d" %ord(byte_in))
|
|
|
|
|
|
def possible_values(loc,len,type,bf):
|
|
cur= bf.tell()
|
|
vals=""
|
|
if type=='d' or type=='Q' or type=='>d' or type=='>Q':
|
|
tl=8
|
|
elif type=='i' or type=='f' or type=='l' or type=='L' or type=='I':
|
|
tl=4
|
|
elif type=='>i' or type=='>f' or type=='>l' or type=='>L' or type=='>I':
|
|
tl=4
|
|
elif type=='h' or type=='H' or type=='>h' or type=='>H':
|
|
tl=2
|
|
|
|
for i in range(tl):
|
|
for j in range(i,(len-tl+1),tl):
|
|
bf.seek(loc+j)
|
|
vals = vals + "\t"+ str( struct.unpack(type,bf.read(tl))[0] )
|
|
vals = vals+"\n"
|
|
bf.seek(cur)
|
|
return vals
|
|
##############################################################################
|
|
|
|
|
|
class LYZ_CLASS:
|
|
def __init__(self):
|
|
# DEFINE HEADER FIELDS
|
|
self.header_fields = []
|
|
|
|
self.header_data = []
|
|
self.feature_list = []
|
|
self.left_over = ""
|
|
self.EOF = ""
|
|
|
|
########################## Description ,location,length,type,default value
|
|
self.header_fields.append(["EXTENSION" , 9999999, 4, 't', ".LYZ" ]) #0
|
|
self.header_fields.append(["LENGTH" , 9999999, 4, 'i', 221 ]) #1
|
|
self.header_fields.append(["N FEATURES" , 9999999, 4, 'i', 0 ]) #2
|
|
self.header_fields.append(["?A(4)" , 9999999, 4, 'i', 0 ]) #3
|
|
self.header_fields.append(["CREATOR" , 9999999, 50, 't',"Creater: LaserDraw.exe(Lihuiyu software Co., Ltd.)"]) #4
|
|
self.header_fields.append(["?B(14)" , 9999999, 14, 'z',[0,0,0,0,0,0,0,0,0,2,0,0,0,128] ]) #5
|
|
self.header_fields.append(["DESC" , 9999999, 37, 't',"Description: LaserDraw Graphics File."]) #6
|
|
self.header_fields.append(["?C(1)" , 9999999, 1, 'z', [0] ]) #7
|
|
self.header_fields.append(["?D(1)" , 9999999, 1, 'z', [0] ]) #8
|
|
self.header_fields.append(["Time(8)" , 9999999, 8, 'z', [0,0,0,0,0,0,0,0] ]) #9
|
|
self.header_fields.append(["TIME(8)" , 9999999, 8, 'z', [0,0,0,0,0,0,0,0] ]) #10
|
|
self.header_fields.append(["?G(8)" , 9999999, 4, 'z', [0,0,0,0] ]) #11
|
|
self.header_fields.append(["?H(8)" , 9999999, 4, 'z', [0,0,0,0] ]) #12
|
|
self.header_fields.append(["?I(18)" , 9999999, 18, 'z', [176,8,210,125,0,65,206,0,0,19,17,126,0,36,35,23,0,2] ]) #13
|
|
self.header_fields.append(["OFFSET" , 9999999, 8, 'd', 0.0 ]) #14 #was 84
|
|
self.header_fields.append(["X SIZE" , 9999999, 8, 'd', 42.0 ]) #15
|
|
self.header_fields.append(["Y SIZE" , 9999999, 8, 'd', 42.0 ]) #16
|
|
self.header_fields.append(["BORDER1" , 9999999, 8, 'd', 1.0 ]) #17
|
|
self.header_fields.append(["BORDER2" , 9999999, 8, 'd', 1.0 ]) #18
|
|
self.header_fields.append(["BORDER3" , 9999999, 8, 'd', 1.0 ]) #19
|
|
self.header_fields.append(["BORDER4" , 9999999, 8, 'd', 1.0 ]) #20
|
|
|
|
# DEFINE FEATURE FIELDS
|
|
self.feature_fields=[]
|
|
########################## Description ,location,length,type,default value
|
|
self.feature_fields.append(["?a(4)" , 9999999, 4, 'i', 0 ]) #0
|
|
self.feature_fields.append(["SHAPE TYPE", 9999999, 1, 'b', 10 ]) #1
|
|
#SHAPE TYPE NUMBERS
|
|
#0, circle
|
|
#1 square
|
|
#2 Square Rounded Corners
|
|
#3 Square Bevel Corners
|
|
#4 triangle
|
|
#5 diamond
|
|
#8 Star
|
|
#10 line
|
|
#12 PNG
|
|
#22 line text
|
|
self.feature_fields.append(["AC Density", 9999999, 4, 'z', [75,0,0,0] ]) #2 [ACdensity,color 0 or 8, ?,?]
|
|
self.feature_fields.append(["?b(1)" , 9999999, 1, 'z', [134] ]) #3 #solid fill 134
|
|
self.feature_fields.append(["AC cnt" , 9999999, 1, 'z', [2] ]) #4 This needs to be 2 for lines
|
|
self.feature_fields.append(["?c(1)" , 9999999, 1, 'z', [0] ]) #5
|
|
self.feature_fields.append(["?d(1)" , 9999999, 1, 'z', [6] ]) #6
|
|
self.feature_fields.append(["?e(3)" , 9999999, 3, 'z', [0 ,0 ,0 ] ]) #7
|
|
self.feature_fields.append(["?f(4)" , 9999999, 4, 'i', 16 ]) #8
|
|
self.feature_fields.append(["ZOOM" , 9999999, 8, 'd', 96 ]) #9
|
|
self.feature_fields.append(["?g(8)" , 9999999, 8, 'd', 0 ]) #10
|
|
self.feature_fields.append(["?h(8)" , 9999999, 8, 'd', 0 ]) #11
|
|
self.feature_fields.append(["?i(8)" , 9999999, 8, 'd', 0 ]) #12
|
|
self.feature_fields.append(["?j(8)" , 9999999, 8, 'd', 0 ]) #13
|
|
self.feature_fields.append(["X cent Loc", 9999999, 8, 'd', 0 ]) #14 To the Right of the center of the laser area
|
|
self.feature_fields.append(["Y cent Loc", 9999999, 8, 'd', 0 ]) #15 Down from the center of the laser area
|
|
self.feature_fields.append(["Width" , 9999999, 8, 'd', 0 ]) #16
|
|
self.feature_fields.append(["Height" , 9999999, 8, 'd', 0 ]) #17
|
|
self.feature_fields.append(["Pen Width" , 9999999, 8, 'd', 0.025 ]) #18
|
|
self.feature_fields.append(["AC Line" , 9999999, 8, 'd', 0.127 ]) #19
|
|
self.feature_fields.append(["Rot(deg)" , 9999999, 8, 'd', 0 ]) #20
|
|
self.feature_fields.append(["Corner Rad", 9999999, 8, 'd', 0 ]) #21
|
|
self.feature_fields.append(["?k(8)" , 9999999, 8, 'd', 0 ]) #22
|
|
self.feature_fields.append(["?l(8)" , 9999999, 8, 'd', 0 ]) #23
|
|
self.feature_fields.append(["?m(8)" , 9999999, 8, 'd', 0 ]) #24
|
|
self.feature_fields.append(["?n(4)" , 9999999, 4, 'i', 4 ]) #25
|
|
self.feature_fields.append(["?o(4)" , 9999999, 4, 'i', 0 ]) #26
|
|
self.feature_fields.append(["?p(4)" , 9999999, 4, 'z', [0 ,0 ,0 ,0 ] ]) #27
|
|
self.feature_fields.append(["?q(4)" , 9999999, 4, 'z', [255,255,255,0 ] ]) #28
|
|
self.feature_fields.append(["?r(4)" , 9999999, 4, 'z', [0 ,0 ,0 ,0 ] ]) #29
|
|
self.feature_fields.append(["string_len", 9999999, 4, 'i', 0 ]) #30
|
|
self.feature_fields.append(["filename" , 9999999, 4, 'x', "\000" ]) #31
|
|
self.feature_fields.append(["?u(4)" , 9999999, 4, 'z', [0 ,0 ,0 ,0 ] ]) #32
|
|
self.feature_fields.append(["ACtexture1", 9999999, 4, 'z', [255,255,255,255] ]) #33 [0 ,0 ,0 ,0 ]
|
|
self.feature_fields.append(["ACtexture2", 9999999, 4, 'z', [0 ,0 ,0 ,0 ] ]) #34
|
|
self.feature_fields.append(["?v(4)" , 9999999, 4, 'z', [0 ,0 ,0 ,0 ] ]) #35
|
|
self.feature_fields.append(["?w(4)" , 9999999, 4, 'z', [2 ,0 ,0 ,0 ] ]) #36
|
|
self.feature_fields.append(["data length", 9999999, 4, 'i', 2 ]) #37 needs to be 2 for line
|
|
|
|
self.feature_appendix = []
|
|
for i in range(13):
|
|
self.feature_appendix.append([])
|
|
## Appendix values for Line
|
|
self.feature_appendix[10].append(["line X1", 9999999, 4, 'i', -10000 ]) #position as 1000*value
|
|
self.feature_appendix[10].append(["line Y1", 9999999, 4, 'i', -10000 ]) #position as 1000*value
|
|
self.feature_appendix[10].append(["line X2", 9999999, 4, 'i', 10000 ]) #position as 1000*value
|
|
self.feature_appendix[10].append(["line Y2", 9999999, 4, 'i', 10000 ]) #position as 1000*value
|
|
self.feature_appendix[10].append(["lineEND", 9999999, 4, 'i', 0 ])
|
|
##Appendix values for PNG
|
|
self.feature_appendix[12].append(["PNGdata", 9999999, 0, 't', "" ])
|
|
self.feature_appendix[12].append(["PNGend" , 9999999, 8, 'z', [0,0,0,0,0,0,0,0] ])
|
|
|
|
|
|
def lyz_read(self,loc,len,type,bf):
|
|
#try:
|
|
if 1==1:
|
|
#bf.seek(loc)
|
|
if type=='t':
|
|
data = bf.read(len)
|
|
elif type == 'z':
|
|
data = []
|
|
for i in range(len):
|
|
data.append(ord(bf.read(1)))
|
|
elif type == 'x':
|
|
data = ""
|
|
for i in range(0,len,4):
|
|
data_temp = bf.read(4)
|
|
data = data + data_temp[0]
|
|
else:
|
|
data = struct.unpack(type, bf.read(len))[0]
|
|
return data
|
|
#except:
|
|
# print("Error Reading data (lyz_read)")
|
|
# return []
|
|
|
|
|
|
def lyz_write(self,data,type,bf):
|
|
#print("type,data: ",type,data)
|
|
if type=='t':
|
|
#print("data:",data)
|
|
#bf.write(data)
|
|
try:
|
|
bf.write(data)
|
|
except:
|
|
bf.write(data.encode())
|
|
elif type == 'z':
|
|
for i in range(len(data)):
|
|
#bf.write(chr(data[i]))
|
|
bf.write(struct.pack('B',data[i]))
|
|
elif type == 'x':
|
|
for char in data:
|
|
bf.write(char.encode())
|
|
bf.write(struct.pack('B',0))
|
|
bf.write(struct.pack('B',0))
|
|
bf.write(struct.pack('B',0))
|
|
else:
|
|
bf.write(struct.pack(type,data))
|
|
|
|
def read_header(self,f):
|
|
self.header_data=[]
|
|
for line in self.header_fields:
|
|
#pos = line[1]
|
|
len = line[2]
|
|
typ = line[3]
|
|
self.header_data.append(self.lyz_read(None,len,typ,f))
|
|
|
|
|
|
def read_feature(self,f):
|
|
feature_data=[]
|
|
for i in range(len(self.feature_fields)):
|
|
length = self.feature_fields[i][2]
|
|
typ = self.feature_fields[i][3]
|
|
|
|
if i==31 and feature_data[1]==12:
|
|
string_length = feature_data[-1]*4
|
|
feature_data.append(self.lyz_read(None,string_length,typ,f))
|
|
else:
|
|
feature_data.append(self.lyz_read(None,length,typ,f))
|
|
|
|
#if i==30 and feature_data[1]==12:
|
|
# self.feature_fields[i+1][2] = feature_data[-1]*4
|
|
|
|
|
|
feat_type = feature_data[1]
|
|
if feat_type==10 or feat_type==12:
|
|
for i in range(len(self.feature_appendix[feat_type])):
|
|
if feat_type==12 and i==0:
|
|
length = feature_data[-1]
|
|
else:
|
|
length = self.feature_appendix[feat_type][i][2]
|
|
typ = self.feature_appendix[feat_type][i][3]
|
|
feature_data.append(self.lyz_read(None,length,typ,f))
|
|
return feature_data
|
|
|
|
|
|
def setup_new_header(self):
|
|
self.header_data=[]
|
|
for line in self.header_fields:
|
|
data = line[4]
|
|
self.header_data.append(data)
|
|
|
|
|
|
def add_line(self,x1,y1,x2,y2,Pen_Width=.025):
|
|
feature_data=[]
|
|
for line in self.feature_fields:
|
|
data = line[4]
|
|
feature_data.append(data)
|
|
feature_data.append(int(x1*1000.0))
|
|
feature_data.append(int(y1*1000.0))
|
|
feature_data.append(int(x2*1000.0))
|
|
feature_data.append(int(y2*1000.0))
|
|
feature_data.append(0)
|
|
feature_data[1]=10 #set type to line
|
|
feature_data[4]=[2] #Not sure what this is for lines but it needs to be 2
|
|
feature_data[18]=Pen_Width
|
|
|
|
self.header_data[2]=self.header_data[2]+1
|
|
self.feature_list.append(feature_data)
|
|
|
|
def add_png(self,PNG_DATA,Xsixe,Ysize):
|
|
filename="filename"
|
|
feature_data=[]
|
|
for line in self.feature_fields:
|
|
data = line[4]
|
|
feature_data.append(data)
|
|
feature_data.append(PNG_DATA)
|
|
feature_data.append([0,0,0,0,0,0,0,0])
|
|
feature_data[1] = 12 # set type to PNG
|
|
feature_data[3] = [150]
|
|
feature_data[2] = [75, 4, 0, 144]
|
|
feature_data[4] = [0] # Number of Anti-Counterfeit lines
|
|
feature_data[6] = [12] # if this is not set to [12] the image does not get passed to the engrave window
|
|
feature_data[16] = Xsixe # set PNG width
|
|
feature_data[17] = Ysize # set PNG height
|
|
feature_data[18] = 1.0
|
|
feature_data[26] = 16777215
|
|
feature_data[30]= len(filename) # set filename length
|
|
feature_data[31]= filename # set filename
|
|
feature_data[33]=[0 ,0 ,0 ,0 ]
|
|
feature_data[34]=[255,255,255,255]
|
|
feature_data[36]=[226, 29, 5, 175]
|
|
feature_data[37]= len(PNG_DATA) # set PNG data length
|
|
|
|
self.header_data[2]=self.header_data[2]+1
|
|
self.feature_list.append(feature_data)
|
|
|
|
def set_size(self,Xsize,Ysize):
|
|
self.header_data[15]=Xsize
|
|
self.header_data[16]=Ysize
|
|
|
|
def set_margin(self,margin):
|
|
self.header_data[17]=margin/2
|
|
self.header_data[18]=margin/2
|
|
self.header_data[19]=margin/2
|
|
self.header_data[20]=margin/2
|
|
|
|
def find_PNG(self,f):
|
|
self.PNGstart = -1
|
|
self.PNGend = -1
|
|
f.seek(0)
|
|
loc=0
|
|
flag = True
|
|
while flag:
|
|
byte=f.read(1)
|
|
if byte=="":
|
|
flag=False
|
|
if byte =="P":
|
|
if byte =="N":
|
|
if byte =="G":
|
|
self.PNGstart = f.tell()-4
|
|
if byte =="E":
|
|
if byte =="N":
|
|
if byte =="D":
|
|
self.PNGend = f.tell()+4
|
|
flag = False
|
|
f.seek(0)
|
|
|
|
|
|
def read_file(self, file_name):
|
|
with open(file_name, "rb") as f:
|
|
self.find_PNG(f)
|
|
PNGlen = self.PNGend-self.PNGstart
|
|
self.png_message = "PNGlen: ",PNGlen
|
|
self.read_header(f)
|
|
for i in range(self.header_data[2]):
|
|
data = self.read_feature(f)
|
|
self.feature_list.append(data)
|
|
|
|
self.left_over = f.read( self.header_data[1]-4-f.tell() )
|
|
|
|
self.EOF = ""
|
|
byte = f.read(1)
|
|
while byte!="":
|
|
self.EOF=self.EOF+byte
|
|
byte = f.read(1)
|
|
#print(possible_values(200+217,348-200,'d',f))
|
|
|
|
|
|
def write_file(self, file_name):
|
|
with open(file_name, "wb") as f:
|
|
for i in range(len(self.header_fields)):
|
|
typ = self.header_fields[i][3]
|
|
data = self.header_data[i]
|
|
self.lyz_write(data,typ,f)
|
|
|
|
for j in range(len(self.feature_list)):
|
|
for i in range(len(self.feature_fields)):
|
|
typ = self.feature_fields[i][3]
|
|
data = self.feature_list[j][i]
|
|
#print(j,i," typ,data: ",typ,data)
|
|
self.lyz_write(data,typ,f)
|
|
|
|
feat_type=self.feature_list[j][1]
|
|
if feat_type==10 or feat_type==12:
|
|
appendix_data=[]
|
|
for i in range(len(self.feature_appendix[feat_type])):
|
|
typ = self.feature_appendix[feat_type][i][3]
|
|
data = self.feature_list[j][i+len(self.feature_fields)] #appendix_data
|
|
#print(j,i," typ,data: "typ,data)
|
|
self.lyz_write(data,typ,f)
|
|
|
|
f.write("@EOF".encode())
|
|
length=f.tell()
|
|
f.seek(4)
|
|
f.write(struct.pack('i',length))
|
|
|
|
|
|
def print_header(self):
|
|
print("\nHEADER DATA:")
|
|
print("--------------------")
|
|
for i in range(len(self.header_data)):
|
|
print("%11s : " %(self.header_fields[i][0]),self.header_data[i])
|
|
|
|
|
|
def print_features(self):
|
|
for i in range(len(self.feature_list)):
|
|
print("\nFEATURE #%d:" %(i+1))
|
|
print("--------------------")
|
|
feature = self.feature_list[i]
|
|
for j in range(len(self.feature_fields)):
|
|
try:
|
|
print("%11s : " %(self.feature_fields[j][0]),feature[j])
|
|
except:
|
|
print("error")
|
|
feat_type = feature[1]
|
|
if feat_type==10 or feat_type==12:
|
|
print("---LINE COORDS---")
|
|
for j in range(len(self.feature_appendix[feat_type])):
|
|
jj = j+len(self.feature_fields)
|
|
if feat_type==12 and jj==38:
|
|
print("%11s : " %(self.feature_appendix[feat_type][j][0]),"....")
|
|
else:
|
|
print("%11s : " %(self.feature_appendix[feat_type][j][0]),feature[jj])
|
|
print("--------------------")
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
###############################
|
|
try:
|
|
file_name = sys.argv[1]
|
|
print("input: ",file_name)
|
|
except:
|
|
file_name = ""
|
|
###############################
|
|
try:
|
|
file_out = sys.argv[2]
|
|
print("output: ",file_name)
|
|
except:
|
|
file_out = ""
|
|
###############################
|
|
|
|
if file_name=="test":
|
|
LYZ=LYZ_CLASS()
|
|
LYZ.setup_new_header()
|
|
#image_file = "squigles.png"
|
|
#image_file = "drawing_mod.png"
|
|
image_file = "temp.png"
|
|
with open(image_file, 'rb') as f:
|
|
PNG_DATA = f.read()
|
|
LYZ.add_png(PNG_DATA,20,20)
|
|
|
|
LYZ.add_line(5,5,-10,-10,0.025)
|
|
#LYZ.print_header()
|
|
#LYZ.print_features()
|
|
LYZ.write_file("test.lyz")
|
|
else:
|
|
if file_name!="":
|
|
LYZ=LYZ_CLASS()
|
|
LYZ.read_file(file_name)
|
|
LYZ.print_header()
|
|
LYZ.print_features()
|
|
print("LEFTOVER :", LYZ.left_over)
|
|
print("EOF :",LYZ.EOF)
|
|
|
|
if file_out!="":
|
|
LYZ.write_file(file_out) |