3 # ***** BEGIN GPL LICENSE BLOCK *****
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # ***** END GPL LICENCE BLOCK *****
21 ######################################################
27 # Creates a browsable DNA output to HTML.
33 # v0.1 (12-05-2009) - migration of original source code to python.
34 # Added code to support blender 2.5 branch
35 # v0.2 (25-05-2009) - integrated with BlendFileReader.py
38 # blender build executable
42 # dna.css (will only be created when not existing)
45 # ./blender -P BlendFileDnaExporter.py
48 # 1: write blend file with SDNA info
49 # 2: read blend header from blend file
50 # 3: seek DNA1 file-block
51 # 4: read dna record from blend file
52 # 5: close and eventually delete temp blend file
53 # 6: export dna to html and css
56 ######################################################
60 import getopt # command line arguments handling
61 from string import Template # strings completion
66 log = logging.getLogger("BlendFileDnaExporter")
68 if '--dna-debug' in sys.argv:
69 logging.basicConfig(level=logging.DEBUG)
71 logging.basicConfig(level=logging.INFO)
76 DNACatalog is a catalog of all information in the DNA1 file-block
79 def __init__(self, catalog, bpy_module=None):
80 self.Catalog = catalog
83 def WriteToHTML(self, handle):
85 dna_html_template = """
86 <!DOCTYPE html PUBLIC -//W3C//DTD HTML 4.01 Transitional//EN http://www.w3.org/TR/html4/loose.dtd>
89 <link rel="stylesheet" type="text/css" href="dna.css" media="screen, print" />
90 <meta http-equiv="Content-Type" content="text/html"; charset="ISO-8859-1" />
91 <title>The mystery of the blend</title>
95 Blender ${version}<br/>
96 Internal SDNA structures
98 Architecture: ${bitness} ${endianness}<br/>
99 Build revision: <a href="https://svn.blender.org/svnroot/bf-blender/!svn/bc/${revision}/trunk/">${revision}</a><br/>
100 File format reference: <a href="mystery_of_the_blend.html">The mystery of the blend</a> by Jeroen Bakker<br/>
101 <h1>Index of blender structures</h1>
102 <ul class=multicolumn>
109 header = self.Catalog.Header
112 # ${version} and ${revision}
114 version = '.'.join(map(str, bpy.app.version))
115 revision = bpy.app.build_hash
117 version = str(header.Version)
121 if header.PointerSize == 8:
127 if header.LittleEndianness:
128 endianess = 'Little endianness'
130 endianess = 'Big endianness'
133 log.debug("Creating structs index")
135 list_item = '<li class="multicolumn">({0}) <a href="#{1}">{1}</a></li>\n'
137 for structure in self.Catalog.Structs:
138 structs_list += list_item.format(structureIndex, structure.Type.Name)
142 log.debug("Creating structs content")
144 for structure in self.Catalog.Structs:
145 log.debug(structure.Type.Name)
146 structs_content += self.Structure(structure)
152 endianness=endianess,
153 structs_list=structs_list,
154 structs_content=structs_content
157 dna_html = Template(dna_html_template).substitute(d)
158 dna_html = self.format(dna_html)
159 handle.write(dna_html)
161 def Structure(self, structure):
162 struct_table_template = """
163 <table><a name="${struct_name}"></a>
164 <caption><a href="#${struct_name}">${struct_name}</a></caption>
179 <label>Total size: ${size} bytes</label><br/>
180 <label>(<a href="#top">top</a>)</label><br/>"""
183 struct_name=structure.Type.Name,
184 fields=self.StructureFields(structure, None, 0),
185 size=str(structure.Type.Size)
188 struct_table = Template(struct_table_template).substitute(d)
191 def StructureFields(self, structure, parentReference, offset):
193 for field in structure.Fields:
194 fields += self.StructureField(field, structure, parentReference, offset)
195 offset += field.Size(self.Catalog.Header)
198 def StructureField(self, field, structure, parentReference, offset):
199 structure_field_template = """
201 <td>${reference}</td>
209 if field.Type.Structure is None or field.Name.IsPointer():
212 reference = field.Name.AsReference(parentReference)
215 if parentReference is not None:
216 struct = '<a href="#{0}">{0}</a>'.format(structure.Type.Name)
218 struct = structure.Type.Name
221 type = field.Type.Name
224 name = field.Name.Name
230 size = field.Size(self.Catalog.Header)
241 structure_field = Template(structure_field_template).substitute(d)
243 elif field.Type.Structure is not None:
244 reference = field.Name.AsReference(parentReference)
245 structure_field = self.StructureFields(field.Type.Structure, reference, offset)
247 return structure_field
249 def indent(self, input, dent, startswith=''):
252 for line in input.split('\n'):
254 output += line[dent:] + '\n' # unindent of a desired amount
256 for line in input.split('\n'):
257 output += line.lstrip() + '\n' # remove indentation completely
259 for line in input.split('\n'):
260 output += ' ' * dent + line + '\n'
263 def format(self, input):
265 '\n<!DOCTYPE': '<!DOCTYPE',
267 '<a name': '\n<a name',
272 '<tbody>\n': '<tbody>'
274 output = self.indent(input, 0)
275 for key, value in diff.items():
276 output = output.replace(key, value)
279 def WriteToCSS(self, handle):
281 Write the Cascading stylesheet template to the handle
282 It is expected that the handle is a Filehandle
285 @CHARSET "ISO-8859-1";
288 font-family: verdana;
298 page-break-before: always;
302 background-color: #D3D3D3;
309 background-color: #EBEBEB;
319 border-color: #000000;
320 border-collapse: collapse;
322 margin: 20px 3% 10px;
330 background-color: #000000;
342 border-color: #a0a0a0;
367 text-decoration:none;
372 text-decoration:underline;
376 css = self.indent(css, 0)
382 print("\nUsage: \n\tblender2.5 --background -noaudio --python BlendFileDnaExporter_25.py [-- [options]]")
384 print("\t--dna-keep-blend: doesn't delete the produced blend file DNA export to html")
385 print("\t--dna-debug: sets the logging level to DEBUG (lots of additional info)")
386 print("\t--dna-versioned saves version information in the html and blend filenames")
387 print("\t--dna-overwrite-css overwrite dna.css, useful when modifying css in the script")
389 print("\tdefault: % blender2.5 --background -noaudio --python BlendFileDnaExporter_25.py")
390 print("\twith options: % blender2.5 --background -noaudio --python BlendFileDnaExporter_25.py -- --dna-keep-blend --dna-debug\n")
393 ######################################################
395 ######################################################
402 bpy = __import__('bpy')
405 if '--dna-versioned' in sys.argv:
406 blender_version = '_'.join(map(str, bpy.app.version))
407 filename = 'dna-{0}-{1}_endian-{2}-{3}'.format(sys.arch, sys.byteorder, blender_version, bpy.app.build_hash)
410 dir = os.path.dirname(__file__)
411 Path_Blend = os.path.join(dir, filename + '.blend') # temporary blend file
412 Path_HTML = os.path.join(dir, filename + '.html') # output html file
413 Path_CSS = os.path.join(dir, 'dna.css') # output css file
415 # create a blend file for dna parsing
416 if not os.path.exists(Path_Blend):
417 log.info("1: write temp blend file with SDNA info")
418 log.info(" saving to: " + Path_Blend)
420 bpy.ops.wm.save_as_mainfile(filepath=Path_Blend, copy=True, compress=False)
422 log.error("Filename {0} does not exist and can't be created... quitting".format(Path_Blend))
425 log.info("1: found blend file with SDNA info")
426 log.info(" " + Path_Blend)
428 # read blend header from blend file
429 log.info("2: read file:")
431 if not dir in sys.path:
433 import BlendFileReader
435 handle = BlendFileReader.openBlendFile(Path_Blend)
436 blendfile = BlendFileReader.BlendFile(handle)
437 catalog = DNACatalogHTML(blendfile.Catalog, bpy)
443 if '--dna-keep-blend' in sys.argv:
444 # keep the blend, useful for studying hexdumps
445 log.info("5: closing blend file:")
446 log.info(" {0}".format(Path_Blend))
449 log.info("5: close and delete temp blend:")
450 log.info(" {0}".format(Path_Blend))
451 os.remove(Path_Blend)
453 # export dna to xhtml
454 log.info("6: export sdna to xhtml file: %r" % Path_HTML)
455 handleHTML = open(Path_HTML, "w")
456 catalog.WriteToHTML(handleHTML)
459 # only write the css when doesn't exist or at explicit request
460 if not os.path.exists(Path_CSS) or '--dna-overwrite-css' in sys.argv:
461 handleCSS = open(Path_CSS, "w")
462 catalog.WriteToCSS(handleCSS)
466 if not bpy.app.background:
467 log.info("7: quit blender")
468 bpy.ops.wm.exit_blender()
471 log.warning(" skipping, not running in Blender")
476 if __name__ == '__main__':